summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArchana Polampalli <archana.polampalli@windriver.com>2025-11-07 15:50:58 +0530
committerSteve Sakoman <steve@sakoman.com>2025-11-14 06:45:29 -0800
commita6d452646eb9cabbc5e3ef86caf2d80e571fd690 (patch)
tree91e9cbd4d4083356fedad483a50a86407e47840b
parent0c4e0286278d0a818fc8026513b4698f48827f9c (diff)
downloadpoky-a6d452646eb9cabbc5e3ef86caf2d80e571fd690.tar.gz
go: fix CVE-2025-58187
Due to the design of the name constraint checking algorithm, the processing time of some inputs scals non-linearly with respect to the size of the certificate. This affects programs which validate arbitrary certificate chains. (From OE-Core rev: ce1626d1f1e232bc6da81e89088d0c0f5f3c52b4) Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-devtools/go/go-1.22.12.inc1
-rw-r--r--meta/recipes-devtools/go/go/CVE-2025-58187.patch349
2 files changed, 350 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 38992219c8..a1c14ea684 100644
--- a/meta/recipes-devtools/go/go-1.22.12.inc
+++ b/meta/recipes-devtools/go/go-1.22.12.inc
@@ -22,6 +22,7 @@ SRC_URI += "\
22 file://CVE-2025-47907.patch \ 22 file://CVE-2025-47907.patch \
23 file://CVE-2025-47906.patch \ 23 file://CVE-2025-47906.patch \
24 file://CVE-2025-58185.patch \ 24 file://CVE-2025-58185.patch \
25 file://CVE-2025-58187.patch \
25" 26"
26SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71" 27SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71"
27 28
diff --git a/meta/recipes-devtools/go/go/CVE-2025-58187.patch b/meta/recipes-devtools/go/go/CVE-2025-58187.patch
new file mode 100644
index 0000000000..d3b7dd5264
--- /dev/null
+++ b/meta/recipes-devtools/go/go/CVE-2025-58187.patch
@@ -0,0 +1,349 @@
1From f334417e71f8b078ad64035bddb6df7f8910da6c Mon Sep 17 00:00:00 2001
2From: Neal Patel <nealpatel@google.com>
3Date: Mon, 15 Sep 2025 16:31:22 -0400
4Subject: [PATCH] [release-branch.go1.24] crypto/x509: improve domain name
5 verification
6
7Don't use domainToReverseLabels to check if domain names are
8valid, since it is not particularly performant, and can contribute to DoS
9vectors. Instead just iterate over the name and enforce the properties we
10care about.
11
12This also enforces that DNS names, both in SANs and name constraints,
13are valid. We previously allowed invalid SANs, because some
14intermediates had these weird names (see #23995), but there are
15currently no trusted intermediates that have this property, and since we
16target the web PKI, supporting this particular case is not a high
17priority.
18
19Thank you to Jakub Ciolek for reporting this issue.
20
21Fixes CVE-2025-58187
22For #75681
23Fixes #75714
24
25Change-Id: I6ebce847dcbe5fc63ef2f9a74f53f11c4c56d3d1
26Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2820
27Reviewed-by: Damien Neil <dneil@google.com>
28Reviewed-by: Roland Shoemaker <bracewell@google.com>
29Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2982
30Reviewed-by: Nicholas Husin <husin@google.com>
31Reviewed-on: https://go-review.googlesource.com/c/go/+/709839
32Auto-Submit: Michael Pratt <mpratt@google.com>
33Reviewed-by: Carlos Amedee <carlos@golang.org>
34TryBot-Bypass: Michael Pratt <mpratt@google.com>
35
36CVE: CVE-2025-58187
37
38Upstream-Status: Backport [https://github.com/golang/go/commit/f334417e71f8b078ad64035bddb6df7f8910da6c]
39
40Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
41---
42 src/crypto/x509/name_constraints_test.go | 66 ++------------------
43 src/crypto/x509/parser.go | 77 ++++++++++++++----------
44 src/crypto/x509/parser_test.go | 43 +++++++++++++
45 src/crypto/x509/verify.go | 1 +
46 4 files changed, 95 insertions(+), 92 deletions(-)
47
48diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go
49index 78263fc..9aaa6d7 100644
50--- a/src/crypto/x509/name_constraints_test.go
51+++ b/src/crypto/x509/name_constraints_test.go
52@@ -1456,63 +1456,7 @@ var nameConstraintsTests = []nameConstraintsTest{
53 expectedError: "incompatible key usage",
54 },
55
56- // An invalid DNS SAN should be detected only at validation time so
57- // that we can process CA certificates in the wild that have invalid SANs.
58- // See https://github.com/golang/go/issues/23995
59-
60- // #77: an invalid DNS or mail SAN will not be detected if name constraint
61- // checking is not triggered.
62- {
63- roots: make([]constraintsSpec, 1),
64- intermediates: [][]constraintsSpec{
65- {
66- {},
67- },
68- },
69- leaf: leafSpec{
70- sans: []string{"dns:this is invalid", "email:this @ is invalid"},
71- },
72- },
73-
74- // #78: an invalid DNS SAN will be detected if any name constraint checking
75- // is triggered.
76- {
77- roots: []constraintsSpec{
78- {
79- bad: []string{"uri:"},
80- },
81- },
82- intermediates: [][]constraintsSpec{
83- {
84- {},
85- },
86- },
87- leaf: leafSpec{
88- sans: []string{"dns:this is invalid"},
89- },
90- expectedError: "cannot parse dnsName",
91- },
92-
93- // #79: an invalid email SAN will be detected if any name constraint
94- // checking is triggered.
95- {
96- roots: []constraintsSpec{
97- {
98- bad: []string{"uri:"},
99- },
100- },
101- intermediates: [][]constraintsSpec{
102- {
103- {},
104- },
105- },
106- leaf: leafSpec{
107- sans: []string{"email:this @ is invalid"},
108- },
109- expectedError: "cannot parse rfc822Name",
110- },
111-
112- // #80: if several EKUs are requested, satisfying any of them is sufficient.
113+ // #77: if several EKUs are requested, satisfying any of them is sufficient.
114 {
115 roots: make([]constraintsSpec, 1),
116 intermediates: [][]constraintsSpec{
117@@ -1527,7 +1471,7 @@ var nameConstraintsTests = []nameConstraintsTest{
118 requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageEmailProtection},
119 },
120
121- // #81: EKUs that are not asserted in VerifyOpts are not required to be
122+ // #78: EKUs that are not asserted in VerifyOpts are not required to be
123 // nested.
124 {
125 roots: make([]constraintsSpec, 1),
126@@ -1546,7 +1490,7 @@ var nameConstraintsTests = []nameConstraintsTest{
127 },
128 },
129
130- // #82: a certificate without SANs and CN is accepted in a constrained chain.
131+ // #79: a certificate without SANs and CN is accepted in a constrained chain.
132 {
133 roots: []constraintsSpec{
134 {
135@@ -1563,7 +1507,7 @@ var nameConstraintsTests = []nameConstraintsTest{
136 },
137 },
138
139- // #83: a certificate without SANs and with a CN that does not parse as a
140+ // #80: a certificate without SANs and with a CN that does not parse as a
141 // hostname is accepted in a constrained chain.
142 {
143 roots: []constraintsSpec{
144@@ -1582,7 +1526,7 @@ var nameConstraintsTests = []nameConstraintsTest{
145 },
146 },
147
148- // #84: a certificate with SANs and CN is accepted in a constrained chain.
149+ // #81: a certificate with SANs and CN is accepted in a constrained chain.
150 {
151 roots: []constraintsSpec{
152 {
153diff --git a/src/crypto/x509/parser.go b/src/crypto/x509/parser.go
154index 812b0d2..9a3bcd6 100644
155--- a/src/crypto/x509/parser.go
156+++ b/src/crypto/x509/parser.go
157@@ -378,10 +378,14 @@ func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string
158 if err := isIA5String(email); err != nil {
159 return errors.New("x509: SAN rfc822Name is malformed")
160 }
161+ parsed, ok := parseRFC2821Mailbox(email)
162+ if !ok || (ok && !domainNameValid(parsed.domain, false)) {
163+ return errors.New("x509: SAN rfc822Name is malformed")
164+ }
165 emailAddresses = append(emailAddresses, email)
166 case nameTypeDNS:
167 name := string(data)
168- if err := isIA5String(name); err != nil {
169+ if err := isIA5String(name); err != nil || (err == nil && !domainNameValid(name, false)) {
170 return errors.New("x509: SAN dNSName is malformed")
171 }
172 dnsNames = append(dnsNames, string(name))
173@@ -391,14 +395,9 @@ func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string
174 return errors.New("x509: SAN uniformResourceIdentifier is malformed")
175 }
176 uri, err := url.Parse(uriStr)
177- if err != nil {
178+ if err != nil || (err == nil && uri.Host != "" && !domainNameValid(uri.Host, false)) {
179 return fmt.Errorf("x509: cannot parse URI %q: %s", uriStr, err)
180 }
181- if len(uri.Host) > 0 {
182- if _, ok := domainToReverseLabels(uri.Host); !ok {
183- return fmt.Errorf("x509: cannot parse URI %q: invalid domain", uriStr)
184- }
185- }
186 uris = append(uris, uri)
187 case nameTypeIP:
188 switch len(data) {
189@@ -538,15 +537,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
190 return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
191 }
192
193- trimmedDomain := domain
194- if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' {
195- // constraints can have a leading
196- // period to exclude the domain
197- // itself, but that's not valid in a
198- // normal domain name.
199- trimmedDomain = trimmedDomain[1:]
200- }
201- if _, ok := domainToReverseLabels(trimmedDomain); !ok {
202+ if !domainNameValid(domain, true) {
203 return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse dnsName constraint %q", domain)
204 }
205 dnsNames = append(dnsNames, domain)
206@@ -587,12 +578,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
207 return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
208 }
209 } else {
210- // Otherwise it's a domain name.
211- domain := constraint
212- if len(domain) > 0 && domain[0] == '.' {
213- domain = domain[1:]
214- }
215- if _, ok := domainToReverseLabels(domain); !ok {
216+ if !domainNameValid(constraint, true) {
217 return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
218 }
219 }
220@@ -608,15 +594,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
221 return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q: cannot be IP address", domain)
222 }
223
224- trimmedDomain := domain
225- if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' {
226- // constraints can have a leading
227- // period to exclude the domain itself,
228- // but that's not valid in a normal
229- // domain name.
230- trimmedDomain = trimmedDomain[1:]
231- }
232- if _, ok := domainToReverseLabels(trimmedDomain); !ok {
233+ if !domainNameValid(domain, true) {
234 return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q", domain)
235 }
236 uriDomains = append(uriDomains, domain)
237@@ -1197,3 +1175,40 @@ func ParseRevocationList(der []byte) (*RevocationList, error) {
238
239 return rl, nil
240 }
241+
242+// domainNameValid does minimal domain name validity checking. In particular it
243+// enforces the following properties:
244+// - names cannot have the trailing period
245+// - names can only have a leading period if constraint is true
246+// - names must be <= 253 characters
247+// - names cannot have empty labels
248+// - names cannot labels that are longer than 63 characters
249+//
250+// Note that this does not enforce the LDH requirements for domain names.
251+func domainNameValid(s string, constraint bool) bool {
252+ if len(s) == 0 && constraint {
253+ return true
254+ }
255+ if len(s) == 0 || (!constraint && s[0] == '.') || s[len(s)-1] == '.' || len(s) > 253 {
256+ return false
257+ }
258+ lastDot := -1
259+ if constraint && s[0] == '.' {
260+ s = s[1:]
261+ }
262+
263+ for i := 0; i <= len(s); i++ {
264+ if i == len(s) || s[i] == '.' {
265+ labelLen := i
266+ if lastDot >= 0 {
267+ labelLen -= lastDot + 1
268+ }
269+ if labelLen == 0 || labelLen > 63 {
270+ return false
271+ }
272+ lastDot = i
273+ }
274+ }
275+
276+ return true
277+}
278diff --git a/src/crypto/x509/parser_test.go b/src/crypto/x509/parser_test.go
279index b31f9cd..a6cdfb8 100644
280--- a/src/crypto/x509/parser_test.go
281+++ b/src/crypto/x509/parser_test.go
282@@ -6,6 +6,7 @@ package x509
283
284 import (
285 "encoding/asn1"
286+ "strings"
287 "testing"
288
289 cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
290@@ -101,3 +102,45 @@ func TestParseASN1String(t *testing.T) {
291 })
292 }
293 }
294+
295+func TestDomainNameValid(t *testing.T) {
296+ for _, tc := range []struct {
297+ name string
298+ dnsName string
299+ constraint bool
300+ valid bool
301+ }{
302+ {"empty name, name", "", false, false},
303+ {"empty name, constraint", "", true, true},
304+ {"empty label, name", "a..a", false, false},
305+ {"empty label, constraint", "a..a", true, false},
306+ {"period, name", ".", false, false},
307+ {"period, constraint", ".", true, false}, // TODO(roland): not entirely clear if this is a valid constraint (require at least one label?)
308+ {"valid, name", "a.b.c", false, true},
309+ {"valid, constraint", "a.b.c", true, true},
310+ {"leading period, name", ".a.b.c", false, false},
311+ {"leading period, constraint", ".a.b.c", true, true},
312+ {"trailing period, name", "a.", false, false},
313+ {"trailing period, constraint", "a.", true, false},
314+ {"bare label, name", "a", false, true},
315+ {"bare label, constraint", "a", true, true},
316+ {"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, false},
317+ {"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, false},
318+ {"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, false},
319+ {"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, false},
320+ {"64 char single label, name", strings.Repeat("a", 64), false, false},
321+ {"64 char single label, constraint", strings.Repeat("a", 64), true, false},
322+ {"63 char single label, name", strings.Repeat("a", 63), false, true},
323+ {"63 char single label, constraint", strings.Repeat("a", 63), true, true},
324+ {"64 char label, name", "a." + strings.Repeat("a", 64), false, false},
325+ {"64 char label, constraint", "a." + strings.Repeat("a", 64), true, false},
326+ {"63 char label, name", "a." + strings.Repeat("a", 63), false, true},
327+ {"63 char label, constraint", "a." + strings.Repeat("a", 63), true, true},
328+ } {
329+ t.Run(tc.name, func(t *testing.T) {
330+ if tc.valid != domainNameValid(tc.dnsName, tc.constraint) {
331+ t.Errorf("domainNameValid(%q, %t) = %v; want %v", tc.dnsName, tc.constraint, !tc.valid, tc.valid)
332+ }
333+ })
334+ }
335+}
336diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
337index 2d2a271..4502d4c 100644
338--- a/src/crypto/x509/verify.go
339+++ b/src/crypto/x509/verify.go
340@@ -360,6 +360,7 @@ func parseRFC2821Mailbox(in string) (mailbox rfc2821Mailbox, ok bool) {
341 // domainToReverseLabels converts a textual domain name like foo.example.com to
342 // the list of labels in reverse order, e.g. ["com", "example", "foo"].
343 func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
344+ reverseLabels = make([]string, 0, strings.Count(domain, ".")+1)
345 for len(domain) > 0 {
346 if i := strings.LastIndexByte(domain, '.'); i == -1 {
347 reverseLabels = append(reverseLabels, domain)
348--
3492.40.0