Chromium Code Reviews| Index: net/base/x509_certificate_win.cc |
| diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc |
| index 9e018fdde6564e76aee70b28754af7936de735b8..789766d0eb19f9b0daa4e4f6de9a5e42b5f47a5a 100644 |
| --- a/net/base/x509_certificate_win.cc |
| +++ b/net/base/x509_certificate_win.cc |
| @@ -4,6 +4,7 @@ |
| #include "net/base/x509_certificate.h" |
| +#include "base/crypto/scoped_capi_types.h" |
| #include "base/logging.h" |
| #include "base/pickle.h" |
| #include "base/string_tokenizer.h" |
| @@ -14,6 +15,7 @@ |
| #include "net/base/ev_root_ca_metadata.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/scoped_cert_chain_context.h" |
| +#include "net/base/temporary_root_certs.h" |
| #pragma comment(lib, "crypt32.lib") |
| @@ -23,6 +25,11 @@ namespace net { |
| namespace { |
| +typedef base::ScopedCAPIHandle< |
| + HCERTSTORE, |
| + base::CAPIDestroyerWithFlags<HCERTSTORE, |
| + CertCloseStore, 0> > ScopedHCERTSTORE; |
| + |
| //----------------------------------------------------------------------------- |
| // TODO(wtc): This is a copy of the MapSecurityError function in |
| @@ -291,6 +298,58 @@ void GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context, |
| } |
| } |
| +void FixTestRootTrust(PCERT_CHAIN_CONTEXT chain_context) { |
|
bulach
2010/11/09 16:21:09
same as above, I think this should be exposed some
|
| + if ((chain_context->TrustStatus.dwErrorStatus & |
| + CERT_TRUST_IS_UNTRUSTED_ROOT) == 0) |
| + return; // Trusted certificate - nothing to fix. |
| + |
| + TemporaryRootCerts* temporary_roots = TemporaryRootCerts::GetInstance(); |
| + if (temporary_roots->IsEmpty()) |
| + return; // No need to scan - no temporary trusted certificates. |
| + |
| + // Windows does not support application-level trusts until Win 7, via |
| + // CERT_CHAIN_ENGINE_CONFIG.hExclusiveRoot. Because of this, a messy, |
| + // manual, brute-force method is used for unit tests. Look through every |
| + // chain on |chain_context|, looking for a chain which contains one of the |
| + // trusted certificates. If a matching certificate is found, unset the |
| + // three status-bits that Windows sets when an untrusted root is found. |
| + // Any other failure states are left unmodified, so that situations like |
| + // name or date mismatches are properly reported. |
| + for (DWORD chain_index = 0; chain_index < chain_context->cChain; |
| + ++chain_index) { |
| + PCERT_SIMPLE_CHAIN chain = chain_context->rgpChain[chain_index]; |
| + // Scan through all the certificates, rather than just the root, since |
| + // an RFC 3280/5280 trust anchor may be any certificate in the chain, not |
| + // just the root certificate. |
| + for (DWORD element_index = 0; element_index < chain->cElement; |
| + ++element_index) { |
| + PCERT_CHAIN_ELEMENT element = chain->rgpElement[element_index]; |
| + PCCERT_CONTEXT cert = CertFindCertificateInStore( |
| + temporary_roots->temporary_roots(), |
| + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_EXISTING, |
| + element->pCertContext, NULL); |
| + if (cert != NULL) { |
| + CertFreeCertificateContext(cert); |
| + |
| + // Unset both the element status and the overall chain status, in the |
| + // event a Windows function drills down into the chain. |
| + if (element->TrustStatus.dwErrorStatus & |
| + CERT_TRUST_IS_UNTRUSTED_ROOT) { |
| + element->TrustStatus.dwErrorStatus &= |
| + ~(CERT_TRUST_IS_UNTRUSTED_ROOT | |
| + CERT_TRUST_REVOCATION_STATUS_UNKNOWN | |
| + CERT_TRUST_IS_OFFLINE_REVOCATION); |
| + chain_context->TrustStatus.dwErrorStatus &= |
| + ~(CERT_TRUST_IS_UNTRUSTED_ROOT | |
| + CERT_TRUST_REVOCATION_STATUS_UNKNOWN | |
| + CERT_TRUST_IS_OFFLINE_REVOCATION); |
| + return; |
| + } |
| + } |
| + } |
| + } |
| +} |
| + |
| // Decodes the cert's certificatePolicies extension into a CERT_POLICIES_INFO |
| // structure and stores it in *output. |
| void GetCertPoliciesInfo(PCCERT_CONTEXT cert, |
| @@ -605,6 +664,36 @@ int X509Certificate::Verify(const std::string& hostname, |
| } |
| } |
| + // Temporary roots may not exist in any system store, and therefore may not |
| + // be available for certificate chain building. Create a certificate store |
| + // collection containing both the certificate chain (the cert_handle's |
| + // HCERTSTORE) and the temporary root's HCERTSTORE, so that Windows will |
| + // search both. |
| + TemporaryRootCerts* temporary_roots = TemporaryRootCerts::GetInstance(); |
| + HCERTSTORE collection_store = |
| + temporary_roots->IsEmpty() ? NULL : |
| + CertOpenStore(CERT_STORE_PROV_COLLECTION, |
| + 0, NULL, 0, NULL); |
| + if (collection_store) { |
| + // Add the temporary roots with priority 0, so that certificates from the |
| + // temporary roots will be of a higher priority for chain building. If |
| + // they were a lower priority, it's possible a different root certificate |
| + // than the one in the temporary roots will be used, which would cause |
| + // the detection logic in FixTestRootTrust() to fail. |
| + BOOL ok = CertAddStoreToCollection(collection_store, |
| + temporary_roots->temporary_roots(), |
| + 0, 0); |
| + if (ok) |
| + ok = CertAddStoreToCollection(collection_store, |
| + cert_handle_->hCertStore, 0, 1); |
| + if (!ok) { |
| + PLOG(ERROR) << "Unable to create temporary linked certificate store"; |
| + CertCloseStore(collection_store, 0); |
| + collection_store = NULL; |
| + } |
| + } |
| + ScopedHCERTSTORE scoped_collection_store(collection_store); |
| + |
| PCCERT_CHAIN_CONTEXT chain_context; |
| // IE passes a non-NULL pTime argument that specifies the current system |
| // time. IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the |
| @@ -613,7 +702,8 @@ int X509Certificate::Verify(const std::string& hostname, |
| NULL, // default chain engine, HCCE_CURRENT_USER |
| cert_handle_, |
| NULL, // current system time |
| - cert_handle_->hCertStore, // search this store |
| + scoped_collection_store ? scoped_collection_store : |
|
wtc
2010/11/16 23:24:01
Nit: use a local variable for this, so that we don
|
| + cert_handle_->hCertStore, |
| &chain_para, |
| chain_flags, |
| NULL, // reserved |
| @@ -630,7 +720,8 @@ int X509Certificate::Verify(const std::string& hostname, |
| NULL, // default chain engine, HCCE_CURRENT_USER |
| cert_handle_, |
| NULL, // current system time |
| - cert_handle_->hCertStore, // search this store |
| + scoped_collection_store ? scoped_collection_store : |
| + cert_handle_->hCertStore, |
| &chain_para, |
| chain_flags, |
| NULL, // reserved |
| @@ -642,6 +733,8 @@ int X509Certificate::Verify(const std::string& hostname, |
| GetCertChainInfo(chain_context, verify_result); |
| + FixTestRootTrust(const_cast<PCERT_CHAIN_CONTEXT>(chain_context)); |
|
wtc
2010/11/16 23:24:01
BUG: Move this up, so that we call FixTestRootTrus
|
| + |
| verify_result->cert_status |= MapCertChainErrorStatusToCertStatus( |
| chain_context->TrustStatus.dwErrorStatus); |