1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/transport_security_state.h"
7 #if defined(USE_OPENSSL)
8 #include <openssl/ecdsa.h>
9 #include <openssl/ssl.h>
10 #else // !defined(USE_OPENSSL)
20 #include "base/base64.h"
21 #include "base/build_time.h"
22 #include "base/logging.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/metrics/histogram.h"
25 #include "base/sha1.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/time/time.h"
30 #include "base/values.h"
31 #include "crypto/sha2.h"
32 #include "net/base/dns_util.h"
33 #include "net/cert/x509_cert_types.h"
34 #include "net/cert/x509_certificate.h"
35 #include "net/http/http_security_headers.h"
36 #include "net/ssl/ssl_info.h"
39 #if defined(USE_OPENSSL)
40 #include "crypto/openssl_util.h"
47 std::string HashesToBase64String(const HashValueVector& hashes) {
49 for (size_t i = 0; i != hashes.size(); ++i) {
52 str += hashes[i].ToString();
57 std::string HashHost(const std::string& canonicalized_host) {
58 char hashed[crypto::kSHA256Length];
59 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
60 return std::string(hashed, sizeof(hashed));
63 // Returns true if the intersection of |a| and |b| is not empty. If either
64 // |a| or |b| is empty, returns false.
65 bool HashesIntersect(const HashValueVector& a,
66 const HashValueVector& b) {
67 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) {
68 HashValueVector::const_iterator j =
69 std::find_if(b.begin(), b.end(), HashValuesEqual(*i));
76 bool AddHash(const char* sha1_hash,
77 HashValueVector* out) {
78 HashValue hash(HASH_VALUE_SHA1);
79 memcpy(hash.data(), sha1_hash, hash.size());
86 TransportSecurityState::TransportSecurityState()
88 DCHECK(CalledOnValidThread());
91 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state)
92 : iterator_(state.enabled_hosts_.begin()),
93 end_(state.enabled_hosts_.end()) {
96 TransportSecurityState::Iterator::~Iterator() {}
98 void TransportSecurityState::SetDelegate(
99 TransportSecurityState::Delegate* delegate) {
100 DCHECK(CalledOnValidThread());
101 delegate_ = delegate;
104 void TransportSecurityState::EnableHost(const std::string& host,
105 const DomainState& state) {
106 DCHECK(CalledOnValidThread());
108 const std::string canonicalized_host = CanonicalizeHost(host);
109 if (canonicalized_host.empty())
112 DomainState state_copy(state);
113 // No need to store this value since it is redundant. (|canonicalized_host|
115 state_copy.domain.clear();
117 enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
121 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
122 DCHECK(CalledOnValidThread());
124 const std::string canonicalized_host = CanonicalizeHost(host);
125 if (canonicalized_host.empty())
128 DomainStateMap::iterator i = enabled_hosts_.find(
129 HashHost(canonicalized_host));
130 if (i != enabled_hosts_.end()) {
131 enabled_hosts_.erase(i);
138 bool TransportSecurityState::GetDomainState(const std::string& host,
140 DomainState* result) {
141 DCHECK(CalledOnValidThread());
144 const std::string canonicalized_host = CanonicalizeHost(host);
145 if (canonicalized_host.empty())
148 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled,
150 std::string canonicalized_preload = CanonicalizeHost(state.domain);
151 GetDynamicDomainState(host, &state);
153 base::Time current_time(base::Time::Now());
155 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
156 std::string host_sub_chunk(&canonicalized_host[i],
157 canonicalized_host.size() - i);
158 // Exact match of a preload always wins.
159 if (has_preload && host_sub_chunk == canonicalized_preload) {
164 DomainStateMap::iterator j =
165 enabled_hosts_.find(HashHost(host_sub_chunk));
166 if (j == enabled_hosts_.end())
169 if (current_time > j->second.upgrade_expiry &&
170 current_time > j->second.dynamic_spki_hashes_expiry) {
171 enabled_hosts_.erase(j);
177 state.domain = DNSDomainToString(host_sub_chunk);
179 // Succeed if we matched the domain exactly or if subdomain matches are
181 if (i == 0 || j->second.sts_include_subdomains ||
182 j->second.pkp_include_subdomains) {
193 void TransportSecurityState::ClearDynamicData() {
194 DCHECK(CalledOnValidThread());
195 enabled_hosts_.clear();
198 void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) {
199 DCHECK(CalledOnValidThread());
201 bool dirtied = false;
202 DomainStateMap::iterator i = enabled_hosts_.begin();
203 while (i != enabled_hosts_.end()) {
204 if (i->second.sts_observed >= time && i->second.pkp_observed >= time) {
206 enabled_hosts_.erase(i++);
210 if (i->second.sts_observed >= time) {
212 i->second.upgrade_mode = DomainState::MODE_DEFAULT;
213 } else if (i->second.pkp_observed >= time) {
215 i->second.dynamic_spki_hashes.clear();
224 TransportSecurityState::~TransportSecurityState() {
225 DCHECK(CalledOnValidThread());
228 void TransportSecurityState::DirtyNotify() {
229 DCHECK(CalledOnValidThread());
232 delegate_->StateIsDirty(this);
236 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) {
237 // We cannot perform the operations as detailed in the spec here as |host|
238 // has already undergone IDN processing before it reached us. Thus, we check
239 // that there are no invalid characters in the host and lowercase the result.
241 std::string new_host;
242 if (!DNSDomainFromDot(host, &new_host)) {
243 // DNSDomainFromDot can fail if any label is > 63 bytes or if the whole
244 // name is >255 bytes. However, search terms can have those properties.
245 return std::string();
248 for (size_t i = 0; new_host[i]; i += new_host[i] + 1) {
249 const unsigned label_length = static_cast<unsigned>(new_host[i]);
253 for (size_t j = 0; j < label_length; ++j) {
254 new_host[i + 1 + j] = tolower(new_host[i + 1 + j]);
261 // |ReportUMAOnPinFailure| uses these to report which domain was associated
262 // with the public key pinning failure.
264 // DO NOT CHANGE THE ORDERING OF THESE NAMES OR REMOVE ANY OF THEM. Add new
265 // domains at the END of the listing (but before DOMAIN_NUM_EVENTS).
266 enum SecondLevelDomainName {
271 DOMAIN_GOOGLE_ANALYTICS_COM,
272 DOMAIN_GOOGLEPLEX_COM,
274 DOMAIN_GOOGLEUSERCONTENT_COM,
276 DOMAIN_GOOGLEAPIS_COM,
277 DOMAIN_GOOGLEADSERVICES_COM,
278 DOMAIN_GOOGLECODE_COM,
280 DOMAIN_GOOGLESYNDICATION_COM,
281 DOMAIN_DOUBLECLICK_NET,
284 DOMAIN_GOOGLEMAIL_COM,
285 DOMAIN_GOOGLEGROUPS_COM,
287 DOMAIN_TORPROJECT_ORG,
297 DOMAIN_GOOGLECOMMERCE_COM,
524 // Boundary value for UMA_HISTOGRAM_ENUMERATION:
528 // PublicKeyPins contains a number of SubjectPublicKeyInfo hashes for a site.
529 // The validated certificate chain for the site must not include any of
530 // |excluded_hashes| and must include one or more of |required_hashes|.
531 struct PublicKeyPins {
532 const char* const* required_hashes;
533 const char* const* excluded_hashes;
538 bool include_subdomains;
542 SecondLevelDomainName second_level_domain_name;
545 static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries,
546 const std::string& canonicalized_host, size_t i,
547 TransportSecurityState::DomainState* out, bool* ret) {
548 for (size_t j = 0; j < num_entries; j++) {
549 if (entries[j].length == canonicalized_host.size() - i &&
550 memcmp(entries[j].dns_name, &canonicalized_host[i],
551 entries[j].length) == 0) {
552 if (!entries[j].include_subdomains && i != 0) {
555 out->sts_include_subdomains = entries[j].include_subdomains;
556 out->pkp_include_subdomains = entries[j].include_subdomains;
558 if (!entries[j].https_required)
559 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT;
560 if (entries[j].pins.required_hashes) {
561 const char* const* sha1_hash = entries[j].pins.required_hashes;
563 AddHash(*sha1_hash, &out->static_spki_hashes);
567 if (entries[j].pins.excluded_hashes) {
568 const char* const* sha1_hash = entries[j].pins.excluded_hashes;
570 AddHash(*sha1_hash, &out->bad_static_spki_hashes);
581 #include "net/http/transport_security_state_static.h"
583 // Returns the HSTSPreload entry for the |canonicalized_host| in |entries|,
584 // or NULL if there is none. Prefers exact hostname matches to those that
585 // match only because HSTSPreload.include_subdomains is true.
587 // |canonicalized_host| should be the hostname as canonicalized by
589 static const struct HSTSPreload* GetHSTSPreload(
590 const std::string& canonicalized_host,
591 const struct HSTSPreload* entries,
592 size_t num_entries) {
593 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
594 for (size_t j = 0; j < num_entries; j++) {
595 const struct HSTSPreload* entry = entries + j;
597 if (i != 0 && !entry->include_subdomains)
600 if (entry->length == canonicalized_host.size() - i &&
601 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) {
610 bool TransportSecurityState::AddHSTSHeader(const std::string& host,
611 const std::string& value) {
612 DCHECK(CalledOnValidThread());
614 base::Time now = base::Time::Now();
615 base::TimeDelta max_age;
616 TransportSecurityState::DomainState domain_state;
617 GetDynamicDomainState(host, &domain_state);
618 if (ParseHSTSHeader(value, &max_age, &domain_state.sts_include_subdomains)) {
619 // Handle max-age == 0
620 if (max_age.InSeconds() == 0)
621 domain_state.upgrade_mode = DomainState::MODE_DEFAULT;
623 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
624 domain_state.sts_observed = now;
625 domain_state.upgrade_expiry = now + max_age;
626 EnableHost(host, domain_state);
632 bool TransportSecurityState::AddHPKPHeader(const std::string& host,
633 const std::string& value,
634 const SSLInfo& ssl_info) {
635 DCHECK(CalledOnValidThread());
637 base::Time now = base::Time::Now();
638 base::TimeDelta max_age;
639 TransportSecurityState::DomainState domain_state;
640 GetDynamicDomainState(host, &domain_state);
641 if (ParseHPKPHeader(value, ssl_info.public_key_hashes,
642 &max_age, &domain_state.pkp_include_subdomains,
643 &domain_state.dynamic_spki_hashes)) {
644 // TODO(palmer): http://crbug.com/243865 handle max-age == 0.
645 domain_state.pkp_observed = now;
646 domain_state.dynamic_spki_hashes_expiry = now + max_age;
647 EnableHost(host, domain_state);
653 bool TransportSecurityState::AddHSTS(const std::string& host,
654 const base::Time& expiry,
655 bool include_subdomains) {
656 DCHECK(CalledOnValidThread());
658 // Copy-and-modify the existing DomainState for this host (if any).
659 TransportSecurityState::DomainState domain_state;
660 const std::string canonicalized_host = CanonicalizeHost(host);
661 const std::string hashed_host = HashHost(canonicalized_host);
662 DomainStateMap::const_iterator i = enabled_hosts_.find(
664 if (i != enabled_hosts_.end())
665 domain_state = i->second;
667 domain_state.sts_observed = base::Time::Now();
668 domain_state.sts_include_subdomains = include_subdomains;
669 domain_state.upgrade_expiry = expiry;
670 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
671 EnableHost(host, domain_state);
675 bool TransportSecurityState::AddHPKP(const std::string& host,
676 const base::Time& expiry,
677 bool include_subdomains,
678 const HashValueVector& hashes) {
679 DCHECK(CalledOnValidThread());
681 // Copy-and-modify the existing DomainState for this host (if any).
682 TransportSecurityState::DomainState domain_state;
683 const std::string canonicalized_host = CanonicalizeHost(host);
684 const std::string hashed_host = HashHost(canonicalized_host);
685 DomainStateMap::const_iterator i = enabled_hosts_.find(
687 if (i != enabled_hosts_.end())
688 domain_state = i->second;
690 domain_state.pkp_observed = base::Time::Now();
691 domain_state.pkp_include_subdomains = include_subdomains;
692 domain_state.dynamic_spki_hashes_expiry = expiry;
693 domain_state.dynamic_spki_hashes = hashes;
694 EnableHost(host, domain_state);
699 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host,
701 std::string canonicalized_host = CanonicalizeHost(host);
702 const struct HSTSPreload* entry =
703 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS);
705 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts)
709 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS,
710 kNumPreloadedSNISTS);
711 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts)
719 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) {
720 std::string canonicalized_host = CanonicalizeHost(host);
722 const struct HSTSPreload* entry =
723 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS);
726 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS,
727 kNumPreloadedSNISTS);
731 // We don't care to report pin failures for dynamic pins.
736 DCHECK(entry->pins.required_hashes);
737 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED);
739 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain",
740 entry->second_level_domain_name, DOMAIN_NUM_EVENTS);
744 bool TransportSecurityState::IsBuildTimely() {
745 const base::Time build_time = base::GetBuildTime();
746 // We consider built-in information to be timely for 10 weeks.
747 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
750 bool TransportSecurityState::GetStaticDomainState(
751 const std::string& canonicalized_host,
754 DCHECK(CalledOnValidThread());
756 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS;
757 out->sts_include_subdomains = false;
758 out->pkp_include_subdomains = false;
760 const bool is_build_timely = IsBuildTimely();
762 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
763 std::string host_sub_chunk(&canonicalized_host[i],
764 canonicalized_host.size() - i);
765 out->domain = DNSDomainToString(host_sub_chunk);
767 if (is_build_timely &&
768 HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out,
774 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i,
783 bool TransportSecurityState::GetDynamicDomainState(const std::string& host,
784 DomainState* result) {
785 DCHECK(CalledOnValidThread());
788 const std::string canonicalized_host = CanonicalizeHost(host);
789 if (canonicalized_host.empty())
792 base::Time current_time(base::Time::Now());
794 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
795 std::string host_sub_chunk(&canonicalized_host[i],
796 canonicalized_host.size() - i);
797 DomainStateMap::iterator j =
798 enabled_hosts_.find(HashHost(host_sub_chunk));
799 if (j == enabled_hosts_.end())
802 if (current_time > j->second.upgrade_expiry &&
803 current_time > j->second.dynamic_spki_hashes_expiry) {
804 enabled_hosts_.erase(j);
810 state.domain = DNSDomainToString(host_sub_chunk);
812 // Succeed if we matched the domain exactly or if subdomain matches are
814 if (i == 0 || j->second.sts_include_subdomains ||
815 j->second.pkp_include_subdomains) {
827 void TransportSecurityState::AddOrUpdateEnabledHosts(
828 const std::string& hashed_host, const DomainState& state) {
829 DCHECK(CalledOnValidThread());
830 enabled_hosts_[hashed_host] = state;
833 TransportSecurityState::DomainState::DomainState()
834 : upgrade_mode(MODE_DEFAULT),
835 sts_include_subdomains(false),
836 pkp_include_subdomains(false) {
837 base::Time now(base::Time::Now());
842 TransportSecurityState::DomainState::~DomainState() {
845 bool TransportSecurityState::DomainState::CheckPublicKeyPins(
846 const HashValueVector& hashes) const {
847 // Validate that hashes is not empty. By the time this code is called (in
848 // production), that should never happen, but it's good to be defensive.
849 // And, hashes *can* be empty in some test scenarios.
850 if (hashes.empty()) {
851 LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned "
856 if (HashesIntersect(bad_static_spki_hashes, hashes)) {
857 LOG(ERROR) << "Rejecting public key chain for domain " << domain
858 << ". Validated chain: " << HashesToBase64String(hashes)
859 << ", matches one or more bad hashes: "
860 << HashesToBase64String(bad_static_spki_hashes);
864 // If there are no pins, then any valid chain is acceptable.
865 if (dynamic_spki_hashes.empty() && static_spki_hashes.empty())
868 if (HashesIntersect(dynamic_spki_hashes, hashes) ||
869 HashesIntersect(static_spki_hashes, hashes)) {
873 LOG(ERROR) << "Rejecting public key chain for domain " << domain
874 << ". Validated chain: " << HashesToBase64String(hashes)
875 << ", expected: " << HashesToBase64String(dynamic_spki_hashes)
876 << " or: " << HashesToBase64String(static_spki_hashes);
880 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const {
881 return upgrade_mode == MODE_FORCE_HTTPS;
884 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const {
888 bool TransportSecurityState::DomainState::HasPublicKeyPins() const {
889 return static_spki_hashes.size() > 0 ||
890 bad_static_spki_hashes.size() > 0 ||
891 dynamic_spki_hashes.size() > 0;