Module: wine Branch: master Commit: 1a392e1a3045ba827e24b4940bf64334daefc690 URL: http://source.winehq.org/git/wine.git/?a=commit;h=1a392e1a3045ba827e24b4940b...
Author: Juan Lang juan.lang@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);