1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This code is made available to you under your choice of the following sets
4  * of licensing terms:
5  */
6 /* This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9  */
10 /* Copyright 2014 Mozilla Contributors
11  *
12  * Licensed under the Apache License, Version 2.0 (the "License");
13  * you may not use this file except in compliance with the License.
14  * You may obtain a copy of the License at
15  *
16  *     http://www.apache.org/licenses/LICENSE-2.0
17  *
18  * Unless required by applicable law or agreed to in writing, software
19  * distributed under the License is distributed on an "AS IS" BASIS,
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  * See the License for the specific language governing permissions and
22  * limitations under the License.
23  */
24 
25 // This code implements RFC6125-ish name matching, RFC5280-ish name constraint
26 // checking, and related things.
27 //
28 // In this code, identifiers are classified as either "presented" or
29 // "reference" identifiers are defined in
30 // http://tools.ietf.org/html/rfc6125#section-1.8. A "presented identifier" is
31 // one in the subjectAltName of the certificate, or sometimes within a CN of
32 // the certificate's subject. The "reference identifier" is the one we are
33 // being asked to match the certificate against. When checking name
34 // constraints, the reference identifier is the entire encoded name constraint
35 // extension value.
36 
37 #include <algorithm>
38 
39 #include "pkixcheck.h"
40 #include "pkixutil.h"
41 
42 namespace mozilla {
43 namespace pkix {
44 
45 namespace {
46 
47 // GeneralName ::= CHOICE {
48 //      otherName                       [0]     OtherName,
49 //      rfc822Name                      [1]     IA5String,
50 //      dNSName                         [2]     IA5String,
51 //      x400Address                     [3]     ORAddress,
52 //      directoryName                   [4]     Name,
53 //      ediPartyName                    [5]     EDIPartyName,
54 //      uniformResourceIdentifier       [6]     IA5String,
55 //      iPAddress                       [7]     OCTET STRING,
56 //      registeredID                    [8]     OBJECT IDENTIFIER }
57 enum class GeneralNameType : uint8_t {
58   // Note that these values are NOT contiguous. Some values have the
59   // der::CONSTRUCTED bit set while others do not.
60   // (The der::CONSTRUCTED bit is for types where the value is a SEQUENCE.)
61   otherName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
62   rfc822Name = der::CONTEXT_SPECIFIC | 1,
63   dNSName = der::CONTEXT_SPECIFIC | 2,
64   x400Address = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3,
65   directoryName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 4,
66   ediPartyName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 5,
67   uniformResourceIdentifier = der::CONTEXT_SPECIFIC | 6,
68   iPAddress = der::CONTEXT_SPECIFIC | 7,
69   registeredID = der::CONTEXT_SPECIFIC | 8,
70   // nameConstraints is a pseudo-GeneralName used to signify that a
71   // reference ID is actually the entire name constraint extension.
72   nameConstraints = 0xff
73 };
74 
ReadGeneralName(Reader & reader,GeneralNameType & generalNameType,Input & value)75 inline Result ReadGeneralName(Reader& reader,
76                               /*out*/ GeneralNameType& generalNameType,
77                               /*out*/ Input& value) {
78   uint8_t tag;
79   Result rv = der::ReadTagAndGetValue(reader, tag, value);
80   if (rv != Success) {
81     return rv;
82   }
83   switch (tag) {
84     case static_cast<uint8_t>(GeneralNameType::otherName):
85       generalNameType = GeneralNameType::otherName;
86       break;
87     case static_cast<uint8_t>(GeneralNameType::rfc822Name):
88       generalNameType = GeneralNameType::rfc822Name;
89       break;
90     case static_cast<uint8_t>(GeneralNameType::dNSName):
91       generalNameType = GeneralNameType::dNSName;
92       break;
93     case static_cast<uint8_t>(GeneralNameType::x400Address):
94       generalNameType = GeneralNameType::x400Address;
95       break;
96     case static_cast<uint8_t>(GeneralNameType::directoryName):
97       generalNameType = GeneralNameType::directoryName;
98       break;
99     case static_cast<uint8_t>(GeneralNameType::ediPartyName):
100       generalNameType = GeneralNameType::ediPartyName;
101       break;
102     case static_cast<uint8_t>(GeneralNameType::uniformResourceIdentifier):
103       generalNameType = GeneralNameType::uniformResourceIdentifier;
104       break;
105     case static_cast<uint8_t>(GeneralNameType::iPAddress):
106       generalNameType = GeneralNameType::iPAddress;
107       break;
108     case static_cast<uint8_t>(GeneralNameType::registeredID):
109       generalNameType = GeneralNameType::registeredID;
110       break;
111     default:
112       return Result::ERROR_BAD_DER;
113   }
114   return Success;
115 }
116 
117 enum class MatchResult { NoNamesOfGivenType = 0, Mismatch = 1, Match = 2 };
118 
119 Result SearchNames(const Input* subjectAltName, Input subject,
120                    GeneralNameType referenceIDType, Input referenceID,
121                    FallBackToSearchWithinSubject fallBackToCommonName,
122                    /*out*/ MatchResult& match);
123 Result SearchWithinRDN(Reader& rdn, GeneralNameType referenceIDType,
124                        Input referenceID,
125                        FallBackToSearchWithinSubject fallBackToEmailAddress,
126                        FallBackToSearchWithinSubject fallBackToCommonName,
127                        /*in/out*/ MatchResult& match);
128 Result MatchAVA(Input type, uint8_t valueEncodingTag, Input presentedID,
129                 GeneralNameType referenceIDType, Input referenceID,
130                 FallBackToSearchWithinSubject fallBackToEmailAddress,
131                 FallBackToSearchWithinSubject fallBackToCommonName,
132                 /*in/out*/ MatchResult& match);
133 Result ReadAVA(Reader& rdn,
134                /*out*/ Input& type,
135                /*out*/ uint8_t& valueTag,
136                /*out*/ Input& value);
137 void MatchSubjectPresentedIDWithReferenceID(GeneralNameType presentedIDType,
138                                             Input presentedID,
139                                             GeneralNameType referenceIDType,
140                                             Input referenceID,
141                                             /*in/out*/ MatchResult& match);
142 
143 Result MatchPresentedIDWithReferenceID(GeneralNameType presentedIDType,
144                                        Input presentedID,
145                                        GeneralNameType referenceIDType,
146                                        Input referenceID,
147                                        /*in/out*/ MatchResult& matchResult);
148 Result CheckPresentedIDConformsToConstraints(GeneralNameType referenceIDType,
149                                              Input presentedID,
150                                              Input nameConstraints);
151 
152 uint8_t LocaleInsensitveToLower(uint8_t a);
153 bool StartsWithIDNALabel(Input id);
154 
155 enum class IDRole {
156   ReferenceID = 0,
157   PresentedID = 1,
158   NameConstraint = 2,
159 };
160 
161 enum class AllowWildcards { No = 0, Yes = 1 };
162 
163 // DNSName constraints implicitly allow subdomain matching when there is no
164 // leading dot ("foo.example.com" matches a constraint of "example.com"), but
165 // RFC822Name constraints only allow subdomain matching when there is a leading
166 // dot ("foo.example.com" does not match "example.com" but does match
167 // ".example.com").
168 enum class AllowDotlessSubdomainMatches { No = 0, Yes = 1 };
169 
170 bool IsValidDNSID(Input hostname, IDRole idRole, AllowWildcards allowWildcards);
171 
172 Result MatchPresentedDNSIDWithReferenceDNSID(
173     Input presentedDNSID, AllowWildcards allowWildcards,
174     AllowDotlessSubdomainMatches allowDotlessSubdomainMatches,
175     IDRole referenceDNSIDRole, Input referenceDNSID,
176     /*out*/ bool& matches);
177 
178 Result MatchPresentedRFC822NameWithReferenceRFC822Name(
179     Input presentedRFC822Name, IDRole referenceRFC822NameRole,
180     Input referenceRFC822Name, /*out*/ bool& matches);
181 
182 }  // namespace
183 
184 bool IsValidReferenceDNSID(Input hostname);
185 bool IsValidPresentedDNSID(Input hostname);
186 bool ParseIPv4Address(Input hostname, /*out*/ uint8_t (&out)[4]);
187 bool ParseIPv6Address(Input hostname, /*out*/ uint8_t (&out)[16]);
188 
189 // This is used by the pkixnames_tests.cpp tests.
MatchPresentedDNSIDWithReferenceDNSID(Input presentedDNSID,Input referenceDNSID,bool & matches)190 Result MatchPresentedDNSIDWithReferenceDNSID(Input presentedDNSID,
191                                              Input referenceDNSID,
192                                              /*out*/ bool& matches) {
193   return MatchPresentedDNSIDWithReferenceDNSID(
194       presentedDNSID, AllowWildcards::Yes, AllowDotlessSubdomainMatches::Yes,
195       IDRole::ReferenceID, referenceDNSID, matches);
196 }
197 
198 // Verify that the given end-entity cert, which is assumed to have been already
199 // validated with BuildCertChain, is valid for the given hostname. hostname is
200 // assumed to be a string representation of an IPv4 address, an IPv6 addresss,
201 // or a normalized ASCII (possibly punycode) DNS name.
CheckCertHostname(Input endEntityCertDER,Input hostname,NameMatchingPolicy & nameMatchingPolicy)202 Result CheckCertHostname(Input endEntityCertDER, Input hostname,
203                          NameMatchingPolicy& nameMatchingPolicy) {
204   BackCert cert(endEntityCertDER, EndEntityOrCA::MustBeEndEntity, nullptr);
205   Result rv = cert.Init();
206   if (rv != Success) {
207     return rv;
208   }
209 
210   Time notBefore(Time::uninitialized);
211   rv = ParseValidity(cert.GetValidity(), &notBefore);
212   if (rv != Success) {
213     return rv;
214   }
215   FallBackToSearchWithinSubject fallBackToSearchWithinSubject;
216   rv = nameMatchingPolicy.FallBackToCommonName(notBefore,
217                                                fallBackToSearchWithinSubject);
218   if (rv != Success) {
219     return rv;
220   }
221 
222   const Input* subjectAltName(cert.GetSubjectAltName());
223   Input subject(cert.GetSubject());
224 
225   // For backward compatibility with legacy certificates, we may fall back to
226   // searching for a name match in the subject common name for DNS names and
227   // IPv4 addresses. We don't do so for IPv6 addresses because we do not think
228   // there are many certificates that would need such fallback, and because
229   // comparisons of string representations of IPv6 addresses are particularly
230   // error prone due to the syntactic flexibility that IPv6 addresses have.
231   //
232   // IPv4 and IPv6 addresses are represented using the same type of GeneralName
233   // (iPAddress); they are differentiated by the lengths of the values.
234   MatchResult match;
235   uint8_t ipv6[16];
236   uint8_t ipv4[4];
237   if (IsValidReferenceDNSID(hostname)) {
238     rv = SearchNames(subjectAltName, subject, GeneralNameType::dNSName,
239                      hostname, fallBackToSearchWithinSubject, match);
240   } else if (ParseIPv6Address(hostname, ipv6)) {
241     rv = SearchNames(subjectAltName, subject, GeneralNameType::iPAddress,
242                      Input(ipv6), FallBackToSearchWithinSubject::No, match);
243   } else if (ParseIPv4Address(hostname, ipv4)) {
244     rv = SearchNames(subjectAltName, subject, GeneralNameType::iPAddress,
245                      Input(ipv4), fallBackToSearchWithinSubject, match);
246   } else {
247     return Result::ERROR_BAD_CERT_DOMAIN;
248   }
249   if (rv != Success) {
250     return rv;
251   }
252   switch (match) {
253     case MatchResult::NoNamesOfGivenType:  // fall through
254     case MatchResult::Mismatch:
255       return Result::ERROR_BAD_CERT_DOMAIN;
256     case MatchResult::Match:
257       return Success;
258       MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
259   }
260 }
261 
262 // 4.2.1.10. Name Constraints
CheckNameConstraints(Input encodedNameConstraints,const BackCert & firstChild,KeyPurposeId requiredEKUIfPresent)263 Result CheckNameConstraints(Input encodedNameConstraints,
264                             const BackCert& firstChild,
265                             KeyPurposeId requiredEKUIfPresent) {
266   for (const BackCert* child = &firstChild; child; child = child->childCert) {
267     FallBackToSearchWithinSubject fallBackToCommonName =
268         (child->endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
269          requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth)
270             ? FallBackToSearchWithinSubject::Yes
271             : FallBackToSearchWithinSubject::No;
272 
273     MatchResult match;
274     Result rv =
275         SearchNames(child->GetSubjectAltName(), child->GetSubject(),
276                     GeneralNameType::nameConstraints, encodedNameConstraints,
277                     fallBackToCommonName, match);
278     if (rv != Success) {
279       return rv;
280     }
281     switch (match) {
282       case MatchResult::Match:  // fall through
283       case MatchResult::NoNamesOfGivenType:
284         break;
285       case MatchResult::Mismatch:
286         return Result::ERROR_CERT_NOT_IN_NAME_SPACE;
287     }
288   }
289 
290   return Success;
291 }
292 
293 namespace {
294 
295 // SearchNames is used by CheckCertHostname and CheckNameConstraints.
296 //
297 // When called during name constraint checking, referenceIDType is
298 // GeneralNameType::nameConstraints and referenceID is the entire encoded name
299 // constraints extension value.
300 //
301 // The main benefit of using the exact same code paths for both is that we
302 // ensure consistency between name validation and name constraint enforcement
303 // regarding thing like "Which CN attributes should be considered as potential
304 // CN-IDs" and "Which character sets are acceptable for CN-IDs?" If the name
305 // matching and the name constraint enforcement logic were out of sync on these
306 // issues (e.g. if name matching were to consider all subject CN attributes,
307 // but name constraints were only enforced on the most specific subject CN),
308 // trivial name constraint bypasses could result.
309 
SearchNames(const Input * subjectAltName,Input subject,GeneralNameType referenceIDType,Input referenceID,FallBackToSearchWithinSubject fallBackToCommonName,MatchResult & match)310 Result SearchNames(/*optional*/ const Input* subjectAltName, Input subject,
311                    GeneralNameType referenceIDType, Input referenceID,
312                    FallBackToSearchWithinSubject fallBackToCommonName,
313                    /*out*/ MatchResult& match) {
314   Result rv;
315 
316   match = MatchResult::NoNamesOfGivenType;
317 
318   // RFC 6125 says "A client MUST NOT seek a match for a reference identifier
319   // of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, URI-ID, or
320   // any application-specific identifier types supported by the client."
321   // Accordingly, we only consider CN-IDs if there are no DNS-IDs in the
322   // subjectAltName.
323   //
324   // RFC 6125 says that IP addresses are out of scope, but for backward
325   // compatibility we accept them, by considering IP addresses to be an
326   // "application-specific identifier type supported by the client."
327   //
328   // TODO(bug XXXXXXX): Consider strengthening this check to "A client MUST NOT
329   // seek a match for a reference identifier of CN-ID if the certificate
330   // contains a subjectAltName extension."
331   //
332   // TODO(bug XXXXXXX): Consider dropping support for IP addresses as
333   // identifiers completely.
334 
335   if (subjectAltName) {
336     Reader altNames;
337     rv = der::ExpectTagAndGetValueAtEnd(*subjectAltName, der::SEQUENCE,
338                                         altNames);
339     if (rv != Success) {
340       return rv;
341     }
342 
343     // According to RFC 5280, "If the subjectAltName extension is present, the
344     // sequence MUST contain at least one entry." For compatibility reasons, we
345     // do not enforce this. See bug 1143085.
346     while (!altNames.AtEnd()) {
347       GeneralNameType presentedIDType;
348       Input presentedID;
349       rv = ReadGeneralName(altNames, presentedIDType, presentedID);
350       if (rv != Success) {
351         return rv;
352       }
353 
354       rv = MatchPresentedIDWithReferenceID(presentedIDType, presentedID,
355                                            referenceIDType, referenceID, match);
356       if (rv != Success) {
357         return rv;
358       }
359       if (referenceIDType != GeneralNameType::nameConstraints &&
360           match == MatchResult::Match) {
361         return Success;
362       }
363       if (presentedIDType == GeneralNameType::dNSName ||
364           presentedIDType == GeneralNameType::iPAddress) {
365         fallBackToCommonName = FallBackToSearchWithinSubject::No;
366       }
367     }
368   }
369 
370   if (referenceIDType == GeneralNameType::nameConstraints) {
371     rv = CheckPresentedIDConformsToConstraints(GeneralNameType::directoryName,
372                                                subject, referenceID);
373     if (rv != Success) {
374       return rv;
375     }
376   }
377 
378   FallBackToSearchWithinSubject fallBackToEmailAddress;
379   if (!subjectAltName &&
380       (referenceIDType == GeneralNameType::rfc822Name ||
381        referenceIDType == GeneralNameType::nameConstraints)) {
382     fallBackToEmailAddress = FallBackToSearchWithinSubject::Yes;
383   } else {
384     fallBackToEmailAddress = FallBackToSearchWithinSubject::No;
385   }
386 
387   // Short-circuit the parsing of the subject name if we're not going to match
388   // any names in it
389   if (fallBackToEmailAddress == FallBackToSearchWithinSubject::No &&
390       fallBackToCommonName == FallBackToSearchWithinSubject::No) {
391     return Success;
392   }
393 
394   // Attempt to match the reference ID against the CN-ID, which we consider to
395   // be the most-specific CN AVA in the subject field.
396   //
397   // https://tools.ietf.org/html/rfc6125#section-2.3.1 says:
398   //
399   //   To reduce confusion, in this specification we avoid such terms and
400   //   instead use the terms provided under Section 1.8; in particular, we
401   //   do not use the term "(most specific) Common Name field in the subject
402   //   field" from [HTTP-TLS] and instead state that a CN-ID is a Relative
403   //   Distinguished Name (RDN) in the certificate subject containing one
404   //   and only one attribute-type-and-value pair of type Common Name (thus
405   //   removing the possibility that an RDN might contain multiple AVAs
406   //   (Attribute Value Assertions) of type CN, one of which could be
407   //   considered "most specific").
408   //
409   // https://tools.ietf.org/html/rfc6125#section-7.4 says:
410   //
411   //   [...] Although it would be preferable to
412   //   forbid multiple CN-IDs entirely, there are several reasons at this
413   //   time why this specification states that they SHOULD NOT (instead of
414   //   MUST NOT) be included [...]
415   //
416   // Consequently, it is unclear what to do when there are multiple CNs in the
417   // subject, regardless of whether there "SHOULD NOT" be.
418   //
419   // NSS's CERT_VerifyCertName mostly follows RFC2818 in this instance, which
420   // says:
421   //
422   //   If a subjectAltName extension of type dNSName is present, that MUST
423   //   be used as the identity. Otherwise, the (most specific) Common Name
424   //   field in the Subject field of the certificate MUST be used.
425   //
426   //   [...]
427   //
428   //   In some cases, the URI is specified as an IP address rather than a
429   //   hostname. In this case, the iPAddress subjectAltName must be present
430   //   in the certificate and must exactly match the IP in the URI.
431   //
432   // (The main difference from RFC2818 is that NSS's CERT_VerifyCertName also
433   // matches IP addresses in the most-specific CN.)
434   //
435   // NSS's CERT_VerifyCertName finds the most specific CN via
436   // CERT_GetCommoName, which uses CERT_GetLastNameElement. Note that many
437   // NSS-based applications, including Gecko, also use CERT_GetCommonName. It
438   // is likely that other, non-NSS-based, applications also expect only the
439   // most specific CN to be matched against the reference ID.
440   //
441   // "A Layman's Guide to a Subset of ASN.1, BER, and DER" and other sources
442   // agree that an RDNSequence is ordered from most significant (least
443   // specific) to least significant (most specific), as do other references.
444   //
445   // However, Chromium appears to use the least-specific (first) CN instead of
446   // the most-specific; see https://crbug.com/366957. Also, MSIE and some other
447   // popular implementations apparently attempt to match the reference ID
448   // against any/all CNs in the subject. Since we're trying to phase out the
449   // use of CN-IDs, we intentionally avoid trying to match MSIE's more liberal
450   // behavior.
451 
452   // Name ::= CHOICE { -- only one possibility for now --
453   //   rdnSequence  RDNSequence }
454   //
455   // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
456   //
457   // RelativeDistinguishedName ::=
458   //   SET SIZE (1..MAX) OF AttributeTypeAndValue
459   Reader subjectReader(subject);
460   return der::NestedOf(subjectReader, der::SEQUENCE, der::SET,
461                        der::EmptyAllowed::Yes, [&](Reader& r) {
462                          return SearchWithinRDN(r, referenceIDType, referenceID,
463                                                 fallBackToEmailAddress,
464                                                 fallBackToCommonName, match);
465                        });
466 }
467 
468 // RelativeDistinguishedName ::=
469 //   SET SIZE (1..MAX) OF AttributeTypeAndValue
470 //
471 // AttributeTypeAndValue ::= SEQUENCE {
472 //   type     AttributeType,
473 //   value    AttributeValue }
SearchWithinRDN(Reader & rdn,GeneralNameType referenceIDType,Input referenceID,FallBackToSearchWithinSubject fallBackToEmailAddress,FallBackToSearchWithinSubject fallBackToCommonName,MatchResult & match)474 Result SearchWithinRDN(Reader& rdn, GeneralNameType referenceIDType,
475                        Input referenceID,
476                        FallBackToSearchWithinSubject fallBackToEmailAddress,
477                        FallBackToSearchWithinSubject fallBackToCommonName,
478                        /*in/out*/ MatchResult& match) {
479   do {
480     Input type;
481     uint8_t valueTag;
482     Input value;
483     Result rv = ReadAVA(rdn, type, valueTag, value);
484     if (rv != Success) {
485       return rv;
486     }
487     rv = MatchAVA(type, valueTag, value, referenceIDType, referenceID,
488                   fallBackToEmailAddress, fallBackToCommonName, match);
489     if (rv != Success) {
490       return rv;
491     }
492   } while (!rdn.AtEnd());
493 
494   return Success;
495 }
496 
497 // AttributeTypeAndValue ::= SEQUENCE {
498 //   type     AttributeType,
499 //   value    AttributeValue }
500 //
501 // AttributeType ::= OBJECT IDENTIFIER
502 //
503 // AttributeValue ::= ANY -- DEFINED BY AttributeType
504 //
505 // DirectoryString ::= CHOICE {
506 //       teletexString           TeletexString (SIZE (1..MAX)),
507 //       printableString         PrintableString (SIZE (1..MAX)),
508 //       universalString         UniversalString (SIZE (1..MAX)),
509 //       utf8String              UTF8String (SIZE (1..MAX)),
510 //       bmpString               BMPString (SIZE (1..MAX)) }
MatchAVA(Input type,uint8_t valueEncodingTag,Input presentedID,GeneralNameType referenceIDType,Input referenceID,FallBackToSearchWithinSubject fallBackToEmailAddress,FallBackToSearchWithinSubject fallBackToCommonName,MatchResult & match)511 Result MatchAVA(Input type, uint8_t valueEncodingTag, Input presentedID,
512                 GeneralNameType referenceIDType, Input referenceID,
513                 FallBackToSearchWithinSubject fallBackToEmailAddress,
514                 FallBackToSearchWithinSubject fallBackToCommonName,
515                 /*in/out*/ MatchResult& match) {
516   // Try to match the  CN as a DNSName or an IPAddress.
517   //
518   // id-at-commonName        AttributeType ::= { id-at 3 }
519   //
520   // -- Naming attributes of type X520CommonName:
521   // --   X520CommonName ::= DirectoryName (SIZE (1..ub-common-name))
522   // --
523   // -- Expanded to avoid parameterized type:
524   // X520CommonName ::= CHOICE {
525   //       teletexString     TeletexString   (SIZE (1..ub-common-name)),
526   //       printableString   PrintableString (SIZE (1..ub-common-name)),
527   //       universalString   UniversalString (SIZE (1..ub-common-name)),
528   //       utf8String        UTF8String      (SIZE (1..ub-common-name)),
529   //       bmpString         BMPString       (SIZE (1..ub-common-name)) }
530   //
531   // python DottedOIDToCode.py id-at-commonName 2.5.4.3
532   static const uint8_t id_at_commonName[] = {0x55, 0x04, 0x03};
533   if (fallBackToCommonName == FallBackToSearchWithinSubject::Yes &&
534       InputsAreEqual(type, Input(id_at_commonName))) {
535     // We might have previously found a match. Now that we've found another CN,
536     // we no longer consider that previous match to be a match, so "forget"
537     // about it.
538     match = MatchResult::NoNamesOfGivenType;
539 
540     // PrintableString is a subset of ASCII that contains all the characters
541     // allowed in CN-IDs except '*'. Although '*' is illegal, there are many
542     // real-world certificates that are encoded this way, so we accept it.
543     //
544     // In the case of UTF8String, we rely on the fact that in UTF-8 the octets
545     // in a multi-byte encoding of a code point are always distinct from ASCII.
546     // Any non-ASCII byte in a UTF-8 string causes us to fail to match. We make
547     // no attempt to detect or report malformed UTF-8 (e.g. incomplete or
548     // overlong encodings of code points, or encodings of invalid code points).
549     //
550     // TeletexString is supported as long as it does not contain any escape
551     // sequences, which are not supported. We'll reject escape sequences as
552     // invalid characters in names, which means we only accept strings that are
553     // in the default character set, which is a superset of ASCII. Note that NSS
554     // actually treats TeletexString as ISO-8859-1. Many certificates that have
555     // wildcard CN-IDs (e.g. "*.example.com") use TeletexString because
556     // PrintableString is defined to not allow '*' and because, at one point in
557     // history, UTF8String was too new to use for compatibility reasons.
558     //
559     // UniversalString and BMPString are also deprecated, and they are a little
560     // harder to support because they are not single-byte ASCII superset
561     // encodings, so we don't bother.
562     if (valueEncodingTag != der::PrintableString &&
563         valueEncodingTag != der::UTF8String &&
564         valueEncodingTag != der::TeletexString) {
565       return Success;
566     }
567 
568     if (IsValidPresentedDNSID(presentedID)) {
569       MatchSubjectPresentedIDWithReferenceID(GeneralNameType::dNSName,
570                                              presentedID, referenceIDType,
571                                              referenceID, match);
572     } else {
573       // We don't match CN-IDs for IPv6 addresses.
574       // MatchSubjectPresentedIDWithReferenceID ensures that it won't match an
575       // IPv4 address with an IPv6 address, so we don't need to check that
576       // referenceID is an IPv4 address here.
577       uint8_t ipv4[4];
578       if (ParseIPv4Address(presentedID, ipv4)) {
579         MatchSubjectPresentedIDWithReferenceID(GeneralNameType::iPAddress,
580                                                Input(ipv4), referenceIDType,
581                                                referenceID, match);
582       }
583     }
584 
585     // Regardless of whether there was a match, we keep going in case we find
586     // another CN later. If we do find another one, then this match/mismatch
587     // will be ignored, because we only care about the most specific CN.
588 
589     return Success;
590   }
591 
592   // Match an email address against an emailAddress attribute in the
593   // subject.
594   //
595   // id-emailAddress      AttributeType ::= { pkcs-9 1 }
596   //
597   // EmailAddress ::=     IA5String (SIZE (1..ub-emailaddress-length))
598   //
599   // python DottedOIDToCode.py id-emailAddress 1.2.840.113549.1.9.1
600   static const uint8_t id_emailAddress[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
601                                             0x0d, 0x01, 0x09, 0x01};
602   if (fallBackToEmailAddress == FallBackToSearchWithinSubject::Yes &&
603       InputsAreEqual(type, Input(id_emailAddress))) {
604     if (referenceIDType == GeneralNameType::rfc822Name &&
605         match == MatchResult::Match) {
606       // We already found a match; we don't need to match another one
607       return Success;
608     }
609     if (valueEncodingTag != der::IA5String) {
610       return Result::ERROR_BAD_DER;
611     }
612     return MatchPresentedIDWithReferenceID(GeneralNameType::rfc822Name,
613                                            presentedID, referenceIDType,
614                                            referenceID, match);
615   }
616 
617   return Success;
618 }
619 
MatchSubjectPresentedIDWithReferenceID(GeneralNameType presentedIDType,Input presentedID,GeneralNameType referenceIDType,Input referenceID,MatchResult & match)620 void MatchSubjectPresentedIDWithReferenceID(GeneralNameType presentedIDType,
621                                             Input presentedID,
622                                             GeneralNameType referenceIDType,
623                                             Input referenceID,
624                                             /*in/out*/ MatchResult& match) {
625   Result rv = MatchPresentedIDWithReferenceID(
626       presentedIDType, presentedID, referenceIDType, referenceID, match);
627   if (rv != Success) {
628     match = MatchResult::Mismatch;
629   }
630 }
631 
MatchPresentedIDWithReferenceID(GeneralNameType presentedIDType,Input presentedID,GeneralNameType referenceIDType,Input referenceID,MatchResult & matchResult)632 Result MatchPresentedIDWithReferenceID(GeneralNameType presentedIDType,
633                                        Input presentedID,
634                                        GeneralNameType referenceIDType,
635                                        Input referenceID,
636                                        /*out*/ MatchResult& matchResult) {
637   if (referenceIDType == GeneralNameType::nameConstraints) {
638     // matchResult is irrelevant when checking name constraints; only the
639     // pass/fail result of CheckPresentedIDConformsToConstraints matters.
640     return CheckPresentedIDConformsToConstraints(presentedIDType, presentedID,
641                                                  referenceID);
642   }
643 
644   if (presentedIDType != referenceIDType) {
645     matchResult = MatchResult::Mismatch;
646     return Success;
647   }
648 
649   Result rv;
650   bool foundMatch;
651 
652   switch (referenceIDType) {
653     case GeneralNameType::dNSName:
654       rv = MatchPresentedDNSIDWithReferenceDNSID(
655           presentedID, AllowWildcards::Yes, AllowDotlessSubdomainMatches::Yes,
656           IDRole::ReferenceID, referenceID, foundMatch);
657       break;
658 
659     case GeneralNameType::iPAddress:
660       foundMatch = InputsAreEqual(presentedID, referenceID);
661       rv = Success;
662       break;
663 
664     case GeneralNameType::rfc822Name:
665       rv = MatchPresentedRFC822NameWithReferenceRFC822Name(
666           presentedID, IDRole::ReferenceID, referenceID, foundMatch);
667       break;
668 
669     case GeneralNameType::directoryName:
670       // TODO: At some point, we may add APIs for matching DirectoryNames.
671       // fall through
672 
673     case GeneralNameType::otherName:                  // fall through
674     case GeneralNameType::x400Address:                // fall through
675     case GeneralNameType::ediPartyName:               // fall through
676     case GeneralNameType::uniformResourceIdentifier:  // fall through
677     case GeneralNameType::registeredID:               // fall through
678     case GeneralNameType::nameConstraints:
679       return NotReached("unexpected nameType for SearchType::Match",
680                         Result::FATAL_ERROR_INVALID_ARGS);
681 
682       MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
683   }
684 
685   if (rv != Success) {
686     return rv;
687   }
688   matchResult = foundMatch ? MatchResult::Match : MatchResult::Mismatch;
689   return Success;
690 }
691 
692 enum class NameConstraintsSubtrees : uint8_t {
693   permittedSubtrees = der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
694   excludedSubtrees = der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1
695 };
696 
697 Result CheckPresentedIDConformsToNameConstraintsSubtrees(
698     GeneralNameType presentedIDType, Input presentedID, Reader& nameConstraints,
699     NameConstraintsSubtrees subtreesType);
700 Result MatchPresentedIPAddressWithConstraint(Input presentedID,
701                                              Input iPAddressConstraint,
702                                              /*out*/ bool& foundMatch);
703 Result MatchPresentedDirectoryNameWithConstraint(
704     NameConstraintsSubtrees subtreesType, Input presentedID,
705     Input directoryNameConstraint, /*out*/ bool& matches);
706 
CheckPresentedIDConformsToConstraints(GeneralNameType presentedIDType,Input presentedID,Input encodedNameConstraints)707 Result CheckPresentedIDConformsToConstraints(GeneralNameType presentedIDType,
708                                              Input presentedID,
709                                              Input encodedNameConstraints) {
710   // NameConstraints ::= SEQUENCE {
711   //      permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
712   //      excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }
713   Reader nameConstraints;
714   Result rv = der::ExpectTagAndGetValueAtEnd(encodedNameConstraints,
715                                              der::SEQUENCE, nameConstraints);
716   if (rv != Success) {
717     return rv;
718   }
719 
720   // RFC 5280 says "Conforming CAs MUST NOT issue certificates where name
721   // constraints is an empty sequence. That is, either the permittedSubtrees
722   // field or the excludedSubtrees MUST be present."
723   if (nameConstraints.AtEnd()) {
724     return Result::ERROR_BAD_DER;
725   }
726 
727   rv = CheckPresentedIDConformsToNameConstraintsSubtrees(
728       presentedIDType, presentedID, nameConstraints,
729       NameConstraintsSubtrees::permittedSubtrees);
730   if (rv != Success) {
731     return rv;
732   }
733 
734   rv = CheckPresentedIDConformsToNameConstraintsSubtrees(
735       presentedIDType, presentedID, nameConstraints,
736       NameConstraintsSubtrees::excludedSubtrees);
737   if (rv != Success) {
738     return rv;
739   }
740 
741   return der::End(nameConstraints);
742 }
743 
CheckPresentedIDConformsToNameConstraintsSubtrees(GeneralNameType presentedIDType,Input presentedID,Reader & nameConstraints,NameConstraintsSubtrees subtreesType)744 Result CheckPresentedIDConformsToNameConstraintsSubtrees(
745     GeneralNameType presentedIDType, Input presentedID, Reader& nameConstraints,
746     NameConstraintsSubtrees subtreesType) {
747   if (!nameConstraints.Peek(static_cast<uint8_t>(subtreesType))) {
748     return Success;
749   }
750 
751   Reader subtrees;
752   Result rv = der::ExpectTagAndGetValue(
753       nameConstraints, static_cast<uint8_t>(subtreesType), subtrees);
754   if (rv != Success) {
755     return rv;
756   }
757 
758   bool hasPermittedSubtreesMatch = false;
759   bool hasPermittedSubtreesMismatch = false;
760 
761   // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
762   //
763   // do { ... } while(...) because subtrees isn't allowed to be empty.
764   do {
765     // GeneralSubtree ::= SEQUENCE {
766     //      base                    GeneralName,
767     //      minimum         [0]     BaseDistance DEFAULT 0,
768     //      maximum         [1]     BaseDistance OPTIONAL }
769     Reader subtree;
770     rv = ExpectTagAndGetValue(subtrees, der::SEQUENCE, subtree);
771     if (rv != Success) {
772       return rv;
773     }
774     GeneralNameType nameConstraintType;
775     Input base;
776     rv = ReadGeneralName(subtree, nameConstraintType, base);
777     if (rv != Success) {
778       return rv;
779     }
780     // http://tools.ietf.org/html/rfc5280#section-4.2.1.10: "Within this
781     // profile, the minimum and maximum fields are not used with any name
782     // forms, thus, the minimum MUST be zero, and maximum MUST be absent."
783     //
784     // Since the default value isn't allowed to be encoded according to the DER
785     // encoding rules for DEFAULT, this is equivalent to saying that neither
786     // minimum or maximum must be encoded.
787     rv = der::End(subtree);
788     if (rv != Success) {
789       return rv;
790     }
791 
792     if (presentedIDType == nameConstraintType) {
793       bool matches;
794 
795       switch (presentedIDType) {
796         case GeneralNameType::dNSName:
797           rv = MatchPresentedDNSIDWithReferenceDNSID(
798               presentedID, AllowWildcards::Yes,
799               AllowDotlessSubdomainMatches::Yes, IDRole::NameConstraint, base,
800               matches);
801           if (rv != Success) {
802             return rv;
803           }
804           break;
805 
806         case GeneralNameType::iPAddress:
807           rv =
808               MatchPresentedIPAddressWithConstraint(presentedID, base, matches);
809           if (rv != Success) {
810             return rv;
811           }
812           break;
813 
814         case GeneralNameType::directoryName:
815           rv = MatchPresentedDirectoryNameWithConstraint(
816               subtreesType, presentedID, base, matches);
817           if (rv != Success) {
818             return rv;
819           }
820           break;
821 
822         case GeneralNameType::rfc822Name:
823           rv = MatchPresentedRFC822NameWithReferenceRFC822Name(
824               presentedID, IDRole::NameConstraint, base, matches);
825           if (rv != Success) {
826             return rv;
827           }
828           break;
829 
830         // RFC 5280 says "Conforming CAs [...] SHOULD NOT impose name
831         // constraints on the x400Address, ediPartyName, or registeredID
832         // name forms. It also says "Applications conforming to this profile
833         // [...] SHOULD be able to process name constraints that are imposed
834         // on [...] uniformResourceIdentifier [...]", but we don't bother.
835         //
836         // TODO: Ask to have spec updated to say ""Conforming CAs [...] SHOULD
837         // NOT impose name constraints on the otherName, x400Address,
838         // ediPartyName, uniformResourceIdentifier, or registeredID name
839         // forms."
840         case GeneralNameType::otherName:                  // fall through
841         case GeneralNameType::x400Address:                // fall through
842         case GeneralNameType::ediPartyName:               // fall through
843         case GeneralNameType::uniformResourceIdentifier:  // fall through
844         case GeneralNameType::registeredID:               // fall through
845           return Result::ERROR_CERT_NOT_IN_NAME_SPACE;
846 
847         case GeneralNameType::nameConstraints:
848           return NotReached("invalid presentedIDType",
849                             Result::FATAL_ERROR_LIBRARY_FAILURE);
850 
851           MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
852       }
853 
854       switch (subtreesType) {
855         case NameConstraintsSubtrees::permittedSubtrees:
856           if (matches) {
857             hasPermittedSubtreesMatch = true;
858           } else {
859             hasPermittedSubtreesMismatch = true;
860           }
861           break;
862         case NameConstraintsSubtrees::excludedSubtrees:
863           if (matches) {
864             return Result::ERROR_CERT_NOT_IN_NAME_SPACE;
865           }
866           break;
867       }
868     }
869   } while (!subtrees.AtEnd());
870 
871   if (hasPermittedSubtreesMismatch && !hasPermittedSubtreesMatch) {
872     // If there was any entry of the given type in permittedSubtrees, then it
873     // required that at least one of them must match. Since none of them did,
874     // we have a failure.
875     return Result::ERROR_CERT_NOT_IN_NAME_SPACE;
876   }
877 
878   return Success;
879 }
880 
881 // We do not distinguish between a syntactically-invalid presentedDNSID and one
882 // that is syntactically valid but does not match referenceDNSID; in both
883 // cases, the result is false.
884 //
885 // We assume that both presentedDNSID and referenceDNSID are encoded in such a
886 // way that US-ASCII (7-bit) characters are encoded in one byte and no encoding
887 // of a non-US-ASCII character contains a code point in the range 0-127. For
888 // example, UTF-8 is OK but UTF-16 is not.
889 //
890 // RFC6125 says that a wildcard label may be of the form <x>*<y>.<DNSID>, where
891 // <x> and/or <y> may be empty. However, NSS requires <y> to be empty, and we
892 // follow NSS's stricter policy by accepting wildcards only of the form
893 // <x>*.<DNSID>, where <x> may be empty.
894 //
895 // An relative presented DNS ID matches both an absolute reference ID and a
896 // relative reference ID. Absolute presented DNS IDs are not supported:
897 //
898 //      Presented ID   Reference ID  Result
899 //      -------------------------------------
900 //      example.com    example.com   Match
901 //      example.com.   example.com   Mismatch
902 //      example.com    example.com.  Match
903 //      example.com.   example.com.  Mismatch
904 //
905 // There are more subtleties documented inline in the code.
906 //
907 // Name constraints ///////////////////////////////////////////////////////////
908 //
909 // This is all RFC 5280 has to say about DNSName constraints:
910 //
911 //     DNS name restrictions are expressed as host.example.com.  Any DNS
912 //     name that can be constructed by simply adding zero or more labels to
913 //     the left-hand side of the name satisfies the name constraint.  For
914 //     example, www.host.example.com would satisfy the constraint but
915 //     host1.example.com would not.
916 //
917 // This lack of specificity has lead to a lot of uncertainty regarding
918 // subdomain matching. In particular, the following questions have been
919 // raised and answered:
920 //
921 //     Q: Does a presented identifier equal (case insensitive) to the name
922 //        constraint match the constraint? For example, does the presented
923 //        ID "host.example.com" match a "host.example.com" constraint?
924 //     A: Yes. RFC5280 says "by simply adding zero or more labels" and this
925 //        is the case of adding zero labels.
926 //
927 //     Q: When the name constraint does not start with ".", do subdomain
928 //        presented identifiers match it? For example, does the presented
929 //        ID "www.host.example.com" match a "host.example.com" constraint?
930 //     A: Yes. RFC5280 says "by simply adding zero or more labels" and this
931 //        is the case of adding more than zero labels. The example is the
932 //        one from RFC 5280.
933 //
934 //     Q: When the name constraint does not start with ".", does a
935 //        non-subdomain prefix match it? For example, does "bigfoo.bar.com"
936 //        match "foo.bar.com"? [4]
937 //     A: No. We interpret RFC 5280's language of "adding zero or more labels"
938 //        to mean that whole labels must be prefixed.
939 //
940 //     (Note that the above three scenarios are the same as the RFC 6265
941 //     domain matching rules [0].)
942 //
943 //     Q: Is a name constraint that starts with "." valid, and if so, what
944 //        semantics does it have? For example, does a presented ID of
945 //        "www.example.com" match a constraint of ".example.com"? Does a
946 //        presented ID of "example.com" match a constraint of ".example.com"?
947 //     A: This implementation, NSS[1], and SChannel[2] all support a
948 //        leading ".", but OpenSSL[3] does not yet. Amongst the
949 //        implementations that support it, a leading "." is legal and means
950 //        the same thing as when the "." is omitted, EXCEPT that a
951 //        presented identifier equal (case insensitive) to the name
952 //        constraint is not matched; i.e. presented DNSName identifiers
953 //        must be subdomains. Some CAs in Mozilla's CA program (e.g. HARICA)
954 //        have name constraints with the leading "." in their root
955 //        certificates. The name constraints imposed on DCISS by Mozilla also
956 //        have the it, so supporting this is a requirement for backward
957 //        compatibility, even if it is not yet standardized. So, for example, a
958 //        presented ID of "www.example.com" matches a constraint of
959 //        ".example.com" but a presented ID of "example.com" does not.
960 //
961 //     Q: Is there a way to prevent subdomain matches?
962 //     A: Yes.
963 //
964 //        Some people have proposed that dNSName constraints that do not
965 //        start with a "." should be restricted to exact (case insensitive)
966 //        matches. However, such a change of semantics from what RFC5280
967 //        specifies would be a non-backward-compatible change in the case of
968 //        permittedSubtrees constraints, and it would be a security issue for
969 //        excludedSubtrees constraints.
970 //
971 //        However, it can be done with a combination of permittedSubtrees and
972 //        excludedSubtrees, e.g. "example.com" in permittedSubtrees and
973 //        ".example.com" in excudedSubtrees.
974 //
975 //     Q: Are name constraints allowed to be specified as absolute names?
976 //        For example, does a presented ID of "example.com" match a name
977 //        constraint of "example.com." and vice versa.
978 //     A: Absolute names are not supported as presented IDs or name
979 //        constraints. Only reference IDs may be absolute.
980 //
981 //     Q: Is "" a valid DNSName constraints? If so, what does it mean?
982 //     A: Yes. Any valid presented DNSName can be formed "by simply adding zero
983 //        or more labels to the left-hand side" of "". In particular, an
984 //        excludedSubtrees DNSName constraint of "" forbids all DNSNames.
985 //
986 //     Q: Is "." a valid DNSName constraints? If so, what does it mean?
987 //     A: No, because absolute names are not allowed (see above).
988 //
989 // [0] RFC 6265 (Cookies) Domain Matching rules:
990 //     http://tools.ietf.org/html/rfc6265#section-5.1.3
991 // [1] NSS source code:
992 //     https://mxr.mozilla.org/nss/source/lib/certdb/genname.c?rev=2a7348f013cb#1209
993 // [2] Description of SChannel's behavior from Microsoft:
994 //     http://www.imc.org/ietf-pkix/mail-archive/msg04668.html
995 // [3] Proposal to add such support to OpenSSL:
996 //     http://www.mail-archive.com/openssl-dev%40openssl.org/msg36204.html
997 //     https://rt.openssl.org/Ticket/Display.html?id=3562
998 // [4] Feedback on the lack of clarify in the definition that never got
999 //     incorporated into the spec:
1000 //     https://www.ietf.org/mail-archive/web/pkix/current/msg21192.html
MatchPresentedDNSIDWithReferenceDNSID(Input presentedDNSID,AllowWildcards allowWildcards,AllowDotlessSubdomainMatches allowDotlessSubdomainMatches,IDRole referenceDNSIDRole,Input referenceDNSID,bool & matches)1001 Result MatchPresentedDNSIDWithReferenceDNSID(
1002     Input presentedDNSID, AllowWildcards allowWildcards,
1003     AllowDotlessSubdomainMatches allowDotlessSubdomainMatches,
1004     IDRole referenceDNSIDRole, Input referenceDNSID,
1005     /*out*/ bool& matches) {
1006   if (!IsValidDNSID(presentedDNSID, IDRole::PresentedID, allowWildcards)) {
1007     return Result::ERROR_BAD_DER;
1008   }
1009 
1010   if (!IsValidDNSID(referenceDNSID, referenceDNSIDRole, AllowWildcards::No)) {
1011     return Result::ERROR_BAD_DER;
1012   }
1013 
1014   Reader presented(presentedDNSID);
1015   Reader reference(referenceDNSID);
1016 
1017   switch (referenceDNSIDRole) {
1018     case IDRole::ReferenceID:
1019       break;
1020 
1021     case IDRole::NameConstraint: {
1022       if (presentedDNSID.GetLength() > referenceDNSID.GetLength()) {
1023         if (referenceDNSID.GetLength() == 0) {
1024           // An empty constraint matches everything.
1025           matches = true;
1026           return Success;
1027         }
1028         // If the reference ID starts with a dot then skip the prefix of
1029         // of the presented ID and start the comparison at the position of that
1030         // dot. Examples:
1031         //
1032         //                                       Matches     Doesn't Match
1033         //     -----------------------------------------------------------
1034         //       original presented ID:  www.example.com    badexample.com
1035         //                     skipped:  www                ba
1036         //     presented ID w/o prefix:     .example.com      dexample.com
1037         //                reference ID:     .example.com      .example.com
1038         //
1039         // If the reference ID does not start with a dot then we skip the
1040         // prefix of the presented ID but also verify that the prefix ends with
1041         // a dot. Examples:
1042         //
1043         //                                       Matches     Doesn't Match
1044         //     -----------------------------------------------------------
1045         //       original presented ID:  www.example.com    badexample.com
1046         //                     skipped:  www                ba
1047         //                 must be '.':     .                 d
1048         //     presented ID w/o prefix:      example.com       example.com
1049         //                reference ID:      example.com       example.com
1050         //
1051         if (reference.Peek('.')) {
1052           if (presented.Skip(static_cast<Input::size_type>(
1053                   presentedDNSID.GetLength() - referenceDNSID.GetLength())) !=
1054               Success) {
1055             return NotReached("skipping subdomain failed",
1056                               Result::FATAL_ERROR_LIBRARY_FAILURE);
1057           }
1058         } else if (allowDotlessSubdomainMatches ==
1059                    AllowDotlessSubdomainMatches::Yes) {
1060           if (presented.Skip(static_cast<Input::size_type>(
1061                   presentedDNSID.GetLength() - referenceDNSID.GetLength() -
1062                   1)) != Success) {
1063             return NotReached("skipping subdomains failed",
1064                               Result::FATAL_ERROR_LIBRARY_FAILURE);
1065           }
1066           uint8_t b;
1067           if (presented.Read(b) != Success) {
1068             return NotReached("reading from presentedDNSID failed",
1069                               Result::FATAL_ERROR_LIBRARY_FAILURE);
1070           }
1071           if (b != '.') {
1072             matches = false;
1073             return Success;
1074           }
1075         }
1076       }
1077       break;
1078     }
1079 
1080     case IDRole::PresentedID:  // fall through
1081       return NotReached("IDRole::PresentedID is not a valid referenceDNSIDRole",
1082                         Result::FATAL_ERROR_INVALID_ARGS);
1083   }
1084 
1085   // We only allow wildcard labels that consist only of '*'.
1086   if (presented.Peek('*')) {
1087     if (presented.Skip(1) != Success) {
1088       return NotReached("Skipping '*' failed",
1089                         Result::FATAL_ERROR_LIBRARY_FAILURE);
1090     }
1091     do {
1092       // This will happen if reference is a single, relative label
1093       if (reference.AtEnd()) {
1094         matches = false;
1095         return Success;
1096       }
1097       uint8_t referenceByte;
1098       if (reference.Read(referenceByte) != Success) {
1099         return NotReached("invalid reference ID",
1100                           Result::FATAL_ERROR_INVALID_ARGS);
1101       }
1102     } while (!reference.Peek('.'));
1103   }
1104 
1105   for (;;) {
1106     uint8_t presentedByte;
1107     if (presented.Read(presentedByte) != Success) {
1108       matches = false;
1109       return Success;
1110     }
1111     uint8_t referenceByte;
1112     if (reference.Read(referenceByte) != Success) {
1113       matches = false;
1114       return Success;
1115     }
1116     if (LocaleInsensitveToLower(presentedByte) !=
1117         LocaleInsensitveToLower(referenceByte)) {
1118       matches = false;
1119       return Success;
1120     }
1121     if (presented.AtEnd()) {
1122       // Don't allow presented IDs to be absolute.
1123       if (presentedByte == '.') {
1124         return Result::ERROR_BAD_DER;
1125       }
1126       break;
1127     }
1128   }
1129 
1130   // Allow a relative presented DNS ID to match an absolute reference DNS ID,
1131   // unless we're matching a name constraint.
1132   if (!reference.AtEnd()) {
1133     if (referenceDNSIDRole != IDRole::NameConstraint) {
1134       uint8_t referenceByte;
1135       if (reference.Read(referenceByte) != Success) {
1136         return NotReached("read failed but not at end",
1137                           Result::FATAL_ERROR_LIBRARY_FAILURE);
1138       }
1139       if (referenceByte != '.') {
1140         matches = false;
1141         return Success;
1142       }
1143     }
1144     if (!reference.AtEnd()) {
1145       matches = false;
1146       return Success;
1147     }
1148   }
1149 
1150   matches = true;
1151   return Success;
1152 }
1153 
1154 // https://tools.ietf.org/html/rfc5280#section-4.2.1.10 says:
1155 //
1156 //     For IPv4 addresses, the iPAddress field of GeneralName MUST contain
1157 //     eight (8) octets, encoded in the style of RFC 4632 (CIDR) to represent
1158 //     an address range [RFC4632].  For IPv6 addresses, the iPAddress field
1159 //     MUST contain 32 octets similarly encoded.  For example, a name
1160 //     constraint for "class C" subnet 192.0.2.0 is represented as the
1161 //     octets C0 00 02 00 FF FF FF 00, representing the CIDR notation
1162 //     192.0.2.0/24 (mask 255.255.255.0).
MatchPresentedIPAddressWithConstraint(Input presentedID,Input iPAddressConstraint,bool & foundMatch)1163 Result MatchPresentedIPAddressWithConstraint(Input presentedID,
1164                                              Input iPAddressConstraint,
1165                                              /*out*/ bool& foundMatch) {
1166   if (presentedID.GetLength() != 4 && presentedID.GetLength() != 16) {
1167     return Result::ERROR_BAD_DER;
1168   }
1169   if (iPAddressConstraint.GetLength() != 8 &&
1170       iPAddressConstraint.GetLength() != 32) {
1171     return Result::ERROR_BAD_DER;
1172   }
1173 
1174   // an IPv4 address never matches an IPv6 constraint, and vice versa.
1175   if (presentedID.GetLength() * 2 != iPAddressConstraint.GetLength()) {
1176     foundMatch = false;
1177     return Success;
1178   }
1179 
1180   Reader constraint(iPAddressConstraint);
1181   Reader constraintAddress;
1182   Result rv =
1183       constraint.Skip(iPAddressConstraint.GetLength() / 2u, constraintAddress);
1184   if (rv != Success) {
1185     return rv;
1186   }
1187   Reader constraintMask;
1188   rv = constraint.Skip(iPAddressConstraint.GetLength() / 2u, constraintMask);
1189   if (rv != Success) {
1190     return rv;
1191   }
1192   rv = der::End(constraint);
1193   if (rv != Success) {
1194     return rv;
1195   }
1196 
1197   Reader presented(presentedID);
1198   do {
1199     uint8_t presentedByte;
1200     rv = presented.Read(presentedByte);
1201     if (rv != Success) {
1202       return rv;
1203     }
1204     uint8_t constraintAddressByte;
1205     rv = constraintAddress.Read(constraintAddressByte);
1206     if (rv != Success) {
1207       return rv;
1208     }
1209     uint8_t constraintMaskByte;
1210     rv = constraintMask.Read(constraintMaskByte);
1211     if (rv != Success) {
1212       return rv;
1213     }
1214     foundMatch =
1215         ((presentedByte ^ constraintAddressByte) & constraintMaskByte) == 0;
1216   } while (foundMatch && !presented.AtEnd());
1217 
1218   return Success;
1219 }
1220 
1221 // AttributeTypeAndValue ::= SEQUENCE {
1222 //   type     AttributeType,
1223 //   value    AttributeValue }
1224 //
1225 // AttributeType ::= OBJECT IDENTIFIER
1226 //
1227 // AttributeValue ::= ANY -- DEFINED BY AttributeType
ReadAVA(Reader & rdn,Input & type,uint8_t & valueTag,Input & value)1228 Result ReadAVA(Reader& rdn,
1229                /*out*/ Input& type,
1230                /*out*/ uint8_t& valueTag,
1231                /*out*/ Input& value) {
1232   return der::Nested(rdn, der::SEQUENCE, [&](Reader& ava) -> Result {
1233     Result rv = der::ExpectTagAndGetValue(ava, der::OIDTag, type);
1234     if (rv != Success) {
1235       return rv;
1236     }
1237     rv = der::ReadTagAndGetValue(ava, valueTag, value);
1238     if (rv != Success) {
1239       return rv;
1240     }
1241     return Success;
1242   });
1243 }
1244 
1245 // Names are sequences of RDNs. RDNS are sets of AVAs. That means that RDNs are
1246 // unordered, so in theory we should match RDNs with equivalent AVAs that are
1247 // in different orders. Within the AVAs are DirectoryNames that are supposed to
1248 // be compared according to LDAP stringprep normalization rules (e.g.
1249 // normalizing whitespace), consideration of different character encodings,
1250 // etc. Indeed, RFC 5280 says we MUST deal with all of that.
1251 //
1252 // In practice, many implementations, including NSS, only match Names in a way
1253 // that only meets a subset of the requirements of RFC 5280. Those
1254 // normalization and character encoding conversion steps appear to be
1255 // unnecessary for processing real-world certificates, based on experience from
1256 // having used NSS in Firefox for many years.
1257 //
1258 // RFC 5280 also says "CAs issuing certificates with a restriction of the form
1259 // directoryName SHOULD NOT rely on implementation of the full
1260 // ISO DN name comparison algorithm. This implies name restrictions MUST
1261 // be stated identically to the encoding used in the subject field or
1262 // subjectAltName extension." It goes on to say, in the security
1263 // considerations:
1264 //
1265 //     In addition, name constraints for distinguished names MUST be stated
1266 //     identically to the encoding used in the subject field or
1267 //     subjectAltName extension.  If not, then name constraints stated as
1268 //     excludedSubtrees will not match and invalid paths will be accepted
1269 //     and name constraints expressed as permittedSubtrees will not match
1270 //     and valid paths will be rejected.  To avoid acceptance of invalid
1271 //     paths, CAs SHOULD state name constraints for distinguished names as
1272 //     permittedSubtrees wherever possible.
1273 //
1274 // For permittedSubtrees, the MUST-level requirement is relaxed for
1275 // compatibility in the case of PrintableString and UTF8String. That is, if a
1276 // name constraint has been encoded using UTF8String and the presented ID has
1277 // been encoded with a PrintableString (or vice-versa), they are considered to
1278 // match if they are equal everywhere except for the tag identifying the
1279 // encoding. See bug 1150114.
1280 //
1281 // For excludedSubtrees, we simply prohibit any non-empty directoryName
1282 // constraint to ensure we are not being too lenient. We support empty
1283 // DirectoryName constraints in excludedSubtrees so that a CA can say "Do not
1284 // allow any DirectoryNames in issued certificates."
MatchPresentedDirectoryNameWithConstraint(NameConstraintsSubtrees subtreesType,Input presentedID,Input directoryNameConstraint,bool & matches)1285 Result MatchPresentedDirectoryNameWithConstraint(
1286     NameConstraintsSubtrees subtreesType, Input presentedID,
1287     Input directoryNameConstraint,
1288     /*out*/ bool& matches) {
1289   Reader constraintRDNs;
1290   Result rv = der::ExpectTagAndGetValueAtEnd(directoryNameConstraint,
1291                                              der::SEQUENCE, constraintRDNs);
1292   if (rv != Success) {
1293     return rv;
1294   }
1295   Reader presentedRDNs;
1296   rv =
1297       der::ExpectTagAndGetValueAtEnd(presentedID, der::SEQUENCE, presentedRDNs);
1298   if (rv != Success) {
1299     return rv;
1300   }
1301 
1302   switch (subtreesType) {
1303     case NameConstraintsSubtrees::permittedSubtrees:
1304       break;  // dealt with below
1305     case NameConstraintsSubtrees::excludedSubtrees:
1306       if (!constraintRDNs.AtEnd() || !presentedRDNs.AtEnd()) {
1307         return Result::ERROR_CERT_NOT_IN_NAME_SPACE;
1308       }
1309       matches = true;
1310       return Success;
1311   }
1312 
1313   for (;;) {
1314     // The AVAs have to be fully equal, but the constraint RDNs just need to be
1315     // a prefix of the presented RDNs.
1316     if (constraintRDNs.AtEnd()) {
1317       matches = true;
1318       return Success;
1319     }
1320     if (presentedRDNs.AtEnd()) {
1321       matches = false;
1322       return Success;
1323     }
1324     Reader constraintRDN;
1325     rv = der::ExpectTagAndGetValue(constraintRDNs, der::SET, constraintRDN);
1326     if (rv != Success) {
1327       return rv;
1328     }
1329     Reader presentedRDN;
1330     rv = der::ExpectTagAndGetValue(presentedRDNs, der::SET, presentedRDN);
1331     if (rv != Success) {
1332       return rv;
1333     }
1334     while (!constraintRDN.AtEnd() && !presentedRDN.AtEnd()) {
1335       Input constraintType;
1336       uint8_t constraintValueTag;
1337       Input constraintValue;
1338       rv = ReadAVA(constraintRDN, constraintType, constraintValueTag,
1339                    constraintValue);
1340       if (rv != Success) {
1341         return rv;
1342       }
1343       Input presentedType;
1344       uint8_t presentedValueTag;
1345       Input presentedValue;
1346       rv = ReadAVA(presentedRDN, presentedType, presentedValueTag,
1347                    presentedValue);
1348       if (rv != Success) {
1349         return rv;
1350       }
1351       // TODO (bug 1155767): verify that if an AVA is a PrintableString it
1352       // consists only of characters valid for PrintableStrings.
1353       bool avasMatch = InputsAreEqual(constraintType, presentedType) &&
1354                        InputsAreEqual(constraintValue, presentedValue) &&
1355                        (constraintValueTag == presentedValueTag ||
1356                         (constraintValueTag == der::Tag::UTF8String &&
1357                          presentedValueTag == der::Tag::PrintableString) ||
1358                         (constraintValueTag == der::Tag::PrintableString &&
1359                          presentedValueTag == der::Tag::UTF8String));
1360       if (!avasMatch) {
1361         matches = false;
1362         return Success;
1363       }
1364     }
1365     if (!constraintRDN.AtEnd() || !presentedRDN.AtEnd()) {
1366       matches = false;
1367       return Success;
1368     }
1369   }
1370 }
1371 
1372 // RFC 5280 says:
1373 //
1374 //     The format of an rfc822Name is a "Mailbox" as defined in Section 4.1.2
1375 //     of [RFC2821]. A Mailbox has the form "Local-part@Domain".  Note that a
1376 //     Mailbox has no phrase (such as a common name) before it, has no comment
1377 //     (text surrounded in parentheses) after it, and is not surrounded by "<"
1378 //     and ">".  Rules for encoding Internet mail addresses that include
1379 //     internationalized domain names are specified in Section 7.5.
1380 //
1381 // and:
1382 //
1383 //     A name constraint for Internet mail addresses MAY specify a
1384 //     particular mailbox, all addresses at a particular host, or all
1385 //     mailboxes in a domain.  To indicate a particular mailbox, the
1386 //     constraint is the complete mail address.  For example,
1387 //     "root@example.com" indicates the root mailbox on the host
1388 //     "example.com".  To indicate all Internet mail addresses on a
1389 //     particular host, the constraint is specified as the host name.  For
1390 //     example, the constraint "example.com" is satisfied by any mail
1391 //     address at the host "example.com".  To specify any address within a
1392 //     domain, the constraint is specified with a leading period (as with
1393 //     URIs).  For example, ".example.com" indicates all the Internet mail
1394 //     addresses in the domain "example.com", but not Internet mail
1395 //     addresses on the host "example.com".
1396 
IsValidRFC822Name(Input input)1397 bool IsValidRFC822Name(Input input) {
1398   Reader reader(input);
1399 
1400   // Local-part@.
1401   bool startOfAtom = true;
1402   for (;;) {
1403     uint8_t presentedByte;
1404     if (reader.Read(presentedByte) != Success) {
1405       return false;
1406     }
1407     switch (presentedByte) {
1408       // atext is defined in https://tools.ietf.org/html/rfc2822#section-3.2.4
1409       case 'A':
1410       case 'a':
1411       case 'N':
1412       case 'n':
1413       case '0':
1414       case '!':
1415       case '#':
1416       case 'B':
1417       case 'b':
1418       case 'O':
1419       case 'o':
1420       case '1':
1421       case '$':
1422       case '%':
1423       case 'C':
1424       case 'c':
1425       case 'P':
1426       case 'p':
1427       case '2':
1428       case '&':
1429       case '\'':
1430       case 'D':
1431       case 'd':
1432       case 'Q':
1433       case 'q':
1434       case '3':
1435       case '*':
1436       case '+':
1437       case 'E':
1438       case 'e':
1439       case 'R':
1440       case 'r':
1441       case '4':
1442       case '-':
1443       case '/':
1444       case 'F':
1445       case 'f':
1446       case 'S':
1447       case 's':
1448       case '5':
1449       case '=':
1450       case '?':
1451       case 'G':
1452       case 'g':
1453       case 'T':
1454       case 't':
1455       case '6':
1456       case '^':
1457       case '_':
1458       case 'H':
1459       case 'h':
1460       case 'U':
1461       case 'u':
1462       case '7':
1463       case '`':
1464       case '{':
1465       case 'I':
1466       case 'i':
1467       case 'V':
1468       case 'v':
1469       case '8':
1470       case '|':
1471       case '}':
1472       case 'J':
1473       case 'j':
1474       case 'W':
1475       case 'w':
1476       case '9':
1477       case '~':
1478       case 'K':
1479       case 'k':
1480       case 'X':
1481       case 'x':
1482       case 'L':
1483       case 'l':
1484       case 'Y':
1485       case 'y':
1486       case 'M':
1487       case 'm':
1488       case 'Z':
1489       case 'z':
1490         startOfAtom = false;
1491         break;
1492 
1493       case '.':
1494         if (startOfAtom) {
1495           return false;
1496         }
1497         startOfAtom = true;
1498         break;
1499 
1500       case '@': {
1501         if (startOfAtom) {
1502           return false;
1503         }
1504         Input domain;
1505         if (reader.SkipToEnd(domain) != Success) {
1506           return false;
1507         }
1508         return IsValidDNSID(domain, IDRole::PresentedID, AllowWildcards::No);
1509       }
1510 
1511       default:
1512         return false;
1513     }
1514   }
1515 }
1516 
MatchPresentedRFC822NameWithReferenceRFC822Name(Input presentedRFC822Name,IDRole referenceRFC822NameRole,Input referenceRFC822Name,bool & matches)1517 Result MatchPresentedRFC822NameWithReferenceRFC822Name(
1518     Input presentedRFC822Name, IDRole referenceRFC822NameRole,
1519     Input referenceRFC822Name,
1520     /*out*/ bool& matches) {
1521   if (!IsValidRFC822Name(presentedRFC822Name)) {
1522     return Result::ERROR_BAD_DER;
1523   }
1524   Reader presented(presentedRFC822Name);
1525 
1526   switch (referenceRFC822NameRole) {
1527     case IDRole::PresentedID:
1528       return Result::FATAL_ERROR_INVALID_ARGS;
1529 
1530     case IDRole::ReferenceID:
1531       break;
1532 
1533     case IDRole::NameConstraint: {
1534       if (InputContains(referenceRFC822Name, '@')) {
1535         // The constraint is of the form "Local-part@Domain".
1536         break;
1537       }
1538 
1539       // The constraint is of the form "example.com" or ".example.com".
1540 
1541       // Skip past the '@' in the presented ID.
1542       for (;;) {
1543         uint8_t presentedByte;
1544         if (presented.Read(presentedByte) != Success) {
1545           return Result::FATAL_ERROR_LIBRARY_FAILURE;
1546         }
1547         if (presentedByte == '@') {
1548           break;
1549         }
1550       }
1551 
1552       Input presentedDNSID;
1553       if (presented.SkipToEnd(presentedDNSID) != Success) {
1554         return Result::FATAL_ERROR_LIBRARY_FAILURE;
1555       }
1556 
1557       return MatchPresentedDNSIDWithReferenceDNSID(
1558           presentedDNSID, AllowWildcards::No, AllowDotlessSubdomainMatches::No,
1559           IDRole::NameConstraint, referenceRFC822Name, matches);
1560     }
1561   }
1562 
1563   if (!IsValidRFC822Name(referenceRFC822Name)) {
1564     return Result::ERROR_BAD_DER;
1565   }
1566 
1567   Reader reference(referenceRFC822Name);
1568 
1569   for (;;) {
1570     uint8_t presentedByte;
1571     if (presented.Read(presentedByte) != Success) {
1572       matches = reference.AtEnd();
1573       return Success;
1574     }
1575     uint8_t referenceByte;
1576     if (reference.Read(referenceByte) != Success) {
1577       matches = false;
1578       return Success;
1579     }
1580     if (LocaleInsensitveToLower(presentedByte) !=
1581         LocaleInsensitveToLower(referenceByte)) {
1582       matches = false;
1583       return Success;
1584     }
1585   }
1586 }
1587 
1588 // We avoid isdigit because it is locale-sensitive. See
1589 // http://pubs.opengroup.org/onlinepubs/009695399/functions/tolower.html.
LocaleInsensitveToLower(uint8_t a)1590 inline uint8_t LocaleInsensitveToLower(uint8_t a) {
1591   if (a >= 'A' && a <= 'Z') {  // unlikely
1592     return static_cast<uint8_t>(
1593         static_cast<uint8_t>(a - static_cast<uint8_t>('A')) +
1594         static_cast<uint8_t>('a'));
1595   }
1596   return a;
1597 }
1598 
StartsWithIDNALabel(Input id)1599 bool StartsWithIDNALabel(Input id) {
1600   static const uint8_t IDN_ALABEL_PREFIX[4] = {'x', 'n', '-', '-'};
1601   Reader input(id);
1602   for (const uint8_t prefixByte : IDN_ALABEL_PREFIX) {
1603     uint8_t b;
1604     if (input.Read(b) != Success) {
1605       return false;
1606     }
1607     if (b != prefixByte) {
1608       return false;
1609     }
1610   }
1611   return true;
1612 }
1613 
ReadIPv4AddressComponent(Reader & input,bool lastComponent,uint8_t & valueOut)1614 bool ReadIPv4AddressComponent(Reader& input, bool lastComponent,
1615                               /*out*/ uint8_t& valueOut) {
1616   size_t length = 0;
1617   unsigned int value = 0;  // Must be larger than uint8_t.
1618 
1619   for (;;) {
1620     if (input.AtEnd() && lastComponent) {
1621       break;
1622     }
1623 
1624     uint8_t b;
1625     if (input.Read(b) != Success) {
1626       return false;
1627     }
1628 
1629     if (b >= '0' && b <= '9') {
1630       if (value == 0 && length > 0) {
1631         return false;  // Leading zeros are not allowed.
1632       }
1633       value = (value * 10) + (b - '0');
1634       if (value > 255) {
1635         return false;  // Component's value is too large.
1636       }
1637       ++length;
1638     } else if (!lastComponent && b == '.') {
1639       break;
1640     } else {
1641       return false;  // Invalid character.
1642     }
1643   }
1644 
1645   if (length == 0) {
1646     return false;  // empty components not allowed
1647   }
1648 
1649   valueOut = static_cast<uint8_t>(value);
1650   return true;
1651 }
1652 
1653 }  // namespace
1654 
1655 // On Windows and maybe other platforms, OS-provided IP address parsing
1656 // functions might fail if the protocol (IPv4 or IPv6) has been disabled, so we
1657 // can't rely on them.
ParseIPv4Address(Input hostname,uint8_t (& out)[4])1658 bool ParseIPv4Address(Input hostname, /*out*/ uint8_t (&out)[4]) {
1659   Reader input(hostname);
1660   return ReadIPv4AddressComponent(input, false, out[0]) &&
1661          ReadIPv4AddressComponent(input, false, out[1]) &&
1662          ReadIPv4AddressComponent(input, false, out[2]) &&
1663          ReadIPv4AddressComponent(input, true, out[3]);
1664 }
1665 
1666 namespace {
1667 
FinishIPv6Address(uint8_t (& address)[16],int numComponents,int contractionIndex)1668 bool FinishIPv6Address(/*in/out*/ uint8_t (&address)[16], int numComponents,
1669                        int contractionIndex) {
1670   assert(numComponents >= 0);
1671   assert(numComponents <= 8);
1672   assert(contractionIndex >= -1);
1673   assert(contractionIndex <= 8);
1674   assert(contractionIndex <= numComponents);
1675   if (!(numComponents >= 0 && numComponents <= 8 && contractionIndex >= -1 &&
1676         contractionIndex <= 8 && contractionIndex <= numComponents)) {
1677     return false;
1678   }
1679 
1680   if (contractionIndex == -1) {
1681     // no contraction
1682     return numComponents == 8;
1683   }
1684 
1685   if (numComponents >= 8) {
1686     return false;  // no room left to expand the contraction.
1687   }
1688 
1689   // Shift components that occur after the contraction over.
1690   std::copy_backward(address + (2u * static_cast<size_t>(contractionIndex)),
1691                      address + (2u * static_cast<size_t>(numComponents)),
1692                      address + (2u * 8u));
1693   // Fill in the contracted area with zeros.
1694   std::fill_n(address + 2u * static_cast<size_t>(contractionIndex),
1695               (8u - static_cast<size_t>(numComponents)) * 2u,
1696               static_cast<uint8_t>(0u));
1697 
1698   return true;
1699 }
1700 
1701 }  // namespace
1702 
1703 // On Windows and maybe other platforms, OS-provided IP address parsing
1704 // functions might fail if the protocol (IPv4 or IPv6) has been disabled, so we
1705 // can't rely on them.
ParseIPv6Address(Input hostname,uint8_t (& out)[16])1706 bool ParseIPv6Address(Input hostname, /*out*/ uint8_t (&out)[16]) {
1707   Reader input(hostname);
1708 
1709   int currentComponentIndex = 0;
1710   int contractionIndex = -1;
1711 
1712   if (input.Peek(':')) {
1713     // A valid input can only start with ':' if there is a contraction at the
1714     // beginning.
1715     uint8_t b;
1716     if (input.Read(b) != Success || b != ':') {
1717       assert(false);
1718       return false;
1719     }
1720     if (input.Read(b) != Success) {
1721       return false;
1722     }
1723     if (b != ':') {
1724       return false;
1725     }
1726     contractionIndex = 0;
1727   }
1728 
1729   for (;;) {
1730     // If we encounter a '.' then we'll have to backtrack to parse the input
1731     // from startOfComponent to the end of the input as an IPv4 address.
1732     Reader::Mark startOfComponent(input.GetMark());
1733     uint16_t componentValue = 0;
1734     size_t componentLength = 0;
1735     while (!input.AtEnd() && !input.Peek(':')) {
1736       uint8_t value;
1737       uint8_t b;
1738       if (input.Read(b) != Success) {
1739         assert(false);
1740         return false;
1741       }
1742       switch (b) {
1743         case '0':
1744         case '1':
1745         case '2':
1746         case '3':
1747         case '4':
1748         case '5':
1749         case '6':
1750         case '7':
1751         case '8':
1752         case '9':
1753           value = static_cast<uint8_t>(b - static_cast<uint8_t>('0'));
1754           break;
1755         case 'a':
1756         case 'b':
1757         case 'c':
1758         case 'd':
1759         case 'e':
1760         case 'f':
1761           value =
1762               static_cast<uint8_t>(b - static_cast<uint8_t>('a') + UINT8_C(10));
1763           break;
1764         case 'A':
1765         case 'B':
1766         case 'C':
1767         case 'D':
1768         case 'E':
1769         case 'F':
1770           value =
1771               static_cast<uint8_t>(b - static_cast<uint8_t>('A') + UINT8_C(10));
1772           break;
1773         case '.': {
1774           // A dot indicates we hit a IPv4-syntax component. Backtrack, parsing
1775           // the input from startOfComponent to the end of the input as an IPv4
1776           // address, and then combine it with the other components.
1777 
1778           if (currentComponentIndex > 6) {
1779             return false;  // Too many components before the IPv4 component
1780           }
1781 
1782           input.SkipToEnd();
1783           Input ipv4Component;
1784           if (input.GetInput(startOfComponent, ipv4Component) != Success) {
1785             return false;
1786           }
1787           uint8_t(*ipv4)[4] =
1788               reinterpret_cast<uint8_t(*)[4]>(&out[2 * currentComponentIndex]);
1789           if (!ParseIPv4Address(ipv4Component, *ipv4)) {
1790             return false;
1791           }
1792           assert(input.AtEnd());
1793           currentComponentIndex += 2;
1794 
1795           return FinishIPv6Address(out, currentComponentIndex,
1796                                    contractionIndex);
1797         }
1798         default:
1799           return false;
1800       }
1801       if (componentLength >= 4) {
1802         // component too long
1803         return false;
1804       }
1805       ++componentLength;
1806       componentValue = (componentValue * 0x10u) + value;
1807     }
1808 
1809     if (currentComponentIndex >= 8) {
1810       return false;  // too many components
1811     }
1812 
1813     if (componentLength == 0) {
1814       if (input.AtEnd() && currentComponentIndex == contractionIndex) {
1815         if (contractionIndex == 0) {
1816           // don't accept "::"
1817           return false;
1818         }
1819         return FinishIPv6Address(out, currentComponentIndex, contractionIndex);
1820       }
1821       return false;
1822     }
1823 
1824     out[2 * currentComponentIndex] =
1825         static_cast<uint8_t>(componentValue / 0x100);
1826     out[(2 * currentComponentIndex) + 1] =
1827         static_cast<uint8_t>(componentValue % 0x100);
1828 
1829     ++currentComponentIndex;
1830 
1831     if (input.AtEnd()) {
1832       return FinishIPv6Address(out, currentComponentIndex, contractionIndex);
1833     }
1834 
1835     uint8_t b;
1836     if (input.Read(b) != Success || b != ':') {
1837       assert(false);
1838       return false;
1839     }
1840 
1841     if (input.Peek(':')) {
1842       // Contraction
1843       if (contractionIndex != -1) {
1844         return false;  // multiple contractions are not allowed.
1845       }
1846       if (input.Read(b) != Success || b != ':') {
1847         assert(false);
1848         return false;
1849       }
1850       contractionIndex = currentComponentIndex;
1851       if (input.AtEnd()) {
1852         // "::" at the end of the input.
1853         return FinishIPv6Address(out, currentComponentIndex, contractionIndex);
1854       }
1855     }
1856   }
1857 }
1858 
IsValidReferenceDNSID(Input hostname)1859 bool IsValidReferenceDNSID(Input hostname) {
1860   return IsValidDNSID(hostname, IDRole::ReferenceID, AllowWildcards::No);
1861 }
1862 
IsValidPresentedDNSID(Input hostname)1863 bool IsValidPresentedDNSID(Input hostname) {
1864   return IsValidDNSID(hostname, IDRole::PresentedID, AllowWildcards::Yes);
1865 }
1866 
1867 namespace {
1868 
1869 // RFC 5280 Section 4.2.1.6 says that a dNSName "MUST be in the 'preferred name
1870 // syntax', as specified by Section 3.5 of [RFC1034] and as modified by Section
1871 // 2.1 of [RFC1123]" except "a dNSName of ' ' MUST NOT be used." Additionally,
1872 // we allow underscores for compatibility with existing practice.
IsValidDNSID(Input hostname,IDRole idRole,AllowWildcards allowWildcards)1873 bool IsValidDNSID(Input hostname, IDRole idRole,
1874                   AllowWildcards allowWildcards) {
1875   if (hostname.GetLength() > 253) {
1876     return false;
1877   }
1878 
1879   Reader input(hostname);
1880 
1881   if (idRole == IDRole::NameConstraint && input.AtEnd()) {
1882     return true;
1883   }
1884 
1885   size_t dotCount = 0;
1886   size_t labelLength = 0;
1887   bool labelIsAllNumeric = false;
1888   bool labelEndsWithHyphen = false;
1889 
1890   // Only presented IDs are allowed to have wildcard labels. And, like
1891   // Chromium, be stricter than RFC 6125 requires by insisting that a
1892   // wildcard label consist only of '*'.
1893   bool isWildcard = allowWildcards == AllowWildcards::Yes && input.Peek('*');
1894   bool isFirstByte = !isWildcard;
1895   if (isWildcard) {
1896     Result rv = input.Skip(1);
1897     if (rv != Success) {
1898       assert(false);
1899       return false;
1900     }
1901 
1902     uint8_t b;
1903     rv = input.Read(b);
1904     if (rv != Success) {
1905       return false;
1906     }
1907     if (b != '.') {
1908       return false;
1909     }
1910     ++dotCount;
1911   }
1912 
1913   do {
1914     static const size_t MAX_LABEL_LENGTH = 63;
1915 
1916     uint8_t b;
1917     if (input.Read(b) != Success) {
1918       return false;
1919     }
1920     switch (b) {
1921       case '-':
1922         if (labelLength == 0) {
1923           return false;  // Labels must not start with a hyphen.
1924         }
1925         labelIsAllNumeric = false;
1926         labelEndsWithHyphen = true;
1927         ++labelLength;
1928         if (labelLength > MAX_LABEL_LENGTH) {
1929           return false;
1930         }
1931         break;
1932 
1933       // We avoid isdigit because it is locale-sensitive. See
1934       // http://pubs.opengroup.org/onlinepubs/009695399/functions/isdigit.html
1935       case '0':
1936       case '5':
1937       case '1':
1938       case '6':
1939       case '2':
1940       case '7':
1941       case '3':
1942       case '8':
1943       case '4':
1944       case '9':
1945         if (labelLength == 0) {
1946           labelIsAllNumeric = true;
1947         }
1948         labelEndsWithHyphen = false;
1949         ++labelLength;
1950         if (labelLength > MAX_LABEL_LENGTH) {
1951           return false;
1952         }
1953         break;
1954 
1955       // We avoid using islower/isupper/tolower/toupper or similar things, to
1956       // avoid any possibility of this code being locale-sensitive. See
1957       // http://pubs.opengroup.org/onlinepubs/009695399/functions/isupper.html
1958       case 'a':
1959       case 'A':
1960       case 'n':
1961       case 'N':
1962       case 'b':
1963       case 'B':
1964       case 'o':
1965       case 'O':
1966       case 'c':
1967       case 'C':
1968       case 'p':
1969       case 'P':
1970       case 'd':
1971       case 'D':
1972       case 'q':
1973       case 'Q':
1974       case 'e':
1975       case 'E':
1976       case 'r':
1977       case 'R':
1978       case 'f':
1979       case 'F':
1980       case 's':
1981       case 'S':
1982       case 'g':
1983       case 'G':
1984       case 't':
1985       case 'T':
1986       case 'h':
1987       case 'H':
1988       case 'u':
1989       case 'U':
1990       case 'i':
1991       case 'I':
1992       case 'v':
1993       case 'V':
1994       case 'j':
1995       case 'J':
1996       case 'w':
1997       case 'W':
1998       case 'k':
1999       case 'K':
2000       case 'x':
2001       case 'X':
2002       case 'l':
2003       case 'L':
2004       case 'y':
2005       case 'Y':
2006       case 'm':
2007       case 'M':
2008       case 'z':
2009       case 'Z':
2010       // We allow underscores for compatibility with existing practices.
2011       // See bug 1136616.
2012       case '_':
2013         labelIsAllNumeric = false;
2014         labelEndsWithHyphen = false;
2015         ++labelLength;
2016         if (labelLength > MAX_LABEL_LENGTH) {
2017           return false;
2018         }
2019         break;
2020 
2021       case '.':
2022         ++dotCount;
2023         if (labelLength == 0 &&
2024             (idRole != IDRole::NameConstraint || !isFirstByte)) {
2025           return false;
2026         }
2027         if (labelEndsWithHyphen) {
2028           return false;  // Labels must not end with a hyphen.
2029         }
2030         labelLength = 0;
2031         break;
2032 
2033       default:
2034         return false;  // Invalid character.
2035     }
2036     isFirstByte = false;
2037   } while (!input.AtEnd());
2038 
2039   // Only reference IDs, not presented IDs or name constraints, may be
2040   // absolute.
2041   if (labelLength == 0 && idRole != IDRole::ReferenceID) {
2042     return false;
2043   }
2044 
2045   if (labelEndsWithHyphen) {
2046     return false;  // Labels must not end with a hyphen.
2047   }
2048 
2049   if (labelIsAllNumeric) {
2050     return false;  // Last label must not be all numeric.
2051   }
2052 
2053   if (isWildcard) {
2054     // If the DNS ID ends with a dot, the last dot signifies an absolute ID.
2055     size_t labelCount = (labelLength == 0) ? dotCount : (dotCount + 1);
2056 
2057     // Like NSS, require at least two labels to follow the wildcard label.
2058     //
2059     // TODO(bug XXXXXXX): Allow the TrustDomain to control this on a
2060     // per-eTLD+1 basis, similar to Chromium. Even then, it might be better to
2061     // still enforce that there are at least two labels after the wildcard.
2062     if (labelCount < 3) {
2063       return false;
2064     }
2065     // XXX: RFC6125 says that we shouldn't accept wildcards within an IDN
2066     // A-Label. The consequence of this is that we effectively discriminate
2067     // against users of languages that cannot be encoded with ASCII.
2068     if (StartsWithIDNALabel(hostname)) {
2069       return false;
2070     }
2071 
2072     // TODO(bug XXXXXXX): Wildcards are not allowed for EV certificates.
2073     // Provide an option to indicate whether wildcards should be matched, for
2074     // the purpose of helping the application enforce this.
2075   }
2076 
2077   return true;
2078 }
2079 
2080 }  // namespace
2081 
2082 }  // namespace pkix
2083 }  // namespace mozilla
2084