1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/http/transport_security_state.h"
6
7 #include <algorithm>
8 #include <cstdint>
9 #include <memory>
10 #include <optional>
11 #include <string_view>
12 #include <tuple>
13 #include <utility>
14 #include <vector>
15
16 #include "base/base64.h"
17 #include "base/build_time.h"
18 #include "base/containers/contains.h"
19 #include "base/containers/span.h"
20 #include "base/feature_list.h"
21 #include "base/functional/bind.h"
22 #include "base/json/json_writer.h"
23 #include "base/logging.h"
24 #include "base/metrics/field_trial.h"
25 #include "base/metrics/field_trial_params.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/strings/utf_string_conversions.h"
30 #include "base/time/time.h"
31 #include "base/values.h"
32 #include "build/branding_buildflags.h"
33 #include "build/build_config.h"
34 #include "crypto/sha2.h"
35 #include "net/base/features.h"
36 #include "net/base/hash_value.h"
37 #include "net/base/host_port_pair.h"
38 #include "net/cert/ct_policy_status.h"
39 #include "net/cert/x509_certificate.h"
40 #include "net/dns/dns_names_util.h"
41 #include "net/extras/preload_data/decoder.h"
42 #include "net/http/http_security_headers.h"
43 #include "net/net_buildflags.h"
44 #include "net/ssl/ssl_info.h"
45
46 namespace net {
47
48 namespace {
49
50 #if BUILDFLAG(INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST)
51 #include "net/http/transport_security_state_static.h" // nogncheck
52 // Points to the active transport security state source.
53 const TransportSecurityStateSource* const kDefaultHSTSSource = &kHSTSSource;
54 #else
55 const TransportSecurityStateSource* const kDefaultHSTSSource = nullptr;
56 #endif
57
58 const TransportSecurityStateSource* g_hsts_source = kDefaultHSTSSource;
59
HashHost(base::span<const uint8_t> canonicalized_host)60 TransportSecurityState::HashedHost HashHost(
61 base::span<const uint8_t> canonicalized_host) {
62 return crypto::SHA256Hash(canonicalized_host);
63 }
64
65 // Returns true if the intersection of |a| and |b| is not empty. If either
66 // |a| or |b| is empty, returns false.
HashesIntersect(const HashValueVector & a,const HashValueVector & b)67 bool HashesIntersect(const HashValueVector& a, const HashValueVector& b) {
68 for (const auto& hash : a) {
69 if (base::Contains(b, hash))
70 return true;
71 }
72 return false;
73 }
74
AddHash(const char * sha256_hash,HashValueVector * out)75 bool AddHash(const char* sha256_hash, HashValueVector* out) {
76 HashValue hash(HASH_VALUE_SHA256);
77 memcpy(hash.data(), sha256_hash, hash.size());
78 out->push_back(hash);
79 return true;
80 }
81
82 // Converts |hostname| from dotted form ("www.google.com") to the form
83 // used in DNS: "\x03www\x06google\x03com", lowercases that, and returns
84 // the result.
CanonicalizeHost(const std::string & host)85 std::vector<uint8_t> CanonicalizeHost(const std::string& host) {
86 // We cannot perform the operations as detailed in the spec here as `host`
87 // has already undergone IDN processing before it reached us. Thus, we
88 // lowercase the input (probably redudnant since most input here has been
89 // lowercased through URL canonicalization) and check that there are no
90 // invalid characters in the host (via DNSDomainFromDot()).
91 std::string lowered_host = base::ToLowerASCII(host);
92
93 std::optional<std::vector<uint8_t>> new_host =
94 dns_names_util::DottedNameToNetwork(
95 lowered_host,
96 /*require_valid_internet_hostname=*/true);
97 if (!new_host.has_value()) {
98 // DNSDomainFromDot can fail if any label is > 63 bytes or if the whole
99 // name is >255 bytes. However, search terms can have those properties.
100 return std::vector<uint8_t>();
101 }
102
103 return new_host.value();
104 }
105
106 // PreloadResult is the result of resolving a specific name in the preloaded
107 // data.
108 struct PreloadResult {
109 uint32_t pinset_id = 0;
110 // hostname_offset contains the number of bytes from the start of the given
111 // hostname where the name of the matching entry starts.
112 size_t hostname_offset = 0;
113 bool sts_include_subdomains = false;
114 bool pkp_include_subdomains = false;
115 bool force_https = false;
116 bool has_pins = false;
117 };
118
119 using net::extras::PreloadDecoder;
120
121 // Extracts the current PreloadResult entry from the given Huffman encoded trie.
122 // If an "end of string" matches a period in the hostname then the information
123 // is remembered because, if no more specific node is found, then that
124 // information applies to the hostname.
125 class HSTSPreloadDecoder : public net::extras::PreloadDecoder {
126 public:
127 using net::extras::PreloadDecoder::PreloadDecoder;
128
129 // net::extras::PreloadDecoder:
ReadEntry(net::extras::PreloadDecoder::BitReader * reader,const std::string & search,size_t current_search_offset,bool * out_found)130 bool ReadEntry(net::extras::PreloadDecoder::BitReader* reader,
131 const std::string& search,
132 size_t current_search_offset,
133 bool* out_found) override {
134 bool is_simple_entry;
135 if (!reader->Next(&is_simple_entry)) {
136 return false;
137 }
138 PreloadResult tmp;
139 // Simple entries only configure HSTS with IncludeSubdomains and use a
140 // compact serialization format where the other policy flags are
141 // omitted. The omitted flags are assumed to be 0 and the associated
142 // policies are disabled.
143 if (is_simple_entry) {
144 tmp.force_https = true;
145 tmp.sts_include_subdomains = true;
146 } else {
147 if (!reader->Next(&tmp.sts_include_subdomains) ||
148 !reader->Next(&tmp.force_https) || !reader->Next(&tmp.has_pins)) {
149 return false;
150 }
151
152 tmp.pkp_include_subdomains = tmp.sts_include_subdomains;
153
154 if (tmp.has_pins) {
155 if (!reader->Read(4, &tmp.pinset_id) ||
156 (!tmp.sts_include_subdomains &&
157 !reader->Next(&tmp.pkp_include_subdomains))) {
158 return false;
159 }
160 }
161 }
162
163 tmp.hostname_offset = current_search_offset;
164
165 if (current_search_offset == 0 ||
166 search[current_search_offset - 1] == '.') {
167 *out_found = tmp.sts_include_subdomains || tmp.pkp_include_subdomains;
168
169 result_ = tmp;
170
171 if (current_search_offset > 0) {
172 result_.force_https &= tmp.sts_include_subdomains;
173 } else {
174 *out_found = true;
175 return true;
176 }
177 }
178 return true;
179 }
180
result() const181 PreloadResult result() const { return result_; }
182
183 private:
184 PreloadResult result_;
185 };
186
DecodeHSTSPreload(const std::string & search_hostname,PreloadResult * out)187 bool DecodeHSTSPreload(const std::string& search_hostname, PreloadResult* out) {
188 #if !BUILDFLAG(INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST)
189 if (g_hsts_source == nullptr)
190 return false;
191 #endif
192 bool found = false;
193
194 // Ensure that |search_hostname| is a valid hostname before
195 // processing.
196 if (CanonicalizeHost(search_hostname).empty()) {
197 return false;
198 }
199 // Normalize any trailing '.' used for DNS suffix searches.
200 std::string hostname = search_hostname;
201 size_t trailing_dot_found = hostname.find_last_not_of('.');
202 if (trailing_dot_found != std::string::npos) {
203 hostname.erase(trailing_dot_found + 1);
204 } else {
205 hostname.clear();
206 }
207
208 // |hostname| has already undergone IDN conversion, so should be
209 // entirely A-Labels. The preload data is entirely normalized to
210 // lower case.
211 hostname = base::ToLowerASCII(hostname);
212 if (hostname.empty()) {
213 return false;
214 }
215
216 HSTSPreloadDecoder decoder(
217 g_hsts_source->huffman_tree, g_hsts_source->huffman_tree_size,
218 g_hsts_source->preloaded_data, g_hsts_source->preloaded_bits,
219 g_hsts_source->root_position);
220 if (!decoder.Decode(hostname, &found)) {
221 DCHECK(false) << "Internal error in DecodeHSTSPreload for hostname "
222 << hostname;
223 return false;
224 }
225 if (found)
226 *out = decoder.result();
227 return found;
228 }
229
230 } // namespace
231
SetTransportSecurityStateSourceForTesting(const TransportSecurityStateSource * source)232 void SetTransportSecurityStateSourceForTesting(
233 const TransportSecurityStateSource* source) {
234 g_hsts_source = source ? source : kDefaultHSTSSource;
235 }
236
TransportSecurityState()237 TransportSecurityState::TransportSecurityState()
238 : TransportSecurityState(std::vector<std::string>()) {}
239
TransportSecurityState(std::vector<std::string> hsts_host_bypass_list)240 TransportSecurityState::TransportSecurityState(
241 std::vector<std::string> hsts_host_bypass_list) {
242 // Static pinning is only enabled for official builds to make sure that
243 // others don't end up with pins that cannot be easily updated.
244 #if !BUILDFLAG(GOOGLE_CHROME_BRANDING) || BUILDFLAG(IS_IOS)
245 enable_static_pins_ = false;
246 #endif
247 // Check that there no invalid entries in the static HSTS bypass list.
248 for (auto& host : hsts_host_bypass_list) {
249 DCHECK(host.find('.') == std::string::npos);
250 hsts_host_bypass_list_.insert(host);
251 }
252 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
253 }
254
255 // Both HSTS and HPKP cause fatal SSL errors, so return true if a
256 // host has either.
ShouldSSLErrorsBeFatal(const std::string & host)257 bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) {
258 STSState unused_sts;
259 PKPState unused_pkp;
260 return GetSTSState(host, &unused_sts) || GetPKPState(host, &unused_pkp);
261 }
262
NetLogUpgradeToSSLParam(const std::string & host)263 base::Value::Dict TransportSecurityState::NetLogUpgradeToSSLParam(
264 const std::string& host) {
265 STSState sts_state;
266 base::Value::Dict dict;
267 dict.Set("host", host);
268 dict.Set("get_sts_state_result", GetSTSState(host, &sts_state));
269 dict.Set("should_upgrade_to_ssl", sts_state.ShouldUpgradeToSSL());
270 dict.Set("host_found_in_hsts_bypass_list",
271 hsts_host_bypass_list_.find(host) != hsts_host_bypass_list_.end());
272 return dict;
273 }
274
ShouldUpgradeToSSL(const std::string & host,const NetLogWithSource & net_log)275 bool TransportSecurityState::ShouldUpgradeToSSL(
276 const std::string& host,
277 const NetLogWithSource& net_log) {
278 STSState sts_state;
279 net_log.AddEvent(
280 NetLogEventType::TRANSPORT_SECURITY_STATE_SHOULD_UPGRADE_TO_SSL,
281 [&] { return NetLogUpgradeToSSLParam(host); });
282 return GetSTSState(host, &sts_state) && sts_state.ShouldUpgradeToSSL();
283 }
284
CheckPublicKeyPins(const HostPortPair & host_port_pair,bool is_issued_by_known_root,const HashValueVector & public_key_hashes)285 TransportSecurityState::PKPStatus TransportSecurityState::CheckPublicKeyPins(
286 const HostPortPair& host_port_pair,
287 bool is_issued_by_known_root,
288 const HashValueVector& public_key_hashes) {
289 // Perform pin validation only if the server actually has public key pins.
290 if (!HasPublicKeyPins(host_port_pair.host())) {
291 return PKPStatus::OK;
292 }
293
294 return CheckPublicKeyPinsImpl(host_port_pair, is_issued_by_known_root,
295 public_key_hashes);
296 }
297
HasPublicKeyPins(const std::string & host)298 bool TransportSecurityState::HasPublicKeyPins(const std::string& host) {
299 PKPState pkp_state;
300 return GetPKPState(host, &pkp_state) && pkp_state.HasPublicKeyPins();
301 }
302
303 TransportSecurityState::CTRequirementsStatus
CheckCTRequirements(const net::HostPortPair & host_port_pair,bool is_issued_by_known_root,const HashValueVector & public_key_hashes,const X509Certificate * validated_certificate_chain,ct::CTPolicyCompliance policy_compliance)304 TransportSecurityState::CheckCTRequirements(
305 const net::HostPortPair& host_port_pair,
306 bool is_issued_by_known_root,
307 const HashValueVector& public_key_hashes,
308 const X509Certificate* validated_certificate_chain,
309 ct::CTPolicyCompliance policy_compliance) {
310 using CTRequirementLevel = RequireCTDelegate::CTRequirementLevel;
311 std::string hostname = host_port_pair.host();
312
313 // If CT is emergency disabled, we don't require CT for any host.
314 if (ct_emergency_disable_) {
315 return CT_NOT_REQUIRED;
316 }
317
318 // CT is not required if the certificate does not chain to a publicly
319 // trusted root certificate.
320 if (!is_issued_by_known_root) {
321 return CT_NOT_REQUIRED;
322 }
323
324 // A connection is considered compliant if it has sufficient SCTs or if the
325 // build is outdated. Other statuses are not considered compliant; this
326 // includes COMPLIANCE_DETAILS_NOT_AVAILABLE because compliance must have been
327 // evaluated in order to determine that the connection is compliant.
328 bool complies =
329 (policy_compliance ==
330 ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS ||
331 policy_compliance == ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY);
332
333 CTRequirementLevel ct_required = CTRequirementLevel::NOT_REQUIRED;
334 if (require_ct_delegate_) {
335 // Allow the delegate to override the CT requirement state.
336 ct_required = require_ct_delegate_->IsCTRequiredForHost(
337 hostname, validated_certificate_chain, public_key_hashes);
338 }
339 switch (ct_required) {
340 case CTRequirementLevel::REQUIRED:
341 return complies ? CT_REQUIREMENTS_MET : CT_REQUIREMENTS_NOT_MET;
342 case CTRequirementLevel::NOT_REQUIRED:
343 return CT_NOT_REQUIRED;
344 }
345 }
346
SetDelegate(TransportSecurityState::Delegate * delegate)347 void TransportSecurityState::SetDelegate(
348 TransportSecurityState::Delegate* delegate) {
349 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
350 delegate_ = delegate;
351 }
352
SetRequireCTDelegate(RequireCTDelegate * delegate)353 void TransportSecurityState::SetRequireCTDelegate(RequireCTDelegate* delegate) {
354 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
355 require_ct_delegate_ = delegate;
356 }
357
UpdatePinList(const std::vector<PinSet> & pinsets,const std::vector<PinSetInfo> & host_pins,base::Time update_time)358 void TransportSecurityState::UpdatePinList(
359 const std::vector<PinSet>& pinsets,
360 const std::vector<PinSetInfo>& host_pins,
361 base::Time update_time) {
362 pinsets_ = pinsets;
363 key_pins_list_last_update_time_ = update_time;
364 host_pins_.emplace();
365 std::map<std::string, PinSet const*> pinset_names_map;
366 for (const auto& pinset : pinsets_) {
367 pinset_names_map[pinset.name()] = &pinset;
368 }
369 for (const auto& pin : host_pins) {
370 if (!base::Contains(pinset_names_map, pin.pinset_name_)) {
371 // This should never happen, but if the component is bad and missing an
372 // entry, we will ignore that particular pin.
373 continue;
374 }
375 host_pins_.value()[pin.hostname_] =
376 std::pair(pinset_names_map[pin.pinset_name_], pin.include_subdomains_);
377 }
378 }
379
AddHSTSInternal(const std::string & host,TransportSecurityState::STSState::UpgradeMode upgrade_mode,const base::Time & expiry,bool include_subdomains)380 void TransportSecurityState::AddHSTSInternal(
381 const std::string& host,
382 TransportSecurityState::STSState::UpgradeMode upgrade_mode,
383 const base::Time& expiry,
384 bool include_subdomains) {
385 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
386 const std::vector<uint8_t> canonicalized_host = CanonicalizeHost(host);
387 if (canonicalized_host.empty())
388 return;
389
390 STSState sts_state;
391 // No need to store |sts_state.domain| since it is redundant.
392 // (|canonicalized_host| is the map key.)
393 sts_state.last_observed = base::Time::Now();
394 sts_state.include_subdomains = include_subdomains;
395 sts_state.expiry = expiry;
396 sts_state.upgrade_mode = upgrade_mode;
397
398 // Only store new state when HSTS is explicitly enabled. If it is
399 // disabled, remove the state from the enabled hosts.
400 if (sts_state.ShouldUpgradeToSSL()) {
401 enabled_sts_hosts_[HashHost(canonicalized_host)] = sts_state;
402 } else {
403 const HashedHost hashed_host = HashHost(canonicalized_host);
404 enabled_sts_hosts_.erase(hashed_host);
405 }
406
407 DirtyNotify();
408 }
409
AddHPKPInternal(const std::string & host,const base::Time & last_observed,const base::Time & expiry,bool include_subdomains,const HashValueVector & hashes)410 void TransportSecurityState::AddHPKPInternal(const std::string& host,
411 const base::Time& last_observed,
412 const base::Time& expiry,
413 bool include_subdomains,
414 const HashValueVector& hashes) {
415 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
416 const std::vector<uint8_t> canonicalized_host = CanonicalizeHost(host);
417 if (canonicalized_host.empty())
418 return;
419
420 PKPState pkp_state;
421 // No need to store |pkp_state.domain| since it is redundant.
422 // (|canonicalized_host| is the map key.)
423 pkp_state.last_observed = last_observed;
424 pkp_state.expiry = expiry;
425 pkp_state.include_subdomains = include_subdomains;
426 pkp_state.spki_hashes = hashes;
427
428 // Only store new state when HPKP is explicitly enabled. If it is
429 // disabled, remove the state from the enabled hosts.
430 if (pkp_state.HasPublicKeyPins()) {
431 enabled_pkp_hosts_[HashHost(canonicalized_host)] = pkp_state;
432 } else {
433 const HashedHost hashed_host = HashHost(canonicalized_host);
434 enabled_pkp_hosts_.erase(hashed_host);
435 }
436
437 DirtyNotify();
438 }
439
440 void TransportSecurityState::
SetEnablePublicKeyPinningBypassForLocalTrustAnchors(bool value)441 SetEnablePublicKeyPinningBypassForLocalTrustAnchors(bool value) {
442 enable_pkp_bypass_for_local_trust_anchors_ = value;
443 }
444
CheckPins(const HostPortPair & host_port_pair,bool is_issued_by_known_root,const TransportSecurityState::PKPState & pkp_state,const HashValueVector & hashes)445 TransportSecurityState::PKPStatus TransportSecurityState::CheckPins(
446 const HostPortPair& host_port_pair,
447 bool is_issued_by_known_root,
448 const TransportSecurityState::PKPState& pkp_state,
449 const HashValueVector& hashes) {
450 if (pkp_state.CheckPublicKeyPins(hashes)) {
451 return PKPStatus::OK;
452 }
453
454 // Don't report violations for certificates that chain to local roots.
455 if (!is_issued_by_known_root && enable_pkp_bypass_for_local_trust_anchors_)
456 return PKPStatus::BYPASSED;
457
458 return PKPStatus::VIOLATED;
459 }
460
DeleteDynamicDataForHost(const std::string & host)461 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
462 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
463
464 const std::vector<uint8_t> canonicalized_host = CanonicalizeHost(host);
465 if (canonicalized_host.empty())
466 return false;
467
468 const HashedHost hashed_host = HashHost(canonicalized_host);
469 bool deleted = false;
470 auto sts_interator = enabled_sts_hosts_.find(hashed_host);
471 if (sts_interator != enabled_sts_hosts_.end()) {
472 enabled_sts_hosts_.erase(sts_interator);
473 deleted = true;
474 }
475
476 auto pkp_iterator = enabled_pkp_hosts_.find(hashed_host);
477 if (pkp_iterator != enabled_pkp_hosts_.end()) {
478 enabled_pkp_hosts_.erase(pkp_iterator);
479 deleted = true;
480 }
481
482 if (deleted)
483 DirtyNotify();
484 return deleted;
485 }
486
ClearDynamicData()487 void TransportSecurityState::ClearDynamicData() {
488 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
489 enabled_sts_hosts_.clear();
490 enabled_pkp_hosts_.clear();
491 }
492
DeleteAllDynamicDataBetween(base::Time start_time,base::Time end_time,base::OnceClosure callback)493 void TransportSecurityState::DeleteAllDynamicDataBetween(
494 base::Time start_time,
495 base::Time end_time,
496 base::OnceClosure callback) {
497 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
498
499 bool dirtied = false;
500 auto sts_iterator = enabled_sts_hosts_.begin();
501 while (sts_iterator != enabled_sts_hosts_.end()) {
502 if (sts_iterator->second.last_observed >= start_time &&
503 sts_iterator->second.last_observed < end_time) {
504 dirtied = true;
505 enabled_sts_hosts_.erase(sts_iterator++);
506 continue;
507 }
508
509 ++sts_iterator;
510 }
511
512 auto pkp_iterator = enabled_pkp_hosts_.begin();
513 while (pkp_iterator != enabled_pkp_hosts_.end()) {
514 if (pkp_iterator->second.last_observed >= start_time &&
515 pkp_iterator->second.last_observed < end_time) {
516 dirtied = true;
517 enabled_pkp_hosts_.erase(pkp_iterator++);
518 continue;
519 }
520
521 ++pkp_iterator;
522 }
523
524 if (dirtied && delegate_)
525 delegate_->WriteNow(this, std::move(callback));
526 else
527 std::move(callback).Run();
528 }
529
~TransportSecurityState()530 TransportSecurityState::~TransportSecurityState() {
531 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
532 }
533
DirtyNotify()534 void TransportSecurityState::DirtyNotify() {
535 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
536
537 if (delegate_)
538 delegate_->StateIsDirty(this);
539 }
540
AddHSTSHeader(const std::string & host,const std::string & value)541 bool TransportSecurityState::AddHSTSHeader(const std::string& host,
542 const std::string& value) {
543 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
544
545 base::Time now = base::Time::Now();
546 base::TimeDelta max_age;
547 bool include_subdomains;
548 if (!ParseHSTSHeader(value, &max_age, &include_subdomains)) {
549 return false;
550 }
551
552 // Handle max-age == 0.
553 STSState::UpgradeMode upgrade_mode;
554 if (max_age.InSeconds() == 0) {
555 upgrade_mode = STSState::MODE_DEFAULT;
556 } else {
557 upgrade_mode = STSState::MODE_FORCE_HTTPS;
558 }
559
560 AddHSTSInternal(host, upgrade_mode, now + max_age, include_subdomains);
561 return true;
562 }
563
AddHSTS(const std::string & host,const base::Time & expiry,bool include_subdomains)564 void TransportSecurityState::AddHSTS(const std::string& host,
565 const base::Time& expiry,
566 bool include_subdomains) {
567 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
568 AddHSTSInternal(host, STSState::MODE_FORCE_HTTPS, expiry, include_subdomains);
569 }
570
AddHPKP(const std::string & host,const base::Time & expiry,bool include_subdomains,const HashValueVector & hashes)571 void TransportSecurityState::AddHPKP(const std::string& host,
572 const base::Time& expiry,
573 bool include_subdomains,
574 const HashValueVector& hashes) {
575 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
576 AddHPKPInternal(host, base::Time::Now(), expiry, include_subdomains, hashes);
577 }
578
num_sts_entries() const579 size_t TransportSecurityState::num_sts_entries() const {
580 return enabled_sts_hosts_.size();
581 }
582
583 // static
IsBuildTimely()584 bool TransportSecurityState::IsBuildTimely() {
585 const base::Time build_time = base::GetBuildTime();
586 // We consider built-in information to be timely for 10 weeks.
587 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
588 }
589
590 TransportSecurityState::PKPStatus
CheckPublicKeyPinsImpl(const HostPortPair & host_port_pair,bool is_issued_by_known_root,const HashValueVector & hashes)591 TransportSecurityState::CheckPublicKeyPinsImpl(
592 const HostPortPair& host_port_pair,
593 bool is_issued_by_known_root,
594 const HashValueVector& hashes) {
595 PKPState pkp_state;
596 bool found_state = GetPKPState(host_port_pair.host(), &pkp_state);
597
598 // HasPublicKeyPins should have returned true in order for this method to have
599 // been called.
600 DCHECK(found_state);
601 return CheckPins(host_port_pair, is_issued_by_known_root, pkp_state, hashes);
602 }
603
GetStaticSTSState(const std::string & host,STSState * sts_result) const604 bool TransportSecurityState::GetStaticSTSState(const std::string& host,
605 STSState* sts_result) const {
606 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
607
608 if (!IsBuildTimely())
609 return false;
610
611 PreloadResult result;
612 if (DecodeHSTSPreload(host, &result) &&
613 hsts_host_bypass_list_.find(host) == hsts_host_bypass_list_.end() &&
614 result.force_https) {
615 sts_result->domain = host.substr(result.hostname_offset);
616 sts_result->include_subdomains = result.sts_include_subdomains;
617 sts_result->last_observed = base::GetBuildTime();
618 sts_result->upgrade_mode = STSState::MODE_FORCE_HTTPS;
619 return true;
620 }
621
622 return false;
623 }
624
GetStaticPKPState(const std::string & host,PKPState * pkp_result) const625 bool TransportSecurityState::GetStaticPKPState(const std::string& host,
626 PKPState* pkp_result) const {
627 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
628
629 if (!enable_static_pins_ || !IsStaticPKPListTimely() ||
630 !base::FeatureList::IsEnabled(features::kStaticKeyPinningEnforcement)) {
631 return false;
632 }
633
634 PreloadResult result;
635 if (host_pins_.has_value()) {
636 // Ensure that |host| is a valid hostname before processing.
637 if (CanonicalizeHost(host).empty()) {
638 return false;
639 }
640 // Normalize any trailing '.' used for DNS suffix searches.
641 std::string normalized_host = host;
642 size_t trailing_dot_found = normalized_host.find_last_not_of('.');
643 if (trailing_dot_found == std::string::npos) {
644 // Hostname is either empty or all dots
645 return false;
646 }
647 normalized_host.erase(trailing_dot_found + 1);
648 normalized_host = base::ToLowerASCII(normalized_host);
649
650 std::string_view search_hostname = normalized_host;
651 while (true) {
652 auto iter = host_pins_->find(search_hostname);
653 // Only consider this a match if either include_subdomains is set, or
654 // this is an exact match of the full hostname.
655 if (iter != host_pins_->end() &&
656 (iter->second.second || search_hostname == normalized_host)) {
657 pkp_result->domain = std::string(search_hostname);
658 pkp_result->last_observed = key_pins_list_last_update_time_;
659 pkp_result->include_subdomains = iter->second.second;
660 const PinSet* pinset = iter->second.first;
661 for (auto hash : pinset->static_spki_hashes()) {
662 // If the update is malformed, it's preferable to skip the hash than
663 // crash.
664 if (hash.size() == 32) {
665 AddHash(reinterpret_cast<const char*>(hash.data()),
666 &pkp_result->spki_hashes);
667 }
668 }
669 for (auto hash : pinset->bad_static_spki_hashes()) {
670 // If the update is malformed, it's preferable to skip the hash than
671 // crash.
672 if (hash.size() == 32) {
673 AddHash(reinterpret_cast<const char*>(hash.data()),
674 &pkp_result->bad_spki_hashes);
675 }
676 }
677 return true;
678 }
679 auto dot_pos = search_hostname.find(".");
680 if (dot_pos == std::string::npos) {
681 // If this was not a match, and there are no more dots in the string,
682 // there are no more domains to try.
683 return false;
684 }
685 // Try again in case this is a subdomain of a pinned domain that includes
686 // subdomains.
687 search_hostname = search_hostname.substr(dot_pos + 1);
688 }
689 } else if (DecodeHSTSPreload(host, &result) && result.has_pins) {
690 if (result.pinset_id >= g_hsts_source->pinsets_count)
691 return false;
692
693 pkp_result->domain = host.substr(result.hostname_offset);
694 pkp_result->include_subdomains = result.pkp_include_subdomains;
695 pkp_result->last_observed = base::GetBuildTime();
696
697 const TransportSecurityStateSource::Pinset* pinset =
698 &g_hsts_source->pinsets[result.pinset_id];
699
700 if (pinset->accepted_pins) {
701 const char* const* sha256_hash = pinset->accepted_pins;
702 while (*sha256_hash) {
703 AddHash(*sha256_hash, &pkp_result->spki_hashes);
704 sha256_hash++;
705 }
706 }
707 if (pinset->rejected_pins) {
708 const char* const* sha256_hash = pinset->rejected_pins;
709 while (*sha256_hash) {
710 AddHash(*sha256_hash, &pkp_result->bad_spki_hashes);
711 sha256_hash++;
712 }
713 }
714 return true;
715 }
716
717 return false;
718 }
719
GetSTSState(const std::string & host,STSState * result)720 bool TransportSecurityState::GetSTSState(const std::string& host,
721 STSState* result) {
722 return GetDynamicSTSState(host, result) || GetStaticSTSState(host, result);
723 }
724
GetPKPState(const std::string & host,PKPState * result)725 bool TransportSecurityState::GetPKPState(const std::string& host,
726 PKPState* result) {
727 return GetDynamicPKPState(host, result) || GetStaticPKPState(host, result);
728 }
729
GetDynamicSTSState(const std::string & host,STSState * result)730 bool TransportSecurityState::GetDynamicSTSState(const std::string& host,
731 STSState* result) {
732 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
733
734 const std::vector<uint8_t> canonicalized_host = CanonicalizeHost(host);
735 if (canonicalized_host.empty())
736 return false;
737
738 base::Time current_time(base::Time::Now());
739
740 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
741 base::span<const uint8_t> host_sub_chunk =
742 base::make_span(canonicalized_host).subspan(i);
743 auto j = enabled_sts_hosts_.find(HashHost(host_sub_chunk));
744 if (j == enabled_sts_hosts_.end())
745 continue;
746
747 // If the entry is invalid, drop it.
748 if (current_time > j->second.expiry) {
749 enabled_sts_hosts_.erase(j);
750 DirtyNotify();
751 continue;
752 }
753
754 // An entry matches if it is either an exact match, or if it is a prefix
755 // match and the includeSubDomains directive was included.
756 if (i == 0 || j->second.include_subdomains) {
757 std::optional<std::string> dotted_name =
758 dns_names_util::NetworkToDottedName(host_sub_chunk);
759 if (!dotted_name)
760 return false;
761
762 *result = j->second;
763 result->domain = std::move(dotted_name).value();
764 return true;
765 }
766 }
767
768 return false;
769 }
770
GetDynamicPKPState(const std::string & host,PKPState * result)771 bool TransportSecurityState::GetDynamicPKPState(const std::string& host,
772 PKPState* result) {
773 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
774
775 const std::vector<uint8_t> canonicalized_host = CanonicalizeHost(host);
776 if (canonicalized_host.empty())
777 return false;
778
779 base::Time current_time(base::Time::Now());
780
781 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
782 base::span<const uint8_t> host_sub_chunk =
783 base::make_span(canonicalized_host).subspan(i);
784 auto j = enabled_pkp_hosts_.find(HashHost(host_sub_chunk));
785 if (j == enabled_pkp_hosts_.end())
786 continue;
787
788 // If the entry is invalid, drop it.
789 if (current_time > j->second.expiry) {
790 enabled_pkp_hosts_.erase(j);
791 DirtyNotify();
792 continue;
793 }
794
795 // If this is the most specific PKP match, add it to the result. Note: a PKP
796 // entry at a more specific domain overrides a less specific domain whether
797 // or not |include_subdomains| is set.
798 //
799 // TODO(davidben): This does not match the HSTS behavior. We no longer
800 // implement HPKP, so this logic is only used via AddHPKP(), reachable from
801 // Cronet.
802 if (i == 0 || j->second.include_subdomains) {
803 std::optional<std::string> dotted_name =
804 dns_names_util::NetworkToDottedName(host_sub_chunk);
805 if (!dotted_name)
806 return false;
807
808 *result = j->second;
809 result->domain = std::move(dotted_name).value();
810 return true;
811 }
812
813 break;
814 }
815
816 return false;
817 }
818
AddOrUpdateEnabledSTSHosts(const HashedHost & hashed_host,const STSState & state)819 void TransportSecurityState::AddOrUpdateEnabledSTSHosts(
820 const HashedHost& hashed_host,
821 const STSState& state) {
822 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
823 DCHECK(state.ShouldUpgradeToSSL());
824 enabled_sts_hosts_[hashed_host] = state;
825 }
826
827 TransportSecurityState::STSState::STSState() = default;
828
829 TransportSecurityState::STSState::~STSState() = default;
830
ShouldUpgradeToSSL() const831 bool TransportSecurityState::STSState::ShouldUpgradeToSSL() const {
832 return upgrade_mode == MODE_FORCE_HTTPS;
833 }
834
STSStateIterator(const TransportSecurityState & state)835 TransportSecurityState::STSStateIterator::STSStateIterator(
836 const TransportSecurityState& state)
837 : iterator_(state.enabled_sts_hosts_.begin()),
838 end_(state.enabled_sts_hosts_.end()) {}
839
840 TransportSecurityState::STSStateIterator::~STSStateIterator() = default;
841
842 TransportSecurityState::PKPState::PKPState() = default;
843
844 TransportSecurityState::PKPState::PKPState(const PKPState& other) = default;
845
846 TransportSecurityState::PKPState::~PKPState() = default;
847
PinSet(std::string name,std::vector<std::vector<uint8_t>> static_spki_hashes,std::vector<std::vector<uint8_t>> bad_static_spki_hashes)848 TransportSecurityState::PinSet::PinSet(
849 std::string name,
850 std::vector<std::vector<uint8_t>> static_spki_hashes,
851 std::vector<std::vector<uint8_t>> bad_static_spki_hashes)
852 : name_(std::move(name)),
853 static_spki_hashes_(std::move(static_spki_hashes)),
854 bad_static_spki_hashes_(std::move(bad_static_spki_hashes)) {}
855
856 TransportSecurityState::PinSet::PinSet(const PinSet& other) = default;
857 TransportSecurityState::PinSet::~PinSet() = default;
858
PinSetInfo(std::string hostname,std::string pinset_name,bool include_subdomains)859 TransportSecurityState::PinSetInfo::PinSetInfo(std::string hostname,
860 std::string pinset_name,
861 bool include_subdomains)
862 : hostname_(std::move(hostname)),
863 pinset_name_(std::move(pinset_name)),
864 include_subdomains_(std::move(include_subdomains)) {}
865
CheckPublicKeyPins(const HashValueVector & hashes) const866 bool TransportSecurityState::PKPState::CheckPublicKeyPins(
867 const HashValueVector& hashes) const {
868 // Validate that hashes is not empty. By the time this code is called (in
869 // production), that should never happen, but it's good to be defensive.
870 // And, hashes *can* be empty in some test scenarios.
871 if (hashes.empty()) {
872 return false;
873 }
874
875 if (HashesIntersect(bad_spki_hashes, hashes)) {
876 return false;
877 }
878
879 // If there are no pins, then any valid chain is acceptable.
880 if (spki_hashes.empty())
881 return true;
882
883 if (HashesIntersect(spki_hashes, hashes)) {
884 return true;
885 }
886
887 return false;
888 }
889
HasPublicKeyPins() const890 bool TransportSecurityState::PKPState::HasPublicKeyPins() const {
891 return spki_hashes.size() > 0 || bad_spki_hashes.size() > 0;
892 }
893
IsStaticPKPListTimely() const894 bool TransportSecurityState::IsStaticPKPListTimely() const {
895 if (pins_list_always_timely_for_testing_) {
896 return true;
897 }
898
899 // If the list has not been updated via component updater, freshness depends
900 // on the compiled-in list freshness.
901 if (!host_pins_.has_value()) {
902 #if BUILDFLAG(INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST)
903 return (base::Time::Now() - kPinsListTimestamp).InDays() < 70;
904 #else
905 return false;
906 #endif
907 }
908 DCHECK(!key_pins_list_last_update_time_.is_null());
909 // Else, we use the last update time.
910 return (base::Time::Now() - key_pins_list_last_update_time_).InDays() <
911 70 /* 10 weeks */;
912 }
913
914 } // namespace net
915