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