xref: /aosp_15_r20/external/cronet/net/http/transport_security_state.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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