Index: net/http/transport_security_state.cc |
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc |
index a2d377e38813a365312823f4eea6e4d93042f605..a174e9875ad25b45e34c31cb4a3512e10dfa2736 100644 |
--- a/net/http/transport_security_state.cc |
+++ b/net/http/transport_security_state.cc |
@@ -167,6 +167,50 @@ void TransportSecurityState::SetDelegate( |
delegate_ = delegate; |
} |
+void TransportSecurityState::AddHSTSInternal( |
+ const std::string& host, |
+ TransportSecurityState::DomainState::UpgradeMode upgrade_mode, |
+ const base::Time& expiry, |
+ bool include_subdomains) { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ // Copy-and-modify the existing DomainState for this host (if any). |
+ DomainState domain_state; |
+ const std::string canonicalized_host = CanonicalizeHost(host); |
+ const std::string hashed_host = HashHost(canonicalized_host); |
+ DomainStateMap::const_iterator i = enabled_hosts_.find(hashed_host); |
+ if (i != enabled_hosts_.end()) |
+ domain_state = i->second; |
+ |
+ domain_state.sts.last_observed = base::Time::Now(); |
+ domain_state.sts.include_subdomains = include_subdomains; |
+ domain_state.sts.expiry = expiry; |
+ domain_state.sts.upgrade_mode = upgrade_mode; |
+ EnableHost(host, domain_state); |
+} |
+ |
+void TransportSecurityState::AddHPKPInternal(const std::string& host, |
+ const base::Time& last_observed, |
+ const base::Time& expiry, |
+ bool include_subdomains, |
+ const HashValueVector& hashes) { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ // Copy-and-modify the existing DomainState for this host (if any). |
+ DomainState domain_state; |
+ const std::string canonicalized_host = CanonicalizeHost(host); |
+ const std::string hashed_host = HashHost(canonicalized_host); |
+ DomainStateMap::const_iterator i = enabled_hosts_.find(hashed_host); |
+ if (i != enabled_hosts_.end()) |
+ domain_state = i->second; |
+ |
+ domain_state.pkp.last_observed = last_observed; |
+ domain_state.pkp.expiry = expiry; |
+ domain_state.pkp.include_subdomains = include_subdomains; |
+ domain_state.pkp.spki_hashes = hashes; |
+ EnableHost(host, domain_state); |
+} |
+ |
void TransportSecurityState::EnableHost(const std::string& host, |
const DomainState& state) { |
DCHECK(CalledOnValidThread()); |
@@ -178,7 +222,8 @@ void TransportSecurityState::EnableHost(const std::string& host, |
DomainState state_copy(state); |
// No need to store this value since it is redundant. (|canonicalized_host| |
// is the map key.) |
- state_copy.domain.clear(); |
+ state_copy.sts.domain.clear(); |
+ state_copy.pkp.domain.clear(); |
enabled_hosts_[HashHost(canonicalized_host)] = state_copy; |
DirtyNotify(); |
@@ -212,21 +257,24 @@ void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { |
bool dirtied = false; |
DomainStateMap::iterator i = enabled_hosts_.begin(); |
while (i != enabled_hosts_.end()) { |
- if (i->second.sts.last_observed >= time && |
- i->second.pkp.last_observed >= time) { |
- dirtied = true; |
- enabled_hosts_.erase(i++); |
- continue; |
- } |
- |
+ // Clear STS and PKP state independently. |
if (i->second.sts.last_observed >= time) { |
dirtied = true; |
i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT; |
- } else if (i->second.pkp.last_observed >= time) { |
+ } |
+ if (i->second.pkp.last_observed >= time) { |
dirtied = true; |
i->second.pkp.spki_hashes.clear(); |
i->second.pkp.expiry = base::Time(); |
} |
+ |
+ // If both are now invalid, drop the entry altogether. |
+ if (!i->second.ShouldUpgradeToSSL() && !i->second.HasPublicKeyPins()) { |
+ dirtied = true; |
+ enabled_hosts_.erase(i++); |
+ continue; |
+ } |
+ |
++i; |
} |
@@ -621,20 +669,21 @@ bool TransportSecurityState::AddHSTSHeader(const std::string& host, |
base::Time now = base::Time::Now(); |
base::TimeDelta max_age; |
- TransportSecurityState::DomainState domain_state; |
- GetDynamicDomainState(host, &domain_state); |
- if (ParseHSTSHeader(value, &max_age, &domain_state.sts.include_subdomains)) { |
- // Handle max-age == 0. |
- if (max_age.InSeconds() == 0) |
- domain_state.sts.upgrade_mode = DomainState::MODE_DEFAULT; |
- else |
- domain_state.sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
- domain_state.sts.last_observed = now; |
- domain_state.sts.expiry = now + max_age; |
- EnableHost(host, domain_state); |
- return true; |
+ bool include_subdomains; |
+ if (!ParseHSTSHeader(value, &max_age, &include_subdomains)) { |
+ return false; |
} |
- return false; |
+ |
+ // Handle max-age == 0. |
+ DomainState::UpgradeMode upgrade_mode; |
+ if (max_age.InSeconds() == 0) { |
+ upgrade_mode = DomainState::MODE_DEFAULT; |
+ } else { |
+ upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
+ } |
+ |
+ AddHSTSInternal(host, upgrade_mode, now + max_age, include_subdomains); |
+ return true; |
} |
bool TransportSecurityState::AddHPKPHeader(const std::string& host, |
@@ -644,67 +693,33 @@ bool TransportSecurityState::AddHPKPHeader(const std::string& host, |
base::Time now = base::Time::Now(); |
base::TimeDelta max_age; |
- TransportSecurityState::DomainState domain_state; |
- GetDynamicDomainState(host, &domain_state); |
- if (ParseHPKPHeader(value, |
- ssl_info.public_key_hashes, |
- &max_age, |
- &domain_state.pkp.include_subdomains, |
- &domain_state.pkp.spki_hashes)) { |
- // Handle max-age == 0. |
- if (max_age.InSeconds() == 0) |
- domain_state.pkp.spki_hashes.clear(); |
- domain_state.pkp.last_observed = now; |
- domain_state.pkp.expiry = now + max_age; |
- EnableHost(host, domain_state); |
- return true; |
+ bool include_subdomains; |
+ HashValueVector spki_hashes; |
+ if (!ParseHPKPHeader(value, ssl_info.public_key_hashes, &max_age, |
+ &include_subdomains, &spki_hashes)) { |
+ return false; |
} |
- return false; |
+ // Handle max-age == 0. |
+ if (max_age.InSeconds() == 0) |
+ spki_hashes.clear(); |
+ AddHPKPInternal(host, now, now + max_age, include_subdomains, spki_hashes); |
+ return true; |
} |
-bool TransportSecurityState::AddHSTS(const std::string& host, |
+void TransportSecurityState::AddHSTS(const std::string& host, |
const base::Time& expiry, |
bool include_subdomains) { |
DCHECK(CalledOnValidThread()); |
- |
- // Copy-and-modify the existing DomainState for this host (if any). |
- TransportSecurityState::DomainState domain_state; |
- const std::string canonicalized_host = CanonicalizeHost(host); |
- const std::string hashed_host = HashHost(canonicalized_host); |
- DomainStateMap::const_iterator i = enabled_hosts_.find( |
- hashed_host); |
- if (i != enabled_hosts_.end()) |
- domain_state = i->second; |
- |
- domain_state.sts.last_observed = base::Time::Now(); |
- domain_state.sts.include_subdomains = include_subdomains; |
- domain_state.sts.expiry = expiry; |
- domain_state.sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
- EnableHost(host, domain_state); |
- return true; |
+ AddHSTSInternal(host, DomainState::MODE_FORCE_HTTPS, expiry, |
+ include_subdomains); |
} |
-bool TransportSecurityState::AddHPKP(const std::string& host, |
+void TransportSecurityState::AddHPKP(const std::string& host, |
const base::Time& expiry, |
bool include_subdomains, |
const HashValueVector& hashes) { |
DCHECK(CalledOnValidThread()); |
- |
- // Copy-and-modify the existing DomainState for this host (if any). |
- TransportSecurityState::DomainState domain_state; |
- const std::string canonicalized_host = CanonicalizeHost(host); |
- const std::string hashed_host = HashHost(canonicalized_host); |
- DomainStateMap::const_iterator i = enabled_hosts_.find( |
- hashed_host); |
- if (i != enabled_hosts_.end()) |
- domain_state = i->second; |
- |
- domain_state.pkp.last_observed = base::Time::Now(); |
- domain_state.pkp.include_subdomains = include_subdomains; |
- domain_state.pkp.expiry = expiry; |
- domain_state.pkp.spki_hashes = hashes; |
- EnableHost(host, domain_state); |
- return true; |
+ AddHPKPInternal(host, base::Time::Now(), expiry, include_subdomains, hashes); |
} |
// static |
@@ -776,7 +791,8 @@ bool TransportSecurityState::GetStaticDomainState(const std::string& host, |
if (!DecodeHSTSPreload(host, &result)) |
return false; |
- out->domain = host.substr(result.hostname_offset); |
+ out->sts.domain = host.substr(result.hostname_offset); |
+ out->pkp.domain = out->sts.domain; |
out->sts.include_subdomains = result.sts_include_subdomains; |
out->sts.last_observed = base::GetBuildTime(); |
out->sts.upgrade_mode = |
@@ -824,6 +840,8 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host, |
base::Time current_time(base::Time::Now()); |
+ bool found_sts = false; |
+ bool found_pkp = false; |
for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
std::string host_sub_chunk(&canonicalized_host[i], |
canonicalized_host.size() - i); |
@@ -832,6 +850,7 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host, |
if (j == enabled_hosts_.end()) |
continue; |
+ // If both halves of the entry are invalid, drop it. |
if (current_time > j->second.sts.expiry && |
current_time > j->second.pkp.expiry) { |
enabled_hosts_.erase(j); |
@@ -839,21 +858,32 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host, |
continue; |
} |
- state = j->second; |
- state.domain = DNSDomainToString(host_sub_chunk); |
+ // If this is the most specific STS match, add it to the result. |
+ if (!found_sts && (i == 0 || j->second.sts.include_subdomains) && |
+ current_time <= j->second.sts.expiry && |
+ j->second.ShouldUpgradeToSSL()) { |
+ found_sts = true; |
+ state.sts = j->second.sts; |
+ state.sts.domain = DNSDomainToString(host_sub_chunk); |
+ } |
- // Succeed if we matched the domain exactly or if subdomain matches are |
- // allowed. |
- if (i == 0 || j->second.sts.include_subdomains || |
- j->second.pkp.include_subdomains) { |
- *result = state; |
- return true; |
+ // If this is the most specific PKP match, add it to the result. |
+ if (!found_pkp && (i == 0 || j->second.pkp.include_subdomains) && |
+ current_time <= j->second.pkp.expiry && j->second.HasPublicKeyPins()) { |
+ found_pkp = true; |
+ state.pkp = j->second.pkp; |
+ state.pkp.domain = DNSDomainToString(host_sub_chunk); |
} |
- return false; |
+ if (found_sts && found_pkp) |
+ break; |
} |
- return false; |
+ if (!found_sts && !found_pkp) |
+ return false; |
+ |
+ *result = state; |
+ return true; |
} |
void TransportSecurityState::AddOrUpdateEnabledHosts( |
@@ -879,12 +909,12 @@ bool TransportSecurityState::DomainState::CheckPublicKeyPins( |
if (hashes.empty()) { |
failure_log->append( |
"Rejecting empty public key chain for public-key-pinned domains: " + |
- domain); |
+ pkp.domain); |
return false; |
} |
if (HashesIntersect(pkp.bad_spki_hashes, hashes)) { |
- failure_log->append("Rejecting public key chain for domain " + domain + |
+ failure_log->append("Rejecting public key chain for domain " + pkp.domain + |
". Validated chain: " + HashesToBase64String(hashes) + |
", matches one or more bad hashes: " + |
HashesToBase64String(pkp.bad_spki_hashes)); |
@@ -899,7 +929,7 @@ bool TransportSecurityState::DomainState::CheckPublicKeyPins( |
return true; |
} |
- failure_log->append("Rejecting public key chain for domain " + domain + |
+ failure_log->append("Rejecting public key chain for domain " + pkp.domain + |
". Validated chain: " + HashesToBase64String(hashes) + |
", expected: " + HashesToBase64String(pkp.spki_hashes)); |
return false; |
@@ -910,6 +940,8 @@ bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { |
} |
bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { |
+ // Both HSTS and HPKP cause fatal SSL errors, so enable this on the presense |
+ // of either. (If neither is active, no DomainState will be returned.) |
return true; |
} |
@@ -917,6 +949,12 @@ bool TransportSecurityState::DomainState::HasPublicKeyPins() const { |
return pkp.spki_hashes.size() > 0 || pkp.bad_spki_hashes.size() > 0; |
} |
+TransportSecurityState::DomainState::STSState::STSState() { |
+} |
+ |
+TransportSecurityState::DomainState::STSState::~STSState() { |
+} |
+ |
TransportSecurityState::DomainState::PKPState::PKPState() { |
} |