@@ -44,6 +44,7 @@ SRC_URI += "\
file://CVE-2026-27140.patch \
file://CVE-2026-27143.patch \
file://CVE-2026-27144.patch \
+ file://CVE-2026-32280.patch \
"
SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71"
new file mode 100644
@@ -0,0 +1,294 @@
+From edc1e4a5f2af48b648502d987b8d4eebf43c884b Mon Sep 17 00:00:00 2001
+From: Roland Shoemaker <bracewell@google.com>
+Date: Thu, 5 Mar 2026 14:28:44 -0800
+Subject: [PATCH] [release-branch.go1.25] crypto/x509: fix signature checking
+ limit
+
+We added the "is this cert already in the chain" check (alreadyInChain)
+to considerCandidates before the signature limit. considerCandidates
+bails out when we exceed the signature check, but buildChains keeps
+calling considerCandidates until it exhausts all potential parents. In
+the case where a large number of certificates look to have signed each
+other (e.g. all have subject==issuerSubject and the same key),
+alreadyInChain is not particularly cheap, meaning even though we hit our
+"this is too much work" limit, we still do a lot of work.
+
+Move alreadyInChain after the signature limit, and also return a
+sentinel error, and check it in buildChains so we can break out of the
+loop early if we aren't actually going to do any more work.
+
+Thanks to Jakub Ciolek for reporting this issue.
+
+Updates #78282
+Fixes #78361
+Fixes CVE-2026-32280
+
+Change-Id: Ie6f05c6ba3b0a40c21f64f7c4f846e74fae3b10e
+Reviewed-on: https://go-review.googlesource.com/c/go/+/758320
+Reviewed-by: Damien Neil <dneil@google.com>
+Reviewed-by: Neal Patel <nealpatel@google.com>
+LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
+Reviewed-by: Jakub Ciolek <jakub@ciolek.dev>
+(cherry picked from commit 26d8a902002a2b41bc4c302044110f2eae8d597f)
+Reviewed-on: https://go-review.googlesource.com/c/go/+/759221
+Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
+
+CVE: CVE-2026-32280
+Upstream-Status: Backport [https://github.com/golang/go/commit/edc1e4a5f2af48b648502d987b8d4eebf43c884b]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ src/crypto/x509/verify.go | 31 ++++---
+ src/crypto/x509/verify_test.go | 150 ++++++++++++++++-----------------
+ 2 files changed, 96 insertions(+), 85 deletions(-)
+
+diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
+index 0ae8aef..1de06bc 100644
+--- a/src/crypto/x509/verify.go
++++ b/src/crypto/x509/verify.go
+@@ -939,6 +939,8 @@ func alreadyInChain(candidate *Certificate, chain []*Certificate) bool {
+ // for failed checks due to different intermediates having the same Subject.
+ const maxChainSignatureChecks = 100
+
++var errSignatureLimit = errors.New("x509: signature check attempts limit reached while verifying certificate chain")
++
+ func (c *Certificate) buildChains(currentChain []*Certificate, sigChecks *int, opts *VerifyOptions) (chains [][]*Certificate, err error) {
+ var (
+ hintErr error
+@@ -946,16 +948,16 @@ func (c *Certificate) buildChains(currentChain []*Certificate, sigChecks *int, o
+ )
+
+ considerCandidate := func(certType int, candidate potentialParent) {
+- if candidate.cert.PublicKey == nil || alreadyInChain(candidate.cert, currentChain) {
+- return
+- }
+-
+ if sigChecks == nil {
+ sigChecks = new(int)
+ }
+ *sigChecks++
+ if *sigChecks > maxChainSignatureChecks {
+- err = errors.New("x509: signature check attempts limit reached while verifying certificate chain")
++ err = errSignatureLimit
++ return
++ }
++
++ if candidate.cert.PublicKey == nil || alreadyInChain(candidate.cert, currentChain) {
+ return
+ }
+
+@@ -996,11 +998,20 @@ func (c *Certificate) buildChains(currentChain []*Certificate, sigChecks *int, o
+ }
+ }
+
+- for _, root := range opts.Roots.findPotentialParents(c) {
+- considerCandidate(rootCertificate, root)
+- }
+- for _, intermediate := range opts.Intermediates.findPotentialParents(c) {
+- considerCandidate(intermediateCertificate, intermediate)
++candidateLoop:
++ for _, parents := range []struct {
++ certType int
++ potentials []potentialParent
++ }{
++ {rootCertificate, opts.Roots.findPotentialParents(c)},
++ {intermediateCertificate, opts.Intermediates.findPotentialParents(c)},
++ } {
++ for _, parent := range parents.potentials {
++ considerCandidate(parents.certType, parent)
++ if err == errSignatureLimit {
++ break candidateLoop
++ }
++ }
+ }
+
+ if len(chains) > 0 {
+diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
+index 223c250..f3711ac 100644
+--- a/src/crypto/x509/verify_test.go
++++ b/src/crypto/x509/verify_test.go
+@@ -1765,10 +1765,13 @@ func TestValidHostname(t *testing.T) {
+ }
+ }
+
+-func generateCert(cn string, isCA bool, issuer *Certificate, issuerKey crypto.PrivateKey) (*Certificate, crypto.PrivateKey, error) {
+- priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+- if err != nil {
+- return nil, nil, err
++func generateCert(cn string, isCA bool, issuer *Certificate, issuerKey crypto.PrivateKey, priv crypto.PrivateKey) (*Certificate, crypto.PrivateKey, error) {
++ if priv == nil {
++ var err error
++ priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
++ if err != nil {
++ return nil, nil, err
++ }
+ }
+
+ serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+@@ -1779,6 +1782,7 @@ func generateCert(cn string, isCA bool, issuer *Certificate, issuerKey crypto.Pr
+ Subject: pkix.Name{CommonName: cn},
+ NotBefore: time.Now().Add(-1 * time.Hour),
+ NotAfter: time.Now().Add(24 * time.Hour),
++ DNSNames: []string{rand.Text()},
+
+ KeyUsage: KeyUsageKeyEncipherment | KeyUsageDigitalSignature | KeyUsageCertSign,
+ ExtKeyUsage: []ExtKeyUsage{ExtKeyUsageServerAuth},
+@@ -1790,7 +1794,7 @@ func generateCert(cn string, isCA bool, issuer *Certificate, issuerKey crypto.Pr
+ issuerKey = priv
+ }
+
+- derBytes, err := CreateCertificate(rand.Reader, template, issuer, priv.Public(), issuerKey)
++ derBytes, err := CreateCertificate(rand.Reader, template, issuer, priv.(crypto.Signer).Public(), issuerKey)
+ if err != nil {
+ return nil, nil, err
+ }
+@@ -1802,81 +1806,77 @@ func generateCert(cn string, isCA bool, issuer *Certificate, issuerKey crypto.Pr
+ return cert, priv, nil
+ }
+
+-func TestPathologicalChain(t *testing.T) {
+- if testing.Short() {
+- t.Skip("skipping generation of a long chain of certificates in short mode")
+- }
+-
+- // Build a chain where all intermediates share the same subject, to hit the
+- // path building worst behavior.
+- roots, intermediates := NewCertPool(), NewCertPool()
+-
+- parent, parentKey, err := generateCert("Root CA", true, nil, nil)
+- if err != nil {
+- t.Fatal(err)
+- }
+- roots.AddCert(parent)
+-
+- for i := 1; i < 100; i++ {
+- parent, parentKey, err = generateCert("Intermediate CA", true, parent, parentKey)
+- if err != nil {
+- t.Fatal(err)
+- }
+- intermediates.AddCert(parent)
+- }
+-
+- leaf, _, err := generateCert("Leaf", false, parent, parentKey)
+- if err != nil {
+- t.Fatal(err)
+- }
+-
+- start := time.Now()
+- _, err = leaf.Verify(VerifyOptions{
+- Roots: roots,
+- Intermediates: intermediates,
+- })
+- t.Logf("verification took %v", time.Since(start))
+-
+- if err == nil || !strings.Contains(err.Error(), "signature check attempts limit") {
+- t.Errorf("expected verification to fail with a signature checks limit error; got %v", err)
+- }
+-}
+-
+-func TestLongChain(t *testing.T) {
++func TestPathologicalChains(t *testing.T) {
+ if testing.Short() {
+- t.Skip("skipping generation of a long chain of certificates in short mode")
+- }
+-
+- roots, intermediates := NewCertPool(), NewCertPool()
+-
+- parent, parentKey, err := generateCert("Root CA", true, nil, nil)
+- if err != nil {
+- t.Fatal(err)
+- }
+- roots.AddCert(parent)
++ t.Skip("skipping generation of a long chains of certificates in short mode")
++ }
++
++ // Test four pathological cases, where the intermediates in the chain have
++ // the same/different subjects and the same/different keys. This covers a
++ // number of cases where the chain building algorithm might be inefficient,
++ // such as when there are many intermediates with the same subject but
++ // different keys, many intermediates with the same key but different
++ // subjects, many intermediates with the same subject and key, or many
++ // intermediates with different subjects and keys.
++ //
++ // The worst case for our algorithm is when all of the intermediates share
++ // both subject and key, in which case all of the intermediates appear to
++ // have signed each other, causing us to see a large number of potential
++ // parents for each intermediate.
++ //
++ // All of these cases, Certificate.Verify should return errSignatureLimit.
++ //
++ // In all cases, don't have a root in the pool, so a valid chain cannot actually be built.
++
++ for _, test := range []struct {
++ sameSubject bool
++ sameKey bool
++ }{
++ {sameSubject: false, sameKey: false},
++ {sameSubject: true, sameKey: false},
++ {sameSubject: false, sameKey: true},
++ {sameSubject: true, sameKey: true},
++ } {
++ t.Run(fmt.Sprintf("sameSubject=%t,sameKey=%t", test.sameSubject, test.sameKey), func(t *testing.T) {
++ intermediates := NewCertPool()
++
++ var intermediateKey crypto.PrivateKey
++ if test.sameKey {
++ var err error
++ intermediateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
++ if err != nil {
++ t.Fatal(err)
++ }
++ }
+
+- for i := 1; i < 15; i++ {
+- name := fmt.Sprintf("Intermediate CA #%d", i)
+- parent, parentKey, err = generateCert(name, true, parent, parentKey)
+- if err != nil {
+- t.Fatal(err)
+- }
+- intermediates.AddCert(parent)
+- }
++ var leafSigner crypto.PrivateKey
++ var intermediate *Certificate
++ for i := range 100 {
++ cn := "Intermediate CA"
++ if !test.sameSubject {
++ cn += fmt.Sprintf(" #%d", i)
++ }
++ var err error
++ intermediate, leafSigner, err = generateCert(cn, true, intermediate, leafSigner, intermediateKey)
++ if err != nil {
++ t.Fatal(err)
++ }
++ intermediates.AddCert(intermediate)
++ }
+
+- leaf, _, err := generateCert("Leaf", false, parent, parentKey)
+- if err != nil {
+- t.Fatal(err)
+- }
++ leaf, _, err := generateCert("Leaf", false, intermediate, leafSigner, nil)
++ if err != nil {
++ t.Fatal(err)
++ }
+
+- start := time.Now()
+- if _, err := leaf.Verify(VerifyOptions{
+- Roots: roots,
+- Intermediates: intermediates,
+- }); err != nil {
+- t.Error(err)
++ start := time.Now()
++ _, err = leaf.Verify(VerifyOptions{
++ Roots: NewCertPool(),
++ Intermediates: intermediates,
++ })
++ t.Logf("verification took %v", time.Since(start))
++ })
+ }
+- t.Logf("verification took %v", time.Since(start))
+ }
+
+ func TestSystemRootsError(t *testing.T) {
+--
+2.50.1
+
Pick patch from [1] also mentioned at Debian report in [2] [1] https://github.com/golang/go/commit/edc1e4a5f2af48b648502d987b8d4eebf43c884b [2] https://security-tracker.debian.org/tracker/CVE-2026-32280 [3] https://nvd.nist.gov/vuln/detail/CVE-2026-32280 Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> --- meta/recipes-devtools/go/go-1.22.12.inc | 1 + .../go/go/CVE-2026-32280.patch | 294 ++++++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 meta/recipes-devtools/go/go/CVE-2026-32280.patch