diff --git a/ra/ra.go b/ra/ra.go index 6f60c50e9cb..b3214b6b463 100644 --- a/ra/ra.go +++ b/ra/ra.go @@ -7,10 +7,8 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/asn1" - "encoding/json" "errors" "fmt" - "net/url" "os" "slices" "strconv" @@ -42,7 +40,6 @@ import ( "github.com/letsencrypt/boulder/issuance" blog "github.com/letsencrypt/boulder/log" "github.com/letsencrypt/boulder/metrics" - "github.com/letsencrypt/boulder/policy" "github.com/letsencrypt/boulder/probs" pubpb "github.com/letsencrypt/boulder/publisher/proto" rapb "github.com/letsencrypt/boulder/ra/proto" @@ -515,69 +512,6 @@ func (ra *RegistrationAuthorityImpl) NewRegistration(ctx context.Context, reques return res, nil } -// validateContacts checks the provided list of contacts, returning an error if -// any are not acceptable. Unacceptable contacts lists include: -// * An empty list -// * A list has more than maxContactsPerReg contacts -// * A list containing an empty contact -// * A list containing a contact that does not parse as a URL -// * A list containing a contact that has a URL scheme other than mailto -// * A list containing a mailto contact that contains hfields -// * A list containing a contact that has non-ascii characters -// * A list containing a contact that doesn't pass `policy.ValidEmail` -func (ra *RegistrationAuthorityImpl) validateContacts(contacts []string) error { - if len(contacts) == 0 { - return nil // Nothing to validate - } - if ra.maxContactsPerReg > 0 && len(contacts) > ra.maxContactsPerReg { - return berrors.MalformedError( - "too many contacts provided: %d > %d", - len(contacts), - ra.maxContactsPerReg, - ) - } - - for _, contact := range contacts { - if contact == "" { - return berrors.InvalidEmailError("empty contact") - } - parsed, err := url.Parse(contact) - if err != nil { - return berrors.InvalidEmailError("unparsable contact") - } - if parsed.Scheme != "mailto" { - return berrors.UnsupportedContactError("only contact scheme 'mailto:' is supported") - } - if parsed.RawQuery != "" || contact[len(contact)-1] == '?' { - return berrors.InvalidEmailError("contact email contains a question mark") - } - if parsed.Fragment != "" || contact[len(contact)-1] == '#' { - return berrors.InvalidEmailError("contact email contains a '#'") - } - if !core.IsASCII(contact) { - return berrors.InvalidEmailError("contact email contains non-ASCII characters") - } - err = policy.ValidEmail(parsed.Opaque) - if err != nil { - return err - } - } - - // NOTE(@cpu): For historical reasons (= maxContactBytes { - return berrors.InvalidEmailError( - "too many/too long contact(s). Please use shorter or fewer email addresses") - } - - return nil -} - // matchesCSR tests the contents of a generated certificate to make sure // that the PublicKey, CommonName, and identifiers match those provided in // the CSR that was used to generate the certificate. It also checks the diff --git a/ra/ra_test.go b/ra/ra_test.go index 39c5dd3ea1b..06dc663891a 100644 --- a/ra/ra_test.go +++ b/ra/ra_test.go @@ -390,83 +390,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, sapb.StorageAutho return dummyVA, sa, ra, rlSource, fc, registration, cleanUp } -func TestValidateContacts(t *testing.T) { - _, _, ra, _, _, _, cleanUp := initAuthorities(t) - defer cleanUp() - - ansible := "ansible:earth.sol.milkyway.laniakea/letsencrypt" - validEmail := "mailto:admin@email.com" - otherValidEmail := "mailto:other-admin@email.com" - malformedEmail := "mailto:admin.com" - nonASCII := "mailto:seƱor@email.com" - unparsable := "mailto:a@email.com, b@email.com" - forbidden := "mailto:a@example.org" - - err := ra.validateContacts([]string{}) - test.AssertNotError(t, err, "No Contacts") - - err = ra.validateContacts([]string{validEmail, otherValidEmail}) - test.AssertError(t, err, "Too Many Contacts") - - err = ra.validateContacts([]string{validEmail}) - test.AssertNotError(t, err, "Valid Email") - - err = ra.validateContacts([]string{malformedEmail}) - test.AssertError(t, err, "Malformed Email") - - err = ra.validateContacts([]string{ansible}) - test.AssertError(t, err, "Unknown scheme") - - err = ra.validateContacts([]string{""}) - test.AssertError(t, err, "Empty URL") - - err = ra.validateContacts([]string{nonASCII}) - test.AssertError(t, err, "Non ASCII email") - - err = ra.validateContacts([]string{unparsable}) - test.AssertError(t, err, "Unparsable email") - - err = ra.validateContacts([]string{forbidden}) - test.AssertError(t, err, "Forbidden email") - - err = ra.validateContacts([]string{"mailto:admin@localhost"}) - test.AssertError(t, err, "Forbidden email") - - err = ra.validateContacts([]string{"mailto:admin@example.not.a.iana.suffix"}) - test.AssertError(t, err, "Forbidden email") - - err = ra.validateContacts([]string{"mailto:admin@1.2.3.4"}) - test.AssertError(t, err, "Forbidden email") - - err = ra.validateContacts([]string{"mailto:admin@[1.2.3.4]"}) - test.AssertError(t, err, "Forbidden email") - - err = ra.validateContacts([]string{"mailto:admin@a.com?no-reminder-emails"}) - test.AssertError(t, err, "No hfields in email") - - err = ra.validateContacts([]string{"mailto:example@a.com?"}) - test.AssertError(t, err, "No hfields in email") - - err = ra.validateContacts([]string{"mailto:example@a.com#"}) - test.AssertError(t, err, "No fragment") - - err = ra.validateContacts([]string{"mailto:example@a.com#optional"}) - test.AssertError(t, err, "No fragment") - - // The registrations.contact field is VARCHAR(191). 175 'a' characters plus - // the prefix "mailto:" and the suffix "@a.com" makes exactly 191 bytes of - // encoded JSON. The correct size to hit our maximum DB field length. - var longStringBuf strings.Builder - longStringBuf.WriteString("mailto:") - for range 175 { - longStringBuf.WriteRune('a') - } - longStringBuf.WriteString("@a.com") - - err = ra.validateContacts([]string{longStringBuf.String()}) - test.AssertError(t, err, "Too long contacts") -} - func TestNewRegistration(t *testing.T) { _, sa, ra, _, _, _, cleanUp := initAuthorities(t) defer cleanUp()