• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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