Coverage Report

Created: 2025-06-11 06:41

/src/boringssl/pki/name_constraints.cc
Line
Count
Source (jump to first uncovered line)
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.
67
bool DNSNameMatches(std::string_view name, std::string_view dns_constraint,
68
0
                    WildcardMatchType wildcard_matching) {
69
  // Everything matches the empty DNS name constraint.
70
0
  if (dns_constraint.empty()) {
71
0
    return true;
72
0
  }
73
74
  // Normalize absolute DNS names by removing the trailing dot, if any.
75
0
  if (!name.empty() && *name.rbegin() == '.') {
76
0
    name.remove_suffix(1);
77
0
  }
78
0
  if (!dns_constraint.empty() && *dns_constraint.rbegin() == '.') {
79
0
    dns_constraint.remove_suffix(1);
80
0
  }
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
0
  if (wildcard_matching == WILDCARD_PARTIAL_MATCH && name.size() > 2 &&
88
0
      name[0] == '*' && name[1] == '.') {
89
0
    size_t dns_constraint_dot_pos = dns_constraint.find('.');
90
0
    if (dns_constraint_dot_pos != std::string::npos) {
91
0
      std::string_view dns_constraint_domain =
92
0
          dns_constraint.substr(dns_constraint_dot_pos + 1);
93
0
      std::string_view wildcard_domain = name.substr(2);
94
0
      if (bssl::string_util::IsEqualNoCase(wildcard_domain,
95
0
                                           dns_constraint_domain)) {
96
0
        return true;
97
0
      }
98
0
    }
99
0
  }
100
101
0
  if (!bssl::string_util::EndsWithNoCase(name, dns_constraint)) {
102
0
    return false;
103
0
  }
104
105
  // Exact match.
106
0
  if (name.size() == dns_constraint.size()) {
107
0
    return true;
108
0
  }
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
0
  if (!dns_constraint.empty() && dns_constraint[0] == '.') {
113
0
    dns_constraint.remove_prefix(1);
114
0
  }
115
  // Subtree match.
116
0
  if (name.size() > dns_constraint.size() &&
117
0
      name[name.size() - dns_constraint.size() - 1] == '.') {
118
0
    return true;
119
0
  }
120
  // Trailing text matches, but not in a subtree (e.g., "foobar.com" is not a
121
  // match for "bar.com").
122
0
  return false;
123
0
}
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.
131
[[nodiscard]] bool ParseGeneralSubtrees(der::Input value,
132
                                        GeneralNames *subtrees,
133
110
                                        CertErrors *errors) {
134
110
  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
110
  der::Parser sequence_parser(value);
145
  // The GeneralSubtrees sequence should have at least 1 element.
146
110
  if (!sequence_parser.HasMore()) {
147
2
    return false;
148
2
  }
149
270
  while (sequence_parser.HasMore()) {
150
230
    der::Parser subtree_sequence;
151
230
    if (!sequence_parser.ReadSequence(&subtree_sequence)) {
152
6
      return false;
153
6
    }
154
155
224
    der::Input raw_general_name;
156
224
    if (!subtree_sequence.ReadRawTLV(&raw_general_name)) {
157
3
      return false;
158
3
    }
159
160
221
    if (!ParseGeneralName(raw_general_name,
161
221
                          GeneralNames::IP_ADDRESS_AND_NETMASK, subtrees,
162
221
                          errors)) {
163
49
      errors->AddError(kFailedParsingGeneralName);
164
49
      return false;
165
49
    }
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
172
    if (subtree_sequence.HasMore()) {
180
10
      return false;
181
10
    }
182
172
  }
183
40
  return true;
184
108
}
185
186
0
bool IsAlphaDigit(char c) {
187
0
  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
188
0
         (c >= 'A' && c <= 'Z');
189
0
}
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.
194
0
bool IsAllowedRfc822LocalPart(std::string_view local_part) {
195
0
  if (local_part.empty()) {
196
0
    return false;
197
0
  }
198
0
  for (char c : local_part) {
199
0
    if (!(IsAlphaDigit(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
200
0
          c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
201
0
          c == '/' || c == '=' || c == '?' || c == '^' || c == '_' ||
202
0
          c == '`' || c == '{' || c == '|' || c == '}' || c == '~' ||
203
0
          c == '.')) {
204
0
      return false;
205
0
    }
206
0
  }
207
0
  return true;
208
0
}
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.
215
0
bool IsAllowedRfc822Domain(std::string_view domain) {
216
0
  if (domain.empty()) {
217
0
    return false;
218
0
  }
219
0
  for (char c : domain) {
220
0
    if (!(IsAlphaDigit(c) || c == '-' || c == '.')) {
221
0
      return false;
222
0
    }
223
0
  }
224
0
  return true;
225
0
}
226
227
enum class Rfc822NameMatchType { kPermitted, kExcluded };
228
bool Rfc822NameMatches(std::string_view local_part, std::string_view domain,
229
                       std::string_view rfc822_constraint,
230
                       Rfc822NameMatchType match_type,
231
0
                       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
0
  const bool error_value =
235
0
      match_type == Rfc822NameMatchType::kPermitted ? false : true;
236
237
0
  std::vector<std::string_view> constraint_components =
238
0
      bssl::string_util::SplitString(rfc822_constraint, '@');
239
0
  std::string_view constraint_local_part;
240
0
  std::string_view constraint_domain;
241
0
  if (constraint_components.size() == 1) {
242
0
    constraint_domain = constraint_components[0];
243
0
  } else if (constraint_components.size() == 2) {
244
0
    constraint_local_part = constraint_components[0];
245
0
    if (!IsAllowedRfc822LocalPart(constraint_local_part)) {
246
0
      return error_value;
247
0
    }
248
0
    constraint_domain = constraint_components[1];
249
0
  } 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
0
    return error_value;
254
0
  }
255
0
  if (!IsAllowedRfc822Domain(constraint_domain)) {
256
0
    return error_value;
257
0
  }
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
0
  if (!constraint_local_part.empty()) {
264
0
    return (case_insensitive_local_part
265
0
                ? string_util::IsEqualNoCase(local_part, constraint_local_part)
266
0
                : local_part == constraint_local_part) &&
267
0
           string_util::IsEqualNoCase(domain, constraint_domain);
268
0
  }
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
0
  if (!constraint_domain.empty() && constraint_domain[0] == '.') {
276
0
    return string_util::EndsWithNoCase(domain, constraint_domain);
277
0
  }
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
0
  return string_util::IsEqualNoCase(domain, constraint_domain);
284
0
}
285
286
}  // namespace
287
288
102
NameConstraints::~NameConstraints() = default;
289
290
// static
291
std::unique_ptr<NameConstraints> NameConstraints::Create(
292
102
    der::Input extension_value, bool is_critical, CertErrors *errors) {
293
102
  BSSL_CHECK(errors);
294
295
102
  auto name_constraints = std::make_unique<NameConstraints>();
296
102
  if (!name_constraints->Parse(extension_value, is_critical, errors)) {
297
99
    return nullptr;
298
99
  }
299
3
  return name_constraints;
300
102
}
301
302
std::unique_ptr<NameConstraints> NameConstraints::CreateFromPermittedSubtrees(
303
0
    GeneralNames permitted_subtrees) {
304
0
  auto name_constraints = std::make_unique<NameConstraints>();
305
306
0
  name_constraints->constrained_name_types_ =
307
0
      permitted_subtrees.present_name_types;
308
0
  name_constraints->permitted_subtrees_ = std::move(permitted_subtrees);
309
310
0
  return name_constraints;
311
0
}
312
313
bool NameConstraints::Parse(der::Input extension_value, bool is_critical,
314
102
                            CertErrors *errors) {
315
102
  BSSL_CHECK(errors);
316
317
102
  der::Parser extension_parser(extension_value);
318
102
  der::Parser sequence_parser;
319
320
  // NameConstraints ::= SEQUENCE {
321
  //      permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
322
  //      excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }
323
102
  if (!extension_parser.ReadSequence(&sequence_parser)) {
324
5
    return false;
325
5
  }
326
97
  if (extension_parser.HasMore()) {
327
1
    return false;
328
1
  }
329
330
96
  std::optional<der::Input> permitted_subtrees_value;
331
96
  if (!sequence_parser.ReadOptionalTag(
332
96
          CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0,
333
96
          &permitted_subtrees_value)) {
334
1
    return false;
335
1
  }
336
95
  if (permitted_subtrees_value &&
337
95
      !ParseGeneralSubtrees(permitted_subtrees_value.value(),
338
56
                            &permitted_subtrees_, errors)) {
339
32
    return false;
340
32
  }
341
63
  constrained_name_types_ |=
342
63
      permitted_subtrees_.present_name_types &
343
63
      (is_critical ? GENERAL_NAME_ALL_TYPES : kSupportedNameTypes);
344
345
63
  std::optional<der::Input> excluded_subtrees_value;
346
63
  if (!sequence_parser.ReadOptionalTag(
347
63
          CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1,
348
63
          &excluded_subtrees_value)) {
349
3
    return false;
350
3
  }
351
60
  if (excluded_subtrees_value &&
352
60
      !ParseGeneralSubtrees(excluded_subtrees_value.value(),
353
54
                            &excluded_subtrees_, errors)) {
354
38
    return false;
355
38
  }
356
22
  constrained_name_types_ |=
357
22
      excluded_subtrees_.present_name_types &
358
22
      (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
22
  if (!permitted_subtrees_value && !excluded_subtrees_value) {
365
3
    return false;
366
3
  }
367
368
19
  if (sequence_parser.HasMore()) {
369
16
    return false;
370
16
  }
371
372
3
  return true;
373
19
}
374
375
void NameConstraints::IsPermittedCert(der::Input subject_rdn_sequence,
376
                                      const GeneralNames *subject_alt_names,
377
0
                                      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
0
  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
0
  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
0
  size_t constraint_count = 0;
392
0
  if (subject_alt_names) {
393
0
    name_count = subject_alt_names->rfc822_names.size() +
394
0
                 subject_alt_names->dns_names.size() +
395
0
                 subject_alt_names->directory_names.size() +
396
0
                 subject_alt_names->ip_addresses.size();
397
0
    constraint_count = excluded_subtrees_.rfc822_names.size() +
398
0
                       permitted_subtrees_.rfc822_names.size() +
399
0
                       excluded_subtrees_.dns_names.size() +
400
0
                       permitted_subtrees_.dns_names.size() +
401
0
                       excluded_subtrees_.directory_names.size() +
402
0
                       permitted_subtrees_.directory_names.size() +
403
0
                       excluded_subtrees_.ip_address_ranges.size() +
404
0
                       permitted_subtrees_.ip_address_ranges.size();
405
0
  } else {
406
0
    constraint_count += excluded_subtrees_.directory_names.size() +
407
0
                        permitted_subtrees_.directory_names.size();
408
0
    name_count = subject_rdn_sequence.size();
409
0
  }
410
  // Upper bound the number of possible checks, checking for overflow.
411
0
  size_t check_count = constraint_count * name_count;
412
0
  if ((constraint_count > 0 && check_count / constraint_count != name_count) ||
413
0
      check_count > kMaxChecks) {
414
0
    errors->AddError(cert_errors::kTooManyNameConstraintChecks);
415
0
    return;
416
0
  }
417
418
0
  std::vector<std::string> subject_email_addresses_to_check;
419
0
  if (!subject_alt_names &&
420
0
      (constrained_name_types() & GENERAL_NAME_RFC822_NAME)) {
421
0
    if (!FindEmailAddressesInName(subject_rdn_sequence,
422
0
                                  &subject_email_addresses_to_check)) {
423
      // Error parsing |subject_rdn_sequence|.
424
0
      errors->AddError(cert_errors::kNotPermittedByNameConstraints);
425
0
      return;
426
0
    }
427
0
  }
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
0
  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
0
    if (constrained_name_types() & subject_alt_names->present_name_types &
451
0
        ~kSupportedNameTypes) {
452
0
      errors->AddError(cert_errors::kNotPermittedByNameConstraints);
453
0
      return;
454
0
    }
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
0
    if (constrained_name_types() & GENERAL_NAME_RFC822_NAME) {
462
0
      for (const auto &rfc822_name : subject_alt_names->rfc822_names) {
463
0
        if (!IsPermittedRfc822Name(
464
0
                rfc822_name, /*case_insensitive_exclude_localpart=*/false)) {
465
0
          errors->AddError(cert_errors::kNotPermittedByNameConstraints);
466
0
          return;
467
0
        }
468
0
      }
469
0
    }
470
471
0
    for (const auto &dns_name : subject_alt_names->dns_names) {
472
0
      if (!IsPermittedDNSName(dns_name)) {
473
0
        errors->AddError(cert_errors::kNotPermittedByNameConstraints);
474
0
        return;
475
0
      }
476
0
    }
477
478
0
    for (const auto &directory_name : subject_alt_names->directory_names) {
479
0
      if (!IsPermittedDirectoryName(directory_name)) {
480
0
        errors->AddError(cert_errors::kNotPermittedByNameConstraints);
481
0
        return;
482
0
      }
483
0
    }
484
485
0
    for (const auto &ip_address : subject_alt_names->ip_addresses) {
486
0
      if (!IsPermittedIP(ip_address)) {
487
0
        errors->AddError(cert_errors::kNotPermittedByNameConstraints);
488
0
        return;
489
0
      }
490
0
    }
491
0
  }
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
0
  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
0
    if (!IsPermittedRfc822Name(rfc822_name,
515
0
                               /*case_insensitive_exclude_localpart=*/true)) {
516
0
      errors->AddError(cert_errors::kNotPermittedByNameConstraints);
517
0
      return;
518
0
    }
519
0
  }
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
0
  if (subject_alt_names && subject_rdn_sequence.empty()) {
530
0
    return;
531
0
  }
