crypt32(17/17): Check path length constraint on a chain

Juan Lang juan.lang at gmail.com
Thu Aug 30 20:19:30 CDT 2007


--Juan
-------------- next part --------------
From 4fa758268a97d4c80bf877ef2e48eecdd197c827 Mon Sep 17 00:00:00 2001
From: Juan Lang <juan.lang at gmail.com>
Date: Thu, 30 Aug 2007 18:00:19 -0700
Subject: [PATCH] Check path length constraint on a chain
---
 dlls/crypt32/chain.c       |  106 ++++++++++++++++++++++++++++++++++++--------
 dlls/crypt32/tests/chain.c |    2 -
 2 files changed, 88 insertions(+), 20 deletions(-)

diff --git a/dlls/crypt32/chain.c b/dlls/crypt32/chain.c
index d15f551..7efe0c7 100644
--- a/dlls/crypt32/chain.c
+++ b/dlls/crypt32/chain.c
@@ -366,12 +366,20 @@ static BOOL CRYPT_CheckRootCert(HCERTCHA
     return ret;
 }
 
-static BOOL CRYPT_CanCertBeCA(PCCERT_CONTEXT cert, BOOL defaultIfNotSpecified)
+/* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
+ * or szOID_BASIC_CONSTRAINTS2, whichever is present) into a
+ * CERT_BASIC_CONSTRAINTS2_INFO.  If it neither extension is present, sets
+ * constraints->fCA to defaultIfNotSpecified.
+ * Returns FALSE if the extension is present but couldn't be decoded.
+ */
+static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert,
+ CERT_BASIC_CONSTRAINTS2_INFO *constraints, BOOL defaultIfNotSpecified)
 {
-    BOOL ret;
+    BOOL ret = TRUE;
     PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
 
+    constraints->fPathLenConstraint = FALSE;
     if (ext)
     {
         CERT_BASIC_CONSTRAINTS_INFO *info;
@@ -383,7 +391,8 @@ static BOOL CRYPT_CanCertBeCA(PCCERT_CON
         if (ret)
         {
             if (info->SubjectType.cbData == 1)
-                ret = info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
+                constraints->fCA =
+                 info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
             LocalFree(info);
         }
     }
@@ -393,32 +402,80 @@ static BOOL CRYPT_CanCertBeCA(PCCERT_CON
          cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
         if (ext)
         {
-            CERT_BASIC_CONSTRAINTS2_INFO *info;
-            DWORD size = 0;
+            DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
 
             ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
              szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
-             CRYPT_DECODE_ALLOC_FLAG, NULL, (LPBYTE)&info, &size);
-            if (ret)
-            {
-                ret = info->fCA;
-                LocalFree(info);
-            }
+             0, NULL, constraints, &size);
         }
         else
-            ret = defaultIfNotSpecified;
+            constraints->fCA = defaultIfNotSpecified;
     }
     return ret;
 }
 
