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 DOMAIN_GOOGLETAGMANAGER_COM
,
525 DOMAIN_GOOGLETAGSERVICES_COM
,
527 // Boundary value for UMA_HISTOGRAM_ENUMERATION:
531 // PublicKeyPins contains a number of SubjectPublicKeyInfo hashes for a site.
532 // The validated certificate chain for the site must not include any of
533 // |excluded_hashes| and must include one or more of |required_hashes|.
534 struct PublicKeyPins
{
535 const char* const* required_hashes
;
536 const char* const* excluded_hashes
;
541 bool include_subdomains
;
545 SecondLevelDomainName second_level_domain_name
;
548 static bool HasPreload(const struct HSTSPreload
* entries
, size_t num_entries
,
549 const std::string
& canonicalized_host
, size_t i
,
550 TransportSecurityState::DomainState
* out
, bool* ret
) {
551 for (size_t j
= 0; j
< num_entries
; j
++) {
552 if (entries
[j
].length
== canonicalized_host
.size() - i
&&
553 memcmp(entries
[j
].dns_name
, &canonicalized_host
[i
],
554 entries
[j
].length
) == 0) {
555 if (!entries
[j
].include_subdomains
&& i
!= 0) {
558 out
->sts_include_subdomains
= entries
[j
].include_subdomains
;
559 out
->pkp_include_subdomains
= entries
[j
].include_subdomains
;
561 if (!entries
[j
].https_required
)
562 out
->upgrade_mode
= TransportSecurityState::DomainState::MODE_DEFAULT
;
563 if (entries
[j
].pins
.required_hashes
) {
564 const char* const* sha1_hash
= entries
[j
].pins
.required_hashes
;
566 AddHash(*sha1_hash
, &out
->static_spki_hashes
);
570 if (entries
[j
].pins
.excluded_hashes
) {
571 const char* const* sha1_hash
= entries
[j
].pins
.excluded_hashes
;
573 AddHash(*sha1_hash
, &out
->bad_static_spki_hashes
);
584 #include "net/http/transport_security_state_static.h"
586 // Returns the HSTSPreload entry for the |canonicalized_host| in |entries|,
587 // or NULL if there is none. Prefers exact hostname matches to those that
588 // match only because HSTSPreload.include_subdomains is true.
590 // |canonicalized_host| should be the hostname as canonicalized by
592 static const struct HSTSPreload
* GetHSTSPreload(
593 const std::string
& canonicalized_host
,
594 const struct HSTSPreload
* entries
,
595 size_t num_entries
) {
596 for (size_t i
= 0; canonicalized_host
[i
]; i
+= canonicalized_host
[i
] + 1) {
597 for (size_t j
= 0; j
< num_entries
; j
++) {
598 const struct HSTSPreload
* entry
= entries
+ j
;
600 if (i
!= 0 && !entry
->include_subdomains
)
603 if (entry
->length
== canonicalized_host
.size() - i
&&
604 memcmp(entry
->dns_name
, &canonicalized_host
[i
], entry
->length
) == 0) {
613 bool TransportSecurityState::AddHSTSHeader(const std::string
& host
,
614 const std::string
& value
) {
615 DCHECK(CalledOnValidThread());
617 base::Time now
= base::Time::Now();
618 base::TimeDelta max_age
;
619 TransportSecurityState::DomainState domain_state
;
620 GetDynamicDomainState(host
, &domain_state
);
621 if (ParseHSTSHeader(value
, &max_age
, &domain_state
.sts_include_subdomains
)) {
622 // Handle max-age == 0
623 if (max_age
.InSeconds() == 0)
624 domain_state
.upgrade_mode
= DomainState::MODE_DEFAULT
;
626 domain_state
.upgrade_mode
= DomainState::MODE_FORCE_HTTPS
;
627 domain_state
.sts_observed
= now
;
628 domain_state
.upgrade_expiry
= now
+ max_age
;
629 EnableHost(host
, domain_state
);
635 bool TransportSecurityState::AddHPKPHeader(const std::string
& host
,
636 const std::string
& value
,
637 const SSLInfo
& ssl_info
) {
638 DCHECK(CalledOnValidThread());
640 base::Time now
= base::Time::Now();
641 base::TimeDelta max_age
;
642 TransportSecurityState::DomainState domain_state
;
643 GetDynamicDomainState(host
, &domain_state
);
644 if (ParseHPKPHeader(value
, ssl_info
.public_key_hashes
,
645 &max_age
, &domain_state
.pkp_include_subdomains
,
646 &domain_state
.dynamic_spki_hashes
)) {
647 // TODO(palmer): http://crbug.com/243865 handle max-age == 0.
648 domain_state
.pkp_observed
= now
;
649 domain_state
.dynamic_spki_hashes_expiry
= now
+ max_age
;
650 EnableHost(host
, domain_state
);
656 bool TransportSecurityState::AddHSTS(const std::string
& host
,
657 const base::Time
& expiry
,
658 bool include_subdomains
) {
659 DCHECK(CalledOnValidThread());
661 // Copy-and-modify the existing DomainState for this host (if any).
662 TransportSecurityState::DomainState domain_state
;
663 const std::string canonicalized_host
= CanonicalizeHost(host
);
664 const std::string hashed_host
= HashHost(canonicalized_host
);
665 DomainStateMap::const_iterator i
= enabled_hosts_
.find(
667 if (i
!= enabled_hosts_
.end())
668 domain_state
= i
->second
;
670 domain_state
.sts_observed
= base::Time::Now();
671 domain_state
.sts_include_subdomains
= include_subdomains
;
672 domain_state
.upgrade_expiry
= expiry
;
673 domain_state
.upgrade_mode
= DomainState::MODE_FORCE_HTTPS
;
674 EnableHost(host
, domain_state
);
678 bool TransportSecurityState::AddHPKP(const std::string
& host
,
679 const base::Time
& expiry
,
680 bool include_subdomains
,
681 const HashValueVector
& hashes
) {
682 DCHECK(CalledOnValidThread());
684 // Copy-and-modify the existing DomainState for this host (if any).
685 TransportSecurityState::DomainState domain_state
;
686 const std::string canonicalized_host
= CanonicalizeHost(host
);
687 const std::string hashed_host
= HashHost(canonicalized_host
);
688 DomainStateMap::const_iterator i
= enabled_hosts_
.find(
690 if (i
!= enabled_hosts_
.end())
691 domain_state
= i
->second
;
693 domain_state
.pkp_observed
= base::Time::Now();
694 domain_state
.pkp_include_subdomains
= include_subdomains
;
695 domain_state
.dynamic_spki_hashes_expiry
= expiry
;
696 domain_state
.dynamic_spki_hashes
= hashes
;
697 EnableHost(host
, domain_state
);
702 bool TransportSecurityState::IsGooglePinnedProperty(const std::string
& host
,
704 std::string canonicalized_host
= CanonicalizeHost(host
);
705 const struct HSTSPreload
* entry
=
706 GetHSTSPreload(canonicalized_host
, kPreloadedSTS
, kNumPreloadedSTS
);
708 if (entry
&& entry
->pins
.required_hashes
== kGoogleAcceptableCerts
)
712 entry
= GetHSTSPreload(canonicalized_host
, kPreloadedSNISTS
,
713 kNumPreloadedSNISTS
);
714 if (entry
&& entry
->pins
.required_hashes
== kGoogleAcceptableCerts
)
722 void TransportSecurityState::ReportUMAOnPinFailure(const std::string
& host
) {
723 std::string canonicalized_host
= CanonicalizeHost(host
);
725 const struct HSTSPreload
* entry
=
726 GetHSTSPreload(canonicalized_host
, kPreloadedSTS
, kNumPreloadedSTS
);
729 entry
= GetHSTSPreload(canonicalized_host
, kPreloadedSNISTS
,
730 kNumPreloadedSNISTS
);
734 // We don't care to report pin failures for dynamic pins.
739 DCHECK(entry
->pins
.required_hashes
);
740 DCHECK(entry
->second_level_domain_name
!= DOMAIN_NOT_PINNED
);
742 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain",
743 entry
->second_level_domain_name
, DOMAIN_NUM_EVENTS
);
747 bool TransportSecurityState::IsBuildTimely() {
748 const base::Time build_time
= base::GetBuildTime();
749 // We consider built-in information to be timely for 10 weeks.
750 return (base::Time::Now() - build_time
).InDays() < 70 /* 10 weeks */;
753 bool TransportSecurityState::GetStaticDomainState(
754 const std::string
& canonicalized_host
,
757 DCHECK(CalledOnValidThread());
759 out
->upgrade_mode
= DomainState::MODE_FORCE_HTTPS
;
760 out
->sts_include_subdomains
= false;
761 out
->pkp_include_subdomains
= false;
763 const bool is_build_timely
= IsBuildTimely();
765 for (size_t i
= 0; canonicalized_host
[i
]; i
+= canonicalized_host
[i
] + 1) {
766 std::string
host_sub_chunk(&canonicalized_host
[i
],
767 canonicalized_host
.size() - i
);
768 out
->domain
= DNSDomainToString(host_sub_chunk
);
770 if (is_build_timely
&&
771 HasPreload(kPreloadedSTS
, kNumPreloadedSTS
, canonicalized_host
, i
, out
,
777 HasPreload(kPreloadedSNISTS
, kNumPreloadedSNISTS
, canonicalized_host
, i
,
786 bool TransportSecurityState::GetDynamicDomainState(const std::string
& host
,
787 DomainState
* result
) {
788 DCHECK(CalledOnValidThread());
791 const std::string canonicalized_host
= CanonicalizeHost(host
);
792 if (canonicalized_host
.empty())
795 base::Time
current_time(base::Time::Now());
797 for (size_t i
= 0; canonicalized_host
[i
]; i
+= canonicalized_host
[i
] + 1) {
798 std::string
host_sub_chunk(&canonicalized_host
[i
],
799 canonicalized_host
.size() - i
);
800 DomainStateMap::iterator j
=
801 enabled_hosts_
.find(HashHost(host_sub_chunk
));
802 if (j
== enabled_hosts_
.end())
805 if (current_time
> j
->second
.upgrade_expiry
&&
806 current_time
> j
->second
.dynamic_spki_hashes_expiry
) {
807 enabled_hosts_
.erase(j
);
813 state
.domain
= DNSDomainToString(host_sub_chunk
);
815 // Succeed if we matched the domain exactly or if subdomain matches are
817 if (i
== 0 || j
->second
.sts_include_subdomains
||
818 j
->second
.pkp_include_subdomains
) {
830 void TransportSecurityState::AddOrUpdateEnabledHosts(
831 const std::string
& hashed_host
, const DomainState
& state
) {
832 DCHECK(CalledOnValidThread());
833 enabled_hosts_
[hashed_host
] = state
;
836 TransportSecurityState::DomainState::DomainState()
837 : upgrade_mode(MODE_DEFAULT
),
838 sts_include_subdomains(false),
839 pkp_include_subdomains(false) {
840 base::Time
now(base::Time::Now());
845 TransportSecurityState::DomainState::~DomainState() {
848 bool TransportSecurityState::DomainState::CheckPublicKeyPins(
849 const HashValueVector
& hashes
, std::string
* failure_log
) const {
850 // Validate that hashes is not empty. By the time this code is called (in
851 // production), that should never happen, but it's good to be defensive.
852 // And, hashes *can* be empty in some test scenarios.
853 if (hashes
.empty()) {
854 *failure_log
= "Rejecting empty public key chain for public-key-pinned "
855 "domains: " + domain
;
859 if (HashesIntersect(bad_static_spki_hashes
, hashes
)) {
860 *failure_log
= "Rejecting public key chain for domain " + domain
+
861 ". Validated chain: " + HashesToBase64String(hashes
) +
862 ", matches one or more bad hashes: " +
863 HashesToBase64String(bad_static_spki_hashes
);
867 // If there are no pins, then any valid chain is acceptable.
868 if (dynamic_spki_hashes
.empty() && static_spki_hashes
.empty())
871 if (HashesIntersect(dynamic_spki_hashes
, hashes
) ||
872 HashesIntersect(static_spki_hashes
, hashes
)) {
876 *failure_log
= "Rejecting public key chain for domain " + domain
+
877 ". Validated chain: " + HashesToBase64String(hashes
) +
878 ", expected: " + HashesToBase64String(dynamic_spki_hashes
) +
879 " or: " + HashesToBase64String(static_spki_hashes
);
883 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const {
884 return upgrade_mode
== MODE_FORCE_HTTPS
;
887 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const {
891 bool TransportSecurityState::DomainState::HasPublicKeyPins() const {
892 return static_spki_hashes
.size() > 0 ||
893 bad_static_spki_hashes
.size() > 0 ||
894 dynamic_spki_hashes
.size() > 0;