• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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/cert/pki/name_constraints.h"
6 
7 #include <limits.h>
8 
9 #include <memory>
10 
11 #include "base/numerics/clamped_math.h"
12 #include "net/cert/pki/cert_errors.h"
13 #include "net/cert/pki/common_cert_errors.h"
14 #include "net/cert/pki/string_util.h"
15 #include "net/cert/pki/verify_name_match.h"
16 #include "net/der/input.h"
17 #include "net/der/parser.h"
18 #include "net/der/tag.h"
19 #include "third_party/abseil-cpp/absl/types/optional.h"
20 
21 namespace net {
22 
23 namespace {
24 
25 // The name types of GeneralName that are fully supported in name constraints.
26 //
27 // (The other types will have the minimal checking described by RFC 5280
28 // section 4.2.1.10: If a name constraints extension that is marked as critical
29 // imposes constraints on a particular name form, and an instance of
30 // that name form appears in the subject field or subjectAltName
31 // extension of a subsequent certificate, then the application MUST
32 // either process the constraint or reject the certificate.)
33 const int kSupportedNameTypes = GENERAL_NAME_DNS_NAME |
34                                 GENERAL_NAME_DIRECTORY_NAME |
35                                 GENERAL_NAME_IP_ADDRESS;
36 
37 // Controls wildcard handling of DNSNameMatches.
38 // If WildcardMatchType is WILDCARD_PARTIAL_MATCH "*.bar.com" is considered to
39 // match the constraint "foo.bar.com". If it is WILDCARD_FULL_MATCH, "*.bar.com"
40 // will match "bar.com" but not "foo.bar.com".
41 enum WildcardMatchType { WILDCARD_PARTIAL_MATCH, WILDCARD_FULL_MATCH };
42 
43 // Returns true if |name| falls in the subtree defined by |dns_constraint|.
44 // RFC 5280 section 4.2.1.10:
45 // DNS name restrictions are expressed as host.example.com. Any DNS
46 // name that can be constructed by simply adding zero or more labels
47 // to the left-hand side of the name satisfies the name constraint. For
48 // example, www.host.example.com would satisfy the constraint but
49 // host1.example.com would not.
50 //
51 // |wildcard_matching| controls handling of wildcard names (|name| starts with
52 // "*."). Wildcard handling is not specified by RFC 5280, but certificate
53 // verification allows it, name constraints must check it similarly.
DNSNameMatches(std::string_view name,std::string_view dns_constraint,WildcardMatchType wildcard_matching)54 bool DNSNameMatches(std::string_view name,
55                     std::string_view dns_constraint,
56                     WildcardMatchType wildcard_matching) {
57   // Everything matches the empty DNS name constraint.
58   if (dns_constraint.empty())
59     return true;
60 
61   // Normalize absolute DNS names by removing the trailing dot, if any.
62   if (!name.empty() && *name.rbegin() == '.')
63     name.remove_suffix(1);
64   if (!dns_constraint.empty() && *dns_constraint.rbegin() == '.')
65     dns_constraint.remove_suffix(1);
66 
67   // Wildcard partial-match handling ("*.bar.com" matching name constraint
68   // "foo.bar.com"). This only handles the case where the the dnsname and the
69   // constraint match after removing the leftmost label, otherwise it is handled
70   // by falling through to the check of whether the dnsname is fully within or
71   // fully outside of the constraint.
72   if (wildcard_matching == WILDCARD_PARTIAL_MATCH && name.size() > 2 &&
73       name[0] == '*' && name[1] == '.') {
74     size_t dns_constraint_dot_pos = dns_constraint.find('.');
75     if (dns_constraint_dot_pos != std::string::npos) {
76       std::string_view dns_constraint_domain =
77           dns_constraint.substr(dns_constraint_dot_pos + 1);
78       std::string_view wildcard_domain = name.substr(2);
79       if (net::string_util::IsEqualNoCase(wildcard_domain,
80                                           dns_constraint_domain)) {
81         return true;
82       }
83     }
84   }
85 
86   if (!net::string_util::EndsWithNoCase(name, dns_constraint)) {
87     return false;
88   }
89 
90   // Exact match.
91   if (name.size() == dns_constraint.size())
92     return true;
93   // If dNSName constraint starts with a dot, only subdomains should match.
94   // (e.g., "foo.bar.com" matches constraint ".bar.com", but "bar.com" doesn't.)
95   // RFC 5280 is ambiguous, but this matches the behavior of other platforms.
96   if (!dns_constraint.empty() && dns_constraint[0] == '.')
97     dns_constraint.remove_prefix(1);
98   // Subtree match.
99   if (name.size() > dns_constraint.size() &&
100       name[name.size() - dns_constraint.size() - 1] == '.') {
101     return true;
102   }
103   // Trailing text matches, but not in a subtree (e.g., "foobar.com" is not a
104   // match for "bar.com").
105   return false;
106 }
107 
108 // Parses a GeneralSubtrees |value| and store the contents in |subtrees|.
109 // The individual values stored into |subtrees| are not validated by this
110 // function.
111 // NOTE: |subtrees| is not pre-initialized by the function(it is expected to be
112 // a default initialized object), and it will be modified regardless of the
113 // return value.
ParseGeneralSubtrees(const der::Input & value,GeneralNames * subtrees,CertErrors * errors)114 [[nodiscard]] bool ParseGeneralSubtrees(const der::Input& value,
115                                         GeneralNames* subtrees,
116                                         CertErrors* errors) {
117   DCHECK(errors);
118 
119   // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
120   //
121   // GeneralSubtree ::= SEQUENCE {
122   //      base                    GeneralName,
123   //      minimum         [0]     BaseDistance DEFAULT 0,
124   //      maximum         [1]     BaseDistance OPTIONAL }
125   //
126   // BaseDistance ::= INTEGER (0..MAX)
127   der::Parser sequence_parser(value);
128   // The GeneralSubtrees sequence should have at least 1 element.
129   if (!sequence_parser.HasMore())
130     return false;
131   while (sequence_parser.HasMore()) {
132     der::Parser subtree_sequence;
133     if (!sequence_parser.ReadSequence(&subtree_sequence))
134       return false;
135 
136     der::Input raw_general_name;
137     if (!subtree_sequence.ReadRawTLV(&raw_general_name))
138       return false;
139 
140     if (!ParseGeneralName(raw_general_name,
141                           GeneralNames::IP_ADDRESS_AND_NETMASK, subtrees,
142                           errors)) {
143       errors->AddError(kFailedParsingGeneralName);
144       return false;
145     }
146 
147     // RFC 5280 section 4.2.1.10:
148     // Within this profile, the minimum and maximum fields are not used with any
149     // name forms, thus, the minimum MUST be zero, and maximum MUST be absent.
150     // However, if an application encounters a critical name constraints
151     // extension that specifies other values for minimum or maximum for a name
152     // form that appears in a subsequent certificate, the application MUST
153     // either process these fields or reject the certificate.
154 
155     // Note that technically failing here isn't required: rather only need to
156     // fail if a name of this type actually appears in a subsequent cert and
157     // this extension was marked critical. However the minimum and maximum
158     // fields appear uncommon enough that implementing that isn't useful.
159     if (subtree_sequence.HasMore())
160       return false;
161   }
162   return true;
163 }
164 
165 }  // namespace
166 
167 NameConstraints::~NameConstraints() = default;
168 
169 // static
Create(const der::Input & extension_value,bool is_critical,CertErrors * errors)170 std::unique_ptr<NameConstraints> NameConstraints::Create(
171     const der::Input& extension_value,
172     bool is_critical,
173     CertErrors* errors) {
174   DCHECK(errors);
175 
176   auto name_constraints = std::make_unique<NameConstraints>();
177   if (!name_constraints->Parse(extension_value, is_critical, errors))
178     return nullptr;
179   return name_constraints;
180 }
181 
Parse(const der::Input & extension_value,bool is_critical,CertErrors * errors)182 bool NameConstraints::Parse(const der::Input& extension_value,
183                             bool is_critical,
184                             CertErrors* errors) {
185   DCHECK(errors);
186 
187   der::Parser extension_parser(extension_value);
188   der::Parser sequence_parser;
189 
190   // NameConstraints ::= SEQUENCE {
191   //      permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
192   //      excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }
193   if (!extension_parser.ReadSequence(&sequence_parser))
194     return false;
195   if (extension_parser.HasMore())
196     return false;
197 
198   absl::optional<der::Input> permitted_subtrees_value;
199   if (!sequence_parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
200                                        &permitted_subtrees_value)) {
201     return false;
202   }
203   if (permitted_subtrees_value &&
204       !ParseGeneralSubtrees(permitted_subtrees_value.value(),
205                             &permitted_subtrees_, errors)) {
206     return false;
207   }
208   constrained_name_types_ |=
209       permitted_subtrees_.present_name_types &
210       (is_critical ? GENERAL_NAME_ALL_TYPES : kSupportedNameTypes);
211 
212   absl::optional<der::Input> excluded_subtrees_value;
213   if (!sequence_parser.ReadOptionalTag(der::ContextSpecificConstructed(1),
214                                        &excluded_subtrees_value)) {
215     return false;
216   }
217   if (excluded_subtrees_value &&
218       !ParseGeneralSubtrees(excluded_subtrees_value.value(),
219                             &excluded_subtrees_, errors)) {
220     return false;
221   }
222   constrained_name_types_ |=
223       excluded_subtrees_.present_name_types &
224       (is_critical ? GENERAL_NAME_ALL_TYPES : kSupportedNameTypes);
225 
226   // RFC 5280 section 4.2.1.10:
227   // Conforming CAs MUST NOT issue certificates where name constraints is an
228   // empty sequence. That is, either the permittedSubtrees field or the
229   // excludedSubtrees MUST be present.
230   if (!permitted_subtrees_value && !excluded_subtrees_value)
231     return false;
232 
233   if (sequence_parser.HasMore())
234     return false;
235 
236   return true;
237 }
238 
IsPermittedCert(const der::Input & subject_rdn_sequence,const GeneralNames * subject_alt_names,CertErrors * errors) const239 void NameConstraints::IsPermittedCert(const der::Input& subject_rdn_sequence,
240                                       const GeneralNames* subject_alt_names,
241                                       CertErrors* errors) const {
242   // Checking NameConstraints is O(number_of_names * number_of_constraints).
243   // Impose a hard limit to mitigate the use of name constraints as a DoS
244   // mechanism.
245   const size_t kMaxChecks = 1048576;  // 1 << 20
246   base::ClampedNumeric<size_t> check_count = 0;
247 
248   if (subject_alt_names) {
249     check_count +=
250         base::ClampMul(subject_alt_names->dns_names.size(),
251                        base::ClampAdd(excluded_subtrees_.dns_names.size(),
252                                       permitted_subtrees_.dns_names.size()));
253     check_count += base::ClampMul(
254         subject_alt_names->directory_names.size(),
255         base::ClampAdd(excluded_subtrees_.directory_names.size(),
256                        permitted_subtrees_.directory_names.size()));
257     check_count += base::ClampMul(
258         subject_alt_names->ip_addresses.size(),
259         base::ClampAdd(excluded_subtrees_.ip_address_ranges.size(),
260                        permitted_subtrees_.ip_address_ranges.size()));
261   }
262 
263   if (!(subject_alt_names && subject_rdn_sequence.Length() == 0)) {
264     check_count += base::ClampAdd(excluded_subtrees_.directory_names.size(),
265                                   permitted_subtrees_.directory_names.size());
266   }
267 
268   if (check_count > kMaxChecks) {
269     errors->AddError(cert_errors::kTooManyNameConstraintChecks);
270     return;
271   }
272 
273   // Subject Alternative Name handling:
274   //
275   // RFC 5280 section 4.2.1.6:
276   // id-ce-subjectAltName OBJECT IDENTIFIER ::=  { id-ce 17 }
277   //
278   // SubjectAltName ::= GeneralNames
279   //
280   // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
281 
282   if (subject_alt_names) {
283     // Check unsupported name types:
284     // constrained_name_types() for the unsupported types will only be true if
285     // that type of name was present in a name constraint that was marked
286     // critical.
287     //
288     // RFC 5280 section 4.2.1.10:
289     // If a name constraints extension that is marked as critical
290     // imposes constraints on a particular name form, and an instance of
291     // that name form appears in the subject field or subjectAltName
292     // extension of a subsequent certificate, then the application MUST
293     // either process the constraint or reject the certificate.
294     if (constrained_name_types() & subject_alt_names->present_name_types &
295         ~kSupportedNameTypes) {
296       errors->AddError(cert_errors::kNotPermittedByNameConstraints);
297       return;
298     }
299 
300     // Check supported name types:
301     for (const auto& dns_name : subject_alt_names->dns_names) {
302       if (!IsPermittedDNSName(dns_name)) {
303         errors->AddError(cert_errors::kNotPermittedByNameConstraints);
304         return;
305       }
306     }
307 
308     for (const auto& directory_name : subject_alt_names->directory_names) {
309       if (!IsPermittedDirectoryName(directory_name)) {
310         errors->AddError(cert_errors::kNotPermittedByNameConstraints);
311         return;
312       }
313     }
314 
315     for (const auto& ip_address : subject_alt_names->ip_addresses) {
316       if (!IsPermittedIP(ip_address)) {
317         errors->AddError(cert_errors::kNotPermittedByNameConstraints);
318         return;
319       }
320     }
321   }
322 
323   // Subject handling:
324 
325   // RFC 5280 section 4.2.1.10:
326   // Legacy implementations exist where an electronic mail address is embedded
327   // in the subject distinguished name in an attribute of type emailAddress
328   // (Section 4.1.2.6). When constraints are imposed on the rfc822Name name
329   // form, but the certificate does not include a subject alternative name, the
330   // rfc822Name constraint MUST be applied to the attribute of type emailAddress
331   // in the subject distinguished name.
332   if (!subject_alt_names &&
333       (constrained_name_types() & GENERAL_NAME_RFC822_NAME)) {
334     bool contained_email_address = false;
335     if (!NameContainsEmailAddress(subject_rdn_sequence,
336                                   &contained_email_address)) {
337       errors->AddError(cert_errors::kNotPermittedByNameConstraints);
338       return;
339     }
340     if (contained_email_address) {
341       errors->AddError(cert_errors::kNotPermittedByNameConstraints);
342       return;
343     }
344   }
345 
346   // RFC 5280 4.1.2.6:
347   // If subject naming information is present only in the subjectAltName
348   // extension (e.g., a key bound only to an email address or URI), then the
349   // subject name MUST be an empty sequence and the subjectAltName extension
350   // MUST be critical.
351   // This code assumes that criticality condition is checked by the caller, and
352   // therefore only needs to avoid the IsPermittedDirectoryName check against an
353   // empty subject in such a case.
354   if (subject_alt_names && subject_rdn_sequence.Length() == 0)
355     return;
356 
357   if (!IsPermittedDirectoryName(subject_rdn_sequence)) {
358     errors->AddError(cert_errors::kNotPermittedByNameConstraints);
359     return;
360   }
361 }
362 
IsPermittedDNSName(std::string_view name) const363 bool NameConstraints::IsPermittedDNSName(std::string_view name) const {
364   for (const auto& excluded_name : excluded_subtrees_.dns_names) {
365     // When matching wildcard hosts against excluded subtrees, consider it a
366     // match if the constraint would match any expansion of the wildcard. Eg,
367     // *.bar.com should match a constraint of foo.bar.com.
368     if (DNSNameMatches(name, excluded_name, WILDCARD_PARTIAL_MATCH))
369       return false;
370   }
371 
372   // If permitted subtrees are not constrained, any name that is not excluded is
373   // allowed.
374   if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_DNS_NAME))
375     return true;
376 
377   for (const auto& permitted_name : permitted_subtrees_.dns_names) {
378     // When matching wildcard hosts against permitted subtrees, consider it a
379     // match only if the constraint would match all expansions of the wildcard.
380     // Eg, *.bar.com should match a constraint of bar.com, but not foo.bar.com.
381     if (DNSNameMatches(name, permitted_name, WILDCARD_FULL_MATCH))
382       return true;
383   }
384 
385   return false;
386 }
387 
IsPermittedDirectoryName(const der::Input & name_rdn_sequence) const388 bool NameConstraints::IsPermittedDirectoryName(
389     const der::Input& name_rdn_sequence) const {
390   for (const auto& excluded_name : excluded_subtrees_.directory_names) {
391     if (VerifyNameInSubtree(name_rdn_sequence, excluded_name))
392       return false;
393   }
394 
395   // If permitted subtrees are not constrained, any name that is not excluded is
396   // allowed.
397   if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_DIRECTORY_NAME))
398     return true;
399 
400   for (const auto& permitted_name : permitted_subtrees_.directory_names) {
401     if (VerifyNameInSubtree(name_rdn_sequence, permitted_name))
402       return true;
403   }
404 
405   return false;
406 }
407 
IsPermittedIP(const IPAddress & ip) const408 bool NameConstraints::IsPermittedIP(const IPAddress& ip) const {
409   for (const auto& excluded_ip : excluded_subtrees_.ip_address_ranges) {
410     if (IPAddressMatchesPrefix(ip, excluded_ip.first, excluded_ip.second))
411       return false;
412   }
413 
414   // If permitted subtrees are not constrained, any name that is not excluded is
415   // allowed.
416   if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_IP_ADDRESS))
417     return true;
418 
419   for (const auto& permitted_ip : permitted_subtrees_.ip_address_ranges) {
420     if (IPAddressMatchesPrefix(ip, permitted_ip.first, permitted_ip.second))
421       return true;
422   }
423 
424   return false;
425 }
426 
427 }  // namespace net
428