diff mbox series

[scarthgap,2/3] go: Fix CVE-2025-61727

Message ID 20251229154415.422681-2-vanusuri@mvista.com
State Under Review
Delegated to: Steve Sakoman
Headers show
Series [scarthgap,1/3] go: Update CVE-2025-58187 | expand

Commit Message

Vijay Anusuri Dec. 29, 2025, 3:44 p.m. UTC
Upstream-Status: Backport from https://github.com/golang/go/commit/04db77a423cac75bb82cc9a6859991ae9c016344

Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
---
 meta/recipes-devtools/go/go-1.22.12.inc       |   1 +
 .../go/go/CVE-2025-61727.patch                | 226 ++++++++++++++++++
 2 files changed, 227 insertions(+)
 create mode 100644 meta/recipes-devtools/go/go/CVE-2025-61727.patch
diff mbox series

Patch

diff --git a/meta/recipes-devtools/go/go-1.22.12.inc b/meta/recipes-devtools/go/go-1.22.12.inc
index 0729b5eec0..664ccf3edc 100644
--- a/meta/recipes-devtools/go/go-1.22.12.inc
+++ b/meta/recipes-devtools/go/go-1.22.12.inc
@@ -29,6 +29,7 @@  SRC_URI += "\
     file://CVE-2025-47912.patch \
     file://CVE-2025-61723.patch \
     file://CVE-2025-61724.patch \
+    file://CVE-2025-61727.patch \
 "
 SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71"
 
