summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeepak Rathore <deeratho@cisco.com>2026-02-18 23:44:49 -0800
committerRichard Purdie <richard.purdie@linuxfoundation.org>2026-02-27 17:45:06 +0000
commite44ffb5b150004b0b38a28583f7f826c606a76e8 (patch)
tree3b7d176200d562ef47f4409f6c94084b0969086a
parent5f5a2976b2d8b0fb75eb4b84c5fd99e76d14d45f (diff)
downloadpoky-e44ffb5b150004b0b38a28583f7f826c606a76e8.tar.gz
go 1.22.12: Fix CVE-2025-68121
Upstream Repository: https://github.com/golang/go.git Bug details: https://nvd.nist.gov/vuln/detail/CVE-2025-68121 Type: Security Fix CVE: CVE-2025-68121 Score: 4.8 Patch: - https://github.com/golang/go/commit/5f07b226f9aa - https://github.com/golang/go/commit/cb75daf3b291 - https://github.com/golang/go/commit/6a501314718b (From OE-Core rev: a5ded8dd51a520cf190ea094f65301477b057d8f) Signed-off-by: Deepak Rathore <deeratho@cisco.com> Signed-off-by: Yoann Congal <yoann.congal@smile.fr> Signed-off-by: Paul Barker <paul@pbarker.dev> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/recipes-devtools/go/go-1.22.12.inc3
-rw-r--r--meta/recipes-devtools/go/go/CVE-2025-68121_p1.patch253
-rw-r--r--meta/recipes-devtools/go/go/CVE-2025-68121_p2.patch385
-rw-r--r--meta/recipes-devtools/go/go/CVE-2025-68121_p3.patch82
4 files changed, 723 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.22.12.inc b/meta/recipes-devtools/go/go-1.22.12.inc
index cc4f98a8fe..3fa421e223 100644
--- a/meta/recipes-devtools/go/go-1.22.12.inc
+++ b/meta/recipes-devtools/go/go-1.22.12.inc
@@ -38,6 +38,9 @@ SRC_URI += "\
38 file://CVE-2025-68119-dependent.patch \ 38 file://CVE-2025-68119-dependent.patch \
39 file://CVE-2025-68119.patch \ 39 file://CVE-2025-68119.patch \
40 file://CVE-2025-61732.patch \ 40 file://CVE-2025-61732.patch \
41 file://CVE-2025-68121_p1.patch \
42 file://CVE-2025-68121_p2.patch \
43 file://CVE-2025-68121_p3.patch \
41" 44"
42SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71" 45SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71"
43 46
diff --git a/meta/recipes-devtools/go/go/CVE-2025-68121_p1.patch b/meta/recipes-devtools/go/go/CVE-2025-68121_p1.patch
new file mode 100644
index 0000000000..811bb17ee8
--- /dev/null
+++ b/meta/recipes-devtools/go/go/CVE-2025-68121_p1.patch
@@ -0,0 +1,253 @@
1From 529caf01aff2314585688c0f92f009d0ad0914be Mon Sep 17 00:00:00 2001
2From: Roland Shoemaker <roland@golang.org>
3Date: Mon, 26 Jan 2026 10:55:32 -0800
4Subject: [PATCH 1/2] [release-branch.go1.24] crypto/tls: add verifiedChains
5 expiration checking during resumption
6
7When resuming a session, check that the verifiedChains contain at least
8one chain that is still valid at the time of resumption. If not, trigger
9a new handshake.
10
11Updates #77113
12Updates #77355
13Updates CVE-2025-68121
14
15CVE: CVE-2025-68121
16Upstream-Status: Backport [https://github.com/golang/go/commit/5f07b226f9aa]
17
18Change-Id: I14f585c43da17802513cbdd5b10c552d7a38b34e
19Reviewed-on: https://go-review.googlesource.com/c/go/+/739321
20Reviewed-by: Coia Prant <coiaprant@gmail.com>
21Reviewed-by: Filippo Valsorda <filippo@golang.org>
22Auto-Submit: Roland Shoemaker <roland@golang.org>
23LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
24Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
25Reviewed-on: https://go-review.googlesource.com/c/go/+/740061
26Reviewed-by: Nicholas Husin <husin@google.com>
27Reviewed-by: Nicholas Husin <nsh@golang.org>
28Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
29Reviewed-by: Damien Neil <dneil@google.com>
30(cherry picked from commit 5f07b226f9aa185aca4b88a9ae58456d7800fc06)
31Signed-off-by: Deepak Rathore <deeratho@cisco.com>
32---
33 src/crypto/tls/common.go | 13 +++
34 src/crypto/tls/handshake_client.go | 10 +-
35 src/crypto/tls/handshake_server.go | 2 +-
36 src/crypto/tls/handshake_server_test.go | 122 +++++++++++++++++++++++
37 src/crypto/tls/handshake_server_tls13.go | 2 +-
38 5 files changed, 144 insertions(+), 5 deletions(-)
39
40diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go
41index 849e8b0a20..738c7e100b 100644
42--- a/src/crypto/tls/common.go
43+++ b/src/crypto/tls/common.go
44@@ -1555,3 +1555,16 @@ func (e *CertificateVerificationError) Error() string {
45 func (e *CertificateVerificationError) Unwrap() error {
46 return e.Err
47 }
48+
49+// anyUnexpiredChain reports if at least one of verifiedChains is still
50+// unexpired. If verifiedChains is empty, it returns false.
51+func anyUnexpiredChain(verifiedChains [][]*x509.Certificate, now time.Time) bool {
52+ for _, chain := range verifiedChains {
53+ if len(chain) != 0 && !slices.ContainsFunc(chain, func(cert *x509.Certificate) bool {
54+ return now.Before(cert.NotBefore) || now.After(cert.NotAfter) // cert is expired
55+ }) {
56+ return true
57+ }
58+ }
59+ return false
60+}
61diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go
62index 08a2d47974..c2ff9e1959 100644
63--- a/src/crypto/tls/handshake_client.go
64+++ b/src/crypto/tls/handshake_client.go
65@@ -322,9 +322,6 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (
66 return nil, nil, nil, nil
67 }
68
69- // Check that the cached server certificate is not expired, and that it's
70- // valid for the ServerName. This should be ensured by the cache key, but
71- // protect the application from a faulty ClientSessionCache implementation.
72 if c.config.time().After(session.peerCertificates[0].NotAfter) {
73 // Expired certificate, delete the entry.
74 c.config.ClientSessionCache.Put(cacheKey, nil)
75@@ -336,6 +333,13 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (
76 return nil, nil, nil, nil
77 }
78 if err := session.peerCertificates[0].VerifyHostname(c.config.ServerName); err != nil {
79+ // This should be ensured by the cache key, but protect the
80+ // application from a faulty ClientSessionCache implementation.
81+ return nil, nil, nil, nil
82+ }
83+ if !anyUnexpiredChain(session.verifiedChains, c.config.time()) {
84+ // No valid chains, delete the entry.
85+ c.config.ClientSessionCache.Put(cacheKey, nil)
86 return nil, nil, nil, nil
87 }
88 }
89diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go
90index 17b6891783..608b2535f1 100644
91--- a/src/crypto/tls/handshake_server.go
92+++ b/src/crypto/tls/handshake_server.go
93@@ -483,7 +483,7 @@ func (hs *serverHandshakeState) checkForResumption() error {
94 return nil
95 }
96 if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
97- len(sessionState.verifiedChains) == 0 {
98+ !anyUnexpiredChain(sessionState.verifiedChains, c.config.time()) {
99 return nil
100 }
101
102diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go
103index 0f10a3e7a6..9eff106ecf 100644
104--- a/src/crypto/tls/handshake_server_test.go
105+++ b/src/crypto/tls/handshake_server_test.go
106@@ -12,6 +12,7 @@ import (
107 "crypto/elliptic"
108 "crypto/rand"
109 "crypto/x509"
110+ "crypto/x509/pkix"
111 "encoding/pem"
112 "errors"
113 "fmt"
114@@ -2049,3 +2050,124 @@ func TestHandshakeContextHierarchy(t *testing.T) {
115 t.Errorf("Unexpected client error: %v", err)
116 }
117 }
118+
119+func TestHandshakeChainExpiryResumption(t *testing.T) {
120+ t.Run("TLS1.2", func(t *testing.T) {
121+ testHandshakeChainExpiryResumption(t, VersionTLS12)
122+ })
123+ t.Run("TLS1.3", func(t *testing.T) {
124+ testHandshakeChainExpiryResumption(t, VersionTLS13)
125+ })
126+}
127+
128+func testHandshakeChainExpiryResumption(t *testing.T, version uint16) {
129+ now := time.Now()
130+
131+ createChain := func(leafNotAfter, rootNotAfter time.Time) (leafDER, expiredLeafDER []byte, root *x509.Certificate) {
132+ tmpl := &x509.Certificate{
133+ Subject: pkix.Name{CommonName: "root"},
134+ NotBefore: rootNotAfter.Add(-time.Hour * 24),
135+ NotAfter: rootNotAfter,
136+ IsCA: true,
137+ BasicConstraintsValid: true,
138+ }
139+ rootDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
140+ if err != nil {
141+ t.Fatalf("CreateCertificate: %v", err)
142+ }
143+ root, err = x509.ParseCertificate(rootDER)
144+ if err != nil {
145+ t.Fatalf("ParseCertificate: %v", err)
146+ }
147+
148+ tmpl = &x509.Certificate{
149+ Subject: pkix.Name{},
150+ DNSNames: []string{"expired-resume.example.com"},
151+ NotBefore: leafNotAfter.Add(-time.Hour * 24),
152+ NotAfter: leafNotAfter,
153+ KeyUsage: x509.KeyUsageDigitalSignature,
154+ }
155+ leafCertDER, err := x509.CreateCertificate(rand.Reader, tmpl, root, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
156+ if err != nil {
157+ t.Fatalf("CreateCertificate: %v", err)
158+ }
159+ tmpl.NotBefore, tmpl.NotAfter = leafNotAfter.Add(-time.Hour*24*365), leafNotAfter.Add(-time.Hour*24*364)
160+ expiredLeafDERCertDER, err := x509.CreateCertificate(rand.Reader, tmpl, root, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
161+ if err != nil {
162+ t.Fatalf("CreateCertificate: %v", err)
163+ }
164+
165+ return leafCertDER, expiredLeafDERCertDER, root
166+ }
167+ testExpiration := func(name string, leafNotAfter, rootNotAfter time.Time) {
168+ t.Run(name, func(t *testing.T) {
169+ initialLeafDER, expiredLeafDER, initialRoot := createChain(leafNotAfter, rootNotAfter)
170+
171+ serverConfig := testConfig.Clone()
172+ serverConfig.MaxVersion = version
173+ serverConfig.Certificates = []Certificate{{
174+ Certificate: [][]byte{initialLeafDER, expiredLeafDER},
175+ PrivateKey: testECDSAPrivateKey,
176+ }}
177+ serverConfig.ClientCAs = x509.NewCertPool()
178+ serverConfig.ClientCAs.AddCert(initialRoot)
179+ serverConfig.ClientAuth = RequireAndVerifyClientCert
180+ serverConfig.Time = func() time.Time {
181+ return now
182+ }
183+ serverConfig.InsecureSkipVerify = false
184+ serverConfig.ServerName = "expired-resume.example.com"
185+
186+ clientConfig := testConfig.Clone()
187+ clientConfig.MaxVersion = version
188+ clientConfig.Certificates = []Certificate{{
189+ Certificate: [][]byte{initialLeafDER, expiredLeafDER},
190+ PrivateKey: testECDSAPrivateKey,
191+ }}
192+ clientConfig.RootCAs = x509.NewCertPool()
193+ clientConfig.RootCAs.AddCert(initialRoot)
194+ clientConfig.ServerName = "expired-resume.example.com"
195+ clientConfig.ClientSessionCache = NewLRUClientSessionCache(32)
196+ clientConfig.InsecureSkipVerify = false
197+ clientConfig.ServerName = "expired-resume.example.com"
198+ clientConfig.Time = func() time.Time {
199+ return now
200+ }
201+
202+ testResume := func(t *testing.T, sc, cc *Config, expectResume bool) {
203+ t.Helper()
204+ ss, cs, err := testHandshake(t, cc, sc)
205+ if err != nil {
206+ t.Fatalf("handshake: %v", err)
207+ }
208+ if cs.DidResume != expectResume {
209+ t.Fatalf("DidResume = %v; want %v", cs.DidResume, expectResume)
210+ }
211+ if ss.DidResume != expectResume {
212+ t.Fatalf("DidResume = %v; want %v", cs.DidResume, expectResume)
213+ }
214+ }
215+
216+ testResume(t, serverConfig, clientConfig, false)
217+ testResume(t, serverConfig, clientConfig, true)
218+
219+ expiredNow := time.Unix(0, min(leafNotAfter.UnixNano(), rootNotAfter.UnixNano())).Add(time.Minute)
220+
221+ freshLeafDER, expiredLeafDER, freshRoot := createChain(expiredNow.Add(time.Hour), expiredNow.Add(time.Hour))
222+ clientConfig.Certificates = []Certificate{{
223+ Certificate: [][]byte{freshLeafDER, expiredLeafDER},
224+ PrivateKey: testECDSAPrivateKey,
225+ }}
226+ serverConfig.Time = func() time.Time {
227+ return expiredNow
228+ }
229+ serverConfig.ClientCAs = x509.NewCertPool()
230+ serverConfig.ClientCAs.AddCert(freshRoot)
231+
232+ testResume(t, serverConfig, clientConfig, false)
233+ })
234+ }
235+
236+ testExpiration("LeafExpiresBeforeRoot", now.Add(2*time.Hour), now.Add(3*time.Hour))
237+ testExpiration("LeafExpiresAfterRoot", now.Add(2*time.Hour), now.Add(time.Hour))
238+}
239diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go
240index 5aa69e9640..a48a296721 100644
241--- a/src/crypto/tls/handshake_server_tls13.go
242+++ b/src/crypto/tls/handshake_server_tls13.go
243@@ -346,7 +346,7 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error {
244 continue
245 }
246 if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
247- len(sessionState.verifiedChains) == 0 {
248+ !anyUnexpiredChain(sessionState.verifiedChains, c.config.time()) {
249 continue
250 }
251
252--
2532.35.6
diff --git a/meta/recipes-devtools/go/go/CVE-2025-68121_p2.patch b/meta/recipes-devtools/go/go/CVE-2025-68121_p2.patch
new file mode 100644
index 0000000000..8e8cd45019
--- /dev/null
+++ b/meta/recipes-devtools/go/go/CVE-2025-68121_p2.patch
@@ -0,0 +1,385 @@
1From c22ca724688b18d51b4bbf97ec42914a7b2642c5 Mon Sep 17 00:00:00 2001
2From: Roland Shoemaker <roland@golang.org>
3Date: Mon, 26 Jan 2026 11:18:45 -0800
4Subject: [PATCH] [release-branch.go1.24] crypto/tls: check verifiedChains
5 roots when resuming sessions
6
7When resuming TLS sessions, on the server and client verify that the
8chains stored in the session state (verifiedChains) are still acceptable
9with regards to the Config by checking for the inclusion of the root in
10either ClientCAs (server) or RootCAs (client). This prevents resuming
11a session with a certificate chain that would be rejected during a full
12handshake due to an untrusted root.
13
14Updates #77113
15Updates #77355
16Updates CVE-2025-68121
17
18CVE: CVE-2025-68121
19Upstream-Status: Backport [https://github.com/golang/go/commit/cb75daf3b291]
20
21Backport Changes:
22- In src/crypto/tls/common.go, the upstream fix introduces the use of
23 slices.ContainsFunc(). To align with that change, the slices library
24 needs to be imported in our local common.go file as well. Since this
25 package is not available in our current Go version (v1.22), we are
26 adding it manually to resolve the compilation issue.
27- The slices library was originally introduced in Go v1.23 as part of
28 the this commit:https://github.com/golang/go/commit/0b57881571a7
29
30Change-Id: I11fe00909ef1961c24ecf80bf5b97f7b1121d359
31Reviewed-on: https://go-review.googlesource.com/c/go/+/737700
32Auto-Submit: Roland Shoemaker <roland@golang.org>
33Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
34LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
35Reviewed-by: Coia Prant <coiaprant@gmail.com>
36Reviewed-by: Filippo Valsorda <filippo@golang.org>
37Reviewed-on: https://go-review.googlesource.com/c/go/+/740062
38Reviewed-by: Damien Neil <dneil@google.com>
39Reviewed-by: Nicholas Husin <nsh@golang.org>
40Reviewed-by: Nicholas Husin <husin@google.com>
41Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
42(cherry picked from commit cb75daf3b29129620fa4a35ee2d3903e908aeb1c)
43Signed-off-by: Deepak Rathore <deeratho@cisco.com>
44---
45 src/crypto/tls/common.go | 26 ++-
46 src/crypto/tls/handshake_client.go | 7 +-
47 src/crypto/tls/handshake_server.go | 7 +-
48 src/crypto/tls/handshake_server_test.go | 214 +++++++++++++++++++++++
49 src/crypto/tls/handshake_server_tls13.go | 8 +-
50 5 files changed, 254 insertions(+), 8 deletions(-)
51
52diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go
53index 738c7e100b..299d6f32cb 100644
54--- a/src/crypto/tls/common.go
55+++ b/src/crypto/tls/common.go
56@@ -21,6 +21,7 @@ import (
57 "internal/godebug"
58 "io"
59 "net"
60+ "slices"
61 "strings"
62 "sync"
63 "time"
64@@ -1556,13 +1557,28 @@ func (e *CertificateVerificationError) Unwrap() error {
65 return e.Err
66 }
67
68-// anyUnexpiredChain reports if at least one of verifiedChains is still
69-// unexpired. If verifiedChains is empty, it returns false.
70-func anyUnexpiredChain(verifiedChains [][]*x509.Certificate, now time.Time) bool {
71+// anyValidVerifiedChain reports if at least one of the chains in verifiedChains
72+// is valid, as indicated by none of the certificates being expired and the root
73+// being in opts.Roots (or in the system root pool if opts.Roots is nil). If
74+// verifiedChains is empty, it returns false.
75+func anyValidVerifiedChain(verifiedChains [][]*x509.Certificate, opts x509.VerifyOptions) bool {
76 for _, chain := range verifiedChains {
77- if len(chain) != 0 && !slices.ContainsFunc(chain, func(cert *x509.Certificate) bool {
78- return now.Before(cert.NotBefore) || now.After(cert.NotAfter) // cert is expired
79+ if len(chain) == 0 {
80+ continue
81+ }
82+ if slices.ContainsFunc(chain, func(cert *x509.Certificate) bool {
83+ return opts.CurrentTime.Before(cert.NotBefore) || opts.CurrentTime.After(cert.NotAfter)
84 }) {
85+ continue
86+ }
87+ // Since we already validated the chain, we only care that it is
88+ // rooted in a CA in CAs, or in the system pool. On platforms where
89+ // we control chain validation (e.g. not Windows or macOS) this is a
90+ // simple lookup in the CertPool internal hash map. On other
91+ // platforms, this may be more expensive, depending on how they
92+ // implement verification of just root certificates.
93+ root := chain[len(chain)-1]
94+ if _, err := root.Verify(opts); err == nil {
95 return true
96 }
97 }
98diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go
99index c2ff9e1959..c8746b1023 100644
100--- a/src/crypto/tls/handshake_client.go
101+++ b/src/crypto/tls/handshake_client.go
102@@ -337,7 +337,12 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (
103 // application from a faulty ClientSessionCache implementation.
104 return nil, nil, nil, nil
105 }
106- if !anyUnexpiredChain(session.verifiedChains, c.config.time()) {
107+ opts := x509.VerifyOptions{
108+ CurrentTime: c.config.time(),
109+ Roots: c.config.RootCAs,
110+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
111+ }
112+ if !anyValidVerifiedChain(session.verifiedChains, opts) {
113 // No valid chains, delete the entry.
114 c.config.ClientSessionCache.Put(cacheKey, nil)
115 return nil, nil, nil, nil
116diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go
117index 608b2535f1..4e3f5e19fb 100644
118--- a/src/crypto/tls/handshake_server.go
119+++ b/src/crypto/tls/handshake_server.go
120@@ -482,8 +482,13 @@ func (hs *serverHandshakeState) checkForResumption() error {
121 if sessionHasClientCerts && c.config.time().After(sessionState.peerCertificates[0].NotAfter) {
122 return nil
123 }
124+ opts := x509.VerifyOptions{
125+ CurrentTime: c.config.time(),
126+ Roots: c.config.ClientCAs,
127+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
128+ }
129 if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
130- !anyUnexpiredChain(sessionState.verifiedChains, c.config.time()) {
131+ !anyValidVerifiedChain(sessionState.verifiedChains, opts) {
132 return nil
133 }
134
135diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go
136index 9eff106ecf..c44ad51804 100644
137--- a/src/crypto/tls/handshake_server_test.go
138+++ b/src/crypto/tls/handshake_server_test.go
139@@ -2171,3 +2171,217 @@ func testHandshakeChainExpiryResumption(t *testing.T, version uint16) {
140 testExpiration("LeafExpiresBeforeRoot", now.Add(2*time.Hour), now.Add(3*time.Hour))
141 testExpiration("LeafExpiresAfterRoot", now.Add(2*time.Hour), now.Add(time.Hour))
142 }
143+
144+func TestHandshakeGetConfigForClientDifferentClientCAs(t *testing.T) {
145+ t.Run("TLS1.2", func(t *testing.T) {
146+ testHandshakeGetConfigForClientDifferentClientCAs(t, VersionTLS12)
147+ })
148+ t.Run("TLS1.3", func(t *testing.T) {
149+ testHandshakeGetConfigForClientDifferentClientCAs(t, VersionTLS13)
150+ })
151+}
152+
153+func testHandshakeGetConfigForClientDifferentClientCAs(t *testing.T, version uint16) {
154+ now := time.Now()
155+ tmpl := &x509.Certificate{
156+ Subject: pkix.Name{CommonName: "root"},
157+ NotBefore: now.Add(-time.Hour * 24),
158+ NotAfter: now.Add(time.Hour * 24),
159+ IsCA: true,
160+ BasicConstraintsValid: true,
161+ }
162+ rootDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
163+ if err != nil {
164+ t.Fatalf("CreateCertificate: %v", err)
165+ }
166+ rootA, err := x509.ParseCertificate(rootDER)
167+ if err != nil {
168+ t.Fatalf("ParseCertificate: %v", err)
169+ }
170+ rootDER, err = x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
171+ if err != nil {
172+ t.Fatalf("CreateCertificate: %v", err)
173+ }
174+ rootB, err := x509.ParseCertificate(rootDER)
175+ if err != nil {
176+ t.Fatalf("ParseCertificate: %v", err)
177+ }
178+
179+ tmpl = &x509.Certificate{
180+ Subject: pkix.Name{},
181+ DNSNames: []string{"example.com"},
182+ NotBefore: now.Add(-time.Hour * 24),
183+ NotAfter: now.Add(time.Hour * 24),
184+ KeyUsage: x509.KeyUsageDigitalSignature,
185+ }
186+ certDER, err := x509.CreateCertificate(rand.Reader, tmpl, rootA, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
187+ if err != nil {
188+ t.Fatalf("CreateCertificate: %v", err)
189+ }
190+
191+ serverConfig := testConfig.Clone()
192+ serverConfig.MaxVersion = version
193+ serverConfig.Certificates = []Certificate{{
194+ Certificate: [][]byte{certDER},
195+ PrivateKey: testECDSAPrivateKey,
196+ }}
197+ serverConfig.Time = func() time.Time {
198+ return now
199+ }
200+ serverConfig.ClientCAs = x509.NewCertPool()
201+ serverConfig.ClientCAs.AddCert(rootA)
202+ serverConfig.ClientAuth = RequireAndVerifyClientCert
203+ switchConfig := false
204+ serverConfig.GetConfigForClient = func(clientHello *ClientHelloInfo) (*Config, error) {
205+ if !switchConfig {
206+ return nil, nil
207+ }
208+ cfg := serverConfig.Clone()
209+ cfg.ClientCAs = x509.NewCertPool()
210+ cfg.ClientCAs.AddCert(rootB)
211+ return cfg, nil
212+ }
213+ serverConfig.InsecureSkipVerify = false
214+ serverConfig.ServerName = "example.com"
215+
216+ clientConfig := testConfig.Clone()
217+ clientConfig.MaxVersion = version
218+ clientConfig.Certificates = []Certificate{{
219+ Certificate: [][]byte{certDER},
220+ PrivateKey: testECDSAPrivateKey,
221+ }}
222+ clientConfig.ClientSessionCache = NewLRUClientSessionCache(32)
223+ clientConfig.RootCAs = x509.NewCertPool()
224+ clientConfig.RootCAs.AddCert(rootA)
225+ clientConfig.Time = func() time.Time {
226+ return now
227+ }
228+ clientConfig.InsecureSkipVerify = false
229+ clientConfig.ServerName = "example.com"
230+
231+ testResume := func(t *testing.T, sc, cc *Config, expectResume bool) {
232+ t.Helper()
233+ ss, cs, err := testHandshake(t, cc, sc)
234+ if err != nil {
235+ t.Fatalf("handshake: %v", err)
236+ }
237+ if cs.DidResume != expectResume {
238+ t.Fatalf("DidResume = %v; want %v", cs.DidResume, expectResume)
239+ }
240+ if ss.DidResume != expectResume {
241+ t.Fatalf("DidResume = %v; want %v", cs.DidResume, expectResume)
242+ }
243+ }
244+
245+ testResume(t, serverConfig, clientConfig, false)
246+ testResume(t, serverConfig, clientConfig, true)
247+
248+ // Cause GetConfigForClient to return a config cloned from the base config,
249+ // but with a different ClientCAs pool. This should cause resumption to fail.
250+ switchConfig = true
251+
252+ testResume(t, serverConfig, clientConfig, false)
253+ testResume(t, serverConfig, clientConfig, true)
254+}
255+
256+func TestHandshakeChangeRootCAsResumption(t *testing.T) {
257+ t.Run("TLS1.2", func(t *testing.T) {
258+ testHandshakeChangeRootCAsResumption(t, VersionTLS12)
259+ })
260+ t.Run("TLS1.3", func(t *testing.T) {
261+ testHandshakeChangeRootCAsResumption(t, VersionTLS13)
262+ })
263+}
264+
265+func testHandshakeChangeRootCAsResumption(t *testing.T, version uint16) {
266+ now := time.Now()
267+ tmpl := &x509.Certificate{
268+ Subject: pkix.Name{CommonName: "root"},
269+ NotBefore: now.Add(-time.Hour * 24),
270+ NotAfter: now.Add(time.Hour * 24),
271+ IsCA: true,
272+ BasicConstraintsValid: true,
273+ }
274+ rootDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
275+ if err != nil {
276+ t.Fatalf("CreateCertificate: %v", err)
277+ }
278+ rootA, err := x509.ParseCertificate(rootDER)
279+ if err != nil {
280+ t.Fatalf("ParseCertificate: %v", err)
281+ }
282+ rootDER, err = x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
283+ if err != nil {
284+ t.Fatalf("CreateCertificate: %v", err)
285+ }
286+ rootB, err := x509.ParseCertificate(rootDER)
287+ if err != nil {
288+ t.Fatalf("ParseCertificate: %v", err)
289+ }
290+
291+ tmpl = &x509.Certificate{
292+ Subject: pkix.Name{},
293+ DNSNames: []string{"example.com"},
294+ NotBefore: now.Add(-time.Hour * 24),
295+ NotAfter: now.Add(time.Hour * 24),
296+ KeyUsage: x509.KeyUsageDigitalSignature,
297+ }
298+ certDER, err := x509.CreateCertificate(rand.Reader, tmpl, rootA, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
299+ if err != nil {
300+ t.Fatalf("CreateCertificate: %v", err)
301+ }
302+
303+ serverConfig := testConfig.Clone()
304+ serverConfig.MaxVersion = version
305+ serverConfig.Certificates = []Certificate{{
306+ Certificate: [][]byte{certDER},
307+ PrivateKey: testECDSAPrivateKey,
308+ }}
309+ serverConfig.Time = func() time.Time {
310+ return now
311+ }
312+ serverConfig.ClientCAs = x509.NewCertPool()
313+ serverConfig.ClientCAs.AddCert(rootA)
314+ serverConfig.ClientAuth = RequireAndVerifyClientCert
315+ serverConfig.InsecureSkipVerify = false
316+ serverConfig.ServerName = "example.com"
317+
318+ clientConfig := testConfig.Clone()
319+ clientConfig.MaxVersion = version
320+ clientConfig.Certificates = []Certificate{{
321+ Certificate: [][]byte{certDER},
322+ PrivateKey: testECDSAPrivateKey,
323+ }}
324+ clientConfig.ClientSessionCache = NewLRUClientSessionCache(32)
325+ clientConfig.RootCAs = x509.NewCertPool()
326+ clientConfig.RootCAs.AddCert(rootA)
327+ clientConfig.Time = func() time.Time {
328+ return now
329+ }
330+ clientConfig.InsecureSkipVerify = false
331+ clientConfig.ServerName = "example.com"
332+
333+ testResume := func(t *testing.T, sc, cc *Config, expectResume bool) {
334+ t.Helper()
335+ ss, cs, err := testHandshake(t, cc, sc)
336+ if err != nil {
337+ t.Fatalf("handshake: %v", err)
338+ }
339+ if cs.DidResume != expectResume {
340+ t.Fatalf("DidResume = %v; want %v", cs.DidResume, expectResume)
341+ }
342+ if ss.DidResume != expectResume {
343+ t.Fatalf("DidResume = %v; want %v", cs.DidResume, expectResume)
344+ }
345+ }
346+
347+ testResume(t, serverConfig, clientConfig, false)
348+ testResume(t, serverConfig, clientConfig, true)
349+
350+ clientConfig = clientConfig.Clone()
351+ clientConfig.RootCAs = x509.NewCertPool()
352+ clientConfig.RootCAs.AddCert(rootB)
353+
354+ testResume(t, serverConfig, clientConfig, false)
355+ testResume(t, serverConfig, clientConfig, true)
356+}
357diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go
358index a48a296721..1ecee3a867 100644
359--- a/src/crypto/tls/handshake_server_tls13.go
360+++ b/src/crypto/tls/handshake_server_tls13.go
361@@ -11,6 +11,7 @@ import (
362 "crypto/hmac"
363 "crypto/rsa"
364 "encoding/binary"
365+ "crypto/x509"
366 "errors"
367 "hash"
368 "io"
369@@ -345,8 +346,13 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error {
370 if sessionHasClientCerts && c.config.time().After(sessionState.peerCertificates[0].NotAfter) {
371 continue
372 }
373+ opts := x509.VerifyOptions{
374+ CurrentTime: c.config.time(),
375+ Roots: c.config.ClientCAs,
376+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
377+ }
378 if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
379- !anyUnexpiredChain(sessionState.verifiedChains, c.config.time()) {
380+ !anyValidVerifiedChain(sessionState.verifiedChains, opts) {
381 continue
382 }
383
384--
3852.35.6
diff --git a/meta/recipes-devtools/go/go/CVE-2025-68121_p3.patch b/meta/recipes-devtools/go/go/CVE-2025-68121_p3.patch
new file mode 100644
index 0000000000..40266f9f9e
--- /dev/null
+++ b/meta/recipes-devtools/go/go/CVE-2025-68121_p3.patch
@@ -0,0 +1,82 @@
1From f38ac662b21e333b77951848a7e0549e4f69799e Mon Sep 17 00:00:00 2001
2From: Filippo Valsorda <filippo@golang.org>
3Date: Thu, 29 Jan 2026 11:32:25 +0100
4Subject: [PATCH] [release-branch.go1.24] crypto/tls: document resumption
5 behavior across Configs
6
7Updates #77113
8Updates #77217
9Updates CVE-2025-68121
10
11CVE: CVE-2025-68121
12Upstream-Status: Backport [https://github.com/golang/go/commit/6a501314718b]
13
14Change-Id: Ia47904a9ed001275aad0243a6a0ce57e6a6a6964
15Reviewed-on: https://go-review.googlesource.com/c/go/+/740240
16LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
17Reviewed-by: Roland Shoemaker <roland@golang.org>
18Reviewed-by: Michael Pratt <mpratt@google.com>
19Auto-Submit: Filippo Valsorda <filippo@golang.org>
20(cherry picked from commit 1c9abbdc8e9032cd613bd147c78b166ebacc8a2e)
21Reviewed-on: https://go-review.googlesource.com/c/go/+/741180
22Auto-Submit: Michael Pratt <mpratt@google.com>
23(cherry picked from commit 6a501314718b6d69bad1723b3065ca6067b560ea)
24Signed-off-by: Deepak Rathore <deeratho@cisco.com>
25---
26 src/crypto/tls/common.go | 26 +++++++++++++++++++-------
27 1 file changed, 19 insertions(+), 7 deletions(-)
28
29diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go
30index 299d6f32cb..348bdf0866 100644
31--- a/src/crypto/tls/common.go
32+++ b/src/crypto/tls/common.go
33@@ -595,10 +595,13 @@ type Config struct {
34 // If GetConfigForClient is nil, the Config passed to Server() will be
35 // used for all connections.
36 //
37- // If SessionTicketKey was explicitly set on the returned Config, or if
38- // SetSessionTicketKeys was called on the returned Config, those keys will
39+ // If SessionTicketKey is explicitly set on the returned Config, or if
40+ // SetSessionTicketKeys is called on the returned Config, those keys will
41 // be used. Otherwise, the original Config keys will be used (and possibly
42- // rotated if they are automatically managed).
43+ // rotated if they are automatically managed). WARNING: this allows session
44+ // resumtion of connections originally established with the parent (or a
45+ // sibling) Config, which may bypass the [Config.VerifyPeerCertificate]
46+ // value of the returned Config.
47 GetConfigForClient func(*ClientHelloInfo) (*Config, error)
48
49 // VerifyPeerCertificate, if not nil, is called after normal
50@@ -616,8 +619,10 @@ type Config struct {
51 // rawCerts may be empty on the server if ClientAuth is RequestClientCert or
52 // VerifyClientCertIfGiven.
53 //
54- // This callback is not invoked on resumed connections, as certificates are
55- // not re-verified on resumption.
56+ // This callback is not invoked on resumed connections. WARNING: this
57+ // includes connections resumed across Configs returned by [Config.Clone] or
58+ // [Config.GetConfigForClient] and their parents. If that is not intended,
59+ // use [Config.VerifyConnection] instead, or set [Config.SessionTicketsDisabled].
60 //
61 // verifiedChains and its contents should not be modified.
62 VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
63@@ -825,8 +830,15 @@ func (c *Config) ticketKeyFromBytes(b [32]byte) (key ticketKey) {
64 // ticket, and the lifetime we set for all tickets we send.
65 const maxSessionTicketLifetime = 7 * 24 * time.Hour
66
67-// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a [Config] that is
68-// being used concurrently by a TLS client or server.
69+// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a
70+// [Config] that is being used concurrently by a TLS client or server.
71+//
72+// The returned Config can share session ticket keys with the original Config,
73+// which means connections could be resumed across the two Configs. WARNING:
74+// [Config.VerifyPeerCertificate] does not get called on resumed connections,
75+// including connections that were originally established on the parent Config.
76+// If that is not intended, use [Config.VerifyConnection] instead, or set
77+// [Config.SessionTicketsDisabled].
78 func (c *Config) Clone() *Config {
79 if c == nil {
80 return nil
81--
822.35.6