summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/cert/cert_policy_enforcer.cc165
-rw-r--r--net/cert/cert_policy_enforcer.h56
-rw-r--r--net/cert/cert_policy_enforcer_unittest.cc212
-rw-r--r--net/http/http_network_session.cc24
-rw-r--r--net/http/http_network_session.h2
-rw-r--r--net/http/http_network_transaction_unittest.cc1
-rw-r--r--net/http/http_proxy_client_socket_pool_unittest.cc1
-rw-r--r--net/http/http_stream_factory_impl_unittest.cc13
-rw-r--r--net/net.gypi3
-rw-r--r--net/socket/client_socket_pool_manager_impl.cc49
-rw-r--r--net/socket/client_socket_pool_manager_impl.h2
-rw-r--r--net/socket/ssl_client_socket.h7
-rw-r--r--net/socket/ssl_client_socket_nss.cc46
-rw-r--r--net/socket/ssl_client_socket_nss.h3
-rw-r--r--net/socket/ssl_client_socket_openssl.cc48
-rw-r--r--net/socket/ssl_client_socket_openssl.h2
-rw-r--r--net/socket/ssl_client_socket_pool.cc3
-rw-r--r--net/socket/ssl_client_socket_pool.h2
-rw-r--r--net/socket/ssl_client_socket_pool_unittest.cc21
19 files changed, 547 insertions, 113 deletions
diff --git a/net/cert/cert_policy_enforcer.cc b/net/cert/cert_policy_enforcer.cc
new file mode 100644
index 0000000..c9ce7cc
--- /dev/null
+++ b/net/cert/cert_policy_enforcer.cc
@@ -0,0 +1,165 @@
+// Copyright 2014 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.
+
+#include "net/cert/cert_policy_enforcer.h"
+
+#include <algorithm>
+
+#include "base/build_time.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/cert/ct_ev_whitelist.h"
+#include "net/cert/ct_verify_result.h"
+#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+bool IsEmbeddedSCT(const scoped_refptr<ct::SignedCertificateTimestamp>& sct) {
+ return sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED;
+}
+
+// Returns true if the current build is recent enough to ensure that
+// built-in security information (e.g. CT Logs) is fresh enough.
+// TODO(eranm): Move to base or net/base
+bool IsBuildTimely() {
+#if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD)
+ return true;
+#else
+ const base::Time build_time = base::GetBuildTime();
+ // We consider built-in information to be timely for 10 weeks.
+ return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
+#endif
+}
+
+uint32_t ApproximateMonthDifference(const base::Time& start,
+ const base::Time& end) {
+ base::Time::Exploded exploded_start;
+ base::Time::Exploded exploded_expiry;
+ start.UTCExplode(&exploded_start);
+ end.UTCExplode(&exploded_expiry);
+ uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 +
+ (exploded_expiry.month - exploded_start.month);
+
+ // Add any remainder as a full month.
+ if (exploded_expiry.day_of_month > exploded_start.day_of_month)
+ ++month_diff;
+
+ return month_diff;
+}
+
+enum CTComplianceStatus {
+ CT_NOT_COMPLIANT = 0,
+ CT_IN_WHITELIST = 1,
+ CT_ENOUGH_SCTS = 2,
+ CT_COMPLIANCE_MAX,
+};
+
+void LogCTComplianceStatusToUMA(CTComplianceStatus status) {
+ UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status,
+ CT_COMPLIANCE_MAX);
+}
+
+} // namespace
+
+CertPolicyEnforcer::CertPolicyEnforcer(size_t num_ct_logs,
+ bool require_ct_for_ev)
+ : num_ct_logs_(num_ct_logs), require_ct_for_ev_(require_ct_for_ev) {
+}
+
+CertPolicyEnforcer::~CertPolicyEnforcer() {
+}
+
+bool CertPolicyEnforcer::DoesConformToCTEVPolicy(
+ X509Certificate* cert,
+ const ct::EVCertsWhitelist* ev_whitelist,
+ const ct::CTVerifyResult& ct_result) {
+ if (!require_ct_for_ev_)
+ return true;
+
+ if (!IsBuildTimely())
+ return false;
+
+ if (IsCertificateInWhitelist(cert, ev_whitelist)) {
+ LogCTComplianceStatusToUMA(CT_IN_WHITELIST);
+ return true;
+ }
+
+ if (HasRequiredNumberOfSCTs(cert, ct_result)) {
+ LogCTComplianceStatusToUMA(CT_ENOUGH_SCTS);
+ return true;
+ }
+
+ LogCTComplianceStatusToUMA(CT_NOT_COMPLIANT);
+ return false;
+}
+
+bool CertPolicyEnforcer::IsCertificateInWhitelist(
+ X509Certificate* cert,
+ const ct::EVCertsWhitelist* ev_whitelist) {
+ bool cert_in_ev_whitelist = false;
+ if (ev_whitelist && ev_whitelist->IsValid()) {
+ const SHA256HashValue fingerprint(
+ X509Certificate::CalculateFingerprint256(cert->os_cert_handle()));
+
+ std::string truncated_fp =
+ std::string(reinterpret_cast<const char*>(fingerprint.data), 8);
+ cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp);
+
+ UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist",
+ cert_in_ev_whitelist);
+ }
+ return cert_in_ev_whitelist;
+}
+
+bool CertPolicyEnforcer::HasRequiredNumberOfSCTs(
+ X509Certificate* cert,
+ const ct::CTVerifyResult& ct_result) {
+ // TODO(eranm): Count the number of *independent* SCTs once the information
+ // about log operators is available, crbug.com/425174
+ size_t num_valid_scts = ct_result.verified_scts.size();
+ size_t num_embedded_scts =
+ std::count_if(ct_result.verified_scts.begin(),
+ ct_result.verified_scts.end(), IsEmbeddedSCT);
+
+ size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts;
+ // If at least two valid SCTs were delivered by means other than embedding
+ // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet
+ // number 3 of the "Qualifying Certificate" section of the CT/EV policy.
+ if (num_non_embedded_scts >= 2)
+ return true;
+
+ if (cert->valid_start().is_null() || cert->valid_expiry().is_null() ||
+ cert->valid_start().is_max() || cert->valid_expiry().is_max()) {
+ // Will not be able to calculate the certificate's validity period.
+ return false;
+ }
+
+ uint32_t expiry_in_months_approx =
+ ApproximateMonthDifference(cert->valid_start(), cert->valid_expiry());
+
+ // For embedded SCTs, if the certificate has the number of SCTs specified in
+ // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then
+ // it qualifies.
+ size_t num_required_embedded_scts;
+ if (expiry_in_months_approx > 39) {
+ num_required_embedded_scts = 5;
+ } else if (expiry_in_months_approx > 27) {
+ num_required_embedded_scts = 4;
+ } else if (expiry_in_months_approx >= 15) {
+ num_required_embedded_scts = 3;
+ } else {
+ num_required_embedded_scts = 2;
+ }
+
+ size_t min_acceptable_logs = std::max(num_ct_logs_, static_cast<size_t>(2u));
+ return num_embedded_scts >=
+ std::min(num_required_embedded_scts, min_acceptable_logs);
+}
+
+} // namespace net
diff --git a/net/cert/cert_policy_enforcer.h b/net/cert/cert_policy_enforcer.h
new file mode 100644
index 0000000..68039b3
--- /dev/null
+++ b/net/cert/cert_policy_enforcer.h
@@ -0,0 +1,56 @@
+// Copyright 2014 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_CERT_CERT_POLICY_ENFORCER_H
+#define NET_CERT_CERT_POLICY_ENFORCER_H
+
+#include <stddef.h>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+namespace ct {
+
+struct CTVerifyResult;
+class EVCertsWhitelist;
+
+} // namespace ct
+
+class X509Certificate;
+
+// Class for checking that a given certificate conforms to security-related
+// policies.
+class NET_EXPORT CertPolicyEnforcer {
+ public:
+ // Set the parameters for this policy enforcer:
+ // |num_ct_logs| is the number of Certificate Transparency log currently
+ // known to Chrome.
+ // |require_ct_for_ev| indicates whether Certificate Transparency presence
+ // is required for EV certificates.
+ CertPolicyEnforcer(size_t num_ct_logs, bool require_ct_for_ev);
+ virtual ~CertPolicyEnforcer();
+
+ // Returns true if the collection of SCTs for the given certificate
+ // conforms with the CT/EV policy.
+ // |cert| is the certificate for which the SCTs apply.
+ // |ct_result| must contain the result of verifying any SCTs associated with
+ // |cert| prior to invoking this method.
+ bool DoesConformToCTEVPolicy(X509Certificate* cert,
+ const ct::EVCertsWhitelist* ev_whitelist,
+ const ct::CTVerifyResult& ct_result);
+
+ private:
+ bool IsCertificateInWhitelist(X509Certificate* cert,
+ const ct::EVCertsWhitelist* ev_whitelist);
+
+ bool HasRequiredNumberOfSCTs(X509Certificate* cert,
+ const ct::CTVerifyResult& ct_result);
+
+ size_t num_ct_logs_;
+ bool require_ct_for_ev_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_POLICY_ENFORCER_H
diff --git a/net/cert/cert_policy_enforcer_unittest.cc b/net/cert/cert_policy_enforcer_unittest.cc
new file mode 100644
index 0000000..45a2e6a
--- /dev/null
+++ b/net/cert/cert_policy_enforcer_unittest.cc
@@ -0,0 +1,212 @@
+// Copyright 2014 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.
+
+#include "net/cert/cert_policy_enforcer.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/ct_ev_whitelist.h"
+#include "net/cert/ct_verify_result.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/ct_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class DummyEVCertsWhitelist : public ct::EVCertsWhitelist {
+ public:
+ DummyEVCertsWhitelist(bool is_valid_response, bool contains_hash_response)
+ : canned_is_valid_(is_valid_response),
+ canned_contains_response_(contains_hash_response) {}
+
+ bool IsValid() const override { return canned_is_valid_; }
+
+ bool ContainsCertificateHash(
+ const std::string& certificate_hash) const override {
+ return canned_contains_response_;
+ }
+
+ protected:
+ ~DummyEVCertsWhitelist() override {}
+
+ private:
+ bool canned_is_valid_;
+ bool canned_contains_response_;
+};
+
+class CertPolicyEnforcerTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ policy_enforcer_.reset(new CertPolicyEnforcer(5, true));
+
+ std::string der_test_cert(ct::GetDerEncodedX509Cert());
+ chain_ = X509Certificate::CreateFromBytes(der_test_cert.data(),
+ der_test_cert.size());
+ ASSERT_TRUE(chain_.get());
+ }
+
+ void FillResultWithSCTsOfOrigin(
+ ct::SignedCertificateTimestamp::Origin desired_origin,
+ int num_scts,
+ ct::CTVerifyResult* result) {
+ for (int i = 0; i < num_scts; ++i) {
+ scoped_refptr<ct::SignedCertificateTimestamp> sct(
+ new ct::SignedCertificateTimestamp());
+ sct->origin = desired_origin;
+ result->verified_scts.push_back(sct);
+ }
+ }
+
+ protected:
+ scoped_ptr<CertPolicyEnforcer> policy_enforcer_;
+ scoped_refptr<X509Certificate> chain_;
+};
+
+TEST_F(CertPolicyEnforcerTest, ConformsToCTEVPolicyWithNonEmbeddedSCTs) {
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(
+ ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, 2, &result);
+
+ EXPECT_TRUE(
+ policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+}
+
+TEST_F(CertPolicyEnforcerTest, ConformsToCTEVPolicyWithEmbeddedSCTs) {
+ // This chain_ is valid for 10 years - over 121 months - so requires 5 SCTs.
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+ &result);
+
+ EXPECT_TRUE(
+ policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+}
+
+TEST_F(CertPolicyEnforcerTest, DoesNotConformToCTEVPolicyNotEnoughSCTs) {
+ scoped_refptr<ct::EVCertsWhitelist> non_including_whitelist(
+ new DummyEVCertsWhitelist(true, false));
+ // This chain_ is valid for 10 years - over 121 months - so requires 5 SCTs.
+ // However, as there are only two logs, two SCTs will be required - supply one
+ // to guarantee the test fails.
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ &result);
+
+ EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
+ chain_.get(), non_including_whitelist.get(), result));
+
+ // ... but should be OK if whitelisted.
+ scoped_refptr<ct::EVCertsWhitelist> whitelist(
+ new DummyEVCertsWhitelist(true, true));
+ EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
+ chain_.get(), whitelist.get(), result));
+}
+
+TEST_F(CertPolicyEnforcerTest, DoesNotEnforceCTPolicyIfNotRequired) {
+ scoped_ptr<CertPolicyEnforcer> enforcer(new CertPolicyEnforcer(3, false));
+
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ &result);
+ // Expect true despite the chain not having enough SCTs as the policy
+ // is not enforced.
+ EXPECT_TRUE(enforcer->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+}
+
+TEST_F(CertPolicyEnforcerTest, DoesNotConformToPolicyInvalidDates) {
+ scoped_refptr<X509Certificate> no_valid_dates_cert(new X509Certificate(
+ "subject", "issuer", base::Time(), base::Time::Now()));
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+ &result);
+ EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
+ no_valid_dates_cert.get(), nullptr, result));
+ // ... but should be OK if whitelisted.
+ scoped_refptr<ct::EVCertsWhitelist> whitelist(
+ new DummyEVCertsWhitelist(true, true));
+ EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
+ chain_.get(), whitelist.get(), result));
+}
+
+TEST_F(CertPolicyEnforcerTest,
+ ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
+ // Test multiple validity periods: Over 27 months, Over 15 months (but less
+ // than 27 months),
+ // Less than 15 months.
+ const size_t validity_period[] = {12, 19, 30, 50};
+ const size_t needed_scts[] = {2, 3, 4, 5};
+
+ for (int i = 0; i < 3; ++i) {
+ size_t curr_validity = validity_period[i];
+ scoped_refptr<X509Certificate> cert(new X509Certificate(
+ "subject", "issuer", base::Time::Now(),
+ base::Time::Now() + base::TimeDelta::FromDays(31 * curr_validity)));
+ size_t curr_required_scts = needed_scts[i];
+ ct::CTVerifyResult result;
+ for (size_t j = 0; j < curr_required_scts - 1; ++j) {
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
+ 1, &result);
+ EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(cert.get(),
+ nullptr, result))
+ << " for: " << curr_validity << " and " << curr_required_scts
+ << " scts=" << result.verified_scts.size() << " j=" << j;
+ }
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ &result);
+ EXPECT_TRUE(
+ policy_enforcer_->DoesConformToCTEVPolicy(cert.get(), nullptr, result));
+ }
+}
+
+TEST_F(CertPolicyEnforcerTest,
+ ConformsToPolicyButDoesNotRequireMoreThanNumLogs) {
+ scoped_ptr<CertPolicyEnforcer> enforcer(new CertPolicyEnforcer(2, true));
+
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 2,
+ &result);
+ // Expect true despite the chain not having enough SCTs according to the
+ // policy
+ // since we only have 2 logs.
+ EXPECT_TRUE(enforcer->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+}
+
+TEST_F(CertPolicyEnforcerTest, ConformsToPolicyByEVWhitelistPresence) {
+ scoped_refptr<ct::EVCertsWhitelist> whitelist(
+ new DummyEVCertsWhitelist(true, true));
+
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ &result);
+ EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
+ chain_.get(), whitelist.get(), result));
+}
+
+TEST_F(CertPolicyEnforcerTest, IgnoresInvalidEVWhitelist) {
+ scoped_refptr<ct::EVCertsWhitelist> whitelist(
+ new DummyEVCertsWhitelist(false, true));
+
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ &result);
+ EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
+ chain_.get(), whitelist.get(), result));
+}
+
+TEST_F(CertPolicyEnforcerTest, IgnoresNullEVWhitelist) {
+ ct::CTVerifyResult result;
+ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ &result);
+ EXPECT_FALSE(
+ policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 28b3b22..656db54 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -37,21 +37,14 @@ net::ClientSocketPoolManager* CreateSocketPoolManager(
// TODO(yutak): Differentiate WebSocket pool manager and allow more
// simultaneous connections for WebSockets.
return new net::ClientSocketPoolManagerImpl(
- params.net_log,
- params.client_socket_factory
- ? params.client_socket_factory
- : net::ClientSocketFactory::GetDefaultFactory(),
- params.host_resolver,
- params.cert_verifier,
- params.channel_id_service,
- params.transport_security_state,
- params.cert_transparency_verifier,
- params.ssl_session_cache_shard,
- params.proxy_service,
- params.ssl_config_service,
- params.enable_ssl_connect_job_waiting,
- params.proxy_delegate,
- pool_type);
+ params.net_log, params.client_socket_factory
+ ? params.client_socket_factory
+ : net::ClientSocketFactory::GetDefaultFactory(),
+ params.host_resolver, params.cert_verifier, params.channel_id_service,
+ params.transport_security_state, params.cert_transparency_verifier,
+ params.cert_policy_enforcer, params.ssl_session_cache_shard,
+ params.proxy_service, params.ssl_config_service,
+ params.enable_ssl_connect_job_waiting, params.proxy_delegate, pool_type);
}
} // unnamed namespace
@@ -62,6 +55,7 @@ HttpNetworkSession::Params::Params()
: client_socket_factory(NULL),
host_resolver(NULL),
cert_verifier(NULL),
+ cert_policy_enforcer(NULL),
channel_id_service(NULL),
transport_security_state(NULL),
cert_transparency_verifier(NULL),
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 557b84e..bf93c83 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -29,6 +29,7 @@ class Value;
namespace net {
+class CertPolicyEnforcer;
class CertVerifier;
class ChannelIDService;
class ClientSocketFactory;
@@ -66,6 +67,7 @@ class NET_EXPORT HttpNetworkSession
ClientSocketFactory* client_socket_factory;
HostResolver* host_resolver;
CertVerifier* cert_verifier;
+ CertPolicyEnforcer* cert_policy_enforcer;
ChannelIDService* channel_id_service;
TransportSecurityState* transport_security_state;
CTVerifier* cert_transparency_verifier;
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 878406e..eacd06c 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -583,6 +583,7 @@ CaptureGroupNameSSLSocketPool::CaptureGroupNameSocketPool(
NULL,
NULL,
NULL,
+ NULL,
std::string(),
NULL,
NULL,
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index bdaf57f..f818432 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -171,6 +171,7 @@ class HttpProxyClientSocketPoolTest
NULL /* channel_id_store */,
NULL /* transport_security_state */,
NULL /* cert_transparency_verifier */,
+ NULL /* cert_policy_enforcer */,
std::string() /* ssl_session_cache_shard */,
session_deps_.deterministic_socket_factory.get(),
&transport_socket_pool_,
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc
index 4ac5467..beac733 100644
--- a/net/http/http_stream_factory_impl_unittest.cc
+++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -403,16 +403,17 @@ CapturePreconnectsSSLSocketPool::CapturePreconnectsSocketPool(
nullptr, // ssl_histograms
host_resolver,
cert_verifier,
- nullptr, // channel_id_store
- nullptr, // transport_security_state
- nullptr, // cert_transparency_verifier
+ nullptr, // channel_id_store
+ nullptr, // transport_security_state
+ nullptr, // cert_transparency_verifier
+ nullptr, // cert_policy_enforcer
std::string(), // ssl_session_cache_shard
- nullptr, // deterministic_socket_factory
- nullptr, // transport_socket_pool
+ nullptr, // deterministic_socket_factory
+ nullptr, // transport_socket_pool
nullptr,
nullptr,
nullptr, // ssl_config_service
- false, // enable_ssl_connect_job_waiting
+ false, // enable_ssl_connect_job_waiting
nullptr), // net_log
last_num_streams_(-1) {
}
diff --git a/net/net.gypi b/net/net.gypi
index cc9ff357..89a0503 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -64,6 +64,8 @@
'cert/cert_database.cc',
'cert/cert_database.h',
'cert/cert_database_openssl.cc',
+ 'cert/cert_policy_enforcer.cc',
+ 'cert/cert_policy_enforcer.h',
'cert/cert_status_flags.cc',
'cert/cert_status_flags.h',
'cert/cert_verifier.cc',
@@ -1297,6 +1299,7 @@
'base/upload_bytes_element_reader_unittest.cc',
'base/upload_file_element_reader_unittest.cc',
'base/url_util_unittest.cc',
+ 'cert/cert_policy_enforcer_unittest.cc',
'cert/cert_verify_proc_unittest.cc',
'cert/crl_set_unittest.cc',
'cert/ct_log_response_parser_unittest.cc',
diff --git a/net/socket/client_socket_pool_manager_impl.cc b/net/socket/client_socket_pool_manager_impl.cc
index 5ed31fc..190023c 100644
--- a/net/socket/client_socket_pool_manager_impl.cc
+++ b/net/socket/client_socket_pool_manager_impl.cc
@@ -42,6 +42,7 @@ ClientSocketPoolManagerImpl::ClientSocketPoolManagerImpl(
ChannelIDService* channel_id_service,
TransportSecurityState* transport_security_state,
CTVerifier* cert_transparency_verifier,
+ CertPolicyEnforcer* cert_policy_enforcer,
const std::string& ssl_session_cache_shard,
ProxyService* proxy_service,
SSLConfigService* ssl_config_service,
@@ -55,6 +56,7 @@ ClientSocketPoolManagerImpl::ClientSocketPoolManagerImpl(
channel_id_service_(channel_id_service),
transport_security_state_(transport_security_state),
cert_transparency_verifier_(cert_transparency_verifier),
+ cert_policy_enforcer_(cert_policy_enforcer),
ssl_session_cache_shard_(ssl_session_cache_shard),
proxy_service_(proxy_service),
ssl_config_service_(ssl_config_service),
@@ -85,6 +87,7 @@ ClientSocketPoolManagerImpl::ClientSocketPoolManagerImpl(
channel_id_service,
transport_security_state,
cert_transparency_verifier,
+ cert_policy_enforcer,
ssl_session_cache_shard,
socket_factory,
transport_socket_pool_.get(),
@@ -297,22 +300,17 @@ ClientSocketPoolManagerImpl::GetSocketPoolForHTTPProxy(
std::pair<SSLSocketPoolMap::iterator, bool> ssl_https_ret =
ssl_socket_pools_for_https_proxies_.insert(std::make_pair(
http_proxy,
- new SSLClientSocketPool(max_sockets_per_proxy_server(pool_type_),
- max_sockets_per_group(pool_type_),
- &ssl_for_https_proxy_pool_histograms_,
- host_resolver_,
- cert_verifier_,
- channel_id_service_,
- transport_security_state_,
- cert_transparency_verifier_,
- ssl_session_cache_shard_,
- socket_factory_,
- tcp_https_ret.first->second /* https proxy */,
- NULL /* no socks proxy */,
- NULL /* no http proxy */,
- ssl_config_service_.get(),
- enable_ssl_connect_job_waiting_,
- net_log_)));
+ new SSLClientSocketPool(
+ max_sockets_per_proxy_server(pool_type_),
+ max_sockets_per_group(pool_type_),
+ &ssl_for_https_proxy_pool_histograms_, host_resolver_,
+ cert_verifier_, channel_id_service_, transport_security_state_,
+ cert_transparency_verifier_, cert_policy_enforcer_,
+ ssl_session_cache_shard_, socket_factory_,
+ tcp_https_ret.first->second /* https proxy */,
+ NULL /* no socks proxy */, NULL /* no http proxy */,
+ ssl_config_service_.get(), enable_ssl_connect_job_waiting_,
+ net_log_)));
DCHECK(tcp_https_ret.second);
std::pair<HTTPProxySocketPoolMap::iterator, bool> ret =
@@ -341,21 +339,14 @@ SSLClientSocketPool* ClientSocketPoolManagerImpl::GetSocketPoolForSSLWithProxy(
SSLClientSocketPool* new_pool = new SSLClientSocketPool(
max_sockets_per_proxy_server(pool_type_),
- max_sockets_per_group(pool_type_),
- &ssl_pool_histograms_,
- host_resolver_,
- cert_verifier_,
- channel_id_service_,
- transport_security_state_,
- cert_transparency_verifier_,
- ssl_session_cache_shard_,
- socket_factory_,
+ max_sockets_per_group(pool_type_), &ssl_pool_histograms_, host_resolver_,
+ cert_verifier_, channel_id_service_, transport_security_state_,
+ cert_transparency_verifier_, cert_policy_enforcer_,
+ ssl_session_cache_shard_, socket_factory_,
NULL, /* no tcp pool, we always go through a proxy */
GetSocketPoolForSOCKSProxy(proxy_server),
- GetSocketPoolForHTTPProxy(proxy_server),
- ssl_config_service_.get(),
- enable_ssl_connect_job_waiting_,
- net_log_);
+ GetSocketPoolForHTTPProxy(proxy_server), ssl_config_service_.get(),
+ enable_ssl_connect_job_waiting_, net_log_);
std::pair<SSLSocketPoolMap::iterator, bool> ret =
ssl_socket_pools_for_proxies_.insert(std::make_pair(proxy_server,
diff --git a/net/socket/client_socket_pool_manager_impl.h b/net/socket/client_socket_pool_manager_impl.h
index f9f8d3b..ca609d5 100644
--- a/net/socket/client_socket_pool_manager_impl.h
+++ b/net/socket/client_socket_pool_manager_impl.h
@@ -65,6 +65,7 @@ class ClientSocketPoolManagerImpl : public base::NonThreadSafe,
ChannelIDService* channel_id_service,
TransportSecurityState* transport_security_state,
CTVerifier* cert_transparency_verifier,
+ CertPolicyEnforcer* cert_policy_enforcer,
const std::string& ssl_session_cache_shard,
ProxyService* proxy_service,
SSLConfigService* ssl_config_service,
@@ -114,6 +115,7 @@ class ClientSocketPoolManagerImpl : public base::NonThreadSafe,
ChannelIDService* const channel_id_service_;
TransportSecurityState* const transport_security_state_;
CTVerifier* const cert_transparency_verifier_;
+ CertPolicyEnforcer* const cert_policy_enforcer_;
const std::string ssl_session_cache_shard_;
ProxyService* const proxy_service_;
const scoped_refptr<SSLConfigService> ssl_config_service_;
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index af4f3ba..11b19a1 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -16,6 +16,7 @@
namespace net {
+class CertPolicyEnforcer;
class CertVerifier;
class ChannelIDService;
class CTVerifier;
@@ -34,23 +35,27 @@ struct SSLClientSocketContext {
: cert_verifier(NULL),
channel_id_service(NULL),
transport_security_state(NULL),
- cert_transparency_verifier(NULL) {}
+ cert_transparency_verifier(NULL),
+ cert_policy_enforcer(NULL) {}
SSLClientSocketContext(CertVerifier* cert_verifier_arg,
ChannelIDService* channel_id_service_arg,
TransportSecurityState* transport_security_state_arg,
CTVerifier* cert_transparency_verifier_arg,
+ CertPolicyEnforcer* cert_policy_enforcer_arg,
const std::string& ssl_session_cache_shard_arg)
: cert_verifier(cert_verifier_arg),
channel_id_service(channel_id_service_arg),
transport_security_state(transport_security_state_arg),
cert_transparency_verifier(cert_transparency_verifier_arg),
+ cert_policy_enforcer(cert_policy_enforcer_arg),
ssl_session_cache_shard(ssl_session_cache_shard_arg) {}
CertVerifier* cert_verifier;
ChannelIDService* channel_id_service;
TransportSecurityState* transport_security_state;
CTVerifier* cert_transparency_verifier;
+ CertPolicyEnforcer* cert_policy_enforcer;
// ssl_session_cache_shard is an opaque string that identifies a shard of the
// SSL session cache. SSL sockets with the same ssl_session_cache_shard may
// resume each other's SSL sessions but we'll never sessions between shards.
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 1319e4b..3651e8d 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -91,6 +91,7 @@
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/cert/asn1_util.h"
+#include "net/cert/cert_policy_enforcer.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/ct_ev_whitelist.h"
@@ -2831,6 +2832,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(
nss_fd_(NULL),
net_log_(transport_->socket()->NetLog()),
transport_security_state_(context.transport_security_state),
+ policy_enforcer_(context.cert_policy_enforcer),
valid_thread_id_(base::kInvalidThreadId) {
EnterFunction("");
InitCore();
@@ -3528,21 +3530,6 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) {
result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
}
- scoped_refptr<ct::EVCertsWhitelist> ev_whitelist =
- SSLConfigService::GetEVCertsWhitelist();
- if (server_cert_verify_result_.cert_status & CERT_STATUS_IS_EV) {
- if (ev_whitelist.get() && ev_whitelist->IsValid()) {
- const SHA256HashValue fingerprint(
- X509Certificate::CalculateFingerprint256(
- server_cert_verify_result_.verified_cert->os_cert_handle()));
-
- UMA_HISTOGRAM_BOOLEAN(
- "Net.SSL_EVCertificateInWhitelist",
- ev_whitelist->ContainsCertificateHash(
- std::string(reinterpret_cast<const char*>(fingerprint.data), 8)));
- }
- }
-
if (result == OK) {
// Only check Certificate Transparency if there were no other errors with
// the connection.
@@ -3566,20 +3553,31 @@ void SSLClientSocketNSS::VerifyCT() {
// Note that this is a completely synchronous operation: The CT Log Verifier
// gets all the data it needs for SCT verification and does not do any
// external communication.
- int result = cert_transparency_verifier_->Verify(
+ cert_transparency_verifier_->Verify(
server_cert_verify_result_.verified_cert.get(),
core_->state().stapled_ocsp_response,
- core_->state().sct_list_from_tls_extension,
- &ct_verify_result_,
- net_log_);
+ core_->state().sct_list_from_tls_extension, &ct_verify_result_, net_log_);
// TODO(ekasper): wipe stapled_ocsp_response and sct_list_from_tls_extension
// from the state after verification is complete, to conserve memory.
- VLOG(1) << "CT Verification complete: result " << result
- << " Invalid scts: " << ct_verify_result_.invalid_scts.size()
- << " Verified scts: " << ct_verify_result_.verified_scts.size()
- << " scts from unknown logs: "
- << ct_verify_result_.unknown_logs_scts.size();
+ if (!policy_enforcer_) {
+ server_cert_verify_result_.cert_status &= ~CERT_STATUS_IS_EV;
+ } else {
+ if (server_cert_verify_result_.cert_status & CERT_STATUS_IS_EV) {
+ scoped_refptr<ct::EVCertsWhitelist> ev_whitelist =
+ SSLConfigService::GetEVCertsWhitelist();
+ if (!policy_enforcer_->DoesConformToCTEVPolicy(
+ server_cert_verify_result_.verified_cert.get(),
+ ev_whitelist.get(), ct_verify_result_)) {
+ // TODO(eranm): Log via the BoundNetLog, see crbug.com/437766
+ VLOG(1) << "EV certificate for "
+ << server_cert_verify_result_.verified_cert->subject()
+ .GetDisplayName()
+ << " does not conform to CT policy, removing EV status.";
+ server_cert_verify_result_.cert_status &= ~CERT_STATUS_IS_EV;
+ }
+ }
+ }
}
void SSLClientSocketNSS::EnsureThreadIdAssigned() const {
diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h
index 71f09c0..10bb57f 100644
--- a/net/socket/ssl_client_socket_nss.h
+++ b/net/socket/ssl_client_socket_nss.h
@@ -36,6 +36,7 @@ class SequencedTaskRunner;
namespace net {
class BoundNetLog;
+class CertPolicyEnforcer;
class CertVerifier;
class ChannelIDService;
class CTVerifier;
@@ -199,6 +200,8 @@ class SSLClientSocketNSS : public SSLClientSocket {
TransportSecurityState* transport_security_state_;
+ CertPolicyEnforcer* const policy_enforcer_;
+
// pinning_failure_log contains a message produced by
// TransportSecurityState::CheckPublicKeyPins in the event of a
// pinning failure. It is a (somewhat) human-readable string.
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index b417e13..4e86c05 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -24,6 +24,7 @@
#include "crypto/openssl_util.h"
#include "crypto/scoped_openssl_types.h"
#include "net/base/net_errors.h"
+#include "net/cert/cert_policy_enforcer.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/ct_ev_whitelist.h"
#include "net/cert/ct_verifier.h"
@@ -380,6 +381,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
handshake_succeeded_(false),
marked_session_as_good_(false),
transport_security_state_(context.transport_security_state),
+ policy_enforcer_(context.cert_policy_enforcer),
net_log_(transport_->socket()->NetLog()),
weak_factory_(this) {
}
@@ -1144,21 +1146,6 @@ int SSLClientSocketOpenSSL::DoVerifyCertComplete(int result) {
result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
}
- scoped_refptr<ct::EVCertsWhitelist> ev_whitelist =
- SSLConfigService::GetEVCertsWhitelist();
- if (server_cert_verify_result_.cert_status & CERT_STATUS_IS_EV) {
- if (ev_whitelist.get() && ev_whitelist->IsValid()) {
- const SHA256HashValue fingerprint(
- X509Certificate::CalculateFingerprint256(
- server_cert_verify_result_.verified_cert->os_cert_handle()));
-
- UMA_HISTOGRAM_BOOLEAN(
- "Net.SSL_EVCertificateInWhitelist",
- ev_whitelist->ContainsCertificateHash(
- std::string(reinterpret_cast<const char*>(fingerprint.data), 8)));
- }
- }
-
if (result == OK) {
// Only check Certificate Transparency if there were no other errors with
// the connection.
@@ -1251,15 +1238,28 @@ void SSLClientSocketOpenSSL::VerifyCT() {
// Note that this is a completely synchronous operation: The CT Log Verifier
// gets all the data it needs for SCT verification and does not do any
// external communication.
- int result = cert_transparency_verifier_->Verify(
- server_cert_verify_result_.verified_cert.get(),
- ocsp_response, sct_list, &ct_verify_result_, net_log_);
-
- VLOG(1) << "CT Verification complete: result " << result
- << " Invalid scts: " << ct_verify_result_.invalid_scts.size()
- << " Verified scts: " << ct_verify_result_.verified_scts.size()
- << " scts from unknown logs: "
- << ct_verify_result_.unknown_logs_scts.size();
+ cert_transparency_verifier_->Verify(
+ server_cert_verify_result_.verified_cert.get(), ocsp_response, sct_list,
+ &ct_verify_result_, net_log_);
+
+ if (!policy_enforcer_) {
+ server_cert_verify_result_.cert_status &= ~CERT_STATUS_IS_EV;
+ } else {
+ if (server_cert_verify_result_.cert_status & CERT_STATUS_IS_EV) {
+ scoped_refptr<ct::EVCertsWhitelist> ev_whitelist =
+ SSLConfigService::GetEVCertsWhitelist();
+ if (!policy_enforcer_->DoesConformToCTEVPolicy(
+ server_cert_verify_result_.verified_cert.get(),
+ ev_whitelist.get(), ct_verify_result_)) {
+ // TODO(eranm): Log via the BoundNetLog, see crbug.com/437766
+ VLOG(1) << "EV certificate for "
+ << server_cert_verify_result_.verified_cert->subject()
+ .GetDisplayName()
+ << " does not conform to CT policy, removing EV status.";
+ server_cert_verify_result_.cert_status &= ~CERT_STATUS_IS_EV;
+ }
+ }
+ }
}
void SSLClientSocketOpenSSL::OnHandshakeIOComplete(int result) {
diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h
index 53d33c4..6343cb7 100644
--- a/net/socket/ssl_client_socket_openssl.h
+++ b/net/socket/ssl_client_socket_openssl.h
@@ -304,6 +304,8 @@ class SSLClientSocketOpenSSL : public SSLClientSocket {
TransportSecurityState* transport_security_state_;
+ CertPolicyEnforcer* const policy_enforcer_;
+
// pinning_failure_log contains a message produced by
// TransportSecurityState::CheckPublicKeyPins in the event of a
// pinning failure. It is a (somewhat) human-readable string.
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 56df1d8..c3b98b8 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -197,6 +197,7 @@ SSLConnectJob::SSLConnectJob(const std::string& group_name,
context.channel_id_service,
context.transport_security_state,
context.cert_transparency_verifier,
+ context.cert_policy_enforcer,
(params->privacy_mode() == PRIVACY_MODE_ENABLED
? "pm/" + context.ssl_session_cache_shard
: context.ssl_session_cache_shard)),
@@ -634,6 +635,7 @@ SSLClientSocketPool::SSLClientSocketPool(
ChannelIDService* channel_id_service,
TransportSecurityState* transport_security_state,
CTVerifier* cert_transparency_verifier,
+ CertPolicyEnforcer* cert_policy_enforcer,
const std::string& ssl_session_cache_shard,
ClientSocketFactory* client_socket_factory,
TransportClientSocketPool* transport_pool,
@@ -661,6 +663,7 @@ SSLClientSocketPool::SSLClientSocketPool(
channel_id_service,
transport_security_state,
cert_transparency_verifier,
+ cert_policy_enforcer,
ssl_session_cache_shard),
base::Bind(
&SSLClientSocketPool::GetOrCreateSSLConnectJobMessenger,
diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h
index c7f613e..59e754a 100644
--- a/net/socket/ssl_client_socket_pool.h
+++ b/net/socket/ssl_client_socket_pool.h
@@ -23,6 +23,7 @@
namespace net {
+class CertPolicyEnforcer;
class CertVerifier;
class ClientSocketFactory;
class ConnectJobFactory;
@@ -285,6 +286,7 @@ class NET_EXPORT_PRIVATE SSLClientSocketPool
ChannelIDService* channel_id_service,
TransportSecurityState* transport_security_state,
CTVerifier* cert_transparency_verifier,
+ CertPolicyEnforcer* cert_policy_enforcer,
const std::string& ssl_session_cache_shard,
ClientSocketFactory* client_socket_factory,
TransportClientSocketPool* transport_pool,
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
index 23bb111..1e4a14f 100644
--- a/net/socket/ssl_client_socket_pool_unittest.cc
+++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -143,22 +143,15 @@ class SSLClientSocketPoolTest
void CreatePool(bool transport_pool, bool http_proxy_pool, bool socks_pool) {
ssl_histograms_.reset(new ClientSocketPoolHistograms("SSLUnitTest"));
pool_.reset(new SSLClientSocketPool(
- kMaxSockets,
- kMaxSocketsPerGroup,
- ssl_histograms_.get(),
- NULL /* host_resolver */,
- NULL /* cert_verifier */,
- NULL /* channel_id_service */,
- NULL /* transport_security_state */,
- NULL /* cert_transparency_verifier */,
- std::string() /* ssl_session_cache_shard */,
- &socket_factory_,
+ kMaxSockets, kMaxSocketsPerGroup, ssl_histograms_.get(),
+ NULL /* host_resolver */, NULL /* cert_verifier */,
+ NULL /* channel_id_service */, NULL /* transport_security_state */,
+ NULL /* cert_transparency_verifier */, NULL /* cert_policy_enforcer */,
+ std::string() /* ssl_session_cache_shard */, &socket_factory_,
transport_pool ? &transport_socket_pool_ : NULL,
socks_pool ? &socks_socket_pool_ : NULL,
- http_proxy_pool ? &http_proxy_socket_pool_ : NULL,
- NULL,
- enable_ssl_connect_job_waiting_,
- NULL));
+ http_proxy_pool ? &http_proxy_socket_pool_ : NULL, NULL,
+ enable_ssl_connect_job_waiting_, NULL));
}
scoped_refptr<SSLSocketParams> SSLParams(ProxyServer::Scheme proxy,