aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRudolf Polzer <rpolzer@google.com>2026-03-02 01:48:29 -0800
committerCarlos Amedee <carlos@golang.org>2026-03-20 09:37:55 -0700
commit9a6627a5db7cc71aa363510fa0f4a2ccd56ee696 (patch)
tree426722094ba069a4b26e291d3e05693cb5c6d56e
parent19c660de6bc9e520dd5e3fece09752b396deb386 (diff)
downloadgo-9a6627a5db7cc71aa363510fa0f4a2ccd56ee696.tar.xz
[release-branch.go1.25] crypto/x509: fix wildcard domain exclusions.
This fixes the case of an exclusion of bar.com being applied to a certificate for *.foo.com. When only applying the test change, this error shows the issue: --- FAIL: TestConstraintCases/#99 (0.00s) name_constraints_test.go:2198: unexpected failure: x509: a root or intermediate certificate is not authorized to sign for this name: DNS name "*.bar.example.com" is excluded by constraint "foo.example.com" See change I60fba0d635f23d53f2146cb64b9f6a29755712e3 for a matching change to master to just add the test cases (which are already passing there and in Go 1.26). Found as part of https://issues.chromium.org/issues/488306305. Fixes #77968 Change-Id: I747e51edc16c1111f6a114de33af35f618793c90 Reviewed-on: https://go-review.googlesource.com/c/go/+/750980 Reviewed-by: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
-rw-r--r--src/crypto/x509/name_constraints_test.go168
-rw-r--r--src/crypto/x509/verify.go21
2 files changed, 187 insertions, 2 deletions
diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go
index 1f50650267..11b2c05b64 100644
--- a/src/crypto/x509/name_constraints_test.go
+++ b/src/crypto/x509/name_constraints_test.go
@@ -1674,6 +1674,174 @@ var nameConstraintsTests = []nameConstraintsTest{
sans: []string{"dns:*.example.com"},
},
},
+
+ // #90: subdomain excluded constraints preclude outer wildcard names
+ {
+ roots: []constraintsSpec{
+ {
+ bad: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.example.com"},
+ },
+ expectedError: "\"*.example.com\" is excluded by constraint \"foo.example.com\"",
+ },
+ // #91: subdomain excluded constraints do not preclude far outer wildcard names
+ {
+ roots: []constraintsSpec{
+ {
+ bad: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.com"},
+ },
+ },
+ // #92: subdomain excluded constraints preclude inner wildcard names
+ {
+ roots: []constraintsSpec{
+ {
+ bad: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.foo.example.com"},
+ },
+ expectedError: "\"*.foo.example.com\" is excluded by constraint \"foo.example.com\"",
+ },
+ // #93: subdomain excluded constraints preclude far inner wildcard names
+ {
+ roots: []constraintsSpec{
+ {
+ bad: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.bar.foo.example.com"},
+ },
+ expectedError: "\"*.bar.foo.example.com\" is excluded by constraint \"foo.example.com\"",
+ },
+ // #94: outer wildcard names are not matched by subdomain permitted constraints
+ {
+ roots: []constraintsSpec{
+ {
+ ok: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.example.com"},
+ },
+ expectedError: "\"*.example.com\" is not permitted",
+ },
+ // #95: far outer wildcard names are not matched by subdomain permitted constraints
+ {
+ roots: []constraintsSpec{
+ {
+ ok: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.com"},
+ },
+ expectedError: "\"*.com\" is not permitted",
+ },
+ // #96: inner wildcard names are matched by subdomain permitted constraints
+ {
+ roots: []constraintsSpec{
+ {
+ ok: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.foo.example.com"},
+ },
+ },
+ // #97: far inner wildcard names are matched by subdomain permitted constraints
+ {
+ roots: []constraintsSpec{
+ {
+ ok: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.bar.foo.example.com"},
+ },
+ },
+
+ // #98: cross include should not match
+ {
+ roots: []constraintsSpec{
+ {
+ ok: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.bar.example.com"},
+ },
+ expectedError: "\"*.bar.example.com\" is not permitted by any constraint",
+ },
+ // #99: cross exclude should not match
+ {
+ roots: []constraintsSpec{
+ {
+ bad: []string{"dns:foo.example.com"},
+ },
+ },
+ intermediates: [][]constraintsSpec{
+ {
+ {},
+ },
+ },
+ leaf: leafSpec{
+ sans: []string{"dns:*.bar.example.com"},
+ },
+ },
}
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 076e82666a..fd901a58f8 100644
--- a/src/crypto/x509/verify.go
+++ b/src/crypto/x509/verify.go
@@ -547,8 +547,25 @@ func matchDomainConstraint(domain, constraint string, excluded bool, reversedDom
}
if excluded && wildcardDomain && len(domainLabels) > 1 && len(constraintLabels) > 1 {
- domainLabels = domainLabels[:len(domainLabels)-1]
- constraintLabels = constraintLabels[:len(constraintLabels)-1]
+ // Rules must apply to wildcard domains as if the wildcard could be any DNS label.
+ //
+ // For inclusion rules this works simply by treating the wildcard like a label
+ // (which does not exist in the constraints, and thus must be within a subtree).
+ //
+ // For exclusion rules, however, care must be taken that the excluded
+ // tree is not covered by the wildcard domain range.
+ //
+ // The following cases exist:
+ //
+ // 1. excluded.example.com <-> *.com: no match, as wildcards can only match one label.
+ // 2. excluded.example.com <-> *.example.com: match, as this contains excluded.example.com.
+ // 3. excluded.example.com <-> *.excluded.example.com: match (but matches just as well when treating the wildcard like a label).
+ //
+ // As such, only case 2 needs explicit handling here.
+ if len(domainLabels) == len(constraintLabels) {
+ domainLabels = domainLabels[:len(domainLabels)-1]
+ constraintLabels = constraintLabels[:len(constraintLabels)-1]
+ }
}
for i, constraintLabel := range constraintLabels {