| 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());
|
|
|