crypt32(17/17): Consider alternate issuers when building chains

Juan Lang juan.lang at gmail.com
Thu Sep 6 12:10:39 CDT 2007


--Juan
-------------- next part --------------
From 6fc4b67eae2868356b3448cb6204f4eff9d4ffde Mon Sep 17 00:00:00 2001
From: Juan Lang <juan.lang at gmail.com>
Date: Thu, 6 Sep 2007 10:02:54 -0700
Subject: [PATCH] Consider alternate issuers when building chains
---
 dlls/crypt32/chain.c       |  340 ++++++++++++++++++++++++++++++++++++++++++--
 dlls/crypt32/tests/chain.c |    4 -
 2 files changed, 328 insertions(+), 16 deletions(-)

diff --git a/dlls/crypt32/chain.c b/dlls/crypt32/chain.c
index 8d0d78c..4424f52 100644
--- a/dlls/crypt32/chain.c
+++ b/dlls/crypt32/chain.c
@@ -602,6 +602,312 @@ static BOOL CRYPT_BuildCandidateChainFro
     return ret;
 }
 
+/* Makes and returns a copy of chain, up to and including element iElement. */
+static PCERT_SIMPLE_CHAIN CRYPT_CopySimpleChainToElement(
+ PCERT_SIMPLE_CHAIN chain, DWORD iElement)
+{
+    PCERT_SIMPLE_CHAIN copy = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
+
+    if (copy)
+    {
+        memset(copy, 0, sizeof(CERT_SIMPLE_CHAIN));
+        copy->cbSize = sizeof(CERT_SIMPLE_CHAIN);
+        copy->rgpElement =
+         CryptMemAlloc((iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
+        if (copy->rgpElement)
+        {
+            DWORD i;
+            BOOL ret = TRUE;
+
+            memset(copy->rgpElement, 0,
+             (iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
+            for (i = 0; ret && i <= iElement; i++)
+            {
+                PCERT_CHAIN_ELEMENT element =
+                 CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
+
+                if (element)
+                {
+                    memcpy(element, chain->rgpElement[i],
+                     sizeof(CERT_CHAIN_ELEMENT));
+                    element->pCertContext = CertDuplicateCertificateContext(
+                     chain->rgpElement[i]->pCertContext);
+                    /* Reset the trust status of the copied element, it'll get
+                     * rechecked after the new chain is done.
+                     */
+                    memset(&element->TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
+                    copy->rgpElement[copy->cElement++] = element;
+                }
+                else
+                    ret = FALSE;
+            }
+            if (!ret)
+            {
+                for (i = 0; i <= iElement; i++)
+                    CryptMemFree(copy->rgpElement[i]);
+                CryptMemFree(copy->rgpElement);
+                CryptMemFree(copy);
+                copy = NULL;
+            }
+        }
+        else
+        {
+            CryptMemFree(copy);
+            copy = NULL;
+        }
+    }
+    return copy;
+}
+
+static void CRYPT_FreeLowerQualityChains(PCertificateChain chain)
+{
+    DWORD i;
+
+    for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
+        CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
+    CryptMemFree(chain->context.rgpLowerQualityChainContext);
+}
+
+static void CRYPT_FreeChainContext(PCertificateChain chain)
+{
+    DWORD i;
+
+    CRYPT_FreeLowerQualityChains(chain);
+    for (i = 0; i < chain->context.cChain; i++)
+        CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
+    CryptMemFree(chain->context.rgpChain);
+    CertCloseStore(chain->world, 0);
+    CryptMemFree(chain);
+}
+
+/* Makes and returns a copy of chain, up to and including element iElement of
+ * simple chain iChain.
+ */
+static PCertificateChain CRYPT_CopyChainToElement(PCertificateChain chain,
+ DWORD iChain, DWORD iElement)
+{
+    PCertificateChain copy = CryptMemAlloc(sizeof(CertificateChain));
+
+    if (copy)
+    {
+        copy->ref = 1;
+        copy->world = CertDuplicateStore(chain->world);
+        copy->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
+        /* Leave the trust status of the copied chain unset, it'll get
+         * rechecked after the new chain is done.
+         */
+        memset(&copy->context.TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
+        copy->context.cLowerQualityChainContext = 0;
+        copy->context.rgpLowerQualityChainContext = NULL;
+        copy->context.fHasRevocationFreshnessTime = FALSE;
+        copy->context.dwRevocationFreshnessTime = 0;
+        copy->context.rgpChain = CryptMemAlloc(
+         (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
+        if (copy->context.rgpChain)
+        {
+            BOOL ret = TRUE;
+            DWORD i;
+
+            memset(copy->context.rgpChain, 0,
+             (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
+            if (iChain)
+            {
+                for (i = 0; ret && iChain && i < iChain - 1; i++)
+                {
+                    copy->context.rgpChain[i] =
+                     CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
+                     chain->context.rgpChain[i]->cElement - 1);
+                    if (!copy->context.rgpChain[i])
+                        ret = FALSE;
+                }
+            }
+            else
+                i = 0;
+            if (ret)
+            {
+                copy->context.rgpChain[i] =
+                 CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
+                 iElement);
+                if (!copy->context.rgpChain[i])
+                    ret = FALSE;
+            }
+            if (!ret)
+            {
+                CRYPT_FreeChainContext(copy);
+                copy = NULL;
+            }
+            else
+                copy->context.cChain = iChain + 1;
+        }
+        else
+        {
+            CryptMemFree(copy);
+            copy = NULL;
+        }
+    }
+    return copy;
+}
+
+static PCertificateChain CRYPT_BuildAlternateContextFromChain(
+ HCERTCHAINENGINE hChainEngine, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
+ PCertificateChain chain)
+{
+    PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
+    PCertificateChain alternate;
+
+    TRACE("(%p, %p, %p, %p)\n", hChainEngine, pTime, hAdditionalStore, chain);
+
+    /* Always start with the last "lower quality" chain to ensure a consistent
+     * order of alternate creation:
+     */
+    if (chain->context.cLowerQualityChainContext)
+        chain = (PCertificateChain)chain->context.rgpLowerQualityChainContext[
+         chain->context.cLowerQualityChainContext - 1];
+    /* A chain with only one element can't have any alternates */
+    if (chain->context.cChain <= 1 && chain->context.rgpChain[0]->cElement <= 1)
+        alternate = NULL;
+    else
+    {
+        DWORD i, j, flags;
+        PCCERT_CONTEXT alternateIssuer = NULL;
+
+        alternate = NULL;
+        for (i = 0; !alternateIssuer && i < chain->context.cChain; i++)
+            for (j = 0; !alternateIssuer &&
+             j < chain->context.rgpChain[i]->cElement - 1; j++)
+            {
+                PCCERT_CONTEXT subject =
+                 chain->context.rgpChain[i]->rgpElement[j]->pCertContext;
+                PCCERT_CONTEXT prevIssuer = CertDuplicateCertificateContext(
+                 chain->context.rgpChain[i]->rgpElement[j + 1]->pCertContext);
+
+                flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG;
+                alternateIssuer = CertGetIssuerCertificateFromStore(
+                 prevIssuer->hCertStore, subject, prevIssuer, &flags);
+            }
+        if (alternateIssuer)
+        {
+            i--;
+            j--;
+            alternate = CRYPT_CopyChainToElement(chain, i, j);
+            if (alternate)
+            {
+                BOOL ret = CRYPT_AddCertToSimpleChain(engine,
+                 alternate->context.rgpChain[i], alternateIssuer);
+
+                if (ret)
+                {
+                    ret = CRYPT_BuildSimpleChain(engine, alternate->world,
+                     alternate->context.rgpChain[i]);
+                    if (ret)
+                        CRYPT_CheckSimpleChain(engine,
+                         alternate->context.rgpChain[i], pTime);
+                    CRYPT_CombineTrustStatus(&alternate->context.TrustStatus,
+                     &alternate->context.rgpChain[i]->TrustStatus);
+                }
+                if (!ret)
+                {
+                    CRYPT_FreeChainContext(alternate);
+                    alternate = NULL;
+                }
+            }
+        }
+    }
+    TRACE("%p\n", alternate);
+    return alternate;
+}
+
+#define CHAIN_QUALITY_SIGNATURE_VALID 8
+#define CHAIN_QUALITY_TIME_VALID      4
+#define CHAIN_QUALITY_COMPLETE_CHAIN  2
+#define CHAIN_QUALITY_TRUSTED_ROOT    1
+
+#define CHAIN_QUALITY_HIGHEST \
+ CHAIN_QUALITY_SIGNATURE_VALID | CHAIN_QUALITY_TIME_VALID | \
+ CHAIN_QUALITY_COMPLETE_CHAIN | CHAIN_QUALITY_TRUSTED_ROOT
+
+#define IS_TRUST_ERROR_SET(TrustStatus, bits) \
+ (TrustStatus)->dwErrorStatus & (bits)
+
+static DWORD CRYPT_ChainQuality(PCertificateChain chain)
+{
+    DWORD quality = CHAIN_QUALITY_HIGHEST;
+
+    if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
+     CERT_TRUST_IS_UNTRUSTED_ROOT))
+        quality &= ~CHAIN_QUALITY_TRUSTED_ROOT;
+    if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
+     CERT_TRUST_IS_PARTIAL_CHAIN))
+    if (chain->context.TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
+        quality &= ~CHAIN_QUALITY_COMPLETE_CHAIN;
+    if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
+     CERT_TRUST_IS_NOT_TIME_VALID | CERT_TRUST_IS_NOT_TIME_NESTED))
+        quality &= ~CHAIN_QUALITY_TIME_VALID;
+    if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
+     CERT_TRUST_IS_NOT_SIGNATURE_VALID))
+        quality &= ~CHAIN_QUALITY_SIGNATURE_VALID;
+    return quality;
+}
+
+/* Chooses the highest quality chain among chain and its "lower quality"
+ * alternate chains.  Returns the highest quality chain, with all other
+ * chains as lower quality chains of it.
+ */
+static PCertificateChain CRYPT_ChooseHighestQualityChain(
+ PCertificateChain chain)
+{
+    DWORD i;
+
+    /* There are always only two chains being considered:  chain, and an
+     * alternate at chain->rgpLowerQualityChainContext[i].  If the alternate
+     * has a higher quality than chain, the alternate gets assigned the lower
+     * quality contexts, with chain taking the alternate's place among the
+     * lower quality contexts.
+     */
+    for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
+    {
+        PCertificateChain alternate =
+         (PCertificateChain)chain->context.rgpLowerQualityChainContext[i];
+
+        if (CRYPT_ChainQuality(alternate) > CRYPT_ChainQuality(chain))
+        {
+            alternate->context.cLowerQualityChainContext =
+             chain->context.cLowerQualityChainContext;
+            alternate->context.rgpLowerQualityChainContext =
+             chain->context.rgpLowerQualityChainContext;
+            alternate->context.rgpLowerQualityChainContext[i] =
+             (PCCERT_CHAIN_CONTEXT)chain;
+            chain = alternate;
+        }
+    }
+    return chain;
+}
+
+static BOOL CRYPT_AddAlternateChainToChain(PCertificateChain chain,
+ PCertificateChain alternate)
+{
+    BOOL ret;
+
+    if (chain->context.cLowerQualityChainContext)
+        chain->context.rgpLowerQualityChainContext =
+         CryptMemRealloc(chain->context.rgpLowerQualityChainContext,
+         (chain->context.cLowerQualityChainContext + 1) *
+         sizeof(PCCERT_CHAIN_CONTEXT));
+    else
+        chain->context.rgpLowerQualityChainContext =
+         CryptMemAlloc(sizeof(PCCERT_CHAIN_CONTEXT));
+    if (chain->context.rgpLowerQualityChainContext)
+    {
+        chain->context.rgpLowerQualityChainContext[
+         chain->context.cLowerQualityChainContext++] =
+         (PCCERT_CHAIN_CONTEXT)alternate;
+        ret = TRUE;
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
     DWORD            cbSize;
     CERT_USAGE_MATCH RequestedUsage;
@@ -647,6 +953,26 @@ BOOL WINAPI CertGetCertificateChain(HCER
      hAdditionalStore, &chain);
     if (ret)
     {
+        PCertificateChain alternate = NULL;
+
+        do {
+            alternate = CRYPT_BuildAlternateContextFromChain(hChainEngine,
+             pTime, hAdditionalStore, chain);
+
+            /* Alternate contexts are added as "lower quality" contexts of
+             * chain, to avoid loops in alternate chain creation.
+             * The highest-quality chain is chosen at the end.
+             */
+            if (alternate)
+                ret = CRYPT_AddAlternateChainToChain(chain, alternate);
+        } while (ret && alternate);
+        chain = CRYPT_ChooseHighestQualityChain(chain);
+        if (!(dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS))
+        {
+            CRYPT_FreeLowerQualityChains(chain);
+            chain->context.cLowerQualityChainContext = 0;
+            chain->context.rgpLowerQualityChainContext = NULL;
+        }
         if (ppChainContext)
             *ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
         else
@@ -656,20 +982,6 @@ BOOL WINAPI CertGetCertificateChain(HCER
     return ret;
 }
 
-static void CRYPT_FreeChainContext(PCertificateChain chain)
-{
-    DWORD i;
-
-    for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
-        CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
-    CryptMemFree(chain->context.rgpLowerQualityChainContext);
-    for (i = 0; i < chain->context.cChain; i++)
-        CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
-    CryptMemFree(chain->context.rgpChain);
-    CertCloseStore(chain->world, 0);
-    CryptMemFree(chain);
-}
-
 PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
  PCCERT_CHAIN_CONTEXT pChainContext)
 {
diff --git a/dlls/crypt32/tests/chain.c b/dlls/crypt32/tests/chain.c
index e6cdfd2..015a3cb 100644
--- a/dlls/crypt32/tests/chain.c
+++ b/dlls/crypt32/tests/chain.c
@@ -1589,11 +1589,11 @@ static ChainCheck chainCheck[] = {
  { { sizeof(chain10) / sizeof(chain10[0]), chain10 },
    { { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
      { CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus10 },
-   TODO_ERROR | TODO_INFO },
+   TODO_INFO },
  { { sizeof(chain11) / sizeof(chain11[0]), chain11 },
    { { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
      { CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus10 },
-   TODO_ERROR | TODO_INFO },
+   TODO_INFO },
  { { sizeof(chain12) / sizeof(chain12[0]), chain12 },
    { { 0, CERT_TRUST_HAS_PREFERRED_ISSUER },
      { CERT_TRUST_IS_UNTRUSTED_ROOT, 0 }, 1, simpleStatus12 },
-- 
1.4.1


More information about the wine-patches mailing list