crypt32: Support checking the requested usage for a chain.

This commit is contained in:
Juan Lang 2009-11-20 12:08:38 -08:00 committed by Alexandre Julliard
parent 30de103485
commit 1a392e1a30
2 changed files with 106 additions and 2 deletions

View File

@ -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) static void dump_usage_match(LPCSTR name, const CERT_USAGE_MATCH *usageMatch)
{ {
if (usageMatch->Usage.cUsageIdentifier) if (usageMatch->Usage.cUsageIdentifier)
@ -2534,6 +2639,7 @@ BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
CRYPT_FreeLowerQualityChains(chain); CRYPT_FreeLowerQualityChains(chain);
pChain = (PCERT_CHAIN_CONTEXT)chain; pChain = (PCERT_CHAIN_CONTEXT)chain;
CRYPT_VerifyChainRevocation(pChain, pTime, pChainPara, dwFlags); CRYPT_VerifyChainRevocation(pChain, pTime, pChainPara, dwFlags);
CRYPT_CheckUsages(pChain, pChainPara);
if (ppChainContext) if (ppChainContext)
*ppChainContext = pChain; *ppChainContext = pChain;
else else

View File

@ -3853,7 +3853,6 @@ static void testGetCertChain(void)
ok(ret, "CertGetCertificateChain failed: %08x\n", GetLastError()); ok(ret, "CertGetCertificateChain failed: %08x\n", GetLastError());
if (ret) if (ret)
{ {
todo_wine
ok(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE, ok(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE,
"expected CERT_TRUST_IS_NOT_VALID_FOR_USAGE\n"); "expected CERT_TRUST_IS_NOT_VALID_FOR_USAGE\n");
CertFreeCertificateChain(chain); CertFreeCertificateChain(chain);
@ -3877,7 +3876,6 @@ static void testGetCertChain(void)
ok(ret, "CertGetCertificateChain failed: %08x\n", GetLastError()); ok(ret, "CertGetCertificateChain failed: %08x\n", GetLastError());
if (ret) if (ret)
{ {
todo_wine
ok(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE, ok(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE,
"expected CERT_TRUST_IS_NOT_VALID_FOR_USAGE\n"); "expected CERT_TRUST_IS_NOT_VALID_FOR_USAGE\n");
CertFreeCertificateChain(chain); CertFreeCertificateChain(chain);