chrome_require_ct_delegate.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/certificate_transparency/chrome_require_ct_delegate.h"
  5. #include <algorithm>
  6. #include <iterator>
  7. #include <map>
  8. #include <set>
  9. #include <string>
  10. #include <utility>
  11. #include <vector>
  12. #include "base/bind.h"
  13. #include "base/callback.h"
  14. #include "base/containers/contains.h"
  15. #include "base/containers/cxx20_erase.h"
  16. #include "base/location.h"
  17. #include "base/logging.h"
  18. #include "base/memory/ref_counted.h"
  19. #include "base/strings/string_util.h"
  20. #include "base/task/sequenced_task_runner.h"
  21. #include "base/threading/sequenced_task_runner_handle.h"
  22. #include "base/values.h"
  23. #include "components/url_formatter/url_fixer.h"
  24. #include "components/url_matcher/url_matcher.h"
  25. #include "crypto/sha2.h"
  26. #include "net/base/hash_value.h"
  27. #include "net/base/host_port_pair.h"
  28. #include "net/cert/asn1_util.h"
  29. #include "net/cert/known_roots.h"
  30. #include "net/cert/pki/name_constraints.h"
  31. #include "net/cert/pki/parse_name.h"
  32. #include "net/cert/pki/parsed_certificate.h"
  33. #include "net/cert/x509_certificate.h"
  34. #include "net/cert/x509_util.h"
  35. namespace certificate_transparency {
  36. namespace {
  37. // Helper that takes a given net::RDNSequence and returns only the
  38. // organizationName net::X509NameAttributes.
  39. class OrgAttributeFilter {
  40. public:
  41. // Creates a new OrgAttributeFilter for |sequence| that begins iterating at
  42. // |head|. Note that |head| can be equal to |sequence.end()|, in which case,
  43. // there are no organizationName attributes.
  44. explicit OrgAttributeFilter(const net::RDNSequence& sequence)
  45. : sequence_head_(sequence.begin()), sequence_end_(sequence.end()) {
  46. if (sequence_head_ != sequence_end_) {
  47. rdn_it_ = sequence_head_->begin();
  48. AdvanceIfNecessary();
  49. }
  50. }
  51. bool IsValid() const { return sequence_head_ != sequence_end_; }
  52. const net::X509NameAttribute& GetAttribute() const {
  53. DCHECK(IsValid());
  54. return *rdn_it_;
  55. }
  56. void Advance() {
  57. DCHECK(IsValid());
  58. ++rdn_it_;
  59. AdvanceIfNecessary();
  60. }
  61. private:
  62. // If the current field is an organization field, does nothing, otherwise,
  63. // advances the state to the next organization field, or, if no more are
  64. // present, the end of the sequence.
  65. void AdvanceIfNecessary() {
  66. while (sequence_head_ != sequence_end_) {
  67. while (rdn_it_ != sequence_head_->end()) {
  68. if (rdn_it_->type == net::der::Input(net::kTypeOrganizationNameOid))
  69. return;
  70. ++rdn_it_;
  71. }
  72. ++sequence_head_;
  73. if (sequence_head_ != sequence_end_) {
  74. rdn_it_ = sequence_head_->begin();
  75. }
  76. }
  77. }
  78. net::RDNSequence::const_iterator sequence_head_;
  79. net::RDNSequence::const_iterator sequence_end_;
  80. net::RelativeDistinguishedName::const_iterator rdn_it_;
  81. };
  82. // Returns true if |dn_without_sequence| identifies an
  83. // organizationally-validated certificate, per the CA/Browser Forum's Baseline
  84. // Requirements, storing the parsed RDNSequence in |*out|.
  85. bool ParseOrganizationBoundName(net::der::Input dn_without_sequence,
  86. net::RDNSequence* out) {
  87. if (!net::ParseNameValue(dn_without_sequence, out))
  88. return false;
  89. for (const auto& rdn : *out) {
  90. for (const auto& attribute_type_and_value : rdn) {
  91. if (attribute_type_and_value.type ==
  92. net::der::Input(net::kTypeOrganizationNameOid)) {
  93. return true;
  94. }
  95. }
  96. }
  97. return false;
  98. }
  99. // Returns true if the certificate identified by |leaf_rdn_sequence| is
  100. // considered to be issued under the same organizational authority as
  101. // |org_cert|.
  102. bool AreCertsSameOrganization(const net::RDNSequence& leaf_rdn_sequence,
  103. CRYPTO_BUFFER* org_cert) {
  104. scoped_refptr<net::ParsedCertificate> parsed_org =
  105. net::ParsedCertificate::Create(bssl::UpRef(org_cert),
  106. net::ParseCertificateOptions(), nullptr);
  107. if (!parsed_org)
  108. return false;
  109. // If the candidate cert has nameConstraints, see if it has a
  110. // permittedSubtrees nameConstraint over a DirectoryName that is
  111. // organizationally-bound. If so, the enforcement of nameConstraints is
  112. // sufficient to consider |org_cert| a match.
  113. if (parsed_org->has_name_constraints()) {
  114. const net::NameConstraints& nc = parsed_org->name_constraints();
  115. for (const auto& permitted_name : nc.permitted_subtrees().directory_names) {
  116. net::RDNSequence tmp;
  117. if (ParseOrganizationBoundName(permitted_name, &tmp))
  118. return true;
  119. }
  120. }
  121. net::RDNSequence org_rdn_sequence;
  122. if (!net::ParseNameValue(parsed_org->normalized_subject(), &org_rdn_sequence))
  123. return false;
  124. // Finally, try to match the organization fields within |leaf_rdn_sequence|
  125. // to |org_rdn_sequence|. As |leaf_rdn_sequence| has already been checked
  126. // for all the necessary fields, it's not necessary to check
  127. // |org_rdn_sequence|. Iterate through all of the organization fields in
  128. // each, doing a byte-for-byte equality check.
  129. // Note that this does permit differences in the SET encapsulations between
  130. // RelativeDistinguishedNames, although it does still require that the same
  131. // number of organization fields appear, and with the same overall ordering.
  132. // This is simply as an implementation simplification, and not done for
  133. // semantic or technical reasons.
  134. OrgAttributeFilter leaf_filter(leaf_rdn_sequence);
  135. OrgAttributeFilter org_filter(org_rdn_sequence);
  136. while (leaf_filter.IsValid() && org_filter.IsValid()) {
  137. if (leaf_filter.GetAttribute().type != org_filter.GetAttribute().type ||
  138. leaf_filter.GetAttribute().value_tag !=
  139. org_filter.GetAttribute().value_tag ||
  140. leaf_filter.GetAttribute().value != org_filter.GetAttribute().value) {
  141. return false;
  142. }
  143. leaf_filter.Advance();
  144. org_filter.Advance();
  145. }
  146. // Ensure all attributes were fully consumed.
  147. return !leaf_filter.IsValid() && !org_filter.IsValid();
  148. }
  149. } // namespace
  150. ChromeRequireCTDelegate::ChromeRequireCTDelegate()
  151. : url_matcher_(std::make_unique<url_matcher::URLMatcher>()), next_id_(0) {}
  152. ChromeRequireCTDelegate::~ChromeRequireCTDelegate() {}
  153. net::TransportSecurityState::RequireCTDelegate::CTRequirementLevel
  154. ChromeRequireCTDelegate::IsCTRequiredForHost(
  155. const std::string& hostname,
  156. const net::X509Certificate* chain,
  157. const net::HashValueVector& spki_hashes) {
  158. bool ct_required = false;
  159. if (MatchHostname(hostname, &ct_required) ||
  160. MatchSPKI(chain, spki_hashes, &ct_required)) {
  161. return ct_required ? CTRequirementLevel::REQUIRED
  162. : CTRequirementLevel::NOT_REQUIRED;
  163. }
  164. // Compute >= 2018-05-01, rather than deal with possible fractional
  165. // seconds.
  166. const base::Time kMay_1_2018 =
  167. base::Time::UnixEpoch() + base::Seconds(1525132800);
  168. if (chain->valid_start() >= kMay_1_2018)
  169. return CTRequirementLevel::REQUIRED;
  170. return CTRequirementLevel::DEFAULT;
  171. }
  172. void ChromeRequireCTDelegate::UpdateCTPolicies(
  173. const std::vector<std::string>& required_hosts,
  174. const std::vector<std::string>& excluded_hosts,
  175. const std::vector<std::string>& excluded_spkis,
  176. const std::vector<std::string>& excluded_legacy_spkis) {
  177. url_matcher_ = std::make_unique<url_matcher::URLMatcher>();
  178. filters_.clear();
  179. next_id_ = 0;
  180. url_matcher::URLMatcherConditionSet::Vector all_conditions;
  181. AddFilters(true, required_hosts, &all_conditions);
  182. AddFilters(false, excluded_hosts, &all_conditions);
  183. url_matcher_->AddConditionSets(all_conditions);
  184. ParseSpkiHashes(excluded_spkis, &spkis_);
  185. ParseSpkiHashes(excluded_legacy_spkis, &legacy_spkis_);
  186. // Filter out SPKIs that aren't for legacy CAs.
  187. base::EraseIf(legacy_spkis_, [](const net::HashValue& hash) {
  188. if (!net::IsLegacyPubliclyTrustedCA(hash)) {
  189. LOG(ERROR) << "Non-legacy SPKI configured " << hash.ToString();
  190. return true;
  191. }
  192. return false;
  193. });
  194. }
  195. bool ChromeRequireCTDelegate::MatchHostname(const std::string& hostname,
  196. bool* ct_required) const {
  197. if (url_matcher_->IsEmpty())
  198. return false;
  199. // Scheme and port are ignored by the policy, so it's OK to construct a
  200. // new GURL here. However, |hostname| is in network form, not URL form,
  201. // so it's necessary to wrap IPv6 addresses in brackets.
  202. std::set<base::MatcherStringPattern::ID> matching_ids =
  203. url_matcher_->MatchURL(
  204. GURL("https://" + net::HostPortPair(hostname, 443).HostForURL()));
  205. if (matching_ids.empty())
  206. return false;
  207. // Determine the overall policy by determining the most specific policy.
  208. auto it = filters_.begin();
  209. const Filter* active_filter = nullptr;
  210. for (const auto& match : matching_ids) {
  211. // Because both |filters_| and |matching_ids| are sorted on the ID,
  212. // treat both as forward-only iterators.
  213. while (it != filters_.end() && it->first < match)
  214. ++it;
  215. if (it == filters_.end()) {
  216. NOTREACHED();
  217. break;
  218. }
  219. if (!active_filter || FilterTakesPrecedence(it->second, *active_filter))
  220. active_filter = &it->second;
  221. }
  222. CHECK(active_filter);
  223. *ct_required = active_filter->ct_required;
  224. return true;
  225. }
  226. bool ChromeRequireCTDelegate::MatchSPKI(const net::X509Certificate* chain,
  227. const net::HashValueVector& hashes,
  228. bool* ct_required) const {
  229. // Try to scan legacy SPKIs first, if any, since they will only require
  230. // comparing hash values.
  231. if (!legacy_spkis_.empty()) {
  232. for (const auto& hash : hashes) {
  233. if (std::binary_search(legacy_spkis_.begin(), legacy_spkis_.end(),
  234. hash)) {
  235. *ct_required = false;
  236. return true;
  237. }
  238. }
  239. }
  240. if (spkis_.empty())
  241. return false;
  242. // Scan the constrained SPKIs via |hashes| first, as an optimization. If
  243. // there are matches, the SPKI hash will have to be recomputed anyways to
  244. // find the matching certificate, but avoid recomputing all the hashes for
  245. // the case where there is no match.
  246. net::HashValueVector matches;
  247. for (const auto& hash : hashes) {
  248. if (std::binary_search(spkis_.begin(), spkis_.end(), hash)) {
  249. matches.push_back(hash);
  250. }
  251. }
  252. if (matches.empty())
  253. return false;
  254. CRYPTO_BUFFER* leaf_cert = chain->cert_buffer();
  255. // As an optimization, since the leaf is allowed to be listed as an SPKI,
  256. // a match on the leaf's SPKI hash can return early, without comparing
  257. // the organization information to itself.
  258. net::HashValue hash;
  259. if (net::x509_util::CalculateSha256SpkiHash(leaf_cert, &hash) &&
  260. base::Contains(matches, hash)) {
  261. *ct_required = false;
  262. return true;
  263. }
  264. // If there was a match (or multiple matches), it's necessary to recompute
  265. // the hashes to find the associated certificate.
  266. std::vector<CRYPTO_BUFFER*> candidates;
  267. for (const auto& buffer : chain->intermediate_buffers()) {
  268. if (net::x509_util::CalculateSha256SpkiHash(buffer.get(), &hash) &&
  269. base::Contains(matches, hash)) {
  270. candidates.push_back(buffer.get());
  271. }
  272. }
  273. if (candidates.empty())
  274. return false;
  275. scoped_refptr<net::ParsedCertificate> parsed_leaf =
  276. net::ParsedCertificate::Create(bssl::UpRef(leaf_cert),
  277. net::ParseCertificateOptions(), nullptr);
  278. if (!parsed_leaf)
  279. return false;
  280. // If the leaf is not organizationally-bound, it's not a match.
  281. net::RDNSequence leaf_rdn_sequence;
  282. if (!ParseOrganizationBoundName(parsed_leaf->normalized_subject(),
  283. &leaf_rdn_sequence)) {
  284. return false;
  285. }
  286. for (auto* cert : candidates) {
  287. if (AreCertsSameOrganization(leaf_rdn_sequence, cert)) {
  288. *ct_required = false;
  289. return true;
  290. }
  291. }
  292. return false;
  293. }
  294. void ChromeRequireCTDelegate::AddFilters(
  295. bool ct_required,
  296. const std::vector<std::string>& hosts,
  297. url_matcher::URLMatcherConditionSet::Vector* conditions) {
  298. for (const auto& pattern : hosts) {
  299. Filter filter;
  300. filter.ct_required = ct_required;
  301. // Parse the pattern just to the hostname, ignoring all other portions of
  302. // the URL.
  303. url::Parsed parsed;
  304. std::string ignored_scheme = url_formatter::SegmentURL(pattern, &parsed);
  305. if (!parsed.host.is_nonempty())
  306. continue; // If there is no host to match, can't apply the filter.
  307. std::string lc_host = base::ToLowerASCII(
  308. base::StringPiece(pattern).substr(parsed.host.begin, parsed.host.len));
  309. if (lc_host == "*") {
  310. // Wildcard hosts are not allowed and ignored.
  311. continue;
  312. } else if (lc_host[0] == '.') {
  313. // A leading dot means exact match and to not match subdomains.
  314. lc_host.erase(0, 1);
  315. filter.match_subdomains = false;
  316. } else {
  317. // Canonicalize the host to make sure it's an actual hostname, not an
  318. // IP address or a BROKEN canonical host, as matching subdomains is
  319. // not desirable for those.
  320. url::RawCanonOutputT<char> output;
  321. url::CanonHostInfo host_info;
  322. url::CanonicalizeHostVerbose(pattern.c_str(), parsed.host, &output,
  323. &host_info);
  324. // TODO(rsleevi): Use canonicalized form?
  325. if (host_info.family == url::CanonHostInfo::NEUTRAL) {
  326. // Match subdomains (implicit by the omission of '.'). Add in a
  327. // leading dot to make sure matches only happen at the domain
  328. // component boundary.
  329. lc_host.insert(lc_host.begin(), '.');
  330. filter.match_subdomains = true;
  331. } else {
  332. filter.match_subdomains = false;
  333. }
  334. }
  335. filter.host_length = lc_host.size();
  336. // Create a condition for the URLMatcher that matches the hostname (and/or
  337. // subdomains).
  338. url_matcher::URLMatcherConditionFactory* condition_factory =
  339. url_matcher_->condition_factory();
  340. std::set<url_matcher::URLMatcherCondition> condition_set;
  341. condition_set.insert(
  342. filter.match_subdomains
  343. ? condition_factory->CreateHostSuffixCondition(lc_host)
  344. : condition_factory->CreateHostEqualsCondition(lc_host));
  345. conditions->push_back(
  346. new url_matcher::URLMatcherConditionSet(next_id_, condition_set));
  347. filters_[next_id_] = filter;
  348. ++next_id_;
  349. }
  350. }
  351. void ChromeRequireCTDelegate::ParseSpkiHashes(
  352. const std::vector<std::string> spki_list,
  353. net::HashValueVector* hashes) const {
  354. hashes->clear();
  355. for (const auto& value : spki_list) {
  356. net::HashValue hash;
  357. if (!hash.FromString(value)) {
  358. continue;
  359. }
  360. hashes->push_back(std::move(hash));
  361. }
  362. std::sort(hashes->begin(), hashes->end());
  363. }
  364. bool ChromeRequireCTDelegate::FilterTakesPrecedence(const Filter& lhs,
  365. const Filter& rhs) const {
  366. if (lhs.match_subdomains != rhs.match_subdomains)
  367. return !lhs.match_subdomains; // Prefer the more explicit policy.
  368. if (lhs.host_length != rhs.host_length)
  369. return lhs.host_length > rhs.host_length; // Prefer the longer host match.
  370. if (lhs.ct_required != rhs.ct_required)
  371. return lhs.ct_required; // Prefer the policy that requires CT.
  372. return false;
  373. }
  374. } // namespace certificate_transparency