From 6611d81140dcea830107c1b7810fa4b281179824 Mon Sep 17 00:00:00 2001 From: xingwei Date: Thu, 27 Mar 2025 08:21:02 +0000 Subject: [PATCH] backport patches --- ...teger-overflow-in-expire-calculation.patch | 44 ++++ ...t391-verify-path-as-is-with-redirect.patch | 105 ++++++++ ...o-a-new-fragment-or-query-only-adapt.patch | 241 ++++++++++++++++++ curl.spec | 13 +- 4 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 backport-altsvc-avoid-integer-overflow-in-expire-calculation.patch create mode 100644 backport-test391-verify-path-as-is-with-redirect.patch create mode 100644 backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch diff --git a/backport-altsvc-avoid-integer-overflow-in-expire-calculation.patch b/backport-altsvc-avoid-integer-overflow-in-expire-calculation.patch new file mode 100644 index 0000000..67515e8 --- /dev/null +++ b/backport-altsvc-avoid-integer-overflow-in-expire-calculation.patch @@ -0,0 +1,44 @@ +From c3857eca70e3bf293fff2fe0b3766cfcad1b1251 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Sat, 14 Dec 2024 23:09:16 +0100 +Subject: [PATCH] altsvc: avoid integer overflow in expire calculation + +A bad value here just makes for a bad alt-svc experience, not a security +problem. + +Detected by OSS-Fuzz + +Bug: https://issues.oss-fuzz.com/issues/383911309 + +Closes #15745 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/c3857eca70e3bf293fff2fe0b3766cfcad1b1251 +--- + lib/altsvc.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/lib/altsvc.c b/lib/altsvc.c +index c83e81f..17f9d9e 100644 +--- a/lib/altsvc.c ++++ b/lib/altsvc.c +@@ -560,9 +560,13 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, + srcalpnid, dstalpnid, + srcport, dstport); + if(as) { +- /* The expires time also needs to take the Age: value (if any) into +- account. [See RFC 7838 section 3.1] */ +- as->expires = maxage + time(NULL); ++ time_t secs = time(NULL); ++ /* The expires time also needs to take the Age: value (if any) ++ into account. [See RFC 7838 section 3.1] */ ++ if(maxage > (TIME_T_MAX - secs)) ++ as->expires = TIME_T_MAX; ++ else ++ as->expires = maxage + secs; + as->persist = persist; + Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); + infof(data, "Added alt-svc: %s:%d over %s\n", dsthost, dstport, +-- +2.27.0 + diff --git a/backport-test391-verify-path-as-is-with-redirect.patch b/backport-test391-verify-path-as-is-with-redirect.patch new file mode 100644 index 0000000..d789f86 --- /dev/null +++ b/backport-test391-verify-path-as-is-with-redirect.patch @@ -0,0 +1,105 @@ +From 5394cbf570cda0510d6f10bd875e9aba9f898ce4 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Tue, 7 Jun 2022 23:26:59 +0200 +Subject: [PATCH] test391: verify --path-as-is with redirect + +Conflict:context adapt + replace TESTNUMBER macro with the number 391 +Reference:https://github.com/curl/curl/commit/5394cbf570cda0510d6f10bd875e9aba9f898ce4 +--- + tests/data/Makefile.inc | 2 +- + tests/data/test391 | 72 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 73 insertions(+), 1 deletion(-) + create mode 100644 tests/data/test391 + +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index 1c32cfc..a2ca78a 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -61,7 +61,7 @@ test334 test335 test336 test337 test338 test339 test340 test341 test342 \ + test343 test344 test345 test346 \ + test350 test351 test352 test353 test354 test355 test356 test357 test358 \ + test359 \ +-test393 test394 test395 \ ++test391 test393 test394 test395 \ + \ + test400 test401 test402 test403 test404 test405 test406 test407 test408 \ + test409 \ +diff --git a/tests/data/test391 b/tests/data/test391 +new file mode 100644 +index 000000000000..1eff2ef3eb13 +--- /dev/null ++++ b/tests/data/test391 +@@ -0,0 +1,72 @@ ++ ++ ++ ++HTTP ++HTTP GET ++--path-as-is ++ ++ ++ ++# ++# Server-side ++ ++ ++HTTP/1.1 301 OK ++Content-Length: 6 ++Content-Type: text/html ++Location: ../3910002 ++ ++-foo- ++ ++ ++HTTP/1.1 200 OK ++Content-Length: 6 ++Content-Type: text/html ++ ++-muu- ++ ++ ++HTTP/1.1 301 OK ++Content-Length: 6 ++Content-Type: text/html ++Location: ../3910002 ++ ++HTTP/1.1 200 OK ++Content-Length: 6 ++Content-Type: text/html ++ ++-muu- ++ ++ ++ ++# ++# Client-side ++ ++ ++http ++ ++ ++--path-as-is with redirect, keeping dotdots ++ ++ ++http://%HOSTIP:%HTTPPORT/../../391 --path-as-is -L ++ ++ ++ ++# ++# Verify data after the test has been "shot" ++ ++ ++GET /../../391 HTTP/1.1 ++Host: %HOSTIP:%HTTPPORT ++User-Agent: curl/%VERSION ++Accept: */* ++ ++GET /../3910002 HTTP/1.1 ++Host: %HOSTIP:%HTTPPORT ++User-Agent: curl/%VERSION ++Accept: */* ++ ++ ++ ++ diff --git a/backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch b/backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch new file mode 100644 index 0000000..a1c8486 --- /dev/null +++ b/backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch @@ -0,0 +1,241 @@ +From 66e5351e0adda5891b2ff17ccbafc81f620c0e01 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Sat, 28 Dec 2024 14:47:01 +0100 +Subject: [PATCH] urlapi: fix redirect to a new fragment or query (only) + +The redirect logic was broken when the redirect-to URL was a relative +URL only as a fragment or query (starting with '#' or '?'). + +Extended test 1560 to reproduce, then verify. + +Reported-by: Jeroen Ooms +Fixes #15836 +Closes #15848 + +Conflict:remove doc CURLOPT_PATH_AS_IS.md which is not exist + context adapt + replaceTESTNUMBERmacrowiththenumber391 +Reference:https://github.com/curl/curl/commit/66e5351e0adda5891b2ff17ccbafc81f620c0e01 +--- + lib/urlapi.c | 102 +++++++++++----------------------------- + tests/data/test391 | 2 +- + tests/libtest/lib1560.c | 32 +++++++++++++ + 3 files changed, 60 insertions(+), 76 deletions(-) + +diff --git a/lib/urlapi.c b/lib/urlapi.c +index acbfb82..1a2a28a 100644 +--- a/lib/urlapi.c ++++ b/lib/urlapi.c +@@ -275,8 +275,6 @@ static char *concat_url(const char *base, const char *relurl) + problems in the future... + */ + char *newest; +- char *protsep; +- char *pathsep; + size_t newlen; + bool host_changed = FALSE; + +@@ -291,66 +289,35 @@ static char *concat_url(const char *base, const char *relurl) + return NULL; /* skip out of this NOW */ + + /* protsep points to the start of the host name */ +- protsep = strstr(url_clone, "//"); ++ char *protsep = strstr(url_clone, "//"); ++ DEBUGASSERT(protsep); + if(!protsep) + protsep = url_clone; + else + protsep += 2; /* pass the slashes */ + +- if('/' != relurl[0]) { +- int level = 0; +- +- /* First we need to find out if there's a ?-letter in the URL, ++ if(('/' != relurl[0]) && ('#' != relurl[0])) { ++ /* First we need to find out if there is a ?-letter in the original URL, + and cut it and the right-side of that off */ +- pathsep = strchr(protsep, '?'); ++ char *pathsep = strchr(protsep, '?'); + if(pathsep) + *pathsep = 0; +- +- /* we have a relative path to append to the last slash if there's one +- available, or if the new URL is just a query string (starts with a +- '?') we append the new one at the end of the entire currently worked +- out URL */ +- if(useurl[0] != '?') { +- pathsep = strrchr(protsep, '/'); ++ else { ++ /* if not, cut off the potential fragment */ ++ pathsep = strchr(protsep, '#'); + if(pathsep) + *pathsep = 0; + } + +- /* Check if there's any slash after the host name, and if so, remember +- that position instead */ +- pathsep = strchr(protsep, '/'); +- if(pathsep) +- protsep = pathsep + 1; +- else +- protsep = NULL; +- +- /* now deal with one "./" or any amount of "../" in the newurl +- and act accordingly */ +- +- if((useurl[0] == '.') && (useurl[1] == '/')) +- useurl += 2; /* just skip the "./" */ +- +- while((useurl[0] == '.') && +- (useurl[1] == '.') && +- (useurl[2] == '/')) { +- level++; +- useurl += 3; /* pass the "../" */ +- } +- +- if(protsep) { +- while(level--) { +- /* cut off one more level from the right of the original URL */ +- pathsep = strrchr(protsep, '/'); +- if(pathsep) +- *pathsep = 0; +- else { +- *protsep = 0; +- break; +- } +- } ++ /* if the redirect-to piece is not just a query, cut the path after the ++ last slash */ ++ if(useurl[0] != '?') { ++ pathsep = strrchr(protsep, '/'); ++ if(pathsep) ++ pathsep[1] = 0; /* leave the slash */ + } + } +- else { ++ else if('/' == relurl[0]) { + /* We got a new absolute path for this server */ + + if(relurl[1] == '/') { +@@ -362,29 +329,20 @@ static char *concat_url(const char *base, const char *relurl) + host_changed = TRUE; + } + else { +- /* cut off the original URL from the first slash, or deal with URLs +- without slash */ +- pathsep = strchr(protsep, '/'); +- if(pathsep) { +- /* When people use badly formatted URLs, such as +- "http://www.url.com?dir=/home/daniel" we must not use the first +- slash, if there's a ?-letter before it! */ +- char *sep = strchr(protsep, '?'); +- if(sep && (sep < pathsep)) +- pathsep = sep; ++ /* cut the original URL at first slash */ ++ char *pathsep = strchr(protsep, '/'); ++ if(pathsep) + *pathsep = 0; +- } +- else { +- /* There was no slash. Now, since we might be operating on a badly +- formatted URL, such as "http://www.url.com?id=2380" which doesn't +- use a slash separator as it is supposed to, we need to check for a +- ?-letter as well! */ +- pathsep = strchr(protsep, '?'); +- if(pathsep) +- *pathsep = 0; +- } + } + } ++ else { ++ /* the relative piece starts with '#' */ ++ ++ /* If there is a fragment in the original URL, cut it off */ ++ char *pathsep = strchr(protsep, '#'); ++ if(pathsep) ++ *pathsep = 0; ++ } + + /* If the new part contains a space, this is a mighty stupid redirect + but we still make an effort to do "right". To the left of a '?' +@@ -406,12 +364,6 @@ static char *concat_url(const char *base, const char *relurl) + /* copy over the root url part */ + memcpy(newest, url_clone, urllen); + +- /* check if we need to append a slash */ +- if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0])) +- ; +- else +- newest[urllen++]='/'; +- + /* then append the new piece on the right side */ + strcpy_url(&newest[urllen], useurl, !host_changed); + +@@ -1366,7 +1318,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, + free(redired_url); + return CURLUE_OUT_OF_MEMORY; + } +- result = parseurl(redired_url, handle2, flags); ++ result = parseurl(redired_url, handle2, flags&~CURLU_PATH_AS_IS); + free(redired_url); + if(!result) + mv_urlhandle(handle2, u); +diff --git a/tests/data/test391 b/tests/data/test391 +index 1eff2ef..045d894 100644 +--- a/tests/data/test391 ++++ b/tests/data/test391 +@@ -62,7 +62,7 @@ Host: %HOSTIP:%HTTPPORT + User-Agent: curl/%VERSION + Accept: */* + +-GET /../3910002 HTTP/1.1 ++GET /3910002 HTTP/1.1 + Host: %HOSTIP:%HTTPPORT + User-Agent: curl/%VERSION + Accept: */* +diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c +index d158534..2fbd14a 100644 +--- a/tests/libtest/lib1560.c ++++ b/tests/libtest/lib1560.c +@@ -686,6 +686,38 @@ static CURLUcode updateurl(CURLU *u, const char *cmd, unsigned int setflags) + } + + static struct redircase set_url_list[] = { ++ {"http://example.org#withs/ash", "/moo#frag", ++ "http://example.org/moo#frag", ++ 0, 0, CURLUE_OK}, ++ {"http://example.org/", "../path/././../././../moo", ++ "http://example.org/moo", ++ 0, 0, CURLUE_OK}, ++ ++ {"http://example.org?bar/moo", "?weird", ++ "http://example.org/?weird", 0, 0, CURLUE_OK}, ++ {"http://example.org/foo?bar", "?weird", ++ "http://example.org/foo?weird", 0, 0, CURLUE_OK}, ++ {"http://example.org/foo", "?weird", ++ "http://example.org/foo?weird", 0, 0, CURLUE_OK}, ++ {"http://example.org", "?weird", ++ "http://example.org/?weird", 0, 0, CURLUE_OK}, ++ {"http://example.org/#original", "?weird#moo", ++ "http://example.org/?weird#moo", 0, 0, CURLUE_OK}, ++ ++ {"http://example.org?bar/moo#yes/path", "#new/slash", ++ "http://example.org/?bar/moo#new/slash", 0, 0, CURLUE_OK}, ++ {"http://example.org/foo?bar", "#weird", ++ "http://example.org/foo?bar#weird", 0, 0, CURLUE_OK}, ++ {"http://example.org/foo?bar#original", "#weird", ++ "http://example.org/foo?bar#weird", 0, 0, CURLUE_OK}, ++ {"http://example.org/foo#original", "#weird", ++ "http://example.org/foo#weird", 0, 0, CURLUE_OK}, ++ {"http://example.org/#original", "#weird", ++ "http://example.org/#weird", 0, 0, CURLUE_OK}, ++ {"http://example.org#original", "#weird", ++ "http://example.org/#weird", 0, 0, CURLUE_OK}, ++ {"http://example.org/foo?bar", "moo?hey#weird", ++ "http://example.org/moo?hey#weird", 0, 0, CURLUE_OK}, + {"file://localhost/path?query#frag", + "foo#another", + "file:///foo#another", +-- +2.27.0 + diff --git a/curl.spec b/curl.spec index e2e294c..3e679b2 100644 --- a/curl.spec +++ b/curl.spec @@ -6,7 +6,7 @@ Name: curl Version: 7.71.1 -Release: 40 +Release: 41 Summary: Curl is used in command lines or scripts to transfer data License: MIT URL: https://curl.haxx.se/ @@ -89,6 +89,9 @@ Patch176: backport-url-allow-DoH-transfers-to-override-max-connection-limi Patch177: backport-multi-check-that-the-multi-handle-is-valid-in-curl_m.patch Patch178: backport-cookie-treat-cookie-name-case-sensitively.patch Patch179: backport-CVE-2025-0725.patch +Patch180: backport-altsvc-avoid-integer-overflow-in-expire-calculation.patch +Patch181: backport-test391-verify-path-as-is-with-redirect.patch +Patch182: backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch BuildRequires: automake brotli-devel coreutils gcc groff krb5-devel BuildRequires: libidn2-devel libnghttp2-devel libpsl-devel @@ -253,6 +256,14 @@ rm -rf ${RPM_BUILD_ROOT}%{_libdir}/libcurl.la %{_mandir}/man3/* %changelog +* Thu Mar 27 2025 xingwei - 7.71.1-41 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:altsvc: avoid integer overflow in expire calculation + test391: verify --path-as-is with redirect + urlapi: fix redirect to a new fragment or query (only) + * Sat Feb 08 2025 zhouyihang - 7.71.1-40 - Type:CVE - CVE:CVE-2025-0725