| Index: net/http/transport_security_state.cc
|
| diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc
|
| index 8ad73581e85a2670ac48fcd5c43e3f7dd2161ba3..44f96b15c71e92062b29bb68ae1949ceb912defa 100644
|
| --- a/net/http/transport_security_state.cc
|
| +++ b/net/http/transport_security_state.cc
|
| @@ -19,6 +19,7 @@
|
|
|
| #include "base/base64.h"
|
| #include "base/build_time.h"
|
| +#include "base/json/json_writer.h"
|
| #include "base/logging.h"
|
| #include "base/memory/scoped_ptr.h"
|
| #include "base/metrics/histogram_macros.h"
|
| @@ -26,11 +27,13 @@
|
| #include "base/sha1.h"
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_util.h"
|
| +#include "base/strings/stringprintf.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| #include "base/time/time.h"
|
| #include "base/values.h"
|
| #include "crypto/sha2.h"
|
| #include "net/base/dns_util.h"
|
| +#include "net/base/host_port_pair.h"
|
| #include "net/cert/x509_cert_types.h"
|
| #include "net/cert/x509_certificate.h"
|
| #include "net/http/http_security_headers.h"
|
| @@ -47,6 +50,91 @@ namespace {
|
|
|
| #include "net/http/transport_security_state_static.h"
|
|
|
| +std::string TimeToISO8601(const base::Time& t) {
|
| + base::Time::Exploded exploded;
|
| + t.UTCExplode(&exploded);
|
| + return base::StringPrintf(
|
| + "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded.year, exploded.month,
|
| + exploded.day_of_month, exploded.hour, exploded.minute, exploded.second,
|
| + exploded.millisecond);
|
| +}
|
| +
|
| +scoped_ptr<base::ListValue> GetPEMEncodedChainAsList(
|
| + const net::X509Certificate* cert_chain) {
|
| + if (!cert_chain)
|
| + return make_scoped_ptr(new base::ListValue());
|
| +
|
| + scoped_ptr<base::ListValue> result(new base::ListValue());
|
| + std::vector<std::string> pem_encoded_chain;
|
| + cert_chain->GetPEMEncodedChain(&pem_encoded_chain);
|
| + for (const std::string& cert : pem_encoded_chain)
|
| + result->Append(make_scoped_ptr(new base::StringValue(cert)));
|
| +
|
| + return result.Pass();
|
| +}
|
| +
|
| +bool GetHPKPReport(const HostPortPair& host_port_pair,
|
| + const TransportSecurityState::PKPState& pkp_state,
|
| + const X509Certificate* served_certificate_chain,
|
| + const X509Certificate* validated_certificate_chain,
|
| + std::string* serialized_report) {
|
| + // TODO(estark): keep track of reports already sent and rate-limit,
|
| + // break loops
|
| + if (pkp_state.report_uri.is_empty())
|
| + return false;
|
| +
|
| + base::DictionaryValue report;
|
| + base::Time now = base::Time::Now();
|
| + report.SetString("date-time", TimeToISO8601(now));
|
| + report.SetString("hostname", host_port_pair.host());
|
| + report.SetInteger("port", host_port_pair.port());
|
| + report.SetString("effective-expiration-date",
|
| + TimeToISO8601(pkp_state.expiry));
|
| + report.SetBoolean("include-subdomains", pkp_state.include_subdomains);
|
| + report.SetString("noted-hostname", pkp_state.domain);
|
| +
|
| + scoped_ptr<base::ListValue> served_certificate_chain_list =
|
| + GetPEMEncodedChainAsList(served_certificate_chain);
|
| + scoped_ptr<base::ListValue> validated_certificate_chain_list =
|
| + GetPEMEncodedChainAsList(validated_certificate_chain);
|
| + report.Set("served-certificate-chain", served_certificate_chain_list.Pass());
|
| + report.Set("validated-certificate-chain",
|
| + validated_certificate_chain_list.Pass());
|
| +
|
| + scoped_ptr<base::ListValue> known_pin_list(new base::ListValue());
|
| + for (const auto& hash_value : pkp_state.spki_hashes) {
|
| + std::string known_pin;
|
| +
|
| + switch (hash_value.tag) {
|
| + case HASH_VALUE_SHA1:
|
| + known_pin += "pin-sha1=";
|
| + break;
|
| + case HASH_VALUE_SHA256:
|
| + known_pin += "pin-sha256=";
|
| + break;
|
| + }
|
| +
|
| + std::string base64_value;
|
| + base::Base64Encode(
|
| + base::StringPiece(reinterpret_cast<const char*>(hash_value.data()),
|
| + hash_value.size()),
|
| + &base64_value);
|
| + known_pin += "\"" + base64_value + "\"";
|
| +
|
| + known_pin_list->Append(
|
| + scoped_ptr<base::Value>(new base::StringValue(known_pin)));
|
| + }
|
| +
|
| + report.Set("known-pins", known_pin_list.Pass());
|
| +
|
| + if (!base::JSONWriter::Write(report, serialized_report)) {
|
| + LOG(ERROR) << "Failed to serialize HPKP violation report.";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| std::string HashesToBase64String(const HashValueVector& hashes) {
|
| std::string str;
|
| for (size_t i = 0; i != hashes.size(); ++i) {
|
| @@ -510,24 +598,28 @@ bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host) {
|
| }
|
|
|
| bool TransportSecurityState::CheckPublicKeyPins(
|
| - const std::string& host,
|
| + const HostPortPair& host_port_pair,
|
| bool is_issued_by_known_root,
|
| const HashValueVector& public_key_hashes,
|
| + const X509Certificate* served_certificate_chain,
|
| + const X509Certificate* validated_certificate_chain,
|
| + const PublicKeyPinReportStatus report_status,
|
| std::string* pinning_failure_log) {
|
| // Perform pin validation if, and only if, all these conditions obtain:
|
| //
|
| // * the server's certificate chain chains up to a known root (i.e. not a
|
| // user-installed trust anchor); and
|
| // * the server actually has public key pins.
|
| - if (!is_issued_by_known_root || !HasPublicKeyPins(host)) {
|
| + if (!is_issued_by_known_root || !HasPublicKeyPins(host_port_pair.host())) {
|
| return true;
|
| }
|
|
|
| - bool pins_are_valid =
|
| - CheckPublicKeyPinsImpl(host, public_key_hashes, pinning_failure_log);
|
| + bool pins_are_valid = CheckPublicKeyPinsImpl(
|
| + host_port_pair, public_key_hashes, served_certificate_chain,
|
| + validated_certificate_chain, report_status, pinning_failure_log);
|
| if (!pins_are_valid) {
|
| LOG(ERROR) << *pinning_failure_log;
|
| - ReportUMAOnPinFailure(host);
|
| + ReportUMAOnPinFailure(host_port_pair.host());
|
| }
|
|
|
| UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid);
|
| @@ -555,6 +647,12 @@ void TransportSecurityState::SetDelegate(
|
| delegate_ = delegate;
|
| }
|
|
|
| +void TransportSecurityState::SetReportSender(
|
| + TransportSecurityState::ReportSender* report_sender) {
|
| + DCHECK(CalledOnValidThread());
|
| + report_sender_ = report_sender;
|
| +}
|
| +
|
| void TransportSecurityState::AddHSTSInternal(
|
| const std::string& host,
|
| TransportSecurityState::STSState::UpgradeMode upgrade_mode,
|
| @@ -813,20 +911,41 @@ bool TransportSecurityState::IsBuildTimely() {
|
| }
|
|
|
| bool TransportSecurityState::CheckPublicKeyPinsImpl(
|
| - const std::string& host,
|
| + const HostPortPair& host_port_pair,
|
| const HashValueVector& hashes,
|
| + const X509Certificate* served_certificate_chain,
|
| + const X509Certificate* validated_certificate_chain,
|
| + const PublicKeyPinReportStatus report_status,
|
| std::string* failure_log) {
|
| - PKPState dynamic_state;
|
| - if (GetDynamicPKPState(host, &dynamic_state))
|
| - return dynamic_state.CheckPublicKeyPins(hashes, failure_log);
|
| -
|
| - PKPState static_pkp_state;
|
| + PKPState pkp_state;
|
| STSState unused;
|
| - if (GetStaticDomainState(host, &unused, &static_pkp_state))
|
| - return static_pkp_state.CheckPublicKeyPins(hashes, failure_log);
|
|
|
| - // HasPublicKeyPins should have returned true in order for this method
|
| - // to have been called, so if we fall through to here, it's an error.
|
| + if (!GetDynamicPKPState(host_port_pair.host(), &pkp_state) &&
|
| + !GetStaticDomainState(host_port_pair.host(), &unused, &pkp_state)) {
|
| + // HasPublicKeyPins should have returned true in order for this method
|
| + // to have been called, so if we fall through to here, it's an error.
|
| + return false;
|
| + }
|
| +
|
| + if (pkp_state.CheckPublicKeyPins(hashes, failure_log))
|
| + return true;
|
| +
|
| + if (!report_sender_ || report_status != ENABLE_PIN_REPORTS ||
|
| + pkp_state.report_uri.is_empty()) {
|
| + return false;
|
| + }
|
| +
|
| + DCHECK(pkp_state.report_uri.is_valid());
|
| +
|
| + std::string serialized_report;
|
| +
|
| + if (!GetHPKPReport(host_port_pair, pkp_state, served_certificate_chain,
|
| + validated_certificate_chain, &serialized_report)) {
|
| + return false;
|
| + }
|
| +
|
| + report_sender_->Send(pkp_state.report_uri, serialized_report);
|
| +
|
| return false;
|
| }
|
|
|
|
|