📄 cmp_rd.c
字号:
status = sseek( stream, endPos );
return( status );
}
/****************************************************************************
* *
* Read a PKI Message *
* *
****************************************************************************/
/* Read a PKI message:
PkiMessage ::= SEQUENCE {
header PKIHeader,
body CHOICE { [0]... [24]... },
protection [0] BIT STRING
} */
int readPkiMessage( SESSION_INFO *sessionInfoPtr,
CMP_PROTOCOL_INFO *protocolInfo,
int messageType )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
STREAM stream;
const BOOLEAN isServerInitialMessage = ( messageType == CRYPT_UNUSED );
BOOLEAN useMAC;
int protPartStart, protPartSize, bodyStart;
int length, integrityInfoLength, tag, status;
/* Strip off the header and PKIStatus wrapper */
sMemConnect( &stream, sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufEnd );
readSequence( &stream, NULL ); /* Outer wrapper */
protPartStart = stell( &stream );
status = readPkiHeader( &stream, protocolInfo, sessionInfoPtr,
isServerInitialMessage );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Set up state information based on the header that we've just read. If
this is the first message from the client and we've been sent a new
user ID or cert ID, process the user/authentication info. We
couldn't process this info before this point because we didn't know
what would be required, but now that we've read the header we can
set it up and get the user authentication information from the cert
store */
useMAC = ( protocolInfo->macInfoPos > 0 ) ? TRUE : FALSE;
if( protocolInfo->isCryptlib )
sessionInfoPtr->flags |= SESSION_ISCRYPTLIB;
if( protocolInfo->userIDchanged )
{
/* We've got a new PKI user ID, if it looks like a cryptlib encoded
ID save it in encoded form, otherwise save it as is. Note that
the value passed to encodePKIUserValue() is the number of code
groups to produce in the encoded value, not the input length */
if( protocolInfo->isCryptlib && \
protocolInfo->userIDsize == 9 )
{
sessionInfoPtr->userNameLength = \
encodePKIUserValue( sessionInfoPtr->userName,
protocolInfo->userID, 3 );
sessionInfoPtr->flags |= SESSION_ISENCODEDUSERID;
}
else
{
memcpy( sessionInfoPtr->userName, protocolInfo->userID,
protocolInfo->userIDsize );
sessionInfoPtr->userNameLength = protocolInfo->userIDsize;
}
if( isServerInitialMessage && useMAC )
status = initServerAuthentMAC( sessionInfoPtr, protocolInfo );
}
if( protocolInfo->certIDchanged )
{
memcpy( sessionInfoPtr->keyFingerprint, protocolInfo->certID,
protocolInfo->certIDsize );
sessionInfoPtr->keyFingerprintSize = protocolInfo->certIDsize;
if( isServerInitialMessage )
status = initServerAuthentSign( sessionInfoPtr, protocolInfo );
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Determine the message body type. An error response can occur at any
point in an exchange so we process this immediately. We don't do an
integrity verification at this point since it's not certain what we
should report if the check fails, and an unauthenticated error
message is better than an authenticated paketewhainau */
tag = EXTRACT_CTAG( peekTag( &stream ) );
if( tag == CTAG_PB_ERROR )
{
status = readErrorBody( &stream, sessionInfoPtr );
sMemDisconnect( &stream );
return( status );
}
/* If this is an initial message we don't know what to expect yet so we
set the type to whatever we find, as long as it's a valid message to
send to a CA */
if( isServerInitialMessage && \
( tag == CTAG_PB_IR || tag == CTAG_PB_CR || tag == CTAG_PB_P10CR || \
tag == CTAG_PB_KUR || tag == CTAG_PB_RR || tag == CTAG_PB_GENM ) )
protocolInfo->operation = messageType = tag;
/* If we're using a MAC for authentication, we can finally set up the
MAC info using the appropriate password */
if( useMAC )
{
BYTE decodedValue[ CRYPT_MAX_TEXTSIZE ];
const BYTE *decodedValuePtr = decodedValue;
int decodedValueLength;
if( sessionInfoPtr->flags & SESSION_ISENCODEDPW )
{
/* It's an encoded value, get the decoded form */
decodedValueLength = decodePKIUserValue( decodedValue,
sessionInfoPtr->password,
sessionInfoPtr->passwordLength );
if( cryptStatusError( decodedValueLength ) )
{
assert( NOTREACHED );
sMemDisconnect( &stream );
retExt( sessionInfoPtr, decodedValueLength,
"Invalid PKI user password" );
}
}
else
{
decodedValuePtr = sessionInfoPtr->password;
decodedValueLength = sessionInfoPtr->passwordLength;
}
/* We couldn't initialise the MAC information when we read the
header because the order of the information used to set this up
is backwards, so we have to go back and re-process it now */
if( cryptStatusOK( status ) )
{
const int streamPos = stell( &stream );
sseek( &stream, protocolInfo->macInfoPos );
status = readMacInfo( &stream, protocolInfo, decodedValuePtr,
decodedValueLength, sessionInfoPtr );
sseek( &stream, streamPos );
}
zeroise( decodedValue, CRYPT_MAX_TEXTSIZE );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
}
/* Make sure that it's what we're after, remember where the message body
starts, and skip it (it'll be processed after we verify its integrity) */
if( tag != messageType )
{
sMemDisconnect( &stream );
protocolInfo->pkiFailInfo = CMPFAILINFO_BADREQUEST;
if( isServerInitialMessage )
/* This is the first message and we got no recognisable message
of any type */
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid message type %d", tag );
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid message type, expected %d, got %d", messageType,
tag );
}
status = readConstructed( &stream, &length, messageType );
if( cryptStatusOK ( status ) )
{
bodyStart = stell( &stream );
status = sSkip( &stream, length );
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
protocolInfo->pkiFailInfo = CMPFAILINFO_BADDATAFORMAT;
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid message body start" );
}
/* Read the start of the message integrity info */
protPartSize = stell( &stream ) - protPartStart;
status = readConstructed( &stream, &integrityInfoLength,
CTAG_PM_PROTECTION );
if( cryptStatusOK( status ) && \
integrityInfoLength > sMemDataLeft( &stream ) )
{
/* If there integrity protection is missing, report it as a wrong-
integrity-info problem, the closest we can get to the real
error. This has already been checked by the high-level PKI
datagram read code anyway, but we check gain here just to be
safe */
protocolInfo->pkiFailInfo = CMPFAILINFO_WRONGINTEGRITY;
strcpy( sessionInfoPtr->errorMessage,
"Signature/MAC data is missing or truncated" );
status = CRYPT_ERROR_SIGNATURE;
}
if( cryptStatusOK( status ) && tag == CTAG_PB_IR && !useMAC )
{
/* An ir has to be MAC'ed, in theory this doesn't really matter but
the spec requires that we only allow a MAC. If it's not MAC'ed it
has to be a cr, which is exactly the same only different */
protocolInfo->pkiFailInfo = CMPFAILINFO_WRONGINTEGRITY;
strcpy( sessionInfoPtr->errorMessage,
"Received signed ir, should be MAC'ed" );
status = CRYPT_ERROR_SIGNATURE;
}
if( cryptStatusOK( status ) && tag == CTAG_PB_RR && useMAC )
{
/* An rr can't be MAC'ed because the trail from the original PKI
user to the cert being revoked can become arbitrarily blurred,
with the cert being revoked having a different DN,
issuerAndSerialNumber, and public key after various updates,
replacements, and reissues. In fact cryptlib tracks the
resulting directed graph via the cert store log and allows
fetching the original authorising issuer of a cert using the
KEYMGMT_FLAG_GETISSUER option, however this requires that the
client also be running cryptlib, or specifically that it submit
a cert ID in the request, this being the only identifier that
reliably identifies the cert being revoked. Since it's somewhat
unsound to assume this, we don't currently handle MAC'ed rr's,
however everything is in place to allow them to be implemented
if they're really needed */
protocolInfo->pkiFailInfo = CMPFAILINFO_WRONGINTEGRITY;
strcpy( sessionInfoPtr->errorMessage,
"Received MAC'ed rr, should be signed" );
status = CRYPT_ERROR_SIGNATURE;
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Verify the message integrity */
if( protocolInfo->useMACreceive )
{
const CRYPT_CONTEXT iMacContext = ( protocolInfo->useAltMAC ) ? \
protocolInfo->iAltMacContext : protocolInfo->iMacContext;
RESOURCE_DATA msgData;
BYTE macBuffer[ CRYPT_MAX_HASHSIZE ];
int protectionLength;
/* Read the BIT STRING encapsulation, MAC the data, and make sure
that it matches the value attached to the message */
status = readBitStringHole( &stream, &protectionLength, DEFAULT_TAG );
if( cryptStatusOK( status ) && \
protectionLength > sMemDataLeft( &stream ) )
status = CRYPT_ERROR_UNDERFLOW;
if( cryptStatusOK( status ) )
status = hashMessageContents( iMacContext,
sessionInfoPtr->receiveBuffer + protPartStart,
protPartSize );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, macBuffer, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( iMacContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_HASHVALUE );
}
if( cryptStatusError( status ) || \
protectionLength != msgData.length || \
memcmp( macBuffer, sMemBufPtr( &stream ), msgData.length ) )
{
sMemDisconnect( &stream );
retExt( sessionInfoPtr, CRYPT_ERROR_SIGNATURE,
"Bad message MAC" );
}
}
else
{
if( !protocolInfo->isCryptlib )
{
RESOURCE_DATA msgData;
/* Make sure that the sig-check key that we'll be using is the
correct one. Because of CMP's use of a raw signature format
we have to do this manually rather than relying on the sig-
check code to do it for us, and because of the braindamaged
way of identifying integrity-protection keys for non-cryptlib
messages even this isn't enough to definitely tell us that
we're using the right key, in which case we'll get a bad data
or bad sig response from the sig check code */
setMessageData( &msgData, protocolInfo->senderDNPtr,
protocolInfo->senderDNlength );
status = krnlSendMessage( sessionInfoPtr->iAuthInContext,
IMESSAGE_COMPARE, &msgData,
MESSAGE_COMPARE_SUBJECT );
if( cryptStatusError( status ) )
{
/* A failed comparison is reported as a generic CRYPT_ERROR,
convert it into a wrong-key error if necessary */
sMemDisconnect( &stream );
retExt( sessionInfoPtr, ( status == CRYPT_ERROR ) ? \
CRYPT_ERROR_WRONGKEY : status,
"Message signature key doesn't match our signature "
"check key, signature can't be checked" );
}
}
/* Hash the data and verify the signature */
setMessageCreateObjectInfo( &createInfo, protocolInfo->hashAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT, &createInfo,
OBJECT_TYPE_CONTEXT );
if( cryptStatusOK( status ) )
{
status = hashMessageContents( createInfo.cryptHandle,
sessionInfoPtr->receiveBuffer + protPartStart,
protPartSize );
if( cryptStatusOK( status ) )
status = checkRawSignature( sMemBufPtr( &stream ),
integrityInfoLength,
sessionInfoPtr->iAuthInContext,
createInfo.cryptHandle );
krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
retExt( sessionInfoPtr, CRYPT_ERROR_SIGNATURE,
"Bad message signature" );
}
}
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
sseek( &stream, bodyStart );
/* If it's a client request, import the encapsulated request data */
switch( messageType )
{
case CTAG_PB_IR:
case CTAG_PB_CR:
case CTAG_PB_P10CR:
case CTAG_PB_KUR:
case CTAG_PB_RR:
status = readRequestBody( &stream, sessionInfoPtr, protocolInfo,
messageType );
break;
case CTAG_PB_IP:
case CTAG_PB_CP:
case CTAG_PB_KUP:
case CTAG_PB_RP:
status = readResponseBody( &stream, sessionInfoPtr,
protocolInfo );
break;
case CTAG_PB_CERTCONF:
status = readConfBody( &stream, sessionInfoPtr, protocolInfo );
break;
case CTAG_PB_PKICONF:
/* If it's a confirmation there's no message body and we're
done */
break;
case CTAG_PB_GENM:
case CTAG_PB_GENP:
status = readGenMsgBody( &stream, sessionInfoPtr,
messageType == CTAG_PB_GENM );
break;
default:
assert( NOTREACHED );
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Unexpected message type %d", messageType );
}
sMemDisconnect( &stream );
return( status );
}
#endif /* USE_CMP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -