summaryrefslogtreecommitdiffstats
path: root/net/url_request
diff options
context:
space:
mode:
authorestark <estark@chromium.org>2016-03-09 19:46:47 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-10 03:48:24 +0000
commit1614475fe5a3366df2c4b73e33cf17e89d8b3a72 (patch)
tree1b6ba88d91585644aa4b8f8ee129aaf44404fc94 /net/url_request
parent2234bb3f2dc8f63de5eb2b6d25ed3a9ba2a49f2f (diff)
downloadchromium_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.cc23
-rw-r--r--net/url_request/url_request_http_job.h5
-rw-r--r--net/url_request/url_request_test_util.cc4
-rw-r--r--net/url_request/url_request_test_util.h7
-rw-r--r--net/url_request/url_request_unittest.cc122
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) {