📄 chk_cert.c
字号:
the DNS name portion */
status = sNetParseURL( &urlInfo, constrainedString,
constrainedStringLength, URL_TYPE_NONE );
if( cryptStatusError( status ) )
{
/* Exactly what to do in the case of a URL parse error is
uncertain. As usual no-one on the PKIX list has any idea
what the appropriate behaviour is here. The best option
seems to be to fail closed, otherwise anyone who creates
a URL that the certificate software can't parse but
that's still accepted by other apps (who in general will
bend over backwards to try and accept almost any
malformed URI, if they didn't do this then half the
Internet would stop working) would be able to bypass the
name constraint. The handling is complicated by the
fact that to report a failure at this point we need to
report a match for excluded subtrees but a non-match for
permitted subtrees. Since it's more likely that we'll
encounter a permitted-subtrees whitelist (who in their
right mind would trust something as flaky as PKI software
to reliably apply an excluded-subtrees blacklist? Even
something as trivial as "ex%41mple.com", let alone
"ex%u0041mple.com", "exAmple.com", or
"ex%EF%BC%A1mpple.com", is likely to trivially fool all
certificate software in existence, so permitted-subtrees
will never work anyway) we report the constraint as being
not matched which will reject the certificate for
permitted-subtrees. In addition we throw an exception in
debug mode */
assert( DEBUG_WARN );
return( FALSE );
}
/* Adjust the constrained string info to contain only the DNS
name portion of the URI */
constrainedString = urlInfo.host;
startPos = urlInfo.hostLen - stringLength;
if( startPos < 0 || startPos > MAX_INTLENGTH_SHORT )
return( FALSE );
ENSURES_B( startPos + stringLength <= urlInfo.hostLen );
/* URIs have a special-case requirement where the absence of a
wildcard-match indicator (the leading dot) indicates that the
constraining DNS name is for a standalone host and not a
portion of the constrained string's DNS name. This means
that the DNS-name portion of the URI must be an exact match
for the constraining string */
if( !isWildcardMatch && startPos != 0 )
return( FALSE );
}
}
/* Check whether the constraining string is a suffix of the constrained
string. For DNS name constraints the rule for RFC 3280 became
"adding to the LHS" as for other constraints, in RFC 2459 it was
another special case where it had to be a subdomain as if an
implicit "." was present */
return( !strCompare( constrainedString + startPos, string,
stringLength ) ? TRUE : FALSE );
}
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static BOOLEAN matchAltnameComponent( const ATTRIBUTE_LIST *constrainedAttribute,
const ATTRIBUTE_LIST *attribute,
IN_ATTRIBUTE \
const CRYPT_ATTRIBUTE_TYPE attributeType )
{
assert( isReadPtr( constrainedAttribute, sizeof( ATTRIBUTE_LIST ) ) );
assert( isReadPtr( attribute, sizeof( ATTRIBUTE_LIST ) ) );
REQUIRES( attributeType == CRYPT_CERTINFO_DIRECTORYNAME || \
attributeType == CRYPT_CERTINFO_RFC822NAME || \
attributeType == CRYPT_CERTINFO_DNSNAME || \
attributeType == CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER );
/* If the attribute being matched is a DN, use a DN-specific match */
if( attributeType == CRYPT_CERTINFO_DIRECTORYNAME )
{
return( compareDN( constrainedAttribute->value, attribute->value,
TRUE ) );
}
/* It's a string name, use a substring match with attribute type-specific
special cases */
return( wildcardMatch( constrainedAttribute, attribute,
( attributeType == CRYPT_CERTINFO_RFC822NAME ) ? \
MATCH_EMAIL : \
( attributeType == CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER ) ? \
MATCH_URI : \
MATCH_NONE ) );
}
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static BOOLEAN checkAltnameConstraints( const ATTRIBUTE_LIST *subjectAttributes,
const ATTRIBUTE_LIST *issuerAttributes,
IN_ATTRIBUTE \
const CRYPT_ATTRIBUTE_TYPE attributeType,
const BOOLEAN isExcluded )
{
const ATTRIBUTE_LIST *attributeListPtr, *constrainedAttributeListPtr;
int iterationCount;
assert( isReadPtr( subjectAttributes, sizeof( ATTRIBUTE_LIST ) ) );
assert( isReadPtr( issuerAttributes, sizeof( ATTRIBUTE_LIST ) ) );
REQUIRES( attributeType == CRYPT_CERTINFO_DIRECTORYNAME || \
attributeType == CRYPT_CERTINFO_RFC822NAME || \
attributeType == CRYPT_CERTINFO_DNSNAME || \
attributeType == CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER );
/* Check for the presence of constrained or constraining altName
components. If either are absent, there are no constraints to
apply */
attributeListPtr = findAttributeField( issuerAttributes,
isExcluded ? \
CRYPT_CERTINFO_EXCLUDEDSUBTREES : \
CRYPT_CERTINFO_PERMITTEDSUBTREES,
attributeType );
if( attributeListPtr == NULL )
return( TRUE );
for( constrainedAttributeListPtr = \
findAttributeField( subjectAttributes,
CRYPT_CERTINFO_SUBJECTALTNAME, attributeType ), \
iterationCount = 0;
constrainedAttributeListPtr != NULL && \
iterationCount < FAILSAFE_ITERATIONS_MAX;
constrainedAttributeListPtr = \
findNextFieldInstance( constrainedAttributeListPtr ), \
iterationCount++ )
{
const ATTRIBUTE_LIST *attributeListCursor;
int innerIterationCount;
BOOLEAN isMatch = FALSE;
/* Step through the constraining attributes checking if any match
the constrained attribute. If it's an excluded subtree then none
can match, if it's a permitted subtree then at least one must
match */
for( attributeListCursor = attributeListPtr, \
innerIterationCount = 0;
attributeListCursor != NULL && !isMatch && \
innerIterationCount < FAILSAFE_ITERATIONS_MAX;
attributeListCursor =
findNextFieldInstance( attributeListCursor ), \
innerIterationCount++ )
{
isMatch = matchAltnameComponent( constrainedAttributeListPtr,
attributeListCursor,
attributeType );
}
ENSURES_B( iterationCount < FAILSAFE_ITERATIONS_MAX );
if( isExcluded == isMatch )
return( FALSE );
}
ENSURES_B( iterationCount < FAILSAFE_ITERATIONS_MAX );
return( TRUE );
}
/****************************************************************************
* *
* Check for Constraint Violations *
* *
****************************************************************************/
/* Check name constraints placed by an issuer, checked if complianceLevel >=
CRYPT_COMPLIANCELEVEL_PKIX_FULL */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
int checkNameConstraints( const CERT_INFO *subjectCertInfoPtr,
const ATTRIBUTE_LIST *issuerAttributes,
const BOOLEAN isExcluded,
OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
CRYPT_ATTRIBUTE_TYPE *errorLocus,
OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
CRYPT_ERRTYPE_TYPE *errorType )
{
const ATTRIBUTE_LIST *subjectAttributes = subjectCertInfoPtr->attributes;
const CRYPT_ATTRIBUTE_TYPE constraintType = isExcluded ? \
CRYPT_CERTINFO_EXCLUDEDSUBTREES : CRYPT_CERTINFO_PERMITTEDSUBTREES;
ATTRIBUTE_LIST *attributeListPtr;
BOOLEAN isMatch = FALSE;
assert( isReadPtr( subjectCertInfoPtr, sizeof( CERT_INFO ) ) );
assert( isReadPtr( issuerAttributes, sizeof( ATTRIBUTE_LIST ) ) );
assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
/* If this is a PKIX path-kludge CA certificate then the name
constraints don't apply to it (PKIX section 4.2.1.11). This is
required in order to allow extra certificates to be kludged into the
path without violating the constraint. For example with the chain:
Issuer Subject Constraint
------ ------- ----------
Root CA permitted = "EE"
CA' CA'
CA EE
the kludge certificate CA' must be excluded from name constraint
restrictions in order for the path to be valid. Obviously this is
only necessary for constraints set by the immediate parent but PKIX
says it's for constraints set by all certificates in the chain (!!),
thus making the pathkludge certificate exempt from any name
constraints and not just the one that would cause problems */
if( isPathKludge( subjectCertInfoPtr ) )
return( CRYPT_OK );
/* Check the subject DN if constraints exist. If it's an excluded
subtree then none can match, if it's a permitted subtree then at
least one must match */
attributeListPtr = findAttributeField( issuerAttributes, constraintType,
CRYPT_CERTINFO_DIRECTORYNAME );
if( attributeListPtr != NULL )
{
int iterationCount;
for( iterationCount = 0;
attributeListPtr != NULL && !isMatch && \
iterationCount < FAILSAFE_ITERATIONS_MAX;
iterationCount++ )
{
isMatch = compareDN( subjectCertInfoPtr->subjectName,
attributeListPtr->value, TRUE );
attributeListPtr = findNextFieldInstance( attributeListPtr );
}
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
if( isExcluded == isMatch )
{
setErrorValues( CRYPT_CERTINFO_SUBJECTNAME,
CRYPT_ERRTYPE_CONSTRAINT );
return( CRYPT_ERROR_INVALID );
}
}
/* DN constraints apply to both the main subject DN and any other DNs
that may be present as subject altNames, so after we've checked the
main DN we check any altName DNs as well */
if( !checkAltnameConstraints( subjectAttributes, issuerAttributes,
CRYPT_CERTINFO_DIRECTORYNAME, isExcluded ) )
{
setErrorValues( CRYPT_CERTINFO_SUBJECTALTNAME,
CRYPT_ERRTYPE_CONSTRAINT );
return( CRYPT_ERROR_INVALID );
}
/* Compare the Internet-related names if constraints exist. We don't
have to check for the special case of an email address in the DN
since the certificate import code transparently maps this to the
appropriate altName component */
if( !checkAltnameConstraints( subjectAttributes, issuerAttributes,
CRYPT_CERTINFO_RFC822NAME, isExcluded ) || \
!checkAltnameConstraints( subjectAttributes, issuerAttributes,
CRYPT_CERTINFO_DNSNAME, isExcluded ) || \
!checkAltnameConstraints( subjectAttributes, issuerAttributes,
CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER,
isExcluded ) )
{
setErrorValues( CRYPT_CERTINFO_SUBJECTALTNAME,
CRYPT_ERRTYPE_CONSTRAINT );
return( CRYPT_ERROR_INVALID );
}
return( CRYPT_OK );
}
/* Check policy constraints placed by an issuer, checked if complianceLevel
>= CRYPT_COMPLIANCELEVEL_PKIX_FULL */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
int checkPolicyConstraints( const CERT_INFO *subjectCertInfoPtr,
const ATTRIBUTE_LIST *issuerAttributes,
IN_ENUM_OPT( POLICY ) const POLICY_TYPE policyType,
OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
CRYPT_ATTRIBUTE_TYPE *errorLocus,
OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
CRYPT_ERRTYPE_TYPE *errorType )
{
const ATTRIBUTE_LIST *attributeListPtr = \
findAttributeField( issuerAttributes,
CRYPT_CERTINFO_CERTPOLICYID,
CRYPT_ATTRIBUTE_NONE );
const ATTRIBUTE_LIST *constrainedAttributeListPtr = \
findAttributeField( subjectCertInfoPtr->attributes,
CRYPT_CERTINFO_CERTPOLICYID,
CRYPT_ATTRIBUTE_NONE );
ATTRIBUTE_LIST *attributeCursor;
BOOLEAN subjectHasPolicy, issuerHasPolicy;
BOOLEAN subjectHasAnyPolicy, issuerHasAnyPolicy;
int iterationCount;
assert( isReadPtr( subjectCertInfoPtr, sizeof( CERT_INFO ) ) );
assert( isReadPtr( issuerAttributes, sizeof( ATTRIBUTE_LIST ) ) );
assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
ENSURES( policyType >= POLICY_NONE && policyType < POLICY_LAST );
/* If there's a policy mapping present then neither the issuer nor
subject domain policies can be the wildcard anyPolicy (PKIX section
4.2.1.6) */
if( containsAnyPolicy( issuerAttributes,
CRYPT_CERTINFO_ISSUERDOMAINPOLICY ) || \
containsAnyPolicy( issuerAttributes,
CRYPT_CERTINFO_SUBJECTDOMAINPOLICY ) )
{
setErrorValues( CRYPT_CERTINFO_POLICYMAPPINGS,
CRYPT_ERRTYPE_ISSUERCONSTRAINT );
return( CRYPT_ERROR_INVALID );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -