diff options
author | rsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-17 04:35:08 +0000 |
---|---|---|
committer | rsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-17 04:35:08 +0000 |
commit | b6f2de3332cb9de2be479f82c258016da15d419c (patch) | |
tree | c6bae46b4f2afe6e48bff8ddd98edfc223ed6492 /net | |
parent | e21c547e3d5f9f502836f19c7c69075f70813f27 (diff) | |
download | chromium_src-b6f2de3332cb9de2be479f82c258016da15d419c.zip chromium_src-b6f2de3332cb9de2be479f82c258016da15d419c.tar.gz chromium_src-b6f2de3332cb9de2be479f82c258016da15d419c.tar.bz2 |
Do not perform online revocation checking when the user has explicitly disabled it, except for when verifying EV certificates where a CRLSet is not present or fresh.
This changes how EVRootMetaData exposes the EV information when NSS is used, in order to efficiently detect when a leaf certificate may be an EV certificate.
BUG=142815
TEST=Test modem enrollment on CrOS as described in chrome-os-partner:9087
Review URL: https://chromiumcodereview.appspot.com/10857020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@152043 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/cert_verify_proc.cc | 7 | ||||
-rw-r--r-- | net/base/cert_verify_proc_mac.cc | 5 | ||||
-rw-r--r-- | net/base/cert_verify_proc_nss.cc | 121 | ||||
-rw-r--r-- | net/base/cert_verify_proc_win.cc | 25 | ||||
-rw-r--r-- | net/base/ev_root_ca_metadata.cc | 31 | ||||
-rw-r--r-- | net/base/ev_root_ca_metadata.h | 12 | ||||
-rw-r--r-- | net/base/ev_root_ca_metadata_unittest.cc | 120 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 22 | ||||
-rw-r--r-- | net/base/x509_util_mac.cc | 98 | ||||
-rw-r--r-- | net/base/x509_util_mac.h | 14 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 50 |
11 files changed, 312 insertions, 193 deletions
diff --git a/net/base/cert_verify_proc.cc b/net/base/cert_verify_proc.cc index 420a8a5..b53dd2e 100644 --- a/net/base/cert_verify_proc.cc +++ b/net/base/cert_verify_proc.cc @@ -82,12 +82,11 @@ int CertVerifyProc::Verify(X509Certificate* cert, // CRLSet has expired, then enable online revocation checks. If the online // check fails, EV status won't be shown. // - // A possible optimisation is to only enable online revocation checking in - // the event that the leaf certificate appears to include a EV policy ID. - // However, it's expected that having a current CRLSet will be very common. + // TODO(rsleevi): http://crbug.com/142974 - Allow preferences to fully + // disable revocation checking. if ((flags & X509Certificate::VERIFY_EV_CERT) && (!crl_set || crl_set->IsExpired())) { - flags |= X509Certificate::VERIFY_REV_CHECKING_ENABLED; + flags |= X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY; } int rv = VerifyInternal(cert, hostname, flags, crl_set, verify_result); diff --git a/net/base/cert_verify_proc_mac.cc b/net/base/cert_verify_proc_mac.cc index 49798b5..2cbffef 100644 --- a/net/base/cert_verify_proc_mac.cc +++ b/net/base/cert_verify_proc_mac.cc @@ -157,6 +157,7 @@ OSStatus CreateTrustPolicies(const std::string& hostname, // revocation preference. status = x509_util::CreateRevocationPolicies( (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED), + (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY), local_policies); if (status) return status; @@ -380,6 +381,8 @@ int CertVerifyProcMac::VerifyInternal(X509Certificate* cert, tp_action_data.ActionFlags = CSSM_TP_ACTION_FETCH_CERT_FROM_NET | CSSM_TP_ACTION_TRUST_SETTINGS; + // Note: For EV certificates, the Apple TP will handle setting these flags + // as part of EV evaluation. if (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) { // Require a positive result from an OCSP responder or a CRL (or both) // for every certificate in the chain. The Apple TP automatically @@ -564,6 +567,8 @@ int CertVerifyProcMac::VerifyInternal(X509Certificate* cert, if (CFDictionaryContainsKey(ev_dict, kSecEVOrganizationName)) { verify_result->cert_status |= CERT_STATUS_IS_EV; + if (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) + verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; } } } diff --git a/net/base/cert_verify_proc_nss.cc b/net/base/cert_verify_proc_nss.cc index 3108555..9edcbda 100644 --- a/net/base/cert_verify_proc_nss.cc +++ b/net/base/cert_verify_proc_nss.cc @@ -28,21 +28,11 @@ namespace net { namespace { -class ScopedCERTCertificatePolicies { - public: - explicit ScopedCERTCertificatePolicies(CERTCertificatePolicies* policies) - : policies_(policies) {} - - ~ScopedCERTCertificatePolicies() { - if (policies_) - CERT_DestroyCertificatePoliciesExtension(policies_); - } - - private: - CERTCertificatePolicies* policies_; - - DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertificatePolicies); -}; +typedef scoped_ptr_malloc< + CERTCertificatePolicies, + crypto::NSSDestroyer<CERTCertificatePolicies, + CERT_DestroyCertificatePoliciesExtension> > + ScopedCERTCertificatePolicies; // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam // array that cvout points to. cvout must be initialized as passed to @@ -551,10 +541,10 @@ CERTCertificatePolicies* DecodeCertPolicies( // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate // has no certificate policy. SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) { - CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); - if (!policies) + ScopedCERTCertificatePolicies policies(DecodeCertPolicies(cert_handle)); + if (!policies.get()) return SEC_OID_UNKNOWN; - ScopedCERTCertificatePolicies scoped_policies(policies); + CERTPolicyInfo* policy_info = policies->policyInfos[0]; if (!policy_info) return SEC_OID_UNKNOWN; @@ -576,27 +566,6 @@ SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) { return SECOID_AddEntry(&od); } -bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle, - SECOidTag ev_policy_tag) { - CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); - if (!policies) { - LOG(ERROR) << "Cert has no policies extension or extension couldn't be " - "decoded."; - return false; - } - ScopedCERTCertificatePolicies scoped_policies(policies); - CERTPolicyInfo** policy_infos = policies->policyInfos; - while (*policy_infos != NULL) { - CERTPolicyInfo* policy_info = *policy_infos++; - SECOidTag oid_tag = policy_info->oid; - if (oid_tag == SEC_OID_UNKNOWN) - continue; - if (oid_tag == ev_policy_tag) - return true; - } - return false; -} - SHA1Fingerprint CertPublicKeyHash(CERTCertificate* cert) { SHA1Fingerprint hash; SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, hash.data, @@ -617,6 +586,35 @@ void AppendPublicKeyHashes(CERTCertList* cert_list, hashes->push_back(CertPublicKeyHash(root_cert)); } +// Returns true if |cert_handle| contains a policy OID that is an EV policy +// OID according to |metadata|, storing the resulting policy OID in +// |*ev_policy_oid|. A true return is not sufficient to establish that a +// certificate is EV, but a false return is sufficient to establish the +// certificate cannot be EV. +bool IsEVCandidate(EVRootCAMetadata* metadata, + CERTCertificate* cert_handle, + SECOidTag* ev_policy_oid) { + DCHECK(cert_handle); + ScopedCERTCertificatePolicies policies(DecodeCertPolicies(cert_handle)); + if (!policies.get()) + return false; + + CERTPolicyInfo** policy_infos = policies->policyInfos; + while (*policy_infos != NULL) { + CERTPolicyInfo* policy_info = *policy_infos++; + // If the Policy OID is unknown, that implicitly means it has not been + // registered as an EV policy. + if (policy_info->oid == SEC_OID_UNKNOWN) + continue; + if (metadata->IsEVPolicyOID(policy_info->oid)) { + *ev_policy_oid = policy_info->oid; + return true; + } + } + + return false; +} + // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp // and nsNSSCertHelper.cpp) to learn how to verify EV certificate. // TODO(wtc): A possible optimization is that we get the trust anchor from @@ -624,9 +622,11 @@ void AppendPublicKeyHashes(CERTCertList* cert_list, // anchor. If the trust anchor has no EV policy, we know the cert isn't EV. // Otherwise, we pass just that EV policy (as opposed to all the EV policies) // to the second PKIXVerifyCert call. -bool VerifyEV(CERTCertificate* cert_handle, int flags, CRLSet* crl_set) { - EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); - +bool VerifyEV(CERTCertificate* cert_handle, + int flags, + CRLSet* crl_set, + EVRootCAMetadata* metadata, + SECOidTag ev_policy_oid) { CERTValOutParam cvout[3]; int cvout_index = 0; cvout[cvout_index].type = cert_po_certList; @@ -640,12 +640,16 @@ bool VerifyEV(CERTCertificate* cert_handle, int flags, CRLSet* crl_set) { cvout[cvout_index].type = cert_po_end; ScopedCERTValOutParam scoped_cvout(cvout); + bool rev_checking_enabled = + (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) || + (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY); + SECStatus status = PKIXVerifyCert( cert_handle, - flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED, + rev_checking_enabled, flags & X509Certificate::VERIFY_CERT_IO_ENABLED, - metadata->GetPolicyOIDs(), - metadata->NumPolicyOIDs(), + &ev_policy_oid, + 1, cvout); if (status != SECSuccess) return false; @@ -669,17 +673,7 @@ bool VerifyEV(CERTCertificate* cert_handle, int flags, CRLSet* crl_set) { SHA1Fingerprint fingerprint = X509Certificate::CalculateFingerprint(root_ca); - std::vector<SECOidTag> ev_policy_tags; - if (!metadata->GetPolicyOIDsForCA(fingerprint, &ev_policy_tags)) - return false; - DCHECK(!ev_policy_tags.empty()); - - for (std::vector<SECOidTag>::const_iterator - i = ev_policy_tags.begin(); i != ev_policy_tags.end(); ++i) { - if (CheckCertPolicies(cert_handle, *i)) - return true; - } - return false; + return metadata->HasEVPolicyOID(fingerprint, ev_policy_oid); } } // namespace @@ -718,10 +712,17 @@ int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, cvout[cvout_index].type = cert_po_end; ScopedCERTValOutParam scoped_cvout(cvout); + EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); + SECOidTag ev_policy_oid = SEC_OID_UNKNOWN; + bool is_ev_candidate = + (flags & X509Certificate::VERIFY_EV_CERT) && + IsEVCandidate(metadata, cert_handle, &ev_policy_oid); bool cert_io_enabled = flags & X509Certificate::VERIFY_CERT_IO_ENABLED; bool check_revocation = - (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) && - cert_io_enabled; + cert_io_enabled && + ((flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) || + ((flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) && + is_ev_candidate)); if (check_revocation) verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; @@ -770,8 +771,8 @@ int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, verify_result->is_issued_by_known_root = IsKnownRoot(cvout[cvout_trust_anchor_index].value.pointer.cert); - if ((flags & X509Certificate::VERIFY_EV_CERT) && - VerifyEV(cert_handle, flags, crl_set)) { + if ((flags & X509Certificate::VERIFY_EV_CERT) && is_ev_candidate && + VerifyEV(cert_handle, flags, crl_set, metadata, ev_policy_oid)) { verify_result->cert_status |= CERT_STATUS_IS_EV; } diff --git a/net/base/cert_verify_proc_win.cc b/net/base/cert_verify_proc_win.cc index 045ea16..a733d13 100644 --- a/net/base/cert_verify_proc_win.cc +++ b/net/base/cert_verify_proc_win.cc @@ -541,17 +541,6 @@ int CertVerifyProcWin::VerifyInternal(X509Certificate* cert, chain_para.RequestedUsage.Usage.cUsageIdentifier = arraysize(usage); chain_para.RequestedUsage.Usage.rgpszUsageIdentifier = const_cast<LPSTR*>(usage); - // We can set CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS to get more chains. - DWORD chain_flags = CERT_CHAIN_CACHE_END_CERT | - CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; - const bool rev_checking_enabled = - flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED; - - if (rev_checking_enabled) { - verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; - } else { - chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; - } // Get the certificatePolicies extension of the certificate. scoped_ptr_malloc<CERT_POLICIES_INFO> policies_info; @@ -574,6 +563,20 @@ int CertVerifyProcWin::VerifyInternal(X509Certificate* cert, } } + // We can set CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS to get more chains. + DWORD chain_flags = CERT_CHAIN_CACHE_END_CERT | + CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; + const bool rev_checking_enabled = + (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) || + (ev_policy_oid != NULL && + (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY)); + + if (rev_checking_enabled) { + verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; + } else { + chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; + } + // For non-test scenarios, use the default HCERTCHAINENGINE, NULL, which // corresponds to HCCE_CURRENT_USER and is is initialized as needed by // crypt32. However, when testing, it is necessary to create a new diff --git a/net/base/ev_root_ca_metadata.cc b/net/base/ev_root_ca_metadata.cc index 301732f..d51c3b9b 100644 --- a/net/base/ev_root_ca_metadata.cc +++ b/net/base/ev_root_ca_metadata.cc @@ -320,26 +320,22 @@ EVRootCAMetadata* EVRootCAMetadata::GetInstance() { } #if defined(USE_NSS) +bool EVRootCAMetadata::IsEVPolicyOID(PolicyOID policy_oid) const { + return policy_oids_.find(policy_oid) != policy_oids_.end(); +} -bool EVRootCAMetadata::GetPolicyOIDsForCA( +bool EVRootCAMetadata::HasEVPolicyOID( const SHA1Fingerprint& fingerprint, - std::vector<PolicyOID>* policy_oids) const { + PolicyOID policy_oid) const { PolicyOIDMap::const_iterator iter = ev_policy_.find(fingerprint); if (iter == ev_policy_.end()) return false; for (std::vector<PolicyOID>::const_iterator j = iter->second.begin(); j != iter->second.end(); ++j) { - policy_oids->push_back(*j); + if (*j == policy_oid) + return true; } - return true; -} - -const EVRootCAMetadata::PolicyOID* EVRootCAMetadata::GetPolicyOIDs() const { - return &policy_oids_[0]; -} - -int EVRootCAMetadata::NumPolicyOIDs() const { - return policy_oids_.size(); + return false; } bool EVRootCAMetadata::AddEVCA(const SHA1Fingerprint& fingerprint, @@ -352,7 +348,7 @@ bool EVRootCAMetadata::AddEVCA(const SHA1Fingerprint& fingerprint, return false; ev_policy_[fingerprint].push_back(oid); - policy_oids_.push_back(oid); + policy_oids_.insert(oid); return true; } @@ -363,12 +359,7 @@ bool EVRootCAMetadata::RemoveEVCA(const SHA1Fingerprint& fingerprint) { return false; PolicyOID oid = it->second[0]; ev_policy_.erase(it); - - std::vector<PolicyOID>::iterator it2 = std::find( - policy_oids_.begin(), policy_oids_.end(), oid); - if (it2 == policy_oids_.end()) - return false; - policy_oids_.erase(it2); + policy_oids_.erase(oid); return true; } @@ -491,7 +482,7 @@ EVRootCAMetadata::EVRootCAMetadata() { } ev_policy_[metadata.fingerprint].push_back(policy); - policy_oids_.push_back(policy); + policy_oids_.insert(policy); } } #endif diff --git a/net/base/ev_root_ca_metadata.h b/net/base/ev_root_ca_metadata.h index ab76c49..4e7af40 100644 --- a/net/base/ev_root_ca_metadata.h +++ b/net/base/ev_root_ca_metadata.h @@ -12,6 +12,7 @@ #endif #include <map> +#include <set> #include <vector> #include "net/base/net_export.h" @@ -36,14 +37,7 @@ class NET_EXPORT_PRIVATE EVRootCAMetadata { static EVRootCAMetadata* GetInstance(); -#if defined(USE_NSS) - // If the root CA cert has an EV policy OID, returns true and appends the - // policy OIDs to |*policy_oids|. Otherwise, returns false. - bool GetPolicyOIDsForCA(const SHA1Fingerprint& fingerprint, - std::vector<PolicyOID>* policy_oids) const; - const PolicyOID* GetPolicyOIDs() const; - int NumPolicyOIDs() const; -#elif defined(OS_WIN) +#if defined(USE_NSS) || defined(OS_WIN) // Returns true if policy_oid is an EV policy OID of some root CA. bool IsEVPolicyOID(PolicyOID policy_oid) const; @@ -77,7 +71,7 @@ class NET_EXPORT_PRIVATE EVRootCAMetadata { static bool RegisterOID(const char* policy, PolicyOID* out); PolicyOIDMap ev_policy_; - std::vector<PolicyOID> policy_oids_; + std::set<PolicyOID> policy_oids_; #elif defined(OS_WIN) typedef std::map<SHA1Fingerprint, std::string, SHA1FingerprintLessThan> ExtraEVCAMap; diff --git a/net/base/ev_root_ca_metadata_unittest.cc b/net/base/ev_root_ca_metadata_unittest.cc index 1a1ef35..8859c70 100644 --- a/net/base/ev_root_ca_metadata_unittest.cc +++ b/net/base/ev_root_ca_metadata_unittest.cc @@ -8,8 +8,14 @@ #include "net/base/x509_cert_types.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(USE_NSS) +#include "crypto/scoped_nss_types.h" +#endif + namespace net { +namespace { + static const char kVerisignPolicy[] = "2.16.840.1.113733.1.7.23.6"; static const char kThawtePolicy[] = "2.16.840.1.113733.1.7.48.1"; static const char kFakePolicy[] = "2.16.840.1.42"; @@ -20,75 +26,119 @@ static const SHA1Fingerprint kFakeFingerprint = { { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99 } }; +#if defined(USE_NSS) || defined(OS_WIN) +class EVOidData { + public: + EVOidData(); + bool Init(); + + EVRootCAMetadata::PolicyOID verisign_policy; + EVRootCAMetadata::PolicyOID thawte_policy; + EVRootCAMetadata::PolicyOID fake_policy; +}; + +#endif // defined(USE_NSS) || defined(OS_WIN) + #if defined(USE_NSS) -TEST(EVRootCAMetadataTest, Basic) { - EVRootCAMetadata* ev_metadata(EVRootCAMetadata::GetInstance()); - std::vector<EVRootCAMetadata::PolicyOID> oids; +SECOidTag RegisterOID(PLArenaPool* arena, const char* oid_string) { + SECOidData oid_data; + memset(&oid_data, 0, sizeof(oid_data)); + oid_data.offset = SEC_OID_UNKNOWN; + oid_data.desc = oid_string; + oid_data.mechanism = CKM_INVALID_MECHANISM; + oid_data.supportedExtension = INVALID_CERT_EXTENSION; - EXPECT_TRUE(ev_metadata->GetPolicyOIDsForCA(kVerisignFingerprint, &oids)); - EXPECT_LT(0u, oids.size()); - oids.clear(); + SECStatus rv = SEC_StringToOID(arena, &oid_data.oid, oid_string, 0); + if (rv != SECSuccess) + return SEC_OID_UNKNOWN; - EXPECT_FALSE(ev_metadata->GetPolicyOIDsForCA(kFakeFingerprint, &oids)); - EXPECT_EQ(0u, oids.size()); + return SECOID_AddEntry(&oid_data); } -TEST(EVRootCAMetadataTest, AddRemove) { - EVRootCAMetadata* ev_metadata(EVRootCAMetadata::GetInstance()); - std::vector<EVRootCAMetadata::PolicyOID> oids; - - EXPECT_FALSE(ev_metadata->GetPolicyOIDsForCA(kFakeFingerprint, &oids)); +EVOidData::EVOidData() + : verisign_policy(SEC_OID_UNKNOWN), + thawte_policy(SEC_OID_UNKNOWN), + fake_policy(SEC_OID_UNKNOWN) { +} - { - ScopedTestEVPolicy test_ev_policy(ev_metadata, kFakeFingerprint, - kFakePolicy); +bool EVOidData::Init() { + crypto::ScopedPLArenaPool pool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!pool.get()) + return false; - EXPECT_TRUE(ev_metadata->GetPolicyOIDsForCA(kFakeFingerprint, &oids)); - EXPECT_EQ(1u, oids.size()); - } + verisign_policy = RegisterOID(pool.get(), kVerisignPolicy); + thawte_policy = RegisterOID(pool.get(), kThawtePolicy); + fake_policy = RegisterOID(pool.get(), kFakePolicy); - EXPECT_FALSE(ev_metadata->GetPolicyOIDsForCA(kFakeFingerprint, &oids)); + return verisign_policy != SEC_OID_UNKNOWN && + thawte_policy != SEC_OID_UNKNOWN && + fake_policy != SEC_OID_UNKNOWN; } #elif defined(OS_WIN) -TEST(EVRootCAMetadataTest, Basic) { +EVOidData::EVOidData() + : verisign_policy(kVerisignPolicy), + thawte_policy(kThawtePolicy), + fake_policy(kFakePolicy) { +} + +bool EVOidData::Init() { + return true; +} + +#endif + +#if defined(USE_NSS) || defined(OS_WIN) + +class EVRootCAMetadataTest : public testing::Test { + protected: + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(ev_oid_data.Init()); + } + + EVOidData ev_oid_data; +}; + +TEST_F(EVRootCAMetadataTest, Basic) { EVRootCAMetadata* ev_metadata(EVRootCAMetadata::GetInstance()); - EXPECT_TRUE(ev_metadata->IsEVPolicyOID(kVerisignPolicy)); - EXPECT_FALSE(ev_metadata->IsEVPolicyOID(kFakePolicy)); + EXPECT_TRUE(ev_metadata->IsEVPolicyOID(ev_oid_data.verisign_policy)); + EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy)); EXPECT_TRUE(ev_metadata->HasEVPolicyOID(kVerisignFingerprint, - kVerisignPolicy)); + ev_oid_data.verisign_policy)); EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kFakeFingerprint, - kVerisignPolicy)); + ev_oid_data.verisign_policy)); EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kVerisignFingerprint, - kFakePolicy)); + ev_oid_data.fake_policy)); EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kVerisignFingerprint, - kThawtePolicy)); + ev_oid_data.thawte_policy)); } -TEST(EVRootCAMetadataTest, AddRemove) { +TEST_F(EVRootCAMetadataTest, AddRemove) { EVRootCAMetadata* ev_metadata(EVRootCAMetadata::GetInstance()); - EXPECT_FALSE(ev_metadata->IsEVPolicyOID(kFakePolicy)); + EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy)); EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kFakeFingerprint, - kFakePolicy)); + ev_oid_data.fake_policy)); { ScopedTestEVPolicy test_ev_policy(ev_metadata, kFakeFingerprint, kFakePolicy); - EXPECT_TRUE(ev_metadata->IsEVPolicyOID(kFakePolicy)); + EXPECT_TRUE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy)); EXPECT_TRUE(ev_metadata->HasEVPolicyOID(kFakeFingerprint, - kFakePolicy)); + ev_oid_data.fake_policy)); } - EXPECT_FALSE(ev_metadata->IsEVPolicyOID(kFakePolicy)); + EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy)); EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kFakeFingerprint, - kFakePolicy)); + ev_oid_data.fake_policy)); } -#endif // defined(OS_WIN) +#endif // defined(USE_NSS) || defined(OS_WIN) + +} // namespace } // namespace net diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index 4055f0a..b75098c 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -89,9 +89,31 @@ class NET_EXPORT X509Certificate }; enum VerifyFlags { + // If set, enables online revocation checking via CRLs and OCSP for the + // certificate chain. VERIFY_REV_CHECKING_ENABLED = 1 << 0, + + // If set, and the certificate being verified may be an EV certificate, + // attempt to verify the certificate according to the EV processing + // guidelines. In order to successfully verify a certificate as EV, + // either an online or offline revocation check must be successfully + // completed. To ensure it's possible to complete a revocation check, + // callers should also specify either VERIFY_REV_CHECKING_ENABLED or + // VERIFY_REV_CHECKING_ENABLED_EV_ONLY (to enable online checks), and + // VERIFY_CERT_IO_ENABLED (to enable network fetches for online checks). VERIFY_EV_CERT = 1 << 1, + + // If set, permits NSS to use the network when verifying certificates, + // such as to fetch missing intermediates or to check OCSP or CRLs. + // TODO(rsleevi): http://crbug.com/143300 - Define this flag for all + // verification engines with well-defined semantics, rather than being + // NSS only. VERIFY_CERT_IO_ENABLED = 1 << 2, + + // If set, enables online revocation checking via CRLs or OCSP, but only + // for certificates which may be EV, and only when VERIFY_EV_CERT is also + // set. + VERIFY_REV_CHECKING_ENABLED_EV_ONLY = 1 << 3, }; enum Format { diff --git a/net/base/x509_util_mac.cc b/net/base/x509_util_mac.cc index d06009e..a53b5ac 100644 --- a/net/base/x509_util_mac.cc +++ b/net/base/x509_util_mac.cc @@ -74,51 +74,29 @@ OSStatus CreateBasicX509Policy(SecPolicyRef* policy) { } OSStatus CreateRevocationPolicies(bool enable_revocation_checking, + bool enable_ev_checking, CFMutableArrayRef policies) { - // In order to actually disable revocation checking, the SecTrustRef must - // have at least one revocation policy associated with it. If none are - // present, the Apple TP will add policies according to the system - // preferences, which will enable revocation checking even if the caller - // explicitly disabled it. An OCSP policy is used, rather than a CRL policy, - // because the Apple TP will force an OCSP policy to be present and enabled - // if it believes the certificate may chain to an EV root. By explicitly - // disabling network and OCSP cache access, then even if the Apple TP - // enables OCSP checking, no revocation checking will actually succeed. - CSSM_APPLE_TP_OCSP_OPTIONS tp_ocsp_options; - memset(&tp_ocsp_options, 0, sizeof(tp_ocsp_options)); - tp_ocsp_options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; - - if (enable_revocation_checking) { - // The default for the OCSP policy is to fetch responses via the network, - // unlike the CRL policy default. The policy is further modified to - // prefer OCSP over CRLs, if both are specified on the certificate. This - // is because an OCSP response is both sufficient and typically - // significantly smaller than the CRL counterpart. - tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT; - } else { - // Effectively disable OCSP checking by making it impossible to get an - // OCSP response. Even if the Apple TP forces OCSP, no checking will - // be able to succeed. If this happens, the Apple TP will report an error - // that OCSP was unavailable, but this will be handled and suppressed in - // X509Certificate::Verify(). - tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_DISABLE_NET | - CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE; - } - - SecPolicyRef ocsp_policy; - OSStatus status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_OCSP, - &tp_ocsp_options, sizeof(tp_ocsp_options), - &ocsp_policy); - if (status) - return status; - CFArrayAppendValue(policies, ocsp_policy); - CFRelease(ocsp_policy); - - if (enable_revocation_checking) { + OSStatus status = noErr; + + // In order to bypass the system revocation checking settings, the + // SecTrustRef must have at least one revocation policy associated with it. + // Since it is not known prior to verification whether the Apple TP will + // consider a certificate as an EV candidate, the default policy used is a + // CRL policy, since it does not communicate over the network. + // If the TP believes the leaf is an EV cert, it will explicitly add an + // OCSP policy to perform the online checking, and if it doesn't believe + // that the leaf is EV, then the default CRL policy will effectively no-op. + // This behaviour is used to implement EV-only revocation checking. + if (enable_ev_checking || enable_revocation_checking) { CSSM_APPLE_TP_CRL_OPTIONS tp_crl_options; memset(&tp_crl_options, 0, sizeof(tp_crl_options)); tp_crl_options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; - tp_crl_options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET; + // Only allow network CRL fetches if the caller explicitly requests + // online revocation checking. Note that, as of OS X 10.7.2, the system + // will set force this flag on according to system policies, so + // online revocation checks cannot be completely disabled. + if (enable_revocation_checking) + tp_crl_options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET; SecPolicyRef crl_policy; status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_CRL, &tp_crl_options, @@ -129,6 +107,44 @@ OSStatus CreateRevocationPolicies(bool enable_revocation_checking, CFRelease(crl_policy); } + // If revocation checking is explicitly enabled, then add an OCSP policy + // and allow network access. If both revocation checking and EV checking + // are disabled, then the added OCSP policy will be prevented from + // accessing the network. This is done because the TP will force an OCSP + // policy to be present when it believes the certificate is EV. If network + // fetching was not explicitly disabled, then it would be as if + // enable_ev_checking was always set to true. + if (enable_revocation_checking || !enable_ev_checking) { + CSSM_APPLE_TP_OCSP_OPTIONS tp_ocsp_options; + memset(&tp_ocsp_options, 0, sizeof(tp_ocsp_options)); + tp_ocsp_options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; + + if (enable_revocation_checking) { + // The default for the OCSP policy is to fetch responses via the network, + // unlike the CRL policy default. The policy is further modified to + // prefer OCSP over CRLs, if both are specified on the certificate. This + // is because an OCSP response is both sufficient and typically + // significantly smaller than the CRL counterpart. + tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT; + } else { + // Effectively disable OCSP checking by making it impossible to get an + // OCSP response. Even if the Apple TP forces OCSP, no checking will + // be able to succeed. If this happens, the Apple TP will report an error + // that OCSP was unavailable, but this will be handled and suppressed in + // X509Certificate::Verify(). + tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_DISABLE_NET | + CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE; + } + + SecPolicyRef ocsp_policy; + status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_OCSP, &tp_ocsp_options, + sizeof(tp_ocsp_options), &ocsp_policy); + if (status) + return status; + CFArrayAppendValue(policies, ocsp_policy); + CFRelease(ocsp_policy); + } + return status; } diff --git a/net/base/x509_util_mac.h b/net/base/x509_util_mac.h index 9b629cc..9272bc7 100644 --- a/net/base/x509_util_mac.h +++ b/net/base/x509_util_mac.h @@ -41,12 +41,20 @@ OSStatus NET_EXPORT CreateSSLServerPolicy(const std::string& hostname, OSStatus NET_EXPORT CreateBasicX509Policy(SecPolicyRef* policy); // Creates security policies to control revocation checking (OCSP and CRL). -// If |enable_revocation_checking| is false, the policies returned will be -// explicitly disabled from accessing the network or the cache. This may be -// used to override system settings regarding revocation checking. +// If |enable_revocation_checking| is true, revocation checking will be +// explicitly enabled. +// If |enable_revocation_checking| is false, but |enable_ev_checking| is +// true, then the system policies for EV checking (which include checking +// for an online OCSP response) will be permitted. However, if the OS +// does not believe the certificate is EV, no revocation checking will be +// performed. +// If both are false, then the policies returned will be explicitly +// prohibited from accessing the network or the local cache, regardless of +// system settings. // If the policies are successfully created, they will be appended to // |policies|. OSStatus NET_EXPORT CreateRevocationPolicies(bool enable_revocation_checking, + bool enable_ev_checking, CFMutableArrayRef policies); // Wrapper for a CSSM_DATA_PTR that was obtained via one of the CSSM field diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 8ff168c..de020b2 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -1485,9 +1485,10 @@ class HTTPSOCSPTest : public HTTPSRequestTest { public: HTTPSOCSPTest() : context_(true), - ev_test_policy_(EVRootCAMetadata::GetInstance(), - kOCSPTestCertFingerprint, - kOCSPTestCertPolicy) { + ev_test_policy_( + new ScopedTestEVPolicy(EVRootCAMetadata::GetInstance(), + kOCSPTestCertFingerprint, + kOCSPTestCertPolicy)) { } virtual void SetUp() OVERRIDE { @@ -1540,7 +1541,7 @@ class HTTPSOCSPTest : public HTTPSRequestTest { scoped_ptr<ScopedTestRoot> test_root_; TestURLRequestContext context_; - ScopedTestEVPolicy ev_test_policy_; + scoped_ptr<ScopedTestEVPolicy> ev_test_policy_; }; static CertStatus ExpectedCertStatusForFailedOnlineRevocationCheck() { @@ -1570,8 +1571,7 @@ static bool SystemUsesChromiumEVMetadata() { #endif } -static bool -SystemSupportsOCSP() { +static bool SystemSupportsOCSP() { #if defined(USE_OPENSSL) // http://crbug.com/117478 - OpenSSL does not support OCSP. return false; @@ -1674,7 +1674,8 @@ TEST_F(HTTPSEVCRLSetTest, MissingCRLSetAndInvalidOCSP) { cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); - EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); + EXPECT_EQ(SystemUsesChromiumEVMetadata(), + static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); } TEST_F(HTTPSEVCRLSetTest, MissingCRLSetAndGoodOCSP) { @@ -1695,8 +1696,8 @@ TEST_F(HTTPSEVCRLSetTest, MissingCRLSetAndGoodOCSP) { EXPECT_EQ(SystemUsesChromiumEVMetadata(), static_cast<bool>(cert_status & CERT_STATUS_IS_EV)); - - EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); + EXPECT_EQ(SystemUsesChromiumEVMetadata(), + static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); } TEST_F(HTTPSEVCRLSetTest, ExpiredCRLSet) { @@ -1718,7 +1719,8 @@ TEST_F(HTTPSEVCRLSetTest, ExpiredCRLSet) { cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); - EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); + EXPECT_EQ(SystemUsesChromiumEVMetadata(), + static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); } TEST_F(HTTPSEVCRLSetTest, FreshCRLSet) { @@ -1741,6 +1743,34 @@ TEST_F(HTTPSEVCRLSetTest, FreshCRLSet) { EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } +TEST_F(HTTPSEVCRLSetTest, ExpiredCRLSetAndRevokedNonEVCert) { + // Test that when EV verification is requested, but online revocation + // checking is disabled, and the leaf certificate is not in fact EV, that + // no revocation checking actually happens. + if (!SystemSupportsOCSP()) { + LOG(WARNING) << "Skipping test because system doesn't support OCSP"; + return; + } + + // Unmark the certificate's OID as EV, which should disable revocation + // checking (as per the user preference) + ev_test_policy_.reset(); + + TestServer::HTTPSOptions https_options( + TestServer::HTTPSOptions::CERT_AUTO); + https_options.ocsp_status = TestServer::HTTPSOptions::OCSP_REVOKED; + SSLConfigService::SetCRLSet( + scoped_refptr<CRLSet>(CRLSet::ExpiredCRLSetForTesting())); + + CertStatus cert_status; + DoConnection(https_options, &cert_status); + + EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); + + EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); + EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + class HTTPSCRLSetTest : public HTTPSOCSPTest { protected: virtual void SetupContext(URLRequestContext* context) OVERRIDE { |