532
533
0
  if (!IsPermittedDirectoryName(subject_rdn_sequence)) {
534
0
    errors->AddError(cert_errors::kNotPermittedByNameConstraints);
535
0
    return;
536
0
  }
537
0
}
538
539
bool NameConstraints::IsPermittedRfc822Name(
540
0
    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
0
  std::vector<std::string_view> name_components =
608
0
      bssl::string_util::SplitString(name, '@');
609
0
  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
0
    return false;
614
0
  }
615
0
  if (!IsAllowedRfc822LocalPart(name_components[0]) ||
616
0
      !IsAllowedRfc822Domain(name_components[1])) {
617
0
    return false;
618
0
  }
619
620
0
  for (const auto &excluded_name : excluded_subtrees_.rfc822_names) {
621
0
    if (Rfc822NameMatches(name_components[0], name_components[1], excluded_name,
622
0
                          Rfc822NameMatchType::kExcluded,
623
0
                          case_insensitive_exclude_localpart)) {
624
0
      return false;
625
0
    }
626
0
  }
627
628
  // If permitted subtrees are not constrained, any name that is not excluded is
629
  // allowed.
630
0
  if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_RFC822_NAME)) {
631
0
    return true;
632
0
  }
633
634
0
  for (const auto &permitted_name : permitted_subtrees_.rfc822_names) {
635
0
    if (Rfc822NameMatches(name_components[0], name_components[1],
636
0
                          permitted_name, Rfc822NameMatchType::kPermitted,
637
0
                          /*case_insenitive_local_part=*/false)) {
638
0
      return true;
639
0
    }
640
0
  }
