summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-17 04:35:08 +0000
committerrsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-17 04:35:08 +0000
commitb6f2de3332cb9de2be479f82c258016da15d419c (patch)
treec6bae46b4f2afe6e48bff8ddd98edfc223ed6492 /net
parente21c547e3d5f9f502836f19c7c69075f70813f27 (diff)
downloadchromium_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.cc7
-rw-r--r--net/base/cert_verify_proc_mac.cc5
-rw-r--r--net/base/cert_verify_proc_nss.cc121
-rw-r--r--net/base/cert_verify_proc_win.cc25
-rw-r--r--net/base/ev_root_ca_metadata.cc31
-rw-r--r--net/base/ev_root_ca_metadata.h12
-rw-r--r--net/base/ev_root_ca_metadata_unittest.cc120
-rw-r--r--net/base/x509_certificate.h22
-rw-r--r--net/base/x509_util_mac.cc98
-rw-r--r--net/base/x509_util_mac.h14
-rw-r--r--net/url_request/url_request_unittest.cc50
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 {