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; |
} |