diff --git a/meta/recipes-devtools/go/go/CVE-2025-61727.patch b/meta/recipes-devtools/go/go/CVE-2025-61727.patch
new file mode 100644
index 0000000000..e87621ef99
--- /dev/null
+++ b/meta/recipes-devtools/go/go/CVE-2025-61727.patch
@@ -0,0 +1,226 @@ 
+From 04db77a423cac75bb82cc9a6859991ae9c016344 Mon Sep 17 00:00:00 2001
+From: Roland Shoemaker <bracewell@google.com>
+Date: Mon, 24 Nov 2025 08:46:08 -0800
+Subject: [PATCH] [release-branch.go1.24] crypto/x509: excluded subdomain
+ constraints preclude wildcard SANs
+
+When evaluating name constraints in a certificate chain, the presence of
+an excluded subdomain constraint (e.g., excluding "test.example.com")
+should preclude the use of a wildcard SAN (e.g., "*.example.com").
+
+Fixes #76442
+Fixes #76463
+Fixes CVE-2025-61727
+
+Change-Id: I42a0da010cb36d2ec9d1239ae3f61cf25eb78bba
+Reviewed-on: https://go-review.googlesource.com/c/go/+/724401
+Reviewed-by: Nicholas Husin <husin@google.com>
+Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
+LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
+Reviewed-by: Nicholas Husin <nsh@golang.org>
+Reviewed-by: Neal Patel <nealpatel@google.com>
+
+Upstream-Status: Backport [https://github.com/golang/go/commit/04db77a423cac75bb82cc9a6859991ae9c016344]
+CVE: CVE-2025-61727
+Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
+---
+ src/crypto/x509/name_constraints_test.go | 34 ++++++++++++++++++++
+ src/crypto/x509/verify.go                | 40 +++++++++++++++---------
+ src/crypto/x509/verify_test.go           |  2 +-
+ 3 files changed, 60 insertions(+), 16 deletions(-)
+
+diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go
+index a5851845164d10..bc91b28401fce5 100644
+--- a/src/crypto/x509/name_constraints_test.go
++++ b/src/crypto/x509/name_constraints_test.go
+@@ -1624,6 +1624,40 @@ var nameConstraintsTests = []nameConstraintsTest{
+ 		},
+ 		expectedError: "URI with IP",
+ 	},
++	// #87: subdomain excluded constraints preclude 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\"",
++	},
++	// #88: 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",
++	},
+ }
+ 
+ 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 bf7e7ec058db2b..9175fa4dc147a2 100644
+--- a/src/crypto/x509/verify.go
++++ b/src/crypto/x509/verify.go
+@@ -429,7 +429,7 @@ func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
+ 	return reverseLabels, true
+ }
+ 
+-func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
++func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string, excluded bool, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
+ 	// If the constraint contains an @, then it specifies an exact mailbox
+ 	// name.
+ 	if strings.Contains(constraint, "@") {
+@@ -442,10 +442,10 @@ func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string, reversedDom
+ 
+ 	// Otherwise the constraint is like a DNS constraint of the domain part
+ 	// of the mailbox.
+-	return matchDomainConstraint(mailbox.domain, constraint, reversedDomainsCache, reversedConstraintsCache)
++	return matchDomainConstraint(mailbox.domain, constraint, excluded, reversedDomainsCache, reversedConstraintsCache)
+ }
+ 
+-func matchURIConstraint(uri *url.URL, constraint string, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
++func matchURIConstraint(uri *url.URL, constraint string, excluded bool, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
+ 	// From RFC 5280, Section 4.2.1.10:
+ 	// “a uniformResourceIdentifier that does not include an authority
+ 	// component with a host name specified as a fully qualified domain
+@@ -474,7 +474,7 @@ func matchURIConstraint(uri *url.URL, constraint string, reversedDomainsCache ma
+ 		return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String())
+ 	}
+ 
+-	return matchDomainConstraint(host, constraint, reversedDomainsCache, reversedConstraintsCache)
++	return matchDomainConstraint(host, constraint, excluded, reversedDomainsCache, reversedConstraintsCache)
+ }
+ 
+ func matchIPConstraint(ip net.IP, constraint *net.IPNet) (bool, error) {
+@@ -491,7 +491,7 @@ func matchIPConstraint(ip net.IP, constraint *net.IPNet) (bool, error) {
+ 	return true, nil
+ }
+ 
+-func matchDomainConstraint(domain, constraint string, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
++func matchDomainConstraint(domain, constraint string, excluded bool, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
+ 	// The meaning of zero length constraints is not specified, but this
+ 	// code follows NSS and accepts them as matching everything.
+ 	if len(constraint) == 0 {
+@@ -508,6 +508,11 @@ func matchDomainConstraint(domain, constraint string, reversedDomainsCache map[s
+ 		reversedDomainsCache[domain] = domainLabels
+ 	}
+ 
++	wildcardDomain := false
++	if len(domain) > 0 && domain[0] == '*' {
++		wildcardDomain = true
++	}
++
+ 	// RFC 5280 says that a leading period in a domain name means that at
+ 	// least one label must be prepended, but only for URI and email
+ 	// constraints, not DNS constraints. The code also supports that
+@@ -534,6 +539,11 @@ func matchDomainConstraint(domain, constraint string, reversedDomainsCache map[s
+ 		return false, nil
+ 	}
+ 
++	if excluded && wildcardDomain && len(domainLabels) > 1 && len(constraintLabels) > 0 {
++		domainLabels = domainLabels[:len(domainLabels)-1]
++		constraintLabels = constraintLabels[:len(constraintLabels)-1]
++	}
++
+ 	for i, constraintLabel := range constraintLabels {
+ 		if !strings.EqualFold(constraintLabel, domainLabels[i]) {
+ 			return false, nil
+@@ -553,7 +563,7 @@ func (c *Certificate) checkNameConstraints(count *int,
+ 	nameType string,
+ 	name string,
+ 	parsedName any,
+-	match func(parsedName, constraint any) (match bool, err error),
++	match func(parsedName, constraint any, excluded bool) (match bool, err error),
+ 	permitted, excluded any) error {
+ 
+ 	excludedValue := reflect.ValueOf(excluded)
+@@ -565,7 +575,7 @@ func (c *Certificate) checkNameConstraints(count *int,
+ 
+ 	for i := 0; i < excludedValue.Len(); i++ {
+ 		constraint := excludedValue.Index(i).Interface()
+-		match, err := match(parsedName, constraint)
++		match, err := match(parsedName, constraint, true)
+ 		if err != nil {
+ 			return CertificateInvalidError{c, CANotAuthorizedForThisName, err.Error()}
+ 		}
+@@ -587,7 +597,7 @@ func (c *Certificate) checkNameConstraints(count *int,
+ 		constraint := permittedValue.Index(i).Interface()
+ 
+ 		var err error
+-		if ok, err = match(parsedName, constraint); err != nil {
++		if ok, err = match(parsedName, constraint, false); err != nil {
+ 			return CertificateInvalidError{c, CANotAuthorizedForThisName, err.Error()}
+ 		}
+ 
+@@ -679,8 +689,8 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
+ 					}
+ 
+ 					if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "email address", name, mailbox,
+-						func(parsedName, constraint any) (bool, error) {
+-							return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string), reversedDomainsCache, reversedConstraintsCache)
++						func(parsedName, constraint any, excluded bool) (bool, error) {
++							return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string), excluded, reversedDomainsCache, reversedConstraintsCache)
+ 						}, c.PermittedEmailAddresses, c.ExcludedEmailAddresses); err != nil {
+ 						return err
+ 					}
+@@ -692,8 +702,8 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
+ 					}
+ 
+ 					if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "DNS name", name, name,
+-						func(parsedName, constraint any) (bool, error) {
+-							return matchDomainConstraint(parsedName.(string), constraint.(string), reversedDomainsCache, reversedConstraintsCache)
++						func(parsedName, constraint any, excluded bool) (bool, error) {
++							return matchDomainConstraint(parsedName.(string), constraint.(string), excluded, reversedDomainsCache, reversedConstraintsCache)
+ 						}, c.PermittedDNSDomains, c.ExcludedDNSDomains); err != nil {
+ 						return err
+ 					}
+@@ -706,8 +716,8 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
+ 					}
+ 
+ 					if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "URI", name, uri,
+-						func(parsedName, constraint any) (bool, error) {
+-							return matchURIConstraint(parsedName.(*url.URL), constraint.(string), reversedDomainsCache, reversedConstraintsCache)
++						func(parsedName, constraint any, excluded bool) (bool, error) {
++							return matchURIConstraint(parsedName.(*url.URL), constraint.(string), excluded, reversedDomainsCache, reversedConstraintsCache)
+ 						}, c.PermittedURIDomains, c.ExcludedURIDomains); err != nil {
+ 						return err
+ 					}
+@@ -719,7 +729,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
+ 					}
+ 
+ 					if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "IP address", ip.String(), ip,
+-						func(parsedName, constraint any) (bool, error) {
++						func(parsedName, constraint any, _ bool) (bool, error) {
+ 							return matchIPConstraint(parsedName.(net.IP), constraint.(*net.IPNet))
+ 						}, c.PermittedIPRanges, c.ExcludedIPRanges); err != nil {
+ 						return err
+diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
+index 60a4cea9146adf..6a394e46e94f5a 100644
+--- a/src/crypto/x509/verify_test.go
++++ b/src/crypto/x509/verify_test.go
+@@ -1352,7 +1352,7 @@ var nameConstraintTests = []struct {
+ 
+ func TestNameConstraints(t *testing.T) {
+ 	for i, test := range nameConstraintTests {
+-		result, err := matchDomainConstraint(test.domain, test.constraint, map[string][]string{}, map[string][]string{})
++		result, err := matchDomainConstraint(test.domain, test.constraint, false, map[string][]string{}, map[string][]string{})
+ 
+ 		if err != nil && !test.expectError {
+ 			t.Errorf("unexpected error for test #%d: domain=%s, constraint=%s, err=%s", i, test.domain, test.constraint, err)