diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go index 4c22c4cd8e367a8c1fd6a2acf67f4c789f758dc0..78263fc0b2cc3c459262f40994bf721c997a9db1 100644 --- a/src/crypto/x509/name_constraints_test.go +++ b/src/crypto/x509/name_constraints_test.go @@ -1599,6 +1599,24 @@ var nameConstraintsTests = []nameConstraintsTest{ cn: "foo.bar", }, }, + + // #86: URIs with IPv6 addresses with zones and ports are rejected + { + roots: []constraintsSpec{ + { + ok: []string{"uri:example.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + { + {}, + }, + }, + leaf: leafSpec{ + sans: []string{"uri:http://[2006:abcd::1%25.example.com]:16/"}, + }, + expectedError: "URI with IP", + }, } func makeConstraintsCACert(constraints constraintsSpec, name string, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) { diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 6efbff28bf7b6e997988c46a8197b6f7b1430c5d..2d2a271d53e950b3c141b090ae11b268e9c5c69a 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "net" + "net/netip" "net/url" "reflect" "runtime" @@ -429,8 +430,10 @@ func matchURIConstraint(uri *url.URL, constraint string) (bool, error) { } } - if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") || - net.ParseIP(host) != nil { + // netip.ParseAddr will reject the URI IPv6 literal form "[...]", so we + // check if _either_ the string parses as an IP, or if it is enclosed in + // square brackets. + if _, err := netip.ParseAddr(host); err == nil || (strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]")) { return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String()) }