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-tool_getparam-clear-sensitive-arguments-better.patch b/backport-tool_getparam-clear-sensitive-arguments-better.patch new file mode 100644 index 0000000000000000000000000000000000000000..53e8a9e37bba9bb14daa462709d26ee0f64208db --- /dev/null +++ b/backport-tool_getparam-clear-sensitive-arguments-better.patch @@ -0,0 +1,120 @@ +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 | 18 ++++++++++++------ + src/tool_getparam.h | 3 ++- + src/tool_parsecfg.c | 3 ++- + 3 files changed, 16 insertions(+), 8 deletions(-) + +diff --git a/src/tool_getparam.c b/src/tool_getparam.c +index d9772a3..53e3b76 100644 +--- a/src/tool_getparam.c ++++ b/src/tool_getparam.c +@@ -740,7 +740,8 @@ out: + + 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, +@@ -769,7 +770,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ + #ifdef HAVE_WRITABLE_ARGV + argv_item_t clearthis = NULL; + #else +- (void)cleararg; ++ (void)cleararg1; ++ (void)cleararg2; + #endif + + *usedarg = FALSE; /* default is that we don't use the arg */ +@@ -879,6 +881,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) { + err = PARAM_REQUIRES_PARAMETER; +@@ -886,7 +891,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ + } + else { + #ifdef HAVE_WRITABLE_ARGV +- clearthis = cleararg; ++ clearthis = cleararg2; + #endif + *usedarg = TRUE; /* mark it as used */ + } +@@ -2714,8 +2719,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; +@@ -2757,7 +2762,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 a8a9d45..1a7ec4f 100644 +--- a/src/tool_getparam.h ++++ b/src/tool_getparam.h +@@ -56,7 +56,8 @@ struct GlobalConfig; + 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 c15f210..3f9e4e5 100644 +--- a/src/tool_parsecfg.c ++++ b/src/tool_parsecfg.c +@@ -223,7 +223,8 @@ int parseconfig(const char *filename, struct GlobalConfig *global) + #ifdef DEBUG_CONFIG + fprintf(tool_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) +-- +2.43.0 + 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..77eb26784a1f7a06c85cf33c9fc23fa168206b37 --- /dev/null +++ b/backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch @@ -0,0 +1,239 @@ +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 | 103 +++++++++++----------------------------- + tests/data/test391 | 2 +- + tests/libtest/lib1560.c | 32 +++++++++++++ + 3 files changed, 60 insertions(+), 77 deletions(-) + +diff --git a/lib/urlapi.c b/lib/urlapi.c +index 4efab61..f6b0926 100644 +--- a/lib/urlapi.c ++++ b/lib/urlapi.c +@@ -256,72 +256,40 @@ static char *concat_url(char *base, const char *relurl) + problems in the future... + */ + struct dynbuf newest; +- char *protsep; +- char *pathsep; + bool host_changed = FALSE; + const char *useurl = relurl; + + /* protsep points to the start of the host name */ +- protsep = strstr(base, "//"); ++ char *protsep = strstr(base, "//"); ++ DEBUGASSERT(protsep); + if(!protsep) + protsep = base; + 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] == '/') { +@@ -333,29 +301,20 @@ static char *concat_url(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.example.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.example.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; ++ } + + Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH); + +@@ -363,14 +322,6 @@ static char *concat_url(char *base, const char *relurl) + if(Curl_dyn_add(&newest, base)) + return NULL; + +- /* check if we need to append a slash */ +- if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0])) +- ; +- else { +- if(Curl_dyn_addn(&newest, "/", 1)) +- return NULL; +- } +- + /* then append the new piece on the right side */ + urlencode_str(&newest, useurl, strlen(useurl), !host_changed, FALSE); + +@@ -1837,7 +1788,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, + if(!redired_url) + return CURLUE_OUT_OF_MEMORY; + +- result = parseurl_and_replace(redired_url, u, flags); ++ result = parseurl_and_replace(redired_url, u, flags&~CURLU_PATH_AS_IS); + free(redired_url); + return result; + } +diff --git a/tests/data/test391 b/tests/data/test391 +index 24428a0..279c562 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 765df0a..d7df5c4 100644 +--- a/tests/libtest/lib1560.c ++++ b/tests/libtest/lib1560.c +@@ -1066,6 +1066,38 @@ static CURLUcode updateurl(CURLU *u, const char *cmd, unsigned int setflags) + } + + static const 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.com/please/../gimme/%TESTNUMBER?foobar#hello", + "http://example.net/there/it/is/../../tes t case=/%TESTNUMBER0002? yes no", + "http://example.net/there/tes%20t%20case=/%TESTNUMBER0002?+yes+no", +-- +2.43.0 + diff --git a/curl.spec b/curl.spec index d6f465c212acd8588ab346a8d8c8adae9443e87c..85ef6270b544661d42273af8066a95b68ca9eab5 100644 --- a/curl.spec +++ b/curl.spec @@ -7,7 +7,7 @@ Name: curl Version: 8.4.0 -Release: 15 +Release: 16 Summary: Curl is used in command lines or scripts to transfer data License: curl URL: https://curl.se/ @@ -48,6 +48,9 @@ Patch39: backport-CVE-2024-11053-post1.patch Patch40: backport-CVE-2024-11053-post2.patch Patch41: backport-CVE-2025-0167.patch Patch42: backport-CVE-2025-0725.patch +Patch43: backport-altsvc-avoid-integer-overflow-in-expire-calculation.patch +Patch44: backport-urlapi-fix-redirect-to-a-new-fragment-or-query-only-adapt.patch +Patch45: backport-tool_getparam-clear-sensitive-arguments-better.patch BuildRequires: automake brotli-devel coreutils gcc groff krb5-devel BuildRequires: libidn2-devel libnghttp2-devel libpsl-devel @@ -233,6 +236,15 @@ rm -rf ${RPM_BUILD_ROOT}%{_libdir}/libcurl.la %{_mandir}/man3/* %changelog +* Tue Mar 25 2025 xingwei - 8.4.0-16 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:altsvc: avoid integer overflow in expire calculation + urlapi: fix redirect to a new fragment or query (only) + tool_getparam: clear sensitive arguments better + + * Sat Feb 08 2025 zhouyihang - 8.4.0-15 - Type:CVE - CVE:CVE-2025-0167 CVE-2025-0725