xref: /aosp_15_r20/external/cronet/third_party/boringssl/src/pki/name_constraints.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 "name_constraints.h"
6 
7 #include <limits.h>
8 
9 #include <memory>
10 #include <optional>
11 
12 #include <openssl/base.h>
13 #include <openssl/bytestring.h>
14 
15 #include "cert_errors.h"
16 #include "common_cert_errors.h"
17 #include "general_names.h"
18 #include "input.h"
19 #include "ip_util.h"
20 #include "parser.h"
21 #include "string_util.h"
22 #include "verify_name_match.h"
23 
24 namespace bssl {
25 
26 namespace {
27 
28 // The name types of GeneralName that are fully supported in name constraints.
29 //
30 // (The other types will have the minimal checking described by RFC 5280
31 // section 4.2.1.10: If a name constraints extension that is marked as critical
32 // imposes constraints on a particular name form, and an instance of
33 // that name form appears in the subject field or subjectAltName
34 // extension of a subsequent certificate, then the application MUST
35 // either process the constraint or reject the certificate.)
36 const int kSupportedNameTypes =
37     GENERAL_NAME_RFC822_NAME | GENERAL_NAME_DNS_NAME |
38     GENERAL_NAME_DIRECTORY_NAME | GENERAL_NAME_IP_ADDRESS;
39 
40 // Controls wildcard handling of DNSNameMatches.
41 // If WildcardMatchType is WILDCARD_PARTIAL_MATCH "*.bar.com" is considered to
42 // match the constraint "foo.bar.com". If it is WILDCARD_FULL_MATCH, "*.bar.com"
43 // will match "bar.com" but not "foo.bar.com".
44 enum WildcardMatchType { WILDCARD_PARTIAL_MATCH, WILDCARD_FULL_MATCH };
45 
46 // Returns true if |name| falls in the subtree defined by |dns_constraint|.
47 // RFC 5280 section 4.2.1.10:
48 // DNS name restrictions are expressed as host.example.com. Any DNS
49 // name that can be constructed by simply adding zero or more labels
50 // to the left-hand side of the name satisfies the name constraint. For
51 // example, www.host.example.com would satisfy the constraint but
52 // host1.example.com would not.
53 //
54 // |wildcard_matching| controls handling of wildcard names (|name| starts with
55 // "*."). Wildcard handling is not specified by RFC 5280, but certificate
56 // verification allows it, name constraints must check it similarly.
DNSNameMatches(std::string_view name,std::string_view dns_constraint,WildcardMatchType wildcard_matching)57 bool DNSNameMatches(std::string_view name, std::string_view dns_constraint,
58                     WildcardMatchType wildcard_matching) {
59   // Everything matches the empty DNS name constraint.
60   if (dns_constraint.empty()) {
61     return true;
62   }
63 
64   // Normalize absolute DNS names by removing the trailing dot, if any.
65   if (!name.empty() && *name.rbegin() == '.') {
66     name.remove_suffix(1);
67   }
68   if (!dns_constraint.empty() && *dns_constraint.rbegin() == '.') {
69     dns_constraint.remove_suffix(1);
70   }
71 
72   // Wildcard partial-match handling ("*.bar.com" matching name constraint
73   // "foo.bar.com"). This only handles the case where the the dnsname and the
74   // constraint match after removing the leftmost label, otherwise it is handled
75   // by falling through to the check of whether the dnsname is fully within or
76   // fully outside of the constraint.
77   if (wildcard_matching == WILDCARD_PARTIAL_MATCH && name.size() > 2 &&
78       name[0] == '*' && name[1] == '.') {
79     size_t dns_constraint_dot_pos = dns_constraint.find('.');
80     if (dns_constraint_dot_pos != std::string::npos) {
81       std::string_view dns_constraint_domain =
82           dns_constraint.substr(dns_constraint_dot_pos + 1);
83       std::string_view wildcard_domain = name.substr(2);
84       if (bssl::string_util::IsEqualNoCase(wildcard_domain,
85                                            dns_constraint_domain)) {
86         return true;
87       }
88     }
89   }
90 
91   if (!bssl::string_util::EndsWithNoCase(name, dns_constraint)) {
92     return false;
93   }
94 
95   // Exact match.
96   if (name.size() == dns_constraint.size()) {
97     return true;
98   }
99   // If dNSName constraint starts with a dot, only subdomains should match.
100   // (e.g., "foo.bar.com" matches constraint ".bar.com", but "bar.com" doesn't.)
101   // RFC 5280 is ambiguous, but this matches the behavior of other platforms.
102   if (!dns_constraint.empty() && dns_constraint[0] == '.') {
103     dns_constraint.remove_prefix(1);
104   }
105   // Subtree match.
106   if (name.size() > dns_constraint.size() &&
107       name[name.size() - dns_constraint.size() - 1] == '.') {
108     return true;
109   }
110   // Trailing text matches, but not in a subtree (e.g., "foobar.com" is not a
111   // match for "bar.com").
112   return false;
113 }
114 
115 // Parses a GeneralSubtrees |value| and store the contents in |subtrees|.
116 // The individual values stored into |subtrees| are not validated by this
117 // function.
118 // NOTE: |subtrees| is not pre-initialized by the function(it is expected to be
119 // a default initialized object), and it will be modified regardless of the
120 // return value.
ParseGeneralSubtrees(der::Input value,GeneralNames * subtrees,CertErrors * errors)121 [[nodiscard]] bool ParseGeneralSubtrees(der::Input value,
122                                         GeneralNames *subtrees,
123                                         CertErrors *errors) {
124   BSSL_CHECK(errors);
125 
126   // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
127   //
128   // GeneralSubtree ::= SEQUENCE {
129   //      base                    GeneralName,
130   //      minimum         [0]     BaseDistance DEFAULT 0,
131   //      maximum         [1]     BaseDistance OPTIONAL }
132   //
133   // BaseDistance ::= INTEGER (0..MAX)
134   der::Parser sequence_parser(value);
135   // The GeneralSubtrees sequence should have at least 1 element.
136   if (!sequence_parser.HasMore()) {
137     return false;
138   }
139   while (sequence_parser.HasMore()) {
140     der::Parser subtree_sequence;
141     if (!sequence_parser.ReadSequence(&subtree_sequence)) {
142       return false;
143     }
144 
145     der::Input raw_general_name;
146     if (!subtree_sequence.ReadRawTLV(&raw_general_name)) {
147       return false;
148     }
149 
150     if (!ParseGeneralName(raw_general_name,
151                           GeneralNames::IP_ADDRESS_AND_NETMASK, subtrees,
152                           errors)) {
153       errors->AddError(kFailedParsingGeneralName);
154       return false;
155     }
156 
157     // RFC 5280 section 4.2.1.10:
158     // Within this profile, the minimum and maximum fields are not used with any
159     // name forms, thus, the minimum MUST be zero, and maximum MUST be absent.
160     // However, if an application encounters a critical name constraints
161     // extension that specifies other values for minimum or maximum for a name
162     // form that appears in a subsequent certificate, the application MUST
163     // either process these fields or reject the certificate.
164 
165     // Note that technically failing here isn't required: rather only need to
166     // fail if a name of this type actually appears in a subsequent cert and
167     // this extension was marked critical. However the minimum and maximum
168     // fields appear uncommon enough that implementing that isn't useful.
169     if (subtree_sequence.HasMore()) {
170       return false;
171     }
172   }
173   return true;
174 }
175 
IsAlphaDigit(char c)176 bool IsAlphaDigit(char c) {
177   return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
178          (c >= 'A' && c <= 'Z');
179 }
180 
181 // Returns true if 'local_part' contains only characters that are valid in a
182 // non-quoted mailbox local-part. Does not check any other part of the syntax
183 // requirements. Does not allow whitespace.
IsAllowedRfc822LocalPart(std::string_view local_part)184 bool IsAllowedRfc822LocalPart(std::string_view local_part) {
185   if (local_part.empty()) {
186     return false;
187   }
188   for (char c : local_part) {
189     if (!(IsAlphaDigit(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
190           c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
191           c == '/' || c == '=' || c == '?' || c == '^' || c == '_' ||
192           c == '`' || c == '{' || c == '|' || c == '}' || c == '~' ||
193           c == '.')) {
194       return false;
195     }
196   }
197   return true;
198 }
199 
200 // Returns true if 'domain' contains only characters that are valid in a
201 // mailbox domain. Does not check any other part of the syntax
202 // requirements. Does not allow IPv6-address-literal as text IPv6 addresses are
203 // non-unique. Does not allow other address literals either as how to handle
204 // them with domain/subdomain matching isn't specified/possible.
IsAllowedRfc822Domain(std::string_view domain)205 bool IsAllowedRfc822Domain(std::string_view domain) {
206   if (domain.empty()) {
207     return false;
208   }
209   for (char c : domain) {
210     if (!(IsAlphaDigit(c) || c == '-' || c == '.')) {
211       return false;
212     }
213   }
214   return true;
215 }
216 
217 enum class Rfc822NameMatchType { kPermitted, kExcluded };
Rfc822NameMatches(std::string_view local_part,std::string_view domain,std::string_view rfc822_constraint,Rfc822NameMatchType match_type,bool case_insensitive_local_part)218 bool Rfc822NameMatches(std::string_view local_part, std::string_view domain,
219                        std::string_view rfc822_constraint,
220                        Rfc822NameMatchType match_type,
221                        bool case_insensitive_local_part) {
222   // In case of parsing errors, return a value that will cause the name to not
223   // be permitted.
224   const bool error_value =
225       match_type == Rfc822NameMatchType::kPermitted ? false : true;
226 
227   std::vector<std::string_view> constraint_components =
228       bssl::string_util::SplitString(rfc822_constraint, '@');
229   std::string_view constraint_local_part;
230   std::string_view constraint_domain;
231   if (constraint_components.size() == 1) {
232     constraint_domain = constraint_components[0];
233   } else if (constraint_components.size() == 2) {
234     constraint_local_part = constraint_components[0];
235     if (!IsAllowedRfc822LocalPart(constraint_local_part)) {
236       return error_value;
237     }
238     constraint_domain = constraint_components[1];
239   } else {
240     // If we did the full parsing then it is possible for a @ to be in a quoted
241     // local-part of the name, but we don't do that, so just error if @ appears
242     // more than once.
243     return error_value;
244   }
245   if (!IsAllowedRfc822Domain(constraint_domain)) {
246     return error_value;
247   }
248 
249   // RFC 5280 section 4.2.1.10:
250   // To indicate a particular mailbox, the constraint is the complete mail
251   // address.  For example, "[email protected]" indicates the root mailbox on
252   // the host "example.com".
253   if (!constraint_local_part.empty()) {
254     return (case_insensitive_local_part
255                 ? string_util::IsEqualNoCase(local_part, constraint_local_part)
256                 : local_part == constraint_local_part) &&
257            string_util::IsEqualNoCase(domain, constraint_domain);
258   }
259 
260   // RFC 5280 section 4.2.1.10:
261   // To specify any address within a domain, the constraint is specified with a
262   // leading period (as with URIs).  For example, ".example.com" indicates all
263   // the Internet mail addresses in the domain "example.com", but not Internet
264   // mail addresses on the host "example.com".
265   if (!constraint_domain.empty() && constraint_domain[0] == '.') {
266     return string_util::EndsWithNoCase(domain, constraint_domain);
267   }
268 
269   // RFC 5280 section 4.2.1.10:
270   // To indicate all Internet mail addresses on a particular host, the
271   // constraint is specified as the host name.  For example, the constraint
272   // "example.com" is satisfied by any mail address at the host "example.com".
273   return string_util::IsEqualNoCase(domain, constraint_domain);
274 }
275 
276 }  // namespace
277 
278 NameConstraints::~NameConstraints() = default;
279 
280 // static
Create(der::Input extension_value,bool is_critical,CertErrors * errors)281 std::unique_ptr<NameConstraints> NameConstraints::Create(
282     der::Input extension_value, bool is_critical, CertErrors *errors) {
283   BSSL_CHECK(errors);
284 
285   auto name_constraints = std::make_unique<NameConstraints>();
286   if (!name_constraints->Parse(extension_value, is_critical, errors)) {
287     return nullptr;
288   }
289   return name_constraints;
290 }
291 
CreateFromPermittedSubtrees(GeneralNames permitted_subtrees)292 std::unique_ptr<NameConstraints> NameConstraints::CreateFromPermittedSubtrees(
293     GeneralNames permitted_subtrees) {
294   auto name_constraints = std::make_unique<NameConstraints>();
295 
296   name_constraints->constrained_name_types_ =
297       permitted_subtrees.present_name_types;
298   name_constraints->permitted_subtrees_ = std::move(permitted_subtrees);
299 
300   return name_constraints;
301 }
302 
Parse(der::Input extension_value,bool is_critical,CertErrors * errors)303 bool NameConstraints::Parse(der::Input extension_value, bool is_critical,
304                             CertErrors *errors) {
305   BSSL_CHECK(errors);
306 
307   der::Parser extension_parser(extension_value);
308   der::Parser sequence_parser;
309 
310   // NameConstraints ::= SEQUENCE {
311   //      permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
312   //      excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }
313   if (!extension_parser.ReadSequence(&sequence_parser)) {
314     return false;
315   }
316   if (extension_parser.HasMore()) {
317     return false;
318   }
319 
320   std::optional<der::Input> permitted_subtrees_value;
321   if (!sequence_parser.ReadOptionalTag(
322           CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0,
323           &permitted_subtrees_value)) {
324     return false;
325   }
326   if (permitted_subtrees_value &&
327       !ParseGeneralSubtrees(permitted_subtrees_value.value(),
328                             &permitted_subtrees_, errors)) {
329     return false;
330   }
331   constrained_name_types_ |=
332       permitted_subtrees_.present_name_types &
333       (is_critical ? GENERAL_NAME_ALL_TYPES : kSupportedNameTypes);
334 
335   std::optional<der::Input> excluded_subtrees_value;
336   if (!sequence_parser.ReadOptionalTag(
337           CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1,
338           &excluded_subtrees_value)) {
339     return false;
340   }
341   if (excluded_subtrees_value &&
342       !ParseGeneralSubtrees(excluded_subtrees_value.value(),
343                             &excluded_subtrees_, errors)) {
344     return false;
345   }
346   constrained_name_types_ |=
347       excluded_subtrees_.present_name_types &
348       (is_critical ? GENERAL_NAME_ALL_TYPES : kSupportedNameTypes);
349 
350   // RFC 5280 section 4.2.1.10:
351   // Conforming CAs MUST NOT issue certificates where name constraints is an
352   // empty sequence. That is, either the permittedSubtrees field or the
353   // excludedSubtrees MUST be present.
354   if (!permitted_subtrees_value && !excluded_subtrees_value) {
355     return false;
356   }
357 
358   if (sequence_parser.HasMore()) {
359     return false;
360   }
361 
362   return true;
363 }
364 
IsPermittedCert(der::Input subject_rdn_sequence,const GeneralNames * subject_alt_names,CertErrors * errors) const365 void NameConstraints::IsPermittedCert(der::Input subject_rdn_sequence,
366                                       const GeneralNames *subject_alt_names,
367                                       CertErrors *errors) const {
368   // Checking NameConstraints is O(number_of_names * number_of_constraints).
369   // Impose a hard limit to mitigate the use of name constraints as a DoS
370   // mechanism. This mimics the similar check in BoringSSL x509/v_ncons.c
371   // TODO(bbe): make both name constraint mechanisms subquadratic and remove
372   // this check.
373 
374   const size_t kMaxChecks = 1048576;  // 1 << 20
375 
376   // Names all come from a certificate, which is bound by size_t, so adding them
377   // up can not overflow a size_t.
378   size_t name_count = 0;
379   // Constraints all come from a certificate, which is bound by a size_t, so
380   // adding them up can not overflow a size_t.
381   size_t constraint_count = 0;
382   if (subject_alt_names) {
383     name_count = subject_alt_names->rfc822_names.size() +
384                  subject_alt_names->dns_names.size() +
385                  subject_alt_names->directory_names.size() +
386                  subject_alt_names->ip_addresses.size();
387     constraint_count = excluded_subtrees_.rfc822_names.size() +
388                        permitted_subtrees_.rfc822_names.size() +
389                        excluded_subtrees_.dns_names.size() +
390                        permitted_subtrees_.dns_names.size() +
391                        excluded_subtrees_.directory_names.size() +
392                        permitted_subtrees_.directory_names.size() +
393                        excluded_subtrees_.ip_address_ranges.size() +
394                        permitted_subtrees_.ip_address_ranges.size();
395   } else {
396     constraint_count += excluded_subtrees_.directory_names.size() +
397                         permitted_subtrees_.directory_names.size();
398     name_count = subject_rdn_sequence.size();
399   }
400   // Upper bound the number of possible checks, checking for overflow.
401   size_t check_count = constraint_count * name_count;
402   if ((constraint_count > 0 && check_count / constraint_count != name_count) ||
403       check_count > kMaxChecks) {
404     errors->AddError(cert_errors::kTooManyNameConstraintChecks);
405     return;
406   }
407 
408   std::vector<std::string> subject_email_addresses_to_check;
409   if (!subject_alt_names &&
410       (constrained_name_types() & GENERAL_NAME_RFC822_NAME)) {
411     if (!FindEmailAddressesInName(subject_rdn_sequence,
412                                   &subject_email_addresses_to_check)) {
413       // Error parsing |subject_rdn_sequence|.
414       errors->AddError(cert_errors::kNotPermittedByNameConstraints);
415       return;
416     }
417   }
418 
419   // Subject Alternative Name handling:
420   //
421   // RFC 5280 section 4.2.1.6:
422   // id-ce-subjectAltName OBJECT IDENTIFIER ::=  { id-ce 17 }
423   //
424   // SubjectAltName ::= GeneralNames
425   //
426   // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
427 
428   if (subject_alt_names) {
429     // Check unsupported name types:
430     // constrained_name_types() for the unsupported types will only be true if
431     // that type of name was present in a name constraint that was marked
432     // critical.
433     //
434     // RFC 5280 section 4.2.1.10:
435     // If a name constraints extension that is marked as critical
436     // imposes constraints on a particular name form, and an instance of
437     // that name form appears in the subject field or subjectAltName
438     // extension of a subsequent certificate, then the application MUST
439     // either process the constraint or reject the certificate.
440     if (constrained_name_types() & subject_alt_names->present_name_types &
441         ~kSupportedNameTypes) {
442       errors->AddError(cert_errors::kNotPermittedByNameConstraints);
443       return;
444     }
445 
446     // Check supported name types:
447 
448     // Only check rfc822 SANs if any rfc822 constraints are present, since we
449     // might fail if there are email addresses we don't know how to parse but
450     // are technically correct.
451     if (constrained_name_types() & GENERAL_NAME_RFC822_NAME) {
452       for (const auto &rfc822_name : subject_alt_names->rfc822_names) {
453         if (!IsPermittedRfc822Name(
454                 rfc822_name, /*case_insensitive_exclude_localpart=*/false)) {
455           errors->AddError(cert_errors::kNotPermittedByNameConstraints);
456           return;
457         }
458       }
459     }
460 
461     for (const auto &dns_name : subject_alt_names->dns_names) {
462       if (!IsPermittedDNSName(dns_name)) {
463         errors->AddError(cert_errors::kNotPermittedByNameConstraints);
464         return;
465       }
466     }
467 
468     for (const auto &directory_name : subject_alt_names->directory_names) {
469       if (!IsPermittedDirectoryName(directory_name)) {
470         errors->AddError(cert_errors::kNotPermittedByNameConstraints);
471         return;
472       }
473     }
474 
475     for (const auto &ip_address : subject_alt_names->ip_addresses) {
476       if (!IsPermittedIP(ip_address)) {
477         errors->AddError(cert_errors::kNotPermittedByNameConstraints);
478         return;
479       }
480     }
481   }
482 
483   // Subject handling:
484 
485   // RFC 5280 section 4.2.1.10:
486   // Legacy implementations exist where an electronic mail address is embedded
487   // in the subject distinguished name in an attribute of type emailAddress
488   // (Section 4.1.2.6). When constraints are imposed on the rfc822Name name
489   // form, but the certificate does not include a subject alternative name, the
490   // rfc822Name constraint MUST be applied to the attribute of type emailAddress
491   // in the subject distinguished name.
492   for (const auto &rfc822_name : subject_email_addresses_to_check) {
493     // Whether local_part should be matched case-sensitive or not is somewhat
494     // unclear. RFC 2821 says that it should be case-sensitive. RFC 2985 says
495     // that emailAddress attributes in a Name are fully case-insensitive.
496     // Some other verifier implementations always do local-part comparison
497     // case-sensitive, while some always do it case-insensitive. Many but not
498     // all SMTP servers interpret addresses as case-insensitive.
499     //
500     // Give how poorly specified this is, and the conflicting implementations
501     // in the wild, this implementation will do case-insensitive match for
502     // excluded names from the subject to avoid potentially allowing
503     // something that wasn't expected.
504     if (!IsPermittedRfc822Name(rfc822_name,
505                                /*case_insensitive_exclude_localpart=*/true)) {
506       errors->AddError(cert_errors::kNotPermittedByNameConstraints);
507       return;
508     }
509   }
510 
511   // RFC 5280 4.1.2.6:
512   // If subject naming information is present only in the subjectAltName
513   // extension (e.g., a key bound only to an email address or URI), then the
514   // subject name MUST be an empty sequence and the subjectAltName extension
515   // MUST be critical.
516   // This code assumes that criticality condition is checked by the caller, and
517   // therefore only needs to avoid the IsPermittedDirectoryName check against an
518   // empty subject in such a case.
519   if (subject_alt_names && subject_rdn_sequence.empty()) {
520     return;
521   }
522 
523   if (!IsPermittedDirectoryName(subject_rdn_sequence)) {
524     errors->AddError(cert_errors::kNotPermittedByNameConstraints);
525     return;
526   }
527 }
528 
IsPermittedRfc822Name(std::string_view name,bool case_insensitive_exclude_localpart) const529 bool NameConstraints::IsPermittedRfc822Name(
530     std::string_view name, bool case_insensitive_exclude_localpart) const {
531   // RFC 5280 4.2.1.6.  Subject Alternative Name
532   //
533   // When the subjectAltName extension contains an Internet mail address,
534   // the address MUST be stored in the rfc822Name.  The format of an
535   // rfc822Name is a "Mailbox" as defined in Section 4.1.2 of [RFC2821].
536   // A Mailbox has the form "Local-part@Domain".  Note that a Mailbox has
537   // no phrase (such as a common name) before it, has no comment (text
538   // surrounded in parentheses) after it, and is not surrounded by "<" and
539   // ">".  Rules for encoding Internet mail addresses that include
540   // internationalized domain names are specified in Section 7.5.
541 
542   // Relevant parts from RFC 2821 & RFC 2822
543   //
544   // Mailbox = Local-part "@" Domain
545   // Local-part = Dot-string / Quoted-string
546   //       ; MAY be case-sensitive
547   //
548   // Dot-string = Atom *("." Atom)
549   // Atom = 1*atext
550   // Quoted-string = DQUOTE *qcontent DQUOTE
551   //
552   //
553   // atext           =       ALPHA / DIGIT / ; Any character except controls,
554   //                         "!" / "#" /     ;  SP, and specials.
555   //                         "$" / "%" /     ;  Used for atoms
556   //                         "&" / "'" /
557   //                         "*" / "+" /
558   //                         "-" / "/" /
559   //                         "=" / "?" /
560   //                         "^" / "_" /
561   //                         "`" / "{" /
562   //                         "|" / "}" /
563   //                         "~"
564   //
565   // atom            =       [CFWS] 1*atext [CFWS]
566   //
567   //
568   // qtext           =       NO-WS-CTL /     ; Non white space controls
569   //                         %d33 /          ; The rest of the US-ASCII
570   //                         %d35-91 /       ;  characters not including "\"
571   //                         %d93-126        ;  or the quote character
572   //
573   // quoted-pair     =       ("\" text) / obs-qp
574   // qcontent        =       qtext / quoted-pair
575   //
576   //
577   // Domain = (sub-domain 1*("." sub-domain)) / address-literal
578   // sub-domain = Let-dig [Ldh-str]
579   //
580   // Let-dig = ALPHA / DIGIT
581   // Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
582   //
583   // address-literal = "[" IPv4-address-literal /
584   //                       IPv6-address-literal /
585   //                       General-address-literal "]"
586   //       ; See section 4.1.3
587 
588   // However, no one actually implements all that. Known implementations just
589   // do string comparisons, but that is technically incorrect. (Ex: a
590   // constraint excluding |[email protected]| should exclude a SAN of
591   // |"foo"@example.com|, while a naive direct comparison will allow it.)
592   //
593   // We don't implement all that either, but do something a bit more fail-safe
594   // by rejecting any addresses that contain characters that are not allowed in
595   // the non-quoted formats.
596 
597   std::vector<std::string_view> name_components =
598       bssl::string_util::SplitString(name, '@');
599   if (name_components.size() != 2) {
600     // If we did the full parsing then it is possible for a @ to be in a quoted
601     // local-part of the name, but we don't do that, so just fail if @ appears
602     // more than once.
603     return false;
604   }
605   if (!IsAllowedRfc822LocalPart(name_components[0]) ||
606       !IsAllowedRfc822Domain(name_components[1])) {
607     return false;
608   }
609 
610   for (const auto &excluded_name : excluded_subtrees_.rfc822_names) {
611     if (Rfc822NameMatches(name_components[0], name_components[1], excluded_name,
612                           Rfc822NameMatchType::kExcluded,
613                           case_insensitive_exclude_localpart)) {
614       return false;
615     }
616   }
617 
618   // If permitted subtrees are not constrained, any name that is not excluded is
619   // allowed.
620   if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_RFC822_NAME)) {
621     return true;
622   }
623 
624   for (const auto &permitted_name : permitted_subtrees_.rfc822_names) {
625     if (Rfc822NameMatches(name_components[0], name_components[1],
626                           permitted_name, Rfc822NameMatchType::kPermitted,
627                           /*case_insenitive_local_part=*/false)) {
628       return true;
629     }
630   }
631 
632   return false;
633 }
634 
IsPermittedDNSName(std::string_view name) const635 bool NameConstraints::IsPermittedDNSName(std::string_view name) const {
636   for (const auto &excluded_name : excluded_subtrees_.dns_names) {
637     // When matching wildcard hosts against excluded subtrees, consider it a
638     // match if the constraint would match any expansion of the wildcard. Eg,
639     // *.bar.com should match a constraint of foo.bar.com.
640     if (DNSNameMatches(name, excluded_name, WILDCARD_PARTIAL_MATCH)) {
641       return false;
642     }
643   }
644 
645   // If permitted subtrees are not constrained, any name that is not excluded is
646   // allowed.
647   if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_DNS_NAME)) {
648     return true;
649   }
650 
651   for (const auto &permitted_name : permitted_subtrees_.dns_names) {
652     // When matching wildcard hosts against permitted subtrees, consider it a
653     // match only if the constraint would match all expansions of the wildcard.
654     // Eg, *.bar.com should match a constraint of bar.com, but not foo.bar.com.
655     if (DNSNameMatches(name, permitted_name, WILDCARD_FULL_MATCH)) {
656       return true;
657     }
658   }
659 
660   return false;
661 }
662 
IsPermittedDirectoryName(der::Input name_rdn_sequence) const663 bool NameConstraints::IsPermittedDirectoryName(
664     der::Input name_rdn_sequence) const {
665   for (const auto &excluded_name : excluded_subtrees_.directory_names) {
666     if (VerifyNameInSubtree(name_rdn_sequence, excluded_name)) {
667       return false;
668     }
669   }
670 
671   // If permitted subtrees are not constrained, any name that is not excluded is
672   // allowed.
673   if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_DIRECTORY_NAME)) {
674     return true;
675   }
676 
677   for (const auto &permitted_name : permitted_subtrees_.directory_names) {
678     if (VerifyNameInSubtree(name_rdn_sequence, permitted_name)) {
679       return true;
680     }
681   }
682 
683   return false;
684 }
685 
IsPermittedIP(der::Input ip) const686 bool NameConstraints::IsPermittedIP(der::Input ip) const {
687   for (const auto &excluded_ip : excluded_subtrees_.ip_address_ranges) {
688     if (IPAddressMatchesWithNetmask(ip, excluded_ip.first,
689                                     excluded_ip.second)) {
690       return false;
691     }
692   }
693 
694   // If permitted subtrees are not constrained, any name that is not excluded is
695   // allowed.
696   if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_IP_ADDRESS)) {
697     return true;
698   }
699 
700   for (const auto &permitted_ip : permitted_subtrees_.ip_address_ranges) {
701     if (IPAddressMatchesWithNetmask(ip, permitted_ip.first,
702                                     permitted_ip.second)) {
703       return true;
704     }
705   }
706 
707   return false;
708 }
709 
710 }  // namespace bssl
711