summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBhabu Bindu <bhabu.bindu@kpit.com>2023-05-29 17:02:45 +0530
committerSteve Sakoman <steve@sakoman.com>2023-06-01 16:24:07 -1000
commit011b8b47588bc9c7b24fa009d170e91b188f094f (patch)
tree00b45e916509699f3de0d80bcb9da964b752332e
parentd68f782872c3159630bd3fd34bf942242686e49c (diff)
downloadpoky-011b8b47588bc9c7b24fa009d170e91b188f094f.tar.gz
curl: Fix CVE-2023-28321
Add patch to fix CVE-2023-28321 IDN wildcard match curl supports matching of wildcard patterns when listed as "Subject Alternative Name" in TLS server certificates. curl can be built to use its own name matching function for TLS rather than one provided by a TLS library. This private wildcard matching function would match IDN (International Domain Name)hosts incorrectly and could as a result accept patterns that otherwise should mismatch. IDN hostnames are converted to puny code before used for certificate checks. Puny coded names always start with `xn--` and should not be allowed to pattern match, but the wildcard check in curl could still check for `x*`,which would match even though the IDN name most likely contained nothing even resembling an `x`. Link: https://curl.se/docs/CVE-2023-28321.html (From OE-Core rev: 75d8593ab3b090266fd2cde27ddc56ad88de7ac7) Signed-off-by: Bhabu Bindu <bhabu.bindu@kpit.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-support/curl/curl/CVE-2023-28321.patch302
-rw-r--r--meta/recipes-support/curl/curl_7.82.0.bb1
2 files changed, 303 insertions, 0 deletions
diff --git a/meta/recipes-support/curl/curl/CVE-2023-28321.patch b/meta/recipes-support/curl/curl/CVE-2023-28321.patch
new file mode 100644
index 0000000000..bcd8b112db
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2023-28321.patch
@@ -0,0 +1,302 @@
1From 199f2d440d8659b42670c1b796220792b01a97bf Mon Sep 17 00:00:00 2001
2From: Daniel Stenberg <daniel@haxx.se>
3Date: Mon, 24 Apr 2023 21:07:02 +0200
4Subject: [PATCH] hostcheck: fix host name wildcard checking
5
6The leftmost "label" of the host name can now only match against single
7'*'. Like the browsers have worked for a long time.
8
9- extended unit test 1397 for this
10- move some SOURCE variables from unit/Makefile.am to unit/Makefile.inc
11
12Reported-by: Hiroki Kurosawa
13Closes #11018
14
15CVE: CVE-2023-28321
16Upstream-Status: Backport [https://github.com/curl/curl/commit/199f2d440d8659b42]
17Comments: Hunks removed as changes already exist
18Removed hunks from files:
19tests/unit/Makefile.am
20tests/unit/Makefile.inc
21Signed-off-by: Bhabu Bindu <bhabu.bindu@kpit.com>
22---
23 lib/vtls/hostcheck.c | 50 +++++++--------
24 tests/data/test1397 | 10 ++-
25 tests/unit/Makefile.am | 94 ----------------------------
26 tests/unit/Makefile.inc | 94 ++++++++++++++++++++++++++++
27 tests/unit/unit1397.c | 134 ++++++++++++++++++++++++----------------
28 5 files changed, 202 insertions(+), 180 deletions(-)
29
30diff --git a/lib/vtls/hostcheck.c b/lib/vtls/hostcheck.c
31index e827dc58f378c..d061c6356f97f 100644
32--- a/lib/vtls/hostcheck.c
33+++ b/lib/vtls/hostcheck.c
34@@ -71,7 +71,12 @@ static bool pmatch(const char *hostname, size_t hostlen,
35 * apparent distinction between a name and an IP. We need to detect the use of
36 * an IP address and not wildcard match on such names.
37 *
38+ * Only match on "*" being used for the leftmost label, not "a*", "a*b" nor
39+ * "*b".
40+ *
41 * Return TRUE on a match. FALSE if not.
42+ *
43+ * @unittest: 1397
44 */
45
46 static bool hostmatch(const char *hostname,
47@@ -79,53 +84,42 @@ static bool hostmatch(const char *hostname,
48 const char *pattern,
49 size_t patternlen)
50 {
51- const char *pattern_label_end, *wildcard, *hostname_label_end;
52- size_t prefixlen, suffixlen;
53+ const char *pattern_label_end;
54
55- /* normalize pattern and hostname by stripping off trailing dots */
56+ DEBUGASSERT(pattern);
57 DEBUGASSERT(patternlen);
58+ DEBUGASSERT(hostname);
59+ DEBUGASSERT(hostlen);
60+
61+ /* normalize pattern and hostname by stripping off trailing dots */
62 if(hostname[hostlen-1]=='.')
63 hostlen--;
64 if(pattern[patternlen-1]=='.')
65 patternlen--;
66
67- wildcard = memchr(pattern, '*', patternlen);
68- if(!wildcard)
69+ if(strncmp(pattern, "*.", 2))
70 return pmatch(hostname, hostlen, pattern, patternlen);
71
72 /* detect IP address as hostname and fail the match if so */
73- if(Curl_host_is_ipnum(hostname))
74+ else if(Curl_host_is_ipnum(hostname))
75 return FALSE;
76
77 /* We require at least 2 dots in the pattern to avoid too wide wildcard
78 match. */
79 pattern_label_end = memchr(pattern, '.', patternlen);
80 if(!pattern_label_end ||
81- (memrchr(pattern, '.', patternlen) == pattern_label_end) ||
82- strncasecompare(pattern, "xn--", 4))
83+ (memrchr(pattern, '.', patternlen) == pattern_label_end))
84 return pmatch(hostname, hostlen, pattern, patternlen);
85-
86- hostname_label_end = memchr(hostname, '.', hostlen);
87- if(!hostname_label_end)
88- return FALSE;
89 else {
90- size_t skiphost = hostname_label_end - hostname;
91- size_t skiplen = pattern_label_end - pattern;
92- if(!pmatch(hostname_label_end, hostlen - skiphost,
93- pattern_label_end, patternlen - skiplen))
94- return FALSE;
95+ const char *hostname_label_end = memchr(hostname, '.', hostlen);
96+ if(hostname_label_end) {
97+ size_t skiphost = hostname_label_end - hostname;
98+ size_t skiplen = pattern_label_end - pattern;
99+ return pmatch(hostname_label_end, hostlen - skiphost,
100+ pattern_label_end, patternlen - skiplen);
101+ }
102 }
103- /* The wildcard must match at least one character, so the left-most
104- label of the hostname is at least as large as the left-most label
105- of the pattern. */
106- if(hostname_label_end - hostname < pattern_label_end - pattern)
107- return FALSE;
108-
109- prefixlen = wildcard - pattern;
110- suffixlen = pattern_label_end - (wildcard + 1);
111- return strncasecompare(pattern, hostname, prefixlen) &&
112- strncasecompare(wildcard + 1, hostname_label_end - suffixlen,
113- suffixlen) ? TRUE : FALSE;
114+ return FALSE;
115 }
116
117 /*
118diff --git a/tests/data/test1397 b/tests/data/test1397
119index 84f962abebee3..f31b2c2a3f330 100644
120--- a/tests/data/test1397
121+++ b/tests/data/test1397
122@@ -2,8 +2,7 @@
123 <info>
124 <keywords>
125 unittest
126-ssl
127-wildcard
128+Curl_cert_hostcheck
129 </keywords>
130 </info>
131
132@@ -16,9 +15,8 @@ none
133 <features>
134 unittest
135 </features>
136- <name>
137-Check wildcard certificate matching function Curl_cert_hostcheck
138- </name>
139+<name>
140+Curl_cert_hostcheck unit tests
141+</name>
142 </client>
143-
144 </testcase>
145diff --git a/tests/unit/unit1397.c b/tests/unit/unit1397.c
146index 2f3d3aa4d09e1..3ae75618d5d10 100644
147--- a/tests/unit/unit1397.c
148+++ b/tests/unit/unit1397.c
149@@ -23,7 +23,6 @@
150 ***************************************************************************/
151 #include "curlcheck.h"
152
153-#include "vtls/hostcheck.h" /* from the lib dir */
154
155 static CURLcode unit_setup(void)
156 {
157@@ -32,63 +31,94 @@ static CURLcode unit_setup(void)
158
159 static void unit_stop(void)
160 {
161- /* done before shutting down and exiting */
162 }
163
164-UNITTEST_START
165-
166 /* only these backends define the tested functions */
167-#if defined(USE_OPENSSL) || defined(USE_GSKIT)
168-
169- /* here you start doing things and checking that the results are good */
170+#if defined(USE_OPENSSL) || defined(USE_GSKIT) || defined(USE_SCHANNEL)
171+#include "vtls/hostcheck.h"
172+struct testcase {
173+ const char *host;
174+ const char *pattern;
175+ bool match;
176+};
177
178-fail_unless(Curl_cert_hostcheck(STRCONST("www.example.com"),
179- STRCONST("www.example.com")), "good 1");
180-fail_unless(Curl_cert_hostcheck(STRCONST("*.example.com"),
181- STRCONST("www.example.com")),
182- "good 2");
183-fail_unless(Curl_cert_hostcheck(STRCONST("xxx*.example.com"),
184- STRCONST("xxxwww.example.com")), "good 3");
185-fail_unless(Curl_cert_hostcheck(STRCONST("f*.example.com"),
186- STRCONST("foo.example.com")), "good 4");
187-fail_unless(Curl_cert_hostcheck(STRCONST("192.168.0.0"),
188- STRCONST("192.168.0.0")), "good 5");
189+static struct testcase tests[] = {
190+ {"", "", FALSE},
191+ {"a", "", FALSE},
192+ {"", "b", FALSE},
193+ {"a", "b", FALSE},
194+ {"aa", "bb", FALSE},
195+ {"\xff", "\xff", TRUE},
196+ {"aa.aa.aa", "aa.aa.bb", FALSE},
197+ {"aa.aa.aa", "aa.aa.aa", TRUE},
198+ {"aa.aa.aa", "*.aa.bb", FALSE},
199+ {"aa.aa.aa", "*.aa.aa", TRUE},
200+ {"192.168.0.1", "192.168.0.1", TRUE},
201+ {"192.168.0.1", "*.168.0.1", FALSE},
202+ {"192.168.0.1", "*.0.1", FALSE},
203+ {"h.ello", "*.ello", FALSE},
204+ {"h.ello.", "*.ello", FALSE},
205+ {"h.ello", "*.ello.", FALSE},
206+ {"h.e.llo", "*.e.llo", TRUE},
207+ {"h.e.llo", " *.e.llo", FALSE},
208+ {" h.e.llo", "*.e.llo", TRUE},
209+ {"h.e.llo.", "*.e.llo", TRUE},
210+ {"*.e.llo.", "*.e.llo", TRUE},
211+ {"************.e.llo.", "*.e.llo", TRUE},
212+ {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
213+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
214+ "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
215+ "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
216+ "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
217+ ".e.llo.", "*.e.llo", TRUE},
218+ {"\xfe\xfe.e.llo.", "*.e.llo", TRUE},
219+ {"h.e.llo.", "*.e.llo.", TRUE},
220+ {"h.e.llo", "*.e.llo.", TRUE},
221+ {".h.e.llo", "*.e.llo.", FALSE},
222+ {"h.e.llo", "*.*.llo.", FALSE},
223+ {"h.e.llo", "h.*.llo", FALSE},
224+ {"h.e.llo", "h.e.*", FALSE},
225+ {"hello", "*.ello", FALSE},
226+ {"hello", "**llo", FALSE},
227+ {"bar.foo.example.com", "*.example.com", FALSE},
228+ {"foo.example.com", "*.example.com", TRUE},
229+ {"baz.example.net", "b*z.example.net", FALSE},
230+ {"foobaz.example.net", "*baz.example.net", FALSE},
231+ {"xn--l8j.example.local", "x*.example.local", FALSE},
232+ {"xn--l8j.example.net", "*.example.net", TRUE},
233+ {"xn--l8j.example.net", "*j.example.net", FALSE},
234+ {"xn--l8j.example.net", "xn--l8j.example.net", TRUE},
235+ {"xn--l8j.example.net", "xn--l8j.*.net", FALSE},
236+ {"xl8j.example.net", "*.example.net", TRUE},
237+ {"fe80::3285:a9ff:fe46:b619", "*::3285:a9ff:fe46:b619", FALSE},
238+ {"fe80::3285:a9ff:fe46:b619", "fe80::3285:a9ff:fe46:b619", TRUE},
239+ {NULL, NULL, FALSE}
240+};
241
242-fail_if(Curl_cert_hostcheck(STRCONST("xxx.example.com"),
243- STRCONST("www.example.com")), "bad 1");
244-fail_if(Curl_cert_hostcheck(STRCONST("*"),
245- STRCONST("www.example.com")),"bad 2");
246-fail_if(Curl_cert_hostcheck(STRCONST("*.*.com"),
247- STRCONST("www.example.com")), "bad 3");
248-fail_if(Curl_cert_hostcheck(STRCONST("*.example.com"),
249- STRCONST("baa.foo.example.com")), "bad 4");
250-fail_if(Curl_cert_hostcheck(STRCONST("f*.example.com"),
251- STRCONST("baa.example.com")), "bad 5");
252-fail_if(Curl_cert_hostcheck(STRCONST("*.com"),
253- STRCONST("example.com")), "bad 6");
254-fail_if(Curl_cert_hostcheck(STRCONST("*fail.com"),
255- STRCONST("example.com")), "bad 7");
256-fail_if(Curl_cert_hostcheck(STRCONST("*.example."),
257- STRCONST("www.example.")), "bad 8");
258-fail_if(Curl_cert_hostcheck(STRCONST("*.example."),
259- STRCONST("www.example")), "bad 9");
260-fail_if(Curl_cert_hostcheck(STRCONST(""), STRCONST("www")), "bad 10");
261-fail_if(Curl_cert_hostcheck(STRCONST("*"), STRCONST("www")), "bad 11");
262-fail_if(Curl_cert_hostcheck(STRCONST("*.168.0.0"),
263- STRCONST("192.168.0.0")), "bad 12");
264-fail_if(Curl_cert_hostcheck(STRCONST("www.example.com"),
265- STRCONST("192.168.0.0")), "bad 13");
266-
267-#ifdef ENABLE_IPV6
268-fail_if(Curl_cert_hostcheck(STRCONST("*::3285:a9ff:fe46:b619"),
269- STRCONST("fe80::3285:a9ff:fe46:b619")), "bad 14");
270-fail_unless(Curl_cert_hostcheck(STRCONST("fe80::3285:a9ff:fe46:b619"),
271- STRCONST("fe80::3285:a9ff:fe46:b619")),
272- "good 6");
273-#endif
274+UNITTEST_START
275+{
276+ int i;
277+ for(i = 0; tests[i].host; i++) {
278+ if(tests[i].match != Curl_cert_hostcheck(tests[i].pattern,
279+ strlen(tests[i].pattern),
280+ tests[i].host,
281+ strlen(tests[i].host))) {
282+ fprintf(stderr,
283+ "HOST: %s\n"
284+ "PTRN: %s\n"
285+ "did %sMATCH\n",
286+ tests[i].host,
287+ tests[i].pattern,
288+ tests[i].match ? "NOT ": "");
289+ unitfail++;
290+ }
291+ }
292+}
293
294-#endif
295+UNITTEST_STOP
296+#else
297
298- /* you end the test code like this: */
299+UNITTEST_START
300
301 UNITTEST_STOP
302+#endif
diff --git a/meta/recipes-support/curl/curl_7.82.0.bb b/meta/recipes-support/curl/curl_7.82.0.bb
index 422c2bec0f..3241867d7e 100644
--- a/meta/recipes-support/curl/curl_7.82.0.bb
+++ b/meta/recipes-support/curl/curl_7.82.0.bb
@@ -47,6 +47,7 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
47 file://CVE-2023-27536.patch \ 47 file://CVE-2023-27536.patch \
48 file://CVE-2023-28319.patch \ 48 file://CVE-2023-28319.patch \
49 file://CVE-2023-28320.patch \ 49 file://CVE-2023-28320.patch \
50 file://CVE-2023-28321.patch \
50 " 51 "
51SRC_URI[sha256sum] = "0aaa12d7bd04b0966254f2703ce80dd5c38dbbd76af0297d3d690cdce58a583c" 52SRC_URI[sha256sum] = "0aaa12d7bd04b0966254f2703ce80dd5c38dbbd76af0297d3d690cdce58a583c"
52 53