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); |