📄 certchn.c
字号:
certUsed[ 0 ] = TRUE;
lastCertPos = 0;
/* Walk down the chain from the currently selected cert checking for
certs issued by it, until we can't go any further */
do
{
/* Try and find a cert issued by the current cert */
for( i = 0; i < certChainSize; i++ )
if( !certUsed[ i ] && \
isIssuer( &chainingInfo, &certChainInfo[ i ] ) )
{
/* There's another cert below the current one in the chain,
mark the current one as used and move on to the next
one */
getSubjectChainingInfo( &chainingInfo, &certChainInfo[ i ] );
certUsed[ i ] = TRUE;
lastCertPos = i;
break;
}
}
while( i != certChainSize );
return( lastCertPos );
}
/* Find a leaf node as identified by issuerAndSerialNumber. Returns the
position of the leaf node in the chain */
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;
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 == 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 );
issuerDNsize = ( int ) sizeofObject( length );
sSkip( &stream, length );
readTag( &stream );
serialNumberSize = readShortLength( &stream );
serialNumber = sMemBufPtr( &stream );
assert( sStatusOK( &stream ) );
sMemDisconnect( &stream );
while( !serialNumber[ 0 ] && serialNumberSize > 1 )
{
/* Strip any leading zeroes on non-zero serial numbers. This is
necessary because the cert-handling code canonicalises the
serial number value while the raw issuerAndSerialNumber can
contain any random encoding. so we need to canonicalise it as
well for a binary comparison to work */
serialNumber++;
serialNumberSize--;
}
/* Walk down the chain looking for the one identified by the
issuerAndSerialNumber */
for( i = 0; i < certChainSize; i++ )
if( certChainInfo[ i ].issuerDNsize == issuerDNsize && \
certChainInfo[ i ].serialNumberSize == serialNumberSize && \
!memcmp( certChainInfo[ i ].issuerDN, issuerDNptr,
issuerDNsize ) && \
!memcmp( certChainInfo[ i ].serialNumber, serialNumber,
serialNumberSize ) )
return( i );
return( CRYPT_ERROR_NOTFOUND );
}
/* 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 ];
int orderedChainIndex = 0, i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isWritePtrEx( iCertChain, CRYPT_CERTIFICATE, certChainSize ) );
assert( isWritePtrEx( certChainInfo, CERTCHAIN_INFO, certChainSize ) );
assert( isWritePtr( chainingInfo, CHAINING_INFO ) );
/* If we're canonicalising an existing chain, there's a predefined chain
start which we copy over and prepare to look for the next cert up the
chain */
if( certChainStart != CRYPT_UNUSED )
{
orderedChain[ orderedChainIndex++ ] = certChainStart;
getIssuerChainingInfo( chainingInfo, &certChainInfo[ 0 ] );
memset( &certChainInfo[ 0 ], 0, sizeof( CERTCHAIN_INFO ) );
}
/* 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( chainingInfo, &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( chainingInfo, &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 ], RESOURCE_IMESSAGE_DECREFCOUNT );
/* Replace the existing chain with the ordered version */
memset( iCertChain, 0, sizeof( CRYPT_CERTIFICATE ) * MAX_CHAINLENGTH );
if( orderedChainIndex )
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[ ... ] ], ie the chain for the new cert
contains the old cert and its attached cert chain */
int copyCertChain( CERT_INFO *certInfoPtr, const CRYPT_HANDLE certChain )
{
CRYPT_CERTIFICATE iChainCert;
CERT_INFO *chainCertInfoPtr;
CERTCHAIN_INFO certChainInfo[ MAX_CHAINLENGTH ];
int i, status;
assert( isWritePtr( certInfoPtr, CERT_INFO ) );
/* Extract the base certificate from the chain and copy it over. Note
that we pass in the cert chain handle for the copy since the internal
cert won't be visible */
status = krnlSendMessage( certChain, RESOURCE_MESSAGE_GETDEPENDENT,
&iChainCert, OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
return( status );
getCheckInternalResource( iChainCert, chainCertInfoPtr,
OBJECT_TYPE_CERTIFICATE );
krnlSendNotifier( iChainCert, RESOURCE_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++ )
{
certInfoPtr->certChain[ certInfoPtr->certChainEnd++ ] = \
chainCertInfoPtr->certChain[ i ];
krnlSendNotifier( chainCertInfoPtr->certChain[ i ],
RESOURCE_IMESSAGE_INCREFCOUNT );
}
unlockResource( chainCertInfoPtr );
/* 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 )
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( cryptStatusError( status ) )
return( 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 )
{
getCheckInternalResource( certInfoPtr->certChain[ certChainIndex ],
*certChainPtr, OBJECT_TYPE_CERTIFICATE );
return( CRYPT_OK );
}
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. There are three types of
constraints which can cover multiple certs: path constraints, name
constraints, and policy constraints.
Path constraints are easiest to check, just make sure 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 noone actually knows how to apply them. 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 )
{
ATTRIBUTE_LIST *attributeListPtr, *nameAttributeListPtr;
ATTRIBUTE_LIST *policyAttributeListPtr;
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -