summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/net/cert_logger.proto49
-rw-r--r--chrome/browser/net/chrome_fraudulent_certificate_reporter.cc153
-rw-r--r--chrome/browser/net/chrome_fraudulent_certificate_reporter.h61
-rw-r--r--chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc201
-rw-r--r--chrome/chrome_browser.gypi15
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--net/base/transport_security_state.cc488
-rw-r--r--net/base/transport_security_state.h15
-rw-r--r--net/base/transport_security_state_unittest.cc63
-rw-r--r--net/data/ssl/certificates/README5
-rw-r--r--net/data/ssl/certificates/test_mail_google_com.pem26
-rw-r--r--net/net.gyp1
-rw-r--r--net/url_request/fraudulent_certificate_reporter.h35
13 files changed, 894 insertions, 219 deletions
diff --git a/chrome/browser/net/cert_logger.proto b/chrome/browser/net/cert_logger.proto
new file mode 100644
index 0000000..e09ed2a
--- /dev/null
+++ b/chrome/browser/net/cert_logger.proto
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 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.
+//
+// This protobuffer is intended to store reports from Chrome users of
+// certificate pinning errors. A report will be sent from Chrome when it gets
+// e.g. a certificate for google.com that chains up to a root CA not expected by
+// Chrome for that origin, such as DigiNotar (compromised in July 2011), or
+// other pinning errors such as a blacklisted cert in the chain. The
+// report from the user will include the hostname being accessed,
+// the full certificate chain (in PEM format), and the
+// timestamp of when the client tried to access the site. A response is
+// generated by the frontend and logged, including validation and error checking
+// done on the client's input data.
+
+
+syntax = "proto2";
+
+package chrome_browser_net;
+
+// Chrome requires this.
+option optimize_for = LITE_RUNTIME;
+
+// Protocol types
+message CertLoggerRequest {
+ // The hostname being accessed (required as the cert could be valid for
+ // multiple hosts, e.g. a wildcard or a SubjectAltName.
+ required string hostname = 1;
+ // The certificate chain as a series of PEM-encoded certificates, including
+ // intermediates but not necessarily the root.
+ required string cert_chain = 2;
+ // The time (in usec since the epoch) when the client attempted to access the
+ // site generating the pinning error.
+ required int64 time_usec = 3;
+};
+
+// The response sent back to the user.
+message CertLoggerResponse {
+ enum ResponseCode {
+ OK = 1;
+ MALFORMED_CERT_DATA = 2;
+ HOST_CERT_DONT_MATCH = 3;
+ ROOT_NOT_RECOGNIZED = 4;
+ ROOT_NOT_UNEXPECTED = 5;
+ OTHER_ERROR = 6;
+ };
+ required ResponseCode response = 1;
+};
+
diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc b/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc
new file mode 100644
index 0000000..bb1b028
--- /dev/null
+++ b/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2011 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 "chrome/browser/net/chrome_fraudulent_certificate_reporter.h"
+
+#include <set>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/time.h"
+#include "chrome/browser/net/cert_logger.pb.h"
+#include "net/base/ssl_info.h"
+#include "net/base/x509_certificate.h"
+#include "net/url_request/url_request_context.h"
+
+namespace chrome_browser_net {
+
+ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter(
+ net::URLRequestContext* request_context)
+ : request_context_(request_context),
+ upload_url_(FRAUDULENT_CERTIFICATE_UPLOAD_ENDPOINT) {
+}
+
+ChromeFraudulentCertificateReporter::~ChromeFraudulentCertificateReporter() {
+ STLDeleteElements(&inflight_requests_);
+}
+
+// TODO(palmer): Move this to some globally-visible utility module.
+static bool DerToPem(const std::string& der_certificate, std::string* output) {
+ std::string b64_encoded;
+ if (!base::Base64Encode(der_certificate, &b64_encoded))
+ return false;
+
+ *output = "-----BEGIN CERTIFICATE-----\r\n";
+
+ size_t size = b64_encoded.size();
+ for (size_t i = 0; i < size; ) {
+ size_t todo = size - i;
+ if (todo > 64)
+ todo = 64;
+ *output += b64_encoded.substr(i, todo);
+ *output += "\r\n";
+ i += todo;
+ }
+
+ *output += "-----END CERTIFICATE-----\r\n";
+ return true;
+}
+
+static std::string BuildReport(
+ const std::string& hostname,
+ const net::SSLInfo& ssl_info) {
+ CertLoggerRequest request;
+ base::Time now = base::Time::Now();
+ request.set_time_usec(now.ToInternalValue());
+ request.set_hostname(hostname);
+
+ std::string der_encoded, pem_encoded;
+
+ net::X509Certificate* certificate = ssl_info.cert;
+ if (!certificate->GetDEREncoded(&der_encoded) ||
+ !DerToPem(der_encoded, &pem_encoded)) {
+ LOG(ERROR) << "Could not PEM encode DER certificate";
+ }
+
+ std::string* cert_chain = request.mutable_cert_chain();
+ *cert_chain += pem_encoded;
+
+ const net::X509Certificate::OSCertHandles& intermediates =
+ certificate->GetIntermediateCertificates();
+
+ for (net::X509Certificate::OSCertHandles::const_iterator
+ i = intermediates.begin(); i != intermediates.end(); ++i) {
+ scoped_refptr<net::X509Certificate> cert =
+ net::X509Certificate::CreateFromHandle(*i, intermediates);
+
+ if (!cert->GetDEREncoded(&der_encoded) ||
+ !DerToPem(der_encoded, &pem_encoded)) {
+ LOG(ERROR) << "Could not PEM encode DER certificate";
+ continue;
+ }
+
+ *cert_chain += pem_encoded;
+ }
+
+ std::string out;
+ request.SerializeToString(&out);
+ return out;
+}
+
+net::URLRequest* ChromeFraudulentCertificateReporter::CreateURLRequest() {
+ return new net::URLRequest(upload_url_, this);
+}
+
+void ChromeFraudulentCertificateReporter::SendReport(
+ const std::string& hostname,
+ const net::SSLInfo& ssl_info,
+ bool sni_available) {
+ // We do silent/automatic reporting ONLY for Google properties. For other
+ // domains (when we start supporting that), we will ask for user permission.
+ if (!net::TransportSecurityState::IsGooglePinnedProperty(hostname,
+ sni_available)) {
+ return;
+ }
+
+ std::string report = BuildReport(hostname, ssl_info);
+
+ net::URLRequest* url_request = CreateURLRequest();
+ url_request->set_context(request_context_);
+ url_request->set_method("POST");
+ url_request->AppendBytesToUpload(report.data(), report.size());
+
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(net::HttpRequestHeaders::kContentType,
+ "x-application/chrome-fraudulent-cert-report");
+ url_request->SetExtraRequestHeaders(headers);
+
+ inflight_requests_.insert(url_request);
+ url_request->Start();
+}
+
+void ChromeFraudulentCertificateReporter::RequestComplete(
+ net::URLRequest* request) {
+ std::set<net::URLRequest*>::iterator i = inflight_requests_.find(request);
+ DCHECK(i != inflight_requests_.end());
+ delete *i;
+ inflight_requests_.erase(i);
+}
+
+// TODO(palmer): Currently, the upload is fire-and-forget but soon we will
+// try to recover by retrying, and trying different endpoints, and
+// appealing to the user.
+void ChromeFraudulentCertificateReporter::OnResponseStarted(
+ net::URLRequest* request) {
+ const net::URLRequestStatus& status(request->status());
+ if (!status.is_success()) {
+ LOG(WARNING) << "Certificate upload failed"
+ << " status:" << status.status()
+ << " error:" << status.error();
+ } else if (request->GetResponseCode() != 200) {
+ LOG(WARNING) << "Certificate upload HTTP status: "
+ << request->GetResponseCode();
+ }
+ RequestComplete(request);
+}
+
+void ChromeFraudulentCertificateReporter::OnReadCompleted(
+ net::URLRequest* request, int bytes_read) {}
+
+} // namespace chrome_browser_net
+
diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter.h b/chrome/browser/net/chrome_fraudulent_certificate_reporter.h
new file mode 100644
index 0000000..1a78ec2
--- /dev/null
+++ b/chrome/browser/net/chrome_fraudulent_certificate_reporter.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 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 CHROME_BROWSER_NET_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_
+#define CHROME_BROWSER_NET_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "net/url_request/fraudulent_certificate_reporter.h"
+#include "net/url_request/url_request.h"
+
+namespace chrome_browser_net {
+
+// TODO(palmer): Switch to HTTPS when the error handling delegate is more
+// sophisticated. Ultimately we plan to attempt the report on many transports.
+const char FRAUDULENT_CERTIFICATE_UPLOAD_ENDPOINT[] =
+ "http://clients3.google.com/log_cert_error";
+
+class ChromeFraudulentCertificateReporter
+ : public net::FraudulentCertificateReporter,
+ public net::URLRequest::Delegate {
+ public:
+ explicit ChromeFraudulentCertificateReporter(
+ net::URLRequestContext* request_context);
+
+ virtual ~ChromeFraudulentCertificateReporter();
+
+ // Allows users of this class to override this and set their own URLRequest
+ // type. Used by SendReport.
+ virtual net::URLRequest* CreateURLRequest();
+
+ // net::FraudulentCertificateReporter
+ virtual void SendReport(const std::string& hostname,
+ const net::SSLInfo& ssl_info,
+ bool sni_available) OVERRIDE;
+
+ // net::URLRequest::Delegate
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
+ virtual void OnReadCompleted(net::URLRequest* request,
+ int bytes_read) OVERRIDE;
+
+ protected:
+ net::URLRequestContext* const request_context_;
+
+ private:
+ // Performs post-report cleanup.
+ void RequestComplete(net::URLRequest* request);
+
+ const GURL upload_url_;
+ std::set<net::URLRequest*> inflight_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeFraudulentCertificateReporter);
+};
+
+} // namespace chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_
+
diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc b/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc
new file mode 100644
index 0000000..06da291
--- /dev/null
+++ b/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2011 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 "chrome/browser/net/chrome_fraudulent_certificate_reporter.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "content/browser/browser_thread.h"
+#include "net/base/cert_test_util.h"
+#include "net/base/ssl_info.h"
+#include "net/base/transport_security_state.h"
+#include "net/base/x509_certificate.h"
+#include "net/url_request/fraudulent_certificate_reporter.h"
+#include "net/url_request/url_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using net::SSLInfo;
+
+namespace chrome_browser_net {
+
+// Builds an SSLInfo from an invalid cert chain. In this case, the cert is
+// expired; what matters is that the cert would not pass even a normal
+// sanity check. We test that we DO NOT send a fraudulent certificate report
+// in this case.
+static SSLInfo GetBadSSLInfo() {
+ SSLInfo info;
+
+ info.cert = net::ImportCertFromFile(net::GetTestCertsDirectory(),
+ "expired_cert.pem");
+ info.is_issued_by_known_root = false;
+
+ return info;
+}
+
+// Builds an SSLInfo from a "good" cert chain, as defined by IsGoodSSLInfo,
+// but which does not pass DomainState::IsChainOfPublicKeysPermitted. In this
+// case, the certificate is for mail.google.com, signed by our Chrome test
+// CA. During testing, Chrome believes this CA is part of the root system
+// store. But, this CA is not in the pin list; we test that we DO send a
+// fraudulent certicate report in this case.
+static SSLInfo GetGoodSSLInfo() {
+ SSLInfo info;
+
+ info.cert = net::ImportCertFromFile(net::GetTestCertsDirectory(),
+ "test_mail_google_com.pem");
+ info.is_issued_by_known_root = true;
+
+ return info;
+}
+
+// Checks that |info| is good as required by the SSL checks performed in
+// URLRequestHttpJob::OnStartCompleted, which are enough to trigger pin
+// checking but not sufficient to pass
+// DomainState::IsChainOfPublicKeysPermitted.
+static bool IsGoodSSLInfo(const SSLInfo& info) {
+ return info.is_valid() && info.is_issued_by_known_root;
+}
+
+class TestReporter : public ChromeFraudulentCertificateReporter {
+ public:
+ explicit TestReporter(net::URLRequestContext* request_context)
+ : ChromeFraudulentCertificateReporter(request_context) {}
+};
+
+class SendingTestReporter : public TestReporter {
+ public:
+ explicit SendingTestReporter(net::URLRequestContext* request_context)
+ : TestReporter(request_context), passed_(false) {}
+
+ // Passes if invoked with a good SSLInfo and for a hostname that is a Google
+ // pinned property.
+ virtual void SendReport(const std::string& hostname,
+ const SSLInfo& ssl_info,
+ bool sni_available) OVERRIDE {
+ EXPECT_TRUE(IsGoodSSLInfo(ssl_info));
+ EXPECT_TRUE(net::TransportSecurityState::IsGooglePinnedProperty(
+ hostname, sni_available));
+ passed_ = true;
+ }
+
+ virtual ~SendingTestReporter() {
+ // If the object is destroyed without having its SendReport method invoked,
+ // we failed.
+ EXPECT_TRUE(passed_);
+ }
+
+ bool passed_;
+};
+
+class NotSendingTestReporter : public TestReporter {
+ public:
+ explicit NotSendingTestReporter(net::URLRequestContext* request_context)
+ : TestReporter(request_context) {}
+
+ // Passes if invoked with a bad SSLInfo and for a hostname that is not a
+ // Google pinned property.
+ virtual void SendReport(const std::string& hostname,
+ const SSLInfo& ssl_info,
+ bool sni_available) OVERRIDE {
+ EXPECT_FALSE(IsGoodSSLInfo(ssl_info));
+ EXPECT_FALSE(net::TransportSecurityState::IsGooglePinnedProperty(
+ hostname, sni_available));
+ }
+};
+
+// For the first version of the feature, sending reports is "fire and forget".
+// Therefore, we test only that the Reporter tried to send a request at all.
+// In the future, when we have more sophisticated (i.e., any) error handling
+// and re-tries, we will need more sopisticated tests as well.
+//
+// This class doesn't do anything now, but in near future versions it will.
+class MockURLRequest : public net::URLRequest {
+ public:
+ MockURLRequest() : net::URLRequest(GURL(""), NULL), passed_(false) {
+ }
+
+ private:
+ bool passed_;
+};
+
+// A ChromeFraudulentCertificateReporter that uses a MockURLRequest, but is
+// otherwise normal: reports are constructed and sent in the usual way.
+class MockReporter : public ChromeFraudulentCertificateReporter {
+ public:
+ explicit MockReporter(net::URLRequestContext* request_context)
+ : ChromeFraudulentCertificateReporter(request_context) {}
+
+ virtual net::URLRequest* CreateURLRequest() OVERRIDE {
+ return new MockURLRequest();
+ }
+
+ virtual void SendReport(
+ const std::string& hostname,
+ const net::SSLInfo& ssl_info,
+ bool sni_available) {
+ DCHECK(!hostname.empty());
+ DCHECK(ssl_info.is_valid());
+ ChromeFraudulentCertificateReporter::SendReport(hostname, ssl_info, sni_available);
+ }
+};
+
+static void DoReportIsSent() {
+ scoped_refptr<ChromeURLRequestContext> context = new ChromeURLRequestContext;
+ SendingTestReporter reporter(context.get());
+ SSLInfo info = GetGoodSSLInfo();
+ reporter.SendReport("mail.google.com", info, true);
+}
+
+static void DoReportIsNotSent() {
+ scoped_refptr<ChromeURLRequestContext> context = new ChromeURLRequestContext;
+ NotSendingTestReporter reporter(context.get());
+ SSLInfo info = GetBadSSLInfo();
+ reporter.SendReport("127.0.0.1", info, true);
+}
+
+static void DoMockReportIsSent() {
+ scoped_refptr<ChromeURLRequestContext> context = new ChromeURLRequestContext;
+ MockReporter reporter(context.get());
+ SSLInfo info = GetGoodSSLInfo();
+ reporter.SendReport("mail.google.com", info, true);
+}
+
+TEST(ChromeFraudulentCertificateReporterTest, GoodBadInfo) {
+ SSLInfo good = GetGoodSSLInfo();
+ EXPECT_TRUE(IsGoodSSLInfo(good));
+
+ SSLInfo bad = GetBadSSLInfo();
+ EXPECT_FALSE(IsGoodSSLInfo(bad));
+}
+
+TEST(ChromeFraudulentCertificateReporterTest, ReportIsSent) {
+ MessageLoop loop(MessageLoop::TYPE_IO);
+ BrowserThread io_thread(BrowserThread::IO, &loop);
+ loop.PostTask(FROM_HERE, base::Bind(&DoReportIsSent));
+ loop.RunAllPending();
+}
+
+TEST(ChromeFraudulentCertificateReporterTest, MockReportIsSent) {
+ MessageLoop loop(MessageLoop::TYPE_IO);
+ BrowserThread io_thread(BrowserThread::IO, &loop);
+ loop.PostTask(FROM_HERE, base::Bind(&DoMockReportIsSent));
+ loop.RunAllPending();
+}
+
+TEST(ChromeFraudulentCertificateReporterTest, ReportIsNotSent) {
+ MessageLoop loop(MessageLoop::TYPE_IO);
+ BrowserThread io_thread(BrowserThread::IO, &loop);
+ loop.PostTask(FROM_HERE, base::Bind(&DoReportIsNotSent));
+ loop.RunAllPending();
+}
+
+} // namespace chrome_browser_net
+
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 8a5f5f4..d5b190e 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -10,6 +10,7 @@
'dependencies': [
'app/policy/cloud_policy_codegen.gyp:policy',
'browser/sync/protocol/sync_proto.gyp:sync_proto',
+ 'cert_logger_proto',
'chrome_extra_resources',
'chrome_resources',
'chrome_strings',
@@ -1552,6 +1553,8 @@
'browser/net/chrome_dns_cert_provenance_checker.h',
'browser/net/chrome_dns_cert_provenance_checker_factory.cc',
'browser/net/chrome_dns_cert_provenance_checker_factory.h',
+ 'browser/net/chrome_fraudulent_certificate_reporter.cc',
+ 'browser/net/chrome_fraudulent_certificate_reporter.h',
'browser/net/chrome_net_log.cc',
'browser/net/chrome_net_log.h',
'browser/net/chrome_network_delegate.cc',
@@ -5121,6 +5124,18 @@
],
},
{
+ # Protobuf compiler / generator for the fraudulent certificate reporting
+ # protocol buffer.
+ 'target_name': 'cert_logger_proto',
+ 'type': 'static_library',
+ 'sources': [ 'browser/net/cert_logger.proto', ],
+ 'variables': {
+ 'proto_in_dir': 'browser/net',
+ 'proto_out_dir': 'chrome/browser/net',
+ },
+ 'includes': [ '../build/protoc.gypi', ],
+ },
+ {
# Protobuf compiler / generate rule for feedback
'target_name': 'userfeedback_proto',
'type': 'static_library',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 6b6f6f7..f85fdb5 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1374,6 +1374,7 @@
'browser/metrics/thread_watcher_unittest.cc',
'browser/mock_keychain_mac.cc',
'browser/mock_keychain_mac.h',
+ 'browser/net/chrome_fraudulent_certificate_reporter_unittest.cc',
'browser/net/chrome_net_log_unittest.cc',
'browser/net/connection_tester_unittest.cc',
'browser/net/gaia/gaia_oauth_fetcher_unittest.cc',
diff --git a/net/base/transport_security_state.cc b/net/base/transport_security_state.cc
index 0926fc0..b634cfc 100644
--- a/net/base/transport_security_state.cc
+++ b/net/base/transport_security_state.cc
@@ -844,126 +844,114 @@ static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries,
return false;
}
-// IsPreloadedSTS returns true if the canonicalized hostname should always be
-// considered to have STS enabled.
-bool TransportSecurityState::IsPreloadedSTS(
- const std::string& canonicalized_host,
- bool sni_available,
- DomainState* out) {
- DCHECK(CalledOnValidThread());
-
- out->preloaded = true;
- out->mode = DomainState::MODE_STRICT;
- out->include_subdomains = false;
-
- // These hashes are base64 encodings of SHA1 hashes for cert public keys.
- static const char kCertPKHashVerisignClass3[] =
+// These hashes are base64 encodings of SHA1 hashes for cert public keys.
+static const char kCertPKHashVerisignClass3[] =
"sha1/4n972HfV354KP560yw4uqe/baXc=";
- static const char kCertPKHashVerisignClass3G3[] =
+static const char kCertPKHashVerisignClass3G3[] =
"sha1/IvGeLsbqzPxdI0b0wuj2xVTdXgc=";
- static const char kCertPKHashGoogle1024[] =
+static const char kCertPKHashGoogle1024[] =
"sha1/QMVAHW+MuvCLAO3vse6H0AWzuc0=";
- static const char kCertPKHashGoogle2048[] =
+static const char kCertPKHashGoogle2048[] =
"sha1/AbkhxY0L343gKf+cki7NVWp+ozk=";
- static const char kCertPKHashEquifaxSecureCA[] =
+static const char kCertPKHashEquifaxSecureCA[] =
"sha1/SOZo+SvSspXXR9gjIBBPM5iQn9Q=";
- static const char* const kGoogleAcceptableCerts[] = {
- kCertPKHashVerisignClass3,
- kCertPKHashVerisignClass3G3,
- kCertPKHashGoogle1024,
- kCertPKHashGoogle2048,
- kCertPKHashEquifaxSecureCA,
- 0,
- };
-
- static const char kCertRapidSSL[] =
+static const char* const kGoogleAcceptableCerts[] = {
+ kCertPKHashVerisignClass3,
+ kCertPKHashVerisignClass3G3,
+ kCertPKHashGoogle1024,
+ kCertPKHashGoogle2048,
+ kCertPKHashEquifaxSecureCA,
+ NULL,
+};
+
+static const char kCertRapidSSL[] =
"sha1/m9lHYJYke9k0GtVZ+bXSQYE8nDI=";
- static const char kCertDigiCertEVRoot[] =
+static const char kCertDigiCertEVRoot[] =
"sha1/gzF+YoVCU9bXeDGQ7JGQVumRueM=";
- static const char kCertTor1[] =
+static const char kCertTor1[] =
"sha1/juNxSTv9UANmpC9kF5GKpmWNx3Y=";
- static const char kCertTor2[] =
+static const char kCertTor2[] =
"sha1/lia43lPolzSPVIq34Dw57uYcLD8=";
- static const char kCertTor3[] =
+static const char kCertTor3[] =
"sha1/rzEyQIKOh77j87n5bjWUNguXF8Y=";
- static const char* const kTorAcceptableCerts[] = {
- kCertRapidSSL,
- kCertDigiCertEVRoot,
- kCertTor1,
- kCertTor2,
- kCertTor3,
- 0,
- };
-
- static const char kCertVerisignClass1[] =
+static const char* const kTorAcceptableCerts[] = {
+ kCertRapidSSL,
+ kCertDigiCertEVRoot,
+ kCertTor1,
+ kCertTor2,
+ kCertTor3,
+ NULL,
+};
+
+static const char kCertVerisignClass1[] =
"sha1/I0PRSKJViZuUfUYaeX7ATP7RcLc=";
- static const char kCertVerisignClass3[] =
+static const char kCertVerisignClass3[] =
"sha1/4n972HfV354KP560yw4uqe/baXc=";
- static const char kCertVerisignClass3_G4[] =
+static const char kCertVerisignClass3_G4[] =
"sha1/7WYxNdMb1OymFMQp4xkGn5TBJlA=";
- static const char kCertVerisignClass4_G3[] =
+static const char kCertVerisignClass4_G3[] =
"sha1/PANDaGiVHPNpKri0Jtq6j+ki5b0=";
- static const char kCertVerisignClass3_G3[] =
+static const char kCertVerisignClass3_G3[] =
"sha1/IvGeLsbqzPxdI0b0wuj2xVTdXgc=";
- static const char kCertVerisignClass1_G3[] =
+static const char kCertVerisignClass1_G3[] =
"sha1/VRmyeKyygdftp6vBg5nDu2kEJLU=";
- static const char kCertVerisignClass2_G3[] =
+static const char kCertVerisignClass2_G3[] =
"sha1/Wr7Fddyu87COJxlD/H8lDD32YeM=";
- static const char kCertVerisignClass3_G2[] =
+static const char kCertVerisignClass3_G2[] =
"sha1/GiG0lStik84Ys2XsnA6TTLOB5tQ=";
- static const char kCertVerisignClass2_G2[] =
+static const char kCertVerisignClass2_G2[] =
"sha1/Eje6RRfurSkm/cHN/r7t8t7ZFFw=";
- static const char kCertVerisignClass3_G5[] =
+static const char kCertVerisignClass3_G5[] =
"sha1/sYEIGhmkwJQf+uiVKMEkyZs0rMc=";
- static const char kCertVerisignUniversal[] =
+static const char kCertVerisignUniversal[] =
"sha1/u8I+KQuzKHcdrT6iTb30I70GsD0=";
- static const char kCertTwitter1[] =
+static const char kCertTwitter1[] =
"sha1/Vv7zwhR9TtOIN/29MFI4cgHld40=";
- static const char kCertGeoTrustGlobal[] =
+static const char kCertGeoTrustGlobal[] =
"sha1/wHqYaI2J+6sFZAwRfap9ZbjKzE4=";
- static const char kCertGeoTrustGlobal2[] =
+static const char kCertGeoTrustGlobal2[] =
"sha1/cTg28gIxU0crbrplRqkQFVggBQk=";
- static const char kCertGeoTrustUniversal[] =
+static const char kCertGeoTrustUniversal[] =
"sha1/h+hbY1PGI6MSjLD/u/VR/lmADiI=";
- static const char kCertGeoTrustUniversal2[] =
+static const char kCertGeoTrustUniversal2[] =
"sha1/Xk9ThoXdT57KX9wNRW99UbHcm3s=";
- static const char kCertGeoTrustPrimary[] =
+static const char kCertGeoTrustPrimary[] =
"sha1/sBmJ5+/7Sq/LFI9YRjl2IkFQ4bo=";
- static const char kCertGeoTrustPrimaryG2[] =
+static const char kCertGeoTrustPrimaryG2[] =
"sha1/vb6nG6txV/nkddlU0rcngBqCJoI=";
- static const char kCertGeoTrustPrimaryG3[] =
+static const char kCertGeoTrustPrimaryG3[] =
"sha1/nKmNAK90Dd2BgNITRaWLjy6UONY=";
- static const char* const kTwitterComAcceptableCerts[] = {
- kCertVerisignClass1,
- kCertVerisignClass3,
- kCertVerisignClass3_G4,
- kCertVerisignClass4_G3,
- kCertVerisignClass3_G3,
- kCertVerisignClass1_G3,
- kCertVerisignClass2_G3,
- kCertVerisignClass3_G2,
- kCertVerisignClass2_G2,
- kCertVerisignClass3_G5,
- kCertVerisignUniversal,
- kCertGeoTrustGlobal,
- kCertGeoTrustGlobal2,
- kCertGeoTrustUniversal,
- kCertGeoTrustUniversal2,
- kCertGeoTrustPrimary,
- kCertGeoTrustPrimaryG2,
- kCertGeoTrustPrimaryG3,
- kCertTwitter1,
- 0,
- };
-
- // kTestAcceptableCerts doesn't actually match any public keys and is used
- // with "pinningtest.appspot.com", below, to test if pinning is active.
- static const char* const kTestAcceptableCerts[] = {
- "sha1/AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
- };
+static const char* const kTwitterComAcceptableCerts[] = {
+ kCertVerisignClass1,
+ kCertVerisignClass3,
+ kCertVerisignClass3_G4,
+ kCertVerisignClass4_G3,
+ kCertVerisignClass3_G3,
+ kCertVerisignClass1_G3,
+ kCertVerisignClass2_G3,
+ kCertVerisignClass3_G2,
+ kCertVerisignClass2_G2,
+ kCertVerisignClass3_G5,
+ kCertVerisignUniversal,
+ kCertGeoTrustGlobal,
+ kCertGeoTrustGlobal2,
+ kCertGeoTrustUniversal,
+ kCertGeoTrustUniversal2,
+ kCertGeoTrustPrimary,
+ kCertGeoTrustPrimaryG2,
+ kCertGeoTrustPrimaryG3,
+ kCertTwitter1,
+ NULL,
+};
+
+// kTestAcceptableCerts doesn't actually match any public keys and is used
+// with "pinningtest.appspot.com", below, to test if pinning is active.
+static const char* const kTestAcceptableCerts[] = {
+ "sha1/AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
+};
#if defined(OS_CHROMEOS)
static const bool kTwitterHSTS = true;
@@ -971,140 +959,202 @@ bool TransportSecurityState::IsPreloadedSTS(
static const bool kTwitterHSTS = false;
#endif
- // In the medium term this list is likely to just be hardcoded here. This,
- // slightly odd, form removes the need for additional relocations records.
- static const struct HSTSPreload kPreloadedSTS[] = {
- // (*.)google.com, iff using SSL must use an acceptable certificate.
- {12, true, "\006google\003com", false, kGoogleAcceptableCerts },
- {25, true, "\013pinningtest\007appspot\003com", false,
- kTestAcceptableCerts },
- // Now we force HTTPS for subtrees of google.com.
- {19, true, "\006health\006google\003com", true, kGoogleAcceptableCerts },
- {21, true, "\010checkout\006google\003com", true, kGoogleAcceptableCerts },
- {19, true, "\006chrome\006google\003com", true, kGoogleAcceptableCerts },
- {17, true, "\004docs\006google\003com", true, kGoogleAcceptableCerts },
- {18, true, "\005sites\006google\003com", true, kGoogleAcceptableCerts },
- {25, true, "\014spreadsheets\006google\003com", true,
- kGoogleAcceptableCerts },
- {22, false, "\011appengine\006google\003com", true,
- kGoogleAcceptableCerts },
- {22, true, "\011encrypted\006google\003com", true, kGoogleAcceptableCerts },
- {21, true, "\010accounts\006google\003com", true, kGoogleAcceptableCerts },
- {21, true, "\010profiles\006google\003com", true, kGoogleAcceptableCerts },
- {17, true, "\004mail\006google\003com", true, kGoogleAcceptableCerts },
- {23, true, "\012talkgadget\006google\003com", true,
- kGoogleAcceptableCerts },
- {17, true, "\004talk\006google\003com", true, kGoogleAcceptableCerts },
- {29, true, "\020hostedtalkgadget\006google\003com", true,
- kGoogleAcceptableCerts },
- {17, true, "\004plus\006google\003com", true, kGoogleAcceptableCerts },
- // Other Google-related domains that must use HTTPS.
- {20, true, "\006market\007android\003com", true, kGoogleAcceptableCerts },
- {26, true, "\003ssl\020google-analytics\003com", true,
- kGoogleAcceptableCerts },
- {18, true, "\005drive\006google\003com", true, kGoogleAcceptableCerts },
- {16, true, "\012googleplex\003com", true, kGoogleAcceptableCerts },
- // Other Google-related domains that must use an acceptable certificate
- // iff using SSL.
- {11, true, "\005ytimg\003com", false, kGoogleAcceptableCerts },
- {23, true, "\021googleusercontent\003com", false, kGoogleAcceptableCerts },
- {13, true, "\007youtube\003com", false, kGoogleAcceptableCerts },
- {16, true, "\012googleapis\003com", false, kGoogleAcceptableCerts },
- {22, true, "\020googleadservices\003com", false, kGoogleAcceptableCerts },
- {16, true, "\012googlecode\003com", false, kGoogleAcceptableCerts },
- {13, true, "\007appspot\003com", false, kGoogleAcceptableCerts },
- {23, true, "\021googlesyndication\003com", false, kGoogleAcceptableCerts },
- {17, true, "\013doubleclick\003net", false, kGoogleAcceptableCerts },
- {17, true, "\003ssl\007gstatic\003com", false, kGoogleAcceptableCerts },
- // Exclude the learn.doubleclick.net subdomain because it uses a different
- // CA.
- {23, true, "\005learn\013doubleclick\003net", false, 0 },
- // Now we force HTTPS for other sites that have requested it.
- {16, false, "\003www\006paypal\003com", true, 0 },
- {16, false, "\003www\006elanex\003biz", true, 0 },
- {12, true, "\006jottit\003com", true, 0 },
- {19, true, "\015sunshinepress\003org", true, 0 },
- {21, false, "\003www\013noisebridge\003net", true, 0 },
- {10, false, "\004neg9\003org", true, 0 },
- {12, true, "\006riseup\003net", true, 0 },
- {11, false, "\006factor\002cc", true, 0 },
- {22, false, "\007members\010mayfirst\003org", true, 0 },
- {22, false, "\007support\010mayfirst\003org", true, 0 },
- {17, false, "\002id\010mayfirst\003org", true, 0 },
- {20, false, "\005lists\010mayfirst\003org", true, 0 },
- {19, true, "\015splendidbacon\003com", true, 0 },
- {28, false, "\016aladdinschools\007appspot\003com", true, 0 },
- {14, true, "\011ottospora\002nl", true, 0 },
- {25, false, "\003www\017paycheckrecords\003com", true, 0 },
- {14, false, "\010lastpass\003com", true, 0 },
- {18, false, "\003www\010lastpass\003com", true, 0 },
- {14, true, "\010keyerror\003com", true, 0 },
- {13, false, "\010entropia\002de", true, 0 },
- {17, false, "\003www\010entropia\002de", true, 0 },
- {11, true, "\005romab\003com", true, 0 },
- {16, false, "\012logentries\003com", true, 0 },
- {20, false, "\003www\012logentries\003com", true, 0 },
- {12, true, "\006stripe\003com", true, 0 },
- {27, true, "\025cloudsecurityalliance\003org", true, 0 },
- {15, true, "\005login\004sapo\002pt", true, 0 },
- {19, true, "\015mattmccutchen\003net", true, 0 },
- {11, true, "\006betnet\002fr", true, 0 },
- {13, true, "\010uprotect\002it", true, 0 },
- {14, false, "\010squareup\003com", true, 0 },
- {9, true, "\004cert\002se", true, 0 },
- {11, true, "\006crypto\002is", true, 0 },
- {20, true, "\005simon\007butcher\004name", true, 0 },
- {10, true, "\004linx\003net", true, 0 },
- {13, false, "\007dropcam\003com", true, 0 },
- {17, false, "\003www\007dropcam\003com", true, 0 },
- {30, true, "\010ebanking\014indovinabank\003com\002vn", true, 0 },
- {13, false, "\007epoxate\003com", true, 0 },
- {16, false, "\012torproject\003org", true, kTorAcceptableCerts },
- {21, true, "\004blog\012torproject\003org", true, kTorAcceptableCerts },
- {22, true, "\005check\012torproject\003org", true, kTorAcceptableCerts },
- {20, true, "\003www\012torproject\003org", true, kTorAcceptableCerts },
- {22, true, "\003www\014moneybookers\003com", true, 0 },
- {17, false, "\013ledgerscope\003net", true, 0 },
- {21, false, "\003www\013ledgerscope\003net", true, 0 },
- {10, false, "\004kyps\003net", true, 0 },
- {14, false, "\003www\004kyps\003net", true, 0 },
- {17, true, "\003app\007recurly\003com", true, 0 },
- {17, true, "\003api\007recurly\003com", true, 0 },
- {13, false, "\007greplin\003com", true, 0 },
- {17, false, "\003www\007greplin\003com", true, 0 },
- {27, true, "\006luneta\016nearbuysystems\003com", true, 0 },
- {12, true, "\006ubertt\003org", true, 0 },
-
- {13, false, "\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
- {17, true, "\003www\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
- {17, true, "\003api\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
- {19, true, "\005oauth\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
- {20, true, "\006mobile\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
- {17, true, "\003dev\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
- {22, true, "\010business\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
+// In the medium term this list is likely to just be hardcoded here. This
+// slightly odd form removes the need for additional relocations records.
+static const struct HSTSPreload kPreloadedSTS[] = {
+ // (*.)google.com, iff using SSL must use an acceptable certificate.
+ {12, true, "\006google\003com", false, kGoogleAcceptableCerts },
+ {25, true, "\013pinningtest\007appspot\003com", false,
+ kTestAcceptableCerts },
+ // Now we force HTTPS for subtrees of google.com.
+ {19, true, "\006health\006google\003com", true, kGoogleAcceptableCerts },
+ {21, true, "\010checkout\006google\003com", true, kGoogleAcceptableCerts },
+ {19, true, "\006chrome\006google\003com", true, kGoogleAcceptableCerts },
+ {17, true, "\004docs\006google\003com", true, kGoogleAcceptableCerts },
+ {18, true, "\005sites\006google\003com", true, kGoogleAcceptableCerts },
+ {25, true, "\014spreadsheets\006google\003com", true,
+ kGoogleAcceptableCerts },
+ {22, false, "\011appengine\006google\003com", true,
+ kGoogleAcceptableCerts },
+ {22, true, "\011encrypted\006google\003com", true, kGoogleAcceptableCerts },
+ {21, true, "\010accounts\006google\003com", true, kGoogleAcceptableCerts },
+ {21, true, "\010profiles\006google\003com", true, kGoogleAcceptableCerts },
+ {17, true, "\004mail\006google\003com", true, kGoogleAcceptableCerts },
+ {23, true, "\012talkgadget\006google\003com", true,
+ kGoogleAcceptableCerts },
+ {17, true, "\004talk\006google\003com", true, kGoogleAcceptableCerts },
+ {29, true, "\020hostedtalkgadget\006google\003com", true,
+ kGoogleAcceptableCerts },
+ {17, true, "\004plus\006google\003com", true, kGoogleAcceptableCerts },
+ // Other Google-related domains that must use HTTPS.
+ {20, true, "\006market\007android\003com", true, kGoogleAcceptableCerts },
+ {26, true, "\003ssl\020google-analytics\003com", true,
+ kGoogleAcceptableCerts },
+ {18, true, "\005drive\006google\003com", true, kGoogleAcceptableCerts },
+ {16, true, "\012googleplex\003com", true, kGoogleAcceptableCerts },
+ // Other Google-related domains that must use an acceptable certificate
+ // iff using SSL.
+ {11, true, "\005ytimg\003com", false, kGoogleAcceptableCerts },
+ {23, true, "\021googleusercontent\003com", false, kGoogleAcceptableCerts },
+ {13, true, "\007youtube\003com", false, kGoogleAcceptableCerts },
+ {16, true, "\012googleapis\003com", false, kGoogleAcceptableCerts },
+ {22, true, "\020googleadservices\003com", false, kGoogleAcceptableCerts },
+ {16, true, "\012googlecode\003com", false, kGoogleAcceptableCerts },
+ {13, true, "\007appspot\003com", false, kGoogleAcceptableCerts },
+ {23, true, "\021googlesyndication\003com", false, kGoogleAcceptableCerts },
+ {17, true, "\013doubleclick\003net", false, kGoogleAcceptableCerts },
+ {17, true, "\003ssl\007gstatic\003com", false, kGoogleAcceptableCerts },
+ // Exclude the learn.doubleclick.net subdomain because it uses a different
+ // CA.
+ {23, true, "\005learn\013doubleclick\003net", false, 0 },
+ // Now we force HTTPS for other sites that have requested it.
+ {16, false, "\003www\006paypal\003com", true, 0 },
+ {16, false, "\003www\006elanex\003biz", true, 0 },
+ {12, true, "\006jottit\003com", true, 0 },
+ {19, true, "\015sunshinepress\003org", true, 0 },
+ {21, false, "\003www\013noisebridge\003net", true, 0 },
+ {10, false, "\004neg9\003org", true, 0 },
+ {12, true, "\006riseup\003net", true, 0 },
+ {11, false, "\006factor\002cc", true, 0 },
+ {22, false, "\007members\010mayfirst\003org", true, 0 },
+ {22, false, "\007support\010mayfirst\003org", true, 0 },
+ {17, false, "\002id\010mayfirst\003org", true, 0 },
+ {20, false, "\005lists\010mayfirst\003org", true, 0 },
+ {19, true, "\015splendidbacon\003com", true, 0 },
+ {28, false, "\016aladdinschools\007appspot\003com", true, 0 },
+ {14, true, "\011ottospora\002nl", true, 0 },
+ {25, false, "\003www\017paycheckrecords\003com", true, 0 },
+ {14, false, "\010lastpass\003com", true, 0 },
+ {18, false, "\003www\010lastpass\003com", true, 0 },
+ {14, true, "\010keyerror\003com", true, 0 },
+ {13, false, "\010entropia\002de", true, 0 },
+ {17, false, "\003www\010entropia\002de", true, 0 },
+ {11, true, "\005romab\003com", true, 0 },
+ {16, false, "\012logentries\003com", true, 0 },
+ {20, false, "\003www\012logentries\003com", true, 0 },
+ {12, true, "\006stripe\003com", true, 0 },
+ {27, true, "\025cloudsecurityalliance\003org", true, 0 },
+ {15, true, "\005login\004sapo\002pt", true, 0 },
+ {19, true, "\015mattmccutchen\003net", true, 0 },
+ {11, true, "\006betnet\002fr", true, 0 },
+ {13, true, "\010uprotect\002it", true, 0 },
+ {14, false, "\010squareup\003com", true, 0 },
+ {9, true, "\004cert\002se", true, 0 },
+ {11, true, "\006crypto\002is", true, 0 },
+ {20, true, "\005simon\007butcher\004name", true, 0 },
+ {10, true, "\004linx\003net", true, 0 },
+ {13, false, "\007dropcam\003com", true, 0 },
+ {17, false, "\003www\007dropcam\003com", true, 0 },
+ {30, true, "\010ebanking\014indovinabank\003com\002vn", true, 0 },
+ {13, false, "\007epoxate\003com", true, 0 },
+ {16, false, "\012torproject\003org", true, kTorAcceptableCerts },
+ {21, true, "\004blog\012torproject\003org", true, kTorAcceptableCerts },
+ {22, true, "\005check\012torproject\003org", true, kTorAcceptableCerts },
+ {20, true, "\003www\012torproject\003org", true, kTorAcceptableCerts },
+ {22, true, "\003www\014moneybookers\003com", true, 0 },
+ {17, false, "\013ledgerscope\003net", true, 0 },
+ {21, false, "\003www\013ledgerscope\003net", true, 0 },
+ {10, false, "\004kyps\003net", true, 0 },
+ {14, false, "\003www\004kyps\003net", true, 0 },
+ {17, true, "\003app\007recurly\003com", true, 0 },
+ {17, true, "\003api\007recurly\003com", true, 0 },
+ {13, false, "\007greplin\003com", true, 0 },
+ {17, false, "\003www\007greplin\003com", true, 0 },
+ {27, true, "\006luneta\016nearbuysystems\003com", true, 0 },
+ {12, true, "\006ubertt\003org", true, 0 },
+
+ {13, false, "\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
+ {17, true, "\003www\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
+ {17, true, "\003api\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
+ {19, true, "\005oauth\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
+ {20, true, "\006mobile\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
+ {17, true, "\003dev\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
+ {22, true, "\010business\007twitter\003com", kTwitterHSTS, kTwitterComAcceptableCerts },
#if 0
- // Twitter CDN pins disabled in order to track down pinning failures --agl
- {22, true, "\010platform\007twitter\003com", false, kTwitterCDNAcceptableCerts },
- {15, true, "\003si0\005twimg\003com", false, kTwitterCDNAcceptableCerts },
- {23, true, "\010twimg0-a\010akamaihd\003net", false, kTwitterCDNAcceptableCerts },
+ // Twitter CDN pins disabled in order to track down pinning failures --agl
+ {22, true, "\010platform\007twitter\003com", false, kTwitterCDNAcceptableCerts },
+ {15, true, "\003si0\005twimg\003com", false, kTwitterCDNAcceptableCerts },
+ {23, true, "\010twimg0-a\010akamaihd\003net", false, kTwitterCDNAcceptableCerts },
#endif
- };
- static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS);
-
- static const struct HSTSPreload kPreloadedSNISTS[] = {
- // These SNI-only domains must always use HTTPS.
- {11, false, "\005gmail\003com", true, kGoogleAcceptableCerts },
- {16, false, "\012googlemail\003com", true, kGoogleAcceptableCerts },
- {15, false, "\003www\005gmail\003com", true, kGoogleAcceptableCerts },
- {20, false, "\003www\012googlemail\003com", true, kGoogleAcceptableCerts },
- // These SNI-only domains must use an acceptable certificate iff using
- // HTTPS.
- {22, true, "\020google-analytics\003com", false, kGoogleAcceptableCerts },
- // www. requires SNI.
- {18, true, "\014googlegroups\003com", false, kGoogleAcceptableCerts },
- };
- static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS);
+};
+static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS);
+
+static const struct HSTSPreload kPreloadedSNISTS[] = {
+ // These SNI-only domains must always use HTTPS.
+ {11, false, "\005gmail\003com", true, kGoogleAcceptableCerts },
+ {16, false, "\012googlemail\003com", true, kGoogleAcceptableCerts },
+ {15, false, "\003www\005gmail\003com", true, kGoogleAcceptableCerts },
+ {20, false, "\003www\012googlemail\003com", true, kGoogleAcceptableCerts },
+ // These SNI-only domains must use an acceptable certificate iff using
+ // HTTPS.
+ {22, true, "\020google-analytics\003com", false, kGoogleAcceptableCerts },
+ // www. requires SNI.
+ {18, true, "\014googlegroups\003com", false, kGoogleAcceptableCerts },
+};
+static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS);
+
+// Returns true if there is an HSTSPreload entry for the host in |entries|, and
+// if its |required_hashes| member is identical (by address) to |certs|.
+static bool ScanForHostAndCerts(
+ const std::string& canonicalized_host,
+ const struct HSTSPreload* entries,
+ size_t num_entries,
+ const char* const certs[]) {
+ bool hit = false;
+
+ for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
+ for (size_t j = 0; j < num_entries; j++) {
+ const struct HSTSPreload& entry = entries[j];
+
+ if (i != 0 && !entry.include_subdomains)
+ continue;
+
+ if (entry.length == canonicalized_host.size() - i &&
+ memcmp(entry.dns_name, &canonicalized_host[i], entry.length) == 0) {
+ hit = entry.required_hashes == certs;
+ // Return immediately upon exact match:
+ if (i == 0)
+ return hit;
+ }
+ }
+ }
+
+ return hit;
+}
+
+// static
+bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host,
+ bool sni_available) {
+ std::string canonicalized_host = CanonicalizeHost(host);
+
+ if (ScanForHostAndCerts(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS,
+ kGoogleAcceptableCerts)) {
+ return true;
+ }
+
+ if (sni_available) {
+ if (ScanForHostAndCerts(canonicalized_host, kPreloadedSNISTS, kNumPreloadedSNISTS,
+ kGoogleAcceptableCerts)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// IsPreloadedSTS returns true if the canonicalized hostname should always be
+// considered to have STS enabled.
+bool TransportSecurityState::IsPreloadedSTS(
+ const std::string& canonicalized_host,
+ bool sni_available,
+ DomainState* out) {
+ DCHECK(CalledOnValidThread());
+
+ out->preloaded = true;
+ out->mode = DomainState::MODE_STRICT;
+ out->include_subdomains = false;
for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
std::string host_sub_chunk(&canonicalized_host[i],
diff --git a/net/base/transport_security_state.h b/net/base/transport_security_state.h
index f65da62..6832daf 100644
--- a/net/base/transport_security_state.h
+++ b/net/base/transport_security_state.h
@@ -113,6 +113,21 @@ class NET_EXPORT TransportSecurityState
const std::string& host,
bool sni_available);
+ // Returns true if we have a preloaded certificate pin for the |host| and if
+ // its set of required certificates is the set we expect for Google
+ // properties. If |sni_available| is true, searches the preloads defined for
+ // SNI-using hosts as well as the usual preload list.
+ //
+ // Note that like HasMetadata, if |host| matches both an exact entry and is a
+ // subdomain of another entry, the exact match determines the return value.
+ //
+ // This function is used by ChromeFraudulentCertificateReporter to determine
+ // whether or not we can automatically post fraudulent certificate reports to
+ // Google; we only do so automatically in cases when the user was trying to
+ // connect to Google in the first place.
+ static bool IsGooglePinnedProperty(const std::string& host,
+ bool sni_available);
+
// Deletes all records created since a given time.
void DeleteSince(const base::Time& time);
diff --git a/net/base/transport_security_state_unittest.cc b/net/base/transport_security_state_unittest.cc
index 8bbf641..d9337a9 100644
--- a/net/base/transport_security_state_unittest.cc
+++ b/net/base/transport_security_state_unittest.cc
@@ -1032,4 +1032,67 @@ TEST_F(TransportSecurityStateTest, DISABLED_ParseSidePinsFuzz) {
}
}
+TEST_F(TransportSecurityStateTest, GooglePinnedProperties) {
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "www.example.com", true));
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "www.paypal.com", true));
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "mail.twitter.com", true));
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "www.google.com.int", true));
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "jottit.com", true));
+ // learn.doubleclick.net has a more specific match than
+ // *.doubleclick.com, and has 0 or NULL for its required certs.
+ // This test ensures that the exact-match-preferred behavior
+ // works.
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "learn.doubleclick.net", true));
+
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "encrypted.google.com", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "mail.google.com", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "accounts.google.com", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "doubleclick.net", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "ad.doubleclick.net", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "youtube.com", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "www.profiles.google.com", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "checkout.google.com", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "googleadservices.com", true));
+
+ // Test with sni_enabled false:
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "www.example.com", false));
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "www.paypal.com", false));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "checkout.google.com", false));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "googleadservices.com", false));
+
+ // Test some SNI hosts:
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "gmail.com", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "googlegroups.com", true));
+ EXPECT_TRUE(TransportSecurityState::IsGooglePinnedProperty(
+ "www.googlegroups.com", true));
+ // Expect to fail for SNI hosts when not searching the SNI list:
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "gmail.com", false));
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "googlegroups.com", false));
+ EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
+ "www.googlegroups.com", false));
+}
+
} // namespace net
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README
index 8ca5c9c..d782cdb4 100644
--- a/net/data/ssl/certificates/README
+++ b/net/data/ssl/certificates/README
@@ -51,3 +51,8 @@ unit tests.
- google_diginotar.pem
- diginotar_public_ca_2025.pem : A certificate chain for the regression test
of http://crbug.com/94673
+
+- test_mail_google_com.pem : A certificate signed by the test CA for
+ "mail.google.com". Because it is signed by that CA instead of the true CA
+ for that host, it will fail the
+ TransportSecurityState::IsChainOfPublicKeysPermitted test.
diff --git a/net/data/ssl/certificates/test_mail_google_com.pem b/net/data/ssl/certificates/test_mail_google_com.pem
new file mode 100644
index 0000000..d72d562
--- /dev/null
+++ b/net/data/ssl/certificates/test_mail_google_com.pem
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEXDCCAkSgAwIBAgIBBjANBgkqhkiG9w0BAQUFADBgMRAwDgYDVQQDEwdUZXN0
+IENBMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
+TW91bnRhaW4gVmlldzESMBAGA1UEChMJQ2VydCBUZXN0MB4XDTExMTAxMTE5MTYy
+MVoXDTEzMDcyNzAwMDAwMFowbTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
+b3JuaWExEjAQBgNVBAoTCUNlcnQgVGVzdDEbMBkGA1UECxMSR29hdCBUZWxlcG9y
+dGF0aW9uMRgwFgYDVQQDEw9tYWlsLmdvb2dsZS5jb20wXDANBgkqhkiG9w0BAQEF
+AANLADBIAkEAvy9N7zZ2yuMamRGUDc7KiLHq+OwVkfmvDRsrj77+MMR1DkUx1Qez
+s+tKtm6dyi5mariRL5ChbgIBqNYhb/cecQIDAQABo4HbMIHYMBIGA1UdEwEB/wQI
+MAYBAf8CAQEwDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQQFF3/oZUdPuM69r0i
+Gl5tEK5e7TCBkgYDVR0jBIGKMIGHgBRdzn+Z49QZQTFPxs+xJfVar+OXMaFkpGIw
+YDEQMA4GA1UEAxMHVGVzdCBDQTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
+b3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxEjAQBgNVBAoTCUNlcnQgVGVz
+dIIJANRRk9Q/3tlOMA0GCSqGSIb3DQEBBQUAA4ICAQCPO6wgG6cFmu5ZgAN9q+dS
+BVrMiJhHj62Tlw7qNjD+VAfidTTtQPM8T0y2LtNe2epO6jDOyIpRwsKkFi5mozcs
+Dd3CfXAs7fkdY4ZnAxjXhhk1fvMkomR6CfTHEwcGkfwVm2MDozZmYbS83OP+E82B
++yKA41ppbw75/meJzH4nSECBd/Whzi8AuuX6e3bSae6XEAdhBQoLHyNAvZ0IEeCb
+sI3DvXdpIP0LyYJH6+F/KG5Jugby44HuAK1MBn9/f5tYplucOj5cyw/fYWd8REGD
+Ob71lh9/eZVcYjvbF6LxlizZQ+DNHV2QkHvSQqAACDbpFCUcU9KO5xvN8RaVtFmJ
+sDuHtxDDXFcXHhLh6bcC2KFrsmwEV68jmek0++eMa/W99ADzNWUWCmGoyZQafP2e
+eqQ6Ry8wgH+ZVkhQaaGk4fCKZATpX7//qdj7IzO52Kpx0dwsW7mHxPjdRKQzThkn
+lwFSiKByJDMOm9JbjpGf52JsCX4OSFuHCRcc2TB867xKRfBoAXE06fMS2lTwAcQh
+3vdzO0gEv9WOvdvehvngcrWzGwIdGaP6BBXi+9b5wPBR8ravMPAgQBXg01vME+/+
+TkpEaCFACOttO0YkVqG6lFFT1wigsh3k4/+Eyh/RsLTsFObZBJsMLetbY/XzwhTf
+LyeXa2sT1sk6l+EfrzWS1Q==
+-----END CERTIFICATE-----
diff --git a/net/net.gyp b/net/net.gyp
index 6bc9340..370a585 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -635,6 +635,7 @@
'udp/udp_socket_libevent.h',
'udp/udp_socket_win.cc',
'udp/udp_socket_win.h',
+ 'url_request/fraudulent_certificate_reporter.h',
'url_request/url_request.cc',
'url_request/url_request.h',
'url_request/url_request_about_job.cc',
diff --git a/net/url_request/fraudulent_certificate_reporter.h b/net/url_request/fraudulent_certificate_reporter.h
new file mode 100644
index 0000000..7522c13
--- /dev/null
+++ b/net/url_request/fraudulent_certificate_reporter.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 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_URL_REQUEST_FRAUDULENT_CERTIFICATE_REPORTER_H_
+#define NET_URL_REQUEST_FRAUDULENT_CERTIFICATE_REPORTER_H_
+
+#include <string>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+class SSLInfo;
+
+// FraudulentCertificateReporter is an interface for asynchronously
+// reporting certificate chains that fail the certificate pinning
+// check.
+class NET_EXPORT FraudulentCertificateReporter {
+ public:
+ virtual ~FraudulentCertificateReporter() {}
+
+ // Sends a report to the report collection server containing the |ssl_info|
+ // associated with a connection to |hostname|. If |sni_available| is true,
+ // searches the SNI transport security metadata as well as the usual
+ // transport security metadata when determining policy for sending the report.
+ virtual void SendReport(const std::string& hostname,
+ const SSLInfo& ssl_info,
+ bool sni_available) = 0;
+};
+
+} // namespace net
+
+#endif // NET_URL_REQUEST_FRAUDULENT_CERTIFICATE_REPORTER_H_
+