Juan Lang : crypt32: Support checking the requested usage for a chain.

Alexandre Julliard julliard at winehq.org
Mon Nov 23 08:49:48 CST 2009


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

Author: Juan Lang <juan.lang at gmail.com>
Date:   Fri Nov 20 12:08:38 2009 -0800

crypt32: Support checking the requested usage for a chain.

---

 dlls/crypt32/chain.c       |  106 ++++++++++++++++++++++++++++++++++++++++++++
 dlls/crypt32/tests/chain.c |    2 -
 2 files changed, 106 insertions(+), 2 deletions(-)

diff --git a/dlls/crypt32/chain.c b/dlls/crypt32/chain.c
index a53b030..de482ae 100644
--- a/dlls/crypt32/chain.c
+++ b/dlls/crypt32/chain.c
@@ -2454,6 +2454,111 @@ static void CRYPT_VerifyChainRevocation(PCERT_CHAIN_CONTEXT chain,
     }
 }
 
+static void CRYPT_CheckUsages(PCERT_CHAIN_CONTEXT chain,
+ const CERT_CHAIN_PARA *pChainPara)
+{
+    if (pChainPara->cbSize >= sizeof(CERT_CHAIN_PARA_NO_EXTRA_FIELDS) &&
+     pChainPara->RequestedUsage.Usage.cUsageIdentifier)
+    {
+        PCCERT_CONTEXT endCert;
+        PCERT_EXTENSION ext;
+        BOOL validForUsage;
+
+        /* A chain, if created, always includes the end certificate */
+        endCert = chain->rgpChain[0]->rgpElement[0]->pCertContext;
+        /* The extended key usage extension specifies how a certificate's
+         * public key may be used.  From RFC 5280, section 4.2.1.12:
+         * "This extension indicates one or more purposes for which the
+         *  certified public key may be used, in addition to or in place of the
+         *  basic purposes indicated in the key usage extension."
+         * If the extension is present, it only satisfies the requested usage
+         * if that usage is included in the extension:
+         * "If the extension is present, then the certificate MUST only be used
+         *  for one of the purposes indicated."
+         * There is also the special anyExtendedKeyUsage OID, but it doesn't
+         * have to be respected:
+         * "Applications that require the presence of a particular purpose
+         *  MAY reject certificates that include the anyExtendedKeyUsage OID
+         *  but not the particular OID expected for the application."
+         * For now, I'm being more conservative and ignoring the presence of
+         * the anyExtendedKeyUsage OID.
+         */
+        if ((ext = CertFindExtension(szOID_ENHANCED_KEY_USAGE,
+         endCert->pCertInfo->cExtension, endCert->pCertInfo->rgExtension)))
+        {
+            const CERT_ENHKEY_USAGE *requestedUsage =
+             &pChainPara->RequestedUsage.Usage;
+            CERT_ENHKEY_USAGE *usage;
+            DWORD size;
+
+            if (CryptDecodeObjectEx(X509_ASN_ENCODING,
+             X509_ENHANCED_KEY_USAGE, ext->Value.pbData, ext->Value.cbData,
+             CRYPT_DECODE_ALLOC_FLAG, NULL, &usage, &size))
+            {
+                if (pChainPara->RequestedUsage.dwType == USAGE_MATCH_TYPE_AND)
+                {
+                    DWORD i, j;
+
+                    /* For AND matches, all usages must be present */
+                    validForUsage = TRUE;
+                    for (i = 0; validForUsage &&
+                     i < requestedUsage->cUsageIdentifier; i++)
+                    {
+                        BOOL match = FALSE;
+
+                        for (j = 0; !match && j < usage->cUsageIdentifier; j++)
+                            match = !strcmp(usage->rgpszUsageIdentifier[j],
+                             requestedUsage->rgpszUsageIdentifier[i]);
+                        if (!match)
+                            validForUsage = FALSE;
+                    }
+                }
+                else
+                {
+                    DWORD i, j;
+
+                    /* For OR matches, any matching usage suffices */
+                    validForUsage = FALSE;
+                    for (i = 0; !validForUsage &&
+                     i < requestedUsage->cUsageIdentifier; i++)
+                    {
+                        for (j = 0; !validForUsage &&
+                         j < usage->cUsageIdentifier; j++)
+                            validForUsage =
+                             !strcmp(usage->rgpszUsageIdentifier[j],
+                             requestedUsage->rgpszUsageIdentifier[i]);
+                    }
+                }
+                LocalFree(usage);
+            }
+            else
+                validForUsage = FALSE;
+        }
+        else
+        {
+            /* If the extension isn't present, any interpretation is valid:
+             * "Certificate using applications MAY require that the extended
+             *  key usage extension be present and that a particular purpose
+             *  be indicated in order for the certificate to be acceptable to
+             *  that application."
+             * For now I'm being more conservative and disallowing it.
+             */
+            WARN_(chain)("requested usage from a certificate with no usages\n");
+            validForUsage = FALSE;
+        }
+        if (!validForUsage)
+        {
+            chain->TrustStatus.dwErrorStatus |=
+             CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
+            chain->rgpChain[0]->rgpElement[0]->TrustStatus.dwErrorStatus |=
+             CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
+        }
+    }
+    if (pChainPara->cbSize >= sizeof(CERT_CHAIN_PARA) &&
+     pChainPara->RequestedIssuancePolicy.Usage.cUsageIdentifier)
+        FIXME("unimplemented for RequestedIssuancePolicy\n");
+}
+
 static void dump_usage_match(LPCSTR name, const CERT_USAGE_MATCH *usageMatch)
 {
     if (usageMatch->Usage.cUsageIdentifier)
@@ -2534,6 +2639,7 @@ BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
             CRYPT_FreeLowerQualityChains(chain);
         pChain = (PCERT_CHAIN_CONTEXT)chain;
         CRYPT_VerifyChainRevocation(pChain, pTime, pChainPara, dwFlags);
+        CRYPT_CheckUsages(pChain, pChainPara);
         if (ppChainContext)
             *ppChainContext = pChain;
         else
diff --git a/dlls/crypt32/tests/chain.c b/dlls/crypt32/tests/chain.c
index b21fba6..d41a8ca 100644
--- a/dlls/crypt32/tests/chain.c
+++ b/dlls/crypt32/tests/chain.c
@@ -3853,7 +3853,6 @@ static void testGetCertChain(void)
     ok(ret, "CertGetCertificateChain failed: %08x\n", GetLastError());
     if (ret)
     {
-        todo_wine
         ok(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE,
          "expected CERT_TRUST_IS_NOT_VALID_FOR_USAGE\n");
         CertFreeCertificateChain(chain);
@@ -3877,7 +3876,6 @@ static void testGetCertChain(void)
     ok(ret, "CertGetCertificateChain failed: %08x\n", GetLastError());
     if (ret)
     {
-        todo_wine
         ok(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE,
          "expected CERT_TRUST_IS_NOT_VALID_FOR_USAGE\n");
         CertFreeCertificateChain(chain);




More information about the wine-cvs mailing list