641
642
0
  return false;
643
0
}
644
645
0
bool NameConstraints::IsPermittedDNSName(std::string_view name) const {
646
0
  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
0
    if (DNSNameMatches(name, excluded_name, WILDCARD_PARTIAL_MATCH)) {
651
0
      return false;
652
0
    }
653
0
  }
654
655
  // If permitted subtrees are not constrained, any name that is not excluded is
656
  // allowed.
657
0
  if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_DNS_NAME)) {
658
0
    return true;
659
0
  }
660
661
0
  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
0
    if (DNSNameMatches(name, permitted_name, WILDCARD_FULL_MATCH)) {
666
0
      return true;
667
0
    }
668
0
  }
669
670
0
  return false;
671
0
}
672
673
bool NameConstraints::IsPermittedDirectoryName(
674
0
    der::Input name_rdn_sequence) const {
675
0
  for (const auto &excluded_name : excluded_subtrees_.directory_names) {
676
0
    if (VerifyNameInSubtree(name_rdn_sequence, excluded_name)) {
677
0
      return false;
678
0
    }
679
0
  }
680
681
  // If permitted subtrees are not constrained, any name that is not excluded is
682
  // allowed.
683
0
  if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_DIRECTORY_NAME)) {
684
0
    return true;
685
0
  }
