diff options
author | estark <estark@chromium.org> | 2016-03-09 19:46:47 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-10 03:48:24 +0000 |
commit | 1614475fe5a3366df2c4b73e33cf17e89d8b3a72 (patch) | |
tree | 1b6ba88d91585644aa4b8f8ee129aaf44404fc94 /net/url_request | |
parent | 2234bb3f2dc8f63de5eb2b6d25ed3a9ba2a49f2f (diff) | |
download | chromium_src-1614475fe5a3366df2c4b73e33cf17e89d8b3a72.zip chromium_src-1614475fe5a3366df2c4b73e33cf17e89d8b3a72.tar.gz chromium_src-1614475fe5a3366df2c4b73e33cf17e89d8b3a72.tar.bz2 |
Implement a skeleton version of Expect CT reports
This CL implements the skeleton of Expect CT reporting, which will
eventually send reports when an opted-in site fails to conform to CT
policy as determined by CertPolicyEnforcer. Introduces a new
TransportSecurityState interface called ExpectCTReporter
for observing CT policy violations that pass through
URLRequestHTTPJob. There is a skeleton implementation called
ChromeExpectCTReporter in chrome/browser/ssl which will eventually
use a net::CertificateReportSender to send reports.
BUG=568806
Review URL: https://codereview.chromium.org/1579063002
Cr-Commit-Position: refs/heads/master@{#380322}
Diffstat (limited to 'net/url_request')
-rw-r--r-- | net/url_request/url_request_http_job.cc | 23 | ||||
-rw-r--r-- | net/url_request/url_request_http_job.h | 5 | ||||
-rw-r--r-- | net/url_request/url_request_test_util.cc | 4 | ||||
-rw-r--r-- | net/url_request/url_request_test_util.h | 7 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 122 |
5 files changed, 161 insertions, 0 deletions
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc index 39a4a41..ccc38d5 100644 --- a/net/url_request/url_request_http_job.cc +++ b/net/url_request/url_request_http_job.cc @@ -404,6 +404,7 @@ void URLRequestHttpJob::NotifyHeadersComplete() { // The ordering of these calls is not important. ProcessStrictTransportSecurityHeader(); ProcessPublicKeyPinsHeader(); + ProcessExpectCTHeader(); // Handle the server notification of a new SDCH dictionary. SdchManager* sdch_manager(request()->context()->sdch_manager()); @@ -904,6 +905,28 @@ void URLRequestHttpJob::ProcessPublicKeyPinsHeader() { } } +void URLRequestHttpJob::ProcessExpectCTHeader() { + DCHECK(response_info_); + TransportSecurityState* security_state = + request_->context()->transport_security_state(); + const SSLInfo& ssl_info = response_info_->ssl_info; + + // Only accept Expect CT headers on HTTPS connections that have no + // certificate errors. + if (!ssl_info.is_valid() || IsCertStatusError(ssl_info.cert_status) || + !security_state) { + return; + } + + // Only process the first Expect-CT header value. + HttpResponseHeaders* headers = GetResponseHeaders(); + std::string value; + if (headers->EnumerateHeader(nullptr, "Expect-CT", &value)) { + security_state->ProcessExpectCTHeader( + value, HostPortPair::FromURL(request_info_.url), ssl_info); + } +} + void URLRequestHttpJob::OnStartCompleted(int result) { RecordTimer(); diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h index 56051fcc..c914ff1 100644 --- a/net/url_request/url_request_http_job.h +++ b/net/url_request/url_request_http_job.h @@ -98,6 +98,11 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob { // Processes the Public-Key-Pins header, if one exists. void ProcessPublicKeyPinsHeader(); + // Processes the Expect-CT header, if one exists. This header + // indicates that the server wants the user agent to send a report + // when a connection violates the Expect CT policy. + void ProcessExpectCTHeader(); + // |result| should be OK, or the request is canceled. void OnHeadersReceivedCallback(int result); void OnStartCompleted(int result); diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc index c209b4b..44a7da6 100644 --- a/net/url_request/url_request_test_util.cc +++ b/net/url_request/url_request_test_util.cc @@ -16,6 +16,7 @@ #include "base/threading/worker_pool.h" #include "net/base/host_port_pair.h" #include "net/cert/cert_verifier.h" +#include "net/cert/ct_verifier.h" #include "net/dns/mock_host_resolver.h" #include "net/http/http_network_session.h" #include "net/http/http_response_headers.h" @@ -108,6 +109,9 @@ void TestURLRequestContext::Init() { params.proxy_delegate = proxy_delegate(); params.host_resolver = host_resolver(); params.cert_verifier = cert_verifier(); + params.cert_transparency_verifier = cert_transparency_verifier(); + if (ct_policy_enforcer()) + params.ct_policy_enforcer = ct_policy_enforcer(); params.transport_security_state = transport_security_state(); params.proxy_service = proxy_service(); params.ssl_config_service = ssl_config_service(); diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h index 27fa9fc..dc8d0e1 100644 --- a/net/url_request/url_request_test_util.h +++ b/net/url_request/url_request_test_util.h @@ -84,6 +84,11 @@ class TestURLRequestContext : public URLRequestContext { context_storage_.set_sdch_manager(std::move(sdch_manager)); } + CTPolicyEnforcer* ct_policy_enforcer() { return ct_policy_enforcer_; } + void set_ct_policy_enforcer(CTPolicyEnforcer* ct_policy_enforcer) { + ct_policy_enforcer_ = ct_policy_enforcer; + } + private: bool initialized_; @@ -97,6 +102,8 @@ class TestURLRequestContext : public URLRequestContext { ProxyDelegate* proxy_delegate_; + CTPolicyEnforcer* ct_policy_enforcer_; + protected: URLRequestContextStorage context_storage_; }; diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 68e9997..a2da83a 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -59,6 +59,9 @@ #include "net/base/upload_data_stream.h" #include "net/base/upload_file_element_reader.h" #include "net/base/url_util.h" +#include "net/cert/ct_policy_status.h" +#include "net/cert/ct_verifier.h" +#include "net/cert/ct_verify_result.h" #include "net/cert/ev_root_ca_metadata.h" #include "net/cert/mock_cert_verifier.h" #include "net/cert/test_root_certs.h" @@ -5888,6 +5891,7 @@ TEST_F(URLRequestTestHTTP, STSNotProcessedOnIP) { #endif namespace { +const char kExpectCTStaticHostname[] = "preloaded-expect-ct.badssl.com"; const char kHPKPReportUri[] = "https://hpkp-report.test"; } // namespace @@ -6239,6 +6243,124 @@ TEST_F(URLRequestTestHTTP, ProcessSTSAndPKP2) { EXPECT_FALSE(pkp_state.include_subdomains); } +// An ExpectCTReporter that records the number of times OnExpectCTFailed() was +// called. +class MockExpectCTReporter : public TransportSecurityState::ExpectCTReporter { + public: + MockExpectCTReporter() : num_failures_(0) {} + ~MockExpectCTReporter() override {} + + void OnExpectCTFailed(const HostPortPair& host_port_pair, + const GURL& report_uri, + const net::SSLInfo& ssl_info) override { + num_failures_++; + } + + uint32_t num_failures() { return num_failures_; } + + private: + uint32_t num_failures_; +}; + +// A CTVerifier that returns net::OK for every certificate. +class MockCTVerifier : public CTVerifier { + public: + MockCTVerifier() {} + ~MockCTVerifier() override {} + + int Verify(X509Certificate* cert, + const std::string& stapled_ocsp_response, + const std::string& sct_list_from_tls_extension, + ct::CTVerifyResult* result, + const BoundNetLog& net_log) override { + return net::OK; + } + + void SetObserver(Observer* observer) override {} +}; + +// A CTPolicyEnforcer that returns a default CertPolicyCompliance value +// for every certificate. +class MockCTPolicyEnforcer : public CTPolicyEnforcer { + public: + MockCTPolicyEnforcer() + : default_result_( + ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS) {} + ~MockCTPolicyEnforcer() override {} + + ct::CertPolicyCompliance DoesConformToCertPolicy( + X509Certificate* cert, + const SCTList& verified_scts, + const BoundNetLog& net_log) override { + return default_result_; + } + + void set_default_result(ct::CertPolicyCompliance default_result) { + default_result_ = default_result; + } + + private: + ct::CertPolicyCompliance default_result_; +}; + +// Tests that Expect CT headers are processed correctly. +TEST_F(URLRequestTestHTTP, ExpectCTHeader) { + EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); + https_test_server.SetSSLConfig( + net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); + https_test_server.ServeFilesFromSourceDirectory( + base::FilePath(kTestFilePath)); + ASSERT_TRUE(https_test_server.Start()); + + MockExpectCTReporter reporter; + TransportSecurityState transport_security_state; + transport_security_state.enable_static_expect_ct_ = true; + transport_security_state.SetExpectCTReporter(&reporter); + + // Set up a MockCertVerifier to accept the certificate that the server sends. + scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate(); + ASSERT_TRUE(cert); + MockCertVerifier cert_verifier; + CertVerifyResult verify_result; + verify_result.verified_cert = cert; + verify_result.is_issued_by_known_root = true; + cert_verifier.AddResultForCert(cert.get(), verify_result, OK); + + // Set up a MockCTVerifier and MockCTPolicyEnforcer to trigger an Expect CT + // violation. + MockCTVerifier ct_verifier; + MockCTPolicyEnforcer ct_policy_enforcer; + ct_policy_enforcer.set_default_result( + ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS); + + TestNetworkDelegate network_delegate; + // Use a MockHostResolver (which by default maps all hosts to + // 127.0.0.1) so that the request can be sent to a site on the Expect + // CT preload list. + MockHostResolver host_resolver; + TestURLRequestContext context(true); + context.set_host_resolver(&host_resolver); + context.set_transport_security_state(&transport_security_state); + context.set_network_delegate(&network_delegate); + context.set_cert_verifier(&cert_verifier); + context.set_cert_transparency_verifier(&ct_verifier); + context.set_ct_policy_enforcer(&ct_policy_enforcer); + context.Init(); + + // Now send a request to trigger the violation. + TestDelegate d; + GURL url = https_test_server.GetURL("/expect-ct-header.html"); + GURL::Replacements replace_host; + replace_host.SetHostStr(kExpectCTStaticHostname); + url = url.ReplaceComponents(replace_host); + scoped_ptr<URLRequest> violating_request( + context.CreateRequest(url, DEFAULT_PRIORITY, &d)); + violating_request->Start(); + base::RunLoop().Run(); + + EXPECT_EQ(1u, reporter.num_failures()); +} + #endif // !defined(OS_IOS) TEST_F(URLRequestTestHTTP, ContentTypeNormalizationTest) { |