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