mirror of
https://git.yoctoproject.org/poky
synced 2026-06-17 10:53:49 +02:00
go: patch CVE-2026-32280
Backport patch from [1] [1] https://go.dev/cl/758320 (From OE-Core rev: e52259f1d09c722390b49adf3d4e3d863fbde7e8) Signed-off-by: Theo Gaige (Schneider Electric) <tgaige.opensource@witekio.com> Reviewed-by: Bruno Vernay <bruno.vernay@se.com> Signed-off-by: Jeremy Rosen <jeremy.rosen@smile.fr> Signed-off-by: Paul Barker <paul@pbarker.dev>
This commit is contained in:
committed by
Paul Barker
parent
d942ca707b
commit
4c319bd87f
@@ -42,6 +42,7 @@ SRC_URI += "\
|
||||
file://CVE-2025-68121_p2.patch \
|
||||
file://CVE-2025-68121_p3.patch \
|
||||
file://CVE-2026-27142.patch \
|
||||
file://CVE-2026-32280.patch \
|
||||
"
|
||||
SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71"
|
||||
|
||||
|
||||
289
meta/recipes-devtools/go/go/CVE-2026-32280.patch
Normal file
289
meta/recipes-devtools/go/go/CVE-2026-32280.patch
Normal file
@@ -0,0 +1,289 @@
|
||||
From 1d71a2882078ea5057e68a7d2fedc83a5227c764 Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <bracewell@google.com>
|
||||
Date: Thu, 5 Mar 2026 14:28:44 -0800
|
||||
Subject: [PATCH] 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.
|
||||
|
||||
Fixes #78282
|
||||
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>
|
||||
|
||||
CVE: CVE-2026-32280
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/26d8a902002a2b41bc4c302044110f2eae8d597f]
|
||||
Signed-off-by: Theo Gaige (Schneider Electric) <tgaige.opensource@witekio.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.43.0
|
||||
|
||||
Reference in New Issue
Block a user