diff --git a/smx509/name_constraints_test.go b/smx509/name_constraints_test.go index 19deec7..e0d81a5 100644 --- a/smx509/name_constraints_test.go +++ b/smx509/name_constraints_test.go @@ -1607,6 +1607,23 @@ var nameConstraintsTests = []nameConstraintsTest{ 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/smx509/verify.go b/smx509/verify.go index ef7c124..7100d11 100644 --- a/smx509/verify.go +++ b/smx509/verify.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "net" + "net/netip" "net/url" "reflect" "runtime" @@ -260,7 +261,7 @@ func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) { // domain is prefixed with an empty label, append an empty // string to reverseLabels to indicate this. reverseLabels = append(reverseLabels, "") - } + } } } @@ -324,8 +325,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()) } @@ -784,7 +787,7 @@ func (c *Certificate) buildChains(currentChain []*Certificate, sigChecks *int, o ) considerCandidate := func(certType int, candidate potentialParent) { - if candidate.cert.PublicKey == nil ||alreadyInChain(candidate.cert, currentChain) { + if candidate.cert.PublicKey == nil || alreadyInChain(candidate.cert, currentChain) { return } @@ -823,7 +826,7 @@ func (c *Certificate) buildChains(currentChain []*Certificate, sigChecks *int, o return } } - + switch certType { case rootCertificate: chains = append(chains, appendToFreshChain(currentChain, candidate.cert))