📄 cmp_rd.c
字号:
INOUT ERROR_INFO *errorInfo,
const BOOLEAN isServerInitialMessage )
{
CRYPT_ALGO_TYPE cryptAlgo, hashAlgo;
BYTE buffer[ CRYPT_MAX_HASHSIZE + 8 ];
int length, streamPos, endPos, status;
/* Clear per-message state information */
protocolInfo->userIDchanged = protocolInfo->certIDchanged = FALSE;
protocolInfo->macInfoPos = CRYPT_ERROR;
protocolInfo->senderDNPtr = NULL;
protocolInfo->senderDNlength = 0;
protocolInfo->headerRead = FALSE;
/* Read the wrapper and skip the static info, which matches what we sent
and is protected by the MAC so there's little point in looking at
it */
readSequence( stream, &length );
endPos = stell( stream ) + length;
readShortInteger( stream, NULL ); /* Version */
if( !protocolInfo->isCryptlib )
{
/* The ID of the key used for integrity protection (or in general
the identity of the sender) can be specified either as the sender
DN or the senderKID or both, or in some cases even indirectly via
the transaction ID. With no real guidance as to which one to use,
implementors are using any of these options to identify the key.
Since we need to check that the integrity-protection key we're
using is correct so that we can report a more appropriate error
than bad signature or bad data, we need to remember the sender DN
for later in case this is the only form of key identification
provided. Unfortunately since the sender DN can't uniquely
identify a cert, if this is all we get then the caller can still
get a bad signature error, yet another one of CMPs many wonderful
features */
status = readConstructed( stream, &protocolInfo->senderDNlength, 4 );
if( cryptStatusOK( status ) && protocolInfo->senderDNlength > 0 )
{
status = sMemGetDataBlock( stream, &protocolInfo->senderDNPtr,
protocolInfo->senderDNlength );
if( cryptStatusOK( status ) )
status = readUniversal( stream );/* Sender DN */
}
}
else
{
/* cryptlib includes a proper certID so the whole signer
identification mess is avoided and we can ignore the sender DN */
status = readUniversal( stream ); /* Sender DN */
}
if( cryptStatusOK( status ) )
status = readUniversal( stream ); /* Recipient */
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_MESSAGETIME ) )
status = readUniversal( stream ); /* Message time */
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, errorInfo, "Invalid PKI header" ) );
}
if( peekTag( stream ) != MAKE_CTAG( CTAG_PH_PROTECTIONALGO ) )
{
/* The message was sent without integrity protection, report it as
a signature error rather than the generic bad data error that
we'd get from the following read */
retExt( CRYPT_ERROR_SIGNATURE,
( CRYPT_ERROR_SIGNATURE, errorInfo,
"Message was sent without integrity protection" ) );
}
status = readConstructed( stream, NULL, CTAG_PH_PROTECTIONALGO );
if( cryptStatusError( status ) )
{
/* If there was a problem we should exit now since an error status
from the following readAlgoIDex() is interpreted to indicate the
presence of the weird Entrust MAC rather than a real error */
retExt( status,
( status, errorInfo,
"Invalid integrity protection info in PKI header" ) );
}
streamPos = stell( stream );
status = readAlgoIDext( stream, &cryptAlgo, &hashAlgo );
if( cryptStatusOK( status ) )
{
/* It's a known signature algorithm, use the CA cert to verify it
rather than the MAC */
protocolInfo->useMACreceive = FALSE;
protocolInfo->hashAlgo = hashAlgo;
}
else
{
/* It's nothing normal, it must be the Entrust MAC algorithm info,
remember where it starts so that we can process it later */
sClearError( stream );
protocolInfo->macInfoPos = streamPos;
readUniversal( stream );
protocolInfo->useMACreceive = TRUE;
}
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERKID ) )
{ /* Sender protection keyID */
if( isServerInitialMessage )
{
BYTE userID[ CRYPT_MAX_HASHSIZE + 8 ];
int userIDsize;
/* Read the PKI user ID that we'll need to handle the integrity
protection on the message */
readConstructed( stream, NULL, CTAG_PH_SENDERKID );
status = readOctetString( stream, userID, &userIDsize,
8, CRYPT_MAX_HASHSIZE );
if( cryptStatusError( status ) )
retExt( status,
( status, errorInfo,
"Invalid user ID in PKI header" ) );
/* If there's already been a previous transaction (which means
that we have PKI user info present) and the current
transaction matches what was used in the previous one, we
don't have to update the user info */
if( protocolInfo->userIDsize <= 0 || \
protocolInfo->userIDsize != userIDsize || \
memcmp( protocolInfo->userID, userID, userIDsize ) )
{
memcpy( protocolInfo->userID, userID, userIDsize );
protocolInfo->userIDsize = userIDsize;
protocolInfo->userIDchanged = TRUE;
if( protocolInfo->iMacContext != CRYPT_ERROR )
{
krnlSendNotifier( protocolInfo->iMacContext,
IMESSAGE_DECREFCOUNT );
protocolInfo->iMacContext = CRYPT_ERROR;
}
}
}
else
{
/* We're in the middle of an ongoing transaction, skip the user
ID, which we already know */
readUniversal( stream );
}
}
else
{
/* If we're the server, the client must provide a PKI user ID in the
first message unless we got one in an earlier transaction */
if( isServerInitialMessage && protocolInfo->userIDsize <= 0 )
{
retExt( status,
( status, errorInfo, "Missing user ID in PKI header" ) );
}
}
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPKID ) )
readUniversal( stream ); /* Recipient protection keyID */
/* Record the transaction ID or make sure that it matches the one that
we sent. There's no real need to do an explicit duplicate check
since a replay attempt will be rejected as a duplicate by the cert
store and the locking performed at that level makes it a much better
place to catch duplicates, but we do it anyway */
status = readConstructed( stream, NULL, CTAG_PH_TRANSACTIONID );
if( cryptStatusError( status ) )
{
retExt( status,
( status, errorInfo,
"Missing transaction ID in PKI header" ) );
}
if( isServerInitialMessage )
{
/* This is the first message and we're the server, record the
transaction ID for later */
status = readOctetString( stream, protocolInfo->transID,
&protocolInfo->transIDsize,
4, CRYPT_MAX_HASHSIZE );
}
else
{
/* Make sure that the transaction ID for this message matches the
recorded value (the bad recipient nonce/bad signature error code
is the best that we can provide here) */
status = readOctetString( stream, buffer, &length,
4, CRYPT_MAX_HASHSIZE );
if( cryptStatusOK( status ) && \
( protocolInfo->transIDsize < 4 || \
protocolInfo->transIDsize != length || \
memcmp( protocolInfo->transID, buffer, length ) ) )
{
protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
retExt( CRYPT_ERROR_SIGNATURE,
( CRYPT_ERROR_SIGNATURE, errorInfo,
"Returned message transaction ID doesn't match our "
"transaction ID" ) );
}
}
if( cryptStatusError( status ) )
{
retExt( status,
( status, errorInfo,
"Invalid transaction ID in PKI header" ) );
}
/* Read the sender nonce, which becomes the new recipient nonce, and skip
the recipient nonce if there's one present. These values may be
absent, either because the other side doesn't implement them or
because they're not available, for example because it's sending a
response to an error that occurred before it could read the nonce from
a request. In any case we don't bother checking the nonce values
since the transaction ID serves the same purpose */
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERNONCE ) )
{
readConstructed( stream, NULL, CTAG_PH_SENDERNONCE );
status = readOctetString( stream, protocolInfo->recipNonce,
&protocolInfo->recipNonceSize,
4, CRYPT_MAX_HASHSIZE );
if( cryptStatusError( status ) )
{
protocolInfo->pkiFailInfo = CMPFAILINFO_BADSENDERNONCE;
retExt( status,
( status, errorInfo,
"Invalid sender nonce in PKI header" ) );
}
}
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPNONCE ) )
{
readConstructed( stream, NULL, CTAG_PH_RECIPNONCE );
status = readUniversal( stream );
if( cryptStatusError( status ) )
{
protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
retExt( status,
( status, errorInfo,
"Invalid recipient nonce in PKI header" ) );
}
}
/* Remember that we've successfully read the header information, or at
least the information that we need to generate a response */
protocolInfo->headerRead = TRUE;
/* Generate a new sender nonce (unless this is the first message and
we're still setting things up) and see if there's anything useful
present in the general info */
if( protocolInfo->senderNonceSize > 0 )
{
MESSAGE_DATA msgData;
setMessageData( &msgData, protocolInfo->senderNonce,
protocolInfo->senderNonceSize );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
}
if( stell( stream ) < endPos && \
peekTag( stream ) == MAKE_CTAG( CTAG_PH_FREETEXT ) )
status = readUniversal( stream ); /* Junk */
if( stell( stream ) < endPos && \
peekTag( stream ) == MAKE_CTAG( CTAG_PH_GENERALINFO ) )
{
status = readGeneralInfo( stream, protocolInfo );
if( cryptStatusError( status ) )
retExt( status,
( status, errorInfo,
"Invalid generalInfo information in PKI header" ) );
}
if( stell( stream ) < endPos )
{
/* Skip any remaining junk */
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
}
Note that readPkiDatagram() has already performed an initial valid-ASN.1
check before we get here */
int readPkiMessage( SESSION_INFO *sessionInfoPtr,
CMP_PROTOCOL_INFO *protocolInfo,
int messageType )
{
ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
MESSAGE_CREATEOBJECT_INFO createInfo;
STREAM stream;
const BOOLEAN isServerInitialMessage = ( messageType == CRYPT_UNUSED );
BOOLEAN useMAC;
void *integrityInfoPtr = DUMMY_INIT_PTR;
int protPartStart, protPartSize, bodyStart = DUMMY_INIT;
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, SESSION_ERRINFO,
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 */
if( protocolInfo->isCryptlib && \
protocolInfo->userIDsize == 9 )
{
BYTE encodedUserID[ CRYPT_MAX_TEXTSIZE + 8 ];
int encodedUserIDLength;
status = encodePKIUserValue( encodedUserID, CRYPT_MAX_TEXTSIZE,
&encodedUserIDLength,
protocolInfo->userID,
protocolInfo->userIDsize, 3 );
if( cryptStatusOK( status ) )
status = updateSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME, encodedUserID,
encodedUserIDLength, CRYPT_MAX_TEXTSIZE,
ATTR_FLAG_ENCODEDVALUE );
}
else
{
status = updateSessionInfo( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME,
protocolInfo->userID,
protocolInfo->userIDsize,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -