Juan Lang : crypt32: Implement cert chain revocation checking.

Alexandre Julliard julliard at winehq.org
Wed Oct 24 11:04:41 CDT 2007


Module: wine
Branch: master
Commit: 912c3e609bafdec7eb39c06fb6a7337b61dfde23
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=912c3e609bafdec7eb39c06fb6a7337b61dfde23

Author: Juan Lang <juan.lang at gmail.com>
Date:   Tue Oct 23 13:03:26 2007 -0700

crypt32: Implement cert chain revocation checking.

---

 dlls/crypt32/chain.c |  137 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 125 insertions(+), 12 deletions(-)

diff --git a/dlls/crypt32/chain.c b/dlls/crypt32/chain.c
index 92043b1..068e039 100644
--- a/dlls/crypt32/chain.c
+++ b/dlls/crypt32/chain.c
@@ -20,6 +20,8 @@
 #define NONAMELESSUNION
 #include "windef.h"
 #include "winbase.h"
+#define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
+#define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
 #include "wincrypt.h"
 #include "wine/debug.h"
 #include "wine/unicode.h"
@@ -775,7 +777,6 @@ static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine,
          CERT_TRUST_IS_SELF_SIGNED | CERT_TRUST_HAS_NAME_MATCH_ISSUER;
         CRYPT_CheckRootCert(engine->hRoot, rootElement);
     }
-    /* FIXME: check revocation of every cert with CertVerifyRevocation */
     CRYPT_CombineTrustStatus(&chain->TrustStatus, &rootElement->TrustStatus);
 }
 
@@ -1307,19 +1308,123 @@ static BOOL CRYPT_AddAlternateChainToChain(PCertificateChain chain,
     return ret;
 }
 
+static PCERT_CHAIN_ELEMENT CRYPT_FindIthElementInChain(
+ PCERT_CHAIN_CONTEXT chain, DWORD i)
+{
+    DWORD j, iElement;
+    PCERT_CHAIN_ELEMENT element = NULL;
+
+    for (j = 0, iElement = 0; !element && j < chain->cChain; j++)
+    {
+        if (iElement + chain->rgpChain[j]->cElement < i)
+            iElement += chain->rgpChain[j]->cElement;
+        else
+            element = chain->rgpChain[j]->rgpElement[i - iElement];
+    }
+    return element;
+}
+
 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;
