diff --git a/gui/apply-boulder b/gui/apply-boulder
index 92eba0a..dc43038 100755
--- a/gui/apply-boulder
+++ b/gui/apply-boulder
@@ -86,6 +86,15 @@ if [ "$PKI_DOMAIN_MODE" == "lockdown" ] && [ "$PKI_LOCKDOWN_DOMAINS" != "" ]; th
for d in $(echo $PKI_LOCKDOWN_DOMAINS | sed -e "s/\\\r/ /g" | sed -e "s/\\\n/ /g" | tr '\r' ' '); do
echo " - \"$d\"" >> hostname-policy.yaml
done
+
+ allow_public="false"
+ ld_public_contacts=$(grep ld_public_contacts $dataDir/config.json | grep true || echo "")
+ if [ "$ld_public_contacts" != "" ]; then
+ allow_public="true"
+ fi
+
+ echo >> hostname-policy.yaml
+ echo "LockdownAllowPublicContacts: $allow_public" >> hostname-policy.yaml
fi
if [ "$PKI_DOMAIN_MODE" == "whitelist" ] && [ "$PKI_WHITELIST_DOMAINS" != "" ]; then
echo >> hostname-policy.yaml
diff --git a/gui/main.go b/gui/main.go
index 8ca3982..d5213b3 100644
--- a/gui/main.go
+++ b/gui/main.go
@@ -176,6 +176,7 @@ type SetupConfig struct {
DomainMode string
LockdownDomains string
WhitelistDomains string
+ LDPublicContacts bool
ExtendedTimeout bool
RequestBase string
Errors map[string]string
@@ -666,6 +667,7 @@ func _configUpdateHandler(w http.ResponseWriter, r *http.Request) {
DomainMode: r.Form.Get("domain_mode"),
LockdownDomains: r.Form.Get("lockdown_domains"),
WhitelistDomains: r.Form.Get("whitelist_domains"),
+ LDPublicContacts: (r.Form.Get("ld_public_contacts") == "true"),
ExtendedTimeout: (r.Form.Get("extended_timeout") == "true"),
}
@@ -715,6 +717,11 @@ func _configUpdateHandler(w http.ResponseWriter, r *http.Request) {
delta = true
viper.Set("labca.lockdown", cfg.LockdownDomains)
}
+
+ if cfg.LDPublicContacts != viper.GetBool("labca.ld_public_contacts") {
+ delta = true
+ viper.Set("labca.ld_public_contacts", cfg.LDPublicContacts)
+ }
}
if domainMode == "whitelist" {
if cfg.WhitelistDomains != viper.GetString("labca.whitelist") {
@@ -1585,6 +1592,7 @@ func _manageGet(w http.ResponseWriter, r *http.Request) {
manageData["DomainMode"] = domainMode
if domainMode == "lockdown" {
manageData["LockdownDomains"] = viper.GetString("labca.lockdown")
+ manageData["LDPublicContacts"] = viper.GetBool("labca.ld_public_contacts")
}
if domainMode == "whitelist" {
manageData["WhitelistDomains"] = viper.GetString("labca.whitelist")
@@ -2536,6 +2544,7 @@ func _setupBaseConfig(w http.ResponseWriter, r *http.Request) bool {
DomainMode: "lockdown",
LockdownDomains: domain,
WhitelistDomains: domain,
+ LDPublicContacts: true,
RequestBase: r.Header.Get("X-Request-Base"),
}
@@ -2553,6 +2562,7 @@ func _setupBaseConfig(w http.ResponseWriter, r *http.Request) bool {
DomainMode: r.Form.Get("domain_mode"),
LockdownDomains: r.Form.Get("lockdown_domains"),
WhitelistDomains: r.Form.Get("whitelist_domains"),
+ LDPublicContacts: (r.Form.Get("ld_public_contacts") == "true"),
RequestBase: r.Header.Get("X-Request-Base"),
}
@@ -2571,6 +2581,7 @@ func _setupBaseConfig(w http.ResponseWriter, r *http.Request) bool {
viper.Set("labca.domain_mode", cfg.DomainMode)
if cfg.DomainMode == "lockdown" {
viper.Set("labca.lockdown", cfg.LockdownDomains)
+ viper.Set("labca.ld_public_contacts", cfg.LDPublicContacts)
}
if cfg.DomainMode == "whitelist" {
viper.Set("labca.whitelist", cfg.WhitelistDomains)
diff --git a/gui/static/js/labca.js b/gui/static/js/labca.js
index 326c0af..25f51de 100644
--- a/gui/static/js/labca.js
+++ b/gui/static/js/labca.js
@@ -165,8 +165,10 @@ $(function() {
if ($("input[type=radio]#whitelist").prop('checked') || $("input[type=radio]#standard").prop('checked') ) {
$("#domain_mode_warning").show();
+ $("#ld_options").hide();
} else {
$("#domain_mode_warning").hide();
+ $("#ld_options").show();
}
}
@@ -175,6 +177,7 @@ $(function() {
});
$("#domain_mode_warning").hide();
+ $("#ld_options").show();
radioDisable();
diff --git a/gui/templates/views/manage.tmpl b/gui/templates/views/manage.tmpl
index 04dd695..a4a0d18 100644
--- a/gui/templates/views/manage.tmpl
+++ b/gui/templates/views/manage.tmpl
@@ -295,17 +295,26 @@
- Standard - any official domains
+ Standard - any official domains
+
+
+
+
+ Still allow all public domain names in contact email addresses when creating new ACME accounts
+
+
If you see timeout related errors on the Dashboard / Audit Log, try checking this box.
+
+

Are you sure? This facilitates man-in-the-middle attacks!
+
@@ -1001,6 +1010,7 @@
domain_mode: ($("#standard").prop('checked') ? 'standard' : ($("#whitelist").prop('checked') ? 'whitelist' : 'lockdown')),
lockdown_domains: $("#lockdown_domains").val(),
whitelist_domains: $("#whitelist_domains").val(),
+ ld_public_contacts: $("#ld_public_contacts").prop("checked"),
extended_timeout: $("#extended_timeout").prop("checked"),
},
})
diff --git a/gui/templates/views/setup.tmpl b/gui/templates/views/setup.tmpl
index 44b5064..2fc610a 100644
--- a/gui/templates/views/setup.tmpl
+++ b/gui/templates/views/setup.tmpl
@@ -37,11 +37,18 @@
{{ . }}
{{ end }}
- Standard - any official domains
+ Standard - any official domains
+
+
+
+
+ Still allow all public domain names in contact email addresses when creating new ACME accounts
+
+
+

Are you sure? This facilitates man-in-the-middle attacks!
{{end}}
diff --git a/patches/policy_pa.patch b/patches/policy_pa.patch
index 39d26ca..4288a17 100644
--- a/patches/policy_pa.patch
+++ b/patches/policy_pa.patch
@@ -1,27 +1,29 @@
diff --git a/policy/pa.go b/policy/pa.go
-index d872d5cbe..49daeb7d4 100644
+index d872d5cbe..faa052d6c 100644
--- a/policy/pa.go
+++ b/policy/pa.go
-@@ -32,6 +32,8 @@ type AuthorityImpl struct {
+@@ -32,6 +32,9 @@ type AuthorityImpl struct {
blocklist map[string]bool
exactBlocklist map[string]bool
wildcardExactBlocklist map[string]bool
+ whitelist map[string]bool
+ lockdown map[string]bool
++ ldPublicContacts bool
blocklistMu sync.RWMutex
enabledChallenges map[core.AcmeChallenge]bool
-@@ -72,6 +74,9 @@ type blockedNamesPolicy struct {
+@@ -72,6 +75,10 @@ type blockedNamesPolicy struct {
// time above and beyond the high-risk domains. Managing these entries separately
// from HighRiskBlockedNames makes it easier to vet changes accurately.
AdminBlockedNames []string `yaml:"AdminBlockedNames"`
+
-+ Whitelist []string `yaml:"Whitelist"`
-+ Lockdown []string `yaml:"Lockdown"`
++ Whitelist []string `yaml:"Whitelist"`
++ Lockdown []string `yaml:"Lockdown"`
++ LockdownAllowPublicContacts bool `yaml:"LockdownAllowPublicContacts"`
}
// LoadHostnamePolicyFile will load the given policy file, returning an error if
-@@ -131,10 +136,20 @@ func (pa *AuthorityImpl) processHostnamePolicy(policy blockedNamesPolicy) error
+@@ -131,10 +138,21 @@ func (pa *AuthorityImpl) processHostnamePolicy(policy blockedNamesPolicy) error
// wildcardNameMap to block issuance for `*.`+parts[1]
wildcardNameMap[parts[1]] = true
}
@@ -39,23 +41,35 @@ index d872d5cbe..49daeb7d4 100644
pa.wildcardExactBlocklist = wildcardNameMap
+ pa.whitelist = whiteMap
+ pa.lockdown = lockMap
++ pa.ldPublicContacts = policy.LockdownAllowPublicContacts
pa.blocklistMu.Unlock()
return nil
}
-@@ -203,7 +218,7 @@ var (
+@@ -203,7 +221,7 @@ var (
// - exactly equal to an IANA registered TLD
//
// It does NOT ensure that the domain is absent from any PA blocked lists.
-func ValidNonWildcardDomain(domain string) error {
-+func (pa *AuthorityImpl) ValidNonWildcardDomain(domain string) error {
++func (pa *AuthorityImpl) ValidNonWildcardDomain(domain string, isContact bool) error {
if domain == "" {
return errEmptyName
}
-@@ -279,6 +294,14 @@ func ValidNonWildcardDomain(domain string) error {
+@@ -235,7 +253,9 @@ func ValidNonWildcardDomain(domain string) error {
+ return errTooManyLabels
+ }
+ if len(labels) < 2 {
+- return errTooFewLabels
++ if !pa.lockdown[domain] && !pa.whitelist[domain] {
++ return errTooFewLabels
++ }
+ }
+ for _, label := range labels {
+ // Check that this is a valid LDH Label: "A string consisting of ASCII
+@@ -279,6 +299,14 @@ func ValidNonWildcardDomain(domain string) error {
}
}
-+ ok, err := pa.checkWhitelist(domain)
++ ok, err := pa.checkWhitelist(domain, isContact)
+ if err != nil {
+ return err
+ }
@@ -66,7 +80,7 @@ index d872d5cbe..49daeb7d4 100644
// Names must end in an ICANN TLD, but they must not be equal to an ICANN TLD.
icannTLD, err := iana.ExtractSuffix(domain)
if err != nil {
-@@ -294,9 +317,9 @@ func ValidNonWildcardDomain(domain string) error {
+@@ -294,9 +322,9 @@ func ValidNonWildcardDomain(domain string) error {
// ValidDomain checks that a domain is valid and that it doesn't contain any
// invalid wildcard characters. It does NOT ensure that the domain is absent
// from any PA blocked lists.
@@ -74,20 +88,29 @@ index d872d5cbe..49daeb7d4 100644
+func (pa *AuthorityImpl) ValidDomain(domain string) error {
if strings.Count(domain, "*") <= 0 {
- return ValidNonWildcardDomain(domain)
-+ return pa.ValidNonWildcardDomain(domain)
++ return pa.ValidNonWildcardDomain(domain, false)
}
// Names containing more than one wildcard are invalid.
-@@ -323,7 +346,7 @@ func ValidDomain(domain string) error {
+@@ -315,7 +343,7 @@ func ValidDomain(domain string) error {
+
+ // Names must end in an ICANN TLD, but they must not be equal to an ICANN TLD.
+ icannTLD, err := iana.ExtractSuffix(baseDomain)
+- if err != nil {
++ if err != nil && !pa.lockdown[baseDomain] && !pa.whitelist[baseDomain] {
+ return errNonPublic
+ }
+ // Names must have a non-wildcard label immediately adjacent to the ICANN
+@@ -323,7 +351,7 @@ func ValidDomain(domain string) error {
if baseDomain == icannTLD {
return errICANNTLDWildcard
}
- return ValidNonWildcardDomain(baseDomain)
-+ return pa.ValidNonWildcardDomain(baseDomain)
++ return pa.ValidNonWildcardDomain(baseDomain, false)
}
// forbiddenMailDomains is a map of domain names we do not allow after the
-@@ -341,7 +364,7 @@ var forbiddenMailDomains = map[string]bool{
+@@ -341,7 +369,7 @@ var forbiddenMailDomains = map[string]bool{
// ValidEmail returns an error if the input doesn't parse as an email address,
// the domain isn't a valid hostname in Preferred Name Syntax, or its on the
// list of domains forbidden for mail (because they are often used in examples).
@@ -96,16 +119,16 @@ index d872d5cbe..49daeb7d4 100644
email, err := mail.ParseAddress(address)
if err != nil {
if len(address) > 254 {
-@@ -351,7 +374,7 @@ func ValidEmail(address string) error {
+@@ -351,7 +379,7 @@ func ValidEmail(address string) error {
}
splitEmail := strings.SplitN(email.Address, "@", -1)
domain := strings.ToLower(splitEmail[len(splitEmail)-1])
- err = ValidNonWildcardDomain(domain)
-+ err = pa.ValidNonWildcardDomain(domain)
++ err = pa.ValidNonWildcardDomain(domain, true)
if err != nil {
return berrors.InvalidEmailError(
"contact email %q has invalid domain : %s",
-@@ -416,7 +439,7 @@ func (pa *AuthorityImpl) WillingToIssue(domains []string) error {
+@@ -416,7 +444,7 @@ func (pa *AuthorityImpl) WillingToIssue(domains []string) error {
for _, domain := range domains {
if strings.Count(domain, "*") > 0 {
// Domain contains a wildcard, check that it is valid.
@@ -114,28 +137,28 @@ index d872d5cbe..49daeb7d4 100644
if err != nil {
appendSubError(domain, err)
continue
-@@ -433,12 +456,15 @@ func (pa *AuthorityImpl) WillingToIssue(domains []string) error {
+@@ -433,12 +461,15 @@ func (pa *AuthorityImpl) WillingToIssue(domains []string) error {
}
} else {
// Validate that the domain is well-formed.
- err := ValidNonWildcardDomain(domain)
-+ err := pa.ValidNonWildcardDomain(domain)
++ err := pa.ValidNonWildcardDomain(domain, false)
if err != nil {
appendSubError(domain, err)
continue
}
}
-+ if ok, _ := pa.checkWhitelist(domain); ok {
++ if ok, _ := pa.checkWhitelist(domain, false); ok {
+ return nil
+ }
// Require no match against hostname block lists
err := pa.checkHostLists(domain)
if err != nil {
-@@ -471,6 +497,31 @@ func (pa *AuthorityImpl) WillingToIssue(domains []string) error {
+@@ -471,6 +502,34 @@ func (pa *AuthorityImpl) WillingToIssue(domains []string) error {
return nil
}
-+func (pa *AuthorityImpl) checkWhitelist(domain string) (bool, error) {
++func (pa *AuthorityImpl) checkWhitelist(domain string, isContact bool) (bool, error) {
+ pa.blocklistMu.RLock()
+ defer pa.blocklistMu.RUnlock()
+
@@ -152,6 +175,9 @@ index d872d5cbe..49daeb7d4 100644
+ }
+
+ if len(pa.lockdown) > 0 {
++ if isContact && pa.ldPublicContacts {
++ return false, nil
++ }
+ // In Lockdown mode, the domain MUST be in the list, so return an error if not found
+ return false, errPolicyForbidden
+ } else {
@@ -163,3 +189,13 @@ index d872d5cbe..49daeb7d4 100644
// checkWildcardHostList checks the wildcardExactBlocklist for a given domain.
// If the domain is not present on the list nil is returned, otherwise
// errPolicyForbidden is returned.
+@@ -500,6 +559,9 @@ func (pa *AuthorityImpl) checkHostLists(domain string) error {
+ labels := strings.Split(domain, ".")
+ for i := range labels {
+ joined := strings.Join(labels[i:], ".")
++ if pa.lockdown[domain] {
++ continue
++ }
+ if pa.blocklist[joined] {
+ return errPolicyForbidden
+ }