src/crypto/x509/name_constraints_test.go | 17 +++++++++++++++++ src/crypto/x509/verify.go | 7 +++++-- diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go index 008c7028f4e4c44acc8dd3d6afa5b43dad736d7e..a5851845164d1021f417e937d08aef9e943fa346 100644 --- a/src/crypto/x509/name_constraints_test.go +++ b/src/crypto/x509/name_constraints_test.go @@ -1607,6 +1607,23 @@ roots: []constraintsSpec{{ok: []string{"dns:example.com"}}}, leaf: leafSpec{sans: []string{"dns:.example.com"}}, expectedError: "cannot parse dnsName \".example.com\"", }, + // #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 d2384f56653f86c602e0c8aa46093ee3d1b74bc0..5fe93c6124a989223708881a894359474e32c637 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -13,6 +13,7 @@ "fmt" "iter" "maps" "net" + "net/netip" "net/url" "reflect" "runtime" @@ -465,8 +466,10 @@ return false, err } } - 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()) }