github.com/letsencrypt/boulder@v0.20251208.0/policy/pa.go (about) 1 package policy 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "net/mail" 9 "net/netip" 10 "os" 11 "regexp" 12 "slices" 13 "strings" 14 "sync" 15 16 "golang.org/x/net/idna" 17 "golang.org/x/text/unicode/norm" 18 19 "github.com/letsencrypt/boulder/core" 20 berrors "github.com/letsencrypt/boulder/errors" 21 "github.com/letsencrypt/boulder/features" 22 "github.com/letsencrypt/boulder/iana" 23 "github.com/letsencrypt/boulder/identifier" 24 blog "github.com/letsencrypt/boulder/log" 25 "github.com/letsencrypt/boulder/strictyaml" 26 ) 27 28 // AuthorityImpl enforces CA policy decisions. 29 type AuthorityImpl struct { 30 log blog.Logger 31 32 domainBlocklist map[string]bool 33 fqdnBlocklist map[string]bool 34 wildcardFqdnBlocklist map[string]bool 35 ipPrefixBlocklist []netip.Prefix 36 blocklistMu sync.RWMutex 37 38 enabledChallenges map[core.AcmeChallenge]bool 39 enabledIdentifiers map[identifier.IdentifierType]bool 40 } 41 42 // New constructs a Policy Authority. 43 func New(identifierTypes map[identifier.IdentifierType]bool, challengeTypes map[core.AcmeChallenge]bool, log blog.Logger) (*AuthorityImpl, error) { 44 return &AuthorityImpl{ 45 log: log, 46 enabledChallenges: challengeTypes, 47 enabledIdentifiers: identifierTypes, 48 }, nil 49 } 50 51 // blockedIdentsPolicy is a struct holding lists of blocked identifiers. 52 type blockedIdentsPolicy struct { 53 // ExactBlockedNames is a list of Fully Qualified Domain Names (FQDNs). 54 // Issuance for names exactly matching an entry in the list will be 55 // forbidden. (e.g. `ExactBlockedNames` containing `www.example.com` will 56 // not block `example.com`, `mail.example.com`, or `dev.www.example.com`). 57 ExactBlockedNames []string `yaml:"ExactBlockedNames"` 58 59 // HighRiskBlockedNames is a list of domain names: like ExactBlockedNames 60 // except that issuance is blocked for subdomains as well. (e.g. 61 // BlockedNames containing `example.com` will block `www.example.com`). 62 // 63 // This list typically doesn't change with much regularity. 64 HighRiskBlockedNames []string `yaml:"HighRiskBlockedNames"` 65 66 // AdminBlockedNames operates the same as HighRiskBlockedNames but is 67 // changed with more frequency based on administrative blocks/revocations 68 // that are added over time above and beyond the high-risk domains. Managing 69 // these entries separately from HighRiskBlockedNames makes it easier to vet 70 // changes accurately. 71 AdminBlockedNames []string `yaml:"AdminBlockedNames"` 72 73 // AdminBlockedPrefixes is a list of IP address prefixes. All IP addresses 74 // contained within the prefix are blocked. 75 AdminBlockedPrefixes []string `yaml:"AdminBlockedPrefixes"` 76 } 77 78 // LoadIdentPolicyFile will load the given policy file, returning an error if it 79 // fails. 80 func (pa *AuthorityImpl) LoadIdentPolicyFile(f string) error { 81 configBytes, err := os.ReadFile(f) 82 if err != nil { 83 return err 84 } 85 hash := sha256.Sum256(configBytes) 86 pa.log.Infof("loading identifier policy, sha256: %s", hex.EncodeToString(hash[:])) 87 var policy blockedIdentsPolicy 88 err = strictyaml.Unmarshal(configBytes, &policy) 89 if err != nil { 90 return err 91 } 92 if len(policy.HighRiskBlockedNames) == 0 { 93 return fmt.Errorf("no entries in HighRiskBlockedNames") 94 } 95 if len(policy.ExactBlockedNames) == 0 { 96 return fmt.Errorf("no entries in ExactBlockedNames") 97 } 98 return pa.processIdentPolicy(policy) 99 } 100 101 // processIdentPolicy handles loading a new blockedIdentsPolicy into the PA. All 102 // of the policy.ExactBlockedNames will be added to the wildcardExactBlocklist 103 // by processIdentPolicy to ensure that wildcards for exact blocked names 104 // entries are forbidden. 105 func (pa *AuthorityImpl) processIdentPolicy(policy blockedIdentsPolicy) error { 106 nameMap := make(map[string]bool) 107 for _, v := range policy.HighRiskBlockedNames { 108 nameMap[v] = true 109 } 110 for _, v := range policy.AdminBlockedNames { 111 nameMap[v] = true 112 } 113 114 exactNameMap := make(map[string]bool) 115 wildcardNameMap := make(map[string]bool) 116 for _, v := range policy.ExactBlockedNames { 117 exactNameMap[v] = true 118 // Remove the leftmost label of the exact blocked names entry to make an exact 119 // wildcard block list entry that will prevent issuing a wildcard that would 120 // include the exact blocklist entry. e.g. if "highvalue.example.com" is on 121 // the exact blocklist we want "example.com" to be in the 122 // wildcardExactBlocklist so that "*.example.com" cannot be issued. 123 // 124 // First, split the domain into two parts: the first label and the rest of the domain. 125 parts := strings.SplitN(v, ".", 2) 126 // if there are less than 2 parts then this entry is malformed! There should 127 // at least be a "something." and a TLD like "com" 128 if len(parts) < 2 { 129 return fmt.Errorf( 130 "malformed ExactBlockedNames entry, only one label: %q", v) 131 } 132 // Add the second part, the domain minus the first label, to the 133 // wildcardNameMap to block issuance for `*.`+parts[1] 134 wildcardNameMap[parts[1]] = true 135 } 136 137 var prefixes []netip.Prefix 138 for _, p := range policy.AdminBlockedPrefixes { 139 prefix, err := netip.ParsePrefix(p) 140 if err != nil { 141 return fmt.Errorf( 142 "malformed AdminBlockedPrefixes entry, not a prefix: %q", p) 143 } 144 prefixes = append(prefixes, prefix) 145 } 146 147 pa.blocklistMu.Lock() 148 pa.domainBlocklist = nameMap 149 pa.fqdnBlocklist = exactNameMap 150 pa.wildcardFqdnBlocklist = wildcardNameMap 151 pa.ipPrefixBlocklist = prefixes 152 pa.blocklistMu.Unlock() 153 return nil 154 } 155 156 // The values of maxDNSIdentifierLength, maxLabelLength and maxLabels are hard coded 157 // into the error messages errNameTooLong, errLabelTooLong and errTooManyLabels. 158 // If their values change, the related error messages should be updated. 159 160 const ( 161 maxLabels = 10 162 163 // RFC 1034 says DNS labels have a max of 63 octets, and names have a max of 255 164 // octets: https://tools.ietf.org/html/rfc1035#page-10. Since two of those octets 165 // are taken up by the leading length byte and the trailing root period the actual 166 // max length becomes 253. 167 maxLabelLength = 63 168 maxDNSIdentifierLength = 253 169 ) 170 171 var dnsLabelCharacterRegexp = regexp.MustCompile("^[a-z0-9-]+$") 172 173 func isDNSCharacter(ch byte) bool { 174 return ('a' <= ch && ch <= 'z') || 175 ('A' <= ch && ch <= 'Z') || 176 ('0' <= ch && ch <= '9') || 177 ch == '.' || ch == '-' 178 } 179 180 // In these error messages: 181 // 253 is the value of maxDNSIdentifierLength 182 // 63 is the value of maxLabelLength 183 // 10 is the value of maxLabels 184 // If these values change, the related error messages should be updated. 185 186 var ( 187 errNonPublic = berrors.MalformedError("Domain name does not end with a valid public suffix (TLD)") 188 errICANNTLD = berrors.MalformedError("Domain name is an ICANN TLD") 189 errPolicyForbidden = berrors.RejectedIdentifierError("The ACME server refuses to issue a certificate for this domain name, because it is forbidden by policy") 190 errInvalidDNSCharacter = berrors.MalformedError("Domain name contains an invalid character") 191 errNameTooLong = berrors.MalformedError("Domain name is longer than 253 bytes") 192 errIPAddressInDNS = berrors.MalformedError("Identifier type is DNS but value is an IP address") 193 errIPInvalid = berrors.MalformedError("IP address is invalid") 194 errTooManyLabels = berrors.MalformedError("Domain name has more than 10 labels (parts)") 195 errEmptyIdentifier = berrors.MalformedError("Identifier value (name) is empty") 196 errNameEndsInDot = berrors.MalformedError("Domain name ends in a dot") 197 errTooFewLabels = berrors.MalformedError("Domain name needs at least one dot") 198 errLabelTooShort = berrors.MalformedError("Domain name can not have two dots in a row") 199 errLabelTooLong = berrors.MalformedError("Domain has a label (component between dots) longer than 63 bytes") 200 errMalformedIDN = berrors.MalformedError("Domain name contains malformed punycode") 201 errInvalidRLDH = berrors.RejectedIdentifierError("Domain name contains an invalid label in a reserved format (R-LDH: '??--')") 202 errTooManyWildcards = berrors.MalformedError("Domain name has more than one wildcard") 203 errMalformedWildcard = berrors.MalformedError("Domain name contains an invalid wildcard. A wildcard is only permitted before the first dot in a domain name") 204 errICANNTLDWildcard = berrors.MalformedError("Domain name is a wildcard for an ICANN TLD") 205 errWildcardNotSupported = berrors.MalformedError("Wildcard domain names are not supported") 206 errUnsupportedIdent = berrors.MalformedError("Invalid identifier type") 207 ) 208 209 // validNonWildcardDomain checks that a domain isn't: 210 // - empty 211 // - prefixed with the wildcard label `*.` 212 // - made of invalid DNS characters 213 // - longer than the maxDNSIdentifierLength 214 // - an IPv4 or IPv6 address 215 // - suffixed with just "." 216 // - made of too many DNS labels 217 // - made of any invalid DNS labels 218 // - suffixed with something other than an IANA registered TLD 219 // - exactly equal to an IANA registered TLD 220 // 221 // It does NOT ensure that the domain is absent from any PA blocked lists. 222 func validNonWildcardDomain(domain string) error { 223 if domain == "" { 224 return errEmptyIdentifier 225 } 226 227 if strings.HasPrefix(domain, "*.") { 228 return errWildcardNotSupported 229 } 230 231 for _, ch := range []byte(domain) { 232 if !isDNSCharacter(ch) { 233 return errInvalidDNSCharacter 234 } 235 } 236 237 if len(domain) > maxDNSIdentifierLength { 238 return errNameTooLong 239 } 240 241 _, err := netip.ParseAddr(domain) 242 if err == nil { 243 return errIPAddressInDNS 244 } 245 246 if strings.HasSuffix(domain, ".") { 247 return errNameEndsInDot 248 } 249 250 labels := strings.Split(domain, ".") 251 if len(labels) > maxLabels { 252 return errTooManyLabels 253 } 254 if len(labels) < 2 { 255 return errTooFewLabels 256 } 257 for _, label := range labels { 258 // Check that this is a valid LDH Label: "A string consisting of ASCII 259 // letters, digits, and the hyphen with the further restriction that the 260 // hyphen cannot appear at the beginning or end of the string. Like all DNS 261 // labels, its total length must not exceed 63 octets." (RFC 5890, 2.3.1) 262 if len(label) < 1 { 263 return errLabelTooShort 264 } 265 if len(label) > maxLabelLength { 266 return errLabelTooLong 267 } 268 if !dnsLabelCharacterRegexp.MatchString(label) { 269 return errInvalidDNSCharacter 270 } 271 if label[0] == '-' || label[len(label)-1] == '-' { 272 return errInvalidDNSCharacter 273 } 274 275 // Check if this is a Reserved LDH Label: "[has] the property that they 276 // contain "--" in the third and fourth characters but which otherwise 277 // conform to LDH label rules." (RFC 5890, 2.3.1) 278 if len(label) >= 4 && label[2:4] == "--" { 279 // Check if this is an XN-Label: "labels that begin with the prefix "xn--" 280 // (case independent), but otherwise conform to the rules for LDH labels." 281 // (RFC 5890, 2.3.1) 282 if label[0:2] != "xn" { 283 return errInvalidRLDH 284 } 285 286 // Check if this is a P-Label: "A XN-Label that contains valid output of 287 // the Punycode algorithm (as defined in RFC 3492, Section 6.3) from the 288 // fifth and subsequent positions." (Baseline Requirements, 1.6.1) 289 ulabel, err := idna.ToUnicode(label) 290 if err != nil { 291 return errMalformedIDN 292 } 293 if !norm.NFC.IsNormalString(ulabel) { 294 return errMalformedIDN 295 } 296 } 297 } 298 299 // Names must end in an ICANN TLD, but they must not be equal to an ICANN TLD. 300 icannTLD, err := iana.ExtractSuffix(domain) 301 if err != nil { 302 return errNonPublic 303 } 304 if icannTLD == domain { 305 return errICANNTLD 306 } 307 308 return nil 309 } 310 311 // ValidDomain checks that a domain is valid and that it doesn't contain any 312 // invalid wildcard characters. It does NOT ensure that the domain is absent 313 // from any PA blocked lists. 314 func ValidDomain(domain string) error { 315 if strings.Count(domain, "*") <= 0 { 316 return validNonWildcardDomain(domain) 317 } 318 319 // Names containing more than one wildcard are invalid. 320 if strings.Count(domain, "*") > 1 { 321 return errTooManyWildcards 322 } 323 324 // If the domain has a wildcard character, but it isn't the first most 325 // label of the domain name then the wildcard domain is malformed 326 if !strings.HasPrefix(domain, "*.") { 327 return errMalformedWildcard 328 } 329 330 // The base domain is the wildcard request with the `*.` prefix removed 331 baseDomain := strings.TrimPrefix(domain, "*.") 332 333 // Names must end in an ICANN TLD, but they must not be equal to an ICANN TLD. 334 icannTLD, err := iana.ExtractSuffix(baseDomain) 335 if err != nil { 336 return errNonPublic 337 } 338 // Names must have a non-wildcard label immediately adjacent to the ICANN 339 // TLD. No `*.com`! 340 if baseDomain == icannTLD { 341 return errICANNTLDWildcard 342 } 343 return validNonWildcardDomain(baseDomain) 344 } 345 346 // ValidIP checks that an IP address: 347 // - isn't empty 348 // - is an IPv4 or IPv6 address 349 // - doesn't contain a scope zone (RFC 4007) 350 // - isn't in an IANA special-purpose address registry 351 // 352 // It does NOT ensure that the IP address is absent from any PA blocked lists. 353 func ValidIP(ip string) error { 354 if ip == "" { 355 return errEmptyIdentifier 356 } 357 358 // Check the output of netip.Addr.String(), to ensure the input complied 359 // with RFC 8738, Sec. 3. ("The identifier value MUST contain the textual 360 // form of the address as defined in RFC 1123, Sec. 2.1 for IPv4 and in RFC 361 // 5952, Sec. 4 for IPv6.") ParseAddr() will accept a non-compliant but 362 // otherwise valid string; String() will output a compliant string. 363 parsedIP, err := netip.ParseAddr(ip) 364 if err != nil || parsedIP.WithZone("").String() != ip { 365 return errIPInvalid 366 } 367 368 return iana.IsReservedAddr(parsedIP) 369 } 370 371 // forbiddenMailDomains is a map of domain names we do not allow after the 372 // @ symbol in contact mailto addresses. These are frequently used when 373 // copy-pasting example configurations and would not result in expiration 374 // messages and subscriber communications reaching the user that created the 375 // registration if allowed. 376 var forbiddenMailDomains = map[string]bool{ 377 // https://tools.ietf.org/html/rfc2606#section-3 378 "example.com": true, 379 "example.net": true, 380 "example.org": true, 381 } 382 383 // ValidEmail returns an error if the input doesn't parse as an email address, 384 // the domain isn't a valid hostname in Preferred Name Syntax, or its on the 385 // list of domains forbidden for mail (because they are often used in examples). 386 func ValidEmail(address string) error { 387 email, err := mail.ParseAddress(address) 388 if err != nil { 389 return berrors.InvalidEmailError("unable to parse email address") 390 } 391 splitEmail := strings.SplitN(email.Address, "@", -1) 392 domain := strings.ToLower(splitEmail[len(splitEmail)-1]) 393 err = validNonWildcardDomain(domain) 394 if err != nil { 395 return berrors.InvalidEmailError("contact email has invalid domain: %s", err) 396 } 397 if forbiddenMailDomains[domain] { 398 // We're okay including the domain in the error message here because this 399 // case occurs only for a small block-list of domains listed above. 400 return berrors.InvalidEmailError("contact email has forbidden domain %q", domain) 401 } 402 return nil 403 } 404 405 // subError returns an appropriately typed error based on the input error 406 func subError(ident identifier.ACMEIdentifier, err error) berrors.SubBoulderError { 407 var bErr *berrors.BoulderError 408 if errors.As(err, &bErr) { 409 return berrors.SubBoulderError{ 410 Identifier: ident, 411 BoulderError: bErr, 412 } 413 } else { 414 return berrors.SubBoulderError{ 415 Identifier: ident, 416 BoulderError: &berrors.BoulderError{ 417 Type: berrors.RejectedIdentifier, 418 Detail: err.Error(), 419 }, 420 } 421 } 422 } 423 424 // WillingToIssue determines whether the CA is willing to issue for the provided 425 // identifiers. 426 // 427 // It checks the criteria checked by `WellFormedIdentifiers`, and additionally 428 // checks whether any identifier is on a blocklist. 429 // 430 // If multiple identifiers are invalid, the error will contain suberrors 431 // specific to each identifier. 432 // 433 // Precondition: all input identifier values must be in lowercase. 434 func (pa *AuthorityImpl) WillingToIssue(idents identifier.ACMEIdentifiers) error { 435 err := WellFormedIdentifiers(idents) 436 if err != nil { 437 return err 438 } 439 440 var subErrors []berrors.SubBoulderError 441 for _, ident := range idents { 442 if !pa.IdentifierTypeEnabled(ident.Type) { 443 subErrors = append(subErrors, subError(ident, berrors.RejectedIdentifierError("The ACME server has disabled this identifier type"))) 444 continue 445 } 446 447 // Wildcard DNS identifiers are checked against an additional blocklist. 448 if ident.Type == identifier.TypeDNS && strings.Count(ident.Value, "*") > 0 { 449 // The base domain is the wildcard request with the `*.` prefix removed 450 baseDomain := strings.TrimPrefix(ident.Value, "*.") 451 452 // The base domain can't be in the wildcard exact blocklist 453 err = pa.checkWildcardBlocklist(baseDomain) 454 if err != nil { 455 subErrors = append(subErrors, subError(ident, err)) 456 continue 457 } 458 } 459 460 // For all identifier types, check whether the identifier value is 461 // covered by the regular blocklists. 462 err := pa.checkBlocklists(ident) 463 if err != nil { 464 subErrors = append(subErrors, subError(ident, err)) 465 continue 466 } 467 } 468 return combineSubErrors(subErrors) 469 } 470 471 // WellFormedIdentifiers returns an error if any of the provided identifiers do 472 // not meet these criteria: 473 // 474 // For DNS identifiers: 475 // - MUST contains only lowercase characters, numbers, hyphens, and dots 476 // - MUST NOT have more than maxLabels labels 477 // - MUST follow the DNS hostname syntax rules in RFC 1035 and RFC 2181 478 // 479 // In particular, DNS identifiers: 480 // - MUST NOT contain underscores 481 // - MUST NOT match the syntax of an IP address 482 // - MUST end in a public suffix 483 // - MUST have at least one label in addition to the public suffix 484 // - MUST NOT be a label-wise suffix match for a name on the block list, 485 // where comparison is case-independent (normalized to lower case) 486 // 487 // If a DNS identifier contains a *, we additionally require: 488 // - There is at most one `*` wildcard character 489 // - That the wildcard character is the leftmost label 490 // - That the wildcard label is not immediately adjacent to a top level ICANN 491 // TLD 492 // 493 // For IP identifiers: 494 // - MUST match the syntax of an IP address 495 // - MUST NOT contain a scope zone (RFC 4007) 496 // - MUST NOT be in an IANA special-purpose address registry 497 // 498 // If multiple identifiers are invalid, the error will contain suberrors 499 // specific to each identifier. 500 func WellFormedIdentifiers(idents identifier.ACMEIdentifiers) error { 501 var subErrors []berrors.SubBoulderError 502 for _, ident := range idents { 503 switch ident.Type { 504 case identifier.TypeDNS: 505 err := ValidDomain(ident.Value) 506 if err != nil { 507 subErrors = append(subErrors, subError(ident, err)) 508 } 509 case identifier.TypeIP: 510 err := ValidIP(ident.Value) 511 if err != nil { 512 subErrors = append(subErrors, subError(ident, err)) 513 } 514 default: 515 subErrors = append(subErrors, subError(ident, errUnsupportedIdent)) 516 } 517 } 518 return combineSubErrors(subErrors) 519 } 520 521 func combineSubErrors(subErrors []berrors.SubBoulderError) error { 522 if len(subErrors) > 0 { 523 // If there was only one error, then use it as the top level error that is 524 // returned. 525 if len(subErrors) == 1 { 526 return berrors.RejectedIdentifierError( 527 "Cannot issue for %q: %s", 528 subErrors[0].Identifier.Value, 529 subErrors[0].BoulderError.Detail, 530 ) 531 } 532 533 detail := fmt.Sprintf( 534 "Cannot issue for %q: %s (and %d more problems. Refer to sub-problems for more information.)", 535 subErrors[0].Identifier.Value, 536 subErrors[0].BoulderError.Detail, 537 len(subErrors)-1, 538 ) 539 return (&berrors.BoulderError{ 540 Type: berrors.RejectedIdentifier, 541 Detail: detail, 542 }).WithSubErrors(subErrors) 543 } 544 return nil 545 } 546 547 // checkWildcardBlocklist checks the wildcardExactBlocklist for a given domain. 548 // If the domain is not present on the list nil is returned, otherwise 549 // errPolicyForbidden is returned. 550 func (pa *AuthorityImpl) checkWildcardBlocklist(domain string) error { 551 pa.blocklistMu.RLock() 552 defer pa.blocklistMu.RUnlock() 553 554 if pa.wildcardFqdnBlocklist == nil { 555 return fmt.Errorf("identifier policy not yet loaded") 556 } 557 558 if pa.wildcardFqdnBlocklist[domain] { 559 return errPolicyForbidden 560 } 561 562 return nil 563 } 564 565 func (pa *AuthorityImpl) checkBlocklists(ident identifier.ACMEIdentifier) error { 566 pa.blocklistMu.RLock() 567 defer pa.blocklistMu.RUnlock() 568 569 if pa.domainBlocklist == nil { 570 return fmt.Errorf("identifier policy not yet loaded") 571 } 572 573 switch ident.Type { 574 case identifier.TypeDNS: 575 labels := strings.Split(ident.Value, ".") 576 for i := range labels { 577 joined := strings.Join(labels[i:], ".") 578 if pa.domainBlocklist[joined] { 579 return errPolicyForbidden 580 } 581 } 582 583 if pa.fqdnBlocklist[ident.Value] { 584 return errPolicyForbidden 585 } 586 case identifier.TypeIP: 587 ip, err := netip.ParseAddr(ident.Value) 588 if err != nil { 589 return errIPInvalid 590 } 591 for _, prefix := range pa.ipPrefixBlocklist { 592 if prefix.Contains(ip.WithZone("")) { 593 return errPolicyForbidden 594 } 595 } 596 default: 597 return errUnsupportedIdent 598 } 599 return nil 600 } 601 602 // ChallengeTypesFor determines which challenge types are acceptable for the 603 // given identifier. This determination is made purely based on the identifier, 604 // and not based on which challenge types are enabled, so that challenge type 605 // filtering can happen dynamically at request rather than being set in stone 606 // at creation time. 607 func (pa *AuthorityImpl) ChallengeTypesFor(ident identifier.ACMEIdentifier) ([]core.AcmeChallenge, error) { 608 switch ident.Type { 609 case identifier.TypeDNS: 610 // If the identifier is for a DNS wildcard name we only provide DNS-01 611 // or DNS-ACCOUNT-01 challenges, to comply with the BRs Sections 3.2.2.4.19 612 // and 3.2.2.4.20 stating that ACME HTTP-01 and TLS-ALPN-01 are not 613 // suitable for validating Wildcard Domains. 614 if strings.HasPrefix(ident.Value, "*.") { 615 challenges := []core.AcmeChallenge{core.ChallengeTypeDNS01} 616 if features.Get().DNSAccount01Enabled { 617 challenges = append(challenges, core.ChallengeTypeDNSAccount01) 618 } 619 return challenges, nil 620 } 621 622 // Return all challenge types we support for non-wildcard DNS identifiers. 623 challenges := []core.AcmeChallenge{ 624 core.ChallengeTypeHTTP01, 625 core.ChallengeTypeDNS01, 626 core.ChallengeTypeTLSALPN01, 627 } 628 if features.Get().DNSAccount01Enabled { 629 challenges = append(challenges, core.ChallengeTypeDNSAccount01) 630 } 631 return challenges, nil 632 case identifier.TypeIP: 633 // Only HTTP-01 and TLS-ALPN-01 are suitable for IP address identifiers 634 // per RFC 8738, Sec. 4. 635 return []core.AcmeChallenge{ 636 core.ChallengeTypeHTTP01, 637 core.ChallengeTypeTLSALPN01, 638 }, nil 639 default: 640 // Otherwise return an error because we don't support any challenges for this 641 // identifier type. 642 return nil, fmt.Errorf("unrecognized identifier type %q", ident.Type) 643 } 644 } 645 646 // ChallengeTypeEnabled returns whether the specified challenge type is enabled 647 func (pa *AuthorityImpl) ChallengeTypeEnabled(t core.AcmeChallenge) bool { 648 pa.blocklistMu.RLock() 649 defer pa.blocklistMu.RUnlock() 650 return pa.enabledChallenges[t] 651 } 652 653 // CheckAuthzChallenges determines that an authorization was fulfilled by a 654 // challenge that is currently enabled and was appropriate for the kind of 655 // identifier in the authorization. 656 func (pa *AuthorityImpl) CheckAuthzChallenges(authz *core.Authorization) error { 657 chall, err := authz.SolvedBy() 658 if err != nil { 659 return err 660 } 661 662 if !pa.ChallengeTypeEnabled(chall) { 663 return errors.New("authorization fulfilled by disabled challenge type") 664 } 665 666 challTypes, err := pa.ChallengeTypesFor(authz.Identifier) 667 if err != nil { 668 return err 669 } 670 671 if !slices.Contains(challTypes, chall) { 672 return errors.New("authorization fulfilled by inapplicable challenge type") 673 } 674 675 return nil 676 } 677 678 // IdentifierTypeEnabled returns whether the specified identifier type is enabled 679 func (pa *AuthorityImpl) IdentifierTypeEnabled(t identifier.IdentifierType) bool { 680 pa.blocklistMu.RLock() 681 defer pa.blocklistMu.RUnlock() 682 return pa.enabledIdentifiers[t] 683 }