686
687
0
  for (const auto &permitted_name : permitted_subtrees_.directory_names) {
688
0
    if (VerifyNameInSubtree(name_rdn_sequence, permitted_name)) {
689
0
      return true;
690
0
    }
691
0
  }
692
693
0
  return false;
694
0
}
695
696
0
bool NameConstraints::IsPermittedIP(der::Input ip) const {
697
0
  for (const auto &excluded_ip : excluded_subtrees_.ip_address_ranges) {
698
0
    if (IPAddressMatchesWithNetmask(ip, excluded_ip.first,
699
0
                                    excluded_ip.second)) {
700
0
      return false;
701
0
    }
702
0
  }
703
704
  // If permitted subtrees are not constrained, any name that is not excluded is
705
  // allowed.
706
0
  if (!(permitted_subtrees_.present_name_types & GENERAL_NAME_IP_ADDRESS)) {
707
0
    return true;
708
0
  }
709
710
0
  for (const auto &permitted_ip : permitted_subtrees_.ip_address_ranges) {
711
0
    if (IPAddressMatchesWithNetmask(ip, permitted_ip.first,
712
0
                                    permitted_ip.second)) {
713
0
      return true;
714
0
    }
715
0
  }
716
717
0
  return false;
718
0
}
719
720
BSSL_NAMESPACE_END