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 0000000000000000000000000000000000000000..efd0e6ca6b67ad8354107171857daaccab778149 --- /dev/null +++ b/backport-altsvc-avoid-integer-overflow-in-expire-calculation.patch @@ -0,0 +1,41 @@ +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 a3ab368c5014..62f2c545fe55 100644 +--- a/lib/altsvc.c ++++ b/lib/altsvc.c +@@ -659,9 +659,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", dsthost, dstport, 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 0000000000000000000000000000000000000000..36b5d82da431b1a06b5febfd0edc21852e51dce6 --- /dev/null +++ b/backport-test391-verify-path-as-is-with-redirect.patch @@ -0,0 +1,107 @@ +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 +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 2196e2fd9787..d41052e53907 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -64,7 +64,7 @@ test352 test353 test354 test355 test356 test357 test358 test359 test360 \ + test361 test362 test363 test364 test365 test366 \ + test387 test388 \ + \ +-test392 test393 test394 test395 test396 test397 \ ++test391 test392 test393 test394 test395 test396 test397 \ + \ + test400 test401 test402 test403 test404 test405 test406 test407 test408 \ + test409 test410 \ +diff --git a/tests/data/test391 b/tests/data/test391 +new file mode 100644 +index 000000000..1eff2ef3e +--- /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: ../%TESTNUMBER0002 ++ ++-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: ../%TESTNUMBER0002 ++ ++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/../../%TESTNUMBER --path-as-is -L ++ ++ ++ ++# ++# Verify data after the test has been "shot" ++ ++ ++GET /../../%TESTNUMBER HTTP/1.1 ++Host: %HOSTIP:%HTTPPORT ++User-Agent: curl/%VERSION ++Accept: */* ++ ++GET /../%TESTNUMBER0002 HTTP/1.1 ++Host: %HOSTIP:%HTTPPORT ++User-Agent: curl/%VERSION ++Accept: */* ++ ++ ++ ++ +-- +2.33.0 + diff --git a/backport-tool_getparam-clear-sensitive-arguments-better.patch b/backport-tool_getparam-clear-sensitive-arguments-better.patch new file mode 100644 index 0000000000000000000000000000000000000000..0ec84d8549742128cd6b7660259a7a048251ba00 --- /dev/null +++ b/backport-tool_getparam-clear-sensitive-arguments-better.patch @@ -0,0 +1,117 @@ +From 654f8cb5f353905c6eb5b2a6ef7e5beafa7d0634 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Wed, 19 Feb 2025 23:55:31 +0100 +Subject: [PATCH] tool_getparam: clear sensitive arguments better + +curl attempts to clear some flags to hide them from snooping neighbors +(on platforms where it works). For example the credentials provided with +-u. Previously it would only do that if there was a space between the +option and the credentials as in "-u joe:s3cr3t" but not when done +without a separating space as in "-ujoe:s3cr3t". + +This addresses that previous shortcoming. + +Reported-by: kayrus on github +Fixes #16396 +Closes #16401 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/654f8cb5f353905c6eb5b2a6ef7e5beafa7d0634 +--- + src/tool_getparam.c | 19 ++++++++++++------ + src/tool_getparam.h | 3 ++- + src/tool_parsecfg.c | 3 ++- + 3 files changed, 19 insertions(+), 7 deletions(-) + +diff --git a/src/tool_getparam.c b/src/tool_getparam.c +index f66124d40a27..6944059df740 100644 +--- a/src/tool_getparam.c ++++ b/src/tool_getparam.c +@@ -1564,7 +1564,8 @@ static ParameterError parse_time_cond(struct GlobalConfig *global, + + ParameterError getparameter(const char *flag, /* f or -long-flag */ + char *nextarg, /* NULL if unset */ +- argv_item_t cleararg, ++ argv_item_t cleararg1, ++ argv_item_t cleararg2, + bool *usedarg, /* set to TRUE if the arg + has been used */ + struct GlobalConfig *global, +@@ -1590,6 +1591,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ + by using --OPTION or --no-OPTION */ + #ifdef HAVE_WRITABLE_ARGV + argv_item_t clearthis = NULL; ++#else ++ (void)cleararg1; ++ (void)cleararg2; + #endif + *usedarg = FALSE; /* default is that we don't use the arg */ + +@@ -1669,6 +1671,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ + if(!longopt && parse[1]) { + nextarg = (char *)&parse[1]; /* this is the actual extra parameter */ + singleopt = TRUE; /* don't loop anymore after this */ ++#ifdef HAVE_WRITABLE_ARGV ++ clearthis = &cleararg1[parse + 2 - flag]; ++#endif + } + else if(!nextarg) + return PARAM_REQUIRES_PARAMETER; +@@ -1676,7 +1681,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ + return PARAM_REQUIRES_PARAMETER; + else { + #ifdef HAVE_WRITABLE_ARGV +- clearthis = cleararg; ++ clearthis = cleararg2; + #endif + *usedarg = TRUE; /* mark it as used */ + } +@@ -2889,8 +2894,8 @@ ParameterError parse_args(struct GlobalConfig *global, int argc, + } + } + +- result = getparameter(orig_opt, nextarg, argv[i + 1], &passarg, +- global, config); ++ result = getparameter(orig_opt, nextarg, argv[i], argv[i + 1], ++ &passarg, global, config); + curlx_unicodefree(nextarg); + config = global->last; + if(result == PARAM_NEXT_OPERATION) { +@@ -2932,7 +2937,8 @@ ParameterError parse_args(struct GlobalConfig *global, int argc, + bool used; + + /* Just add the URL please */ +- result = getparameter("--url", orig_opt, argv[i], &used, global, config); ++ result = getparameter("--url", orig_opt, NULL, NULL, ++ &used, global, config); + } + + if(!result) +diff --git a/src/tool_getparam.h b/src/tool_getparam.h +index beef191c66e8..bcfb35f0657e 100644 +--- a/src/tool_getparam.h ++++ b/src/tool_getparam.h +@@ -361,7 +361,8 @@ const struct LongShort *findlongopt(const char *opt); + struct OperationConfig; + + ParameterError getparameter(const char *flag, char *nextarg, +- argv_item_t cleararg, ++ argv_item_t cleararg1, ++ argv_item_t cleararg2, + bool *usedarg, + struct GlobalConfig *global, + struct OperationConfig *operation); +diff --git a/src/tool_parsecfg.c b/src/tool_parsecfg.c +index 651ec8e9f401..b9fd56b300ba 100644 +--- a/src/tool_parsecfg.c ++++ b/src/tool_parsecfg.c +@@ -190,7 +190,8 @@ int parseconfig(const char *filename, struct GlobalConfig *global) + #ifdef DEBUG_CONFIG + fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)")); + #endif +- res = getparameter(option, param, NULL, &usedarg, global, operation); ++ res = getparameter(option, param, NULL, NULL, ++ &usedarg, global, operation); + operation = global->last; + + if(!res && param && *param && !usedarg) 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 0000000000000000000000000000000000000000..14f43aea97e331890cf0f4df74d22ce15dfc68de --- /dev/null +++ b/backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch @@ -0,0 +1,238 @@ +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 +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 b676c4d..0f8ffbf 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); + +@@ -1463,7 +1415,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 24428a08f0f2..279c562de317 100644 +--- a/tests/data/test391 ++++ b/tests/data/test391 +@@ -62,7 +62,7 @@ Host: %HOSTIP:%HTTPPORT + User-Agent: curl/%VERSION + Accept: */* + +-GET /../%TESTNUMBER0002 HTTP/1.1 ++GET /%TESTNUMBER0002 HTTP/1.1 + Host: %HOSTIP:%HTTPPORT + User-Agent: curl/%VERSION + Accept: */* +diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c +index d5253a739a10..3103c69f2090 100644 +--- a/tests/libtest/lib1560.c ++++ b/tests/libtest/lib1560.c +@@ -1143,6 +1143,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}, + {"http://example.org/static/favicon/wikipedia.ico", + "//fake.example.com/licenses/by-sa/3.0/", + "http://fake.example.com/licenses/by-sa/3.0/", diff --git a/curl.spec b/curl.spec index f9d655ba6a12a180e82bea10f8a650395d135e47..f9ef409236fed06798cff27416260d539aac909f 100644 --- a/curl.spec +++ b/curl.spec @@ -6,7 +6,7 @@ Name: curl Version: 7.79.1 -Release: 37 +Release: 38 Summary: Curl is used in command lines or scripts to transfer data License: MIT URL: https://curl.haxx.se/ @@ -113,6 +113,10 @@ Patch99: backport-cookie-treat-cookie-name-case-sensitively.patch Patch100: backport-CVE-2024-11053.patch Patch101: backport-CVE-2025-0167.patch Patch102: backport-CVE-2025-0725.patch +Patch103: backport-altsvc-avoid-integer-overflow-in-expire-calculation.patch +Patch104: backport-test391-verify-path-as-is-with-redirect.patch +Patch105: backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch +Patch106: backport-tool_getparam-clear-sensitive-arguments-better.patch BuildRequires: automake brotli-devel coreutils gcc groff krb5-devel BuildRequires: libidn2-devel libnghttp2-devel libpsl-devel @@ -281,6 +285,15 @@ rm -rf ${RPM_BUILD_ROOT}%{_libdir}/libcurl.la %{_mandir}/man3/* %changelog +* Tue Mar 25 2025 xingwei - 7.79.1-38 +- 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) + tool_getparam: clear sensitive arguments better + * Sat Feb 08 2025 zhouyihang - 7.79.1-37 - Type:CVE - CVE:CVE-2025-0167 CVE-2025-0725