📄 certchn.c
字号:
static int findIdentifiedLeafNode( const CERTCHAIN_INFO *certChainInfo,
const int certChainSize,
const CRYPT_KEYID_TYPE keyIDtype,
const void *keyID, const int keyIDlength )
{
STREAM stream;
const BYTE *serialNumber;
const void *issuerDNptr;
int issuerDNsize, serialNumberSize;
int length, i, status;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isReadPtrEx( certChainInfo, CERTCHAIN_INFO, certChainSize ) );
assert( keyIDtype == CRYPT_IKEYID_KEYID || \
keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
assert( keyID != NULL );
assert( keyIDlength > 16 );
/* If it's a subjectKeyIdentifier, walk down the chain looking for a
match */
if( keyIDtype == CRYPT_IKEYID_KEYID )
{
for( i = 0; i < certChainSize; i++ )
if( certChainInfo[ i ].subjectKeyIDsize > MIN_SKID_SIZE && \
certChainInfo[ i ].subjectKeyIDsize == keyIDlength && \
!memcmp( certChainInfo[ i ].subjectKeyIdentifier, keyID,
keyIDlength ) )
return( i );
return( CRYPT_ERROR_NOTFOUND );
}
/* It's an issuerAndSerialNumber, extract the issuer DN and serial
number */
sMemConnect( &stream, keyID, keyIDlength );
readSequence( &stream, NULL );
issuerDNptr = sMemBufPtr( &stream );
readSequence( &stream, &length ); /* Issuer DN */
issuerDNsize = ( int ) sizeofObject( length );
sSkip( &stream, length );
readGenericHole( &stream, &serialNumberSize, BER_INTEGER );
serialNumber = sMemBufPtr( &stream ); /* Serial number */
status = sSkip( &stream, serialNumberSize );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( CRYPT_ERROR_NOTFOUND );
/* Walk down the chain looking for the one identified by the
issuerAndSerialNumber */
for( i = 0; i < certChainSize; i++ )
if( certChainInfo[ i ].issuerDNsize > 0 && \
certChainInfo[ i ].issuerDNsize == issuerDNsize && \
!memcmp( certChainInfo[ i ].issuerDN, issuerDNptr,
issuerDNsize ) && \
!compareSerialNumber( certChainInfo[ i ].serialNumber,
certChainInfo[ i ].serialNumberSize,
serialNumber, serialNumberSize ) )
return( i );
return( CRYPT_ERROR_NOTFOUND );
}
/* Determine whether a cert is present in a cert collection based on its
fingerprint */
static BOOLEAN certPresent( BYTE certChainHashes[][ CRYPT_MAX_HASHSIZE ],
const int certChainLen,
const CRYPT_CERTIFICATE iCryptCert )
{
RESOURCE_DATA msgData;
int i, status;
/* Get the fingerprint of the (potential) next cert in the collection */
setMessageData( &msgData, certChainHashes[ certChainLen ],
CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( iCryptCert, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CERTINFO_FINGERPRINT );
if( cryptStatusError( status ) )
return( status );
/* Make sure that it isn't already present in the collection */
for( i = 0; i < certChainLen; i++ )
if( !memcmp( certChainHashes[ i ],
certChainHashes[ certChainLen ], msgData.length ) )
return( TRUE );
return( FALSE );
}
/* Sort the issuer certs in a cert chain, discarding any unnecessary certs.
If we're canonicalising an existing chain then the start point in the
chain is given by certChainStart and the -1th cert is the end user cert
and isn't part of the ordering process. If we're building a new chain
from an arbitrary set of certs then the start point is given by the
chaining info for the leaf cert. Returns the length of the ordered
chain */
static int sortCertChain( CRYPT_CERTIFICATE *iCertChain,
CERTCHAIN_INFO *certChainInfo,
const int certChainSize,
const CRYPT_CERTIFICATE certChainStart,
CHAINING_INFO *chainingInfo )
{
CRYPT_CERTIFICATE orderedChain[ MAX_CHAINLENGTH ];
CHAINING_INFO localChainingInfo, *chainingInfoPtr = &localChainingInfo;
int orderedChainIndex = 0, i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isWritePtrEx( iCertChain, CRYPT_CERTIFICATE, certChainSize ) );
assert( isWritePtrEx( certChainInfo, CERTCHAIN_INFO, certChainSize ) );
assert( ( checkHandleRange( certChainStart ) && \
chainingInfo == NULL ) || \
( certChainStart == CRYPT_UNUSED && \
isWritePtr( chainingInfo, CHAINING_INFO ) ) );
/* If we're canonicalising an existing chain, there's a predefined chain
start that we copy over and prepare to look for the next cert up the
chain */
if( certChainStart != CRYPT_UNUSED )
{
orderedChain[ orderedChainIndex++ ] = certChainStart;
getIssuerChainingInfo( chainingInfoPtr, &certChainInfo[ 0 ] );
memset( &certChainInfo[ 0 ], 0, sizeof( CERTCHAIN_INFO ) );
}
else
/* We're building a new chain, the caller has supplied the chaining
info */
chainingInfoPtr = chainingInfo;
/* Build an ordered chain of certs from the leaf to the root */
do
{
/* Find the cert with the current issuer as its subject */
for( i = 0; i < certChainSize; i++ )
if( isSubject( chainingInfoPtr, &certChainInfo[ i ] ) )
{
/* We've found the issuer, move the certs to the ordered
chain and prepare to find the issuer of this cert */
orderedChain[ orderedChainIndex++ ] = iCertChain[ i ];
getIssuerChainingInfo( chainingInfoPtr, &certChainInfo[ i ] );
memset( &certChainInfo[ i ], 0, sizeof( CERTCHAIN_INFO ) );
break;
}
}
while( i != certChainSize );
/* If there are any certs left, they're not needed for anything so we can
free the resources */
for( i = 0; i < certChainSize; i++ )
if( certChainInfo[ i ].subjectDN != NULL )
krnlSendNotifier( iCertChain[ i ], IMESSAGE_DECREFCOUNT );
/* Replace the existing chain with the ordered version */
memset( iCertChain, 0, sizeof( CRYPT_CERTIFICATE ) * MAX_CHAINLENGTH );
if( orderedChainIndex > 0 )
memcpy( iCertChain, orderedChain,
sizeof( CRYPT_CERTIFICATE ) * orderedChainIndex );
return( orderedChainIndex );
}
/* Copy a cert chain into a certificate object and canonicalise the chain by
ordering the certs in a cert chain from the leaf cert up to the root.
This function is used when signing a cert with a cert chain, and takes as
input ( oldCert, oldCert.chain[ ... ] ) and produces as output ( newCert,
chain[ oldCert, oldCert.chain[ ... ] ], i.e.the chain for the new cert
contains the old cert and its attached cert chain */
int copyCertChain( CERT_INFO *certInfoPtr, const CRYPT_HANDLE certChain,
const BOOLEAN isCertCollection )
{
CRYPT_CERTIFICATE iChainCert;
CERT_INFO *chainCertInfoPtr;
CERTCHAIN_INFO certChainInfo[ MAX_CHAINLENGTH ];
BYTE certChainHashes[ MAX_CHAINLENGTH + 1 ][ CRYPT_MAX_HASHSIZE ];
int i, status;
assert( isWritePtr( certInfoPtr, CERT_INFO ) );
status = krnlSendMessage( certChain, IMESSAGE_GETDEPENDENT, &iChainCert,
OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
return( status );
/* If we're building a cert collection, all we need to ensure is non-
duplicate certs rather than a strict chain. To handle duplicate-
checking, we build a list of the fingerprints for each cert in the
chain */
if( isCertCollection )
{
for( i = 0; i < certInfoPtr->certChainEnd; i++ )
{
RESOURCE_DATA msgData;
setMessageData( &msgData, certChainHashes[ i ],
CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( certInfoPtr->certChain[ i ],
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_FINGERPRINT );
if( cryptStatusError( status ) )
return( status );
}
}
/* Extract the base certificate from the chain and copy it over */
status = krnlGetObject( iChainCert, OBJECT_TYPE_CERTIFICATE,
( void ** ) &chainCertInfoPtr,
CRYPT_ERROR_SIGNALLED );
if( cryptStatusError( status ) )
return( status );
if( !isCertCollection || \
!certPresent( certChainHashes, certInfoPtr->certChainEnd,
iChainCert ) )
{
krnlSendNotifier( iChainCert, IMESSAGE_INCREFCOUNT );
certInfoPtr->certChain[ certInfoPtr->certChainEnd++ ] = iChainCert;
}
/* Copy the rest of the chain. Because we're about to canonicalise it
(which reorders the certs and deletes unused ones) we copy individual
certs over rather than copying only the base cert and relying on the
chain held in that */
for( i = 0; i < chainCertInfoPtr->certChainEnd; i++ )
if( !isCertCollection || \
!certPresent( certChainHashes, certInfoPtr->certChainEnd,
chainCertInfoPtr->certChain[ i ] ) )
{
certInfoPtr->certChain[ certInfoPtr->certChainEnd++ ] = \
chainCertInfoPtr->certChain[ i ];
krnlSendNotifier( chainCertInfoPtr->certChain[ i ],
IMESSAGE_INCREFCOUNT );
}
krnlReleaseObject( chainCertInfoPtr->objectHandle );
/* If we're building an unordered cert collection, mark the cert chain
object as a cert collection only and exit */
if( isCertCollection )
{
certInfoPtr->flags |= CERT_FLAG_CERTCOLLECTION;
return( CRYPT_OK );
}
/* If the chain being attached consists of a single cert (which occurs
when we're building a new chain by signing a cert with a CA cert), we
don't have to bother doing anything else */
if( chainCertInfoPtr->certChainEnd <= 0 )
return( CRYPT_OK );
/* Extract the chaining info from each certificate and use it to sort the
chain. Since we know what the leaf cert is and since chaining info
such as the encoded DN data in the certinfo structure may not have been
set up yet if it contains an unsigned cert, we feed in the leaf cert
and omit the chaining info */
status = buildCertChainInfo( certChainInfo, certInfoPtr->certChain,
certInfoPtr->certChainEnd );
if( cryptStatusOK( status ) )
status = sortCertChain( certInfoPtr->certChain, certChainInfo,
certInfoPtr->certChainEnd, iChainCert,
NULL );
if( cryptStatusError( status ) )
return( status );
certInfoPtr->certChainEnd = status;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Verify a Certificate Chain *
* *
****************************************************************************/
/* Get the next certificate down the chain. Returns OK_SPECIAL if there are
no more certs present */
static int getNextCert( const CERT_INFO *certInfoPtr,
CERT_INFO **certChainPtr, const int certChainIndex )
{
assert( isReadPtr( certInfoPtr, CERT_INFO ) );
if( certChainIndex >= 0 )
return( krnlGetObject( certInfoPtr->certChain[ certChainIndex ],
OBJECT_TYPE_CERTIFICATE,
( void ** ) certChainPtr,
CRYPT_ERROR_SIGNALLED ) );
if( certChainIndex == -1 )
{
/* The -1th cert is the leaf itself */
*certChainPtr = ( CERT_INFO * ) certInfoPtr;
return( CRYPT_OK );
}
/* We've reached the end of the chain, return a special status value to
indicate this */
*certChainPtr = NULL;
return( OK_SPECIAL );
}
/* Check constraints along a cert chain, checked if complianceLevel >=
CRYPT_COMPLIANCELEVEL_PKIX_FULL. There are three types of constraints
that can cover multiple certs: path constraints, name constraints, and
policy constraints.
Path constraints are easiest to check, just make sure that the number of
certs from the issuer to the leaf is less than the constraint length.
Name constraints are a bit more difficult, the abstract description
requires building and maintaining a (potentially enormous) name constraint
tree which is applied to each cert in turn as it is processed, however
since name constraints are practically nonexistant and chains are short
it's more efficient to walk down the cert chain when a constraint is
encountered and check each cert in turn, which avoids having to maintain
massive amounts of state information and is no less efficient than a
single monolithic state comparison.
Policy constraints are hardest of all because, with the complex mishmash
of policies, policy constraints, qualifiers, and mappings it turns out
that no-one actually knows how to apply them, and even if people could
agree, with the de facto use of the policy extension as the kitchenSink
extension it's uncertain how to apply the constraints to typical
kitchenSink constructs. The ambiguity of name constraints when applied
to altNames is bad enough, with a 50/50 split in PKIX about whether it
should be an AND or OR operation, and whether a DN constraint applies to
a subjectName or altName or both (the latter was fixed in the final
version of RFC 2459, although how many implementations follow exactly
this version rather than the dozen earlier drafts or any other profile is
unknown). With policy constraints it's even worse and no-one seems to be
able to agree on what to do with them. For this reason we should leave
this particular rathole for someone else to fall into, but to claim
buzzword-compliance to PKIX we need to implement this checking (although
we don't handle the weirder constraints on policies, which have never
been seen in the wild, yet). Massa make big magic, gunga din */
static int checkConstraints( CERT_INFO *certInfoPtr,
const CERT_INFO *issuerCertInfoPtr,
int *subjectCertIndex )
{
const ATTRIBUTE_LIST *nameAttributeListPtr, *policyAttributeListPtr;
const ATTRIBUTE_LIST *attributeListPtr;
BOOLEAN hasExcludedSubtrees, hasPermittedSubtrees, hasPolicy;
BOOLEAN requireExplicitPolicyPresent = FALSE;
int requireExplicitPolicyLevel = CRYPT_ERROR;
int certIndex = *subjectCertIndex, status = CRYPT_OK;
assert( isWritePtr( certInfoPtr, CERT_INFO ) );
assert( isReadPtr( issuerCertInfoPtr, CERT_INFO ) );
/* If there's a path length constraint present, check that it's
satisfied: The number of certs from the issuer (at subjectCertIndex
+ 1) to the end entity (at -1) must be less than the length
constraint, i.e. the subjectCertIndex must be greater than the
length */
attributeListPtr = findAttributeField( issuerCertInfoPtr->attributes,
CRYPT_CERTINFO_PATHLENCONSTRAINT,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL && \
!( issuerCertInfoPtr->flags & CERT_FLAG_SELFSIGNED ) && \
attributeListPtr->intValue <= certIndex )
{
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_PATHLENCONSTRAINT,
CRYPT_ERRTYPE_ISSUERCONSTRAINT );
return( CRYPT_ERROR_INVALID );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -