Chromium Code Reviews| 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..12987cc1b3efce815b2732361205b25ee743ddc1 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); |
|
davidben
2015/07/24 20:42:55
[Verified RFC 3339 allows fractional seconds. I di
estark
2015/07/25 00:10:31
Acknowledged.
|
| +} |
| + |
| +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 |
|
davidben
2015/07/24 20:42:55
You should probably talk to ttuttle@. Domain Relia
estark
2015/07/25 00:10:31
Acknowledged.
|
| + 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,21 +911,40 @@ 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. |
| - return false; |
| + 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. |
|
davidben
2015/07/24 20:42:55
[This was there before, so no need to do anything
estark
2015/07/25 00:10:31
Acknowledged.
|
| + return false; |
| + } |
| + |
| + bool passed_pin_check = pkp_state.CheckPublicKeyPins(hashes, failure_log); |
|
davidben
2015/07/24 20:42:55
This might be slightly clearer as
if (pkp_state.
estark
2015/07/25 00:10:31
Done.
|
| + |
| + if (passed_pin_check || !report_sender_ || |
| + report_status != ENABLE_PIN_REPORTS || pkp_state.report_uri.is_empty()) |
| + return passed_pin_check; |
|
davidben
2015/07/24 20:42:55
Nit: IIRC, multi-line if's get curlies.
estark
2015/07/25 00:10:31
Done.
|
| + |
| + 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 passed_pin_check; |
| + } |
| + |
| + report_sender_->Send(pkp_state.report_uri, serialized_report); |
| + |
| + return passed_pin_check; |
| } |
| bool TransportSecurityState::GetStaticDomainState(const std::string& host, |