📄 cmp_rd.c
字号:
/* Read the client's returned confirmation information */
status = readSequence( stream, &length );
if( cryptStatusOK( status ) && length <= 0 )
{
/* Missing certStatus, the client has rejected the cert. This isn't
an explicit error since it's a valid protocol outcome, so we
return an OK status but set the overall protocol status to a
generic error value to indicate that we don't want to continue
normally */
protocolInfo->status = CRYPT_ERROR;
return( CRYPT_OK );
}
readSequence( stream, NULL );
status = readOctetString( stream, certHash, &length,
CRYPT_MAX_HASHSIZE );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, status, "Invalid cert confirmation" );
/* Get the local cert hash and compare it to the client's one. Since
we're the server, this is a cryptlib-issued cert so we know that
it'll always use SHA-1 */
setMessageData( &msgData, certHash, length );
status = krnlSendMessage( sessionInfoPtr->iCertResponse,
IMESSAGE_COMPARE, &msgData,
MESSAGE_COMPARE_FINGERPRINT );
if( cryptStatusError( status ) )
{
/* The user is confirming an unknown cert, the best that we can do
is return a generic cert-mismatch error */
protocolInfo->pkiFailInfo = CMPFAILINFO_BADCERTID;
retExt( sessionInfoPtr, CRYPT_ERROR_NOTFOUND,
"Returned cert hash doesn't match issued certificate" );
}
return( CRYPT_OK );
}
/* Read genMsg body */
static int readGenMsgBody( STREAM *stream, SESSION_INFO *sessionInfoPtr,
const BOOLEAN isRequest )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
void *bodyInfoPtr;
int bodyLength, status;
status = readSequence( stream, &bodyLength );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid genMsg header" );
if( !isRequest )
/* The InfoTypeAndValue is handled as CMS content (see the comment
for writeGenMsgBody()) so we remember where the CMS payload
starts if it's a PKIBoot response */
bodyInfoPtr = sMemBufPtr( stream );
if( bodyLength < 8 || bodyLength > sMemDataLeft( stream ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid genMsg length %d", bodyLength );
readSequence( stream, NULL );
status = readOID( stream, isRequest ? OID_PKIBOOT : OID_CMS_SIGNEDDATA );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, CRYPT_ERROR_NOTAVAIL,
"Invalid genMsg type, expected PKIBoot request or "
"response" );
/* If it's a PKIBoot request, we're done */
if( isRequest )
return( CRYPT_OK );
/* It's a PKIBoot response, import the cert trust list. Since this
isn't a true cert chain and isn't used as such, we use data-only
certs (specified using the special-case CERTFORMAT_CTL format
specifier) */
setMessageCreateObjectIndirectInfo( &createInfo, bodyInfoPtr,
bodyLength, CERTFORMAT_CTL );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
&createInfo, OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, status, "Invalid PKIBoot response" );
sessionInfoPtr->iCertResponse = createInfo.cryptHandle;
return( CRYPT_OK );
}
/* Read error body */
static int readErrorBody( STREAM *stream, SESSION_INFO *sessionInfoPtr )
{
int endPos, length, status;
/* Read the outer wrapper and PKI status info. An error return status
is valid when we read the status info since we're reading an error
status and converting it into a cryptlib status, so we don't exit
unless it's a problem with the status info itself */
readConstructed( stream, NULL, CTAG_PB_ERROR );
readSequence( stream, &length ); /* Outer wrapper */
endPos = stell( stream ) + length;
status = readPkiStatusInfo( stream, &sessionInfoPtr->errorCode,
sessionInfoPtr->errorMessage );
if( status == CRYPT_ERROR_BADDATA || status == CRYPT_ERROR_UNDERFLOW )
return( status );
/* In addition to the PKI status info there can be another layer of
error information wrapped around it which is exactly the same only
different, so if we haven't got anything from the status info we
check to see whether this layer can give us anything */
if( stell( stream ) < endPos && peekTag( stream ) == BER_INTEGER )
{
/* If there's an error code present and we haven't already set the
error code from the pkiStatusInfo, set it now */
if( !sessionInfoPtr->errorCode )
{
long value;
status = readShortInteger( stream, &value );
if( cryptStatusOK( status ) )
sessionInfoPtr->errorCode = ( int ) value;
}
else
readUniversal( stream );
}
if( stell( stream ) < endPos && peekTag( stream ) == BER_SEQUENCE && \
!*sessionInfoPtr->errorMessage )
/* Read the error text, ignoring any possible error status since the
overall error code from the status info is more meaningful than
a data format problem in trying to read the error text */
readFreeText( stream, sessionInfoPtr->errorMessage,
MAX_ERRMSG_SIZE - 1 );
return( status );
}
/****************************************************************************
* *
* Read a PKI Header *
* *
****************************************************************************/
/* Read a PKI header and make sure that it matches the header that we sent
(for EE or non-initial CA/RA messages) or set up the EE information in
response to an initial message (for an initial CA/RA message). We ignore
all the redundant fields in the header that don't directly affect the
protocol, based on the results of CMP interop testing this appears to be
standard practice among implementors. This also helps get around problems
with implementations that get the fields wrong, since most of the fields
aren't generally useful it doesn't affect the processing while making the
code more tolerant of implementation errors:
header SEQUENCE {
version INTEGER (2),
dummy [4] EXPLICIT DirectoryName, -- Ignored
senderDN [4] EXPLICIT DirectoryName, -- Copied if non-clib
protAlgo [1] EXPLICIT AlgorithmIdentifier,
protKeyID [2] EXPLICIT OCTET STRING, -- Copied if changed
dummy [3] EXPLICIT OCTET STRING, -- Ignored
transID [4] EXPLICIT OCTET STRING,
nonce [5] EXPLICIT OCTET STRING, -- Copied if non-clib
dummy [6] EXPLICIT OCTET STRING, -- Ignored
dummy [7] SEQUENCE OF UTF8String, -- Ignored
generalInfo [8] EXPLICIT SEQUENCE OF Info OPT -- cryptlib-specific info
} */
static int readPkiHeader( STREAM *stream, CMP_PROTOCOL_INFO *protocolInfo,
void *errorInfo,
const BOOLEAN isServerInitialMessage )
{
CRYPT_ALGO_TYPE cryptAlgo, hashAlgo;
BYTE buffer[ CRYPT_MAX_HASHSIZE ];
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;
/* 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 );
protocolInfo->senderDNPtr = sMemBufPtr( stream );
if( cryptStatusOK( status ) && protocolInfo->senderDNlength > 0 )
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 */
readUniversal( stream ); /* Sender DN */
status = readUniversal( stream ); /* Recipient */
if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_MESSAGETIME ) )
status = readUniversal( stream ); /* Message time */
if( cryptStatusError( status ) )
retExt( errorInfo, CRYPT_ERROR_BADDATA, "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( errorInfo, CRYPT_ERROR_SIGNATURE,
"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( errorInfo, status,
"Invalid integrity protection info in PKI header" );
streamPos = stell( stream );
status = readAlgoIDex( stream, &cryptAlgo, &hashAlgo, NULL );
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 ];
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,
CRYPT_MAX_HASHSIZE );
if( cryptStatusError( status ) )
retExt( errorInfo, status,
"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( errorInfo, status, "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( errorInfo, status, "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,
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,
CRYPT_MAX_HASHSIZE );
if( cryptStatusOK( status ) && \
( protocolInfo->transIDsize < 4 || \
protocolInfo->transIDsize != length || \
memcmp( protocolInfo->transID, buffer, length ) ) )
{
protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
retExt( errorInfo, CRYPT_ERROR_SIGNATURE,
"Returned message transaction ID doesn't match our "
"transaction ID" );
}
}
if( cryptStatusError( status ) )
retExt( errorInfo, status, "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,
CRYPT_MAX_HASHSIZE );
if( cryptStatusError( status ) )
{
protocolInfo->pkiFailInfo = CMPFAILINFO_BADSENDERNONCE;
retExt( errorInfo, status,
"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( errorInfo, status,
"Invalid recipient nonce in PKI header" );
}
}
/* 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 )
{
RESOURCE_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( errorInfo, status,
"Invalid generalInfo information in PKI header" );
}
if( stell( stream ) < endPos )
/* Skip any remaining junk */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -