diff options
author | joaodasilva@chromium.org <joaodasilva@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-23 19:11:24 +0000 |
---|---|---|
committer | joaodasilva@chromium.org <joaodasilva@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-23 19:11:24 +0000 |
commit | ef15512b2c9044e68d77d6f0bd4344e355163358 (patch) | |
tree | a2413642c294eb7d1903f8f47348717d6287b20a | |
parent | 3166220ef3fe1fc31dabed475264e8fc70bf8c75 (diff) | |
download | chromium_src-ef15512b2c9044e68d77d6f0bd4344e355163358.zip chromium_src-ef15512b2c9044e68d77d6f0bd4344e355163358.tar.gz chromium_src-ef15512b2c9044e68d77d6f0bd4344e355163358.tar.bz2 |
Added a certificate trust anchor provider.
This provider can return a list of additional certificates to trust when
a trust chain is being built to verify trust in a certain certificate.
This enables the chrome layer to pass in certificates from the ONC policy that
should be trusted only for the user session, without having to import them into
the user's NSS database.
BUG=216495
Review URL: https://chromiumcodereview.appspot.com/12553012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190070 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/base/cert_trust_anchor_provider.h | 33 | ||||
-rw-r--r-- | net/base/cert_verify_proc.cc | 4 | ||||
-rw-r--r-- | net/base/cert_verify_proc.h | 12 | ||||
-rw-r--r-- | net/base/cert_verify_proc_android.cc | 16 | ||||
-rw-r--r-- | net/base/cert_verify_proc_android.h | 3 | ||||
-rw-r--r-- | net/base/cert_verify_proc_mac.cc | 16 | ||||
-rw-r--r-- | net/base/cert_verify_proc_mac.h | 3 | ||||
-rw-r--r-- | net/base/cert_verify_proc_nss.cc | 73 | ||||
-rw-r--r-- | net/base/cert_verify_proc_nss.h | 3 | ||||
-rw-r--r-- | net/base/cert_verify_proc_openssl.cc | 16 | ||||
-rw-r--r-- | net/base/cert_verify_proc_openssl.h | 3 | ||||
-rw-r--r-- | net/base/cert_verify_proc_unittest.cc | 103 | ||||
-rw-r--r-- | net/base/cert_verify_proc_win.cc | 16 | ||||
-rw-r--r-- | net/base/cert_verify_proc_win.h | 3 | ||||
-rw-r--r-- | net/base/multi_threaded_cert_verifier.cc | 68 | ||||
-rw-r--r-- | net/base/multi_threaded_cert_verifier.h | 46 | ||||
-rw-r--r-- | net/base/multi_threaded_cert_verifier_unittest.cc | 114 | ||||
-rw-r--r-- | net/base/nss_cert_database_unittest.cc | 27 | ||||
-rw-r--r-- | net/net.gyp | 1 |
19 files changed, 448 insertions, 112 deletions
diff --git a/net/base/cert_trust_anchor_provider.h b/net/base/cert_trust_anchor_provider.h new file mode 100644 index 0000000..6a95068 --- /dev/null +++ b/net/base/cert_trust_anchor_provider.h @@ -0,0 +1,33 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_CERT_TRUST_ANCHOR_PROVIDER_H_ +#define NET_BASE_CERT_TRUST_ANCHOR_PROVIDER_H_ + +#include <vector> + +#include "base/memory/ref_counted.h" +#include "net/base/net_export.h" + +namespace net { + +class X509Certificate; +typedef std::vector<scoped_refptr<X509Certificate> > CertificateList; + +// Interface to retrieve the current list of additional trust anchors. +// This is used by CertVerifier to get a list of anchors to trust in addition to +// the anchors known to the CertVerifier. +class NET_EXPORT CertTrustAnchorProvider { + public: + virtual ~CertTrustAnchorProvider() {} + + // Returns a list of certificates to be used as trust anchors during + // certificate validation, in addition to (eg: the union of) any pre-existing + // or pre-configured trust anchors. + virtual const CertificateList& GetAdditionalTrustAnchors() = 0; +}; + +} // namespace net + +#endif // NET_BASE_CERT_TRUST_ANCHOR_PROVIDER_H_ diff --git a/net/base/cert_verify_proc.cc b/net/base/cert_verify_proc.cc index 7fa8cb4..0c017c5 100644 --- a/net/base/cert_verify_proc.cc +++ b/net/base/cert_verify_proc.cc @@ -74,6 +74,7 @@ int CertVerifyProc::Verify(X509Certificate* cert, const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) { verify_result->Reset(); verify_result->verified_cert = cert; @@ -94,7 +95,8 @@ int CertVerifyProc::Verify(X509Certificate* cert, flags |= CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY; } - int rv = VerifyInternal(cert, hostname, flags, crl_set, verify_result); + int rv = VerifyInternal(cert, hostname, flags, crl_set, + additional_trust_anchors, verify_result); // This check is done after VerifyInternal so that VerifyInternal can fill // in the list of public key hashes. diff --git a/net/base/cert_verify_proc.h b/net/base/cert_verify_proc.h index 475a533..2830874 100644 --- a/net/base/cert_verify_proc.h +++ b/net/base/cert_verify_proc.h @@ -18,6 +18,7 @@ namespace net { class CertVerifyResult; class CRLSet; class X509Certificate; +typedef std::vector<scoped_refptr<X509Certificate> > CertificateList; // Class to perform certificate path building and verification for various // certificate uses. All methods of this class must be thread-safe, as they @@ -49,12 +50,22 @@ class NET_EXPORT CertVerifyProc // // |crl_set| points to an optional CRLSet structure which can be used to // avoid revocation checks over the network. + // + // |additional_trust_anchors| lists certificates that can be trusted when + // building a certificate chain, in addition to the anchors known to the + // implementation. int Verify(X509Certificate* cert, const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result); + // Returns true if the implementation supports passing additional trust + // anchors to the Verify() call. The |additional_trust_anchors| parameter + // passed to Verify() is ignored when this returns false. + virtual bool SupportsAdditionalTrustAnchors() const = 0; + protected: friend class base::RefCountedThreadSafe<CertVerifyProc>; FRIEND_TEST_ALL_PREFIXES(CertVerifyProcTest, DigiNotarCerts); @@ -69,6 +80,7 @@ class NET_EXPORT CertVerifyProc const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) = 0; // Returns true if |cert| is explicitly blacklisted. diff --git a/net/base/cert_verify_proc_android.cc b/net/base/cert_verify_proc_android.cc index cbf9e29..c11e66e 100644 --- a/net/base/cert_verify_proc_android.cc +++ b/net/base/cert_verify_proc_android.cc @@ -77,11 +77,17 @@ CertVerifyProcAndroid::CertVerifyProcAndroid() {} CertVerifyProcAndroid::~CertVerifyProcAndroid() {} -int CertVerifyProcAndroid::VerifyInternal(X509Certificate* cert, - const std::string& hostname, - int flags, - CRLSet* crl_set, - CertVerifyResult* verify_result) { +bool CertVerifyProcAndroid::SupportsAdditionalTrustAnchors() const { + return false; +} + +int CertVerifyProcAndroid::VerifyInternal( + X509Certificate* cert, + const std::string& hostname, + int flags, + CRLSet* crl_set, + const CertificateList& additional_trust_anchors, + CertVerifyResult* verify_result) { if (!cert->VerifyNameMatch(hostname)) verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; diff --git a/net/base/cert_verify_proc_android.h b/net/base/cert_verify_proc_android.h index ea0f5ca..bfd5e12 100644 --- a/net/base/cert_verify_proc_android.h +++ b/net/base/cert_verify_proc_android.h @@ -15,6 +15,8 @@ class CertVerifyProcAndroid : public CertVerifyProc { public: CertVerifyProcAndroid(); + virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE; + protected: virtual ~CertVerifyProcAndroid(); @@ -23,6 +25,7 @@ class CertVerifyProcAndroid : public CertVerifyProc { const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) OVERRIDE; }; diff --git a/net/base/cert_verify_proc_mac.cc b/net/base/cert_verify_proc_mac.cc index 16c2ace..a8f3e28 100644 --- a/net/base/cert_verify_proc_mac.cc +++ b/net/base/cert_verify_proc_mac.cc @@ -347,11 +347,17 @@ CertVerifyProcMac::CertVerifyProcMac() {} CertVerifyProcMac::~CertVerifyProcMac() {} -int CertVerifyProcMac::VerifyInternal(X509Certificate* cert, - const std::string& hostname, - int flags, - CRLSet* crl_set, - CertVerifyResult* verify_result) { +bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const { + return false; +} + +int CertVerifyProcMac::VerifyInternal( + X509Certificate* cert, + const std::string& hostname, + int flags, + CRLSet* crl_set, + const CertificateList& additional_trust_anchors, + CertVerifyResult* verify_result) { ScopedCFTypeRef<CFArrayRef> trust_policies; OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies); if (status) diff --git a/net/base/cert_verify_proc_mac.h b/net/base/cert_verify_proc_mac.h index 8e28a6b..e28ba51 100644 --- a/net/base/cert_verify_proc_mac.h +++ b/net/base/cert_verify_proc_mac.h @@ -15,6 +15,8 @@ class CertVerifyProcMac : public CertVerifyProc { public: CertVerifyProcMac(); + virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE; + protected: virtual ~CertVerifyProcMac(); @@ -23,6 +25,7 @@ class CertVerifyProcMac : public CertVerifyProc { const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) OVERRIDE; }; diff --git a/net/base/cert_verify_proc_nss.cc b/net/base/cert_verify_proc_nss.cc index 630e23f..39c9c68 100644 --- a/net/base/cert_verify_proc_nss.cc +++ b/net/base/cert_verify_proc_nss.cc @@ -39,6 +39,11 @@ #define SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED -8016 #endif +#if NSS_VERSION_NUM < 31402 +// Added in NSS 3.14.2. +#define cert_pi_useOnlyTrustAnchors static_cast<CERTValParamInType>(14) +#endif + namespace net { namespace { @@ -49,6 +54,11 @@ typedef scoped_ptr_malloc< CERT_DestroyCertificatePoliciesExtension> > ScopedCERTCertificatePolicies; +typedef scoped_ptr_malloc< + CERTCertList, + crypto::NSSDestroyer<CERTCertList, CERT_DestroyCertList> > + ScopedCERTCertList; + // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam // array that cvout points to. cvout must be initialized as passed to // CERT_PKIXVerifyCert, so that the array must be terminated with @@ -313,12 +323,15 @@ SECOidTag GetFirstCertPolicy(CERTCertificate* cert_handle); // Verification results are stored in an array of CERTValOutParam. // If policy_oids is not NULL and num_policy_oids is positive, policies // are also checked. +// additional_trust_anchors is an optional list of certificates that can be +// trusted as anchors when building a certificate chain. // Caller must initialize cvout before calling this function. SECStatus PKIXVerifyCert(CERTCertificate* cert_handle, bool check_revocation, bool cert_io_enabled, const SECOidTag* policy_oids, int num_policy_oids, + CERTCertList* additional_trust_anchors, CERTValOutParam* cvout) { bool use_crl = check_revocation; bool use_ocsp = check_revocation; @@ -408,10 +421,10 @@ SECStatus PKIXVerifyCert(CERTCertificate* cert_handle, revocation_flags.chainTests.cert_rev_method_independent_flags = revocation_method_independent_flags; + std::vector<CERTValInParam> cvin; - cvin.reserve(5); + cvin.reserve(7); CERTValInParam in_param; - // No need to set cert_pi_trustAnchors here. in_param.type = cert_pi_revocationFlags; in_param.value.pointer.revocation = &revocation_flags; cvin.push_back(in_param); @@ -421,6 +434,14 @@ SECStatus PKIXVerifyCert(CERTCertificate* cert_handle, in_param.value.array.oids = policy_oids; cvin.push_back(in_param); } + if (additional_trust_anchors) { + in_param.type = cert_pi_trustAnchors; + in_param.value.pointer.chain = additional_trust_anchors; + cvin.push_back(in_param); + in_param.type = cert_pi_useOnlyTrustAnchors; + in_param.value.scalar.b = PR_FALSE; + cvin.push_back(in_param); + } in_param.type = cert_pi_end; cvin.push_back(in_param); @@ -639,7 +660,8 @@ bool VerifyEV(CERTCertificate* cert_handle, int flags, CRLSet* crl_set, EVRootCAMetadata* metadata, - SECOidTag ev_policy_oid) { + SECOidTag ev_policy_oid, + CERTCertList* additional_trust_anchors) { CERTValOutParam cvout[3]; int cvout_index = 0; cvout[cvout_index].type = cert_po_certList; @@ -663,6 +685,7 @@ bool VerifyEV(CERTCertificate* cert_handle, flags & CertVerifier::VERIFY_CERT_IO_ENABLED, &ev_policy_oid, 1, + additional_trust_anchors, cvout); if (status != SECSuccess) return false; @@ -693,17 +716,40 @@ bool VerifyEV(CERTCertificate* cert_handle, return metadata->HasEVPolicyOID(fingerprint, ev_policy_oid); } +CERTCertList* CertificateListToCERTCertList(const CertificateList& list) { + CERTCertList* result = CERT_NewCertList(); + for (size_t i = 0; i < list.size(); ++i) { +#if defined(OS_IOS) + // X509Certificate::os_cert_handle() on iOS is a SecCertificateRef; convert + // it to an NSS CERTCertificate. + CERTCertificate* cert = x509_util_ios::CreateNSSCertHandleFromOSHandle( + list[i]->os_cert_handle()); +#else + CERTCertificate* cert = list[i]->os_cert_handle(); +#endif + CERT_AddCertToListTail(result, CERT_DupCertificate(cert)); + } + return result; +} + } // namespace CertVerifyProcNSS::CertVerifyProcNSS() {} CertVerifyProcNSS::~CertVerifyProcNSS() {} -int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, - const std::string& hostname, - int flags, - CRLSet* crl_set, - CertVerifyResult* verify_result) { +bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const { + // This requires APIs introduced in 3.14.2. + return NSS_VersionCheck("3.14.2"); +} + +int CertVerifyProcNSS::VerifyInternal( + X509Certificate* cert, + const std::string& hostname, + int flags, + CRLSet* crl_set, + const CertificateList& additional_trust_anchors, + CertVerifyResult* verify_result) { #if defined(OS_IOS) // For iOS, the entire chain must be loaded into NSS's in-memory certificate // store. @@ -751,8 +797,14 @@ int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, if (check_revocation) verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; + ScopedCERTCertList trust_anchors; + if (SupportsAdditionalTrustAnchors() && !additional_trust_anchors.empty()) { + trust_anchors.reset( + CertificateListToCERTCertList(additional_trust_anchors)); + } + status = PKIXVerifyCert(cert_handle, check_revocation, cert_io_enabled, - NULL, 0, cvout); + NULL, 0, trust_anchors.get(), cvout); if (status == SECSuccess) { AppendPublicKeyHashes(cvout[cvout_cert_list_index].value.pointer.chain, @@ -800,7 +852,8 @@ int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, return MapCertStatusToNetError(verify_result->cert_status); if ((flags & CertVerifier::VERIFY_EV_CERT) && is_ev_candidate && - VerifyEV(cert_handle, flags, crl_set, metadata, ev_policy_oid)) { + VerifyEV(cert_handle, flags, crl_set, metadata, ev_policy_oid, + trust_anchors.get())) { verify_result->cert_status |= CERT_STATUS_IS_EV; } diff --git a/net/base/cert_verify_proc_nss.h b/net/base/cert_verify_proc_nss.h index a8fa6d7..8dcd235 100644 --- a/net/base/cert_verify_proc_nss.h +++ b/net/base/cert_verify_proc_nss.h @@ -15,6 +15,8 @@ class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc { public: CertVerifyProcNSS(); + virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE; + protected: virtual ~CertVerifyProcNSS(); @@ -23,6 +25,7 @@ class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc { const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) OVERRIDE; }; diff --git a/net/base/cert_verify_proc_openssl.cc b/net/base/cert_verify_proc_openssl.cc index b9cd0b1..964ba26 100644 --- a/net/base/cert_verify_proc_openssl.cc +++ b/net/base/cert_verify_proc_openssl.cc @@ -163,11 +163,17 @@ CertVerifyProcOpenSSL::CertVerifyProcOpenSSL() {} CertVerifyProcOpenSSL::~CertVerifyProcOpenSSL() {} -int CertVerifyProcOpenSSL::VerifyInternal(X509Certificate* cert, - const std::string& hostname, - int flags, - CRLSet* crl_set, - CertVerifyResult* verify_result) { +bool CertVerifyProcOpenSSL::SupportsAdditionalTrustAnchors() const { + return false; +} + +int CertVerifyProcOpenSSL::VerifyInternal( + X509Certificate* cert, + const std::string& hostname, + int flags, + CRLSet* crl_set, + const CertificateList& additional_trust_anchors, + CertVerifyResult* verify_result) { crypto::EnsureOpenSSLInit(); if (!cert->VerifyNameMatch(hostname)) diff --git a/net/base/cert_verify_proc_openssl.h b/net/base/cert_verify_proc_openssl.h index 808b7d1..5777bcc 100644 --- a/net/base/cert_verify_proc_openssl.h +++ b/net/base/cert_verify_proc_openssl.h @@ -14,6 +14,8 @@ class CertVerifyProcOpenSSL : public CertVerifyProc { public: CertVerifyProcOpenSSL(); + virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE; + protected: virtual ~CertVerifyProcOpenSSL(); @@ -22,6 +24,7 @@ class CertVerifyProcOpenSSL : public CertVerifyProc { const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) OVERRIDE; }; diff --git a/net/base/cert_verify_proc_unittest.cc b/net/base/cert_verify_proc_unittest.cc index 165a744..9053de7 100644 --- a/net/base/cert_verify_proc_unittest.cc +++ b/net/base/cert_verify_proc_unittest.cc @@ -7,6 +7,7 @@ #include <vector> #include "base/files/file_path.h" +#include "base/logging.h" #include "base/sha1.h" #include "base/string_number_conversions.h" #include "net/base/asn1_util.h" @@ -51,15 +52,22 @@ class CertVerifyProcTest : public testing::Test { virtual ~CertVerifyProcTest() {} protected: + bool SupportsAdditionalTrustAnchors() { + return verify_proc_->SupportsAdditionalTrustAnchors(); + } + int Verify(X509Certificate* cert, const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) { return verify_proc_->Verify(cert, hostname, flags, crl_set, - verify_result); + additional_trust_anchors, verify_result); } + const CertificateList empty_cert_list_; + private: scoped_refptr<CertVerifyProc> verify_proc_; }; @@ -80,7 +88,7 @@ TEST_F(CertVerifyProcTest, WithoutRevocationChecking) { CertVerifyResult verify_result; EXPECT_EQ(OK, Verify(google_full_chain, "www.google.com", 0 /* flags */, - NULL, &verify_result)); + NULL, empty_cert_list_, &verify_result)); } #if defined(OS_ANDROID) || defined(USE_OPENSSL) @@ -109,7 +117,7 @@ TEST_F(CertVerifyProcTest, MAYBE_EVVerification) { CertVerifyResult verify_result; int flags = CertVerifier::VERIFY_EV_CERT; int error = Verify(comodo_chain, "comodo.com", flags, crl_set.get(), - &verify_result); + empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_IS_EV); } @@ -130,7 +138,7 @@ TEST_F(CertVerifyProcTest, PaypalNullCertParsing) { int flags = 0; CertVerifyResult verify_result; int error = Verify(paypal_null_cert, "www.paypal.com", flags, NULL, - &verify_result); + empty_cert_list_, &verify_result); #if defined(USE_NSS) || defined(OS_IOS) || defined(OS_ANDROID) EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); #else @@ -177,7 +185,7 @@ TEST_F(CertVerifyProcTest, IntermediateCARequireExplicitPolicy) { int flags = 0; CertVerifyResult verify_result; int error = Verify(cert_chain, "www.us.army.mil", flags, NULL, - &verify_result); + empty_cert_list_, &verify_result); if (error == OK) { EXPECT_EQ(0U, verify_result.cert_status); } else { @@ -217,7 +225,7 @@ TEST_F(CertVerifyProcTest, DISABLED_GlobalSignR3EVTest) { int flags = CertVerifier::VERIFY_REV_CHECKING_ENABLED | CertVerifier::VERIFY_EV_CERT; int error = Verify(cert_chain, "2029.globalsign.com", flags, NULL, - &verify_result); + empty_cert_list_, &verify_result); if (error == OK) EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_IS_EV); else @@ -234,7 +242,7 @@ TEST_F(CertVerifyProcTest, ECDSA_RSA) { "prime256v1-ecdsa-ee-by-1024-rsa-intermediate.pem"); CertVerifyResult verify_result; - Verify(cert, "127.0.0.1", 0, NULL, &verify_result); + Verify(cert, "127.0.0.1", 0, NULL, empty_cert_list_, &verify_result); // We don't check verify_result because the certificate is signed by an // unknown CA and will be considered invalid on XP because of the ECDSA @@ -308,7 +316,8 @@ TEST_F(CertVerifyProcTest, RejectWeakKeys) { intermediates); CertVerifyResult verify_result; - int error = Verify(cert_chain, "127.0.0.1", 0, NULL, &verify_result); + int error = Verify(cert_chain, "127.0.0.1", 0, NULL, + empty_cert_list_, &verify_result); if (IsWeakKeyType(*ee_type) || IsWeakKeyType(*signer_type)) { EXPECT_NE(OK, error); @@ -353,7 +362,7 @@ TEST_F(CertVerifyProcTest, ExtraneousMD5RootCert) { CertVerifyResult verify_result; int flags = 0; int error = Verify(cert_chain, "images.etrade.wallst.com", flags, NULL, - &verify_result); + empty_cert_list_, &verify_result); if (error != OK) EXPECT_EQ(ERR_CERT_DATE_INVALID, error); @@ -382,13 +391,14 @@ TEST_F(CertVerifyProcTest, GoogleDigiNotarTest) { CertVerifyResult verify_result; int flags = CertVerifier::VERIFY_REV_CHECKING_ENABLED; int error = Verify(cert_chain, "mail.google.com", flags, NULL, - &verify_result); + empty_cert_list_, &verify_result); EXPECT_NE(OK, error); // Now turn off revocation checking. Certificate verification should still // fail. flags = 0; - error = Verify(cert_chain, "mail.google.com", flags, NULL, &verify_result); + error = Verify(cert_chain, "mail.google.com", flags, NULL, + empty_cert_list_, &verify_result); EXPECT_NE(OK, error); } @@ -445,7 +455,8 @@ TEST_F(CertVerifyProcTest, TestKnownRoot) { CertVerifyResult verify_result; // This will blow up, June 8th, 2014. Sorry! Please disable and file a bug // against agl. See also PublicKeyHashes. - int error = Verify(cert_chain, "cert.se", flags, NULL, &verify_result); + int error = Verify(cert_chain, "cert.se", flags, NULL, + empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result.cert_status); EXPECT_TRUE(verify_result.is_issued_by_known_root); @@ -469,7 +480,8 @@ TEST_F(CertVerifyProcTest, PublicKeyHashes) { // This will blow up, June 8th, 2014. Sorry! Please disable and file a bug // against agl. See also TestKnownRoot. - int error = Verify(cert_chain, "cert.se", flags, NULL, &verify_result); + int error = Verify(cert_chain, "cert.se", flags, NULL, + empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result.cert_status); ASSERT_LE(3u, verify_result.public_key_hashes.size()); @@ -501,7 +513,7 @@ TEST_F(CertVerifyProcTest, InvalidKeyUsage) { int flags = 0; CertVerifyResult verify_result; int error = Verify(server_cert, "jira.aquameta.com", flags, NULL, - &verify_result); + empty_cert_list_, &verify_result); #if defined(USE_OPENSSL) // This certificate has two errors: "invalid key usage" and "untrusted CA". // However, OpenSSL returns only one (the latter), and we can't detect @@ -546,7 +558,8 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) { CertVerifyResult verify_result; EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert); - int error = Verify(google_full_chain, "127.0.0.1", 0, NULL, &verify_result); + int error = Verify(google_full_chain, "127.0.0.1", 0, NULL, + empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert); @@ -590,7 +603,8 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainProperlyOrdered) { CertVerifyResult verify_result; EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert); - int error = Verify(google_full_chain, "127.0.0.1", 0, NULL, &verify_result); + int error = Verify(google_full_chain, "127.0.0.1", 0, NULL, + empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert); @@ -639,7 +653,8 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainFiltersUnrelatedCerts) { CertVerifyResult verify_result; EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert); - int error = Verify(google_full_chain, "127.0.0.1", 0, NULL, &verify_result); + int error = Verify(google_full_chain, "127.0.0.1", 0, NULL, + empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert); @@ -656,6 +671,49 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainFiltersUnrelatedCerts) { certs[2]->os_cert_handle())); } +TEST_F(CertVerifyProcTest, AdditionalTrustAnchors) { + if (!SupportsAdditionalTrustAnchors()) { + LOG(INFO) << "Skipping this test in this platform."; + return; + } + + // |ca_cert| is the issuer of |cert|. + CertificateList ca_cert_list = CreateCertificateListFromFile( + GetTestCertsDirectory(), "root_ca_cert.crt", + X509Certificate::FORMAT_AUTO); + ASSERT_EQ(1U, ca_cert_list.size()); + scoped_refptr<X509Certificate> ca_cert(ca_cert_list[0]); + + CertificateList cert_list = CreateCertificateListFromFile( + GetTestCertsDirectory(), "ok_cert.pem", + X509Certificate::FORMAT_AUTO); + ASSERT_EQ(1U, cert_list.size()); + scoped_refptr<X509Certificate> cert(cert_list[0]); + + // Verification of |cert| fails when |ca_cert| is not in the trust anchors + // list. + int flags = 0; + CertVerifyResult verify_result; + int error = Verify(cert, "127.0.0.1", flags, NULL, + empty_cert_list_, &verify_result); + EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error); + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status); + + // Now add the |ca_cert| to the |trust_anchors|, and verification should pass. + CertificateList trust_anchors; + trust_anchors.push_back(ca_cert); + error = Verify(cert, "127.0.0.1", flags, NULL, trust_anchors, &verify_result); + EXPECT_EQ(OK, error); + EXPECT_EQ(0U, verify_result.cert_status); + + // Clearing the |trust_anchors| makes verification fail again (the cache + // should be skipped). + error = Verify(cert, "127.0.0.1", flags, NULL, + empty_cert_list_, &verify_result); + EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error); + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status); +} + #if defined(USE_NSS) || defined(OS_IOS) || defined(OS_WIN) || defined(OS_MACOSX) static const uint8 kCRLSetThawteSPKIBlocked[] = { 0x8e, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a, @@ -722,7 +780,7 @@ TEST_F(CertVerifyProcTest, CRLSet) { CertVerifyResult verify_result; int error = Verify(google_full_chain, "www.google.com", 0, NULL, - &verify_result); + empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); // First test blocking by SPKI. @@ -733,7 +791,7 @@ TEST_F(CertVerifyProcTest, CRLSet) { ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set)); error = Verify(google_full_chain, "www.google.com", 0, crl_set.get(), - &verify_result); + empty_cert_list_, &verify_result); EXPECT_EQ(ERR_CERT_REVOKED, error); // Second, test revocation by serial number of a cert directly under the @@ -744,7 +802,7 @@ TEST_F(CertVerifyProcTest, CRLSet) { ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set)); error = Verify(google_full_chain, "www.google.com", 0, crl_set.get(), - &verify_result); + empty_cert_list_, &verify_result); EXPECT_EQ(ERR_CERT_REVOKED, error); // Lastly, test revocation by serial number of a certificate not under the @@ -755,7 +813,7 @@ TEST_F(CertVerifyProcTest, CRLSet) { ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set)); error = Verify(google_full_chain, "www.google.com", 0, crl_set.get(), - &verify_result); + empty_cert_list_, &verify_result); EXPECT_EQ(ERR_CERT_REVOKED, error); } #endif @@ -819,7 +877,8 @@ TEST_P(CertVerifyProcWeakDigestTest, Verify) { int flags = 0; CertVerifyResult verify_result; - int rv = Verify(ee_chain, "127.0.0.1", flags, NULL, &verify_result); + int rv = Verify(ee_chain, "127.0.0.1", flags, NULL, + empty_cert_list_, &verify_result); EXPECT_EQ(data.expected_has_md5, verify_result.has_md5); EXPECT_EQ(data.expected_has_md4, verify_result.has_md4); EXPECT_EQ(data.expected_has_md2, verify_result.has_md2); diff --git a/net/base/cert_verify_proc_win.cc b/net/base/cert_verify_proc_win.cc index d54e515..5a1de37 100644 --- a/net/base/cert_verify_proc_win.cc +++ b/net/base/cert_verify_proc_win.cc @@ -519,11 +519,17 @@ CertVerifyProcWin::CertVerifyProcWin() {} CertVerifyProcWin::~CertVerifyProcWin() {} -int CertVerifyProcWin::VerifyInternal(X509Certificate* cert, - const std::string& hostname, - int flags, - CRLSet* crl_set, - CertVerifyResult* verify_result) { +bool CertVerifyProcWin::SupportsAdditionalTrustAnchors() const { + return false; +} + +int CertVerifyProcWin::VerifyInternal( + X509Certificate* cert, + const std::string& hostname, + int flags, + CRLSet* crl_set, + const CertificateList& additional_trust_anchors, + CertVerifyResult* verify_result) { PCCERT_CONTEXT cert_handle = cert->os_cert_handle(); if (!cert_handle) return ERR_UNEXPECTED; diff --git a/net/base/cert_verify_proc_win.h b/net/base/cert_verify_proc_win.h index e030714..1db81db 100644 --- a/net/base/cert_verify_proc_win.h +++ b/net/base/cert_verify_proc_win.h @@ -15,6 +15,8 @@ class CertVerifyProcWin : public CertVerifyProc { public: CertVerifyProcWin(); + virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE; + protected: virtual ~CertVerifyProcWin(); @@ -23,6 +25,7 @@ class CertVerifyProcWin : public CertVerifyProc { const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) OVERRIDE; }; diff --git a/net/base/multi_threaded_cert_verifier.cc b/net/base/multi_threaded_cert_verifier.cc index 46a9e9b..dc01c0e 100644 --- a/net/base/multi_threaded_cert_verifier.cc +++ b/net/base/multi_threaded_cert_verifier.cc @@ -4,7 +4,7 @@ #include "net/base/multi_threaded_cert_verifier.h" -#include <vector> +#include <algorithm> #include "base/bind.h" #include "base/bind_helpers.h" @@ -15,6 +15,7 @@ #include "base/synchronization/lock.h" #include "base/time.h" #include "base/threading/worker_pool.h" +#include "net/base/cert_trust_anchor_provider.h" #include "net/base/cert_verify_proc.h" #include "net/base/crl_set.h" #include "net/base/net_errors.h" @@ -187,12 +188,14 @@ class CertVerifierWorker { const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, MultiThreadedCertVerifier* cert_verifier) : verify_proc_(verify_proc), cert_(cert), hostname_(hostname), flags_(flags), crl_set_(crl_set), + additional_trust_anchors_(additional_trust_anchors), origin_loop_(MessageLoop::current()), cert_verifier_(cert_verifier), canceled_(false), @@ -223,7 +226,7 @@ class CertVerifierWorker { void Run() { // Runs on a worker thread. error_ = verify_proc_->Verify(cert_, hostname_, flags_, crl_set_, - &verify_result_); + additional_trust_anchors_, &verify_result_); #if defined(USE_NSS) || defined(OS_IOS) // Detach the thread from NSPR. // Calling NSS functions attaches the thread to NSPR, which stores @@ -248,7 +251,8 @@ class CertVerifierWorker { base::AutoLock locked(lock_); if (!canceled_) { cert_verifier_->HandleResult(cert_, hostname_, flags_, - error_, verify_result_); + additional_trust_anchors_, error_, + verify_result_); } } delete this; @@ -286,6 +290,7 @@ class CertVerifierWorker { const std::string hostname_; const int flags_; scoped_refptr<CRLSet> crl_set_; + const CertificateList additional_trust_anchors_; MessageLoop* const origin_loop_; MultiThreadedCertVerifier* const cert_verifier_; @@ -382,7 +387,8 @@ MultiThreadedCertVerifier::MultiThreadedCertVerifier( requests_(0), cache_hits_(0), inflight_joins_(0), - verify_proc_(verify_proc) { + verify_proc_(verify_proc), + trust_anchor_provider_(NULL) { CertDatabase::GetInstance()->AddObserver(this); } @@ -391,6 +397,12 @@ MultiThreadedCertVerifier::~MultiThreadedCertVerifier() { CertDatabase::GetInstance()->RemoveObserver(this); } +void MultiThreadedCertVerifier::SetCertTrustAnchorProvider( + CertTrustAnchorProvider* trust_anchor_provider) { + DCHECK(CalledOnValidThread()); + trust_anchor_provider_ = trust_anchor_provider; +} + int MultiThreadedCertVerifier::Verify(X509Certificate* cert, const std::string& hostname, int flags, @@ -408,8 +420,13 @@ int MultiThreadedCertVerifier::Verify(X509Certificate* cert, requests_++; + const CertificateList empty_cert_list; + const CertificateList& additional_trust_anchors = + trust_anchor_provider_ ? + trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list; + const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), - hostname, flags); + hostname, flags, additional_trust_anchors); const CertVerifierCache::value_type* cached_entry = cache_.Get(key, CacheValidityPeriod(base::Time::Now())); if (cached_entry) { @@ -430,9 +447,9 @@ int MultiThreadedCertVerifier::Verify(X509Certificate* cert, job = j->second; } else { // Need to make a new request. - CertVerifierWorker* worker = new CertVerifierWorker(verify_proc_, cert, - hostname, flags, - crl_set, this); + CertVerifierWorker* worker = + new CertVerifierWorker(verify_proc_, cert, hostname, flags, crl_set, + additional_trust_anchors, this); job = new CertVerifierJob( worker, BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB)); @@ -464,11 +481,33 @@ MultiThreadedCertVerifier::RequestParams::RequestParams( const SHA1HashValue& cert_fingerprint_arg, const SHA1HashValue& ca_fingerprint_arg, const std::string& hostname_arg, - int flags_arg) - : cert_fingerprint(cert_fingerprint_arg), - ca_fingerprint(ca_fingerprint_arg), - hostname(hostname_arg), - flags(flags_arg) {} + int flags_arg, + const CertificateList& additional_trust_anchors) + : hostname(hostname_arg), + flags(flags_arg) { + hash_values.reserve(2 + additional_trust_anchors.size()); + hash_values.push_back(cert_fingerprint_arg); + hash_values.push_back(ca_fingerprint_arg); + for (size_t i = 0; i < additional_trust_anchors.size(); ++i) + hash_values.push_back(additional_trust_anchors[i]->fingerprint()); +} + +MultiThreadedCertVerifier::RequestParams::~RequestParams() {} + +bool MultiThreadedCertVerifier::RequestParams::operator<( + const RequestParams& other) const { + // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, and + // |hostname| under assumption that integer comparisons are faster than + // memory and string comparisons. + if (flags != other.flags) + return flags < other.flags; + if (hostname != other.hostname) + return hostname < other.hostname; + return std::lexicographical_compare( + hash_values.begin(), hash_values.end(), + other.hash_values.begin(), other.hash_values.end(), + net::SHA1HashValueLessThan()); +} // HandleResult is called by CertVerifierWorker on the origin message loop. // It deletes CertVerifierJob. @@ -476,12 +515,13 @@ void MultiThreadedCertVerifier::HandleResult( X509Certificate* cert, const std::string& hostname, int flags, + const CertificateList& additional_trust_anchors, int error, const CertVerifyResult& verify_result) { DCHECK(CalledOnValidThread()); const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), - hostname, flags); + hostname, flags, additional_trust_anchors); CachedResult cached_result; cached_result.error = error; diff --git a/net/base/multi_threaded_cert_verifier.h b/net/base/multi_threaded_cert_verifier.h index b218370..bee577c 100644 --- a/net/base/multi_threaded_cert_verifier.h +++ b/net/base/multi_threaded_cert_verifier.h @@ -7,6 +7,7 @@ #include <map> #include <string> +#include <vector> #include "base/basictypes.h" #include "base/gtest_prod_util.h" @@ -17,11 +18,13 @@ #include "net/base/cert_verify_result.h" #include "net/base/completion_callback.h" #include "net/base/expiring_cache.h" +#include "net/base/hash_value.h" #include "net/base/net_export.h" #include "net/base/x509_cert_types.h" namespace net { +class CertTrustAnchorProvider; class CertVerifierJob; class CertVerifierRequest; class CertVerifierWorker; @@ -40,6 +43,16 @@ class NET_EXPORT_PRIVATE MultiThreadedCertVerifier // canceled, and their completion callbacks will not be called. virtual ~MultiThreadedCertVerifier(); + // Configures a source of additional certificates that should be treated as + // trust anchors during verification, provided that the underlying + // CertVerifyProc supports additional trust beyond the default implementation. + // The CertTrustAnchorProvider will only be accessed on the same + // thread that Verify() is called on; that is, it will not be + // accessed from worker threads. + // It must outlive the MultiThreadedCertVerifier. + void SetCertTrustAnchorProvider( + CertTrustAnchorProvider* trust_anchor_provider); + // CertVerifier implementation virtual int Verify(X509Certificate* cert, const std::string& hostname, @@ -63,35 +76,23 @@ class NET_EXPORT_PRIVATE MultiThreadedCertVerifier FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, CancelRequest); FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, RequestParamsComparators); + FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, + CertTrustAnchorProvider); // Input parameters of a certificate verification request. struct NET_EXPORT_PRIVATE RequestParams { RequestParams(const SHA1HashValue& cert_fingerprint_arg, const SHA1HashValue& ca_fingerprint_arg, const std::string& hostname_arg, - int flags_arg); - - bool operator<(const RequestParams& other) const { - // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, and - // |hostname| under assumption that integer comparisons are faster than - // memory and string comparisons. - if (flags != other.flags) - return flags < other.flags; - int rv = memcmp(cert_fingerprint.data, other.cert_fingerprint.data, - sizeof(cert_fingerprint.data)); - if (rv != 0) - return rv < 0; - rv = memcmp(ca_fingerprint.data, other.ca_fingerprint.data, - sizeof(ca_fingerprint.data)); - if (rv != 0) - return rv < 0; - return hostname < other.hostname; - } - - SHA1HashValue cert_fingerprint; - SHA1HashValue ca_fingerprint; + int flags_arg, + const CertificateList& additional_trust_anchors); + ~RequestParams(); + + bool operator<(const RequestParams& other) const; + std::string hostname; int flags; + std::vector<SHA1HashValue> hash_values; }; // CachedResult contains the result of a certificate verification. @@ -131,6 +132,7 @@ class NET_EXPORT_PRIVATE MultiThreadedCertVerifier void HandleResult(X509Certificate* cert, const std::string& hostname, int flags, + const CertificateList& additional_trust_anchors, int error, const CertVerifyResult& verify_result); @@ -157,6 +159,8 @@ class NET_EXPORT_PRIVATE MultiThreadedCertVerifier scoped_refptr<CertVerifyProc> verify_proc_; + CertTrustAnchorProvider* trust_anchor_provider_; + DISALLOW_COPY_AND_ASSIGN(MultiThreadedCertVerifier); }; diff --git a/net/base/multi_threaded_cert_verifier_unittest.cc b/net/base/multi_threaded_cert_verifier_unittest.cc index 5fccde9..8dea596 100644 --- a/net/base/multi_threaded_cert_verifier_unittest.cc +++ b/net/base/multi_threaded_cert_verifier_unittest.cc @@ -9,6 +9,7 @@ #include "base/format_macros.h" #include "base/stringprintf.h" #include "net/base/cert_test_util.h" +#include "net/base/cert_trust_anchor_provider.h" #include "net/base/cert_verify_proc.h" #include "net/base/cert_verify_result.h" #include "net/base/net_errors.h" @@ -16,8 +17,12 @@ #include "net/base/test_completion_callback.h" #include "net/base/test_data_directory.h" #include "net/base/x509_certificate.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using testing::Mock; +using testing::ReturnRef; + namespace net { namespace { @@ -34,10 +39,15 @@ class MockCertVerifyProc : public CertVerifyProc { virtual ~MockCertVerifyProc() {} // CertVerifyProc implementation + virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE { + return false; + } + virtual int VerifyInternal(X509Certificate* cert, const std::string& hostname, int flags, CRLSet* crl_set, + const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) OVERRIDE { verify_result->Reset(); verify_result->verified_cert = cert; @@ -46,6 +56,14 @@ class MockCertVerifyProc : public CertVerifyProc { } }; +class MockCertTrustAnchorProvider : public CertTrustAnchorProvider { + public: + MockCertTrustAnchorProvider() {} + virtual ~MockCertTrustAnchorProvider() {} + + MOCK_METHOD0(GetAdditionalTrustAnchors, const CertificateList&()); +}; + } // namespace class MultiThreadedCertVerifierTest : public ::testing::Test { @@ -248,6 +266,11 @@ TEST_F(MultiThreadedCertVerifierTest, RequestParamsComparators) { SHA1HashValue z_key; memset(z_key.data, 'z', sizeof(z_key.data)); + const CertificateList empty_list; + CertificateList test_list; + test_list.push_back( + ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); + struct { // Keys to test MultiThreadedCertVerifier::RequestParams key1; @@ -261,43 +284,53 @@ TEST_F(MultiThreadedCertVerifierTest, RequestParamsComparators) { } tests[] = { { // Test for basic equivalence. MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", - 0), + 0, test_list), MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", - 0), + 0, test_list), 0, }, { // Test that different certificates but with the same CA and for // the same host are different validation keys. MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", - 0), + 0, test_list), MultiThreadedCertVerifier::RequestParams(z_key, a_key, "www.example.test", - 0), + 0, test_list), -1, }, { // Test that the same EE certificate for the same host, but with // different chains are different validation keys. MultiThreadedCertVerifier::RequestParams(a_key, z_key, "www.example.test", - 0), + 0, test_list), MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", - 0), + 0, test_list), 1, }, { // The same certificate, with the same chain, but for different // hosts are different validation keys. MultiThreadedCertVerifier::RequestParams(a_key, a_key, - "www1.example.test", 0), + "www1.example.test", 0, + test_list), MultiThreadedCertVerifier::RequestParams(a_key, a_key, - "www2.example.test", 0), + "www2.example.test", 0, + test_list), -1, }, { // The same certificate, chain, and host, but with different flags // are different validation keys. MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", - CertVerifier::VERIFY_EV_CERT), + CertVerifier::VERIFY_EV_CERT, + test_list), MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", - 0), + 0, test_list), 1, - } + }, + { // Different additional_trust_anchors. + MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", + 0, empty_list), + MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", + 0, test_list), + -1, + }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i)); @@ -324,4 +357,63 @@ TEST_F(MultiThreadedCertVerifierTest, RequestParamsComparators) { } } +TEST_F(MultiThreadedCertVerifierTest, CertTrustAnchorProvider) { + MockCertTrustAnchorProvider trust_provider; + verifier_.SetCertTrustAnchorProvider(&trust_provider); + + scoped_refptr<X509Certificate> test_cert( + ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); + ASSERT_TRUE(test_cert); + + const CertificateList empty_cert_list; + CertificateList cert_list; + cert_list.push_back(test_cert); + + // Check that Verify() asks the |trust_provider| for the current list of + // additional trust anchors. + int error; + CertVerifyResult verify_result; + TestCompletionCallback callback; + CertVerifier::RequestHandle request_handle; + EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) + .WillOnce(ReturnRef(empty_cert_list)); + error = verifier_.Verify(test_cert, "www.example.com", 0, NULL, + &verify_result, callback.callback(), + &request_handle, BoundNetLog()); + Mock::VerifyAndClearExpectations(&trust_provider); + ASSERT_EQ(ERR_IO_PENDING, error); + ASSERT_TRUE(request_handle); + error = callback.WaitForResult(); + EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); + ASSERT_EQ(1u, verifier_.requests()); + ASSERT_EQ(0u, verifier_.cache_hits()); + + // The next Verify() uses the cached result. + EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) + .WillOnce(ReturnRef(empty_cert_list)); + error = verifier_.Verify(test_cert, "www.example.com", 0, NULL, + &verify_result, callback.callback(), + &request_handle, BoundNetLog()); + Mock::VerifyAndClearExpectations(&trust_provider); + EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); + EXPECT_FALSE(request_handle); + ASSERT_EQ(2u, verifier_.requests()); + ASSERT_EQ(1u, verifier_.cache_hits()); + + // Another Verify() for the same certificate but with a different list of + // trust anchors will not reuse the cache. + EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) + .WillOnce(ReturnRef(cert_list)); + error = verifier_.Verify(test_cert, "www.example.com", 0, NULL, + &verify_result, callback.callback(), + &request_handle, BoundNetLog()); + Mock::VerifyAndClearExpectations(&trust_provider); + ASSERT_EQ(ERR_IO_PENDING, error); + ASSERT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); + ASSERT_EQ(3u, verifier_.requests()); + ASSERT_EQ(1u, verifier_.cache_hits()); +} + } // namespace net diff --git a/net/base/nss_cert_database_unittest.cc b/net/base/nss_cert_database_unittest.cc index bf715f9..1110bf3 100644 --- a/net/base/nss_cert_database_unittest.cc +++ b/net/base/nss_cert_database_unittest.cc @@ -101,6 +101,7 @@ class CertDatabaseNSSTest : public testing::Test { scoped_refptr<CryptoModule> slot_; NSSCertDatabase* cert_db_; + const CertificateList empty_cert_list_; private: bool CleanupSlotContents() { @@ -541,7 +542,7 @@ TEST_F(CertDatabaseNSSTest, DISABLED_ImportServerCert) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(goog_cert, "www.google.com", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result.cert_status); } @@ -568,7 +569,7 @@ TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(puny_cert, "xn--wgv71a119e.com", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error); EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status); } @@ -603,7 +604,7 @@ TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned_Trusted) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(puny_cert, "xn--wgv71a119e.com", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result.cert_status); } @@ -635,7 +636,7 @@ TEST_F(CertDatabaseNSSTest, ImportCaAndServerCert) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result.cert_status); } @@ -679,7 +680,7 @@ TEST_F(CertDatabaseNSSTest, ImportCaAndServerCert_DistrustServer) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(ERR_CERT_REVOKED, error); EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status); } @@ -723,7 +724,7 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result.cert_status); @@ -756,7 +757,7 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa) { // Server cert should fail to verify. CertVerifyResult verify_result2; error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result2); + NULL, empty_cert_list_, &verify_result2); EXPECT_EQ(ERR_CERT_REVOKED, error); EXPECT_EQ(CERT_STATUS_REVOKED, verify_result2.cert_status); } @@ -791,7 +792,7 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa2) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result.cert_status); @@ -802,7 +803,7 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa2) { // Server cert should fail to verify. CertVerifyResult verify_result2; error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result2); + NULL, empty_cert_list_, &verify_result2); EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error); EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status); } @@ -847,7 +848,7 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa3) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result.cert_status); @@ -858,7 +859,7 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa3) { // Server cert should fail to verify. CertVerifyResult verify_result2; error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result2); + NULL, empty_cert_list_, &verify_result2); EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error); EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status); } @@ -909,7 +910,7 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa4) { int flags = 0; CertVerifyResult verify_result; int error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result); + NULL, empty_cert_list_, &verify_result); EXPECT_EQ(ERR_CERT_REVOKED, error); EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status); @@ -920,7 +921,7 @@ TEST_F(CertDatabaseNSSTest, TrustIntermediateCa4) { // Server cert should verify. CertVerifyResult verify_result2; error = verify_proc->Verify(certs[0], "127.0.0.1", flags, - NULL, &verify_result2); + NULL, empty_cert_list_, &verify_result2); EXPECT_EQ(OK, error); EXPECT_EQ(0U, verify_result2.cert_status); } diff --git a/net/net.gyp b/net/net.gyp index 58b00fd..cb8b401 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -104,6 +104,7 @@ 'base/cert_database_win.cc', 'base/cert_status_flags.cc', 'base/cert_status_flags.h', + 'base/cert_trust_anchor_provider.h', 'base/cert_verifier.cc', 'base/cert_verifier.h', 'base/cert_verify_proc.cc', |