crypt32(8/8): Initial implementation of CertGetCertificateChain and
CertFreeCertificateChain
Juan Lang
juan.lang at gmail.com
Tue Aug 14 17:43:49 CDT 2007
--Juan
-------------- next part --------------
From 48ce62d7c01413921c57522642fbfc8f7f6adc53 Mon Sep 17 00:00:00 2001
From: Juan Lang <juan.lang at gmail.com>
Date: Tue, 14 Aug 2007 15:15:02 -0700
Subject: [PATCH] Initial implementation of CertGetCertificateChain and CertFreeCertificateChain
---
dlls/crypt32/chain.c | 300 +++++++++++++++++++++++++++++++++++++++++++-
dlls/crypt32/tests/chain.c | 9 -
2 files changed, 294 insertions(+), 15 deletions(-)
diff --git a/dlls/crypt32/chain.c b/dlls/crypt32/chain.c
index 127739a..b4364b6 100644
--- a/dlls/crypt32/chain.c
+++ b/dlls/crypt32/chain.c
@@ -25,6 +25,8 @@ #include "crypt32_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(crypt);
+static HCERTCHAINENGINE CRYPT_defaultChainEngine;
+
/* This represents a subset of a certificate chain engine: it doesn't include
* the "hOther" store described by MSDN, because I'm not sure how that's used.
* It also doesn't include the "hTrust" store, because I don't yet implement
@@ -171,22 +173,308 @@ void WINAPI CertFreeCertificateChainEngi
}
}
+static HCERTCHAINENGINE CRYPT_GetDefaultChainEngine(void)
+{
+ if (!CRYPT_defaultChainEngine)
+ {
+ CERT_CHAIN_ENGINE_CONFIG config = { 0 };
+ HCERTCHAINENGINE engine;
+
+ config.cbSize = sizeof(config);
+ CertCreateCertificateChainEngine(&config, &engine);
+ InterlockedCompareExchangePointer(&CRYPT_defaultChainEngine, engine,
+ NULL);
+ if (CRYPT_defaultChainEngine != engine)
+ CertFreeCertificateChainEngine(engine);
+ }
+ return CRYPT_defaultChainEngine;
+}
+
+void default_chain_engine_free(void)
+{
+ CertFreeCertificateChainEngine(CRYPT_defaultChainEngine);
+}
+
+typedef struct _CertificateChain
+{
+ CERT_CHAIN_CONTEXT context;
+ LONG ref;
+} CertificateChain, *PCertificateChain;
+
+static inline BOOL WINAPI CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert)
+{
+ return CertCompareCertificateName(cert->dwCertEncodingType,
+ &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
+}
+
+/* Gets cert's issuer from store, and returns the validity flags associated
+ * with it. Returns NULL if no issuer whose public key matches cert's
+ * signature could be found.
+ */
+static PCCERT_CONTEXT CRYPT_GetIssuerFromStore(HCERTSTORE store,
+ PCCERT_CONTEXT cert, PDWORD pdwFlags)
+{
+ PCCERT_CONTEXT issuer = NULL;
+
+ /* There might be more than issuer with the same name, so keep looking until
+ * one produces the correct signature for this cert.
+ */
+ do {
+ *pdwFlags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG |
+ CERT_STORE_TIME_VALIDITY_FLAG;
+ issuer = CertGetIssuerCertificateFromStore(store, cert, issuer,
+ pdwFlags);
+ } while (issuer && (*pdwFlags & CERT_STORE_SIGNATURE_FLAG));
+ return issuer;
+}
+
+static BOOL CRYPT_AddCertToSimpleChain(PCERT_SIMPLE_CHAIN chain,
+ PCCERT_CONTEXT cert, DWORD dwFlags)
+{
+ BOOL ret = FALSE;
+ PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
+
+ if (element)
+ {
+ if (!chain->cElement)
+ chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
+ else
+ chain->rgpElement = CryptMemRealloc(chain->rgpElement,
+ (chain->cElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
+ if (chain->rgpElement)
+ {
+ memset(element, 0, sizeof(CERT_CHAIN_ELEMENT));
+ element->cbSize = sizeof(CERT_CHAIN_ELEMENT);
+ element->pCertContext = cert;
+ if (dwFlags & CERT_STORE_REVOCATION_FLAG &&
+ !(dwFlags & CERT_STORE_NO_CRL_FLAG))
+ element->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_REVOKED;
+ if (dwFlags & CERT_STORE_SIGNATURE_FLAG)
+ element->TrustStatus.dwErrorStatus |=
+ CERT_TRUST_IS_NOT_SIGNATURE_VALID;
+ if (dwFlags & CERT_STORE_TIME_VALIDITY_FLAG)
+ element->TrustStatus.dwErrorStatus |=
+ CERT_TRUST_IS_NOT_TIME_VALID;
+ /* It appears, from every example certificate chain I've found,
+ * that this flag is always set:
+ */
+ element->TrustStatus.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER;
+ if (chain->cElement)
+ {
+ PCERT_CHAIN_ELEMENT prevElement =
+ chain->rgpElement[chain->cElement - 1];
+
+ /* This cert is the issuer of the previous one in the chain, so
+ * retroactively check the previous one's time validity nesting.
+ */
+ if (!CertVerifyValidityNesting(
+ prevElement->pCertContext->pCertInfo, cert->pCertInfo))
+ prevElement->TrustStatus.dwErrorStatus |=
+ CERT_TRUST_IS_NOT_TIME_NESTED;
+ }
+ /* FIXME: check valid usages, name constraints, and for cycles */
+ /* FIXME: initialize the rest of element */
+ chain->TrustStatus.dwErrorStatus |=
+ element->TrustStatus.dwErrorStatus;
+ chain->TrustStatus.dwInfoStatus |=
+ element->TrustStatus.dwInfoStatus;
+ chain->rgpElement[chain->cElement++] = element;
+ ret = TRUE;
+ }
+ else
+ CryptMemFree(element);
+ }
+ return ret;
+}
+
+static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
+{
+ DWORD i;
+
+ for (i = 0; i < chain->cElement; i++)
+ CryptMemFree(chain->rgpElement[i]);
+ CryptMemFree(chain->rgpElement);
+ CryptMemFree(chain);
+}
+
+static BOOL CRYPT_BuildSimpleChain(HCERTCHAINENGINE hChainEngine,
+ PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
+ PCERT_SIMPLE_CHAIN *ppChain)
+{
+ BOOL ret = FALSE;
+ PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
+ PCERT_SIMPLE_CHAIN chain;
+ HCERTSTORE world;
+
+ TRACE("(%p, %p, %p, %p)\n", hChainEngine, cert, pTime, hAdditionalStore);
+
+ world = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
+ CERT_STORE_CREATE_NEW_FLAG, NULL);
+ CertAddStoreToCollection(world, engine->hWorld, 0, 0);
+ if (cert->hCertStore)
+ CertAddStoreToCollection(world, cert->hCertStore, 0, 0);
+ if (hAdditionalStore)
+ CertAddStoreToCollection(world, hAdditionalStore, 0, 0);
+ chain = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
+ if (chain)
+ {
+ memset(chain, 0, sizeof(CERT_SIMPLE_CHAIN));
+ chain->cbSize = sizeof(CERT_SIMPLE_CHAIN);
+ ret = CRYPT_AddCertToSimpleChain(chain, cert, 0);
+ while (ret && !CRYPT_IsCertificateSelfSigned(cert))
+ {
+ DWORD flags;
+ PCCERT_CONTEXT issuer = CRYPT_GetIssuerFromStore(world, cert,
+ &flags);
+
+ if (issuer)
+ {
+ ret = CRYPT_AddCertToSimpleChain(chain, issuer, flags);
+ cert = issuer;
+ }
+ else
+ {
+ TRACE("Couldn't find issuer, aborting chain creation\n");
+ ret = FALSE;
+ }
+ }
+ if (ret)
+ {
+ PCCERT_CONTEXT root = chain->rgpElement[chain->cElement - 1]->
+ pCertContext;
+
+ if (!(ret = CRYPT_IsCertificateSelfSigned(root)))
+ TRACE("Last certificate is not self-signed\n");
+ else
+ {
+ chain->rgpElement[chain->cElement - 1]->TrustStatus.dwInfoStatus
+ |= CERT_TRUST_IS_SELF_SIGNED;
+ if (!(ret = CryptVerifyCertificateSignatureEx(0,
+ root->dwCertEncodingType, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
+ (void *)root, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root,
+ 0, NULL)))
+ TRACE("Last certificate's signature is invalid\n");
+ }
+ if (ret)
+ {
+ BYTE hash[20];
+ DWORD size = sizeof(hash);
+ CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
+ PCCERT_CONTEXT trustedRoot;
+
+ CertGetCertificateContextProperty(root, CERT_HASH_PROP_ID, hash,
+ &size);
+ trustedRoot = CertFindCertificateInStore(engine->hRoot,
+ root->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob, NULL);
+ if (!trustedRoot)
+ chain->TrustStatus.dwErrorStatus |=
+ CERT_TRUST_IS_UNTRUSTED_ROOT;
+ else
+ CertFreeCertificateContext(trustedRoot);
+ }
+ }
+ if (!ret)
+ {
+ CRYPT_FreeSimpleChain(chain);
+ chain = NULL;
+ }
+ *ppChain = chain;
+ }
+ CertCloseStore(world, 0);
+ return ret;
+}
+
+typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
+ DWORD cbSize;
+ CERT_USAGE_MATCH RequestedUsage;
+} CERT_CHAIN_PARA_NO_EXTRA_FIELDS, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS;
+
+typedef struct _CERT_CHAIN_PARA_EXTRA_FIELDS {
+ DWORD cbSize;
+ CERT_USAGE_MATCH RequestedUsage;
+ CERT_USAGE_MATCH RequestedIssuancePolicy;
+ DWORD dwUrlRetrievalTimeout;
+ BOOL fCheckRevocationFreshnessTime;
+ DWORD dwRevocationFreshnessTime;
+} CERT_CHAIN_PARA_EXTRA_FIELDS, *PCERT_CHAIN_PARA_EXTRA_FIELDS;
+
BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
PCERT_CHAIN_PARA pChainPara, DWORD dwFlags, LPVOID pvReserved,
PCCERT_CHAIN_CONTEXT* ppChainContext)
{
- FIXME("(%p, %p, %p, %p, %p, 0x%08X, %p, %p): stub\n", hChainEngine,
- pCertContext, pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved,
- ppChainContext);
+ PCERT_SIMPLE_CHAIN simpleChain = NULL;
+ BOOL ret;
+ TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
+ pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext);
+
+ if (!pChainPara)
+ {
+ SetLastError(E_INVALIDARG);
+ return FALSE;
+ }
if (ppChainContext)
*ppChainContext = NULL;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
+ if (!hChainEngine)
+ hChainEngine = CRYPT_GetDefaultChainEngine();
+ /* FIXME: what about HCCE_LOCAL_MACHINE? */
+ /* FIXME: pChainPara is for now ignored */
+ /* FIXME: only simple chains are supported for now, as CTLs aren't
+ * supported yet.
+ */
+ if ((ret = CRYPT_BuildSimpleChain(hChainEngine, pCertContext, pTime,
+ hAdditionalStore, &simpleChain)))
+ {
+ PCertificateChain chain = CryptMemAlloc(sizeof(CertificateChain));
+
+ if (chain)
+ {
+ chain->ref = 1;
+ chain->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
+ memcpy(&chain->context.TrustStatus, &simpleChain->TrustStatus,
+ sizeof(CERT_TRUST_STATUS));
+ chain->context.cChain = 1;
+ chain->context.rgpChain = CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN));
+ chain->context.rgpChain[0] = simpleChain;
+ chain->context.cLowerQualityChainContext = 0;
+ chain->context.rgpLowerQualityChainContext = NULL;
+ chain->context.fHasRevocationFreshnessTime = FALSE;
+ chain->context.dwRevocationFreshnessTime = 0;
+ }
+ else
+ ret = FALSE;
+ if (ppChainContext)
+ *ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
+ else
+ CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT)chain);
+ }
+ TRACE("returning %d\n", ret);
+ return ret;
+}
+
+static void CRYPT_FreeChainContext(PCertificateChain chain)
+{
+ DWORD i;
+
+ /* Note the chain's rgpLowerQualityChainContext isn't freed, but
+ * it's never set, either.
+ */
+ for (i = 0; i < chain->context.cChain; i++)
+ CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
+ CryptMemFree(chain->context.rgpChain);
+ CryptMemFree(chain);
}
void WINAPI CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext)
{
- FIXME("(%p): stub\n", pChainContext);
+ PCertificateChain chain = (PCertificateChain)pChainContext;
+
+ TRACE("(%p)\n", pChainContext);
+
+ if (chain)
+ {
+ if (InterlockedDecrement(&chain->ref) == 0)
+ CRYPT_FreeChainContext(chain);
+ }
}
diff --git a/dlls/crypt32/tests/chain.c b/dlls/crypt32/tests/chain.c
index 2347fee..9b8f954 100644
--- a/dlls/crypt32/tests/chain.c
+++ b/dlls/crypt32/tests/chain.c
@@ -492,12 +492,10 @@ static void testGetCertChain(void)
/* Basic parameter checks */
ret = CertGetCertificateChain(NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL);
- todo_wine
ok(!ret && GetLastError() == E_INVALIDARG,
"Expected E_INVALIDARG, got %08x\n", GetLastError());
ret = CertGetCertificateChain(NULL, NULL, NULL, NULL, NULL, 0, NULL,
&chain);
- todo_wine
ok(!ret && GetLastError() == E_INVALIDARG,
"Expected E_INVALIDARG, got %08x\n", GetLastError());
/* Crash
@@ -509,7 +507,6 @@ static void testGetCertChain(void)
sizeof(bigCert));
todo_wine
ret = CertGetCertificateChain(NULL, cert, NULL, NULL, NULL, 0, NULL, NULL);
- todo_wine
ok(!ret && GetLastError() == E_INVALIDARG,
"Expected E_INVALIDARG, got %08x\n", GetLastError());
/* Crash
@@ -530,14 +527,11 @@ static void testGetCertChain(void)
cert = CertCreateCertificateContext(X509_ASN_ENCODING, selfSignedCert,
sizeof(selfSignedCert));
ret = CertGetCertificateChain(NULL, cert, NULL, NULL, NULL, 0, NULL, NULL);
- todo_wine
ok(!ret && GetLastError() == E_INVALIDARG,
"Expected E_INVALIDARG, got %08x\n", GetLastError());
ret = CertGetCertificateChain(NULL, cert, NULL, NULL, ¶, 0, NULL,
&chain);
- todo_wine
ok(ret, "CertGetCertificateChain failed: %08x\n", GetLastError());
- todo_wine
ok(chain != NULL, "Expected a chain\n");
if (chain)
{
@@ -564,7 +558,6 @@ static void testGetCertChain(void)
ok(ret, "CertCreateCertificateChainEngine failed: %08x\n", GetLastError());
ret = CertGetCertificateChain(engine, cert, NULL, NULL, ¶, 0, NULL,
&chain);
- todo_wine
ok(chain != NULL, "Expected a chain\n");
if (chain)
{
@@ -625,14 +618,12 @@ static void testGetCertChain(void)
else
ret = CertGetCertificateChain(NULL, cert, NULL,
config.hRestrictedRoot, ¶, 0, NULL, &chain);
- todo_wine
ok(ret, "CertGetCertificateChain failed: %08x\n", GetLastError());
if (ret)
{
/* Since there's no guarantee the Verisign cert is in the Root
* store, either error is acceptable.
*/
- todo_wine
ok(chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR ||
chain->TrustStatus.dwErrorStatus == CERT_TRUST_IS_UNTRUSTED_ROOT,
"Expected no error or CERT_TRUST_IS_UNTRUSTED_ROOT, got %08x\n",
--
1.4.1
More information about the wine-patches
mailing list