📄 chk_chn.c
字号:
Path constraints are the easiest to check, just make sure that the number
of certificates from the issuer to the leaf is less than the constraint
length with special handling for PKIX path kludge certificates.
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 certificate in turn as it's processed,
however since name constraints are practically nonexistant and chains are
short it's more efficient to walk down the certificate chain when a
constraint is encountered and check each certificate in turn, which
avoids having to maintain massive amounts of state information and is no
less efficient than a single monolithic state comparison. Again there's
special handling for PKIX path kludge certificates, see chk_cert.c for
details.
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. In the absence of any consensus on the
issue the latter was fixed in the final version of RFC 2459 by somewhat
arbitrarily requiring an AND rather than an OR although how many
implementations follow exactly this version rather than the dozen earlier
drafts or any other profile or interpretation is unknown. With policy
constraints it's even worse and no-one seems to be able to agree on what
to do with them (or more specifically the people who write the standards
don't seem to be aware that there are ambiguities and inconsistencies in
the handling of these extensions. Anyone who doesn't believe this is
invited to try implementing the path-processing algorithm in RFC 3280 as
described by the pseudocode there).
For example the various policy constraints in effect act as conditional
modifiers on the critical flag of the policies extension and/or the
various blah-policy-set settings in the path-processing algorithm so
that under various conditions imposed by the constraints the extension
goes from being non-critical to being (effectively) critical. In addition
the constraint extensions can have their own critical flags which means
that we can end up having to chain back through multiple layers of
interacting constraint extensions spread across multiple certificates to
see what the current interpretation of a particular extension is.
Finally, the presence of PKIX path-kludge certificates can turn
enforcement of constraints on and off at various stages of path
processing with extra special cases containing exceptions to the
exceptions. In addition the path-kludge exceptions apply to some
constraint types but not to others although the main body of the spec
and the pseudocode path-processing algorithm disagree on which ones and
when they're in effect (this implementation assumes that the body of the
spec is authoritative and the pseudocode represents a buggy attempt to
implement the spec rather than the other way round). Since the
virtual-criticality can switch itself on and off across certificates
depending on where in the path they are, the handling of policy
constraints is reduced to a complete chaos if we try and interpret them
as required by the spec - trying to implement the logic using decision
tables ends up with expressions of more than a dozen variables, which
indicates that the issue is more or less incomprehensible. However
since it's only applied at the CRYPT_COMPLIANCELEVEL_PKIX_FULL compliance
level it's reasonably safe since users should be expecting peculiar
behaviour at this level anyway.
The requireExplicitPolicy constraint is particularly bizarre, it
specifies the number of additional certificates that can be present in
the path before the entire path needs to have policies present. In other
words unlike all other length-based constraints (pathLenConstraint,
inhibitPolicyMapping, inhibitAnyPolicy) this works both forwards and
*backwards* up and down the path, making it the PKI equivalent of a COME
FROM in that at some random point down the path a constraint placed who
knows where can suddenly retroactively render the previously-valid path
invalid. No-one seems to know why it runs backwards or what the purpose
of the retroactive triggering after n certificates is, for now we only check
forwards down the path in the manner of all the other length-based
constraints.
Massa make big magic, gunga din */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
static int checkConstraints( INOUT CERT_INFO *certInfoPtr,
IN_RANGE( -1, MAX_CHAINLENGTH ) \
const int startCertIndex,
const CERT_INFO *issuerCertInfoPtr,
OUT_RANGE( -1, MAX_CHAINLENGTH ) \
int *errorCertIndex,
const BOOLEAN explicitPolicy )
{
const ATTRIBUTE_LIST *nameConstraintPtr = NULL, *policyConstraintPtr = NULL;
const ATTRIBUTE_LIST *inhibitPolicyPtr = NULL, *attributeListPtr;
ATTRIBUTE_LIST pathAttributeList;
BOOLEAN hasExcludedSubtrees = FALSE, hasPermittedSubtrees = FALSE;
BOOLEAN hasPolicy = FALSE, hasPathLength = FALSE;
BOOLEAN hasExplicitPolicy = FALSE, hasInhibitPolicyMap = FALSE;
BOOLEAN hasInhibitAnyPolicy = FALSE;
int requireExplicitPolicyLevel, inhibitPolicyMapLevel;
int inhibitAnyPolicyLevel;
int certIndex = startCertIndex, iterationCount, status = CRYPT_OK;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
assert( isReadPtr( issuerCertInfoPtr, sizeof( CERT_INFO ) ) );
assert( isWritePtr( errorCertIndex, sizeof( int ) ) );
REQUIRES( startCertIndex >= -1 && startCertIndex < MAX_CHAINLENGTH );
REQUIRES( certInfoPtr != issuerCertInfoPtr );
/* Clear return value */
*errorCertIndex = CRYPT_ERROR;
/* Check for path constraints */
memset( &pathAttributeList, 0, sizeof( ATTRIBUTE_LIST ) );
attributeListPtr = findAttributeField( issuerCertInfoPtr->attributes,
CRYPT_CERTINFO_PATHLENCONSTRAINT,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL )
{
pathAttributeList.intValue = attributeListPtr->intValue;
hasPathLength = TRUE;
}
/* Check for policy constraints */
if( explicitPolicy && \
checkAttributePresent( issuerCertInfoPtr->attributes,
CRYPT_CERTINFO_CERTIFICATEPOLICIES ) )
{
/* Policy chaining purely from the presence of a policy extension
is only enforced if the explicit-policy option is set */
hasPolicy = TRUE;
}
attributeListPtr = findAttribute( issuerCertInfoPtr->attributes, \
CRYPT_CERTINFO_POLICYCONSTRAINTS, FALSE );
if( attributeListPtr != NULL )
policyConstraintPtr = attributeListPtr;
attributeListPtr = findAttribute( issuerCertInfoPtr->attributes, \
CRYPT_CERTINFO_INHIBITANYPOLICY, TRUE );
if( attributeListPtr != NULL )
inhibitPolicyPtr = attributeListPtr;
/* Check for name constraints */
attributeListPtr = findAttribute( issuerCertInfoPtr->attributes, \
CRYPT_CERTINFO_NAMECONSTRAINTS, FALSE );
if( attributeListPtr != NULL )
{
nameConstraintPtr = attributeListPtr;
hasExcludedSubtrees = findAttributeField( nameConstraintPtr, \
CRYPT_CERTINFO_EXCLUDEDSUBTREES,
CRYPT_ATTRIBUTE_NONE ) != NULL;
hasPermittedSubtrees = findAttributeField( nameConstraintPtr, \
CRYPT_CERTINFO_PERMITTEDSUBTREES,
CRYPT_ATTRIBUTE_NONE ) != NULL;
}
/* If there aren't any critical policies or constraints present (the
most common case), we're done */
if( !hasPolicy && !hasPathLength && \
policyConstraintPtr == NULL && inhibitPolicyPtr == NULL && \
nameConstraintPtr == NULL )
return( CRYPT_OK );
/* Check whether there are requireExplicitPolicy, inhibitPolicyMapping, or
inhibitAnyPolicy attributes, which act as conditional modifiers on the
criticality and contents of the policies extension */
requireExplicitPolicyLevel = inhibitPolicyMapLevel = inhibitAnyPolicyLevel = 0;
attributeListPtr = findAttributeField( policyConstraintPtr,
CRYPT_CERTINFO_REQUIREEXPLICITPOLICY,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL )
{
requireExplicitPolicyLevel = attributeListPtr->intValue;
hasExplicitPolicy = TRUE;
}
attributeListPtr = findAttributeField( policyConstraintPtr,
CRYPT_CERTINFO_INHIBITPOLICYMAPPING,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL )
{
inhibitPolicyMapLevel = attributeListPtr->intValue;
hasInhibitPolicyMap = TRUE;
}
if( inhibitPolicyPtr != NULL )
{
inhibitAnyPolicyLevel = inhibitPolicyPtr->intValue;
hasInhibitAnyPolicy = TRUE;
}
/* Walk down the chain checking each certificate against the issuer */
for( certIndex = startCertIndex, iterationCount = 0;
cryptStatusOK( status ) && certIndex >= -1 && \
iterationCount < MAX_CHAINLENGTH;
certIndex--, iterationCount++ )
{
CERT_INFO *subjectCertInfoPtr;
POLICY_TYPE policyType;
/* Get info for the current certificate in the chain */
status = getCertInfo( certInfoPtr, &subjectCertInfoPtr, certIndex );
if( cryptStatusError( status ) )
break;
/* Check for the presence of further policy constraints. The path
length value can only ever be decremented once set so if we find
a further value for the length constraint we set the overall
value to the smaller of the two */
attributeListPtr = findAttributeField( subjectCertInfoPtr->attributes,
CRYPT_CERTINFO_REQUIREEXPLICITPOLICY,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL )
{
if( !hasExplicitPolicy || \
attributeListPtr->intValue < requireExplicitPolicyLevel )
requireExplicitPolicyLevel = attributeListPtr->intValue;
hasExplicitPolicy = TRUE;
}
attributeListPtr = findAttributeField( subjectCertInfoPtr->attributes,
CRYPT_CERTINFO_INHIBITPOLICYMAPPING,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL )
{
if( !hasInhibitPolicyMap || \
attributeListPtr->intValue < inhibitPolicyMapLevel )
inhibitPolicyMapLevel = attributeListPtr->intValue;
hasInhibitPolicyMap = TRUE;
}
attributeListPtr = findAttributeField( subjectCertInfoPtr->attributes,
CRYPT_CERTINFO_INHIBITANYPOLICY,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL )
{
if( !hasInhibitAnyPolicy || \
attributeListPtr->intValue < inhibitAnyPolicyLevel )
inhibitAnyPolicyLevel = attributeListPtr->intValue;
hasInhibitAnyPolicy = TRUE;
}
/* If any of the policy constraints have triggered then the policy
extension is now treated as critical even if it wasn't before */
if( ( hasExplicitPolicy && requireExplicitPolicyLevel <= 0 ) || \
( hasInhibitAnyPolicy && inhibitAnyPolicyLevel <= 0 ) )
hasPolicy = TRUE;
/* Determine the necessary policy check type based on the various
policy constraints */
policyType = POLICY_NONE;
if( hasPolicy )
{
const BOOLEAN inhibitAnyPolicy = \
( hasInhibitAnyPolicy && inhibitAnyPolicyLevel <= 0 ) ? \
TRUE : FALSE;
if( hasExplicitPolicy )
{
if( requireExplicitPolicyLevel > 0 )
policyType = inhibitAnyPolicy ? \
POLICY_NONE_SPECIFIC : POLICY_NONE;
else
if( requireExplicitPolicyLevel == 0 )
policyType = inhibitAnyPolicy ? \
POLICY_SUBJECT_SPECIFIC : POLICY_SUBJECT;
else
if( requireExplicitPolicyLevel < 0 )
policyType = inhibitAnyPolicy ? \
POLICY_BOTH_SPECIFIC : POLICY_BOTH;
}
else
policyType = inhibitAnyPolicy ? \
POLICY_NONE_SPECIFIC : POLICY_NONE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -