diff options
author | CoprDistGit <infra@openeuler.org> | 2025-03-04 03:28:50 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2025-03-04 03:28:50 +0000 |
commit | 510937df92473c5a6830d87f078386db8dbf896d (patch) | |
tree | 64d9befb90cb19890926aedd71df1a7a5452e08c | |
parent | dbf64f99d0f0a31203092f9afdc6c07e13917313 (diff) |
automatic import of curlopeneuler24.03_LTS_SP1
37 files changed, 5899 insertions, 0 deletions
@@ -0,0 +1 @@ +/curl-8.4.0.tar.xz diff --git a/backport-0001-CVE-2023-46219.patch b/backport-0001-CVE-2023-46219.patch new file mode 100644 index 0000000..2e2ae77 --- /dev/null +++ b/backport-0001-CVE-2023-46219.patch @@ -0,0 +1,133 @@ +From 73b65e94f3531179de45c6f3c836a610e3d0a846 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Thu, 23 Nov 2023 08:23:17 +0100 +Subject: [PATCH] fopen: create short(er) temporary file name + +Only using random letters in the name plus a ".tmp" extension. Not by +appending characters to the final file name. + +Reported-by: Maksymilian Arciemowicz + +Closes #12388 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/73b65e94f3531179de45c6f3c836a610e3d0a846 +--- + lib/fopen.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 60 insertions(+), 5 deletions(-) + +diff --git a/lib/fopen.c b/lib/fopen.c +index 75b8a7aa5..a73ac068e 100644 +--- a/lib/fopen.c ++++ b/lib/fopen.c +@@ -39,6 +39,51 @@ + #include "curl_memory.h" + #include "memdebug.h" + ++/* ++ The dirslash() function breaks a null-terminated pathname string into ++ directory and filename components then returns the directory component up ++ to, *AND INCLUDING*, a final '/'. If there is no directory in the path, ++ this instead returns a "" string. ++ ++ This function returns a pointer to malloc'ed memory. ++ ++ The input path to this function is expected to have a file name part. ++*/ ++ ++#ifdef _WIN32 ++#define PATHSEP "\\" ++#define IS_SEP(x) (((x) == '/') || ((x) == '\\')) ++#elif defined(MSDOS) || defined(__EMX__) || defined(OS2) ++#define PATHSEP "\\" ++#define IS_SEP(x) ((x) == '\\') ++#else ++#define PATHSEP "/" ++#define IS_SEP(x) ((x) == '/') ++#endif ++ ++static char *dirslash(const char *path) ++{ ++ size_t n; ++ struct dynbuf out; ++ DEBUGASSERT(path); ++ Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH); ++ n = strlen(path); ++ if(n) { ++ /* find the rightmost path separator, if any */ ++ while(n && !IS_SEP(path[n-1])) ++ --n; ++ /* skip over all the path separators, if any */ ++ while(n && IS_SEP(path[n-1])) ++ --n; ++ } ++ if(Curl_dyn_addn(&out, path, n)) ++ return NULL; ++ /* if there was a directory, append a single trailing slash */ ++ if(n && Curl_dyn_addn(&out, PATHSEP, 1)) ++ return NULL; ++ return Curl_dyn_ptr(&out); ++} ++ + /* + * Curl_fopen() opens a file for writing with a temp name, to be renamed + * to the final name when completed. If there is an existing file using this +@@ -50,25 +95,34 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, + FILE **fh, char **tempname) + { + CURLcode result = CURLE_WRITE_ERROR; +- unsigned char randsuffix[9]; ++ unsigned char randbuf[41]; + char *tempstore = NULL; + struct_stat sb; + int fd = -1; ++ char *dir; + *tempname = NULL; + ++ dir = dirslash(filename); ++ if(!dir) ++ goto fail; ++ + *fh = fopen(filename, FOPEN_WRITETEXT); + if(!*fh) + goto fail; +- if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) ++ if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { ++ free(dir); + return CURLE_OK; ++ } + fclose(*fh); + *fh = NULL; + +- result = Curl_rand_alnum(data, randsuffix, sizeof(randsuffix)); ++ result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); + if(result) + goto fail; + +- tempstore = aprintf("%s.%s.tmp", filename, randsuffix); ++ /* The temp file name should not end up too long for the target file ++ system */ ++ tempstore = aprintf("%s%s.tmp", dir, randbuf); + if(!tempstore) { + result = CURLE_OUT_OF_MEMORY; + goto fail; +@@ -95,6 +149,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, + if(!*fh) + goto fail; + ++ free(dir); + *tempname = tempstore; + return CURLE_OK; + +@@ -105,7 +160,7 @@ fail: + } + + free(tempstore); +- ++ free(dir); + return result; + } + +-- +2.33.0 + diff --git a/backport-0002-CVE-2023-46219.patch b/backport-0002-CVE-2023-46219.patch new file mode 100644 index 0000000..c9c08ec --- /dev/null +++ b/backport-0002-CVE-2023-46219.patch @@ -0,0 +1,80 @@ +From f27b8dba73295cb5296a50f2c19c0739b502eb94 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Fri, 24 Nov 2023 09:46:32 +0100 +Subject: [PATCH] fopen: allocate the dir after fopen + +Move the allocation of the directory name down to after the fopen() call +to allow that shortcut code path to avoid a superfluous malloc+free +cycle. + +Follow-up to 73b65e94f35311 + +Closes #12398 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/f27b8dba73295cb5296a50f2c19c0739b502eb94 +--- + lib/fopen.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/lib/fopen.c b/lib/fopen.c +index 2e726cc95..851279fe1 100644 +--- a/lib/fopen.c ++++ b/lib/fopen.c +@@ -99,18 +99,13 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, + char *tempstore = NULL; + struct_stat sb; + int fd = -1; +- char *dir; ++ char *dir = NULL; + *tempname = NULL; + +- dir = dirslash(filename); +- if(!dir) +- goto fail; +- + *fh = fopen(filename, FOPEN_WRITETEXT); + if(!*fh) + goto fail; + if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { +- free(dir); + return CURLE_OK; + } + fclose(*fh); +@@ -120,9 +115,14 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, + if(result) + goto fail; + +- /* The temp file name should not end up too long for the target file +- system */ +- tempstore = aprintf("%s%s.tmp", dir, randbuf); ++ dir = dirslash(filename); ++ if(dir) { ++ /* The temp file name should not end up too long for the target file ++ system */ ++ tempstore = aprintf("%s%s.tmp", dir, randbuf); ++ free(dir); ++ } ++ + if(!tempstore) { + result = CURLE_OUT_OF_MEMORY; + goto fail; +@@ -137,7 +137,6 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, + if(!*fh) + goto fail; + +- free(dir); + *tempname = tempstore; + return CURLE_OK; + +@@ -148,7 +147,6 @@ fail: + } + + free(tempstore); +- free(dir); + return result; + } + +-- +2.33.0 + diff --git a/backport-0101-curl-7.32.0-multilib.patch b/backport-0101-curl-7.32.0-multilib.patch new file mode 100644 index 0000000..b4f8e2a --- /dev/null +++ b/backport-0101-curl-7.32.0-multilib.patch @@ -0,0 +1,91 @@ +From 2a4754a3a7cf60ecc36d83cbe50b8c337cb87632 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka <kdudka@redhat.com> +Date: Fri, 12 Apr 2013 12:04:05 +0200 +Subject: [PATCH] prevent multilib conflicts on the curl-config script + +--- + curl-config.in | 23 +++++------------------ + docs/curl-config.1 | 4 +++- + libcurl.pc.in | 1 + + 3 files changed, 9 insertions(+), 19 deletions(-) + +diff --git a/curl-config.in b/curl-config.in +index 150004d..95d0759 100644 +--- a/curl-config.in ++++ b/curl-config.in +@@ -78,7 +78,7 @@ while test $# -gt 0; do + ;; + + --cc) +- echo "@CC@" ++ echo "gcc" + ;; + + --prefix) +@@ -157,32 +157,19 @@ while test $# -gt 0; do + ;; + + --libs) +- if test "X@libdir@" != "X/usr/lib" -a "X@libdir@" != "X/usr/lib64"; then +- CURLLIBDIR="-L@libdir@ " +- else +- CURLLIBDIR="" +- fi +- if test "X@ENABLE_SHARED@" = "Xno"; then +- echo ${CURLLIBDIR}-lcurl @LIBCURL_LIBS@ +- else +- echo ${CURLLIBDIR}-lcurl +- fi ++ echo -lcurl + ;; + --ssl-backends) + echo "@SSL_BACKENDS@" + ;; + + --static-libs) +- if test "X@ENABLE_STATIC@" != "Xno" ; then +- echo "@libdir@/libcurl.@libext@" @LDFLAGS@ @LIBCURL_LIBS@ +- else +- echo "curl was built with static libraries disabled" >&2 +- exit 1 +- fi ++ echo "curl was built with static libraries disabled" >&2 ++ exit 1 + ;; + + --configure) +- echo @CONFIGURE_OPTIONS@ ++ pkg-config libcurl --variable=configure_options | sed 's/^"//;s/"$//' + ;; + + *) +diff --git a/docs/curl-config.1 b/docs/curl-config.1 +index 14a9d2b..ffcc004 100644 +--- a/docs/curl-config.1 ++++ b/docs/curl-config.1 +@@ -72,7 +72,9 @@ no, one or several names. If more than one name, they will appear + comma-separated. (Added in 7.58.0) + .IP "--static-libs" + Shows the complete set of libs and other linker options you will need in order +-to link your application with libcurl statically. (Added in 7.17.1) ++to link your application with libcurl statically. Note that Fedora/RHEL libcurl ++packages do not provide any static libraries, thus cannot be linked statically. ++(Added in 7.17.1) + .IP "--version" + Outputs version information about the installed libcurl. + .IP "--vernum" +diff --git a/libcurl.pc.in b/libcurl.pc.in +index 2ba9c39..f8f8b00 100644 +--- a/libcurl.pc.in ++++ b/libcurl.pc.in +@@ -31,6 +31,7 @@ libdir=@libdir@ + includedir=@includedir@ + supported_protocols="@SUPPORT_PROTOCOLS@" + supported_features="@SUPPORT_FEATURES@" ++configure_options=@CONFIGURE_OPTIONS@ + + Name: libcurl + URL: https://curl.se/ +-- +2.26.2 + diff --git a/backport-CVE-2023-46218.patch b/backport-CVE-2023-46218.patch new file mode 100644 index 0000000..8158814 --- /dev/null +++ b/backport-CVE-2023-46218.patch @@ -0,0 +1,54 @@ +From 2b0994c29a721c91c572cff7808c572a24d251eb Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Thu, 23 Nov 2023 08:15:47 +0100 +Subject: [PATCH] cookie: lowercase the domain names before PSL checks + +Reported-by: Harry Sintonen + +Closes #12387 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/2b0994c29a721c91c572cff7808c572a24d251eb +--- + lib/cookie.c | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/lib/cookie.c b/lib/cookie.c +index 568cf537a..9095cea3e 100644 +--- a/lib/cookie.c ++++ b/lib/cookie.c +@@ -1027,15 +1027,23 @@ Curl_cookie_add(struct Curl_easy *data, + * dereference it. + */ + if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { +- const psl_ctx_t *psl = Curl_psl_use(data); +- int acceptable; +- +- if(psl) { +- acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain); +- Curl_psl_release(data); ++ bool acceptable = FALSE; ++ char lcase[256]; ++ char lcookie[256]; ++ size_t dlen = strlen(domain); ++ size_t clen = strlen(co->domain); ++ if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { ++ const psl_ctx_t *psl = Curl_psl_use(data); ++ if(psl) { ++ /* the PSL check requires lowercase domain name and pattern */ ++ Curl_strntolower(lcase, domain, dlen + 1); ++ Curl_strntolower(lcookie, co->domain, clen + 1); ++ acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); ++ Curl_psl_release(data); ++ } ++ else ++ acceptable = !bad_domain(domain, strlen(domain)); + } +- else +- acceptable = !bad_domain(domain, strlen(domain)); + + if(!acceptable) { + infof(data, "cookie '%s' dropped, domain '%s' must not " +-- +2.33.0 + diff --git a/backport-CVE-2024-11053-post1.patch b/backport-CVE-2024-11053-post1.patch new file mode 100644 index 0000000..1d69ba5 --- /dev/null +++ b/backport-CVE-2024-11053-post1.patch @@ -0,0 +1,37 @@ +From 4b07b7ebadfbff1d26622719b9048673a78f0bf0 Mon Sep 17 00:00:00 2001 +From: Viktor Szakats <commit@vsz.me> +Date: Sun, 17 Nov 2024 12:46:25 +0100 +Subject: [PATCH] netrc: fix pointer to bool conversion + +with MSVC 2008 and 2010: +``` +lib/netrc.c(107): error C2440: 'initializing' : cannot convert from 'char *' to 'bool' +``` +Ref: https://ci.appveyor.com/project/curlorg/curl/builds/51002792/job/jtoxd4mk984oi6fd#L164 +Ref: https://ci.appveyor.com/project/curlorg/curl/builds/51002792/job/0wxlw9a8g04e56vt#L177 + +Follow-up to e9b9bbac22c26cf67316fa8e6c6b9e831af31949 #15586 +Closes #15601 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/4b07b7ebadfbff1d26622719b9048673a78f0bf0 +--- + lib/netrc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/netrc.c b/lib/netrc.c +index e787a6ffc..d5ee3c0fd 100644 +--- a/lib/netrc.c ++++ b/lib/netrc.c +@@ -104,7 +104,7 @@ static int parsenetrc(struct store_netrc *store, + int retcode = NETRC_FILE_MISSING; + char *login = *loginp; + char *password = NULL; +- bool specific_login = login; /* points to something */ ++ bool specific_login = !!login; /* points to something */ + enum host_lookup_state state = NOTHING; + enum found_state keyword = NONE; + unsigned char found = 0; /* login + password found bits, as they can come in +-- +2.33.0 + diff --git a/backport-CVE-2024-11053-post2.patch b/backport-CVE-2024-11053-post2.patch new file mode 100644 index 0000000..2b9fd4e --- /dev/null +++ b/backport-CVE-2024-11053-post2.patch @@ -0,0 +1,129 @@ +From 9fce2c55d4b0273ac99b59bd8cb982a6d96b88cf Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Tue, 17 Dec 2024 23:56:42 +0100 +Subject: [PATCH] netrc: fix password-only entries + +When a specific hostname matched, and only a password is set before +another machine is specified in the netrc file, the parser would not be +happy and stop there and return the password-only state. It instead +continued and did not return a match. + +Add test 2005 to verify this case + +Regression from e9b9bba, shipped in 8.11.1. + +Reported-by: Ben Zanin +Fixes #15767 +Closes #15768 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/9fce2c55d4b0273ac99b59bd8cb982a6d96b88cf +--- + lib/netrc.c | 7 +++++- + tests/data/Makefile.inc | 2 +- + tests/data/test2005 | 55 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 62 insertions(+), 2 deletions(-) + create mode 100644 tests/data/test2005 + +diff --git a/lib/netrc.c b/lib/netrc.c +index cbc86484f..b517c1dfa 100644 +--- a/lib/netrc.c ++++ b/lib/netrc.c +@@ -267,7 +267,8 @@ static int parsenetrc(struct store_netrc *store, + retcode = NETRC_FAILED; /* allocation failed */ + goto out; + } +- found |= FOUND_PASSWORD; ++ if(!specific_login || our_login) ++ found |= FOUND_PASSWORD; + keyword = NONE; + } + else if(strcasecompare("login", tok)) +@@ -276,6 +277,10 @@ static int parsenetrc(struct store_netrc *store, + keyword = PASSWORD; + else if(strcasecompare("machine", tok)) { + /* a new machine here */ ++ if(found & FOUND_PASSWORD) { ++ done = TRUE; ++ break; ++ } + state = HOSTFOUND; + keyword = NONE; + found = 0; +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index bd9a0bbaa..105108309 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -238,7 +238,7 @@ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \ + test1955 test1956 test1957 test1958 test1959 test1960 test1964 \ + test1970 test1971 test1972 test1973 test1974 test1975 \ + \ +-test2000 test2001 test2002 test2003 test2004 \ ++test2000 test2001 test2002 test2003 test2004 test2005 \ + \ + test2023 \ + test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \ +diff --git a/tests/data/test2005 b/tests/data/test2005 +new file mode 100644 +index 000000000..91e256298 +--- /dev/null ++++ b/tests/data/test2005 +@@ -0,0 +1,55 @@ ++<testcase> ++<info> ++<keywords> ++HTTP ++netrc ++</keywords> ++</info> ++# ++# Server-side ++<reply> ++<data> ++HTTP/1.1 200 OK ++Date: Fri, 05 Aug 2022 10:09:00 GMT ++Server: test-server/fake ++Content-Type: text/plain ++Content-Length: 6 ++Connection: close ++ ++-foo- ++</data> ++</reply> ++ ++# ++# Client-side ++<client> ++<server> ++http ++</server> ++<name> ++netrc match with password only in file, no username. machine follows ++</name> ++<command> ++--netrc-optional --netrc-file %LOGDIR/netrc%TESTNUMBER http://%HOSTIP:%HTTPPORT/ ++</command> ++<file name="%LOGDIR/netrc%TESTNUMBER" > ++machine %HOSTIP ++password 5up3r53cr37 ++ ++machine example.com ++</file> ++</client> ++ ++# ++# Verify data after the test has been "shot" ++<verify> ++<protocol> ++GET / HTTP/1.1
++Host: %HOSTIP:%HTTPPORT
++Authorization: Basic %b64[:5up3r53cr37]b64%
++User-Agent: curl/%VERSION
++Accept: */*
++
++</protocol> ++</verify> ++</testcase> +-- +2.33.0 + diff --git a/backport-CVE-2024-11053-pre1.patch b/backport-CVE-2024-11053-pre1.patch new file mode 100644 index 0000000..1a55294 --- /dev/null +++ b/backport-CVE-2024-11053-pre1.patch @@ -0,0 +1,453 @@ +From 142ac257b3242459b284020c59f1902b9687a954 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Tue, 6 Feb 2024 10:15:52 +0100 +Subject: [PATCH] lib: convert Curl_get_line to use dynbuf + +Create the line in a dynbuf. Aborts the reading of the file on +errors. Avoids having to always allocate maximum amount from the +start. Avoids direct malloc. + +Closes #12846 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/142ac257b3242459b284020c59f1902b9687a954 +--- + lib/altsvc.c | 18 +++------- + lib/cookie.c | 29 ++++------------ + lib/curl_get_line.c | 55 +++++++++++++---------------- + lib/curl_get_line.h | 7 ++-- + lib/hsts.c | 17 +++------ + lib/netrc.c | 10 ++++-- + tests/unit/unit3200.c | 80 ++++++++++++++++++++++++------------------- + 7 files changed, 96 insertions(+), 120 deletions(-) + +diff --git a/lib/altsvc.c b/lib/altsvc.c +index e9f62bf0e..c12d7bda1 100644 +--- a/lib/altsvc.c ++++ b/lib/altsvc.c +@@ -209,7 +209,6 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line) + static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) + { + CURLcode result = CURLE_OK; +- char *line = NULL; + FILE *fp; + + /* we need a private copy of the file name so that the altsvc cache file +@@ -221,11 +220,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) + + fp = fopen(file, FOPEN_READTEXT); + if(fp) { +- line = malloc(MAX_ALTSVC_LINE); +- if(!line) +- goto fail; +- while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) { +- char *lineptr = line; ++ struct dynbuf buf; ++ Curl_dyn_init(&buf, MAX_ALTSVC_LINE); ++ while(Curl_get_line(&buf, fp)) { ++ char *lineptr = Curl_dyn_ptr(&buf); + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; + if(*lineptr == '#') +@@ -234,16 +232,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) + + altsvc_add(asi, lineptr); + } +- free(line); /* free the line buffer */ ++ Curl_dyn_free(&buf); /* free the line buffer */ + fclose(fp); + } + return result; +- +-fail: +- Curl_safefree(asi->filename); +- free(line); +- fclose(fp); +- return CURLE_OUT_OF_MEMORY; + } + + /* +diff --git a/lib/cookie.c b/lib/cookie.c +index dc319b611..d10dd572b 100644 +--- a/lib/cookie.c ++++ b/lib/cookie.c +@@ -1205,7 +1205,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, + bool newsession) + { + struct CookieInfo *c; +- char *line = NULL; + FILE *handle = NULL; + + if(!inc) { +@@ -1241,16 +1240,14 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, + + c->running = FALSE; /* this is not running, this is init */ + if(fp) { +- +- line = malloc(MAX_COOKIE_LINE); +- if(!line) +- goto fail; +- while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) { +- char *lineptr = line; ++ struct dynbuf buf; ++ Curl_dyn_init(&buf, MAX_COOKIE_LINE); ++ while(Curl_get_line(&buf, fp)) { ++ char *lineptr = Curl_dyn_ptr(&buf); + bool headerline = FALSE; +- if(checkprefix("Set-Cookie:", line)) { ++ if(checkprefix("Set-Cookie:", lineptr)) { + /* This is a cookie line, get it! */ +- lineptr = &line[11]; ++ lineptr += 11; + headerline = TRUE; + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; +@@ -1258,7 +1255,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, + + Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); + } +- free(line); /* free the line buffer */ ++ Curl_dyn_free(&buf); /* free the line buffer */ + + /* + * Remove expired cookies from the hash. We must make sure to run this +@@ -1274,18 +1271,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, + c->running = TRUE; /* now, we're running */ + + return c; +- +-fail: +- free(line); +- /* +- * Only clean up if we allocated it here, as the original could still be in +- * use by a share handle. +- */ +- if(!inc) +- Curl_cookie_cleanup(c); +- if(handle) +- fclose(handle); +- return NULL; /* out of memory */ + } + + /* +diff --git a/lib/curl_get_line.c b/lib/curl_get_line.c +index 686abe751..100207331 100644 +--- a/lib/curl_get_line.c ++++ b/lib/curl_get_line.c +@@ -33,14 +33,16 @@ + #include "memdebug.h" + + /* +- * Curl_get_line() makes sure to only return complete whole lines that fit in +- * 'len' bytes and end with a newline. ++ * Curl_get_line() makes sure to only return complete whole lines that end ++ * newlines. + */ +-char *Curl_get_line(char *buf, int len, FILE *input) ++int Curl_get_line(struct dynbuf *buf, FILE *input) + { +- bool partial = FALSE; ++ CURLcode result; ++ char buffer[128]; ++ Curl_dyn_reset(buf); + while(1) { +- char *b = fgets(buf, len, input); ++ char *b = fgets(buffer, sizeof(buffer), input); + + if(b) { + size_t rlen = strlen(b); +@@ -48,39 +50,28 @@ char *Curl_get_line(char *buf, int len, FILE *input) + if(!rlen) + break; + +- if(b[rlen-1] == '\n') { +- /* b is \n terminated */ +- if(partial) { +- partial = FALSE; +- continue; +- } +- return b; +- } +- else if(feof(input)) { +- if(partial) +- /* Line is already too large to return, ignore rest */ +- break; ++ result = Curl_dyn_addn(buf, b, rlen); ++ if(result) ++ /* too long line or out of memory */ ++ return 0; /* error */ + +- if(rlen + 1 < (size_t) len) { +- /* b is EOF terminated, insert missing \n */ +- b[rlen] = '\n'; +- b[rlen + 1] = '\0'; +- return b; +- } +- else +- /* Maximum buffersize reached + EOF +- * This line is impossible to add a \n to so we'll ignore it +- */ +- break; ++ else if(b[rlen-1] == '\n') ++ /* end of the line */ ++ return 1; /* all good */ ++ ++ else if(feof(input)) { ++ /* append a newline */ ++ result = Curl_dyn_addn(buf, "\n", 1); ++ if(result) ++ /* too long line or out of memory */ ++ return 0; /* error */ ++ return 1; /* all good */ + } +- else +- /* Maximum buffersize reached */ +- partial = TRUE; + } + else + break; + } +- return NULL; ++ return 0; + } + + #endif /* if not disabled */ +diff --git a/lib/curl_get_line.h b/lib/curl_get_line.h +index 0ff32c5c2..7907cde88 100644 +--- a/lib/curl_get_line.h ++++ b/lib/curl_get_line.h +@@ -24,8 +24,9 @@ + * + ***************************************************************************/ + +-/* get_line() makes sure to only return complete whole lines that fit in 'len' +- * bytes and end with a newline. */ +-char *Curl_get_line(char *buf, int len, FILE *input); ++#include "dynbuf.h" ++ ++/* Curl_get_line() returns complete lines that end with a newline. */ ++int Curl_get_line(struct dynbuf *buf, FILE *input); + + #endif /* HEADER_CURL_GET_LINE_H */ +diff --git a/lib/hsts.c b/lib/hsts.c +index 8725a35c1..607755e6b 100644 +--- a/lib/hsts.c ++++ b/lib/hsts.c +@@ -511,7 +511,6 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) + static CURLcode hsts_load(struct hsts *h, const char *file) + { + CURLcode result = CURLE_OK; +- char *line = NULL; + FILE *fp; + + /* we need a private copy of the file name so that the hsts cache file +@@ -523,11 +522,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file) + + fp = fopen(file, FOPEN_READTEXT); + if(fp) { +- line = malloc(MAX_HSTS_LINE); +- if(!line) +- goto fail; +- while(Curl_get_line(line, MAX_HSTS_LINE, fp)) { +- char *lineptr = line; ++ struct dynbuf buf; ++ Curl_dyn_init(&buf, MAX_HSTS_LINE); ++ while(Curl_get_line(&buf, fp)) { ++ char *lineptr = Curl_dyn_ptr(&buf); + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; + if(*lineptr == '#') +@@ -536,15 +534,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file) + + hsts_add(h, lineptr); + } +- free(line); /* free the line buffer */ ++ Curl_dyn_free(&buf); /* free the line buffer */ + fclose(fp); + } + return result; +- +-fail: +- Curl_safefree(h->filename); +- fclose(fp); +- return CURLE_OUT_OF_MEMORY; + } + + /* +diff --git a/lib/netrc.c b/lib/netrc.c +index 038c6dca6..cd2a2844e 100644 +--- a/lib/netrc.c ++++ b/lib/netrc.c +@@ -53,6 +53,8 @@ enum host_lookup_state { + #define NETRC_FAILED -1 + #define NETRC_SUCCESS 0 + ++#define MAX_NETRC_LINE 4096 ++ + /* + * Returns zero on success. + */ +@@ -80,13 +82,14 @@ static int parsenetrc(const char *host, + file = fopen(netrcfile, FOPEN_READTEXT); + if(file) { + bool done = FALSE; +- char netrcbuffer[4096]; +- int netrcbuffsize = (int)sizeof(netrcbuffer); ++ struct dynbuf buf; ++ Curl_dyn_init(&buf, MAX_NETRC_LINE); + +- while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) { ++ while(!done && Curl_get_line(&buf, file)) { + char *tok; + char *tok_end; + bool quoted; ++ char *netrcbuffer = Curl_dyn_ptr(&buf); + if(state == MACDEF) { + if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r')) + state = NOTHING; +@@ -245,6 +248,7 @@ static int parsenetrc(const char *host, + } /* while Curl_get_line() */ + + out: ++ Curl_dyn_free(&buf); + if(!retcode) { + /* success */ + if(login_alloc) { +diff --git a/tests/unit/unit3200.c b/tests/unit/unit3200.c +index 0544bcc93..6f508ce07 100644 +--- a/tests/unit/unit3200.c ++++ b/tests/unit/unit3200.c +@@ -69,7 +69,7 @@ static const char *filecontents[] = { + "LINE1\n" + C4096 "SOME EXTRA TEXT", + +- /* First and third line should be read */ ++ /* Only first should be read */ + "LINE1\n" + C4096 "SOME EXTRA TEXT\n" + "LINE3\n", +@@ -84,11 +84,13 @@ static const char *filecontents[] = { + + UNITTEST_START + size_t i; ++ int rc = 0; + for(i = 0; i < NUMTESTS; i++) { + FILE *fp; +- char buf[4096]; ++ struct dynbuf buf; + int len = 4096; + char *line; ++ Curl_dyn_init(&buf, len); + + fp = fopen(arg, "wb"); + abort_unless(fp != NULL, "Cannot open testfile"); +@@ -101,65 +103,73 @@ UNITTEST_START + fprintf(stderr, "Test %zd...", i); + switch(i) { + case 0: +- line = Curl_get_line(buf, len, fp); ++ rc = Curl_get_line(&buf, fp); ++ line = Curl_dyn_ptr(&buf); + fail_unless(line && !strcmp("LINE1\n", line), +- "First line failed (1)"); +- line = Curl_get_line(buf, len, fp); ++ "First line failed (1)"); ++ rc = Curl_get_line(&buf, fp); ++ line = Curl_dyn_ptr(&buf); + fail_unless(line && !strcmp("LINE2 NEWLINE\n", line), +- "Second line failed (1)"); +- line = Curl_get_line(buf, len, fp); +- abort_unless(line == NULL, "Missed EOF (1)"); ++ "Second line failed (1)"); ++ rc = Curl_get_line(&buf, fp); ++ abort_unless(!Curl_dyn_len(&buf), "Missed EOF (1)"); + break; + case 1: +- line = Curl_get_line(buf, len, fp); ++ rc = Curl_get_line(&buf, fp); ++ line = Curl_dyn_ptr(&buf); + fail_unless(line && !strcmp("LINE1\n", line), +- "First line failed (2)"); +- line = Curl_get_line(buf, len, fp); ++ "First line failed (2)"); ++ rc = Curl_get_line(&buf, fp); ++ line = Curl_dyn_ptr(&buf); + fail_unless(line && !strcmp("LINE2 NONEWLINE\n", line), +- "Second line failed (2)"); +- line = Curl_get_line(buf, len, fp); +- abort_unless(line == NULL, "Missed EOF (2)"); ++ "Second line failed (2)"); ++ rc = Curl_get_line(&buf, fp); ++ abort_unless(!Curl_dyn_len(&buf), "Missed EOF (2)"); + break; + case 2: +- line = Curl_get_line(buf, len, fp); ++ rc = Curl_get_line(&buf, fp); ++ line = Curl_dyn_ptr(&buf); + fail_unless(line && !strcmp("LINE1\n", line), +- "First line failed (3)"); +- line = Curl_get_line(buf, len, fp); +- fail_unless(line == NULL, +- "Did not detect max read on EOF (3)"); ++ "First line failed (3)"); ++ rc = Curl_get_line(&buf, fp); ++ fail_unless(!Curl_dyn_len(&buf), ++ "Did not detect max read on EOF (3)"); + break; + case 3: +- line = Curl_get_line(buf, len, fp); ++ rc = Curl_get_line(&buf, fp); ++ line = Curl_dyn_ptr(&buf); + fail_unless(line && !strcmp("LINE1\n", line), +- "First line failed (4)"); +- line = Curl_get_line(buf, len, fp); +- fail_unless(line == NULL, +- "Did not ignore partial on EOF (4)"); ++ "First line failed (4)"); ++ rc = Curl_get_line(&buf, fp); ++ fail_unless(!Curl_dyn_len(&buf), ++ "Did not ignore partial on EOF (4)"); + break; + case 4: +- line = Curl_get_line(buf, len, fp); ++ rc = Curl_get_line(&buf, fp); ++ line = Curl_dyn_ptr(&buf); + fail_unless(line && !strcmp("LINE1\n", line), +- "First line failed (5)"); +- line = Curl_get_line(buf, len, fp); +- fail_unless(line && !strcmp("LINE3\n", line), +- "Third line failed (5)"); +- line = Curl_get_line(buf, len, fp); +- abort_unless(line == NULL, "Missed EOF (5)"); ++ "First line failed (5)"); ++ rc = Curl_get_line(&buf, fp); ++ fail_unless(!Curl_dyn_len(&buf), ++ "Did not bail out on too long line"); + break; + case 5: +- line = Curl_get_line(buf, len, fp); ++ rc = Curl_get_line(&buf, fp); ++ line = Curl_dyn_ptr(&buf); + fail_unless(line && !strcmp("LINE1\x1aTEST\n", line), +- "Missed/Misinterpreted ^Z (6)"); +- line = Curl_get_line(buf, len, fp); +- abort_unless(line == NULL, "Missed EOF (6)"); ++ "Missed/Misinterpreted ^Z (6)"); ++ rc = Curl_get_line(&buf, fp); ++ abort_unless(!Curl_dyn_len(&buf), "Missed EOF (6)"); + break; + default: + abort_unless(1, "Unknown case"); + break; + } ++ Curl_dyn_free(&buf); + fclose(fp); + fprintf(stderr, "OK\n"); + } ++ return rc; + UNITTEST_STOP + + #else +-- +2.33.0 + diff --git a/backport-CVE-2024-11053-pre2.patch b/backport-CVE-2024-11053-pre2.patch new file mode 100644 index 0000000..eff59cc --- /dev/null +++ b/backport-CVE-2024-11053-pre2.patch @@ -0,0 +1,800 @@ +From 3b43a05e000aa8f65bda513f733a73fefe35d5ca Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Thu, 10 Oct 2024 18:08:07 +0200 +Subject: [PATCH] netrc: cache the netrc file in memory + +So that on redirects etc it does not reread the file but just parses it +again. + +Reported-by: Pierre-Etienne Meunier +Fixes #15248 +Closes #15259 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/3b43a05e000aa8f65bda513f733a73fefe35d5ca +--- + lib/multi.c | 2 + + lib/netrc.c | 407 ++++++++++++++++++++++++------------------ + lib/netrc.h | 14 +- + lib/url.c | 4 +- + lib/urldata.h | 5 + + tests/unit/unit1304.c | 48 ++++- + 6 files changed, 292 insertions(+), 188 deletions(-) + +diff --git a/lib/multi.c b/lib/multi.c +index 223c2339c..0f9fedaff 100644 +--- a/lib/multi.c ++++ b/lib/multi.c +@@ -757,6 +757,8 @@ static CURLcode multi_done(struct Curl_easy *data, + data->state.lastconnect_id = -1; + } + ++ /* flush the netrc cache */ ++ Curl_netrc_cleanup(&data->state.netrc); + Curl_safefree(data->state.buffer); + return result; + } +diff --git a/lib/netrc.c b/lib/netrc.c +index 3c0651dcc..c23f927ce 100644 +--- a/lib/netrc.c ++++ b/lib/netrc.c +@@ -31,7 +31,6 @@ + + #include <curl/curl.h> + #include "netrc.h" +-#include "strtok.h" + #include "strcase.h" + #include "curl_get_line.h" + +@@ -49,21 +48,56 @@ enum host_lookup_state { + MACDEF + }; + ++enum found_state { ++ NONE, ++ LOGIN, ++ PASSWORD ++}; ++ + #define NETRC_FILE_MISSING 1 + #define NETRC_FAILED -1 + #define NETRC_SUCCESS 0 + + #define MAX_NETRC_LINE 4096 ++#define MAX_NETRC_FILE (64*1024) ++#define MAX_NETRC_TOKEN 128 ++ ++static CURLcode file2memory(const char *filename, struct dynbuf *filebuf) ++{ ++ CURLcode result = CURLE_OK; ++ FILE *file = fopen(filename, FOPEN_READTEXT); ++ struct dynbuf linebuf; ++ Curl_dyn_init(&linebuf, MAX_NETRC_LINE); ++ ++ if(file) { ++ while(Curl_get_line(&linebuf, file)) { ++ const char *line = Curl_dyn_ptr(&linebuf); ++ /* skip comments on load */ ++ while(ISBLANK(*line)) ++ line++; ++ if(*line == '#') ++ continue; ++ result = Curl_dyn_add(filebuf, line); ++ if(result) ++ goto done; ++ } ++ } ++done: ++ Curl_dyn_free(&linebuf); ++ if(file) ++ fclose(file); ++ return result; ++} + + /* + * Returns zero on success. + */ +-static int parsenetrc(const char *host, ++static int parsenetrc(struct store_netrc *store, ++ const char *host, + char **loginp, + char **passwordp, +- char *netrcfile) ++ const char *netrcfile) + { +- FILE *file; + int retcode = NETRC_FILE_MISSING; + char *login = *loginp; + char *password = *passwordp; +@@ -71,204 +105,212 @@ static int parsenetrc(const char *host, + bool login_alloc = FALSE; + bool password_alloc = FALSE; + enum host_lookup_state state = NOTHING; ++ enum found_state found = NONE; ++ bool our_login = TRUE; /* With specific_login, found *our* login name (or ++ login-less line) */ ++ bool done = FALSE; ++ char *netrcbuffer; ++ struct dynbuf token; ++ struct dynbuf *filebuf = &store->filebuf; ++ Curl_dyn_init(&token, MAX_NETRC_TOKEN); + +- char state_login = 0; /* Found a login keyword */ +- char state_password = 0; /* Found a password keyword */ +- int state_our_login = TRUE; /* With specific_login, found *our* login +- name (or login-less line) */ +- +- DEBUGASSERT(netrcfile); ++ if(!store->loaded) { ++ if(file2memory(netrcfile, filebuf)) ++ return NETRC_FAILED; ++ store->loaded = TRUE; ++ } + +- file = fopen(netrcfile, FOPEN_READTEXT); +- if(file) { +- bool done = FALSE; +- struct dynbuf buf; +- Curl_dyn_init(&buf, MAX_NETRC_LINE); ++ netrcbuffer = Curl_dyn_ptr(filebuf); + +- while(!done && Curl_get_line(&buf, file)) { +- char *tok; ++ while(!done) { ++ char *tok = netrcbuffer; ++ while(tok) { + char *tok_end; + bool quoted; +- char *netrcbuffer = Curl_dyn_ptr(&buf); ++ Curl_dyn_reset(&token); ++ while(ISBLANK(*tok)) ++ tok++; ++ /* tok is first non-space letter */ + if(state == MACDEF) { +- if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r')) +- state = NOTHING; +- else +- continue; ++ if((*tok == '\n') || (*tok == '\r')) ++ state = NOTHING; /* end of macro definition */ + } +- tok = netrcbuffer; +- while(tok) { +- while(ISBLANK(*tok)) +- tok++; +- /* tok is first non-space letter */ +- if(!*tok || (*tok == '#')) +- /* end of line or the rest is a comment */ +- break; + +- /* leading double-quote means quoted string */ +- quoted = (*tok == '\"'); ++ if(!*tok || (*tok == '\n')) ++ /* end of line */ ++ break; + +- tok_end = tok; +- if(!quoted) { +- while(!ISSPACE(*tok_end)) +- tok_end++; +- *tok_end = 0; ++ /* leading double-quote means quoted string */ ++ quoted = (*tok == '\"'); ++ ++ tok_end = tok; ++ if(!quoted) { ++ size_t len = 0; ++ while(!ISSPACE(*tok_end)) { ++ tok_end++; ++ len++; + } +- else { +- bool escape = FALSE; +- bool endquote = FALSE; +- char *store = tok; +- tok_end++; /* pass the leading quote */ +- while(*tok_end) { +- char s = *tok_end; +- if(escape) { +- escape = FALSE; +- switch(s) { +- case 'n': +- s = '\n'; +- break; +- case 'r': +- s = '\r'; +- break; +- case 't': +- s = '\t'; +- break; +- } +- } +- else if(s == '\\') { +- escape = TRUE; +- tok_end++; +- continue; +- } +- else if(s == '\"') { +- tok_end++; /* pass the ending quote */ +- endquote = TRUE; ++ if(!len || Curl_dyn_addn(&token, tok, len)) { ++ retcode = NETRC_FAILED; ++ goto out; ++ } ++ } ++ else { ++ bool escape = FALSE; ++ bool endquote = FALSE; ++ tok_end++; /* pass the leading quote */ ++ while(*tok_end) { ++ char s = *tok_end; ++ if(escape) { ++ escape = FALSE; ++ switch(s) { ++ case 'n': ++ s = '\n'; ++ break; ++ case 'r': ++ s = '\r'; ++ break; ++ case 't': ++ s = '\t'; + break; + } +- *store++ = s; ++ } ++ else if(s == '\\') { ++ escape = TRUE; + tok_end++; ++ continue; ++ } ++ else if(s == '\"') { ++ tok_end++; /* pass the ending quote */ ++ endquote = TRUE; ++ break; + } +- *store = 0; +- if(escape || !endquote) { +- /* bad syntax, get out */ ++ if(Curl_dyn_addn(&token, &s, 1)) { + retcode = NETRC_FAILED; + goto out; + } ++ tok_end++; + } +- +- if((login && *login) && (password && *password)) { +- done = TRUE; +- break; ++ if(escape || !endquote) { ++ /* bad syntax, get out */ ++ retcode = NETRC_FAILED; ++ goto out; + } ++ } + +- switch(state) { +- case NOTHING: +- if(strcasecompare("macdef", tok)) { +- /* Define a macro. A macro is defined with the specified name; its +- contents begin with the next .netrc line and continue until a +- null line (consecutive new-line characters) is encountered. */ +- state = MACDEF; +- } +- else if(strcasecompare("machine", tok)) { +- /* the next tok is the machine name, this is in itself the +- delimiter that starts the stuff entered for this machine, +- after this we need to search for 'login' and +- 'password'. */ +- state = HOSTFOUND; +- } +- else if(strcasecompare("default", tok)) { +- state = HOSTVALID; +- retcode = NETRC_SUCCESS; /* we did find our host */ +- } +- break; +- case MACDEF: +- if(!strlen(tok)) { +- state = NOTHING; +- } +- break; +- case HOSTFOUND: +- if(strcasecompare(host, tok)) { +- /* and yes, this is our host! */ +- state = HOSTVALID; +- retcode = NETRC_SUCCESS; /* we did find our host */ ++ if((login && *login) && (password && *password)) { ++ done = TRUE; ++ break; ++ } ++ ++ tok = Curl_dyn_ptr(&token); ++ ++ switch(state) { ++ case NOTHING: ++ if(strcasecompare("macdef", tok)) ++ /* Define a macro. A macro is defined with the specified name; its ++ contents begin with the next .netrc line and continue until a ++ null line (consecutive new-line characters) is encountered. */ ++ state = MACDEF; ++ else if(strcasecompare("machine", tok)) ++ /* the next tok is the machine name, this is in itself the delimiter ++ that starts the stuff entered for this machine, after this we ++ need to search for 'login' and 'password'. */ ++ state = HOSTFOUND; ++ else if(strcasecompare("default", tok)) { ++ state = HOSTVALID; ++ retcode = NETRC_SUCCESS; /* we did find our host */ ++ } ++ break; ++ case MACDEF: ++ if(!*tok) ++ state = NOTHING; ++ break; ++ case HOSTFOUND: ++ if(strcasecompare(host, tok)) { ++ /* and yes, this is our host! */ ++ state = HOSTVALID; ++ retcode = NETRC_SUCCESS; /* we did find our host */ ++ } ++ else ++ /* not our host */ ++ state = NOTHING; ++ break; ++ case HOSTVALID: ++ /* we are now parsing sub-keywords concerning "our" host */ ++ if(found == LOGIN) { ++ if(specific_login) { ++ our_login = !Curl_timestrcmp(login, tok); + } +- else +- /* not our host */ +- state = NOTHING; +- break; +- case HOSTVALID: +- /* we are now parsing sub-keywords concerning "our" host */ +- if(state_login) { +- if(specific_login) { +- state_our_login = !Curl_timestrcmp(login, tok); ++ else if(!login || Curl_timestrcmp(login, tok)) { ++ if(login_alloc) ++ free(login); ++ login = strdup(tok); ++ if(!login) { ++ retcode = NETRC_FAILED; /* allocation failed */ ++ goto out; + } +- else if(!login || Curl_timestrcmp(login, tok)) { +- if(login_alloc) { +- free(login); +- login_alloc = FALSE; +- } +- login = strdup(tok); +- if(!login) { +- retcode = NETRC_FAILED; /* allocation failed */ +- goto out; +- } +- login_alloc = TRUE; +- } +- state_login = 0; ++ login_alloc = TRUE; + } +- else if(state_password) { +- if((state_our_login || !specific_login) +- && (!password || Curl_timestrcmp(password, tok))) { +- if(password_alloc) { +- free(password); +- password_alloc = FALSE; +- } +- password = strdup(tok); +- if(!password) { +- retcode = NETRC_FAILED; /* allocation failed */ +- goto out; +- } +- password_alloc = TRUE; ++ found = NONE; ++ } ++ else if(found == PASSWORD) { ++ if((our_login || !specific_login) && ++ (!password || Curl_timestrcmp(password, tok))) { ++ if(password_alloc) ++ free(password); ++ password = strdup(tok); ++ if(!password) { ++ retcode = NETRC_FAILED; /* allocation failed */ ++ goto out; + } +- state_password = 0; +- } +- else if(strcasecompare("login", tok)) +- state_login = 1; +- else if(strcasecompare("password", tok)) +- state_password = 1; +- else if(strcasecompare("machine", tok)) { +- /* ok, there's machine here go => */ +- state = HOSTFOUND; +- state_our_login = FALSE; ++ password_alloc = TRUE; + } +- break; +- } /* switch (state) */ +- tok = ++tok_end; +- } +- } /* while Curl_get_line() */ ++ found = NONE; ++ } ++ else if(strcasecompare("login", tok)) ++ found = LOGIN; ++ else if(strcasecompare("password", tok)) ++ found = PASSWORD; ++ else if(strcasecompare("machine", tok)) { ++ /* ok, there is machine here go => */ ++ state = HOSTFOUND; ++ found = NONE; ++ } ++ break; ++ } /* switch (state) */ ++ tok = ++tok_end; ++ } ++ if(!done) { ++ char *nl = NULL; ++ if(tok) ++ nl = strchr(tok, '\n'); ++ if(!nl) ++ break; ++ /* point to next line */ ++ netrcbuffer = &nl[1]; ++ } ++ } /* while !done */ + + out: +- Curl_dyn_free(&buf); +- if(!retcode) { +- /* success */ +- if(login_alloc) { +- if(*loginp) +- free(*loginp); +- *loginp = login; +- } +- if(password_alloc) { +- if(*passwordp) +- free(*passwordp); +- *passwordp = password; +- } ++ Curl_dyn_free(&token); ++ if(!retcode) { ++ /* success */ ++ if(login_alloc) { ++ free(*loginp); ++ *loginp = login; + } +- else { +- if(login_alloc) +- free(login); +- if(password_alloc) +- free(password); ++ if(password_alloc) { ++ free(*passwordp); ++ *passwordp = password; + } +- fclose(file); ++ } ++ else { ++ Curl_dyn_free(filebuf); ++ if(login_alloc) ++ free(login); ++ if(password_alloc) ++ free(password); + } + + return retcode; +@@ -280,7 +322,8 @@ out: + * *loginp and *passwordp MUST be allocated if they aren't NULL when passed + * in. + */ +-int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, ++int Curl_parsenetrc(struct store_netrc *store, const char *host, ++ char **loginp, char **passwordp, + char *netrcfile) + { + int retcode = 1; +@@ -329,7 +372,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, + free(homea); + return -1; + } +- retcode = parsenetrc(host, loginp, passwordp, filealloc); ++ retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + free(filealloc); + #ifdef WIN32 + if(retcode == NETRC_FILE_MISSING) { +@@ -339,15 +382,25 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, + free(homea); + return -1; + } +- retcode = parsenetrc(host, loginp, passwordp, filealloc); ++ retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + free(filealloc); + } + #endif + free(homea); + } + else +- retcode = parsenetrc(host, loginp, passwordp, netrcfile); ++ retcode = parsenetrc(store, host, loginp, passwordp, netrcfile); + return retcode; + } + ++void Curl_netrc_init(struct store_netrc *s) ++{ ++ Curl_dyn_init(&s->filebuf, MAX_NETRC_FILE); ++ s->loaded = FALSE; ++} ++void Curl_netrc_cleanup(struct store_netrc *s) ++{ ++ Curl_dyn_free(&s->filebuf); ++ s->loaded = FALSE; ++} + #endif +diff --git a/lib/netrc.h b/lib/netrc.h +index 37c95db5e..0ef9ff78e 100644 +--- a/lib/netrc.h ++++ b/lib/netrc.h +@@ -26,9 +26,19 @@ + + #include "curl_setup.h" + #ifndef CURL_DISABLE_NETRC ++#include "dynbuf.h" ++ ++struct store_netrc { ++ struct dynbuf filebuf; ++ char *filename; ++ BIT(loaded); ++}; ++ ++void Curl_netrc_init(struct store_netrc *s); ++void Curl_netrc_cleanup(struct store_netrc *s); + + /* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */ +-int Curl_parsenetrc(const char *host, char **loginp, ++int Curl_parsenetrc(struct store_netrc *s, const char *host, char **loginp, + char **passwordp, char *filename); + /* Assume: (*passwordp)[0]=0, host[0] != 0. + * If (*loginp)[0] = 0, search for login and password within a machine +@@ -38,6 +48,8 @@ int Curl_parsenetrc(const char *host, char **loginp, + #else + /* disabled */ + #define Curl_parsenetrc(a,b,c,d,e,f) 1 ++#define Curl_netrc_init(x) ++#define Curl_netrc_cleanup(x) + #endif + + #endif /* HEADER_CURL_NETRC_H */ +diff --git a/lib/url.c b/lib/url.c +index a59cb0e34..45745bc60 100644 +--- a/lib/url.c ++++ b/lib/url.c +@@ -338,6 +338,7 @@ CURLcode Curl_close(struct Curl_easy **datap) + Curl_wildcard_dtor(&data->wildcard); + Curl_freeset(data); + Curl_headers_cleanup(data); ++ Curl_netrc_cleanup(&data->state.netrc); + free(data); + return CURLE_OK; + } +@@ -545,6 +546,7 @@ CURLcode Curl_open(struct Curl_easy **curl) + + data->progress.flags |= PGRS_HIDE; + data->state.current_speed = -1; /* init to negative == impossible */ ++ Curl_netrc_init(&data->state.netrc); + } + + if(result) { +@@ -2689,7 +2691,7 @@ static CURLcode override_login(struct Curl_easy *data, + url_provided = TRUE; + } + +- ret = Curl_parsenetrc(conn->host.name, ++ ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, + userp, passwdp, + data->set.str[STRING_NETRC_FILE]); + if(ret > 0) { +diff --git a/lib/urldata.h b/lib/urldata.h +index 4e0d6ef98..6aa26237d 100644 +--- a/lib/urldata.h ++++ b/lib/urldata.h +@@ -163,6 +163,7 @@ typedef unsigned int curl_prot_t; + #include "splay.h" + #include "dynbuf.h" + #include "dynhds.h" ++#include "netrc.h" + + /* return the count of bytes sent, or -1 on error */ + typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ +@@ -1313,6 +1314,10 @@ struct UrlState { + CURLcode hresult; /* used to pass return codes back from hyper callbacks */ + #endif + ++#ifndef CURL_DISABLE_NETRC ++ struct store_netrc netrc; ++#endif ++ + /* Dynamically allocated strings, MUST be freed before this struct is + killed. */ + struct dynamically_allocated_data { +diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c +index 2171c0736..238d3c0f7 100644 +--- a/tests/unit/unit1304.c ++++ b/tests/unit/unit1304.c +@@ -49,17 +49,22 @@ static void unit_stop(void) + } + + UNITTEST_START ++{ + int result; ++ struct store_netrc store; + + /* + * Test a non existent host in our netrc file. + */ +- result = Curl_parsenetrc("test.example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "test.example.com", &login, &password, arg); + fail_unless(result == 1, "Host not found should return 1"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(login[0] == 0, "login should not have been changed"); ++ Curl_netrc_cleanup(&store); + + /* + * Test a non existent login in our netrc file. +@@ -67,13 +72,16 @@ UNITTEST_START + free(login); + login = strdup("me"); + abort_unless(login != NULL, "returned NULL!"); +- result = Curl_parsenetrc("example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "me", 2) == 0, + "login should not have been changed"); ++ Curl_netrc_cleanup(&store); + + /* + * Test a non existent login and host in our netrc file. +@@ -81,13 +89,16 @@ UNITTEST_START + free(login); + login = strdup("me"); + abort_unless(login != NULL, "returned NULL!"); +- result = Curl_parsenetrc("test.example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "test.example.com", &login, &password, arg); + fail_unless(result == 1, "Host not found should return 1"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "me", 2) == 0, + "login should not have been changed"); ++ Curl_netrc_cleanup(&store); + + /* + * Test a non existent login (substring of an existing one) in our +@@ -96,13 +107,16 @@ UNITTEST_START + free(login); + login = strdup("admi"); + abort_unless(login != NULL, "returned NULL!"); +- result = Curl_parsenetrc("example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "admi", 4) == 0, + "login should not have been changed"); ++ Curl_netrc_cleanup(&store); + + /* + * Test a non existent login (superstring of an existing one) +@@ -111,13 +125,16 @@ UNITTEST_START + free(login); + login = strdup("adminn"); + abort_unless(login != NULL, "returned NULL!"); +- result = Curl_parsenetrc("example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "adminn", 6) == 0, + "login should not have been changed"); ++ Curl_netrc_cleanup(&store); + + /* + * Test for the first existing host in our netrc file +@@ -126,13 +143,16 @@ UNITTEST_START + free(login); + login = strdup(""); + abort_unless(login != NULL, "returned NULL!"); +- result = Curl_parsenetrc("example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(strncmp(password, "passwd", 6) == 0, + "password should be 'passwd'"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'"); ++ Curl_netrc_cleanup(&store); + + /* + * Test for the first existing host in our netrc file +@@ -141,13 +161,16 @@ UNITTEST_START + free(password); + password = strdup(""); + abort_unless(password != NULL, "returned NULL!"); +- result = Curl_parsenetrc("example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(strncmp(password, "passwd", 6) == 0, + "password should be 'passwd'"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'"); ++ Curl_netrc_cleanup(&store); + + /* + * Test for the second existing host in our netrc file +@@ -159,13 +182,16 @@ UNITTEST_START + free(login); + login = strdup(""); + abort_unless(login != NULL, "returned NULL!"); +- result = Curl_parsenetrc("curl.example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "curl.example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(strncmp(password, "none", 4) == 0, + "password should be 'none'"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'"); ++ Curl_netrc_cleanup(&store); + + /* + * Test for the second existing host in our netrc file +@@ -174,14 +200,18 @@ UNITTEST_START + free(password); + password = strdup(""); + abort_unless(password != NULL, "returned NULL!"); +- result = Curl_parsenetrc("curl.example.com", &login, &password, arg); ++ Curl_netrc_init(&store); ++ result = Curl_parsenetrc(&store, ++ "curl.example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(strncmp(password, "none", 4) == 0, + "password should be 'none'"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'"); ++ Curl_netrc_cleanup(&store); + ++} + UNITTEST_STOP + + #else +-- +2.33.0 + diff --git a/backport-CVE-2024-11053-pre3.patch b/backport-CVE-2024-11053-pre3.patch new file mode 100644 index 0000000..286a7f4 --- /dev/null +++ b/backport-CVE-2024-11053-pre3.patch @@ -0,0 +1,349 @@ +From 9bee39bfed2c413b4cc4eb306a57ac92a1854907 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Sat, 12 Oct 2024 23:54:39 +0200 +Subject: [PATCH] url: use same credentials on redirect + +Previously it could lose the username and only use the password. + +Added test 998 and 999 to verify. + +Reported-by: Tobias Bora +Fixes #15262 +Closes #15282 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/9bee39bfed2c413b4cc4eb306a57ac92a1854907 +--- + lib/transfer.c | 3 + + lib/url.c | 19 ++++--- + lib/urldata.h | 9 ++- + tests/data/Makefile.inc | 14 +++++++------- + tests/data/test998 | 92 ++++++++++++++++++++++++++++++ + tests/data/test999 | 81 ++++++++++++++++++++++++++ + 6 files changed, 195 insertions(+), 11 deletions(-) + create mode 100644 tests/data/test998 + create mode 100644 tests/data/test999 + +diff --git a/lib/transfer.c b/lib/transfer.c +index 79d648cab..3a9239254 100644 +--- a/lib/transfer.c ++++ b/lib/transfer.c +@@ -679,6 +679,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) + return CURLE_OUT_OF_MEMORY; + } + ++ if(data->set.str[STRING_USERNAME] || ++ data->set.str[STRING_PASSWORD]) ++ data->state.creds_from = CREDS_OPTION; + if(!result) + result = Curl_setstropt(&data->state.aptr.user, + data->set.str[STRING_USERNAME]); +diff --git a/lib/url.c b/lib/url.c +index 45745bc60..261f61d8d 100644 +--- a/lib/url.c ++++ b/lib/url.c +@@ -1860,10 +1860,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, + return result; + + /* +- * User name and password set with their own options override the +- * credentials possibly set in the URL. ++ * username and password set with their own options override the credentials ++ * possibly set in the URL, but netrc does not. + */ +- if(!data->set.str[STRING_PASSWORD]) { ++ if(!data->state.aptr.passwd || (data->state.creds_from != CREDS_OPTION)) { + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + if(!uc) { + char *decoded; +@@ -1876,12 +1876,13 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, + result = Curl_setstropt(&data->state.aptr.passwd, decoded); + if(result) + return result; ++ data->state.creds_from = CREDS_URL; + } + else if(uc != CURLUE_NO_PASSWORD) + return Curl_uc_to_curlcode(uc); + } + +- if(!data->set.str[STRING_USERNAME]) { ++ if(!data->state.aptr.user || (data->state.creds_from != CREDS_OPTION)) { + /* we don't use the URL API's URL decoder option here since it rejects + control codes and we want to allow them for some schemes in the user + and password fields */ +@@ -1895,13 +1896,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, + return result; + conn->user = decoded; + result = Curl_setstropt(&data->state.aptr.user, decoded); ++ data->state.creds_from = CREDS_URL; + } + else if(uc != CURLUE_NO_USER) + return Curl_uc_to_curlcode(uc); +- else if(data->state.aptr.passwd) { +- /* no user was set but a password, set a blank user */ +- result = Curl_setstropt(&data->state.aptr.user, ""); +- } + if(result) + return result; + } +@@ -2685,7 +2683,8 @@ static CURLcode override_login(struct Curl_easy *data, + int ret; + bool url_provided = FALSE; + +- if(data->state.aptr.user) { ++ if(data->state.aptr.user && ++ (data->state.creds_from != CREDS_NETRC)) { + /* there was a user name in the URL. Use the URL decoded version */ + userp = &data->state.aptr.user; + url_provided = TRUE; +@@ -2733,6 +2732,7 @@ static CURLcode override_login(struct Curl_easy *data, + result = Curl_setstropt(&data->state.aptr.user, *userp); + if(result) + return result; ++ data->state.creds_from = CREDS_NETRC; + } + } + if(data->state.aptr.user) { +@@ -2750,6 +2750,7 @@ static CURLcode override_login(struct Curl_easy *data, + CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); + if(result) + return result; ++ data->state.creds_from = CREDS_NETRC; + } + if(data->state.aptr.passwd) { + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, +diff --git a/lib/urldata.h b/lib/urldata.h +index 6aa26237d..73f662159 100644 +--- a/lib/urldata.h ++++ b/lib/urldata.h +@@ -1206,6 +1206,11 @@ struct urlpieces { + char *query; + }; + ++#define CREDS_NONE 0 ++#define CREDS_URL 1 /* from URL */ ++#define CREDS_OPTION 2 /* set with a CURLOPT_ */ ++#define CREDS_NETRC 3 /* found in netrc */ ++ + struct UrlState { + /* Points to the connection cache */ + struct conncache *conn_cache; +@@ -1344,7 +1349,6 @@ struct UrlState { + char *proxyuser; + char *proxypasswd; + } aptr; +- + unsigned char httpwant; /* when non-zero, a specific HTTP version requested + to be used in the library's request(s) */ + unsigned char httpversion; /* the lowest HTTP version*10 reported by any +@@ -1354,6 +1358,9 @@ struct UrlState { + unsigned char dselect_bits; /* != 0 -> bitmask of socket events for this + transfer overriding anything the socket may + report */ ++ unsigned int creds_from:2; /* where is the server credentials originating ++ from, see the CREDS_* defines above */ ++ + #ifdef CURLDEBUG + BIT(conncache_lock); + #endif +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index 480a88208..02bf2ae25 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -133,7 +133,7 @@ test961 test962 test963 test964 test965 test966 test967 test968 test969 \ + test961 test962 test963 test964 test965 test966 test967 test968 test969 \ + test970 test971 test972 test973 test974 test975 test976 test977 test978 \ + test979 test980 test981 test982 test983 test984 test985 test986 test987 \ +-test988 test989 test990 test991 \ ++test988 test989 test990 test991 test998 test999 \ + \ + test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \ + test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 \ +diff --git a/tests/data/test998 b/tests/data/test998 +new file mode 100644 +index 000000000..c3a8f5169 +--- /dev/null ++++ b/tests/data/test998 +@@ -0,0 +1,92 @@ ++<testcase> ++<info> ++<keywords> ++HTTP ++--location-trusted ++</keywords> ++</info> ++ ++# ++# Server-side ++<reply> ++<data> ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 0 ++Connection: close ++Content-Type: text/html ++Location: http://somewhere.else.example/a/path/%TESTNUMBER0002 ++ ++</data> ++<data2> ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Content-Length: 6 ++Content-Type: text/html ++Funny-head: yesyes ++ ++-foo- ++</data2> ++ ++<datacheck> ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 0 ++Connection: close ++Content-Type: text/html ++Location: http://somewhere.else.example/a/path/%TESTNUMBER0002 ++ ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Content-Length: 6 ++Content-Type: text/html ++Funny-head: yesyes ++ ++-foo- ++</datacheck> ++ ++</reply> ++ ++# ++# Client-side ++<client> ++<features> ++proxy ++</features> ++<server> ++http ++</server> ++<name> ++HTTP with auth in URL redirected to another host ++</name> ++<command> ++-x %HOSTIP:%HTTPPORT http://alberto:einstein@somwhere.example/%TESTNUMBER --location-trusted ++</command> ++</client> ++ ++# ++# Verify data after the test has been "shot" ++<verify> ++<strip> ++QUIT ++</strip> ++<protocol> ++GET http://somwhere.example/998 HTTP/1.1
++Host: somwhere.example
++Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg==
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++GET http://somewhere.else.example/a/path/9980002 HTTP/1.1
++Host: somewhere.else.example
++Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg==
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++</protocol> ++</verify> ++</testcase> +diff --git a/tests/data/test999 b/tests/data/test999 +new file mode 100644 +index 000000000..990a8d09a +--- /dev/null ++++ b/tests/data/test999 +@@ -0,0 +1,81 @@ ++<testcase> ++<info> ++<keywords> ++HTTP ++--location-trusted ++</keywords> ++</info> ++ ++# ++# Server-side ++<reply> ++<data nocheck="yes"> ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Content-Length: 6 ++Content-Type: text/html ++Funny-head: yesyes ++ ++-foo- ++</data> ++ ++<datacheck> ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 0 ++Connection: close ++Content-Type: text/html ++Location: http://somewhere.else.example/a/path/%TESTNUMBER0002 ++ ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Content-Length: 6 ++Content-Type: text/html ++Funny-head: yesyes ++ ++-foo- ++</datacheck> ++ ++</reply> ++ ++# ++# Client-side ++<client> ++<features> ++proxy ++</features> ++<server> ++http ++</server> ++<name> ++HTTP with auth in first URL but not second ++</name> ++<command> ++-x %HOSTIP:%HTTPPORT http://alberto:einstein@somwhere.example/%TESTNUMBER http://somewhere.else.example/%TESTNUMBER ++</command> ++</client> ++ ++# ++# Verify data after the test has been "shot" ++<verify> ++<strip> ++QUIT ++</strip> ++<protocol> ++GET http://somwhere.example/%TESTNUMBER HTTP/1.1
++Host: somwhere.example
++Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg==
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++GET http://somewhere.else.example/%TESTNUMBER HTTP/1.1
++Host: somewhere.else.example
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++</protocol> ++</verify> ++</testcase> +-- +2.33.0 + diff --git a/backport-CVE-2024-11053-pre4.patch b/backport-CVE-2024-11053-pre4.patch new file mode 100644 index 0000000..98e3deb --- /dev/null +++ b/backport-CVE-2024-11053-pre4.patch @@ -0,0 +1,223 @@ +From f5c616930b5cf148b1b2632da4f5963ff48bdf88 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Thu, 7 Nov 2024 08:52:38 +0100 +Subject: [PATCH] duphandle: also init netrc + +The netrc init was only done in the Curl_open, meaning that a duplicated +handle would not get inited properly. + +Added test 2309 to verify. It does netrc auth with a duplicated handle. + +Regression from 3b43a05e000aa8f65bda513f733a + +Reported-by: tranzystorekk on github +Fixes #15496 +Closes #15503 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/f5c616930b5cf148b1b2632da4f5963ff48bdf88 +--- + lib/easy.c | 1 + + tests/data/Makefile.inc | 2 ++ + tests/data/test2309 | 66 ++++++++++++++++++++++++++++++++++++++ + tests/libtest/Makefile.inc | 5 ++- + tests/libtest/lib2309.c | 66 ++++++++++++++++++++++++++++++++++++++ + 5 files changed, 139 insertions(+), 1 deletions(-) + create mode 100644 tests/data/test2309 + create mode 100644 tests/libtest/lib2309.c + +diff --git a/lib/easy.c b/lib/easy.c +index d16fa8c07..ac8fab342 100644 +--- a/lib/easy.c ++++ b/lib/easy.c +@@ -940,6 +940,7 @@ CURL *curl_easy_duphandle(CURL *d) + goto fail; + + Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); ++ Curl_netrc_init(&outcurl->state.netrc); + + /* the connection cache is setup on demand */ + outcurl->state.conn_cache = NULL; +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index 02bf2ae25..ea5221c00 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -255,6 +255,8 @@ test2100 \ + test2200 test2201 test2202 test2203 test2204 test2205 \ + \ + test2300 test2301 test2302 test2303 test2304 test2305 test2306 \ ++\ ++test2309 \ + \ + test2400 test2401 test2402 test2403 test2404 \ + \ +diff --git a/tests/data/test2309 b/tests/data/test2309 +new file mode 100644 +index 000000000..4ba78ee91 +--- /dev/null ++++ b/tests/data/test2309 +@@ -0,0 +1,66 @@ ++<testcase> ++<info> ++<keywords> ++netrc ++HTTP ++</keywords> ++</info> ++# ++# Server-side ++<reply> ++<data crlf="yes" nocheck="yes"> ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 6 ++Connection: close ++Content-Type: text/html ++Funny-head: yesyes ++ ++-foo- ++</data> ++</reply> ++ ++# ++# Client-side ++<client> ++<server> ++http ++</server> ++<features> ++proxy ++</features> ++ ++# Reproducing issue 15496 ++<name> ++HTTP with .netrc using duped easy handle ++</name> ++<tool> ++lib%TESTNUMBER ++</tool> ++<command> ++http://github.com %LOGDIR/netrc%TESTNUMBER http://%HOSTIP:%HTTPPORT/ ++</command> ++<file name="%LOGDIR/netrc%TESTNUMBER" > ++ ++machine github.com ++ ++login daniel ++password $y$j9T$WUVjiVvDbRAWafDLs6cab1$01NX.oaZKf5lw8MR2Nk9Yaxv4CqbE0IaDF.GpGxPul1 ++</file> ++</client> ++ ++<verify> ++<protocol> ++GET http://github.com/ HTTP/1.1
++Host: github.com
++Authorization: Basic %b64[daniel:$y$j9T$WUVjiVvDbRAWafDLs6cab1$01NX.oaZKf5lw8MR2Nk9Yaxv4CqbE0IaDF.GpGxPul1]b64%
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++</protocol> ++</verify> ++</testcase> +diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc +index 339a00fc4..8f58fd642 100644 +--- a/tests/libtest/Makefile.inc ++++ b/tests/libtest/Makefile.inc +@@ -77,7 +77,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \ + lib1945 lib1946 lib1947 lib1948 lib1955 lib1956 lib1957 lib1958 lib1959 \ + lib1960 lib1964 \ + lib1970 lib1971 lib1972 lib1973 lib1974 lib1975 \ +- lib2301 lib2302 lib2304 lib2305 lib2306 \ ++ lib2301 lib2302 lib2304 lib2305 lib2306 lib2309 \ + lib2402 lib2404 \ + lib2502 \ + lib3010 lib3025 lib3026 lib3027 \ +@@ -683,6 +683,9 @@ lib2306_LDADD = $(TESTUTIL_LIBS) + lib2306_SOURCES = lib2306.c $(SUPPORTFILES) + lib2306_LDADD = $(TESTUTIL_LIBS) + ++lib2309_SOURCES = lib2309.c $(SUPPORTFILES) ++lib2309_LDADD = $(TESTUTIL_LIBS) ++ + lib2402_SOURCES = lib2402.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) + lib2402_LDADD = $(TESTUTIL_LIBS) + +diff --git a/tests/libtest/lib2309.c b/tests/libtest/lib2309.c +new file mode 100644 +index 000000000..11f1c1fbd +--- /dev/null ++++ b/tests/libtest/lib2309.c +@@ -0,0 +1,66 @@ ++/*************************************************************************** ++ * _ _ ____ _ ++ * Project ___| | | | _ \| | ++ * / __| | | | |_) | | ++ * | (__| |_| | _ <| |___ ++ * \___|\___/|_| \_\_____| ++ * ++ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. ++ * ++ * This software is licensed as described in the file COPYING, which ++ * you should have received as part of this distribution. The terms ++ * are also available at https://curl.se/docs/copyright.html. ++ * ++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell ++ * copies of the Software, and permit persons to whom the Software is ++ * furnished to do so, under the terms of the COPYING file. ++ * ++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY ++ * KIND, either express or implied. ++ * ++ * SPDX-License-Identifier: curl ++ * ++ ***************************************************************************/ ++ ++#include "test.h" ++#include "testtrace.h" ++ ++#include <curl/curl.h> ++ ++static size_t cb_ignore(char *buffer, size_t size, size_t nmemb, void *userp) ++{ ++ (void)buffer; ++ (void)size; ++ (void)nmemb; ++ (void)userp; ++ return CURL_WRITEFUNC_ERROR; ++} ++ ++int test(char *URL) ++{ ++ CURL *curl; ++ CURL *curldupe; ++ int res = CURLE_OK; ++ ++ global_init(CURL_GLOBAL_ALL); ++ curl = curl_easy_init(); ++ if(curl) { ++ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb_ignore); ++ curl_easy_setopt(curl, CURLOPT_URL, URL); ++ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); ++ curl_easy_setopt(curl, CURLOPT_PROXY, libtest_arg3); ++ curl_easy_setopt(curl, CURLOPT_NETRC, (long)CURL_NETRC_REQUIRED); ++ curl_easy_setopt(curl, CURLOPT_NETRC_FILE, libtest_arg2); ++ ++ curldupe = curl_easy_duphandle(curl); ++ if(curldupe) { ++ res = curl_easy_perform(curldupe); ++ printf("Returned %d, should be %d.\n", res, CURLE_WRITE_ERROR); ++ fflush(stdout); ++ curl_easy_cleanup(curldupe); ++ } ++ curl_easy_cleanup(curl); ++ } ++ curl_global_cleanup(); ++ return 0; ++} +-- +2.33.0 + diff --git a/backport-CVE-2024-11053-pre5.patch b/backport-CVE-2024-11053-pre5.patch new file mode 100644 index 0000000..a50c273 --- /dev/null +++ b/backport-CVE-2024-11053-pre5.patch @@ -0,0 +1,37 @@ +From 0cdde0fdfbeb8c35420f6d03fa4b77ed73497694 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Thu, 7 Nov 2024 17:03:54 +0100 +Subject: [PATCH] netrc: support large file, longer lines, longer tokens + +Regression from 3b43a05e000aa8f6 (shipped in 8.11.0) + +Reported-by: Moritz +Fixes #15513 +Closes #15514 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/0cdde0fdfbeb8c35420f6d03fa4b77ed73497694 +--- + lib/netrc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/lib/netrc.c b/lib/netrc.c +index c23f927ce..034c0307a 100644 +--- a/lib/netrc.c ++++ b/lib/netrc.c +@@ -58,9 +58,9 @@ enum found_state { + #define NETRC_FAILED -1 + #define NETRC_SUCCESS 0 + +-#define MAX_NETRC_LINE 4096 +-#define MAX_NETRC_FILE (64*1024) +-#define MAX_NETRC_TOKEN 128 ++#define MAX_NETRC_LINE 16384 ++#define MAX_NETRC_FILE (128*1024) ++#define MAX_NETRC_TOKEN 4096 + + static CURLcode file2memory(const char *filename, struct dynbuf *filebuf) + { +-- +2.33.0 + diff --git a/backport-CVE-2024-11053.patch b/backport-CVE-2024-11053.patch new file mode 100644 index 0000000..b920c42 --- /dev/null +++ b/backport-CVE-2024-11053.patch @@ -0,0 +1,728 @@ +From e9b9bbac22c26cf67316fa8e6c6b9e831af31949 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Fri, 15 Nov 2024 11:06:36 +0100 +Subject: [PATCH] netrc: address several netrc parser flaws + +- make sure that a match that returns a username also returns a + password, that should be blank if no password is found + +- fix handling of multiple logins for same host where the password/login + order might be reversed. + +- reject credentials provided in the .netrc if they contain ASCII control + codes - if the used protocol does not support such (like HTTP and WS do) + +Reported-by: Harry Sintonen + +Add test 478, 479 and 480 to verify. Updated unit 1304. + +Closes #15586 + +Conflict:context adapt +Reference:https://github.com/curl/curl/e9b9bbac22c26cf67316fa8e6c6b9e831af31949 +--- + lib/netrc.c | 113 +++++++++++++++++++++++------------------ + lib/url.c | 60 +++++++++++++++------- + tests/data/Makefile.inc | 1 + + tests/data/test478 | 73 ++++++++++++++++++++++++++ + tests/data/test479 | 107 ++++++++++++++++++++++++++++++++++++++ + tests/data/test480 | 38 ++++++++++++++ + tests/unit/unit1304.c | 75 ++++++++------------------- + 7 files changed, 345 insertions(+), 122 deletions(-) + create mode 100644 tests/data/test478 + create mode 100644 tests/data/test479 + create mode 100644 tests/data/test480 + +diff --git a/lib/netrc.c b/lib/netrc.c +index 034c0307a..e787a6ffc 100644 +--- a/lib/netrc.c ++++ b/lib/netrc.c +@@ -54,6 +54,9 @@ enum found_state { + PASSWORD + }; + ++#define FOUND_LOGIN 1 ++#define FOUND_PASSWORD 2 ++ + #define NETRC_FILE_MISSING 1 + #define NETRC_FAILED -1 + #define NETRC_SUCCESS 0 +@@ -94,24 +97,24 @@ done: + */ + static int parsenetrc(struct store_netrc *store, + const char *host, +- char **loginp, ++ char **loginp, /* might point to a username */ + char **passwordp, + const char *netrcfile) + { + int retcode = NETRC_FILE_MISSING; + char *login = *loginp; +- char *password = *passwordp; +- bool specific_login = (login && *login != 0); +- bool login_alloc = FALSE; +- bool password_alloc = FALSE; ++ char *password = NULL; ++ bool specific_login = login; /* points to something */ + enum host_lookup_state state = NOTHING; +- enum found_state found = NONE; +- bool our_login = TRUE; /* With specific_login, found *our* login name (or +- login-less line) */ ++ enum found_state keyword = NONE; ++ unsigned char found = 0; /* login + password found bits, as they can come in ++ any order */ ++ bool our_login = FALSE; /* found our login name */ + bool done = FALSE; + char *netrcbuffer; + struct dynbuf token; + struct dynbuf *filebuf = &store->filebuf; ++ DEBUGASSERT(!*passwordp); + Curl_dyn_init(&token, MAX_NETRC_TOKEN); + + if(!store->loaded) { +@@ -124,7 +127,7 @@ static int parsenetrc(struct store_netrc *store, + + while(!done) { + char *tok = netrcbuffer; +- while(tok) { ++ while(tok && !done) { + char *tok_end; + bool quoted; + Curl_dyn_reset(&token); +@@ -198,11 +201,6 @@ static int parsenetrc(struct store_netrc *store, + } + } + +- if((login && *login) && (password && *password)) { +- done = TRUE; +- break; +- } +- + tok = Curl_dyn_ptr(&token); + + switch(state) { +@@ -212,11 +210,18 @@ static int parsenetrc(struct store_netrc *store, + contents begin with the next .netrc line and continue until a + null line (consecutive new-line characters) is encountered. */ + state = MACDEF; +- else if(strcasecompare("machine", tok)) ++ else if(strcasecompare("machine", tok)) { + /* the next tok is the machine name, this is in itself the delimiter + that starts the stuff entered for this machine, after this we + need to search for 'login' and 'password'. */ + state = HOSTFOUND; ++ keyword = NONE; ++ found = 0; ++ our_login = FALSE; ++ Curl_safefree(password); ++ if(!specific_login) ++ Curl_safefree(login); ++ } + else if(strcasecompare("default", tok)) { + state = HOSTVALID; + retcode = NETRC_SUCCESS; /* we did find our host */ +@@ -238,44 +243,54 @@ static int parsenetrc(struct store_netrc *store, + break; + case HOSTVALID: + /* we are now parsing sub-keywords concerning "our" host */ +- if(found == LOGIN) { +- if(specific_login) { ++ if(keyword == LOGIN) { ++ if(specific_login) + our_login = !Curl_timestrcmp(login, tok); +- } +- else if(!login || Curl_timestrcmp(login, tok)) { +- if(login_alloc) +- free(login); ++ else { ++ our_login = TRUE; ++ free(login); + login = strdup(tok); + if(!login) { + retcode = NETRC_FAILED; /* allocation failed */ + goto out; + } +- login_alloc = TRUE; + } +- found = NONE; ++ found |= FOUND_LOGIN; ++ keyword = NONE; + } +- else if(found == PASSWORD) { +- if((our_login || !specific_login) && +- (!password || Curl_timestrcmp(password, tok))) { +- if(password_alloc) +- free(password); +- password = strdup(tok); +- if(!password) { +- retcode = NETRC_FAILED; /* allocation failed */ +- goto out; +- } +- password_alloc = TRUE; ++ else if(keyword == PASSWORD) { ++ free(password); ++ password = strdup(tok); ++ if(!password) { ++ retcode = NETRC_FAILED; /* allocation failed */ ++ goto out; + } +- found = NONE; ++ found |= FOUND_PASSWORD; ++ keyword = NONE; + } + else if(strcasecompare("login", tok)) +- found = LOGIN; ++ keyword = LOGIN; + else if(strcasecompare("password", tok)) +- found = PASSWORD; ++ keyword = PASSWORD; + else if(strcasecompare("machine", tok)) { +- /* ok, there is machine here go => */ ++ /* a new machine here */ + state = HOSTFOUND; +- found = NONE; ++ keyword = NONE; ++ found = 0; ++ Curl_safefree(password); ++ if(!specific_login) ++ Curl_safefree(login); ++ } ++ else if(strcasecompare("default", tok)) { ++ state = HOSTVALID; ++ retcode = NETRC_SUCCESS; /* we did find our host */ ++ Curl_safefree(password); ++ if(!specific_login) ++ Curl_safefree(login); ++ } ++ if((found == (FOUND_PASSWORD|FOUND_LOGIN)) && our_login) { ++ done = TRUE; ++ break; + } + break; + } /* switch (state) */ +@@ -294,23 +309,23 @@ static int parsenetrc(struct store_netrc *store, + + out: + Curl_dyn_free(&token); ++ if(!retcode && !password && our_login) { ++ /* success without a password, set a blank one */ ++ password = strdup(""); ++ if(!password) ++ retcode = 1; /* out of memory */ ++ } + if(!retcode) { + /* success */ +- if(login_alloc) { +- free(*loginp); ++ if(!specific_login) + *loginp = login; +- } +- if(password_alloc) { +- free(*passwordp); +- *passwordp = password; +- } ++ *passwordp = password; + } + else { + Curl_dyn_free(filebuf); +- if(login_alloc) ++ if(!specific_login) + free(login); +- if(password_alloc) +- free(password); ++ free(password); + } + + return retcode; +diff --git a/lib/url.c b/lib/url.c +index f9bb05f79..436edd891 100644 +--- a/lib/url.c ++++ b/lib/url.c +@@ -2651,6 +2651,17 @@ static CURLcode parse_remote_port(struct Curl_easy *data, + return CURLE_OK; + } + ++static bool str_has_ctrl(const char *input) ++{ ++ const unsigned char *str = (const unsigned char *)input; ++ while(*str) { ++ if(*str < 0x20) ++ return TRUE; ++ str++; ++ } ++ return FALSE; ++} ++ + /* + * Override the login details from the URL with that in the CURLOPT_USERPWD + * option or a .netrc file, if applicable. +@@ -2682,29 +2693,40 @@ static CURLcode override_login(struct Curl_easy *data, + + if(data->state.aptr.user && + (data->state.creds_from != CREDS_NETRC)) { +- /* there was a user name in the URL. Use the URL decoded version */ ++ /* there was a username with a length in the URL. Use the URL decoded ++ version */ + userp = &data->state.aptr.user; + url_provided = TRUE; + } + +- ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, +- userp, passwdp, +- data->set.str[STRING_NETRC_FILE]); +- if(ret > 0) { +- infof(data, "Couldn't find host %s in the %s file; using defaults", +- conn->host.name, +- (data->set.str[STRING_NETRC_FILE] ? +- data->set.str[STRING_NETRC_FILE] : ".netrc")); +- } +- else if(ret < 0) { +- failf(data, ".netrc parser error"); +- return CURLE_READ_ERROR; +- } +- else { +- /* set bits.netrc TRUE to remember that we got the name from a .netrc +- file, so that it is safe to use even if we followed a Location: to a +- different host or similar. */ +- conn->bits.netrc = TRUE; ++ if(!*passwdp) { ++ ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, ++ userp, passwdp, ++ data->set.str[STRING_NETRC_FILE]); ++ if(ret > 0) { ++ infof(data, "Couldn't find host %s in the %s file; using defaults", ++ conn->host.name, ++ (data->set.str[STRING_NETRC_FILE] ? ++ data->set.str[STRING_NETRC_FILE] : ".netrc")); ++ } ++ else if(ret < 0) { ++ failf(data, ".netrc parser error"); ++ return CURLE_READ_ERROR; ++ } ++ else { ++ if(!(conn->handler->flags&PROTOPT_USERPWDCTRL)) { ++ /* if the protocol can't handle control codes in credentials, make ++ sure there are none */ ++ if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) { ++ failf(data, "control code detected in .netrc credentials"); ++ return CURLE_READ_ERROR; ++ } ++ } ++ /* set bits.netrc TRUE to remember that we got the name from a .netrc ++ file, so that it is safe to use even if we followed a Location: to a ++ different host or similar. */ ++ conn->bits.netrc = TRUE; ++ } + } + if(url_provided) { + Curl_safefree(conn->user); +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index ea5221c00..53f62c6e2 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -77,6 +77,7 @@ test435 test436 test437 test438 test439 test440 test441 test442 test443 \ + test435 test436 test437 test438 test439 test440 test441 test442 test443 \ + test444 test445 test446 test447 test448 test449 test450 test451 test452 \ + test453 test454 test455 test456 test457 test458 \ ++test478 test479 test480 \ + \ + test490 test491 test492 test493 test494 test495 test496 test497 test498 \ + \ +diff --git a/tests/data/test478 b/tests/data/test478 +new file mode 100644 +index 000000000..6558363f5 +--- /dev/null ++++ b/tests/data/test478 +@@ -0,0 +1,73 @@ ++<testcase> ++<info> ++<keywords> ++netrc ++HTTP ++</keywords> ++</info> ++# ++# Server-side ++<reply> ++<data crlf="yes"> ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 6 ++Connection: close ++Content-Type: text/html ++Funny-head: yesyes ++ ++-foo- ++</data> ++</reply> ++ ++# ++# Client-side ++<client> ++<server> ++http ++</server> ++<features> ++proxy ++</features> ++<name> ++.netrc with multiple accounts for same host ++</name> ++<command> ++--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER -x http://%HOSTIP:%HTTPPORT/ http://debbie@github.com/ ++</command> ++<file name="%LOGDIR/netrc%TESTNUMBER" > ++ ++machine github.com ++password weird ++password firstone ++login daniel ++ ++machine github.com ++ ++machine github.com ++login debbie ++ ++machine github.com ++password weird ++password "second\r" ++login debbie ++ ++</file> ++</client> ++ ++<verify> ++<protocol> ++GET http://github.com/ HTTP/1.1
++Host: github.com
++Authorization: Basic %b64[debbie:second%0D]b64%
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++</protocol> ++</verify> ++</testcase> +diff --git a/tests/data/test479 b/tests/data/test479 +new file mode 100644 +index 000000000..d7ce4652f +--- /dev/null ++++ b/tests/data/test479 +@@ -0,0 +1,107 @@ ++<testcase> ++<info> ++<keywords> ++netrc ++HTTP ++</keywords> ++</info> ++# ++# Server-side ++<reply> ++<data crlf="yes"> ++HTTP/1.1 301 Follow this you fool ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 6 ++Connection: close ++Location: http://b.com/%TESTNUMBER0002 ++ ++-foo- ++</data> ++ ++<data2 crlf="yes"> ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 7 ++Connection: close ++ ++target ++</data2> ++ ++<datacheck crlf="yes"> ++HTTP/1.1 301 Follow this you fool ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 6 ++Connection: close ++Location: http://b.com/%TESTNUMBER0002 ++ ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 7 ++Connection: close ++ ++target ++</datacheck> ++</reply> ++ ++# ++# Client-side ++<client> ++<server> ++http ++</server> ++<features> ++proxy ++</features> ++<name> ++.netrc with redirect and default without password ++</name> ++<command> ++--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER -L -x http://%HOSTIP:%HTTPPORT/ http://a.com/ ++</command> ++<file name="%LOGDIR/netrc%TESTNUMBER" > ++ ++machine a.com ++ login alice ++ password alicespassword ++ ++default ++ login bob ++ ++</file> ++</client> ++ ++<verify> ++<protocol> ++GET http://a.com/ HTTP/1.1
++Host: a.com
++Authorization: Basic %b64[alice:alicespassword]b64%
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++GET http://b.com/%TESTNUMBER0002 HTTP/1.1
++Host: b.com
++Authorization: Basic %b64[bob:]b64%
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++</protocol> ++</verify> ++</testcase> +diff --git a/tests/data/test480 b/tests/data/test480 +new file mode 100644 +index 000000000..aab889f47 +--- /dev/null ++++ b/tests/data/test480 +@@ -0,0 +1,38 @@ ++<testcase> ++<info> ++<keywords> ++netrc ++pop3 ++</keywords> ++</info> ++# ++# Server-side ++<reply> ++ ++</reply> ++ ++# ++# Client-side ++<client> ++<server> ++pop3 ++</server> ++<name> ++Reject .netrc with credentials using CRLF for POP3 ++</name> ++<command> ++--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER pop3://%HOSTIP:%POP3PORT/%TESTNUMBER ++</command> ++<file name="%LOGDIR/netrc%TESTNUMBER" > ++machine %HOSTIP ++ login alice ++ password "password\r\ncommand" ++</file> ++</client> ++ ++<verify> ++<errorcode> ++26 ++</errorcode> ++</verify> ++</testcase> +diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c +index 238d3c0f7..817887b94 100644 +--- a/tests/unit/unit1304.c ++++ b/tests/unit/unit1304.c +@@ -32,13 +32,8 @@ static char *password; + + static CURLcode unit_setup(void) + { +- password = strdup(""); +- login = strdup(""); +- if(!password || !login) { +- Curl_safefree(password); +- Curl_safefree(login); +- return CURLE_OUT_OF_MEMORY; +- } ++ password = NULL; ++ login = NULL; + return CURLE_OK; + } + +@@ -60,89 +55,61 @@ UNITTEST_START + result = Curl_parsenetrc(&store, + "test.example.com", &login, &password, arg); + fail_unless(result == 1, "Host not found should return 1"); +- abort_unless(password != NULL, "returned NULL!"); +- fail_unless(password[0] == 0, "password should not have been changed"); +- abort_unless(login != NULL, "returned NULL!"); +- fail_unless(login[0] == 0, "login should not have been changed"); ++ abort_unless(password == NULL, "password did not return NULL!"); ++ abort_unless(login == NULL, "user did not return NULL!"); + Curl_netrc_cleanup(&store); + + /* + * Test a non existent login in our netrc file. + */ +- free(login); +- login = strdup("me"); +- abort_unless(login != NULL, "returned NULL!"); ++ login = (char *)"me"; + Curl_netrc_init(&store); + result = Curl_parsenetrc(&store, + "example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); +- abort_unless(password != NULL, "returned NULL!"); +- fail_unless(password[0] == 0, "password should not have been changed"); +- abort_unless(login != NULL, "returned NULL!"); +- fail_unless(strncmp(login, "me", 2) == 0, +- "login should not have been changed"); ++ abort_unless(password == NULL, "password is not NULL!"); + Curl_netrc_cleanup(&store); + + /* + * Test a non existent login and host in our netrc file. + */ +- free(login); +- login = strdup("me"); +- abort_unless(login != NULL, "returned NULL!"); ++ login = (char *)"me"; + Curl_netrc_init(&store); + result = Curl_parsenetrc(&store, + "test.example.com", &login, &password, arg); + fail_unless(result == 1, "Host not found should return 1"); +- abort_unless(password != NULL, "returned NULL!"); +- fail_unless(password[0] == 0, "password should not have been changed"); +- abort_unless(login != NULL, "returned NULL!"); +- fail_unless(strncmp(login, "me", 2) == 0, +- "login should not have been changed"); ++ abort_unless(password == NULL, "password is not NULL!"); + Curl_netrc_cleanup(&store); + + /* + * Test a non existent login (substring of an existing one) in our + * netrc file. + */ +- free(login); +- login = strdup("admi"); +- abort_unless(login != NULL, "returned NULL!"); ++ login = (char *)"admi"; + Curl_netrc_init(&store); + result = Curl_parsenetrc(&store, + "example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); +- abort_unless(password != NULL, "returned NULL!"); +- fail_unless(password[0] == 0, "password should not have been changed"); +- abort_unless(login != NULL, "returned NULL!"); +- fail_unless(strncmp(login, "admi", 4) == 0, +- "login should not have been changed"); ++ abort_unless(password == NULL, "password is not NULL!"); + Curl_netrc_cleanup(&store); + + /* + * Test a non existent login (superstring of an existing one) + * in our netrc file. + */ +- free(login); +- login = strdup("adminn"); +- abort_unless(login != NULL, "returned NULL!"); ++ login = (char *)"adminn"; + Curl_netrc_init(&store); + result = Curl_parsenetrc(&store, + "example.com", &login, &password, arg); + fail_unless(result == 0, "Host should have been found"); +- abort_unless(password != NULL, "returned NULL!"); +- fail_unless(password[0] == 0, "password should not have been changed"); +- abort_unless(login != NULL, "returned NULL!"); +- fail_unless(strncmp(login, "adminn", 6) == 0, +- "login should not have been changed"); ++ abort_unless(password == NULL, "password is not NULL!"); + Curl_netrc_cleanup(&store); + + /* + * Test for the first existing host in our netrc file + * with login[0] = 0. + */ +- free(login); +- login = strdup(""); +- abort_unless(login != NULL, "returned NULL!"); ++ login = NULL; + Curl_netrc_init(&store); + result = Curl_parsenetrc(&store, + "example.com", &login, &password, arg); +@@ -159,8 +126,9 @@ UNITTEST_START + * with login[0] != 0. + */ + free(password); +- password = strdup(""); +- abort_unless(password != NULL, "returned NULL!"); ++ free(login); ++ password = NULL; ++ login = NULL; + Curl_netrc_init(&store); + result = Curl_parsenetrc(&store, + "example.com", &login, &password, arg); +@@ -177,11 +145,9 @@ UNITTEST_START + * with login[0] = 0. + */ + free(password); +- password = strdup(""); +- abort_unless(password != NULL, "returned NULL!"); ++ password = NULL; + free(login); +- login = strdup(""); +- abort_unless(login != NULL, "returned NULL!"); ++ login = NULL; + Curl_netrc_init(&store); + result = Curl_parsenetrc(&store, + "curl.example.com", &login, &password, arg); +@@ -198,8 +164,9 @@ UNITTEST_START + * with login[0] != 0. + */ + free(password); +- password = strdup(""); +- abort_unless(password != NULL, "returned NULL!"); ++ free(login); ++ password = NULL; ++ login = NULL; + Curl_netrc_init(&store); + result = Curl_parsenetrc(&store, + "curl.example.com", &login, &password, arg); +-- +2.33.0 + diff --git a/backport-CVE-2024-2004.patch b/backport-CVE-2024-2004.patch new file mode 100644 index 0000000..b8d947b --- /dev/null +++ b/backport-CVE-2024-2004.patch @@ -0,0 +1,139 @@ +From 17d302e56221f5040092db77d4f85086e8a20e0e Mon Sep 17 00:00:00 2001 +From: Daniel Gustafsson <daniel@yesql.se> +Date: Tue, 27 Feb 2024 15:43:56 +0100 +Subject: [PATCH] setopt: Fix disabling all protocols + +When disabling all protocols without enabling any, the resulting +set of allowed protocols remained the default set. Clearing the +allowed set before inspecting the passed value from --proto make +the set empty even in the errorpath of no protocols enabled. + +Co-authored-by: Dan Fandrich <dan@telarity.com> +Reported-by: Dan Fandrich <dan@telarity.com> +Reviewed-by: Daniel Stenberg <daniel@haxx.se> +Closes: #13004 + +Conflict:Context adapt +Reference:https://github.com/curl/curl/commit/17d302e56221f5040092db77d4f85086e8a20e0e +--- + lib/setopt.c | 16 ++++++++-------- + tests/data/Makefile.inc | 2 +- + tests/data/test1474 | 42 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 51 insertions(+), 9 deletions(-) + create mode 100644 tests/data/test1474 + +diff --git a/lib/setopt.c b/lib/setopt.c +index 6a4990cce..ce1321fc8 100644 +--- a/lib/setopt.c ++++ b/lib/setopt.c +@@ -155,6 +155,12 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) + + static CURLcode protocol2num(const char *str, curl_prot_t *val) + { ++ /* ++ * We are asked to cherry-pick protocols, so play it safe and disallow all ++ * protocols to start with, and re-add the wanted ones back in. ++ */ ++ *val = 0; ++ + if(!str) + return CURLE_BAD_FUNCTION_ARGUMENT; + +@@ -163,8 +169,6 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) + return CURLE_OK; + } + +- *val = 0; +- + do { + const char *token = str; + size_t tlen; +@@ -2654,22 +2658,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) + break; + + case CURLOPT_PROTOCOLS_STR: { +- curl_prot_t prot; + argptr = va_arg(param, char *); +- result = protocol2num(argptr, &prot); ++ result = protocol2num(argptr, &data->set.allowed_protocols); + if(result) + return result; +- data->set.allowed_protocols = prot; + break; + } + + case CURLOPT_REDIR_PROTOCOLS_STR: { +- curl_prot_t prot; + argptr = va_arg(param, char *); +- result = protocol2num(argptr, &prot); ++ result = protocol2num(argptr, &data->set.redir_protocols); + if(result) + return result; +- data->set.redir_protocols = prot; + break; + } + +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index c20f90d94..b80ffb618 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -187,7 +187,7 @@ test1439 test1440 test1441 test1442 test1443 test1444 test1445 test1446 \ + test1447 test1448 test1449 test1450 test1451 test1452 test1453 test1454 \ + test1455 test1456 test1457 test1458 test1459 test1460 test1461 test1462 \ + test1463 test1464 test1465 test1466 test1467 test1468 test1469 test1470 \ +-test1471 test1472 test1473 \ ++test1471 test1472 test1473 test1474 \ + \ + test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \ + test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \ +diff --git a/tests/data/test1474 b/tests/data/test1474 +new file mode 100644 +index 000000000..c66fa2810 +--- /dev/null ++++ b/tests/data/test1474 +@@ -0,0 +1,42 @@ ++<testcase> ++<info> ++<keywords> ++HTTP ++HTTP GET ++--proto ++</keywords> ++</info> ++ ++# ++# Server-side ++<reply> ++<data> ++</data> ++</reply> ++ ++# ++# Client-side ++<client> ++<server> ++none ++</server> ++<features> ++http ++</features> ++<name> ++--proto -all disables all protocols ++</name> ++<command> ++--proto -all http://%HOSTIP:%NOLISTENPORT/%TESTNUMBER ++</command> ++</client> ++ ++# ++# Verify data after the test has been "shot" ++<verify> ++# 1 - Protocol "http" disabled ++<errorcode> ++1 ++</errorcode> ++</verify> ++</testcase> +-- +2.33.0 + diff --git a/backport-CVE-2024-2398.patch b/backport-CVE-2024-2398.patch new file mode 100644 index 0000000..c3128b1 --- /dev/null +++ b/backport-CVE-2024-2398.patch @@ -0,0 +1,96 @@ +From deca8039991886a559b67bcd6701db800a5cf764 Mon Sep 17 00:00:00 2001 +From: Stefan Eissing <stefan@eissing.org> +Date: Wed, 6 Mar 2024 09:36:08 +0100 +Subject: [PATCH] http2: push headers better cleanup + +- provide common cleanup method for push headers + +Closes #13054 + +Conflict:struct h2_stream_ctx *stream => struct stream_ctx *stream +Context adapt +Reference:https://github.com/curl/curl/commit/deca8039991886a559b67bcd6701db800a5cf764 +--- + lib/http2.c | 34 +++++++++++++++------------------- + 1 file changed, 15 insertions(+), 19 deletions(-) + +diff --git a/lib/http2.c b/lib/http2.c +index c63ecd383..96868728a 100644 +--- a/lib/http2.c ++++ b/lib/http2.c +@@ -271,6 +271,15 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, + return CURLE_OK; + } + ++static void free_push_headers(struct stream_ctx *stream) ++{ ++ size_t i; ++ for(i = 0; i<stream->push_headers_used; i++) ++ free(stream->push_headers[i]); ++ Curl_safefree(stream->push_headers); ++ stream->push_headers_used = 0; ++} ++ + static void http2_data_done(struct Curl_cfilter *cf, + struct Curl_easy *data, bool premature) + { +@@ -306,15 +315,7 @@ static void http2_data_done(struct Curl_cfilter *cf, + Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); + Curl_dynhds_free(&stream->resp_trailers); +- if(stream->push_headers) { +- /* if they weren't used and then freed before */ +- for(; stream->push_headers_used > 0; --stream->push_headers_used) { +- free(stream->push_headers[stream->push_headers_used - 1]); +- } +- free(stream->push_headers); +- stream->push_headers = NULL; +- } +- ++ free_push_headers(stream); + free(stream); + H2_STREAM_LCTX(data) = NULL; + } +@@ -860,7 +861,6 @@ static int push_promise(struct Curl_cfilter *cf, + struct curl_pushheaders heads; + CURLMcode rc; + CURLcode result; +- size_t i; + /* clone the parent */ + struct Curl_easy *newhandle = h2_duphandle(cf, data); + if(!newhandle) { +@@ -905,11 +905,7 @@ static int push_promise(struct Curl_cfilter *cf, + Curl_set_in_callback(data, false); + + /* free the headers again */ +- for(i = 0; i<stream->push_headers_used; i++) +- free(stream->push_headers[i]); +- free(stream->push_headers); +- stream->push_headers = NULL; +- stream->push_headers_used = 0; ++ free_push_headers(stream); + + if(rv) { + DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); +@@ -1430,14 +1426,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, + if(stream->push_headers_alloc > 1000) { + /* this is beyond crazy many headers, bail out */ + failf(data_s, "Too many PUSH_PROMISE headers"); +- Curl_safefree(stream->push_headers); ++ free_push_headers(stream); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + stream->push_headers_alloc *= 2; +- headp = Curl_saferealloc(stream->push_headers, +- stream->push_headers_alloc * sizeof(char *)); ++ headp = realloc(stream->push_headers, ++ stream->push_headers_alloc * sizeof(char *)); + if(!headp) { +- stream->push_headers = NULL; ++ free_push_headers(stream); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + stream->push_headers = headp; +-- +2.33.0 + diff --git a/backport-CVE-2024-7264-x509asn1-clean-up-GTime2str.patch b/backport-CVE-2024-7264-x509asn1-clean-up-GTime2str.patch new file mode 100644 index 0000000..4f3ef5d --- /dev/null +++ b/backport-CVE-2024-7264-x509asn1-clean-up-GTime2str.patch @@ -0,0 +1,60 @@ +From 3c914bc680155b32178f1f15ca8d47c7f4640afe Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Tue, 30 Jul 2024 10:05:17 +0200 +Subject: [PATCH] x509asn1: clean up GTime2str + +Co-authored-by: Stefan Eissing +Reported-by: Dov Murik + +Closes #14307 +--- + lib/vtls/x509asn1.c | 23 ++++++++++++++--------- + 1 file changed, 14 insertions(+), 9 deletions(-) + +diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c +index c3fd3a3..dd7985d 100644 +--- a/lib/vtls/x509asn1.c ++++ b/lib/vtls/x509asn1.c +@@ -537,7 +537,7 @@ static const char *GTime2str(const char *beg, const char *end) + /* Convert an ASN.1 Generalized time to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + +- for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++) ++ for(fracp = beg; fracp < end && ISDIGIT(*fracp); fracp++) + ; + + /* Get seconds digits. */ +@@ -556,17 +556,22 @@ static const char *GTime2str(const char *beg, const char *end) + return NULL; + } + +- /* Scan for timezone, measure fractional seconds. */ ++ /* timezone follows optional fractional seconds. */ + tzp = fracp; +- fracl = 0; ++ fracl = 0; /* no fractional seconds detected so far */ + if(fracp < end && (*fracp == '.' || *fracp == ',')) { +- fracp++; +- do ++ /* Have fractional seconds, e.g. "[.,]\d+". How many? */ ++ tzp = fracp++; /* should be a digit char or BAD ARGUMENT */ ++ while(tzp < end && ISDIGIT(*tzp)) + tzp++; +- while(tzp < end && *tzp >= '0' && *tzp <= '9'); +- /* Strip leading zeroes in fractional seconds. */ +- for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--) +- ; ++ if(tzp == fracp) /* never looped, no digit after [.,] */ ++ return CURLE_BAD_FUNCTION_ARGUMENT; ++ fracl = tzp - fracp - 1; /* number of fractional sec digits */ ++ DEBUGASSERT(fracl > 0); ++ /* Strip trailing zeroes in fractional seconds. ++ * May reduce fracl to 0 if only '0's are present. */ ++ while(fracl && fracp[fracl - 1] == '0') ++ fracl--; + } + + /* Process timezone. */ +-- +2.41.0 + diff --git a/backport-CVE-2024-7264-x509asn1-unittests-and-fixes-fo.patch b/backport-CVE-2024-7264-x509asn1-unittests-and-fixes-fo.patch new file mode 100644 index 0000000..f4949bc --- /dev/null +++ b/backport-CVE-2024-7264-x509asn1-unittests-and-fixes-fo.patch @@ -0,0 +1,315 @@ +From 27959ecce75cdb2809c0bdb3286e60e08fadb519 Mon Sep 17 00:00:00 2001 +From: Stefan Eissing <stefan@eissing.org> +Date: Tue, 30 Jul 2024 16:40:48 +0200 +Subject: [PATCH] x509asn1: unittests and fixes for gtime2str + +Fix issues in GTime2str() and add unit test cases to verify correct +behaviour. + +Follow-up to 3c914bc6801 + +Closes #14316 +--- + lib/vtls/x509asn1.c | 32 +++++++--- + lib/vtls/x509asn1.h | 11 ++++ + tests/data/Makefile.inc | 2 +- + tests/data/test1656 | 22 +++++++ + tests/unit/Makefile.inc | 4 +- + tests/unit/unit1656.c | 133 ++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 194 insertions(+), 10 deletions(-) + create mode 100644 tests/data/test1656 + create mode 100644 tests/unit/unit1656.c + +diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c +index dd7985d..5c65df1 100644 +--- a/lib/vtls/x509asn1.c ++++ b/lib/vtls/x509asn1.c +@@ -561,12 +561,13 @@ static const char *GTime2str(const char *beg, const char *end) + fracl = 0; /* no fractional seconds detected so far */ + if(fracp < end && (*fracp == '.' || *fracp == ',')) { + /* Have fractional seconds, e.g. "[.,]\d+". How many? */ +- tzp = fracp++; /* should be a digit char or BAD ARGUMENT */ ++ fracp++; /* should be a digit char or BAD ARGUMENT */ ++ tzp = fracp; + while(tzp < end && ISDIGIT(*tzp)) + tzp++; + if(tzp == fracp) /* never looped, no digit after [.,] */ + return CURLE_BAD_FUNCTION_ARGUMENT; +- fracl = tzp - fracp - 1; /* number of fractional sec digits */ ++ fracl = tzp - fracp; /* number of fractional sec digits */ + DEBUGASSERT(fracl > 0); + /* Strip trailing zeroes in fractional seconds. + * May reduce fracl to 0 if only '0's are present. */ +@@ -575,18 +576,24 @@ static const char *GTime2str(const char *beg, const char *end) + } + + /* Process timezone. */ +- if(tzp >= end) +- ; /* Nothing to do. */ ++ if(tzp >= end) { ++ tzp = ""; ++ tzl = 0; ++ } + else if(*tzp == 'Z') { +- tzp = " GMT"; +- end = tzp + 4; ++ sep = " "; ++ tzp = "GMT"; ++ tzl = 3; ++ } ++ else if((*tzp == '+') || (*tzp == '-')) { ++ sep = " UTC"; ++ tzl = end - tzp; + } + else { + sep = " "; +- tzp++; ++ tzl = end - tzp; + } + +- tzl = end - tzp; + return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", + beg, beg + 4, beg + 6, + beg + 8, beg + 10, sec1, sec2, +@@ -594,6 +601,15 @@ static const char *GTime2str(const char *beg, const char *end) + sep, (int)tzl, tzp); + } + ++#ifdef UNITTESTS ++/* used by unit1656.c */ ++CURLcode Curl_x509_GTime2str(struct dynbuf *store, ++ const char *beg, const char *end) ++{ ++ return GTime2str(store, beg, end); ++} ++#endif ++ + /* + * Convert an ASN.1 UTC time to a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. +diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h +index 23a67b8..1d8bbab 100644 +--- a/lib/vtls/x509asn1.h ++++ b/lib/vtls/x509asn1.h +@@ -76,5 +76,16 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, + const char *beg, const char *end); + CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, + const char *beg, const char *end); ++ ++#ifdef UNITTESTS ++#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ ++ defined(USE_MBEDTLS) ++ ++/* used by unit1656.c */ ++CURLcode Curl_x509_GTime2str(struct dynbuf *store, ++ const char *beg, const char *end); ++#endif ++#endif ++ + #endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ + #endif /* HEADER_CURL_X509ASN1_H */ +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index 1472b19..0af94e6 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -207,7 +207,7 @@ test1620 test1621 \ + \ + test1630 test1631 test1632 test1633 test1634 test1635 \ + \ +-test1650 test1651 test1652 test1653 test1654 test1655 \ ++test1650 test1651 test1652 test1653 test1654 test1655 test1656 \ + test1660 test1661 test1662 \ + \ + test1670 test1671 \ +diff --git a/tests/data/test1656 b/tests/data/test1656 +new file mode 100644 +index 0000000..2fab21b +--- /dev/null ++++ b/tests/data/test1656 +@@ -0,0 +1,22 @@ ++<testcase> ++<info> ++<keywords> ++unittest ++Curl_x509_GTime2str ++</keywords> ++</info> ++ ++# ++# Client-side ++<client> ++<server> ++none ++</server> ++<features> ++unittest ++</features> ++<name> ++Curl_x509_GTime2str unit tests ++</name> ++</client> ++</testcase> +diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc +index 36e922b..b0eaf64 100644 +--- a/tests/unit/Makefile.inc ++++ b/tests/unit/Makefile.inc +@@ -36,7 +36,7 @@ UNITPROGS = unit1300 unit1302 unit1303 unit1304 unit1305 unit1307 \ + unit1600 unit1601 unit1602 unit1603 unit1604 unit1605 unit1606 unit1607 \ + unit1608 unit1609 unit1610 unit1611 unit1612 unit1614 \ + unit1620 unit1621 \ +- unit1650 unit1651 unit1652 unit1653 unit1654 unit1655 \ ++ unit1650 unit1651 unit1652 unit1653 unit1654 unit1655 unit1656 \ + unit1660 unit1661 \ + unit2600 unit2601 unit2602 unit2603 \ + unit3200 +@@ -117,6 +117,8 @@ unit1654_SOURCES = unit1654.c $(UNITFILES) + + unit1655_SOURCES = unit1655.c $(UNITFILES) + ++unit1656_SOURCES = unit1656.c $(UNITFILES) ++ + unit1660_SOURCES = unit1660.c $(UNITFILES) + + unit1661_SOURCES = unit1661.c $(UNITFILES) +diff --git a/tests/unit/unit1656.c b/tests/unit/unit1656.c +new file mode 100644 +index 0000000..644e72f +--- /dev/null ++++ b/tests/unit/unit1656.c +@@ -0,0 +1,133 @@ ++/*************************************************************************** ++ * _ _ ____ _ ++ * Project ___| | | | _ \| | ++ * / __| | | | |_) | | ++ * | (__| |_| | _ <| |___ ++ * \___|\___/|_| \_\_____| ++ * ++ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. ++ * ++ * This software is licensed as described in the file COPYING, which ++ * you should have received as part of this distribution. The terms ++ * are also available at https://curl.se/docs/copyright.html. ++ * ++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell ++ * copies of the Software, and permit persons to whom the Software is ++ * furnished to do so, under the terms of the COPYING file. ++ * ++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY ++ * KIND, either express or implied. ++ * ++ * SPDX-License-Identifier: curl ++ * ++ ***************************************************************************/ ++#include "curlcheck.h" ++ ++#include "vtls/x509asn1.h" ++ ++static CURLcode unit_setup(void) ++{ ++ return CURLE_OK; ++} ++ ++static void unit_stop(void) ++{ ++ ++} ++ ++#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ ++ defined(USE_MBEDTLS) ++ ++#ifndef ARRAYSIZE ++#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) ++#endif ++ ++struct test_spec { ++ const char *input; ++ const char *exp_output; ++ CURLcode exp_result; ++}; ++ ++static struct test_spec test_specs[] = { ++ { "190321134340", "1903-21-13 43:40:00", CURLE_OK }, ++ { "", NULL, CURLE_BAD_FUNCTION_ARGUMENT }, ++ { "WTF", NULL, CURLE_BAD_FUNCTION_ARGUMENT }, ++ { "0WTF", NULL, CURLE_BAD_FUNCTION_ARGUMENT }, ++ { "19032113434", NULL, CURLE_BAD_FUNCTION_ARGUMENT }, ++ { "19032113434WTF", NULL, CURLE_BAD_FUNCTION_ARGUMENT }, ++ { "190321134340.", NULL, CURLE_BAD_FUNCTION_ARGUMENT }, ++ { "190321134340.1", "1903-21-13 43:40:00.1", CURLE_OK }, ++ { "19032113434017.0", "1903-21-13 43:40:17", CURLE_OK }, ++ { "19032113434017.01", "1903-21-13 43:40:17.01", CURLE_OK }, ++ { "19032113434003.001", "1903-21-13 43:40:03.001", CURLE_OK }, ++ { "19032113434003.090", "1903-21-13 43:40:03.09", CURLE_OK }, ++ { "190321134340Z", "1903-21-13 43:40:00 GMT", CURLE_OK }, ++ { "19032113434017.0Z", "1903-21-13 43:40:17 GMT", CURLE_OK }, ++ { "19032113434017.01Z", "1903-21-13 43:40:17.01 GMT", CURLE_OK }, ++ { "19032113434003.001Z", "1903-21-13 43:40:03.001 GMT", CURLE_OK }, ++ { "19032113434003.090Z", "1903-21-13 43:40:03.09 GMT", CURLE_OK }, ++ { "190321134340CET", "1903-21-13 43:40:00 CET", CURLE_OK }, ++ { "19032113434017.0CET", "1903-21-13 43:40:17 CET", CURLE_OK }, ++ { "19032113434017.01CET", "1903-21-13 43:40:17.01 CET", CURLE_OK }, ++ { "190321134340+02:30", "1903-21-13 43:40:00 UTC+02:30", CURLE_OK }, ++ { "19032113434017.0+02:30", "1903-21-13 43:40:17 UTC+02:30", CURLE_OK }, ++ { "19032113434017.01+02:30", "1903-21-13 43:40:17.01 UTC+02:30", CURLE_OK }, ++ { "190321134340-3", "1903-21-13 43:40:00 UTC-3", CURLE_OK }, ++ { "19032113434017.0-04", "1903-21-13 43:40:17 UTC-04", CURLE_OK }, ++ { "19032113434017.01-01:10", "1903-21-13 43:40:17.01 UTC-01:10", CURLE_OK }, ++}; ++ ++static bool do_test(struct test_spec *spec, size_t i, struct dynbuf *dbuf) ++{ ++ CURLcode result; ++ const char *in = spec->input; ++ ++ Curl_dyn_reset(dbuf); ++ result = Curl_x509_GTime2str(dbuf, in, in + strlen(in)); ++ if(result != spec->exp_result) { ++ fprintf(stderr, "test %zu: expect result %d, got %d\n", ++ i, spec->exp_result, result); ++ return FALSE; ++ } ++ else if(!result && strcmp(spec->exp_output, Curl_dyn_ptr(dbuf))) { ++ fprintf(stderr, "test %zu: input '%s', expected output '%s', got '%s'\n", ++ i, in, spec->exp_output, Curl_dyn_ptr(dbuf)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++UNITTEST_START ++{ ++ size_t i; ++ struct dynbuf dbuf; ++ bool all_ok = TRUE; ++ ++ Curl_dyn_init(&dbuf, 32*1024); ++ ++ if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { ++ fprintf(stderr, "curl_global_init() failed\n"); ++ return TEST_ERR_MAJOR_BAD; ++ } ++ ++ for(i = 0; i < ARRAYSIZE(test_specs); ++i) { ++ if(!do_test(&test_specs[i], i, &dbuf)) ++ all_ok = FALSE; ++ } ++ fail_unless(all_ok, "some tests of Curl_x509_GTime2str() fails"); ++ ++ Curl_dyn_free(&dbuf); ++ curl_global_cleanup(); ++} ++UNITTEST_STOP ++ ++#else ++ ++UNITTEST_START ++{ ++ puts("not tested since Curl_x509_GTime2str() is not built-in"); ++} ++UNITTEST_STOP ++ ++#endif +-- +2.41.0 + diff --git a/backport-CVE-2024-8096-gtls-fix-OCSP-stapling-management.patch b/backport-CVE-2024-8096-gtls-fix-OCSP-stapling-management.patch new file mode 100644 index 0000000..462971d --- /dev/null +++ b/backport-CVE-2024-8096-gtls-fix-OCSP-stapling-management.patch @@ -0,0 +1,206 @@ +From aeb1a281cab13c7ba791cb104e556b20e713941f Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Tue, 20 Aug 2024 16:14:39 +0200 +Subject: [PATCH] gtls: fix OCSP stapling management + +Reported-by: Hiroki Kurosawa +Closes #14642 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/aeb1a281cab13c7ba791cb104e556b20e713941f +--- + lib/vtls/gtls.c | 146 ++++++++++++++++++++++++------------------------ + 1 file changed, 73 insertions(+), 73 deletions(-) + +diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c +index 03d6fcc03..c7589d9d3 100644 +--- a/lib/vtls/gtls.c ++++ b/lib/vtls/gtls.c +@@ -850,6 +850,13 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, + init_flags |= GNUTLS_NO_TICKETS; + #endif + ++#if defined(GNUTLS_NO_STATUS_REQUEST) ++ if(!config->verifystatus) ++ /* Disable the "status_request" TLS extension, enabled by default since ++ GnuTLS 3.8.0. */ ++ init_flags |= GNUTLS_NO_STATUS_REQUEST; ++#endif ++ + rc = gnutls_init(>ls->session, init_flags); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_init() failed: %d", rc); +@@ -1321,104 +1328,97 @@ Curl_gtls_verifyserver(struct Curl_easy *data, + infof(data, " server certificate verification SKIPPED"); + + if(config->verifystatus) { +- if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) { +- gnutls_datum_t status_request; +- gnutls_ocsp_resp_t ocsp_resp; ++ gnutls_datum_t status_request; ++ gnutls_ocsp_resp_t ocsp_resp; ++ gnutls_ocsp_cert_status_t status; ++ gnutls_x509_crl_reason_t reason; + +- gnutls_ocsp_cert_status_t status; +- gnutls_x509_crl_reason_t reason; ++ rc = gnutls_ocsp_status_request_get(session, &status_request); + +- rc = gnutls_ocsp_status_request_get(session, &status_request); ++ if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { ++ failf(data, "No OCSP response received"); ++ return CURLE_SSL_INVALIDCERTSTATUS; ++ } + +- infof(data, " server certificate status verification FAILED"); ++ if(rc < 0) { ++ failf(data, "Invalid OCSP response received"); ++ return CURLE_SSL_INVALIDCERTSTATUS; ++ } + +- if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { +- failf(data, "No OCSP response received"); +- return CURLE_SSL_INVALIDCERTSTATUS; +- } ++ gnutls_ocsp_resp_init(&ocsp_resp); + +- if(rc < 0) { +- failf(data, "Invalid OCSP response received"); +- return CURLE_SSL_INVALIDCERTSTATUS; +- } ++ rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); ++ if(rc < 0) { ++ failf(data, "Invalid OCSP response received"); ++ return CURLE_SSL_INVALIDCERTSTATUS; ++ } + +- gnutls_ocsp_resp_init(&ocsp_resp); ++ (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, ++ &status, NULL, NULL, NULL, &reason); + +- rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); +- if(rc < 0) { +- failf(data, "Invalid OCSP response received"); +- return CURLE_SSL_INVALIDCERTSTATUS; +- } ++ switch(status) { ++ case GNUTLS_OCSP_CERT_GOOD: ++ break; + +- (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, +- &status, NULL, NULL, NULL, &reason); ++ case GNUTLS_OCSP_CERT_REVOKED: { ++ const char *crl_reason; + +- switch(status) { +- case GNUTLS_OCSP_CERT_GOOD: ++ switch(reason) { ++ default: ++ case GNUTLS_X509_CRLREASON_UNSPECIFIED: ++ crl_reason = "unspecified reason"; + break; + +- case GNUTLS_OCSP_CERT_REVOKED: { +- const char *crl_reason; +- +- switch(reason) { +- default: +- case GNUTLS_X509_CRLREASON_UNSPECIFIED: +- crl_reason = "unspecified reason"; +- break; +- +- case GNUTLS_X509_CRLREASON_KEYCOMPROMISE: +- crl_reason = "private key compromised"; +- break; +- +- case GNUTLS_X509_CRLREASON_CACOMPROMISE: +- crl_reason = "CA compromised"; +- break; +- +- case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: +- crl_reason = "affiliation has changed"; +- break; ++ case GNUTLS_X509_CRLREASON_KEYCOMPROMISE: ++ crl_reason = "private key compromised"; ++ break; + +- case GNUTLS_X509_CRLREASON_SUPERSEDED: +- crl_reason = "certificate superseded"; +- break; ++ case GNUTLS_X509_CRLREASON_CACOMPROMISE: ++ crl_reason = "CA compromised"; ++ break; + +- case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: +- crl_reason = "operation has ceased"; +- break; ++ case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: ++ crl_reason = "affiliation has changed"; ++ break; + +- case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: +- crl_reason = "certificate is on hold"; +- break; ++ case GNUTLS_X509_CRLREASON_SUPERSEDED: ++ crl_reason = "certificate superseded"; ++ break; + +- case GNUTLS_X509_CRLREASON_REMOVEFROMCRL: +- crl_reason = "will be removed from delta CRL"; +- break; ++ case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: ++ crl_reason = "operation has ceased"; ++ break; + +- case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: +- crl_reason = "privilege withdrawn"; +- break; ++ case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: ++ crl_reason = "certificate is on hold"; ++ break; + +- case GNUTLS_X509_CRLREASON_AACOMPROMISE: +- crl_reason = "AA compromised"; +- break; +- } ++ case GNUTLS_X509_CRLREASON_REMOVEFROMCRL: ++ crl_reason = "will be removed from delta CRL"; ++ break; + +- failf(data, "Server certificate was revoked: %s", crl_reason); ++ case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: ++ crl_reason = "privilege withdrawn"; + break; +- } + +- default: +- case GNUTLS_OCSP_CERT_UNKNOWN: +- failf(data, "Server certificate status is unknown"); ++ case GNUTLS_X509_CRLREASON_AACOMPROMISE: ++ crl_reason = "AA compromised"; + break; + } + +- gnutls_ocsp_resp_deinit(ocsp_resp); ++ failf(data, "Server certificate was revoked: %s", crl_reason); ++ break; ++ } + +- return CURLE_SSL_INVALIDCERTSTATUS; ++ default: ++ case GNUTLS_OCSP_CERT_UNKNOWN: ++ failf(data, "Server certificate status is unknown"); ++ break; + } +- else +- infof(data, " server certificate status verification OK"); ++ ++ gnutls_ocsp_resp_deinit(ocsp_resp); ++ if(status != GNUTLS_OCSP_CERT_GOOD) ++ return CURLE_SSL_INVALIDCERTSTATUS; + } + else + infof(data, " server certificate status verification SKIPPED"); +-- +2.33.0 + diff --git a/backport-CVE-2024-9681.patch b/backport-CVE-2024-9681.patch new file mode 100644 index 0000000..6b5abae --- /dev/null +++ b/backport-CVE-2024-9681.patch @@ -0,0 +1,82 @@ +From a94973805df96269bf3f3bf0a20ccb9887313316 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Wed, 9 Oct 2024 10:04:35 +0200 +Subject: [PATCH] hsts: improve subdomain handling + +- on load, only replace existing HSTS entries if there is a full host + match + +- on matching, prefer a full host match and secondary the longest tail + subdomain match + +Closes #15210 +Conflict:Context adapt +Reference:https://github.com/curl/curl/commit/a94973805df96269bf3f3bf0a20ccb9887313316 +--- + lib/hsts.c | 14 ++++++++++---- + tests/data/test1660 | 2 +- + 2 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/lib/hsts.c b/lib/hsts.c +index d5e883f51ef0f7..12052ce53c1c5a 100644 +--- a/lib/hsts.c ++++ b/lib/hsts.c +@@ -249,11 +249,13 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, + struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, + bool subdomain) + { ++ struct stsentry *bestsub = NULL; + if(h) { + time_t now = time(NULL); + size_t hlen = strlen(hostname); + struct Curl_llist_element *e; + struct Curl_llist_element *n; ++ size_t blen = 0; + + if((hlen > MAX_HSTS_HOSTLEN) || !hlen) + return NULL; +@@ -275,15 +277,19 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, + if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { + size_t offs = hlen - ntail; + if((hostname[offs-1] == '.') && +- strncasecompare(&hostname[offs], sts->host, ntail)) +- return sts; ++ strncasecompare(&hostname[offs], sts->host, ntail) && ++ (ntail > blen)) { ++ /* save the tail match with the longest tail */ ++ bestsub = sts; ++ blen = ntail; ++ } + } + /* avoid strcasecompare because the host name is not null terminated */ + if((hlen == ntail) && strncasecompare(hostname, sts->host, hlen)) + return sts; + } + } +- return NULL; /* no match */ ++ return bestsub; + } + + /* +@@ -435,7 +441,7 @@ static CURLcode hsts_add(struct hsts *h, char *line) + e = Curl_hsts(h, p, subdomain); + if(!e) + result = hsts_create(h, p, subdomain, expires); +- else { ++ else if(strcasecompare(p, e->host)) { + /* the same host name, use the largest expire time */ + if(expires > e->expires) + e->expires = expires; +diff --git a/tests/data/test1660 b/tests/data/test1660 +index f86126d19cf269..4b6f9615c9d517 100644 +--- a/tests/data/test1660 ++++ b/tests/data/test1660 +@@ -52,7 +52,7 @@ this.example [this.example]: 1548400797 + Input 12: error 43 + Input 13: error 43 + Input 14: error 43 +-3.example.com [example.com]: 1569905261 includeSubDomains ++3.example.com [3.example.com]: 1569905261 includeSubDomains + 3.example.com [example.com]: 1569905261 includeSubDomains + foo.example.com [example.com]: 1569905261 includeSubDomains + 'foo.xample.com' is not HSTS diff --git a/backport-CVE-2025-0167.patch b/backport-CVE-2025-0167.patch new file mode 100644 index 0000000..4b33584 --- /dev/null +++ b/backport-CVE-2025-0167.patch @@ -0,0 +1,170 @@ +From 0e120c5b925e8ca75d5319e319e5ce4b8080d8eb Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Fri, 3 Jan 2025 16:22:27 +0100 +Subject: [PATCH] netrc: 'default' with no credentials is not a match + +Test 486 verifies. + +Reported-by: Yihang Zhou + +Closes #15908 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/0e120c5b925e8ca75d5319e +--- + lib/netrc.c | 15 ++++-- + tests/data/Makefile.inc | 2 +- + tests/data/test486 | 105 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 116 insertions(+), 6 deletions(-) + create mode 100644 tests/data/test486 + +diff --git a/lib/netrc.c b/lib/netrc.c +index b517c1dfab67..7ad81ece229f 100644 +--- a/lib/netrc.c ++++ b/lib/netrc.c +@@ -316,11 +316,16 @@ static int parsenetrc(struct store_netrc *store, + + out: + Curl_dyn_free(&token); +- if(!retcode && !password && our_login) { +- /* success without a password, set a blank one */ +- password = strdup(""); +- if(!password) +- retcode = 1; /* out of memory */ ++ if(!retcode) { ++ if(!password && our_login) { ++ /* success without a password, set a blank one */ ++ password = strdup(""); ++ if(!password) ++ retcode = 1; /* out of memory */ ++ } ++ else if(!login && !password) ++ /* a default with no credentials */ ++ retcode = NETRC_FILE_MISSING; + } + if(!retcode) { + /* success */ +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index 9ec101a7c74b..fc5e4cef5668 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -78,7 +78,7 @@ test426 test427 test428 test429 test430 test431 test432 test433 test434 \ + test435 test436 test437 test438 test439 test440 test441 test442 test443 \ + test444 test445 test446 test447 test448 test449 test450 test451 test452 \ + test453 test454 test455 test456 test457 test458 \ +-test478 test479 test480 \ ++test478 test479 test480 test486 \ + \ + test490 test491 test492 test493 test494 test495 test496 test497 test498 \ + \ +diff --git a/tests/data/test486 b/tests/data/test486 +new file mode 100644 +index 000000000000..53efae597a1b +--- /dev/null ++++ b/tests/data/test486 +@@ -0,0 +1,105 @@ ++<testcase> ++<info> ++<keywords> ++netrc ++HTTP ++</keywords> ++</info> ++# ++# Server-side ++<reply> ++<data crlf="yes"> ++HTTP/1.1 301 Follow this you fool ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 6 ++Connection: close ++Location: http://b.com/%TESTNUMBER0002 ++ ++-foo- ++</data> ++ ++<data2 crlf="yes"> ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 7 ++Connection: close ++ ++target ++</data2> ++ ++<datacheck crlf="yes"> ++HTTP/1.1 301 Follow this you fool ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 6 ++Connection: close ++Location: http://b.com/%TESTNUMBER0002 ++ ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT ++ETag: "21025-dc7-39462498" ++Accept-Ranges: bytes ++Content-Length: 7 ++Connection: close ++ ++target ++</datacheck> ++</reply> ++ ++# ++# Client-side ++<client> ++<server> ++http ++</server> ++<features> ++proxy ++</features> ++<name> ++.netrc with redirect and "default" with no password or login ++</name> ++<command> ++--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER -L -x http://%HOSTIP:%HTTPPORT/ http://a.com/ ++</command> ++<file name="%LOGDIR/netrc%TESTNUMBER" > ++ ++machine a.com ++ login alice ++ password alicespassword ++ ++default ++ ++</file> ++</client> ++ ++<verify> ++<protocol> ++GET http://a.com/ HTTP/1.1
++Host: a.com
++Authorization: Basic %b64[alice:alicespassword]b64%
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++GET http://b.com/%TESTNUMBER0002 HTTP/1.1
++Host: b.com
++User-Agent: curl/%VERSION
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++</protocol> ++</verify> ++</testcase> diff --git a/backport-CVE-2025-0725.patch b/backport-CVE-2025-0725.patch new file mode 100644 index 0000000..1d36d08 --- /dev/null +++ b/backport-CVE-2025-0725.patch @@ -0,0 +1,324 @@ +From 76f83f0db23846e254d940ec7fe141010077eb88 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Fri, 24 Jan 2025 11:13:24 +0100 +Subject: [PATCH] content_encoding: drop support for zlib before 1.2.0.4 + +zlib 1.2.0.4 was released on 10 August 2003 + +Closes #16079 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/76f83f0db23846e254d940ec7 +--- + docs/INTERNALS.md | 2 +- + lib/content_encoding.c | 232 ++--------------------------------------- + 2 files changed, 8 insertions(+), 226 deletions(-) + +diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md +index d7513a8..f8aba15 100644 +--- a/docs/INTERNALS.md ++++ b/docs/INTERNALS.md +@@ -20,7 +20,7 @@ versions of libs and build tools. + + - OpenSSL 0.9.7 + - GnuTLS 3.1.10 +- - zlib 1.1.4 ++ - zlib 1.2.0.4 + - libssh2 1.0 + - c-ares 1.16.0 + - libidn2 2.0.0 +diff --git a/lib/content_encoding.c b/lib/content_encoding.c +index be7c075..7fc418e 100644 +--- a/lib/content_encoding.c ++++ b/lib/content_encoding.c +@@ -65,31 +65,13 @@ + + #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ + +- + #ifdef HAVE_LIBZ + +-/* Comment this out if zlib is always going to be at least ver. 1.2.0.4 +- (doing so will reduce code size slightly). */ +-#define OLD_ZLIB_SUPPORT 1 +- +-#define GZIP_MAGIC_0 0x1f +-#define GZIP_MAGIC_1 0x8b +- +-/* gzip flag byte */ +-#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +-#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +-#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +-#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +-#define COMMENT 0x10 /* bit 4 set: file comment present */ +-#define RESERVED 0xE0 /* bits 5..7: reserved */ +- + typedef enum { + ZLIB_UNINIT, /* uninitialized */ + ZLIB_INIT, /* initialized */ + ZLIB_INFLATING, /* inflating started. */ + ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ +- ZLIB_GZIP_HEADER, /* reading gzip header */ +- ZLIB_GZIP_INFLATING, /* inflating gzip stream */ + ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ + } zlibInitState; + +@@ -134,9 +116,6 @@ static CURLcode + exit_zlib(struct Curl_easy *data, + z_stream *z, zlibInitState *zlib_init, CURLcode result) + { +- if(*zlib_init == ZLIB_GZIP_HEADER) +- Curl_safefree(z->next_in); +- + if(*zlib_init != ZLIB_UNINIT) { + if(inflateEnd(z) != Z_OK && result == CURLE_OK) + result = process_zlib_error(data, z); +@@ -185,8 +164,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, + /* Check state. */ + if(zp->zlib_init != ZLIB_INIT && + zp->zlib_init != ZLIB_INFLATING && +- zp->zlib_init != ZLIB_INIT_GZIP && +- zp->zlib_init != ZLIB_GZIP_INFLATING) ++ zp->zlib_init != ZLIB_INIT_GZIP) + return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); + + /* Dynamically allocate a buffer for decompression because it's uncommonly +@@ -333,114 +311,27 @@ static CURLcode gzip_init_writer(struct Curl_easy *data, + { + struct zlib_writer *zp = (struct zlib_writer *) writer; + z_stream *z = &zp->z; /* zlib state structure */ ++ const char *v = zlibVersion(); + + /* Initialize zlib */ + z->zalloc = (alloc_func) zalloc_cb; + z->zfree = (free_func) zfree_cb; + +- if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { +- /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ ++ if(strcmp(v, "1.2.0.4") >= 0) { ++ /* zlib version >= 1.2.0.4 supports transparent gzip decompressing */ + if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { + return process_zlib_error(data, z); + } + zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ + } + else { +- /* we must parse the gzip header and trailer ourselves */ +- if(inflateInit2(z, -MAX_WBITS) != Z_OK) { +- return process_zlib_error(data, z); +- } +- zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ +- zp->zlib_init = ZLIB_INIT; /* Initial call state */ ++ failf(data, "too old zlib version: %s", v); ++ return CURLE_FAILED_INIT; + } + + return CURLE_OK; + } + +-#ifdef OLD_ZLIB_SUPPORT +-/* Skip over the gzip header */ +-static enum { +- GZIP_OK, +- GZIP_BAD, +- GZIP_UNDERFLOW +-} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen) +-{ +- int method, flags; +- const ssize_t totallen = len; +- +- /* The shortest header is 10 bytes */ +- if(len < 10) +- return GZIP_UNDERFLOW; +- +- if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) +- return GZIP_BAD; +- +- method = data[2]; +- flags = data[3]; +- +- if(method != Z_DEFLATED || (flags & RESERVED) != 0) { +- /* Can't handle this compression method or unknown flag */ +- return GZIP_BAD; +- } +- +- /* Skip over time, xflags, OS code and all previous bytes */ +- len -= 10; +- data += 10; +- +- if(flags & EXTRA_FIELD) { +- ssize_t extra_len; +- +- if(len < 2) +- return GZIP_UNDERFLOW; +- +- extra_len = (data[1] << 8) | data[0]; +- +- if(len < (extra_len + 2)) +- return GZIP_UNDERFLOW; +- +- len -= (extra_len + 2); +- data += (extra_len + 2); +- } +- +- if(flags & ORIG_NAME) { +- /* Skip over NUL-terminated file name */ +- while(len && *data) { +- --len; +- ++data; +- } +- if(!len || *data) +- return GZIP_UNDERFLOW; +- +- /* Skip over the NUL */ +- --len; +- ++data; +- } +- +- if(flags & COMMENT) { +- /* Skip over NUL-terminated comment */ +- while(len && *data) { +- --len; +- ++data; +- } +- if(!len || *data) +- return GZIP_UNDERFLOW; +- +- /* Skip over the NUL */ +- --len; +- } +- +- if(flags & HEAD_CRC) { +- if(len < 2) +- return GZIP_UNDERFLOW; +- +- len -= 2; +- } +- +- *headerlen = totallen - len; +- return GZIP_OK; +-} +-#endif +- + static CURLcode gzip_unencode_write(struct Curl_easy *data, + struct contenc_writer *writer, + const char *buf, size_t nbytes) +@@ -456,117 +347,8 @@ static CURLcode gzip_unencode_write(struct Curl_easy *data, + return inflate_stream(data, writer, ZLIB_INIT_GZIP); + } + +-#ifndef OLD_ZLIB_SUPPORT +- /* Support for old zlib versions is compiled away and we are running with +- an old version, so return an error. */ ++ /* We are running with an old version: return error. */ + return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); +- +-#else +- /* This next mess is to get around the potential case where there isn't +- * enough data passed in to skip over the gzip header. If that happens, we +- * malloc a block and copy what we have then wait for the next call. If +- * there still isn't enough (this is definitely a worst-case scenario), we +- * make the block bigger, copy the next part in and keep waiting. +- * +- * This is only required with zlib versions < 1.2.0.4 as newer versions +- * can handle the gzip header themselves. +- */ +- +- switch(zp->zlib_init) { +- /* Skip over gzip header? */ +- case ZLIB_INIT: +- { +- /* Initial call state */ +- ssize_t hlen; +- +- switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { +- case GZIP_OK: +- z->next_in = (Bytef *) buf + hlen; +- z->avail_in = (uInt) (nbytes - hlen); +- zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ +- break; +- +- case GZIP_UNDERFLOW: +- /* We need more data so we can find the end of the gzip header. It's +- * possible that the memory block we malloc here will never be freed if +- * the transfer abruptly aborts after this point. Since it's unlikely +- * that circumstances will be right for this code path to be followed in +- * the first place, and it's even more unlikely for a transfer to fail +- * immediately afterwards, it should seldom be a problem. +- */ +- z->avail_in = (uInt) nbytes; +- z->next_in = malloc(z->avail_in); +- if(!z->next_in) { +- return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); +- } +- memcpy(z->next_in, buf, z->avail_in); +- zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ +- /* We don't have any data to inflate yet */ +- return CURLE_OK; +- +- case GZIP_BAD: +- default: +- return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); +- } +- +- } +- break; +- +- case ZLIB_GZIP_HEADER: +- { +- /* Need more gzip header data state */ +- ssize_t hlen; +- z->avail_in += (uInt) nbytes; +- z->next_in = Curl_saferealloc(z->next_in, z->avail_in); +- if(!z->next_in) { +- return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); +- } +- /* Append the new block of data to the previous one */ +- memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); +- +- switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) { +- case GZIP_OK: +- /* This is the zlib stream data */ +- free(z->next_in); +- /* Don't point into the malloced block since we just freed it */ +- z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; +- z->avail_in = (uInt) (z->avail_in - hlen); +- zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ +- break; +- +- case GZIP_UNDERFLOW: +- /* We still don't have any data to inflate! */ +- return CURLE_OK; +- +- case GZIP_BAD: +- default: +- return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); +- } +- +- } +- break; +- +- case ZLIB_EXTERNAL_TRAILER: +- z->next_in = (Bytef *) buf; +- z->avail_in = (uInt) nbytes; +- return process_trailer(data, zp); +- +- case ZLIB_GZIP_INFLATING: +- default: +- /* Inflating stream state */ +- z->next_in = (Bytef *) buf; +- z->avail_in = (uInt) nbytes; +- break; +- } +- +- if(z->avail_in == 0) { +- /* We don't have any data to inflate; wait until next time */ +- return CURLE_OK; +- } +- +- /* We've parsed the header, now uncompress the data */ +- return inflate_stream(data, writer, ZLIB_GZIP_INFLATING); +-#endif + } + + static void gzip_close_writer(struct Curl_easy *data, diff --git a/backport-cookie-treat-cookie-name-case-sensitively.patch b/backport-cookie-treat-cookie-name-case-sensitively.patch new file mode 100644 index 0000000..bc1f790 --- /dev/null +++ b/backport-cookie-treat-cookie-name-case-sensitively.patch @@ -0,0 +1,71 @@ +From 9919149aef67014150e2a1c75a7aa2c79204e30d Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Wed, 6 Nov 2024 11:26:25 +0100 +Subject: [PATCH] cookie: treat cookie name case sensitively + +Extend test 31 to verify + +Reported-by: delogicsreal on github +Fixes #15492 +Closes #15493 + +Conflict:context adapt +Reference:https://github.com/curl/curl/commit/9919149aef67014150e2a1c75a7aa2c79204e30d +--- + lib/cookie.c | 4 ++-- + tests/data/test31 | 3 +++ + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/lib/cookie.c b/lib/cookie.c +index ca8c3c596..e37d58f1d 100644 +--- a/lib/cookie.c ++++ b/lib/cookie.c +@@ -989,7 +989,7 @@ replace_existing(struct Curl_easy *data, + myhash = cookiehash(co->domain); + clist = c->cookies[myhash]; + while(clist) { +- if(strcasecompare(clist->name, co->name)) { ++ if(!strcmp(clist->name, co->name)) { + /* the names are identical */ + bool matching_domains = FALSE; + +@@ -1029,7 +1029,7 @@ replace_existing(struct Curl_easy *data, + } + } + +- if(!replace_co && strcasecompare(clist->name, co->name)) { ++ if(!replace_co && !strcmp(clist->name, co->name)) { + /* the names are identical */ + + if(clist->domain && co->domain) { +diff --git a/tests/data/test31 b/tests/data/test31 +index d9d073996..2d411b5cd 100644 +--- a/tests/data/test31 ++++ b/tests/data/test31 +@@ -26,6 +26,7 @@ Set-Cookie: blankdomain=sure; domain=; path=/ + %if !hyper + Set-Cookie: foobar=name; domain=anything.com; path=/ ; secure
+ Set-Cookie:ismatch=this ; domain=test31.curl; path=/silly/
++Set-Cookie:ISMATCH=this ; domain=test31.curl; path=/silly/
+ Set-Cookie: overwrite=this ; domain=test31.curl; path=/overwrite/
+ Set-Cookie: overwrite=this2 ; domain=test31.curl; path=/overwrite
+ Set-Cookie: sec1value=secure1 ; domain=test31.curl; path=/secure1/ ; secure
+@@ -75,6 +76,7 @@ Set-Cookie: securewithspace=after ; secure = + %else + Set-Cookie: foobar=name; domain=anything.com; path=/ ; secure
+ Set-Cookie: ismatch=this ; domain=test31.curl; path=/silly/
++Set-Cookie:ISMATCH=this ; domain=test31.curl; path=/silly/
+ Set-Cookie: overwrite=this ; domain=test31.curl; path=/overwrite/
+ Set-Cookie: overwrite=this2 ; domain=test31.curl; path=/overwrite
+ Set-Cookie: sec1value=secure1 ; domain=test31.curl; path=/secure1/ ; secure
+@@ -181,6 +183,7 @@ test31.curl FALSE /we/want/ FALSE 2118138987 nodomain value + #HttpOnly_.test31.curl TRUE /p2/ FALSE 0 httpo2 value2 + #HttpOnly_.test31.curl TRUE /p1/ FALSE 0 httpo1 value1 + .test31.curl TRUE /overwrite FALSE 0 overwrite this2 ++.test31.curl TRUE /silly/ FALSE 0 ISMATCH this + .test31.curl TRUE /silly/ FALSE 0 ismatch this + test31.curl FALSE / FALSE 0 blankdomain sure + </file> +-- +2.33.0 + diff --git a/backport-curl-7.84.0-test3026.patch b/backport-curl-7.84.0-test3026.patch new file mode 100644 index 0000000..1098583 --- /dev/null +++ b/backport-curl-7.84.0-test3026.patch @@ -0,0 +1,71 @@ +From 279b990727a1fd3e2828fbbd80581777e4200b67 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka <kdudka@redhat.com> +Date: Mon, 27 Jun 2022 16:50:57 +0200 +Subject: [PATCH] test3026: disable valgrind + +It fails on x86_64 with: +``` + Use --max-threads=INT to specify a larger number of threads + and rerun valgrind + valgrind: the 'impossible' happened: + Max number of threads is too low + host stacktrace: + ==174357== at 0x58042F5A: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x58043087: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x580432EF: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x58043310: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x58099E77: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x580E67E9: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x5809D59D: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x5809901A: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x5809B0B6: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + ==174357== by 0x580E4050: ??? (in /usr/libexec/valgrind/memcheck-amd64-linux) + sched status: + running_tid=1 + Thread 1: status = VgTs_Runnable syscall 56 (lwpid 174357) + ==174357== at 0x4A07816: clone (in /usr/lib64/libc.so.6) + ==174357== by 0x4A08720: __clone_internal (in /usr/lib64/libc.so.6) + ==174357== by 0x4987ACF: create_thread (in /usr/lib64/libc.so.6) + ==174357== by 0x49885F6: pthread_create@@GLIBC_2.34 (in /usr/lib64/libc.so.6) + ==174357== by 0x1093B5: test.part.0 (lib3026.c:64) + ==174357== by 0x492454F: (below main) (in /usr/lib64/libc.so.6) + client stack range: [0x1FFEFFC000 0x1FFF000FFF] client SP: 0x1FFEFFC998 + valgrind stack range: [0x1002BAA000 0x1002CA9FFF] top usage: 11728 of 1048576 +[...] +``` +--- + tests/data/test3026 | 3 +++ + tests/libtest/lib3026.c | 4 ++-- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/tests/data/test3026 b/tests/data/test3026 +index fb80cc8..01f2ba5 100644 +--- a/tests/data/test3026 ++++ b/tests/data/test3026 +@@ -41,5 +41,8 @@ none + <errorcode> + 0 + </errorcode> ++<valgrind> ++disable ++</valgrind> + </verify> + </testcase> +diff --git a/tests/libtest/lib3026.c b/tests/libtest/lib3026.c +index 43fe335..70cd7a4 100644 +--- a/tests/libtest/lib3026.c ++++ b/tests/libtest/lib3026.c +@@ -147,8 +147,8 @@ int test(char *URL) + results[i] = CURL_LAST; /* initialize with invalid value */ + res = pthread_create(&tids[i], NULL, run_thread, &results[i]); + if(res) { +- fprintf(stderr, "%s:%d Couldn't create thread, errno %d\n", +- __FILE__, __LINE__, res); ++ fprintf(stderr, "%s:%d Couldn't create thread, i=%u, errno %d\n", ++ __FILE__, __LINE__, i, res); + tid_count = i; + test_failure = -1; + goto cleanup; +-- +2.37.1 + diff --git a/backport-curl-7.88.0-tests-warnings.patch b/backport-curl-7.88.0-tests-warnings.patch new file mode 100644 index 0000000..04b2ba2 --- /dev/null +++ b/backport-curl-7.88.0-tests-warnings.patch @@ -0,0 +1,30 @@ +From d506d885aa16b4a87acbac082eea41dccdc7b69f Mon Sep 17 00:00:00 2001 +From: Kamil Dudka <kdudka@redhat.com> +Date: Wed, 15 Feb 2023 10:42:38 +0100 +Subject: [PATCH] Revert "runtests: consider warnings fatal and error on them" + +While it might be useful for upstream developers, it is not so useful +for downstream consumers. + +This reverts upstream commit 22f795c834cfdbacbb1b55426028a581e3cf67a8. +--- + tests/runtests.pl | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/tests/runtests.pl b/tests/runtests.pl +index 71644ad18..0cf85c3fe 100755 +--- a/tests/runtests.pl ++++ b/tests/runtests.pl +@@ -55,8 +55,7 @@ + # given, this won't be a problem. + + use strict; +-# Promote all warnings to fatal +-use warnings FATAL => 'all'; ++use warnings; + use 5.006; + + # These should be the only variables that might be needed to get edited: +-- +2.39.1 + diff --git a/backport-libssh2-set-length-to-0-if-strdup-failed.patch b/backport-libssh2-set-length-to-0-if-strdup-failed.patch new file mode 100644 index 0000000..eeeb7c0 --- /dev/null +++ b/backport-libssh2-set-length-to-0-if-strdup-failed.patch @@ -0,0 +1,31 @@ +From 6f3204820052263f488f86e02c206e1d24c4da2c Mon Sep 17 00:00:00 2001 +From: Tobias Stoeckmann <tobias@stoeckmann.org> +Date: Thu, 28 Mar 2024 00:38:09 +0100 +Subject: [PATCH] libssh2: set length to 0 if strdup failed + +Internally, libssh2 dereferences the NULL pointer if length is non-zero. +The callback function cannot return the error condition, so at least +prevent subsequent crash. + +Closes #13213 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/6f3204820052263f488f86e02c206e1d24c4da2c +--- + lib/vssh/libssh2.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c +index 3cfbe126c69df3..7d8d5f46571e9f 100644 +--- a/lib/vssh/libssh2.c ++++ b/lib/vssh/libssh2.c +@@ -201,7 +201,8 @@ kbd_callback(const char *name, int name_len, const char *instruction, + if(num_prompts == 1) { + struct connectdata *conn = data->conn; + responses[0].text = strdup(conn->passwd); +- responses[0].length = curlx_uztoui(strlen(conn->passwd)); ++ responses[0].length = ++ responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); + } + (void)prompts; + } /* kbd_callback */ diff --git a/backport-multi-avoid-memory-leak-risk.patch b/backport-multi-avoid-memory-leak-risk.patch new file mode 100644 index 0000000..0a0ed59 --- /dev/null +++ b/backport-multi-avoid-memory-leak-risk.patch @@ -0,0 +1,46 @@ +From 3572dd65bb233fc2720634804312192e3bdf4adf Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Thu, 25 Apr 2024 09:52:51 +0200 +Subject: [PATCH] multi: avoid memory-leak risk + +'newurl' is allocated in some conditions and used in a few scenarios, +but there were theoretical combinations in which it would not get freed. +Move the free to happen unconditionally. Never triggered by tests, but +spotted by Coverity. + +Closes #13471 + +Conflict:Context adapt +Reference:https://github.com/curl/curl/commit/3572dd65bb233fc2720634804312192e3bdf4adf +--- + lib/multi.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/lib/multi.c b/lib/multi.c +index fb98d80639f3b7..7e7590d60f8bcb 100644 +--- a/lib/multi.c ++++ b/lib/multi.c +@@ -2530,7 +2530,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, + multistate(data, MSTATE_CONNECT); + rc = CURLM_CALL_MULTI_PERFORM; + } +- free(newurl); + } + else { + /* after the transfer is done, go DONE */ +@@ -2542,7 +2541,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, + newurl = data->req.location; + data->req.location = NULL; + result = Curl_follow(data, newurl, FOLLOW_FAKE); +- free(newurl); + if(result) { + stream_error = TRUE; + result = multi_done(data, result, TRUE); +@@ -2561,6 +2559,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, + transfers */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } ++ free(newurl); + break; + } + diff --git a/backport-multi-check-that-the-multi-handle-is-valid-in-curl_m.patch b/backport-multi-check-that-the-multi-handle-is-valid-in-curl_m.patch new file mode 100644 index 0000000..c1dc6f5 --- /dev/null +++ b/backport-multi-check-that-the-multi-handle-is-valid-in-curl_m.patch @@ -0,0 +1,37 @@ +From 48f61e781a01e6a8dbc4a347e280644b1c68ab6a Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Wed, 11 Sep 2024 14:12:41 +0200 +Subject: [PATCH] multi: check that the multi handle is valid in + curl_multi_assign + +By requiring that the multi handle is fine, it can detect bad usage +better and by that avoid crashes. Like in the #14860 case, which is an +application calling curl_multi_assign() with a NULL pointer multi +handle. + +Reported-by: Carlo Cabrera +Fixes #14860 +Closes #14862 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/48f61e781a01e6a8dbc4a347e280644b1c68ab6a +--- + lib/multi.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/lib/multi.c b/lib/multi.c +index 062d09cc0..78e5c0a1e 100644 +--- a/lib/multi.c ++++ b/lib/multi.c +@@ -3688,6 +3688,8 @@ CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, + void *hashp) + { + struct Curl_sh_entry *there = NULL; ++ if(!GOOD_MULTI_HANDLE(multi)) ++ return CURLM_BAD_HANDLE; + + there = sh_getentry(&multi->sockhash, s); + +-- +2.33.0 + diff --git a/backport-openldap-create-ldap-URLs-correctly-for-IPv6-addresses.patch b/backport-openldap-create-ldap-URLs-correctly-for-IPv6-addresses.patch new file mode 100644 index 0000000..aabfa79 --- /dev/null +++ b/backport-openldap-create-ldap-URLs-correctly-for-IPv6-addresses.patch @@ -0,0 +1,35 @@ +From 56935a7dada6975d5a46aa494de0af195e4e8659 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Sat, 30 Mar 2024 11:14:54 +0100 +Subject: [PATCH] openldap: create ldap URLs correctly for IPv6 addresses + +Reported-by: Sergio Durigan Junior +Fixes #13228 +Closes #13235 + +Conflict:Context adapt +Reference:https://github.com/curl/curl/commit/56935a7dada6975d5a46aa494de0af195e4e8659 +--- + lib/openldap.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/lib/openldap.c b/lib/openldap.c +index 47266f64e44733..85a37b8186041a 100644 +--- a/lib/openldap.c ++++ b/lib/openldap.c +@@ -548,9 +548,12 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) + + (void)done; + +- hosturl = aprintf("ldap%s://%s:%d", +- conn->handler->flags & PROTOPT_SSL? "s": "", +- conn->host.name, conn->remote_port); ++ hosturl = aprintf("%s://%s%s%s:%d", ++ conn->handler->scheme, ++ conn->bits.ipv6_ip? "[": "", ++ conn->host.name, ++ conn->bits.ipv6_ip? "]": "", ++ conn->remote_port); + if(!hosturl) + return CURLE_OUT_OF_MEMORY; + diff --git a/backport-openssl-avoid-BN_num_bits-NULL-pointer-derefs.patch b/backport-openssl-avoid-BN_num_bits-NULL-pointer-derefs.patch new file mode 100644 index 0000000..4a75f4c --- /dev/null +++ b/backport-openssl-avoid-BN_num_bits-NULL-pointer-derefs.patch @@ -0,0 +1,34 @@ +From b9f832edcce9db2de31070e76c3cbe59ca9ef512 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Thu, 12 Oct 2023 16:00:38 +0200 +Subject: [PATCH] openssl: avoid BN_num_bits() NULL pointer derefs + +Reported-by: icy17 on github +Fixes #12099 +Closes #12100 + +Conflict: NA +Reference: https://github.com/curl/curl/commit/b9f832edcce9db2de31070e76c3cbe59ca9ef512 +--- + lib/vtls/openssl.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c +index 9f9c8d136..6be86f871 100644 +--- a/lib/vtls/openssl.c ++++ b/lib/vtls/openssl.c +@@ -538,9 +538,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) + #else + RSA_get0_key(rsa, &n, &e, NULL); + #endif /* HAVE_EVP_PKEY_GET_PARAMS */ +- BIO_printf(mem, "%d", BN_num_bits(n)); ++ BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0); + #else +- BIO_printf(mem, "%d", BN_num_bits(rsa->n)); ++ BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0); + #endif /* HAVE_OPAQUE_RSA_DSA_DH */ + push_certinfo("RSA Public Key", i); + print_pubkey_BN(rsa, n, i); +-- +2.33.0 + diff --git a/backport-paramhlp-fix-CRLF-stripping-files-with-d-file.patch b/backport-paramhlp-fix-CRLF-stripping-files-with-d-file.patch new file mode 100644 index 0000000..2e2bd23 --- /dev/null +++ b/backport-paramhlp-fix-CRLF-stripping-files-with-d-file.patch @@ -0,0 +1,100 @@ +From 923f7f8ce51b7f2f20282883cdafeb283310f3d9 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Wed, 6 Mar 2024 15:39:09 +0100 +Subject: [PATCH] paramhlp: fix CRLF-stripping files with "-d @file" + +All CR and LF bytes should be stripped, as documented, and all other +bytes are inluded in the data. Starting now, it also excludes null bytes +as they would otherwise also cut the data short. + +Reported-by: Simon K +Fixes #13063 +Closes #13064 + +Conflict:remove change of docs/cmdline-opts/data.md which is not exist +Reference:https://github.com/curl/curl/commit/923f7f8ce51b7f2f20282883cdafeb283310f3d9 +--- + src/tool_paramhlp.c | 63 +++++++++++++++++++++++++++++++-------- + 1 files changed, 51 insertions(+), 12 deletions(-) + +diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c +index 2725815000dc95..c26f6bbefd775c 100644 +--- a/src/tool_paramhlp.c ++++ b/src/tool_paramhlp.c +@@ -63,6 +63,33 @@ struct getout *new_getout(struct OperationConfig *config) + return node; + } + ++#define ISCRLF(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) ++ ++/* memcrlf() has two modes. Both operate on a given memory area with ++ a specified size. ++ ++ countcrlf FALSE - return number of bytes from the start that DO NOT include ++ any CR or LF or NULL ++ ++ countcrlf TRUE - return number of bytes from the start that are ONLY CR or ++ LF or NULL. ++ ++*/ ++static size_t memcrlf(char *orig, ++ bool countcrlf, /* TRUE if we count CRLF, FALSE ++ if we count non-CRLF */ ++ size_t max) ++{ ++ char *ptr = orig; ++ size_t total = max; ++ for(ptr = orig; max; max--, ptr++) { ++ bool crlf = ISCRLF(*ptr); ++ if(countcrlf ^ crlf) ++ return ptr - orig; ++ } ++ return total; /* no delimiter found */ ++} ++ + #define MAX_FILE2STRING (256*1024*1024) /* big enough ? */ + + ParameterError file2string(char **bufp, FILE *file) +@@ -71,18 +98,30 @@ ParameterError file2string(char **bufp, FILE *file) + DEBUGASSERT(MAX_FILE2STRING < INT_MAX); /* needs to fit in an int later */ + curlx_dyn_init(&dyn, MAX_FILE2STRING); + if(file) { +- char buffer[256]; +- +- while(fgets(buffer, sizeof(buffer), file)) { +- char *ptr = strchr(buffer, '\r'); +- if(ptr) +- *ptr = '\0'; +- ptr = strchr(buffer, '\n'); +- if(ptr) +- *ptr = '\0'; +- if(curlx_dyn_add(&dyn, buffer)) +- return PARAM_NO_MEM; +- } ++ do { ++ char buffer[4096]; ++ char *ptr; ++ size_t nread = fread(buffer, 1, sizeof(buffer), file); ++ if(ferror(file)) { ++ curlx_dyn_free(&dyn); ++ *bufp = NULL; ++ return PARAM_READ_ERROR; ++ } ++ ptr = buffer; ++ while(nread) { ++ size_t nlen = memcrlf(ptr, FALSE, nread); ++ if(curlx_dyn_addn(&dyn, ptr, nlen)) ++ return PARAM_NO_MEM; ++ nread -= nlen; ++ ++ if(nread) { ++ ptr += nlen; ++ nlen = memcrlf(ptr, TRUE, nread); ++ ptr += nlen; ++ nread -= nlen; ++ } ++ } ++ } while(!feof(file)); + } + *bufp = curlx_dyn_ptr(&dyn); + return PARAM_OK; diff --git a/backport-pre-CVE-2024-2004.patch b/backport-pre-CVE-2024-2004.patch new file mode 100644 index 0000000..d297555 --- /dev/null +++ b/backport-pre-CVE-2024-2004.patch @@ -0,0 +1,159 @@ +From de0cd5e8e7c9a0cbf28c4a9dec998ad4b6dfa08c Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Mon, 11 Dec 2023 23:17:26 +0100 +Subject: [PATCH] test1474: removed + +The test was already somewhat flaky and disabled on several platforms, +and after 1da640abb688 even more unstable. + +Conflict:Context adapt +Reference:https://github.com/curl/curl/commit/de0cd5e8e7c9a0cbf28c4a9dec998ad4b6dfa08c +--- + tests/data/Makefile.inc | 2 +- + tests/data/test1474 | 121 ---------------------------------------- + 2 files changed, 1 insertion(+), 122 deletions(-) + delete mode 100644 tests/data/test1474 + +diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc +index de13c525e..6d1a2ad13 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -186,7 +186,7 @@ test1439 test1440 test1441 test1442 test1443 test1444 test1445 test1446 \ + test1447 test1448 test1449 test1450 test1451 test1452 test1453 test1454 \ + test1455 test1456 test1457 test1458 test1459 test1460 test1461 test1462 \ + test1463 test1464 test1465 test1466 test1467 test1468 test1469 test1470 \ +-test1471 test1472 test1473 test1474 \ ++test1471 test1472 test1473 \ + \ + test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \ + test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \ +diff --git a/tests/data/test1474 b/tests/data/test1474 +deleted file mode 100644 +index a87044d1a..000000000 +--- a/tests/data/test1474 ++++ /dev/null +@@ -1,121 +0,0 @@ +-<testcase> +-# This test is quite timing dependent and tricky to set up. The time line of +-# test operations looks like this: +-# +-# 1. curl sends a PUT request with Expect: 100-continue and waits only 1 msec +-# for a 100 response. +-# 2. The HTTP server accepts the connection but waits 500 msec before acting +-# on the request. +-# 3. curl doesn't receive the expected 100 response before its timeout expires, +-# so it starts sending the body. It is throttled by a --limit-rate, so it +-# sends the first 64 KiB then stops for 1000 msec due to this +-# throttling. +-# 4. The server sends its 417 response while curl is throttled. +-# 5. curl responds to this 417 response by closing the connection (because it +-# has a half-completed response outstanding) and starting a new one. This +-# new request does not have an Expect: header so it is sent without delay. +-# It's still throttled, however, so it takes about 16 seconds to finish +-# sending. +-# 6. The server receives the response and this time acks it with 200. +-# +-# Because of the timing sensitivity (scheduling delays of 500 msec can cause +-# the test to fail), this test is marked flaky to avoid it being run in the CI +-# builds which are often run on overloaded servers. +-# Increasing the --limit-rate would decrease the test time, but at the cost of +-# becoming even more sensitive to delays (going from 500 msec to 250 msec or +-# less of accepted delay before failure). Adding a --speed-time would increase +-# the 1 second delay between writes to longer, but it would also increase the +-# total time needed by the test, which is already quite high. +-# +-# The assumption in step 3 is also broken on NetBSD 9.3, OpenBSD 7.3 and +-# Solaris 10 as they only usually send about half the requested amount of data +-# (see https://curl.se/mail/lib-2023-09/0021.html). +-<info> +-<keywords> +-HTTP +-HTTP PUT +-Expect +-flaky +-timing-dependent +-</keywords> +-</info> +-# Server-side +-<reply> +-# 417 means the server didn't like the Expect header +-<data> +-HTTP/1.1 417 BAD swsbounce +-Date: Tue, 09 Nov 2010 14:49:00 GMT +-Server: test-server/fake +-Content-Length: 0 +- +-</data> +-<data1> +-HTTP/1.1 200 OK +-Date: Tue, 09 Nov 2010 14:49:00 GMT +-Server: test-server/fake +-Content-Length: 10 +- +-blablabla +-</data1> +-<datacheck> +-HTTP/1.1 417 BAD swsbounce +-Date: Tue, 09 Nov 2010 14:49:00 GMT +-Server: test-server/fake +-Content-Length: 0 +- +-HTTP/1.1 200 OK +-Date: Tue, 09 Nov 2010 14:49:00 GMT +-Server: test-server/fake +-Content-Length: 10 +- +-blablabla +-</datacheck> +-<servercmd> +-no-expect +-delay: 500 +-connection-monitor +-</servercmd> +-</reply> +- +-# Client-side +-<client> +-<server> +-http +-</server> +-<name> +-HTTP PUT with Expect: 100-continue and 417 response during upload +-</name> +-<command> +-http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER -T %LOGDIR/test%TESTNUMBER.txt --limit-rate 64K --expect100-timeout 0.001 +-</command> +-<precheck> +-perl -e "print 'Test does not work on this BSD system' if ( $^O eq 'netbsd' || $^O eq 'openbsd' || ($^O eq 'solaris' && qx/uname -r/ * 100 <= 510));" +-</precheck> +-# Must be large enough to trigger curl's automatic 100-continue behaviour +-<file name="%LOGDIR/test%TESTNUMBER.txt"> +-%repeat[132 x S]%%repeat[16462 x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%0a]% +-</file> +-</client> +- +-# Verify data after the test has been "shot" +-<verify> +-<protocol> +-PUT /we/want/%TESTNUMBER HTTP/1.1
+-Host: %HOSTIP:%HTTPPORT
+-User-Agent: curl/%VERSION
+-Accept: */*
+-Content-Length: 1053701
+-Expect: 100-continue
+-
+-%repeat[132 x S]%%repeat[1021 x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%0a]%%repeat[60 x x]%[DISCONNECT] +-PUT /we/want/%TESTNUMBER HTTP/1.1
+-Host: %HOSTIP:%HTTPPORT
+-User-Agent: curl/%VERSION
+-Accept: */*
+-Content-Length: 1053701
+-
+-%repeat[132 x S]%%repeat[16462 x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%0a]% +-[DISCONNECT] +-</protocol> +-</verify> +-</testcase> +-- +2.33.0 + diff --git a/backport-pre-CVE-2024-9681.patch b/backport-pre-CVE-2024-9681.patch new file mode 100644 index 0000000..cac0ac4 --- /dev/null +++ b/backport-pre-CVE-2024-9681.patch @@ -0,0 +1,69 @@ +From 60d8663afb0fb7f113604404c50840dfe9320039 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Tue, 8 Oct 2024 11:20:40 +0200 +Subject: [PATCH] hsts: avoid the local buffer and memcpy on lookup + +Closes #15190 +Conflict:Context adapt +Reference:https://github.com/curl/curl/commit/60d8663afb0fb7f113604404c50840dfe9320039 +--- + lib/hsts.c | 22 +++++++++------------- + 1 file changed, 9 insertions(+), 13 deletions(-) + +diff --git a/lib/hsts.c b/lib/hsts.c +index 7ecf004..f5e5bbf 100644 +--- a/lib/hsts.c ++++ b/lib/hsts.c +@@ -250,7 +250,6 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, + bool subdomain) + { + if(h) { +- char buffer[MAX_HSTS_HOSTLEN + 1]; + time_t now = time(NULL); + size_t hlen = strlen(hostname); + struct Curl_llist_element *e; +@@ -258,15 +257,13 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, + + if((hlen > MAX_HSTS_HOSTLEN) || !hlen) + return NULL; +- memcpy(buffer, hostname, hlen); + if(hostname[hlen-1] == '.') + /* remove the trailing dot */ + --hlen; +- buffer[hlen] = 0; +- hostname = buffer; + + for(e = h->list.head; e; e = n) { + struct stsentry *sts = e->ptr; ++ size_t ntail; + n = e->next; + if(sts->expires <= now) { + /* remove expired entries */ +@@ -274,16 +271,15 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, + hsts_free(sts); + continue; + } +- if(subdomain && sts->includeSubDomains) { +- size_t ntail = strlen(sts->host); +- if(ntail < hlen) { +- size_t offs = hlen - ntail; +- if((hostname[offs-1] == '.') && +- strncasecompare(&hostname[offs], sts->host, ntail)) +- return sts; +- } ++ ntail = strlen(sts->host); ++ if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { ++ size_t offs = hlen - ntail; ++ if((hostname[offs-1] == '.') && ++ strncasecompare(&hostname[offs], sts->host, ntail)) ++ return sts; + } +- if(strcasecompare(hostname, sts->host)) ++ /* avoid strcasecompare because the host name is not null terminated */ ++ if((hlen == ntail) && strncasecompare(hostname, sts->host, hlen)) + return sts; + } + } +-- +2.43.0 + diff --git a/backport-tool_cb_rea-limit-rate-unpause-for-T-uploads.patch b/backport-tool_cb_rea-limit-rate-unpause-for-T-uploads.patch new file mode 100644 index 0000000..fd98749 --- /dev/null +++ b/backport-tool_cb_rea-limit-rate-unpause-for-T-uploads.patch @@ -0,0 +1,61 @@ +From 5f4aaf8b66ef04208c1c2121d4b780c792303f32 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Tue, 30 Apr 2024 11:07:28 +0200 +Subject: [PATCH] tool_cb_rea: limit rate unpause for -T . uploads +Reference:https://github.com/curl/curl/pull/13506 + +--- + src/tool_cb_rea.c | 30 ++++++++++++++++++++++++++++-- + 1 file changed, 28 insertions(+), 2 deletions(-) + +diff --git a/src/tool_cb_rea.c b/src/tool_cb_rea.c +index d70a9b9..f510f81 100644 +--- a/src/tool_cb_rea.c ++++ b/src/tool_cb_rea.c +@@ -36,6 +36,7 @@ + #include "tool_operate.h" + #include "tool_util.h" + #include "tool_msgs.h" ++#include "tool_sleep.h" + + #include "memdebug.h" /* keep this as LAST include */ + +@@ -124,8 +125,33 @@ int tool_readbusy_cb(void *clientp, + (void)ulnow; /* unused */ + + if(config->readbusy) { +- config->readbusy = FALSE; +- curl_easy_pause(per->curl, CURLPAUSE_CONT); ++ /* lame code to keep the rate down because the input might not deliver ++ anything, get paused again and come back here immediately */ ++ static long rate = 500; ++ static struct timeval prev; ++ static curl_off_t ulprev; ++ ++ if(ulprev == ulnow) { ++ /* it did not upload anything since last call */ ++ struct timeval now = tvnow(); ++ if(prev.tv_sec) ++ /* get a rolling average rate */ ++ /* rate = rate - rate/4 + tvdiff(now, prev)/4; */ ++ rate -= rate/4 - tvdiff(now, prev)/4; ++ prev = now; ++ } ++ else { ++ rate = 50; ++ ulprev = ulnow; ++ } ++ if(rate >= 50) { ++ /* keeps the looping down to 20 times per second in the crazy case */ ++ config->readbusy = FALSE; ++ curl_easy_pause(per->curl, CURLPAUSE_CONT); ++ } ++ else ++ /* sleep half a period */ ++ tool_go_sleep(25); + } + + return per->noprogress? 0 : CURL_PROGRESSFUNC_CONTINUE; +-- +2.27.0 + diff --git a/backport-tool_cfgable-free-proxy_-cipher13_list-on-exit.patch b/backport-tool_cfgable-free-proxy_-cipher13_list-on-exit.patch new file mode 100644 index 0000000..ada0c62 --- /dev/null +++ b/backport-tool_cfgable-free-proxy_-cipher13_list-on-exit.patch @@ -0,0 +1,28 @@ +From 87d14e77b7d59a961eb56500017c0580f89f252b Mon Sep 17 00:00:00 2001 +From: Jan Venekamp <1422460+jan2000@users.noreply.github.com> +Date: Sat, 4 May 2024 03:05:51 +0200 +Subject: [PATCH] tool_cfgable: free {proxy_}cipher13_list on exit + +Author: Jan Venekamp +Reviewed-by: Daniel Gustafsson <daniel@yesql.se> +Closes: #13531 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/87d14e77b7d59a961eb56500017c0580f89f252b +--- + src/tool_cfgable.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c +index bb271583263db3..5564e250d33782 100644 +--- a/src/tool_cfgable.c ++++ b/src/tool_cfgable.c +@@ -114,6 +114,8 @@ static void free_config_fields(struct OperationConfig *config) + Curl_safefree(config->doh_url); + Curl_safefree(config->cipher_list); + Curl_safefree(config->proxy_cipher_list); ++ Curl_safefree(config->cipher13_list); ++ Curl_safefree(config->proxy_cipher13_list); + Curl_safefree(config->cert); + Curl_safefree(config->proxy_cert); + Curl_safefree(config->cert_type); diff --git a/backport-url-allow-DoH-transfers-to-override-max-connection-limit.patch b/backport-url-allow-DoH-transfers-to-override-max-connection-limit.patch new file mode 100644 index 0000000..11e7a50 --- /dev/null +++ b/backport-url-allow-DoH-transfers-to-override-max-connection-limit.patch @@ -0,0 +1,49 @@ +From b049388d473a9a0189f3180e57e04a39a3793382 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Tue, 4 Jun 2024 17:00:05 +0200 +Subject: [PATCH] url: allow DoH transfers to override max connection limit + +When reaching the set maximum limit of allowed connections, allow a new +connection anyway if the transfer is created for the (internal) purpose +of doing a DoH name resolve. Otherwise, unrelated "normal" transfers can +starve out new DoH requests making it impossible to name resolve for new +transfers. + +Bug: https://curl.se/mail/lib-2024-06/0001.html +Reported-by: kartatz +Closes #13880 + +Conflict:NA +Reference:https://github.com/curl/curl/commit/b049388d473a9a0189f3180e57e04a39a3793382 +--- + lib/url.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/lib/url.c b/lib/url.c +index 41e35e153..4eabf0c87 100644 +--- a/lib/url.c ++++ b/lib/url.c +@@ -3662,10 +3662,16 @@ static CURLcode create_conn(struct Curl_easy *data, + conn_candidate = Curl_conncache_extract_oldest(data); + if(conn_candidate) + Curl_disconnect(data, conn_candidate, FALSE); +- else { +- infof(data, "No connections available in cache"); +- connections_available = FALSE; +- } ++ else ++#ifndef CURL_DISABLE_DOH ++ if(data->set.dohfor) ++ infof(data, "Allowing DoH to override max connection limit"); ++ else ++#endif ++ { ++ infof(data, "No connections available in cache"); ++ connections_available = FALSE; ++ } + } + + if(!connections_available) { +-- +2.33.0 + diff --git a/curl.spec b/curl.spec new file mode 100644 index 0000000..d6f465c --- /dev/null +++ b/curl.spec @@ -0,0 +1,570 @@ +#Global macro or variable +%global libpsl_version %(pkg-config --modversion libpsl 2>/dev/null || echo 0) +%global libssh_version %(pkg-config --modversion libssh 2>/dev/null || echo 0) +%global openssl_version %({ pkg-config --modversion openssl 2>/dev/null || echo 0;} | sed 's|-|-0.|') +%global libnghttp2_version %(pkg-config --modversion libnghttp2 2>/dev/null || echo 0) +%global _configure ../configure + +Name: curl +Version: 8.4.0 +Release: 15 +Summary: Curl is used in command lines or scripts to transfer data +License: curl +URL: https://curl.se/ +Source: https://curl.se/download/curl-%{version}.tar.xz + +Patch1: backport-0101-curl-7.32.0-multilib.patch +Patch2: backport-curl-7.84.0-test3026.patch +Patch4: backport-curl-7.88.0-tests-warnings.patch +Patch11: backport-CVE-2023-46218.patch +Patch12: backport-0001-CVE-2023-46219.patch +Patch13: backport-0002-CVE-2023-46219.patch +Patch15: backport-openssl-avoid-BN_num_bits-NULL-pointer-derefs.patch +Patch16: backport-pre-CVE-2024-2004.patch +Patch17: backport-CVE-2024-2004.patch +Patch18: backport-CVE-2024-2398.patch +Patch19: backport-tool_cb_rea-limit-rate-unpause-for-T-uploads.patch +#https://github.com/curl/curl/pull/13506 +Patch20: backport-paramhlp-fix-CRLF-stripping-files-with-d-file.patch +Patch21: backport-libssh2-set-length-to-0-if-strdup-failed.patch +Patch22: backport-openldap-create-ldap-URLs-correctly-for-IPv6-addresses.patch +Patch23: backport-multi-avoid-memory-leak-risk.patch +Patch24: backport-tool_cfgable-free-proxy_-cipher13_list-on-exit.patch +Patch25: backport-CVE-2024-7264-x509asn1-clean-up-GTime2str.patch +Patch26: backport-CVE-2024-7264-x509asn1-unittests-and-fixes-fo.patch +Patch27: backport-CVE-2024-8096-gtls-fix-OCSP-stapling-management.patch +Patch28: backport-url-allow-DoH-transfers-to-override-max-connection-limit.patch +Patch29: backport-pre-CVE-2024-9681.patch +Patch30: backport-CVE-2024-9681.patch +Patch31: backport-multi-check-that-the-multi-handle-is-valid-in-curl_m.patch +Patch32: backport-cookie-treat-cookie-name-case-sensitively.patch +Patch33: backport-CVE-2024-11053-pre1.patch +Patch34: backport-CVE-2024-11053-pre2.patch +Patch35: backport-CVE-2024-11053-pre3.patch +Patch36: backport-CVE-2024-11053-pre4.patch +Patch37: backport-CVE-2024-11053-pre5.patch +Patch38: backport-CVE-2024-11053.patch +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 + +BuildRequires: automake brotli-devel coreutils gcc groff krb5-devel +BuildRequires: libidn2-devel libnghttp2-devel libpsl-devel +BuildRequires: libssh-devel make openldap-devel openssh-clients openssh-server +BuildRequires: openssl-devel perl-interpreter pkgconfig python3-devel sed +BuildRequires: zlib-devel gnutls-utils nghttp2 perl(IO::Compress::Gzip) +BuildRequires: perl(Getopt::Long) perl(Pod::Usage) perl(strict) perl(warnings) +BuildRequires: perl(Cwd) perl(Digest::MD5) perl(Exporter) perl(File::Basename) +BuildRequires: perl(File::Copy) perl(File::Spec) perl(IPC::Open2) perl(MIME::Base64) +BuildRequires: perl(Time::Local) perl(Time::HiRes) perl(vars) perl(Digest::SHA) + +%ifnarch aarch64 +BuildRequires: stunnel +%endif + +Requires: libcurl = %{version}-%{release} +Provides: curl-full = %{version}-%{release} webclient + +%description +cURL is a computer software project providing a library (libcurl) and +command-line tool (curl) for transferring data using various protocols. + +%package -n libcurl +Summary: A library for getting files from web servers +Requires: libssh >= %{libssh_version} libpsl >= %{libpsl_version} +Requires: openssl-libs >= 1:%{openssl_version} +Requires: libnghttp2 >= %{libnghttp2_version} +Provides: libcurl-full = %{version}-%{release} +Conflicts: curl < 7.66.0-3 + +%description -n libcurl +A library for getting files from web servers. + +%package -n libcurl-devel +Summary: Header files for libcurl +Requires: libcurl = %{version}-%{release} +Provides: curl-devel = %{version}-%{release} +Obsoletes: curl-devel < %{version}-%{release} + +%description -n libcurl-devel +Header files for libcurl. + +%package_help + +%prep +%autosetup -n %{name}-%{version} -p1 + +echo "1801" >> tests/data/DISABLED + +# adapt test 323 for updated OpenSSL +sed -e 's/^35$/35,52/' -i tests/data/test323 +# use localhost6 instead of ip6-localhost in the curl test-suite +( + # avoid glob expansion in the trace output of `bash -x` + { set +x; } 2>/dev/null + cmd="sed -e 's|ip6-localhost|localhost6|' -i tests/data/test[0-9]*" + printf "+ %s\n" "$cmd" >&2 + eval "$cmd" +) + +%build +# regenerate Makefile.in files +aclocal -I m4 +automake + +install -d build-full +export common_configure_opts="--cache-file=../config.cache \ + --enable-hsts --enable-ipv6 --enable-symbol-hiding --enable-threaded-resolver \ + --without-zstd --with-gssapi --with-libidn2 --with-nghttp2 --with-ssl \ + --with-ca-bundle=%{_sysconfdir}/pki/tls/certs/ca-bundle.crt" + +%global _configure ../configure + +# configure full build +( + cd build-full + %configure $common_configure_opts \ + --enable-dict \ + --enable-gopher \ + --enable-imap \ + --enable-ldap \ + --enable-ldaps \ + --enable-manual \ + --enable-mqtt \ + --enable-ntlm \ + --enable-ntlm-wb \ + --enable-pop3 \ + --enable-rtsp \ + --enable-smb \ + --enable-smtp \ + --enable-telnet \ + --enable-tftp \ + --enable-tls-srp \ + --with-brotli \ + --with-libpsl \ + --with-libssh +) + +sed -e 's/^runpath_var=.*/runpath_var=/' \ + -e 's/^hardcode_libdir_flag_spec=".*"$/hardcode_libdir_flag_spec=""/' \ + -i build-full/libtool + +%make_build V=1 -C build-full + +%check +# compile upstream test-cases +%make_build V=1 -C build-full/tests + +# relax crypto policy for the test-suite to make it pass again (#1610888) +export OPENSSL_SYSTEM_CIPHERS_OVERRIDE=XXX +export OPENSSL_CONF= + +# make runtests.pl work for out-of-tree builds +export srcdir=../../tests + +# prevent valgrind from being extremely slow (#1662656) +unset DEBUGINFOD_URLS + +# run the upstream test-suite for curl-full +for size in full; do ( + cd build-${size} + + # we have to override LD_LIBRARY_PATH because we eliminated rpath + export LD_LIBRARY_PATH="${PWD}/lib/.libs" + + cd tests + perl -I../../tests ../../tests/runtests.pl -a -n -p -v '!flaky' +) +done + +%install +rm -f ${RPM_BUILD_ROOT}%{_libdir}/libcurl.{la,so} + +# install libcurl.m4 for devel +install -D -m 644 docs/libcurl/libcurl.m4 $RPM_BUILD_ROOT%{_datadir}/aclocal/libcurl.m4 + +# curl file install +cd build-full +%make_install + +# install zsh completion for curl +LD_LIBRARY_PATH="$RPM_BUILD_ROOT%{_libdir}:$LD_LIBRARY_PATH" %make_install -C scripts + +# do not install /usr/share/fish/completions/curl.fish which is also installed +# by fish-3.0.2-1.module_f31+3716+57207597 and would trigger a conflict +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/fish + +rm -f ${RPM_BUILD_ROOT}%{_libdir}/libcurl.a +rm -rf ${RPM_BUILD_ROOT}%{_libdir}/libcurl.la + +%ldconfig_scriptlets + +%ldconfig_scriptlets -n libcurl + +%files +%defattr(-,root,root) +%license COPYING +%{_bindir}/curl +%{_datadir}/zsh + +%files -n libcurl +%defattr(-,root,root) +%{_libdir}/libcurl.so.4 +%{_libdir}/libcurl.so.4.[0-9].[0-9] + +%files -n libcurl-devel +%defattr(-,root,root) +%doc docs/examples/*.c docs/examples/Makefile.example docs/INTERNALS.md +%doc docs/CONTRIBUTE.md docs/libcurl/ABI.md +%{_bindir}/curl-config* +%{_includedir}/curl +%{_libdir}/*.so +%{_libdir}/pkgconfig/*.pc +%{_datadir}/aclocal/libcurl.m4 + +%files help +%defattr(-,root,root) +%doc CHANGES README* +%doc docs/BUGS.md docs/FAQ docs/FEATURES.md +%doc docs/TheArtOfHttpScripting.md docs/TODO +%{_mandir}/man1/curl.1* +%{_mandir}/man1/curl-config.1* +%{_mandir}/man3/* + +%changelog +* Sat Feb 08 2025 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-15 +- Type:CVE +- CVE:CVE-2025-0167 CVE-2025-0725 +- SUG:NA +- DESC:fix CVE-2025-0167 CVE-2025-0725 + +* Tue Jan 07 2025 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-14 +- Type:CVE +- CVE:CVE-2024-11053 +- SUG:NA +- DESC:fix CVE-2024-11053 + +* Mon Dec 09 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-13 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:cookie: treat cookie name case sensitively + +* Sat Nov 30 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-12 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:multi: check that the multi handle is valid in curl_multi_assign + +* Mon Nov 11 2024 yanglu <yanglu72@h-partners.com> - 8.4.0-11 +- Type:CVE +- CVE:CVE-2024-9681 +- SUG:NA +- DESC:fix CVE-2024-9681 + +* Fri Sep 20 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-10 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:url: allow DoH transfers to override max connection limit + +* Thu Sep 12 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-9 +- Type:CVE +- CVE:CVE-2024-8096 +- SUG:NA +- DESC:fix CVE-2024-8096 + +* Thu Sep 05 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-8 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:revert modify licence from curl to MIT + +* Thu Aug 15 2024 zhangxianjun <zhangxianjun@kylinos.cn> - 8.4.0-7 +- modify licence from curl to MIT + +* Wed Jul 31 2024 yinyongkang <yinyongkang@kylinos.cn> - 8.4.0-6 +- Type:CVE +- CVE:CVE-2024-7264 +- SUG:NA +- DESC:fix CVE-2024-7264 + +* Mon Jun 24 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-5 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:paramhlp: fix CRLF-stripping files with "-d @file" + libssh2: set length to 0 if strdup failed + openldap: create ldap URLs correctly for IPv6 addresses + multi: avoid memory-leak risk + tool_cfgable: free {proxy_}cipher13_list on exit + +* Wed Jun 12 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-4 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:add version require of nghttp2 for libcurl + +* Thu May 09 2024 baiguo <baiguo@kylinos.cn> - 8.4.0-3 +- DESC: tool_cb_rea: limit rate unpause for -T . uploads + +* Mon Apr 01 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-2 +- Type:CVE +- CVE:CVE-2024-2004 CVE-2024-2398 +- SUG:NA +- DESC:fix CVE-2024-2004 CVE-2024-2398 + +* Tue Jan 09 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-1 +- Type:requirement +- CVE:NA +- SUG:NA +- DESC:update curl to 8.4.0 + +* Thu Dec 28 2023 zhouyihang <zhouyihang3@h-partners.com> - 8.1.2-7 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:transfer: also stop the sending on closed connection + openssl: avoid BN_num_bits() NULL pointer derefs + +* Fri Dec 08 2023 zhouyihang <zhouyihang3@h-partners.com> - 8.1.2-6 +- Type:CVE +- CVE:CVE-2023-46218 CVE-2023-46219 +- SUG:NA +- DESC:fix CVE-2023-46218 CVE-2023-46219 + +* Thu Oct 12 2023 zhouyihang <zhouyihang3@h-partners.com> - 8.1.2-5 +- Type:CVE +- CVE:CVE-2023-38545 CVE-2023-38546 +- SUG:NA +- DESC:fix CVE-2023-38545 CVE-2023-38546 + +* Thu Sep 14 2023 gaihuiying <eaglegai@163.com> - 8.1.2-4 +- Type:CVE +- CVE:CVE-2023-38039 +- SUG:NA +- DESC:fix CVE-2023-38039 + +* Wed Sep 06 2023 yanglu <yanglu72@h-partners.com> - 8.1.2-3 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:vtls:avoid memory leak if sha256 call fails + urlapi:make sure zoneid is also duplicated in curl_url_dup + +* Thu Jul 20 2023 zhouyihang <zhouyihang3@h-partners.com> - 8.1.2-2 +- Type:CVE +- CVE:CVE-2023-32001 +- SUG:NA +- DESC:fix CVE-2023-32001 + +* Sat Jul 15 2023 gaihuiying <eaglegai@163.com> - 8.1.2-1 +- Type:requirement +- CVE:NA +- SUG:NA +- DESC:update to curl 8.1.2 + +* Sat Jun 10 2023 zhouyihang <zhouyihang3@h-partners.com> - 7.88.1-4 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:disable valgrind in tests + +* Thu Jun 08 2023 xingwei <xingwei14@h-partners.com> - 7.88.1-3 +- Type:CVE +- CVE:CVE-2023-28320,CVE-2023-28321,CVE-2023-28322 +- SUG:NA +- DESC:fix CVE-2023-28320,CVE-2023-28321,CVE-2023-28322 + +* Wed Mar 22 2023 zengwefeng <zwfeng@huawei.com> - 7.88.1-2 +- Type:cves +- ID:CVE-2023-27533 CVE-2023-27534 CVE-2023-27535 CVE-2023-27536 CVE-2023-27537 CVE-2023-27538 +- SUG:NA +- DESC:fix CVE-2023-27533 CVE-2023-27534 CVE-2023-27535 CVE-2023-27536 CVE-2023-27537 CVE-2023-27538 + + +* Thu Mar 02 2023 xinghe <xinghe2@h-partners.com> - 7.88.1-1 +- Type:requirements +- ID:NA +- SUG:NA +- DESC:upgrade to 7.88.1 + +* Sat Feb 18 2023 xinghe <xinghe2@h-partners.com> - 7.86.0-3 +- Type:cves +- ID:CVE-2023-23914 CVE-2023-23915 CVE-2023-23916 +- SUG:NA +- DESC:fix CVE-2023-23914 CVE-2023-23915 CVE-2023-23916 + +* Thu Dec 22 2022 zhouyihang <zhouyihang3@h-partners.com> - 7.86.0-2 +- Type:cves +- ID:CVE-2022-43551 CVE-2022-43552 +- SUG:NA +- DESC:fix CVE-2022-43551 CVE-2022-43552 + +* Wed Nov 16 2022 xinghe <xinghe2@h-partners.com> - 7.86.0-1 +- Type:requirements +- ID:NA +- SUG:NA +- DESC:upgrade to 7.86.0 + +* Thu Oct 27 2022 yanglu <yanglu72@h-partners.com> - 7.79.1-12 +- Type:cves +- CVE:CVE-2022-32221 CVE-2022-42915 CVE-2022-42916 +- SUG:NA +- DESC:fix CVE-2022-32221 CVE-2022-42915 CVE-2022-42916 + +* Tue Oct 11 2022 huangduirong <huangduirong@huawei.com> - 7.79.1-11 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:Move autoreconf to build + +* Thu Sep 01 2022 zhouyihang <zhouyihang@h-partners.com> - 7.79.1-10 +- Type:cves +- CVE:CVE-2022-35252 +- SUG:NA +- DESC:fix CVE-2022-35252 + +* Thu Jul 28 2022 gaihuiying <eaglegai@163.com> - 7.79.1-9 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:just rebuild release to 7.79.1-9 + +* Mon Jul 25 2022 gaihuiying <eaglegai@163.com> - 7.79.1-8 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:fix build error when add --disable-http-auth configure option + +* Tue Jul 05 2022 gaihuiying <eaglegai@163.com> - 7.79.1-7 +- Type:cves +- CVE:CVE-2022-32207 +- SUG:NA +- DESC:fix CVE-2022-32207 better + +* Wed Jun 29 2022 gaihuiying <eaglegai@163.com> - 7.79.1-6 +- Type:cves +- CVE:CVE-2022-32205 CVE-2022-32206 CVE-2022-32207 CVE-2022-32208 +- SUG:NA +- DESC:fix CVE-2022-32205 CVE-2022-32206 CVE-2022-32207 CVE-2022-32208 + +* Tue May 17 2022 gaihuiying <eaglegai@163.com> - 7.79.1-5 +- Type:cves +- CVE:CVE-2022-27781 CVE-2022-27782 +- SUG:NA +- DESC:fix CVE-2022-27781 CVE-2022-27782 + +* Sat May 14 2022 gaoxingwang <gaoxingwang1@huawei.com> - 7.79.1-4 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:fix dict and neg telnet server start fail in upstream testcase + +* Fri May 06 2022 gaihuiying <eaglegai@163.com> - 7.79.1-3 +- Type:cves +- CVE:CVE-2022-22576 CVE-2022-27774 CVE-2022-27775 CVE-2022-27776 +- SUG:NA +- DESC:fix CVE-2022-22576 CVE-2022-27774 CVE-2022-27775 CVE-2022-27776 + +* Mon Apr 25 2022 gaoxingwang <gaoxingwang1@huawei.com> - 7.79.1-2 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:enable check in spec + +* Thu Jan 20 2022 gaoxingwang <gaoxingwang@huawei.com> - 7.79.1-1 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:update curl to 7.79.1 +* Wed Sep 29 2021 yanglu <yanglu72@huawei.com> - 7.77.0-3 +- Type:CVE +- CVE:CVE-2021-22945 CVE-2021-22946 CVE-2021-22947 +- SUG:NA +- DESC:fix CVE-2021-22945 CVE-2021-22946CVE-2021-22947 + +* Fri Aug 13 2021 gaihuiying <gaihuiying1@huawei.com> - 7.77.0-2 +- Type:CVE +- CVE:CVE-2021-22925 CVE-2021-22926 +- SUG:NA +- DESC:fix CVE-2021-22925 CVE-2021-22926 + +* Thu Jul 8 2021 gaihuiying <gaihuiying1@huawei.com> - 7.77.0-1 +- Type:requirement +- CVE:NA +- SUG:NA +- DESC:update curl to 7.77.0 + +* Tue Jun 8 2021 gaihuiying <gaihuiying1@huawei.com> - 7.71.1-9 +- Type:CVE +- CVE:CVE-2021-22897 CVE-2021-22898 +- SUG:NA +- DESC:fix CVE-2021-22897 CVE-2021-22898 + +* Tue Apr 20 2021 gaihuiying <gaihuiying1@huawei.com> - 7.71.1-8 +- Type:CVE +- CVE:CVE-2021-22890 +- SUG:NA +- DESC:fix CVE-2021-22890 + +* Thu Apr 8 2021 xieliuhua <xieliuhua@huawei.com> - 7.71.1-7 +- Type:CVE +- CVE:CVE-2021-22876 +- SUG:NA +- DESC:fix CVE-2021-22876 + +* Tue Jan 26 2021 wangxiaopeng <wangxiaopeng7@huawei.com> - 7.71.1-6 +- Type:CVE +- CVE:CVE-2020-8285 +- SUG:NA +- DESC:fix CVE-2020-8285 + +* Tue Jan 19 2021 xielh2000 <xielh2000@163.com> - 7.71.1-5 +- Type:CVE +- CVE:CVE-2020-8286 +- SUG:NA +- DESC:fix CVE-2020-8286 + +* Mon Jan 18 2021 xihaochen <xihaochen@huawei.com> - 7.71.1-4 +- Type:CVE +- CVE:CVE-2020-8284 +- SUG:NA +- DESC:fix CVE-2020-8284 + +* Tue Jan 5 2021 gaihuiying <gaihuiying1@huawei.com> - 7.71.1-3 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:fix downgrade error + +* Mon Dec 28 2020 liuxin <liuxin264@huawei.com> - 7.71.1-2 +- Type:cves +- ID:CVE-2020-8231 +- SUG:NA +- DESC:fix CVE-2020-8231 + +* Fri Jul 24 2020 zhujunhao <zhujunhao8@huawei.com> - 7.71.1-1 +- Update to 7.71.1 + +* Thu Apr 9 2020 songnannan <songnannan2@huawei.com> - 7.66.0-3 +- split out the libcurl and libcurl-devel package + +* Tue Mar 17 2020 chenzhen <chenzhen44@huawei.com> - 7.66.0-2 +- Type:cves +- ID:CVE-2019-15601 +- SUG:NA +- DESC:fix CVE-2019-15601 + +* Sat Jan 11 2020 openEuler Buildteam <buildteam@openeuler.org> - 7.66.0-1 +- update to 7.66.0 + +* Sat Dec 21 2019 openEuler Buildteam <buildteam@openeuler.org> - 7.61.1-4 +- Type:cves +- ID:CVE-2019-5481 CVE-2019-5482 +- SUG:NA +- DESC:fix CVE-2019-5481 CVE-2019-5482 + +* Wed Sep 18 2019 guanyanjie <guanyanjie@huawei.com> - 7.61.1-3 +- Init for openEuler @@ -0,0 +1 @@ +8424597f247da68b6041dd7f9ca367fe curl-8.4.0.tar.xz |