Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5186)

Unified Diff: net/http/transport_security_state.cc

Issue 1149753002: Normalize hostnames before searching for HSTS/HPKP preloads (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: ODR cleanup Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/http/transport_security_state.h ('k') | net/http/transport_security_state_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/http/transport_security_state.cc
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc
index 7147b48287f19f2bd9d1eaeb13e2beae24a5d340..d92bd473c4bc315f79e8bc137a9dfa645af46061 100644
--- a/net/http/transport_security_state.cc
+++ b/net/http/transport_security_state.cc
@@ -45,6 +45,8 @@ namespace net {
namespace {
+#include "net/http/transport_security_state_static.h"
+
std::string HashesToBase64String(const HashValueVector& hashes) {
std::string str;
for (size_t i = 0; i != hashes.size(); ++i) {
@@ -82,223 +84,13 @@ bool AddHash(const char* sha1_hash,
return true;
}
-} // namespace
-
-TransportSecurityState::TransportSecurityState()
- : 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());
-}
-
-TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state)
- : iterator_(state.enabled_hosts_.begin()),
- end_(state.enabled_hosts_.end()) {
-}
-
-TransportSecurityState::Iterator::~Iterator() {}
-
-bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) {
- DomainState state;
- if (GetStaticDomainState(host, &state))
- return true;
- return GetDynamicDomainState(host, &state);
-}
-
-bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host) {
- DomainState dynamic_state;
- if (GetDynamicDomainState(host, &dynamic_state))
- return dynamic_state.ShouldUpgradeToSSL();
-
- DomainState static_state;
- if (GetStaticDomainState(host, &static_state) &&
- static_state.ShouldUpgradeToSSL()) {
- return true;
- }
-
- return false;
-}
-
-bool TransportSecurityState::CheckPublicKeyPins(
- const std::string& host,
- 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 server actually has public key pins.
- if (!is_issued_by_known_root || !HasPublicKeyPins(host)) {
- return true;
- }
-
- bool pins_are_valid = CheckPublicKeyPinsImpl(
- host, public_key_hashes, pinning_failure_log);
- if (!pins_are_valid) {
- LOG(ERROR) << *pinning_failure_log;
- ReportUMAOnPinFailure(host);
- }
-
- UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid);
- return pins_are_valid;
-}
-
-bool TransportSecurityState::HasPublicKeyPins(const std::string& host) {
- DomainState dynamic_state;
- if (GetDynamicDomainState(host, &dynamic_state))
- return dynamic_state.HasPublicKeyPins();
-
- DomainState static_state;
- if (GetStaticDomainState(host, &static_state)) {
- if (static_state.HasPublicKeyPins())
- return true;
- }
-
- return false;
-}
-
-void TransportSecurityState::SetDelegate(
- TransportSecurityState::Delegate* delegate) {
- DCHECK(CalledOnValidThread());
- 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());
-
- const std::string canonicalized_host = CanonicalizeHost(host);
- if (canonicalized_host.empty())
- return;
-
- DomainState state_copy(state);
- // No need to store this value since it is redundant. (|canonicalized_host|
- // is the map key.)
- state_copy.sts.domain.clear();
- state_copy.pkp.domain.clear();
-
- enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
- DirtyNotify();
-}
-
-bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
- DCHECK(CalledOnValidThread());
-
- const std::string canonicalized_host = CanonicalizeHost(host);
- if (canonicalized_host.empty())
- return false;
-
- DomainStateMap::iterator i = enabled_hosts_.find(
- HashHost(canonicalized_host));
- if (i != enabled_hosts_.end()) {
- enabled_hosts_.erase(i);
- DirtyNotify();
- return true;
- }
- return false;
-}
-
-void TransportSecurityState::ClearDynamicData() {
- DCHECK(CalledOnValidThread());
- enabled_hosts_.clear();
-}
-
-void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) {
- DCHECK(CalledOnValidThread());
-
- bool dirtied = false;
- DomainStateMap::iterator i = enabled_hosts_.begin();
- while (i != enabled_hosts_.end()) {
- // Clear STS and PKP state independently.
- if (i->second.sts.last_observed >= time) {
- dirtied = true;
- i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT;
- }
- 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;
- }
-
- if (dirtied)
- DirtyNotify();
-}
-
-TransportSecurityState::~TransportSecurityState() {
- DCHECK(CalledOnValidThread());
-}
-
-void TransportSecurityState::DirtyNotify() {
- DCHECK(CalledOnValidThread());
-
- if (delegate_)
- delegate_->StateIsDirty(this);
-}
-
-// static
-std::string TransportSecurityState::CanonicalizeHost(const std::string& host) {
+// Converts |hostname| from dotted form ("www.google.com") to the form
+// used in DNS: "\x03www\x06google\x03com", lowercases that, and returns
+// the result.
+std::string CanonicalizeHost(const std::string& host) {
// We cannot perform the operations as detailed in the spec here as |host|
// has already undergone IDN processing before it reached us. Thus, we check
// that there are no invalid characters in the host and lowercase the result.
-
std::string new_host;
if (!DNSDomainFromDot(host, &new_host)) {
// DNSDomainFromDot can fail if any label is > 63 bytes or if the whole
@@ -454,8 +246,6 @@ class HuffmanDecoder {
const size_t tree_bytes_;
};
-#include "net/http/transport_security_state_static.h"
-
// PreloadResult is the result of resolving a specific name in the preloaded
// data.
struct PreloadResult {
@@ -495,7 +285,7 @@ struct PreloadResult {
//
// Dispatch tables are always given in order, but the "end of string" (zero)
// value always comes before an entry for '.'.
-bool DecodeHSTSPreloadRaw(const std::string& hostname,
+bool DecodeHSTSPreloadRaw(const std::string& search_hostname,
bool* out_found,
PreloadResult* out) {
HuffmanDecoder huffman(kHSTSHuffmanTree, sizeof(kHSTSHuffmanTree));
@@ -506,9 +296,30 @@ bool DecodeHSTSPreloadRaw(const std::string& hostname,
*out_found = false;
+ // Ensure that |search_hostname| is a valid hostname before
+ // processing.
+ if (CanonicalizeHost(search_hostname).empty()) {
+ return true;
+ }
+
+ // Normalize any trailing '.' used for DNS suffix searches.
+ std::string hostname = search_hostname;
+ size_t found = hostname.find_last_not_of('.');
+ if (found != std::string::npos) {
+ hostname.erase(found + 1);
+ } else {
+ hostname.clear();
+ }
+
+ // |hostname| has already undergone IDN conversion, so should be
+ // entirely A-Labels. The preload data is entirely normalized to
+ // lower case.
+ base::StringToLowerASCII(&hostname);
+
if (hostname.empty()) {
return true;
}
+
// hostname_offset contains one more than the index of the current character
// in the hostname that is being considered. It's one greater so that we can
// represent the position just before the beginning (with zero).
@@ -663,6 +474,218 @@ bool DecodeHSTSPreload(const std::string& hostname,
return found;
}
+} // namespace
+
+TransportSecurityState::TransportSecurityState()
+ : 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());
+}
+
+TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state)
+ : iterator_(state.enabled_hosts_.begin()),
+ end_(state.enabled_hosts_.end()) {
+}
+
+TransportSecurityState::Iterator::~Iterator() {
+}
+
+bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) {
+ DomainState state;
+ if (GetStaticDomainState(host, &state))
+ return true;
+ return GetDynamicDomainState(host, &state);
+}
+
+bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host) {
+ DomainState dynamic_state;
+ if (GetDynamicDomainState(host, &dynamic_state))
+ return dynamic_state.ShouldUpgradeToSSL();
+
+ DomainState static_state;
+ if (GetStaticDomainState(host, &static_state) &&
+ static_state.ShouldUpgradeToSSL()) {
+ return true;
+ }
+
+ return false;
+}
+
+bool TransportSecurityState::CheckPublicKeyPins(
+ const std::string& host,
+ 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 server actually has public key pins.
+ if (!is_issued_by_known_root || !HasPublicKeyPins(host)) {
+ return true;
+ }
+
+ bool pins_are_valid =
+ CheckPublicKeyPinsImpl(host, public_key_hashes, pinning_failure_log);
+ if (!pins_are_valid) {
+ LOG(ERROR) << *pinning_failure_log;
+ ReportUMAOnPinFailure(host);
+ }
+
+ UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid);
+ return pins_are_valid;
+}
+
+bool TransportSecurityState::HasPublicKeyPins(const std::string& host) {
+ DomainState dynamic_state;
+ if (GetDynamicDomainState(host, &dynamic_state))
+ return dynamic_state.HasPublicKeyPins();
+
+ DomainState static_state;
+ if (GetStaticDomainState(host, &static_state)) {
+ if (static_state.HasPublicKeyPins())
+ return true;
+ }
+
+ return false;
+}
+
+void TransportSecurityState::SetDelegate(
+ TransportSecurityState::Delegate* delegate) {
+ DCHECK(CalledOnValidThread());
+ 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());
+
+ const std::string canonicalized_host = CanonicalizeHost(host);
+ if (canonicalized_host.empty())
+ return;
+
+ DomainState state_copy(state);
+ // No need to store this value since it is redundant. (|canonicalized_host|
+ // is the map key.)
+ state_copy.sts.domain.clear();
+ state_copy.pkp.domain.clear();
+
+ enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
+ DirtyNotify();
+}
+
+bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
+ DCHECK(CalledOnValidThread());
+
+ const std::string canonicalized_host = CanonicalizeHost(host);
+ if (canonicalized_host.empty())
+ return false;
+
+ DomainStateMap::iterator i =
+ enabled_hosts_.find(HashHost(canonicalized_host));
+ if (i != enabled_hosts_.end()) {
+ enabled_hosts_.erase(i);
+ DirtyNotify();
+ return true;
+ }
+ return false;
+}
+
+void TransportSecurityState::ClearDynamicData() {
+ DCHECK(CalledOnValidThread());
+ enabled_hosts_.clear();
+}
+
+void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) {
+ DCHECK(CalledOnValidThread());
+
+ bool dirtied = false;
+ DomainStateMap::iterator i = enabled_hosts_.begin();
+ while (i != enabled_hosts_.end()) {
+ // Clear STS and PKP state independently.
+ if (i->second.sts.last_observed >= time) {
+ dirtied = true;
+ i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT;
+ }
+ 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;
+ }
+
+ if (dirtied)
+ DirtyNotify();
+}
+
+TransportSecurityState::~TransportSecurityState() {
+ DCHECK(CalledOnValidThread());
+}
+
+void TransportSecurityState::DirtyNotify() {
+ DCHECK(CalledOnValidThread());
+
+ if (delegate_)
+ delegate_->StateIsDirty(this);
+}
+
bool TransportSecurityState::AddHSTSHeader(const std::string& host,
const std::string& value) {
DCHECK(CalledOnValidThread());
« no previous file with comments | « net/http/transport_security_state.h ('k') | net/http/transport_security_state_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698