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(©->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