+static void CRYPT_VerifyChainRevocation(PCERT_CHAIN_CONTEXT chain,
+ LPFILETIME pTime, PCERT_CHAIN_PARA pChainPara, DWORD chainFlags)
+{
+    DWORD cContext;
+
+    if (chainFlags & CERT_CHAIN_REVOCATION_CHECK_END_CERT)
+        cContext = 1;
+    else if ((chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN) ||
+     (chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT))
+    {
+        DWORD i;
+
+        for (i = 0, cContext = 0; i < chain->cChain; i++)
+        {
+            if (i < chain->cChain - 1 ||
+             chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN)
+                cContext += chain->rgpChain[i]->cElement;
+            else
+                cContext += chain->rgpChain[i]->cElement - 1;
+        }
+    }
+    else
+        cContext = 0;
+    if (cContext)
+    {
+        PCCERT_CONTEXT *contexts =
+         CryptMemAlloc(cContext * sizeof(PCCERT_CONTEXT *));
+
+        if (contexts)
+        {
+            DWORD i, j, iContext, revocationFlags;
+            CERT_REVOCATION_PARA revocationPara = { sizeof(revocationPara), 0 };
+            CERT_REVOCATION_STATUS revocationStatus =
+             { sizeof(revocationStatus), 0 };
+            BOOL ret;
+
+            for (i = 0, iContext = 0; iContext < cContext && i < chain->cChain;
+             i++)
+            {
+                for (j = 0; iContext < cContext &&
+                 j < chain->rgpChain[i]->cElement; j++)
+                    contexts[iContext++] =
+                     chain->rgpChain[i]->rgpElement[j]->pCertContext;
+            }
+            revocationFlags = CERT_VERIFY_REV_CHAIN_FLAG;
+            if (chainFlags & CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY)
+                revocationFlags |= CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION;
+            if (chainFlags & CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT)
+                revocationFlags |= CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG;
+            revocationPara.pftTimeToUse = pTime;
+            if (pChainPara->cbSize == sizeof(CERT_CHAIN_PARA))
+            {
+                revocationPara.dwUrlRetrievalTimeout =
+                 pChainPara->dwUrlRetrievalTimeout;
+                revocationPara.fCheckFreshnessTime =
+                 pChainPara->fCheckRevocationFreshnessTime;
+                revocationPara.dwFreshnessTime =
+                 pChainPara->dwRevocationFreshnessTime;
+            }
+            ret = CertVerifyRevocation(X509_ASN_ENCODING,
+             CERT_CONTEXT_REVOCATION_TYPE, cContext, (void **)contexts,
+             revocationFlags, &revocationPara, &revocationStatus);
+            if (!ret)
+            {
+                PCERT_CHAIN_ELEMENT element =
+                 CRYPT_FindIthElementInChain(chain, revocationStatus.dwIndex);
+                DWORD error;
+
+                switch (revocationStatus.dwError)
+                {
+                case CRYPT_E_NO_REVOCATION_CHECK:
+                case CRYPT_E_NO_REVOCATION_DLL:
+                case CRYPT_E_NOT_IN_REVOCATION_DATABASE:
+                    error = CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
+                    break;
+                case CRYPT_E_REVOCATION_OFFLINE:
+                    error = CERT_TRUST_IS_OFFLINE_REVOCATION;
+                    break;
+                case CRYPT_E_REVOKED:
+                    error = CERT_TRUST_IS_REVOKED;
+                    break;
+                default:
+                    WARN("unmapped error %08x\n", revocationStatus.dwError);
+                    error = 0;
+                }
+                if (element)
+                {
+                    /* FIXME: set element's pRevocationInfo member */
+                    element->TrustStatus.dwErrorStatus |= error;
+                }
+                chain->TrustStatus.dwErrorStatus |= error;
+            }
+            CryptMemFree(contexts);
+        }
+    }
+}
 
 BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
  PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
@@ -1344,15 +1449,21 @@ BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
         SetLastError(ERROR_INVALID_DATA);
         return FALSE;
     }
+    if (pChainPara->cbSize != sizeof(CERT_CHAIN_PARA_NO_EXTRA_FIELDS) &&
+     pChainPara->cbSize != sizeof(CERT_CHAIN_PARA))
+    {
+        SetLastError(E_INVALIDARG);
+        return FALSE;
+    }
     if (!hChainEngine)
         hChainEngine = CRYPT_GetDefaultChainEngine();
     /* FIXME: what about HCCE_LOCAL_MACHINE? */
-    /* FIXME: pChainPara is for now ignored */
     ret = CRYPT_BuildCandidateChainFromCert(hChainEngine, pCertContext, pTime,
      hAdditionalStore, &chain);
     if (ret)
     {
         PCertificateChain alternate = NULL;
+        PCERT_CHAIN_CONTEXT pChain;
 
         do {
             alternate = CRYPT_BuildAlternateContextFromChain(hChainEngine,
@@ -1368,10 +1479,12 @@ BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
         chain = CRYPT_ChooseHighestQualityChain(chain);
         if (!(dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS))
             CRYPT_FreeLowerQualityChains(chain);
+        pChain = (PCERT_CHAIN_CONTEXT)chain;
+        CRYPT_VerifyChainRevocation(pChain, pTime, pChainPara, dwFlags);
         if (ppChainContext)
-            *ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
+            *ppChainContext = pChain;
         else
-            CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT)chain);
+            CertFreeCertificateChain(pChain);
     }
     TRACE("returning %d\n", ret);
     return ret;




More information about the wine-cvs mailing list