+/* Checks element's basic constraints to see if it can act as a CA, with
+ * remainingCAs CAs left in this chain.  Updates chainConstraints with the
+ * element's constraints, if:
+ * 1. chainConstraints doesn't have a path length constraint, or
+ * 2. element's path length constraint is smaller than chainConstraints's
+ * Sets *pathLengthConstraintViolated to TRUE if a path length violation
+ * occurs.
+ * Returns TRUE if the element can be a CA, and the length of the remaining
+ * chain is valid.
+ */
+static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert,
+ CERT_BASIC_CONSTRAINTS2_INFO *chainConstraints, DWORD remainingCAs,
+ BOOL *pathLengthConstraintViolated)
+{
+    BOOL validBasicConstraints;
+    CERT_BASIC_CONSTRAINTS2_INFO constraints;
+
+    if ((validBasicConstraints = CRYPT_DecodeBasicConstraints(cert,
+     &constraints, TRUE)))
+    {
+        if (!constraints.fCA)
+        {
+            TRACE("chain element %d can't be a CA\n", remainingCAs + 1);
+            validBasicConstraints = FALSE;
+        }
+        else if (constraints.fPathLenConstraint)
+        {
+            /* If the element has path length constraints, they apply to the
+             * entire remaining chain.
+             */
+            if (!chainConstraints->fPathLenConstraint ||
+             constraints.dwPathLenConstraint <
+             chainConstraints->dwPathLenConstraint)
+            {
+                TRACE("setting path length constraint to %d\n",
+                 chainConstraints->dwPathLenConstraint);
+                chainConstraints->fPathLenConstraint = TRUE;
+                chainConstraints->dwPathLenConstraint =
+                 constraints.dwPathLenConstraint;
+            }
+        }
+    }
+    if (chainConstraints->fPathLenConstraint &&
+     remainingCAs > chainConstraints->dwPathLenConstraint)
+    {
+        TRACE("remaining CAs %d exceed max path length %d\n", remainingCAs,
+         chainConstraints->dwPathLenConstraint);
+        validBasicConstraints = FALSE;
+        *pathLengthConstraintViolated = TRUE;
+    }
+    return validBasicConstraints;
+}
+
 static BOOL CRYPT_CheckSimpleChain(PCertificateChainEngine engine,
  PCERT_SIMPLE_CHAIN chain, LPFILETIME time)
 {
     PCERT_CHAIN_ELEMENT rootElement = chain->rgpElement[chain->cElement - 1];
-    DWORD i;
-    BOOL ret = TRUE;
+    int i;
+    BOOL ret = TRUE, pathLengthConstraintViolated = FALSE;
+    CERT_BASIC_CONSTRAINTS2_INFO constraints = { TRUE, FALSE, 0 };
 
-    for (i = 0; i < chain->cElement; i++)
+    for (i = chain->cElement - 1; i >= 0; i--)
     {
         if (CertVerifyTimeValidity(time,
          chain->rgpElement[i]->pCertContext->pCertInfo) != 0)
@@ -426,12 +483,23 @@ static BOOL CRYPT_CheckSimpleChain(PCert
              CERT_TRUST_IS_NOT_TIME_VALID;
         if (i != 0)
         {
-            BOOL ca;
-
-            ca = CRYPT_CanCertBeCA(chain->rgpElement[i]->pCertContext, TRUE);
-            if (!ca)
+            /* Once a path length constraint has been violated, every remaining
+             * CA cert's basic constraints is considered invalid.
+             */
+            if (pathLengthConstraintViolated)
                 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
                  CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
+            else if (!CRYPT_CheckBasicConstraintsForCA(
+             chain->rgpElement[i]->pCertContext, &constraints, i - 1,
+             &pathLengthConstraintViolated))
+                chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
+                 CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
+            else if (constraints.fPathLenConstraint &&
+             constraints.dwPathLenConstraint)
+            {
+                /* This one's valid - decrement max length */
+                constraints.dwPathLenConstraint--;
+            }
         }
         /* FIXME: check valid usages and name constraints */
         CRYPT_CombineTrustStatus(&chain->TrustStatus,
diff --git a/dlls/crypt32/tests/chain.c b/dlls/crypt32/tests/chain.c
index a1082a4..b174420 100644
--- a/dlls/crypt32/tests/chain.c
+++ b/dlls/crypt32/tests/chain.c
@@ -1507,7 +1507,7 @@ static ChainCheck chainCheck[] = {
      { CERT_TRUST_INVALID_BASIC_CONSTRAINTS | CERT_TRUST_IS_UNTRUSTED_ROOT |
        CERT_TRUST_IS_NOT_TIME_VALID, 0 },
      1, simpleStatus4 },
-   TODO_ERROR | TODO_INFO },
+   TODO_INFO },
  { { sizeof(chain5) / sizeof(chain5[0]), chain5 },
    { { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
      { CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT |
-- 
1.4.1


More information about the wine-patches mailing list