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

Unified Diff: net/base/transport_security_state.cc

Issue 12974003: Improve TransportSecurityState data storage. (Closed) Base URL: https://src.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 8 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/base/transport_security_state.h ('k') | net/base/transport_security_state_preload.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/base/transport_security_state.cc
===================================================================
--- net/base/transport_security_state.cc (revision 186438)
+++ net/base/transport_security_state.cc (working copy)
@@ -23,16 +23,13 @@
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/sha1.h"
-#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/time.h"
-#include "base/utf_string_conversions.h"
-#include "base/values.h"
#include "crypto/sha2.h"
#include "googleurl/src/gurl.h"
#include "net/base/dns_util.h"
#include "net/base/ssl_info.h"
-#include "net/base/x509_cert_types.h"
+#include "net/base/transport_security_state_preload.h"
#include "net/base/x509_certificate.h"
#include "net/http/http_security_headers.h"
@@ -54,9 +51,17 @@
return str;
}
-std::string HashHost(const std::string& canonicalized_host) {
+std::string HashHost(const std::string& host) {
+ std::string lowercase = StringToLowerASCII(host);
+ std::string old_style_canonicalized_name;
+ if (!DNSDomainFromDot(lowercase, &old_style_canonicalized_name)) {
+ old_style_canonicalized_name.clear();
+ LOG(WARNING) << "Bad DNS name passed to TransportSecurityState";
+ }
+
char hashed[crypto::kSHA256Length];
- crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
+ crypto::SHA256HashString(old_style_canonicalized_name, hashed,
+ sizeof(hashed));
return std::string(hashed, sizeof(hashed));
}
@@ -73,782 +78,486 @@
return false;
}
-bool AddHash(const char* sha1_hash,
- HashValueVector* out) {
- HashValue hash(HASH_VALUE_SHA1);
- memcpy(hash.data(), sha1_hash, hash.size());
- out->push_back(hash);
- return true;
-}
+// Iterate over ("www.example.com", "example.com", "com")
+struct DomainNameIterator {
+ explicit DomainNameIterator(const std::string& host) {
+ name_ = StringToLowerASCII(host);
+ index_ = 0;
+ }
-} // namespace
+ bool AtEnd() {
+ return index_ == name_.length();
+ }
-TransportSecurityState::TransportSecurityState()
- : delegate_(NULL) {
-}
+ // Advance to NUL char, or after the next '.'
+ void Advance() {
+ if (AtEnd())
+ return;
+ for (index_++; name_[index_] != '.' && name_[index_] != 0; ++index_);
+ if (name_[index_] == '.')
+ index_++;
+ }
-TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state)
- : iterator_(state.enabled_hosts_.begin()),
- end_(state.enabled_hosts_.end()) {
-}
+ std::string GetName() {
+ return name_.substr(index_);
+ }
-TransportSecurityState::Iterator::~Iterator() {}
+ bool IsFullHostname() {
+ return index_ == 0;
+ }
-void TransportSecurityState::SetDelegate(
- TransportSecurityState::Delegate* delegate) {
- delegate_ = delegate;
-}
+ std::string name_; // The full hostname, canonicalized to lowercase
+ size_t index_; // Index into name_
+};
-void TransportSecurityState::EnableHost(const std::string& host,
- const DomainState& state) {
- DCHECK(CalledOnValidThread());
+// Template functions for maps of DynamicEntries (or subclasses)
+template <typename T, typename MapType = std::map<std::string, T> >
+bool GetDynamicEntry(const std::map<std::string, T>& entries,
+ const base::Time& now,
+ const std::string& hashed_host,
+ bool is_full_hostname,
+ T* result_entry) {
+ // Find the entry, and return if relevant and nonexpired
+ typename MapType::const_iterator find_iter = entries.find(hashed_host);
+ if (find_iter == entries.end())
+ return false;
- const std::string canonicalized_host = CanonicalizeHost(host);
- if (canonicalized_host.empty())
- return;
-
- DomainState existing_state;
-
- // Use the original creation date if we already have this host. (But note
- // that statically-defined states have no |created| date. Therefore, we do
- // not bother to search the SNI-only static states.)
- DomainState state_copy(state);
- if (GetDomainState(host, false /* sni_enabled */, &existing_state) &&
- !existing_state.created.is_null()) {
- state_copy.created = existing_state.created;
+ const T& found_entry = find_iter->second;
+ if ((is_full_hostname || found_entry.include_subdomains_) &&
+ found_entry.expiry_ > now) {
+ *result_entry = found_entry;
+ return true;
}
+ return false;
+}
- // No need to store this value since it is redundant. (|canonicalized_host|
- // is the map key.)
- state_copy.domain.clear();
-
- enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
- DirtyNotify();
+template<typename T, typename MapType = std::map<std::string, T> >
+bool AddDynamicEntry(std::map<std::string, T>& entries,
+ const std::string& hashed_host,
+ const T& new_entry) {
+ typename MapType::iterator find_iter = entries.find(hashed_host);
+ if (find_iter != entries.end()) {
+ // Leave 'created' unchanged
+ T& found_entry = find_iter->second;
+ found_entry.expiry_ = new_entry.expiry_;
+ found_entry.include_subdomains_ = new_entry.include_subdomains_;
+ } else {
+ entries[hashed_host] = new_entry;
+ }
+ return true;
}
-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();
+template<typename T, typename MapType = std::map<std::string, T> >
+bool DeleteDynamicEntry(std::map<std::string, T>& entries,
+ const std::string& hashed_host) {
+ typename MapType::iterator find_iter = entries.find(hashed_host);
+ if (find_iter != entries.end()) {
+ entries.erase(find_iter);
return true;
}
return false;
}
-bool TransportSecurityState::GetDomainState(const std::string& host,
- bool sni_enabled,
- DomainState* result) {
- DCHECK(CalledOnValidThread());
-
- DomainState state;
- const std::string canonicalized_host = CanonicalizeHost(host);
- if (canonicalized_host.empty())
- return false;
-
- bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled,
- &state);
- std::string canonicalized_preload = CanonicalizeHost(state.domain);
-
- base::Time current_time(base::Time::Now());
-
- 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);
- // Exact match of a preload always wins.
- if (has_preload && host_sub_chunk == canonicalized_preload) {
- *result = state;
- return true;
+template<typename T, typename MapType = std::map<std::string, T> >
+bool DeleteDynamicEntriesSince(std::map<std::string, T>& entries,
+ const base::Time& time) {
+ typename MapType::iterator iter = entries.begin();
+ bool deleted = false;
+ while (iter != entries.end()) {
+ if (iter->second.created_ >= time) {
+ entries.erase(iter++);
+ deleted = true;
+ } else {
+ iter++;
}
+ }
+ return deleted;
+}
- DomainStateMap::iterator j =
- enabled_hosts_.find(HashHost(host_sub_chunk));
- if (j == enabled_hosts_.end())
- continue;
+} // namespace
- if (current_time > j->second.upgrade_expiry &&
- current_time > j->second.dynamic_spki_hashes_expiry) {
- enabled_hosts_.erase(j);
- DirtyNotify();
- continue;
- }
- state = j->second;
- state.domain = DNSDomainToString(host_sub_chunk);
+TransportSecurityState::TransportSecurityState()
+ : delegate_(NULL) {
+}
- // Succeed if we matched the domain exactly or if subdomain matches are
- // allowed.
- if (i == 0 || j->second.include_subdomains) {
- *result = state;
- return true;
- }
+TransportSecurityState::~TransportSecurityState() {}
- return false;
- }
-
- return false;
+void TransportSecurityState::SetDelegate(
+ TransportSecurityState::Delegate* delegate) {
+ delegate_ = delegate;
}
void TransportSecurityState::ClearDynamicData() {
- enabled_hosts_.clear();
+ hsts_entries_.clear();
+ hpkp_entries_.clear();
}
void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) {
DCHECK(CalledOnValidThread());
-
- bool dirtied = false;
-
- DomainStateMap::iterator i = enabled_hosts_.begin();
- while (i != enabled_hosts_.end()) {
- if (i->second.created >= time) {
- dirtied = true;
- enabled_hosts_.erase(i++);
- } else {
- i++;
- }
- }
-
- if (dirtied)
- DirtyNotify();
+ bool deleted_hsts = DeleteDynamicEntriesSince(hsts_entries_, time);
+ bool deleted_hpkp = DeleteDynamicEntriesSince(hpkp_entries_, time);
+ if (deleted_hsts || deleted_hpkp)
+ StateIsDirty();
}
-TransportSecurityState::~TransportSecurityState() {}
-
-void TransportSecurityState::DirtyNotify() {
+bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
DCHECK(CalledOnValidThread());
-
- if (delegate_)
- delegate_->StateIsDirty(this);
+ bool deleted_hsts = DeleteHSTS(host);
+ bool deleted_hpkp = DeleteHPKP(host);
+ if (deleted_hsts || deleted_hpkp)
+ StateIsDirty();
+ return deleted_hsts || deleted_hpkp;
}
-// static
-std::string TransportSecurityState::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.
+bool TransportSecurityState::GetDomainState(const std::string& host,
+ bool sni_enabled,
+ DomainState* result) const {
+ DCHECK(CalledOnValidThread());
+ bool found = false;
+ const base::Time now = base::Time::Now();
+ DomainState dynamic_state;
- std::string new_host;
- if (!DNSDomainFromDot(host, &new_host)) {
- // DNSDomainFromDot can fail if any label is > 63 bytes or if the whole
- // name is >255 bytes. However, search terms can have those properties.
- return std::string();
+ found = GetPreloadDomainState(sni_enabled, now, host, result);
+ found = GetDynamicDomainState(now, host, &dynamic_state) || found;
+
+ // Merge dynamic state into preload state
+ // Currently, HSTS and HPKP are set if either state has them set.
+ // However, if both states have HPKP set, the preload pins take precedence.
+ // This behavior may change (e.g. for the most-recent to take priority).
+ if (!result->should_upgrade_ && dynamic_state.should_upgrade_)
+ result->should_upgrade_ = true;
+ if (!result->has_public_key_pins_ && dynamic_state.has_public_key_pins_) {
+ result->has_public_key_pins_ = true;
+ result->public_key_pins_good_hashes_ =
+ dynamic_state.public_key_pins_good_hashes_;
}
+ return found;
+}
- for (size_t i = 0; new_host[i]; i += new_host[i] + 1) {
- const unsigned label_length = static_cast<unsigned>(new_host[i]);
- if (!label_length)
- break;
+bool TransportSecurityState::GetDynamicDomainState(const base::Time& now,
+ const std::string& host,
+ DomainState* result) const {
+ HSTSEntry hsts_entry;
+ HPKPEntry hpkp_entry;
+ // Iterate over 'www.example.com", 'example.com", "com"
+ for (DomainNameIterator iter(host); !iter.AtEnd(); iter.Advance()) {
+ std::string hashed_host = HashHost(iter.GetName());
+ bool is_full_hostname = iter.IsFullHostname();
- for (size_t j = 0; j < label_length; ++j) {
- // RFC 3490, 4.1, step 3
- if (!IsSTD3ASCIIValidCharacter(new_host[i + 1 + j]))
- return std::string();
-
- new_host[i + 1 + j] = tolower(new_host[i + 1 + j]);
+ // Get HSTS data from map
+ if (!result->should_upgrade_ &&
+ GetDynamicEntry(hsts_entries_, now, hashed_host, is_full_hostname,
+ &hsts_entry)) {
+ result->should_upgrade_ = true;
}
- // step 3(b)
- if (new_host[i + 1] == '-' ||
- new_host[i + label_length] == '-') {
- return std::string();
+ // Get HPKP data from map
+ if (!result->has_public_key_pins_ &&
+ GetDynamicEntry(hpkp_entries_, now, hashed_host, is_full_hostname,
+ &hpkp_entry)) {
+ result->has_public_key_pins_ = true;
+ result->public_key_pins_good_hashes_ = hpkp_entry.good_hashes_;
}
- }
- return new_host;
+ // If we've got all possible data, exit early
+ if (result->should_upgrade_ && result->has_public_key_pins_)
+ return true;
+ }
+ return result->should_upgrade_ || result->has_public_key_pins_;
}
-// |ReportUMAOnPinFailure| uses these to report which domain was associated
-// with the public key pinning failure.
-//
-// DO NOT CHANGE THE ORDERING OF THESE NAMES OR REMOVE ANY OF THEM. Add new
-// domains at the END of the listing (but before DOMAIN_NUM_EVENTS).
-enum SecondLevelDomainName {
- DOMAIN_NOT_PINNED,
+bool TransportSecurityState::GetPreloadDomainState(bool sni_enabled,
+ const base::Time& now,
+ const std::string& host,
+ DomainState* result) const {
+#if defined(PRELOADS_PRESENT)
+ const PreloadEntry* entries = kPreloadedEntries;
+ size_t num_entries = kNumPreloaded;
- DOMAIN_GOOGLE_COM,
- DOMAIN_ANDROID_COM,
- DOMAIN_GOOGLE_ANALYTICS_COM,
- DOMAIN_GOOGLEPLEX_COM,
- DOMAIN_YTIMG_COM,
- DOMAIN_GOOGLEUSERCONTENT_COM,
- DOMAIN_YOUTUBE_COM,
- DOMAIN_GOOGLEAPIS_COM,
- DOMAIN_GOOGLEADSERVICES_COM,
- DOMAIN_GOOGLECODE_COM,
- DOMAIN_APPSPOT_COM,
- DOMAIN_GOOGLESYNDICATION_COM,
- DOMAIN_DOUBLECLICK_NET,
- DOMAIN_GSTATIC_COM,
- DOMAIN_GMAIL_COM,
- DOMAIN_GOOGLEMAIL_COM,
- DOMAIN_GOOGLEGROUPS_COM,
+ if (!IsBuildTimely())
+ return false;
- DOMAIN_TORPROJECT_ORG,
+ for (int count = 0; count < 2; count++) {
+ // If sni_enabled, then scan through SNI entries (if necessary)
+ if (count == 1) {
+ if (!sni_enabled)
+ break;
+ entries = kPreloadedEntriesSNI;
+ num_entries = kNumPreloadedSNI;
+ }
- DOMAIN_TWITTER_COM,
- DOMAIN_TWIMG_COM,
+ for (DomainNameIterator iter(host); !iter.AtEnd(); iter.Advance()) {
+ std::string name = iter.GetName();
+ for (size_t index = 0; index < num_entries; index++) {
+ const PreloadEntry& entry = entries[index];
- DOMAIN_AKAMAIHD_NET,
+ // If we find a relevant preload entry, populate the
+ // entire DomainState from it and return
+ if (entry.length == name.size() &&
+ (iter.IsFullHostname() || entry.include_subdomains) &&
+ memcmp(entry.dns_name, name.data(), entry.length) == 0) {
+ if (entry.https_required)
+ result->should_upgrade_ = true;
- DOMAIN_TOR2WEB_ORG,
+ if (entry.pins.required_hashes || entry.pins.excluded_hashes)
+ result->has_public_key_pins_ = true;
+ HashValue hash(HASH_VALUE_SHA1);
+ if (entry.pins.required_hashes) {
+ const char* const* sha1_hashes = entry.pins.required_hashes;
+ while (*sha1_hashes) {
+ memcpy(hash.data(), *sha1_hashes, hash.size());
+ result->public_key_pins_good_hashes_.push_back(hash);
+ sha1_hashes++;
+ }
+ }
+ if (entry.pins.excluded_hashes) {
+ const char* const* sha1_hashes = entry.pins.excluded_hashes;
+ while (*sha1_hashes) {
+ memcpy(hash.data(), *sha1_hashes, hash.size());
+ result->public_key_pins_bad_hashes_.push_back(hash);
+ sha1_hashes++;
+ }
+ }
- DOMAIN_YOUTU_BE,
- DOMAIN_GOOGLECOMMERCE_COM,
- DOMAIN_URCHIN_COM,
- DOMAIN_GOO_GL,
- DOMAIN_G_CO,
- DOMAIN_GOOGLE_AC,
- DOMAIN_GOOGLE_AD,
- DOMAIN_GOOGLE_AE,
- DOMAIN_GOOGLE_AF,
- DOMAIN_GOOGLE_AG,
- DOMAIN_GOOGLE_AM,
- DOMAIN_GOOGLE_AS,
- DOMAIN_GOOGLE_AT,
- DOMAIN_GOOGLE_AZ,
- DOMAIN_GOOGLE_BA,
- DOMAIN_GOOGLE_BE,
- DOMAIN_GOOGLE_BF,
- DOMAIN_GOOGLE_BG,
- DOMAIN_GOOGLE_BI,
- DOMAIN_GOOGLE_BJ,
- DOMAIN_GOOGLE_BS,
- DOMAIN_GOOGLE_BY,
- DOMAIN_GOOGLE_CA,
- DOMAIN_GOOGLE_CAT,
- DOMAIN_GOOGLE_CC,
- DOMAIN_GOOGLE_CD,
- DOMAIN_GOOGLE_CF,
- DOMAIN_GOOGLE_CG,
- DOMAIN_GOOGLE_CH,
- DOMAIN_GOOGLE_CI,
- DOMAIN_GOOGLE_CL,
- DOMAIN_GOOGLE_CM,
- DOMAIN_GOOGLE_CN,
- DOMAIN_CO_AO,
- DOMAIN_CO_BW,
- DOMAIN_CO_CK,
- DOMAIN_CO_CR,
- DOMAIN_CO_HU,
- DOMAIN_CO_ID,
- DOMAIN_CO_IL,
- DOMAIN_CO_IM,
- DOMAIN_CO_IN,
- DOMAIN_CO_JE,
- DOMAIN_CO_JP,
- DOMAIN_CO_KE,
- DOMAIN_CO_KR,
- DOMAIN_CO_LS,
- DOMAIN_CO_MA,
- DOMAIN_CO_MZ,
- DOMAIN_CO_NZ,
- DOMAIN_CO_TH,
- DOMAIN_CO_TZ,
- DOMAIN_CO_UG,
- DOMAIN_CO_UK,
- DOMAIN_CO_UZ,
- DOMAIN_CO_VE,
- DOMAIN_CO_VI,
- DOMAIN_CO_ZA,
- DOMAIN_CO_ZM,
- DOMAIN_CO_ZW,
- DOMAIN_COM_AF,
- DOMAIN_COM_AG,
- DOMAIN_COM_AI,
- DOMAIN_COM_AR,
- DOMAIN_COM_AU,
- DOMAIN_COM_BD,
- DOMAIN_COM_BH,
- DOMAIN_COM_BN,
- DOMAIN_COM_BO,
- DOMAIN_COM_BR,
- DOMAIN_COM_BY,
- DOMAIN_COM_BZ,
- DOMAIN_COM_CN,
- DOMAIN_COM_CO,
- DOMAIN_COM_CU,
- DOMAIN_COM_CY,
- DOMAIN_COM_DO,
- DOMAIN_COM_EC,
- DOMAIN_COM_EG,
- DOMAIN_COM_ET,
- DOMAIN_COM_FJ,
- DOMAIN_COM_GE,
- DOMAIN_COM_GH,
- DOMAIN_COM_GI,
- DOMAIN_COM_GR,
- DOMAIN_COM_GT,
- DOMAIN_COM_HK,
- DOMAIN_COM_IQ,
- DOMAIN_COM_JM,
- DOMAIN_COM_JO,
- DOMAIN_COM_KH,
- DOMAIN_COM_KW,
- DOMAIN_COM_LB,
- DOMAIN_COM_LY,
- DOMAIN_COM_MT,
- DOMAIN_COM_MX,
- DOMAIN_COM_MY,
- DOMAIN_COM_NA,
- DOMAIN_COM_NF,
- DOMAIN_COM_NG,
- DOMAIN_COM_NI,
- DOMAIN_COM_NP,
- DOMAIN_COM_NR,
- DOMAIN_COM_OM,
- DOMAIN_COM_PA,
- DOMAIN_COM_PE,
- DOMAIN_COM_PH,
- DOMAIN_COM_PK,
- DOMAIN_COM_PL,
- DOMAIN_COM_PR,
- DOMAIN_COM_PY,
- DOMAIN_COM_QA,
- DOMAIN_COM_RU,
- DOMAIN_COM_SA,
- DOMAIN_COM_SB,
- DOMAIN_COM_SG,
- DOMAIN_COM_SL,
- DOMAIN_COM_SV,
- DOMAIN_COM_TJ,
- DOMAIN_COM_TN,
- DOMAIN_COM_TR,
- DOMAIN_COM_TW,
- DOMAIN_COM_UA,
- DOMAIN_COM_UY,
- DOMAIN_COM_VC,
- DOMAIN_COM_VE,
- DOMAIN_COM_VN,
- DOMAIN_GOOGLE_CV,
- DOMAIN_GOOGLE_CZ,
- DOMAIN_GOOGLE_DE,
- DOMAIN_GOOGLE_DJ,
- DOMAIN_GOOGLE_DK,
- DOMAIN_GOOGLE_DM,
- DOMAIN_GOOGLE_DZ,
- DOMAIN_GOOGLE_EE,
- DOMAIN_GOOGLE_ES,
- DOMAIN_GOOGLE_FI,
- DOMAIN_GOOGLE_FM,
- DOMAIN_GOOGLE_FR,
- DOMAIN_GOOGLE_GA,
- DOMAIN_GOOGLE_GE,
- DOMAIN_GOOGLE_GG,
- DOMAIN_GOOGLE_GL,
- DOMAIN_GOOGLE_GM,
- DOMAIN_GOOGLE_GP,
- DOMAIN_GOOGLE_GR,
- DOMAIN_GOOGLE_GY,
- DOMAIN_GOOGLE_HK,
- DOMAIN_GOOGLE_HN,
- DOMAIN_GOOGLE_HR,
- DOMAIN_GOOGLE_HT,
- DOMAIN_GOOGLE_HU,
- DOMAIN_GOOGLE_IE,
- DOMAIN_GOOGLE_IM,
- DOMAIN_GOOGLE_INFO,
- DOMAIN_GOOGLE_IQ,
- DOMAIN_GOOGLE_IS,
- DOMAIN_GOOGLE_IT,
- DOMAIN_IT_AO,
- DOMAIN_GOOGLE_JE,
- DOMAIN_GOOGLE_JO,
- DOMAIN_GOOGLE_JOBS,
- DOMAIN_GOOGLE_JP,
- DOMAIN_GOOGLE_KG,
- DOMAIN_GOOGLE_KI,
- DOMAIN_GOOGLE_KZ,
- DOMAIN_GOOGLE_LA,
- DOMAIN_GOOGLE_LI,
- DOMAIN_GOOGLE_LK,
- DOMAIN_GOOGLE_LT,
- DOMAIN_GOOGLE_LU,
- DOMAIN_GOOGLE_LV,
- DOMAIN_GOOGLE_MD,
- DOMAIN_GOOGLE_ME,
- DOMAIN_GOOGLE_MG,
- DOMAIN_GOOGLE_MK,
- DOMAIN_GOOGLE_ML,
- DOMAIN_GOOGLE_MN,
- DOMAIN_GOOGLE_MS,
- DOMAIN_GOOGLE_MU,
- DOMAIN_GOOGLE_MV,
- DOMAIN_GOOGLE_MW,
- DOMAIN_GOOGLE_NE,
- DOMAIN_NE_JP,
- DOMAIN_GOOGLE_NET,
- DOMAIN_GOOGLE_NL,
- DOMAIN_GOOGLE_NO,
- DOMAIN_GOOGLE_NR,
- DOMAIN_GOOGLE_NU,
- DOMAIN_OFF_AI,
- DOMAIN_GOOGLE_PK,
- DOMAIN_GOOGLE_PL,
- DOMAIN_GOOGLE_PN,
- DOMAIN_GOOGLE_PS,
- DOMAIN_GOOGLE_PT,
- DOMAIN_GOOGLE_RO,
- DOMAIN_GOOGLE_RS,
- DOMAIN_GOOGLE_RU,
- DOMAIN_GOOGLE_RW,
- DOMAIN_GOOGLE_SC,
- DOMAIN_GOOGLE_SE,
- DOMAIN_GOOGLE_SH,
- DOMAIN_GOOGLE_SI,
- DOMAIN_GOOGLE_SK,
- DOMAIN_GOOGLE_SM,
- DOMAIN_GOOGLE_SN,
- DOMAIN_GOOGLE_SO,
- DOMAIN_GOOGLE_ST,
- DOMAIN_GOOGLE_TD,
- DOMAIN_GOOGLE_TG,
- DOMAIN_GOOGLE_TK,
- DOMAIN_GOOGLE_TL,
- DOMAIN_GOOGLE_TM,
- DOMAIN_GOOGLE_TN,
- DOMAIN_GOOGLE_TO,
- DOMAIN_GOOGLE_TP,
- DOMAIN_GOOGLE_TT,
- DOMAIN_GOOGLE_US,
- DOMAIN_GOOGLE_UZ,
- DOMAIN_GOOGLE_VG,
- DOMAIN_GOOGLE_VU,
- DOMAIN_GOOGLE_WS,
+ if (entry.pins.required_hashes == kGoogleAcceptableCerts)
+ result->is_google_pinned_property_ = true;
- DOMAIN_CHROMIUM_ORG,
-
- DOMAIN_CRYPTO_CAT,
-
- // Boundary value for UMA_HISTOGRAM_ENUMERATION:
- DOMAIN_NUM_EVENTS
-};
-
-// PublicKeyPins contains a number of SubjectPublicKeyInfo hashes for a site.
-// The validated certificate chain for the site must not include any of
-// |excluded_hashes| and must include one or more of |required_hashes|.
-struct PublicKeyPins {
- const char* const* required_hashes;
- const char* const* excluded_hashes;
-};
-
-struct HSTSPreload {
- uint8 length;
- bool include_subdomains;
- char dns_name[34];
- bool https_required;
- PublicKeyPins pins;
- 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) {
- 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],
- entries[j].length) == 0) {
- if (!entries[j].include_subdomains && i != 0) {
- *ret = false;
- } else {
- out->include_subdomains = entries[j].include_subdomains;
- *ret = true;
- if (!entries[j].https_required)
- out->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->static_spki_hashes);
- sha1_hash++;
+ if (entry.second_level_domain_name != DOMAIN_NOT_PINNED) {
+ result->report_uma_on_pin_failure_ = true;
+ result->second_level_domain_name_ = entry.second_level_domain_name;
}
+ return true;
}
- if (entries[j].pins.excluded_hashes) {
- const char* const* sha1_hash = entries[j].pins.excluded_hashes;
- while (*sha1_hash) {
- AddHash(*sha1_hash, &out->bad_static_spki_hashes);
- sha1_hash++;
- }
- }
}
- return true;
}
}
+#endif
return false;
}
-#include "net/base/transport_security_state_static.h"
-
-// Returns the HSTSPreload entry for the |canonicalized_host| in |entries|,
-// or NULL if there is none. Prefers exact hostname matches to those that
-// match only because HSTSPreload.include_subdomains is true.
-//
-// |canonicalized_host| should be the hostname as canonicalized by
-// CanonicalizeHost.
-static const struct HSTSPreload* GetHSTSPreload(
- const std::string& canonicalized_host,
- const struct HSTSPreload* entries,
- size_t num_entries) {
- for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
- for (size_t j = 0; j < num_entries; j++) {
- const struct HSTSPreload* entry = entries + j;
-
- if (i != 0 && !entry->include_subdomains)
- continue;
-
- if (entry->length == canonicalized_host.size() - i &&
- memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) {
- return entry;
- }
- }
- }
-
- return NULL;
-}
-
-bool TransportSecurityState::AddHSTSHeader(const std::string& host,
+void TransportSecurityState::AddHSTSHeader(const std::string& host,
const std::string& value) {
- base::Time now = base::Time::Now();
- TransportSecurityState::DomainState domain_state;
- if (ParseHSTSHeader(now, value, &domain_state.upgrade_expiry,
- &domain_state.include_subdomains)) {
- // Handle max-age == 0
- if (now == domain_state.upgrade_expiry)
- domain_state.upgrade_mode = DomainState::MODE_DEFAULT;
+ const base::Time now = base::Time::Now();
+ base::TimeDelta max_age;
+ bool include_subdomains = false;
+ if (ParseHSTSHeader(value, &max_age, &include_subdomains)) {
+ if (max_age.InSeconds() == 0)
+ DeleteHSTS(host);
else
- domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
- domain_state.created = now;
- EnableHost(host, domain_state);
- return true;
+ AddHSTS(host, now, now + max_age, include_subdomains);
}
- return false;
}
-bool TransportSecurityState::AddHPKPHeader(const std::string& host,
+void TransportSecurityState::AddHPKPHeader(const std::string& host,
const std::string& value,
const SSLInfo& ssl_info) {
- base::Time now = base::Time::Now();
- TransportSecurityState::DomainState domain_state;
- if (ParseHPKPHeader(now, value, ssl_info.public_key_hashes,
- &domain_state.dynamic_spki_hashes_expiry,
- &domain_state.dynamic_spki_hashes)) {
- domain_state.upgrade_mode = DomainState::MODE_DEFAULT;
- domain_state.created = now;
- EnableHost(host, domain_state);
- return true;
+ const base::Time now = base::Time::Now();
+ base::TimeDelta max_age;
+ HashValueVector public_key_pin_hashes;
+ bool include_subdomains = false; // TODO(trevp) PARSE FROM HEADER
+ if (ParseHPKPHeader(value, ssl_info.public_key_hashes, &max_age,
+ &public_key_pin_hashes)) {
+ if (max_age.InSeconds() == 0) {
+ DeleteHPKP(host);
+ } else {
+ AddHPKP(host, now, now + max_age, include_subdomains,
+ public_key_pin_hashes);
+ }
}
- return false;
}
bool TransportSecurityState::AddHSTS(const std::string& host,
+ const base::Time& created,
const base::Time& expiry,
bool include_subdomains) {
- // 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.created = base::Time::Now();
- domain_state.include_subdomains = include_subdomains;
- domain_state.upgrade_expiry = expiry;
- domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
- EnableHost(host, domain_state);
- return true;
+ return AddHSTSHashedHost(HashHost(host), created, expiry,
+ include_subdomains);
}
bool TransportSecurityState::AddHPKP(const std::string& host,
+ const base::Time& created,
const base::Time& expiry,
bool include_subdomains,
const HashValueVector& hashes) {
- // 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.created = base::Time::Now();
- domain_state.include_subdomains = include_subdomains;
- domain_state.dynamic_spki_hashes_expiry = expiry;
- domain_state.dynamic_spki_hashes = hashes;
- EnableHost(host, domain_state);
- return true;
+ return AddHPKPHashedHost(HashHost(host), created, expiry,
+ include_subdomains, hashes);
}
-// static
-bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host,
- bool sni_enabled) {
- std::string canonicalized_host = CanonicalizeHost(host);
- const struct HSTSPreload* entry =
- GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS);
+bool TransportSecurityState::AddHSTSHashedHost(const std::string& hashed_host,
+ const base::Time& created,
+ const base::Time& expiry,
+ bool include_subdomains) {
- if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts)
+ HSTSEntry entry(include_subdomains, created, expiry);
+ if (AddDynamicEntry(hsts_entries_, hashed_host, entry)) {
+ StateIsDirty();
return true;
-
- if (sni_enabled) {
- entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS,
- kNumPreloadedSNISTS);
- if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts)
- return true;
}
+ return false;
+}
+bool TransportSecurityState::AddHPKPHashedHost(const std::string& hashed_host,
+ const base::Time& created,
+ const base::Time& expiry,
+ bool include_subdomains,
+ const HashValueVector& hashes) {
+ if (hashes.empty())
+ return false;
+ HPKPEntry entry(include_subdomains, created, expiry, hashes);
+ if (AddDynamicEntry(hpkp_entries_, hashed_host, entry)) {
+ StateIsDirty();
+ return true;
+ }
return false;
}
-// static
-void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) {
- std::string canonicalized_host = CanonicalizeHost(host);
-
- const struct HSTSPreload* entry =
- GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS);
-
- if (!entry) {
- entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS,
- kNumPreloadedSNISTS);
+bool TransportSecurityState::DeleteHSTS(const std::string& host) {
+ if (DeleteDynamicEntry(hsts_entries_, HashHost(host))) {
+ StateIsDirty();
+ return true;
}
+ return false;
+}
- if (!entry) {
- // We don't care to report pin failures for dynamic pins.
- return;
+bool TransportSecurityState::DeleteHPKP(const std::string& host) {
+ if (DeleteDynamicEntry(hpkp_entries_, HashHost(host))) {
+ StateIsDirty();
+ return true;
}
+ return false;
+}
- DCHECK(entry);
- DCHECK(entry->pins.required_hashes);
- DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED);
+const std::map<std::string, TransportSecurityState::HSTSEntry>&
+TransportSecurityState::GetHSTSEntries() const {
+ return hsts_entries_;
+}
- UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain",
- entry->second_level_domain_name, DOMAIN_NUM_EVENTS);
+const std::map<std::string, TransportSecurityState::HPKPEntry>&
+TransportSecurityState::GetHPKPEntries() const {
+ return hpkp_entries_;
}
-// static
+void TransportSecurityState::StateIsDirty() {
+ if (delegate_)
+ delegate_->StateIsDirty(this);
+}
+
bool TransportSecurityState::IsBuildTimely() {
const base::Time build_time = base::GetBuildTime();
// We consider built-in information to be timely for 10 weeks.
return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
}
-bool TransportSecurityState::GetStaticDomainState(
- const std::string& canonicalized_host,
- bool sni_enabled,
- DomainState* out) {
- DCHECK(CalledOnValidThread());
+// HSTSEntry and HPKPEntry
- out->upgrade_mode = DomainState::MODE_FORCE_HTTPS;
- out->include_subdomains = false;
+TransportSecurityState::HSTSEntry::HSTSEntry()
+ : include_subdomains_(false),
+ created_(),
+ expiry_() {
+}
- const bool is_build_timely = IsBuildTimely();
+TransportSecurityState::HSTSEntry::~HSTSEntry() {
+}
- 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);
- out->domain = DNSDomainToString(host_sub_chunk);
- std::string hashed_host(HashHost(host_sub_chunk));
- if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) {
- *out = forced_hosts_[hashed_host];
- out->domain = DNSDomainToString(host_sub_chunk);
- return true;
- }
- bool ret;
- if (is_build_timely &&
- HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out,
- &ret)) {
- return ret;
- }
- if (sni_enabled &&
- is_build_timely &&
- HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i,
- out, &ret)) {
- return ret;
- }
- }
+TransportSecurityState::HSTSEntry::HSTSEntry(bool include_subdomains,
+ const base::Time& created,
+ const base::Time& expiry)
+ : include_subdomains_(include_subdomains),
+ created_(created),
+ expiry_(expiry) {
+}
- return false;
+TransportSecurityState::HPKPEntry::HPKPEntry()
+ : include_subdomains_(false),
+ created_(),
+ expiry_(),
+ good_hashes_() {
}
-void TransportSecurityState::AddOrUpdateEnabledHosts(
- const std::string& hashed_host, const DomainState& state) {
- enabled_hosts_[hashed_host] = state;
+TransportSecurityState::HPKPEntry::~HPKPEntry() {
}
-void TransportSecurityState::AddOrUpdateForcedHosts(
- const std::string& hashed_host, const DomainState& state) {
- forced_hosts_[hashed_host] = state;
+TransportSecurityState::HPKPEntry::HPKPEntry(
+ bool include_subdomains,
+ const base::Time& created,
+ const base::Time& expiry,
+ const HashValueVector& good_hashes)
+ : include_subdomains_(include_subdomains),
+ created_(created),
+ expiry_(expiry),
+ good_hashes_(good_hashes) {
}
+// DomainState
+
TransportSecurityState::DomainState::DomainState()
- : upgrade_mode(MODE_FORCE_HTTPS),
- created(base::Time::Now()),
- include_subdomains(false) {
+ : should_upgrade_(false),
+ has_public_key_pins_(false),
+ is_google_pinned_property_(false),
+ report_uma_on_pin_failure_(false),
+ public_key_pins_good_hashes_(),
+ public_key_pins_bad_hashes_(),
+ second_level_domain_name_(DOMAIN_NOT_PINNED) {
}
TransportSecurityState::DomainState::~DomainState() {
}
+bool TransportSecurityState::DomainState::HasPublicKeyPins() const {
+ return has_public_key_pins_;
+}
bool TransportSecurityState::DomainState::CheckPublicKeyPins(
const HashValueVector& hashes) const {
- // Validate that hashes is not empty. By the time this code is called (in
- // production), that should never happen, but it's good to be defensive.
- // And, hashes *can* be empty in some test scenarios.
- if (hashes.empty()) {
- LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned "
- "domain " << domain;
- return false;
- }
-
- if (HashesIntersect(bad_static_spki_hashes, hashes)) {
- LOG(ERROR) << "Rejecting public key chain for domain " << domain
- << ". Validated chain: " << HashesToBase64String(hashes)
+ if (HashesIntersect(public_key_pins_bad_hashes_, hashes)) {
+ LOG(ERROR) << "Rejecting public key chain. Validated chain: "
+ << HashesToBase64String(hashes)
<< ", matches one or more bad hashes: "
- << HashesToBase64String(bad_static_spki_hashes);
+ << HashesToBase64String(public_key_pins_bad_hashes_);
return false;
}
- // If there are no pins, then any valid chain is acceptable.
- if (dynamic_spki_hashes.empty() && static_spki_hashes.empty())
+ // If there are no good pins, then any valid chain is acceptable.
+ // Otherwise, there has to be a match.
+ if (public_key_pins_good_hashes_.empty() ||
+ HashesIntersect(public_key_pins_good_hashes_, hashes)) {
return true;
-
- if (HashesIntersect(dynamic_spki_hashes, hashes) ||
- HashesIntersect(static_spki_hashes, hashes)) {
- return true;
}
- LOG(ERROR) << "Rejecting public key chain for domain " << domain
- << ". Validated chain: " << HashesToBase64String(hashes)
- << ", expected: " << HashesToBase64String(dynamic_spki_hashes)
- << " or: " << HashesToBase64String(static_spki_hashes);
+ LOG(ERROR) << "Rejecting public key chain. Validated chain: "
+ << HashesToBase64String(hashes)
+ << ", expected: "
+ << HashesToBase64String(public_key_pins_good_hashes_);
return false;
}
bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const {
- return upgrade_mode == MODE_FORCE_HTTPS;
+ return should_upgrade_;
}
bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const {
- return true;
+ return should_upgrade_;
}
-bool TransportSecurityState::DomainState::Equals(
- const DomainState& other) const {
- // TODO(palmer): Implement this
- (void) other;
- return true;
+bool TransportSecurityState::DomainState::IsGooglePinnedProperty() const {
+ return is_google_pinned_property_;
}
-bool TransportSecurityState::DomainState::HasPublicKeyPins() const {
- return static_spki_hashes.size() > 0 ||
- bad_static_spki_hashes.size() > 0 ||
- dynamic_spki_hashes.size() > 0;
+void TransportSecurityState::DomainState::ReportUMAOnPinFailure() const {
+ if (report_uma_on_pin_failure_) {
+ UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain",
+ second_level_domain_name_, DOMAIN_NUM_EVENTS);
+ }
}
+const HashValueVector&
+TransportSecurityState::DomainState::GetPublicKeyPinsGoodHashes() const {
+ return public_key_pins_good_hashes_;
+}
+
+const HashValueVector&
+TransportSecurityState::DomainState::GetPublicKeyPinsBadHashes() const {
+ return public_key_pins_bad_hashes_;
+}
+
} // namespace
« no previous file with comments | « net/base/transport_security_state.h ('k') | net/base/transport_security_state_preload.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698