| Index: net/http/transport_security_state.cc
|
| diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc
|
| index 0b209b356e834dc7018bb0d9a743711b3e02cff5..508784ea399c53817f1d7f43fb1e2b700c9696e0 100644
|
| --- a/net/http/transport_security_state.cc
|
| +++ b/net/http/transport_security_state.cc
|
| @@ -84,7 +84,12 @@ bool AddHash(const char* sha1_hash,
|
| } // namespace
|
|
|
| TransportSecurityState::TransportSecurityState()
|
| - : delegate_(NULL) {
|
| + : delegate_(NULL), enable_static_pins_(true) {
|
| +// Static pinning is only enabled for official builds to make sure that
|
| +// others don't end up with pins that cannot be easily updated.
|
| +#if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS)
|
| + enable_static_pins_ = false;
|
| +#endif
|
| DCHECK(CalledOnValidThread());
|
| }
|
|
|
| @@ -118,21 +123,36 @@ bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host,
|
| return false;
|
| }
|
|
|
| -bool TransportSecurityState::CheckPublicKeyPins(const std::string& host,
|
| - bool sni_enabled,
|
| - const HashValueVector& hashes,
|
| - std::string* failure_log) {
|
| - DomainState dynamic_state;
|
| - if (GetDynamicDomainState(host, &dynamic_state))
|
| - return dynamic_state.CheckPublicKeyPins(hashes, failure_log);
|
| +bool TransportSecurityState::CheckPublicKeyPins(
|
| + const std::string& host,
|
| + bool sni_available,
|
| + bool is_issued_by_known_root,
|
| + const HashValueVector& public_key_hashes,
|
| + 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 build is recent (very old builds should fail open so that users
|
| + // have some chance to recover).
|
| + // * the server actually has public key pins.
|
| + //
|
| + // TODO(rsleevi): http://crbug.com/391032 - Only disable static HPKP if the
|
| + // build is not timely.
|
| + if (!is_issued_by_known_root || !IsBuildTimely() ||
|
| + !HasPublicKeyPins(host, sni_available)) {
|
| + return true;
|
| + }
|
|
|
| - DomainState static_state;
|
| - if (GetStaticDomainState(host, sni_enabled, &static_state) &&
|
| - static_state.CheckPublicKeyPins(hashes, failure_log)) {
|
| - return true;
|
| + bool pins_are_valid = CheckPublicKeyPinsImpl(
|
| + host, sni_available, public_key_hashes, pinning_failure_log);
|
| + if (!pins_are_valid) {
|
| + LOG(ERROR) << *pinning_failure_log;
|
| + ReportUMAOnPinFailure(host);
|
| }
|
|
|
| - return false;
|
| + UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid);
|
| + return pins_are_valid;
|
| }
|
|
|
| bool TransportSecurityState::HasPublicKeyPins(const std::string& host,
|
| @@ -549,9 +569,13 @@ struct HSTSPreload {
|
| SecondLevelDomainName second_level_domain_name;
|
| };
|
|
|
| -static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries,
|
| - const std::string& canonicalized_host, size_t i,
|
| - TransportSecurityState::DomainState* out, bool* ret) {
|
| +static bool HasPreload(const struct HSTSPreload* entries,
|
| + size_t num_entries,
|
| + const std::string& canonicalized_host,
|
| + size_t i,
|
| + bool enable_static_pins,
|
| + TransportSecurityState::DomainState* out,
|
| + bool* ret) {
|
| for (size_t j = 0; j < num_entries; j++) {
|
| if (entries[j].length == canonicalized_host.size() - i &&
|
| memcmp(entries[j].dns_name, &canonicalized_host[i],
|
| @@ -561,26 +585,29 @@ static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries,
|
| } else {
|
| out->sts.include_subdomains = entries[j].include_subdomains;
|
| out->sts.last_observed = base::GetBuildTime();
|
| - out->pkp.include_subdomains = entries[j].include_subdomains;
|
| - out->pkp.last_observed = base::GetBuildTime();
|
| *ret = true;
|
| out->sts.upgrade_mode =
|
| TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
|
| if (!entries[j].https_required)
|
| out->sts.upgrade_mode =
|
| TransportSecurityState::DomainState::MODE_DEFAULT;
|
| - if (entries[j].pins.required_hashes) {
|
| - const char* const* sha1_hash = entries[j].pins.required_hashes;
|
| - while (*sha1_hash) {
|
| - AddHash(*sha1_hash, &out->pkp.spki_hashes);
|
| - sha1_hash++;
|
| +
|
| + if (enable_static_pins) {
|
| + out->pkp.include_subdomains = entries[j].include_subdomains;
|
| + out->pkp.last_observed = base::GetBuildTime();
|
| + if (entries[j].pins.required_hashes) {
|
| + const char* const* sha1_hash = entries[j].pins.required_hashes;
|
| + while (*sha1_hash) {
|
| + AddHash(*sha1_hash, &out->pkp.spki_hashes);
|
| + sha1_hash++;
|
| + }
|
| }
|
| - }
|
| - if (entries[j].pins.excluded_hashes) {
|
| - const char* const* sha1_hash = entries[j].pins.excluded_hashes;
|
| - while (*sha1_hash) {
|
| - AddHash(*sha1_hash, &out->pkp.bad_spki_hashes);
|
| - sha1_hash++;
|
| + if (entries[j].pins.excluded_hashes) {
|
| + const char* const* sha1_hash = entries[j].pins.excluded_hashes;
|
| + while (*sha1_hash) {
|
| + AddHash(*sha1_hash, &out->pkp.bad_spki_hashes);
|
| + sha1_hash++;
|
| + }
|
| }
|
| }
|
| }
|
| @@ -763,6 +790,24 @@ bool TransportSecurityState::IsBuildTimely() {
|
| return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
|
| }
|
|
|
| +bool TransportSecurityState::CheckPublicKeyPinsImpl(
|
| + const std::string& host,
|
| + bool sni_enabled,
|
| + const HashValueVector& hashes,
|
| + std::string* failure_log) {
|
| + DomainState dynamic_state;
|
| + if (GetDynamicDomainState(host, &dynamic_state))
|
| + return dynamic_state.CheckPublicKeyPins(hashes, failure_log);
|
| +
|
| + DomainState static_state;
|
| + if (GetStaticDomainState(host, sni_enabled, &static_state))
|
| + return static_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;
|
| +}
|
| +
|
| bool TransportSecurityState::GetStaticDomainState(const std::string& host,
|
| bool sni_enabled,
|
| DomainState* out) const {
|
| @@ -781,15 +826,22 @@ bool TransportSecurityState::GetStaticDomainState(const std::string& host,
|
| canonicalized_host.size() - i);
|
| out->domain = DNSDomainToString(host_sub_chunk);
|
| bool ret;
|
| - if (is_build_timely &&
|
| - HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out,
|
| - &ret)) {
|
| + if (is_build_timely && HasPreload(kPreloadedSTS,
|
| + kNumPreloadedSTS,
|
| + canonicalized_host,
|
| + i,
|
| + enable_static_pins_,
|
| + out,
|
| + &ret)) {
|
| return ret;
|
| }
|
| - if (sni_enabled &&
|
| - is_build_timely &&
|
| - HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i,
|
| - out, &ret)) {
|
| + if (sni_enabled && is_build_timely && HasPreload(kPreloadedSNISTS,
|
| + kNumPreloadedSNISTS,
|
| + canonicalized_host,
|
| + i,
|
| + enable_static_pins_,
|
| + out,
|
| + &ret)) {
|
| return ret;
|
| }
|
| }
|
|
|