@@ -190,7 +190,7 @@ var (
190190 errWildcardNotSupported = berrors .MalformedError ("Wildcard domain names are not supported" )
191191)
192192
193- // ValidNonWildcardDomain checks that a domain isn't:
193+ // validNonWildcardDomain checks that a domain isn't:
194194// - empty
195195// - prefixed with the wildcard label `*.`
196196// - made of invalid DNS characters
@@ -203,7 +203,7 @@ var (
203203// - exactly equal to an IANA registered TLD
204204//
205205// It does NOT ensure that the domain is absent from any PA blocked lists.
206- func ValidNonWildcardDomain (domain string ) error {
206+ func validNonWildcardDomain (domain string ) error {
207207 if domain == "" {
208208 return errEmptyName
209209 }
@@ -296,7 +296,7 @@ func ValidNonWildcardDomain(domain string) error {
296296// from any PA blocked lists.
297297func ValidDomain (domain string ) error {
298298 if strings .Count (domain , "*" ) <= 0 {
299- return ValidNonWildcardDomain (domain )
299+ return validNonWildcardDomain (domain )
300300 }
301301
302302 // Names containing more than one wildcard are invalid.
@@ -323,7 +323,7 @@ func ValidDomain(domain string) error {
323323 if baseDomain == icannTLD {
324324 return errICANNTLDWildcard
325325 }
326- return ValidNonWildcardDomain (baseDomain )
326+ return validNonWildcardDomain (baseDomain )
327327}
328328
329329// forbiddenMailDomains is a map of domain names we do not allow after the
@@ -351,7 +351,7 @@ func ValidEmail(address string) error {
351351 }
352352 splitEmail := strings .SplitN (email .Address , "@" , - 1 )
353353 domain := strings .ToLower (splitEmail [len (splitEmail )- 1 ])
354- err = ValidNonWildcardDomain (domain )
354+ err = validNonWildcardDomain (domain )
355355 if err != nil {
356356 return berrors .InvalidEmailError (
357357 "contact email %q has invalid domain : %s" ,
@@ -365,12 +365,69 @@ func ValidEmail(address string) error {
365365 return nil
366366}
367367
368+ // subError returns an appropriately typed error based on the input error
369+ func subError (name string , err error ) berrors.SubBoulderError {
370+ var bErr * berrors.BoulderError
371+ if errors .As (err , & bErr ) {
372+ return berrors.SubBoulderError {
373+ Identifier : identifier .DNSIdentifier (name ),
374+ BoulderError : bErr ,
375+ }
376+ } else {
377+ return berrors.SubBoulderError {
378+ Identifier : identifier .DNSIdentifier (name ),
379+ BoulderError : & berrors.BoulderError {
380+ Type : berrors .RejectedIdentifier ,
381+ Detail : err .Error (),
382+ },
383+ }
384+ }
385+ }
386+
368387// WillingToIssue determines whether the CA is willing to issue for the provided
369- // domains. It expects each domain to be lowercase to prevent mismatched cases
370- // breaking queries.
388+ // domain names.
389+ //
390+ // It checks the criteria checked by `WellFormedDomainNames`, and additionally checks
391+ // whether any domain is on a blocklist.
392+ //
393+ // If multiple domains are invalid, the error will contain suberrors specific to
394+ // each domain.
395+ //
396+ // Precondition: all input domain names must be in lowercase.
397+ func (pa * AuthorityImpl ) WillingToIssue (domains []string ) error {
398+ err := WellFormedDomainNames (domains )
399+ if err != nil {
400+ return err
401+ }
402+
403+ var subErrors []berrors.SubBoulderError
404+ for _ , domain := range domains {
405+ if strings .Count (domain , "*" ) > 0 {
406+ // The base domain is the wildcard request with the `*.` prefix removed
407+ baseDomain := strings .TrimPrefix (domain , "*." )
408+
409+ // The base domain can't be in the wildcard exact blocklist
410+ err = pa .checkWildcardHostList (baseDomain )
411+ if err != nil {
412+ subErrors = append (subErrors , subError (domain , err ))
413+ continue
414+ }
415+ }
416+
417+ // For both wildcard and non-wildcard domains, check whether any parent domain
418+ // name is on the regular blocklist.
419+ err := pa .checkHostLists (domain )
420+ if err != nil {
421+ subErrors = append (subErrors , subError (domain , err ))
422+ continue
423+ }
424+ }
425+ return combineSubErrors (subErrors )
426+ }
427+
428+ // WellFormedDomainNames returns an error if any of the provided domains do not meet these criteria:
371429//
372- // We place several criteria on domains we are willing to issue for:
373- // - MUST contain only bytes in the DNS hostname character set
430+ // - MUST contains only lowercase characters, numbers, hyphens, and dots
374431// - MUST NOT have more than maxLabels labels
375432// - MUST follow the DNS hostname syntax rules in RFC 1035 and RFC 2181
376433//
@@ -387,65 +444,21 @@ func ValidEmail(address string) error {
387444// - That the wildcard character is the leftmost label
388445// - That the wildcard label is not immediately adjacent to a top level ICANN
389446// TLD
390- // - That the wildcard wouldn't cover an exact blocklist entry (e.g. an exact
391- // blocklist entry for "foo.example.com" should prevent issuance for
392- // "*.example.com")
393447//
394- // If any of the domains are not valid then an error with suberrors specific to
395- // the rejected domains will be returned .
396- func ( pa * AuthorityImpl ) WillingToIssue (domains []string ) error {
448+ // If multiple domains are invalid, the error will contain suberrors specific to
449+ // each domain .
450+ func WellFormedDomainNames (domains []string ) error {
397451 var subErrors []berrors.SubBoulderError
398- appendSubError := func (name string , err error ) {
399- var bErr * berrors.BoulderError
400- if errors .As (err , & bErr ) {
401- subErrors = append (subErrors , berrors.SubBoulderError {
402- Identifier : identifier .DNSIdentifier (name ),
403- BoulderError : bErr ,
404- })
405- } else {
406- subErrors = append (subErrors , berrors.SubBoulderError {
407- Identifier : identifier .DNSIdentifier (name ),
408- BoulderError : & berrors.BoulderError {
409- Type : berrors .RejectedIdentifier ,
410- Detail : err .Error (),
411- },
412- })
413- }
414- }
415-
416452 for _ , domain := range domains {
417- if strings .Count (domain , "*" ) > 0 {
418- // Domain contains a wildcard, check that it is valid.
419- err := ValidDomain (domain )
420- if err != nil {
421- appendSubError (domain , err )
422- continue
423- }
424-
425- // The base domain is the wildcard request with the `*.` prefix removed
426- baseDomain := strings .TrimPrefix (domain , "*." )
427-
428- // The base domain can't be in the wildcard exact blocklist
429- err = pa .checkWildcardHostList (baseDomain )
430- if err != nil {
431- appendSubError (domain , err )
432- continue
433- }
434- } else {
435- // Validate that the domain is well-formed.
436- err := ValidNonWildcardDomain (domain )
437- if err != nil {
438- appendSubError (domain , err )
439- continue
440- }
441- }
442- // Require no match against hostname block lists
443- err := pa .checkHostLists (domain )
453+ err := ValidDomain (domain )
444454 if err != nil {
445- appendSubError (domain , err )
446- continue
455+ subErrors = append (subErrors , subError (domain , err ))
447456 }
448457 }
458+ return combineSubErrors (subErrors )
459+ }
460+
461+ func combineSubErrors (subErrors []berrors.SubBoulderError ) error {
449462 if len (subErrors ) > 0 {
450463 // If there was only one error, then use it as the top level error that is
451464 // returned.
@@ -478,7 +491,7 @@ func (pa *AuthorityImpl) checkWildcardHostList(domain string) error {
478491 pa .blocklistMu .RLock ()
479492 defer pa .blocklistMu .RUnlock ()
480493
481- if pa .blocklist == nil {
494+ if pa .wildcardExactBlocklist == nil {
482495 return fmt .Errorf ("Hostname policy not yet loaded." )
483496 }
484497
0 commit comments