📄 chk_chn.c
字号:
/****************************************************************************
* *
* Certificate Chain Checking Routines *
* Copyright Peter Gutmann 1996-2004 *
* *
****************************************************************************/
/* This module and chk_cert.c implement the following PKIX checks (* =
unhandled, see the code comments. Currently only policy mapping is
unhandled, this is optional in PKIX and given the nature of the
kitchenSink extension no-one really knows how to apply it anyway). For
simplicity we use the more compact form of RFC 2459 rather than the 18
page long one from RFC 3280.
General:
(a) Verify the basic certificate information:
(1) The certificate signature is valid.
(2a) The certificate has not expired.
(2b) If present, the private key usage period is satisfied.
(3) The certificate has not been revoked.
(4a) The subject and issuer name chains correctly.
(4b) If present, the subjectAltName and issuerAltName chains
correctly.
NameConstraints:
(b) Verify that the subject name or critical subjectAltName is consistent
with the constrained subtrees.
(c) Verify that the subject name or critical subjectAltName is consistent
with the excluded subtrees.
Policy Constraints:
(d) Verify that policy info.is consistent with the initial policy set:
(1) If the require explicit policy state variable is less than or
equal to n, a policy identifier in the certificate must be in
the initial policy set.
* (2) If the policy mapping state variable is less than or equal to n,
the policy identifier may not be mapped.
(3) RFC 3280 addition: If the inhibitAnyPolicy state variable is
less than or equal to n, the anyPolicy policy is no longer
considered a match (this also extends into (e) and (g) below).
(e) Verify that policy info.is consistent with the acceptable policy set:
(1) If the policies extension is marked critical, the policies
extension must lie within the acceptable policy set.
(2) The acceptable policy set is assigned the resulting intersection
as its new value.
(g) Verify that the intersection of the acceptable policy set and the
initial policy set is non-null (this is covered by chaining of e(1)).
Other Constraints:
(f) Step (f) is missing in the original, it should probably be: Verify
that the current path length is less than the path length constraint.
If a path length constraint is present in the certificate, update it
as for policy constraints in (l). RFC 3280 addition: If the cert is
a PKIX path kludge cert, it doesn't count for path length constraint
purposes.
(h) Recognize and process any other critical extension present in the
certificate.
(i) Verify that the certificate is a CA certificate.
Update of state:
(j) If permittedSubtrees is present in the certificate, set the
constrained subtrees state variable to the intersection of its
previous value and the value indicated in the extension field.
(k) If excludedSubtrees is present in the certificate, set the excluded
subtrees state variable to the union of its previous value and the
value indicated in the extension field.
(l) If a policy constraints extension is included in the certificate,
modify the explicit policy and policy mapping state variables as
follows:
For any of { requireExplicitPolicy, inhibitPolicyMapping,
inhibitAnyPolicy }, if the constraint value is present and has value
r, the state variable is set to the minimum of (a) its current value
and (b) the sum of r and n (the current certificate in the
sequence)
(m) If a key usage extension is marked critical, ensure that the
keyCertSign bit is set */
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "cert.h"
#else
#include "cert/cert.h"
#endif /* Compiler-specific includes */
/* Prototypes for functions in sign.c */
int checkX509signature( const void *signedObject, const int signedObjectLength,
void **object, int *objectLength,
const CRYPT_CONTEXT sigCheckContext,
const int formatInfo );
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Get certificate information for a cert in the chain */
static int getCertInfo( const CERT_INFO *certInfoPtr,
CERT_INFO **certChainPtr, const int certChainIndex )
{
assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
assert( certChainIndex >= -2 && \
certChainIndex < certInfoPtr->cCertCert->chainEnd );
/* If it's an index into the cert chain, return info for the cert at
that position */
if( certChainIndex >= 0 && \
certChainIndex < certInfoPtr->cCertCert->chainEnd )
return( krnlAcquireObject( certInfoPtr->cCertCert->chain[ certChainIndex ],
OBJECT_TYPE_CERTIFICATE,
( void ** ) certChainPtr,
CRYPT_ERROR_SIGNALLED ) );
/* The -1th cert is the leaf itself */
if( certChainIndex == -1 )
{
*certChainPtr = ( CERT_INFO * ) certInfoPtr;
return( CRYPT_OK );
}
/* We've reached the end of the chain */
*certChainPtr = NULL;
return( CRYPT_ERROR_NOTFOUND );
}
/* Find the trust anchor in a cert chain. The definition of a "trusted
cert" is somewhat ambiguous and can have at least two different
interpretations:
1. Trust the identified cert in the chain and only verify from there on
down.
2. Trust the root of the chain that contains the identified cert (for
the purposes of verifying that particular chain only) and verify the
whole chain.
Situation 1 is useful where there's a requirement that things go up to an
external CA somewhere but no-one particularly cares about (or trusts) the
external CA. This is probably the most common situation in general PKC
usage, in which the external CA requirement is more of an inconvenience
than anything else. In this case the end user can choose to trust the
path at the point where it comes under their control (a local CA or
directly trusting the leaf certs) without having to bother about the
external CA.
Situation 2 is useful where there's a requirement to use the full PKI
model. This can be enabled by having the user mark the root CA as
trusted, although this means that all certs issued by that CA also have
to be trusted, removing user control over certificate use. This is
required by orthodox PKI theology, followed by all manner of hacks and
kludges down the chain to limit what can actually be done with the
cert(s) */
static int findTrustAnchor( CERT_INFO *certInfoPtr, int *trustAnchorIndexPtr,
CRYPT_CERTIFICATE *trustAnchorCertPtr )
{
CRYPT_CERTIFICATE iIssuerCert;
CERT_CERT_INFO *certChainInfo = certInfoPtr->cCertCert;
SELECTION_STATE savedState;
int trustAnchorIndex = 0, status;
/* Clear return value */
*trustAnchorIndexPtr = CRYPT_ERROR;
*trustAnchorCertPtr = CRYPT_ERROR;
/* If the leaf cert is implicitly trusted, exit. To perform this check
we have to explicitly select the leaf cert by making it appear that
the cert chain is empty. This is required in order to ensure that we
check the leaf rather than the currently-selected cert */
saveSelectionState( savedState, certInfoPtr );
certChainInfo->chainPos = CRYPT_ERROR;
status = krnlSendMessage( certInfoPtr->ownerHandle, IMESSAGE_SETATTRIBUTE,
&certInfoPtr->objectHandle,
CRYPT_IATTRIBUTE_CERT_CHECKTRUST );
restoreSelectionState( savedState, certInfoPtr );
if( cryptStatusOK( status ) )
/* Indicate that the leaf is trusted and there's nothing further to
do */
return( OK_SPECIAL );
/* Walk up the chain looking for a trusted cert. Note that the
evaluated trust anchor cert position is one past the current cert
position, since we're looking for the issuer of the current cert at
position n, which will be located at position n+1. This means that
it may end up pointing past the end of the chain if the trust anchor
is present in the trust database but not in the chain */
iIssuerCert = certInfoPtr->objectHandle;
status = krnlSendMessage( certInfoPtr->ownerHandle, IMESSAGE_SETATTRIBUTE,
&iIssuerCert, CRYPT_IATTRIBUTE_CERT_TRUSTEDISSUER );
while( cryptStatusError( status ) && \
trustAnchorIndex < certChainInfo->chainEnd )
{
iIssuerCert = certChainInfo->chain[ trustAnchorIndex++ ];
status = krnlSendMessage( certInfoPtr->ownerHandle,
IMESSAGE_SETATTRIBUTE, &iIssuerCert,
CRYPT_IATTRIBUTE_CERT_TRUSTEDISSUER );
}
if( cryptStatusError( status ) || \
trustAnchorIndex > certChainInfo->chainEnd )
return( CRYPT_ERROR_NOTFOUND );
*trustAnchorCertPtr = iIssuerCert;
*trustAnchorIndexPtr = trustAnchorIndex;
/* If there are more certs in the chain beyond the one that we stopped
at, check to see whether the next cert is the same as the trust
anchor. If it is, we use the copy of the cert in the chain rather
than the external one from the trust database */
if( trustAnchorIndex < certChainInfo->chainEnd - 1 )
{
status = krnlSendMessage( certChainInfo->chain[ trustAnchorIndex ],
IMESSAGE_COMPARE, &iIssuerCert,
MESSAGE_COMPARE_CERTOBJ );
if( cryptStatusOK( status ) )
*trustAnchorCertPtr = certChainInfo->chain[ trustAnchorIndex ];
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* Verify a Certificate Chain *
* *
****************************************************************************/
/* Check constraints along a cert chain in certInfoPtr from startCertIndex
on down, 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 the easiest to check, just make sure that the number
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -