diff --git a/SPECS/curl/CVE-2026-5545.patch b/SPECS/curl/CVE-2026-5545.patch new file mode 100644 index 00000000000..5a4541f5b6b --- /dev/null +++ b/SPECS/curl/CVE-2026-5545.patch @@ -0,0 +1,42 @@ +From 33e43985b8f3b9e66691d06e70be0395849856cd Mon Sep 17 00:00:00 2001 +From: Stefan Eissing +Date: Thu, 2 Apr 2026 11:33:39 +0200 +Subject: [PATCH] url: improve connection reuse on negotiate + +Check state of negotiate to allow proper connection reuse. + +Closes #21203 +Upstream Patch Reference: https://github.com/curl/curl/commit/33e43985b8f3b9e66691d06e70be0395849856cd.patch +https://launchpadlibrarian.net/859770351/curl_8.14.1-2ubuntu1.2_8.14.1-2ubuntu1.3.diff.gz +--- + lib/url.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/lib/url.c b/lib/url.c +index 6ea7b30..984b8db 100644 +--- a/lib/url.c ++++ b/lib/url.c +@@ -1166,11 +1166,17 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) + if(match->want_ntlm_http) { + if(Curl_timestrcmp(needle->user, conn->user) || + Curl_timestrcmp(needle->passwd, conn->passwd)) { +- + /* we prefer a credential match, but this is at least a connection +- that can be reused and "upgraded" to NTLM */ +- if(conn->http_ntlm_state == NTLMSTATE_NONE) ++ that can be reused and "upgraded" to NTLM if it does ++ not have any auth ongoing. */ ++#ifdef USE_SPNEGO ++ if((conn->http_ntlm_state == NTLMSTATE_NONE) ++ && (conn->http_negotiate_state == GSS_AUTHNONE)) { ++#else ++ if(conn->http_ntlm_state == NTLMSTATE_NONE) { ++#endif + match->found = conn; ++ } + return FALSE; + } + } +-- +2.45.4 + diff --git a/SPECS/curl/CVE-2026-6253.patch b/SPECS/curl/CVE-2026-6253.patch new file mode 100644 index 00000000000..3974784bbb9 --- /dev/null +++ b/SPECS/curl/CVE-2026-6253.patch @@ -0,0 +1,713 @@ +From 188c2f166a20fa97c2325b2da7d0e5cecc13725f Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Mon, 13 Apr 2026 17:17:23 +0200 +Subject: [PATCH] http: clear the proxy credentials as well on port or scheme + change + +Add tests 2009-2011 to verify switching between proxies with credentials +when the switch is driven by a redirect + +Reported-by: Dwij Mehta + +Upstream Patch Reference: https://launchpadlibrarian.net/859770351/curl_8.14.1-2ubuntu1.2_8.14.1-2ubuntu1.3.diff.gz +https://github.com/curl/curl/commit/188c2f166a20fa97c2325b2da7d0e5cecc13725f.patch +--- + lib/cf-h1-proxy.c | 25 +++++------ + lib/cf-h2-proxy.c | 2 +- + lib/multi.c | 13 ++++++ + lib/transfer.c | 51 ++++++++++++++++------- + lib/transfer.h | 2 + + tests/data/Makefile.am | 4 +- + tests/data/test1059 | 2 +- + tests/data/test1715 | 54 ++++++++++++++++++++++++ + tests/data/test2009 | 70 +++++++++++++++++++++++++++++++ + tests/data/test2010 | 71 ++++++++++++++++++++++++++++++++ + tests/data/test2011 | 70 +++++++++++++++++++++++++++++++ + tests/data/test217 | 4 +- + tests/data/test287 | 4 +- + tests/data/test302 | 2 +- + tests/data/test440 | 2 +- + tests/data/test441 | 2 +- + tests/data/test445 | 5 +-- + tests/data/test493 | 2 +- + tests/data/test718 | 2 +- + tests/data/test94 | 2 +- + tests/http/test_13_proxy_auth.py | 4 +- + 21 files changed, 347 insertions(+), 46 deletions(-) + create mode 100644 tests/data/test1715 + create mode 100644 tests/data/test2009 + create mode 100644 tests/data/test2010 + create mode 100644 tests/data/test2011 + +diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c +index 6b7f983..8524788 100644 +--- a/lib/cf-h1-proxy.c ++++ b/lib/cf-h1-proxy.c +@@ -418,17 +418,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, + + if(ts->keepon == KEEPON_IGNORE) { + /* This means we are currently ignoring a response-body */ +- +- if(ts->cl) { +- /* A Content-Length based body: simply count down the counter +- and make sure to break out of the loop when we are done! */ +- ts->cl--; +- if(ts->cl <= 0) { +- ts->keepon = KEEPON_DONE; +- break; +- } +- } +- else if(ts->chunked_encoding) { ++ if(ts->chunked_encoding) { + /* chunked-encoded body, so we need to do the chunked dance + properly to know when the end of the body is reached */ + size_t consumed = 0; +@@ -444,6 +434,15 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, + ts->keepon = KEEPON_DONE; + } + } ++ else if(ts->cl) { ++ /* A Content-Length based body: count down the counter ++ and make sure to break out of the loop when we are done! */ ++ ts->cl--; ++ if(ts->cl <= 0) { ++ ts->keepon = KEEPON_DONE; ++ break; ++ } ++ } + continue; + } + +@@ -906,6 +905,8 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, + /* read what is there */ + CURL_TRC_CF(data, cf, "CONNECT receive"); + result = recv_CONNECT_resp(cf, data, ts, &done); ++ if(result) ++ CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result); + if(Curl_pgrsUpdate(data)) { + result = CURLE_ABORTED_BY_CALLBACK; + goto out; +@@ -959,7 +960,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, + streamclose(conn, "proxy CONNECT failure"); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); +- return CURLE_RECV_ERROR; ++ return CURLE_COULDNT_CONNECT; + } + /* 2xx response, SUCCESS! */ + h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); +diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c +index e687dd1..2525cd2 100644 +--- a/lib/cf-h2-proxy.c ++++ b/lib/cf-h2-proxy.c +@@ -1019,7 +1019,7 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, + } + + /* Seems to have failed */ +- return CURLE_RECV_ERROR; ++ return CURLE_COULDNT_CONNECT; + } + + static CURLcode H2_CONNECT(struct Curl_cfilter *cf, +diff --git a/lib/multi.c b/lib/multi.c +index 1851dc7..53ab80e 100644 +--- a/lib/multi.c ++++ b/lib/multi.c +@@ -1979,11 +1979,24 @@ static CURLcode multi_follow(struct Curl_easy *data, + free(scheme); + } + if(clear) { ++ CURLcode result = Curl_reset_userpwd(data); ++ if(result) { ++ free(newurl); ++ return result; ++ } + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); + } + } + } ++ DEBUGASSERT(newurl); ++ { ++ CURLcode result = Curl_reset_proxypwd(data); ++ if(result) { ++ free(newurl); ++ return result; ++ } ++ } + + if(type == FOLLOW_FAKE) { + /* we are only figuring out the new URL if we would have followed locations +diff --git a/lib/transfer.c b/lib/transfer.c +index d7d3d16..0b688a7 100644 +--- a/lib/transfer.c ++++ b/lib/transfer.c +@@ -531,6 +531,40 @@ void Curl_init_CONNECT(struct Curl_easy *data) + data->state.upload = (data->state.httpreq == HTTPREQ_PUT); + } + ++/* ++ * Restore the user credentials to those set in options. ++ */ ++CURLcode Curl_reset_userpwd(struct Curl_easy *data) ++{ ++ CURLcode result; ++ if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) ++ data->state.creds_from = CREDS_OPTION; ++ result = Curl_setstropt(&data->state.aptr.user, ++ data->set.str[STRING_USERNAME]); ++ if(!result) ++ result = Curl_setstropt(&data->state.aptr.passwd, ++ data->set.str[STRING_PASSWORD]); ++ return result; ++} ++ ++/* ++ * Restore the proxy credentials to those set in options. ++ */ ++CURLcode Curl_reset_proxypwd(struct Curl_easy *data) ++{ ++#ifndef CURL_DISABLE_PROXY ++ CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, ++ data->set.str[STRING_PROXYUSERNAME]); ++ if(!result) ++ result = Curl_setstropt(&data->state.aptr.proxypasswd, ++ data->set.str[STRING_PROXYPASSWORD]); ++ return result; ++#else ++ (void)data; ++ return CURLE_OK; ++#endif ++} ++ + /* + * Curl_pretransfer() is called immediately before a transfer starts, and only + * once for one transfer no matter if it has redirects or do multi-pass +@@ -678,23 +712,10 @@ 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]); ++ result = Curl_reset_userpwd(data); + if(!result) +- result = Curl_setstropt(&data->state.aptr.passwd, +- data->set.str[STRING_PASSWORD]); +-#ifndef CURL_DISABLE_PROXY +- if(!result) +- result = Curl_setstropt(&data->state.aptr.proxyuser, +- data->set.str[STRING_PROXYUSERNAME]); +- if(!result) +- result = Curl_setstropt(&data->state.aptr.proxypasswd, +- data->set.str[STRING_PROXYPASSWORD]); +-#endif ++ result = Curl_reset_proxypwd(data); + + data->req.headerbytecount = 0; + Curl_headers_cleanup(data); +diff --git a/lib/transfer.h b/lib/transfer.h +index 8c9b88c..8e28c01 100644 +--- a/lib/transfer.h ++++ b/lib/transfer.h +@@ -31,6 +31,8 @@ char *Curl_checkheaders(const struct Curl_easy *data, + + void Curl_init_CONNECT(struct Curl_easy *data); + ++CURLcode Curl_reset_userpwd(struct Curl_easy *data); ++CURLcode Curl_reset_proxypwd(struct Curl_easy *data); + CURLcode Curl_pretransfer(struct Curl_easy *data); + + typedef enum { +diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am +index 66dbfb7..14dc997 100644 +--- a/tests/data/Makefile.am ++++ b/tests/data/Makefile.am +@@ -225,7 +225,7 @@ test1670 test1671 \ + test1680 test1681 test1682 test1683 \ + \ + test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ +-test1708 test1709 test1710 \ ++test1708 test1709 test1710 test1715 \ + \ + test1800 test1801 \ + \ +@@ -239,7 +239,7 @@ test1955 test1956 test1957 test1958 test1959 test1960 test1964 \ + test1970 test1971 test1972 test1973 test1974 test1975 test1976 \ + \ + test2000 test2001 test2002 test2003 test2004 test2006 \ +-\ ++test2009 test2010 test2011 \ + test2023 \ + test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \ + test2032 test2033 test2034 test2035 test2037 test2038 test2039 \ +diff --git a/tests/data/test1059 b/tests/data/test1059 +index 5da753e..e6faab1 100644 +--- a/tests/data/test1059 ++++ b/tests/data/test1059 +@@ -44,7 +44,7 @@ ftp://test-number:%TESTNUMBER/wanted/page -p -x %HOSTIP:%HTTPPORT + + # The server doesn't implement CONNECT for ftp, so this must be a failure test + +-56 ++7 + + + CONNECT test-number:%TESTNUMBER HTTP/1.1 +diff --git a/tests/data/test1715 b/tests/data/test1715 +new file mode 100644 +index 0000000..a76d1c5 +--- /dev/null ++++ b/tests/data/test1715 +@@ -0,0 +1,54 @@ ++ ++ ++ ++ ++HTTP ++HTTP GET ++HTTP CONNECT ++HTTP proxy ++proxytunnel ++ ++ ++ ++ ++ ++ ++HTTP/1.1 407 Proxy Authentication Required ++Proxy-Authenticate: Special realm="none", nonce="abc123" ++Content-Length: 13 ++Transfer-Encoding: chunked ++Magic-special: true ++ ++some content ++ ++ ++ ++ ++ ++http ++ ++ ++proxy ++ ++ ++HTTP CONNECT with proxy returning Content-Length and chunked ++ ++ ++http://test.example --proxy http://%HOSTIP:%HTTPPORT --proxytunnel -sS ++ ++ ++ ++# Verify data after the test has been "shot" ++ ++ ++CONNECT test.example:80 HTTP/1.1 ++Host: test.example:80 ++User-Agent: curl/%VERSION ++Proxy-Connection: Keep-Alive ++ ++ ++ ++56 ++ ++ ++ +diff --git a/tests/data/test2009 b/tests/data/test2009 +new file mode 100644 +index 0000000..0a691e2 +--- /dev/null ++++ b/tests/data/test2009 +@@ -0,0 +1,70 @@ ++ ++ ++ ++ ++HTTP ++HTTP proxy ++http_proxy ++ ++ ++# Server-side ++ ++ ++HTTP/1.1 407 Denied ++ ++ ++ ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 4 ++Content-Type: text/html ++Location: https://another.example/%TESTNUMBER0002 ++ ++boo ++ ++ ++ ++# Client-side ++ ++ ++proxy ++ ++ ++http ++https ++ ++ ++proxy credentials via env variables, redirect from http to https ++ ++ ++ ++http_proxy=http://user:secret@%HOSTIP:%HTTPPORT ++https_proxy=https://%HOSTIP:%HTTPSPORT/ ++ ++ ++http://somewhere.example/ --location --proxy-insecure ++ ++ ++ ++# Verify data after the test has been "shot" ++ ++ ++GET http://somewhere.example/ HTTP/1.1 ++Host: somewhere.example ++Proxy-Authorization: Basic %b64[user:secret]b64% ++User-Agent: curl/%VERSION ++Accept: */* ++Proxy-Connection: Keep-Alive ++ ++CONNECT another.example:443 HTTP/1.1 ++Host: another.example:443 ++User-Agent: curl/%VERSION ++Proxy-Connection: Keep-Alive ++ ++ ++ ++7 ++ ++ ++ +diff --git a/tests/data/test2010 b/tests/data/test2010 +new file mode 100644 +index 0000000..28c27b8 +--- /dev/null ++++ b/tests/data/test2010 +@@ -0,0 +1,71 @@ ++ ++ ++ ++ ++HTTP ++HTTP proxy ++http_proxy ++ ++ ++# Server-side ++ ++ ++HTTP/1.1 407 Denied ++ ++ ++ ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 4 ++Content-Type: text/html ++Location: https://another.example/%TESTNUMBER0002 ++ ++boo ++ ++ ++ ++# Client-side ++ ++ ++proxy ++ ++ ++http ++https ++ ++ ++proxy credentials via options for two proxies, redirect from http to https ++ ++ ++ ++http_proxy=http://%HOSTIP:%HTTPPORT ++https_proxy=https://%HOSTIP:%HTTPSPORT/ ++ ++ ++--proxy-user batman:robin http://somewhere.example/ --location --proxy-insecure ++ ++ ++ ++# Verify data after the test has been "shot" ++ ++ ++GET http://somewhere.example/ HTTP/1.1 ++Host: somewhere.example ++Proxy-Authorization: Basic %b64[batman:robin]b64% ++User-Agent: curl/%VERSION ++Accept: */* ++Proxy-Connection: Keep-Alive ++ ++CONNECT another.example:443 HTTP/1.1 ++Host: another.example:443 ++Proxy-Authorization: Basic %b64[batman:robin]b64% ++User-Agent: curl/%VERSION ++Proxy-Connection: Keep-Alive ++ ++ ++ ++7 ++ ++ ++ +diff --git a/tests/data/test2011 b/tests/data/test2011 +new file mode 100644 +index 0000000..d385c58 +--- /dev/null ++++ b/tests/data/test2011 +@@ -0,0 +1,70 @@ ++ ++ ++ ++ ++HTTP ++HTTP proxy ++http_proxy ++ ++ ++# Server-side ++ ++ ++HTTP/1.1 407 Denied ++ ++ ++ ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 4 ++Content-Type: text/html ++Location: https://another.example/%TESTNUMBER0002 ++ ++boo ++ ++ ++ ++# Client-side ++ ++ ++proxy ++ ++ ++http ++https ++ ++ ++proxy creds via env, cross-scheme redirect, --location-trusted ++ ++ ++ ++http_proxy=http://user:secret@%HOSTIP:%HTTPPORT ++https_proxy=https://%HOSTIP:%HTTPSPORT/ ++ ++ ++http://somewhere.example/ --location-trusted --proxy-insecure ++ ++ ++ ++# Verify data after the test has been "shot" ++ ++ ++GET http://somewhere.example/ HTTP/1.1 ++Host: somewhere.example ++Proxy-Authorization: Basic %b64[user:secret]b64% ++User-Agent: curl/%VERSION ++Accept: */* ++Proxy-Connection: Keep-Alive ++ ++CONNECT another.example:443 HTTP/1.1 ++Host: another.example:443 ++User-Agent: curl/%VERSION ++Proxy-Connection: Keep-Alive ++ ++ ++ ++7 ++ ++ ++ +diff --git a/tests/data/test217 b/tests/data/test217 +index cb6c673..14bd784 100644 +--- a/tests/data/test217 ++++ b/tests/data/test217 +@@ -48,9 +48,9 @@ User-Agent: curl/%VERSION + Proxy-Connection: Keep-Alive + + +-# CURLE_RECV_ERROR ++# CURLE_COULDNT_CONNECT + +-56 ++7 + + + HTTP/1.1 405 Method Not Allowed swsclose +diff --git a/tests/data/test287 b/tests/data/test287 +index 85d557e..7c32805 100644 +--- a/tests/data/test287 ++++ b/tests/data/test287 +@@ -44,9 +44,9 @@ Proxy-Connection: Keep-Alive + User-Agent: looser/2007 + + +-# CURLE_RECV_ERROR ++# CURLE_COULDNT_CONNECT + +-56 ++7 + + + HTTP/1.1 405 Method Not Allowed swsclose +diff --git a/tests/data/test302 b/tests/data/test302 +index f6f33c2..ddeb348 100644 +--- a/tests/data/test302 ++++ b/tests/data/test302 +@@ -41,7 +41,7 @@ HTTPS GET over HTTP proxy fails + # Verify data after the test has been "shot" + + +-56 ++7 + + + +diff --git a/tests/data/test440 b/tests/data/test440 +index ce98929..1278810 100644 +--- a/tests/data/test440 ++++ b/tests/data/test440 +@@ -71,7 +71,7 @@ https://this.hsts.example./%TESTNUMBER + + # Proxy CONNECT aborted + +-56 ++7 + + + +diff --git a/tests/data/test441 b/tests/data/test441 +index 02aca4d..929c538 100644 +--- a/tests/data/test441 ++++ b/tests/data/test441 +@@ -70,7 +70,7 @@ https://this.hsts.example/%TESTNUMBER + + # Proxy CONNECT aborted + +-56 ++7 + + + +diff --git a/tests/data/test445 b/tests/data/test445 +index dbdcfe6..29d0ba0 100644 +--- a/tests/data/test445 ++++ b/tests/data/test445 +@@ -36,7 +36,6 @@ pop3 + rtsp + scp + sftp +-smb + smtp + + +@@ -46,7 +45,7 @@ http-proxy + Refuse tunneling protocols through HTTP proxy + + +--x http://%HOSTIP:%PROXYPORT/%TESTNUMBER -p gopher://127.0.0.1 dict://127.0.0.1 http://moo https://example telnet://another ftp://yes ftps://again imap://more ldap://perhaps mqtt://yes pop3://mail rtsp://harder scp://copy sftp://files smb://wird smtp://send ++-x http://%HOSTIP:%PROXYPORT/%TESTNUMBER -p gopher://127.0.0.1 dict://127.0.0.1 http://moo https://example telnet://another ftp://yes ftps://again imap://more ldap://perhaps mqtt://yes pop3://mail rtsp://harder scp://copy sftp://files --insecure + + + +@@ -55,7 +54,7 @@ Refuse tunneling protocols through HTTP proxy + + # refused in the CONNECT + +-56 ++7 + + + +diff --git a/tests/data/test493 b/tests/data/test493 +index d157f03..9bda00d 100644 +--- a/tests/data/test493 ++++ b/tests/data/test493 +@@ -69,7 +69,7 @@ https://this.hsts.example/%TESTNUMBER + + # Proxy CONNECT aborted + +-56 ++7 + + + +diff --git a/tests/data/test718 b/tests/data/test718 +index dcbf7d7..8c98bf4 100644 +--- a/tests/data/test718 ++++ b/tests/data/test718 +@@ -55,7 +55,7 @@ Proxy-Connection: Keep-Alive + + + +-56 ++7 + + + +diff --git a/tests/data/test94 b/tests/data/test94 +index eb745d1..74200a0 100644 +--- a/tests/data/test94 ++++ b/tests/data/test94 +@@ -43,7 +43,7 @@ https://test.anything.really.com:%TESTNUMBER --proxy1.0 %HOSTIP:%HTTPPORT + # Verify data after the test has been "shot" + + +-56 ++7 + + + CONNECT test.anything.really.com:%TESTNUMBER HTTP/1.0 +diff --git a/tests/http/test_13_proxy_auth.py b/tests/http/test_13_proxy_auth.py +index f2cda54..8ccf88c 100644 +--- a/tests/http/test_13_proxy_auth.py ++++ b/tests/http/test_13_proxy_auth.py +@@ -105,7 +105,7 @@ class TestProxyAuth: + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + extra_args=xargs) + # expect "COULD_NOT_CONNECT" +- r.check_response(exitcode=56, http_status=None) ++ r.check_response(exitcode=7, http_status=None) + + def test_13_06_tunnel_http_auth(self, env: Env, httpd, repeat): + curl = CurlClient(env=env) +@@ -130,7 +130,7 @@ class TestProxyAuth: + r = curl.http_download(urls=[url], alpn_proto=proto, with_stats=True, + extra_args=xargs) + # expect "COULD_NOT_CONNECT" +- r.check_response(exitcode=56, http_status=None) ++ r.check_response(exitcode=7, http_status=None) + assert self.get_tunnel_proto_used(r) == 'HTTP/2' \ + if tunnel == 'h2' else 'HTTP/1.1' + +-- +2.45.4 + diff --git a/SPECS/curl/CVE-2026-6429.patch b/SPECS/curl/CVE-2026-6429.patch new file mode 100644 index 00000000000..df021bc8a1f --- /dev/null +++ b/SPECS/curl/CVE-2026-6429.patch @@ -0,0 +1,461 @@ +From b4024bf808bd558026fdc6096e8457f199ace306 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Thu, 16 Apr 2026 14:26:20 +0200 +Subject: [PATCH] http: clear credentials better on redirect + +Verify with test 2506: netrc with redirect using proxy + +Updated test 998 which was wrong. + +Reported-by: Muhamad Arga Reksapati + +Closes #21345 +Upstream Patch Reference: https://launchpadlibrarian.net/859770351/curl_8.14.1-2ubuntu1.2_8.14.1-2ubuntu1.3.diff.gz +https://github.com/curl/curl/commit/b4024bf808bd558026fdc6.patch +--- + lib/http2.c | 22 +++++++---- + lib/multi.c | 80 ++++++++++++-------------------------- + lib/urlapi-int.h | 2 + + lib/urlapi.c | 33 ++++++++++++++++ + tests/data/Makefile.am | 2 +- + tests/data/test2506 | 64 ++++++++++++++++++++++++++++++ + tests/data/test998 | 29 +++++++------- + tests/libtest/Makefile.inc | 5 ++- + tests/libtest/lib2506.c | 71 +++++++++++++++++++++++++++++++++ + 9 files changed, 229 insertions(+), 79 deletions(-) + create mode 100644 tests/data/test2506 + create mode 100644 tests/libtest/lib2506.c + +diff --git a/lib/http2.c b/lib/http2.c +index dbe6f1a..ef29686 100644 +--- a/lib/http2.c ++++ b/lib/http2.c +@@ -883,8 +883,9 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, + return second; + } + +-static int set_transfer_url(struct Curl_easy *data, +- struct curl_pushheaders *hp) ++static int set_transfer_url(struct Curl_easy *newhandle, ++ struct curl_pushheaders *hp, ++ struct Curl_easy *data) + { + const char *v; + CURLUcode uc; +@@ -922,6 +923,13 @@ static int set_transfer_url(struct Curl_easy *data, + } + } + ++ /* We can only allow PUSH of resource from the same origin, e.g. ++ * scheme + hostname + port */ ++ if(!Curl_url_same_origin(data->state.uh, u)) { ++ rc = 1; ++ goto fail; ++ } ++ + uc = curl_url_get(u, CURLUPART_URL, &url, 0); + if(uc) + rc = 4; +@@ -930,10 +938,10 @@ fail: + if(rc) + return rc; + +- if(data->state.url_alloc) +- free(data->state.url); +- data->state.url_alloc = TRUE; +- data->state.url = url; ++ if(newhandle->state.url_alloc) ++ free(newhandle->state.url); ++ newhandle->state.url_alloc = TRUE; ++ newhandle->state.url = url; + return 0; + } + +@@ -982,7 +990,7 @@ static int push_promise(struct Curl_cfilter *cf, + heads.stream = stream; + heads.frame = frame; + +- rv = set_transfer_url(newhandle, &heads); ++ rv = set_transfer_url(newhandle, &heads, data); + if(rv) { + discard_newhandle(cf, newhandle); + rv = CURL_PUSH_DENY; +diff --git a/lib/multi.c b/lib/multi.c +index 53ab80e..57f7649 100644 +--- a/lib/multi.c ++++ b/lib/multi.c +@@ -1932,71 +1932,41 @@ static CURLcode multi_follow(struct Curl_easy *data, + return CURLE_OUT_OF_MEMORY; + } + else { +- uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); +- if(uc) ++ bool same_origin; ++ CURLcode result; ++ CURLU *u = curl_url(); ++ if(!u) ++ return CURLE_OUT_OF_MEMORY; ++ uc = curl_url_set(u, CURLUPART_URL, ++ data->state.url, ++ CURLU_URLENCODE | CURLU_ALLOW_SPACE); ++ if(!uc) ++ uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); ++ if(uc) { ++ curl_url_cleanup(u); + return Curl_uc_to_curlcode(uc); ++ } + +- /* Clear auth if this redirects to a different port number or protocol, +- unless permitted */ +- if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { +- char *portnum; +- int port; +- bool clear = FALSE; +- +- if(data->set.use_port && data->state.allow_port) +- /* a custom port is used */ +- port = (int)data->set.use_port; +- else { +- uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, +- CURLU_DEFAULT_PORT); +- if(uc) { +- free(newurl); +- return Curl_uc_to_curlcode(uc); +- } +- port = atoi(portnum); +- free(portnum); +- } +- if(port != data->info.conn_remote_port) { +- infof(data, "Clear auth, redirects to port from %u to %u", +- data->info.conn_remote_port, port); +- clear = TRUE; +- } +- else { +- char *scheme; +- const struct Curl_handler *p; +- uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); +- if(uc) { +- free(newurl); +- return Curl_uc_to_curlcode(uc); +- } ++ same_origin = Curl_url_same_origin(u, data->state.uh); ++ curl_url_cleanup(u); + +- p = Curl_get_scheme_handler(scheme); +- if(p && (p->protocol != data->info.conn_protocol)) { +- infof(data, "Clear auth, redirects scheme from %s to %s", +- data->info.conn_scheme, scheme); +- clear = TRUE; +- } +- free(scheme); +- } +- if(clear) { +- CURLcode result = Curl_reset_userpwd(data); +- if(result) { +- free(newurl); +- return result; +- } +- Curl_safefree(data->state.aptr.user); +- Curl_safefree(data->state.aptr.passwd); ++ if((!same_origin && !data->set.allow_auth_to_other_hosts) || ++ !data->set.str[STRING_USERNAME]) { ++ result = Curl_reset_userpwd(data); ++ if(result) { ++ free(newurl); ++ return result; + } ++ Curl_safefree(data->state.aptr.user); ++ Curl_safefree(data->state.aptr.passwd); + } +- } +- DEBUGASSERT(newurl); +- { +- CURLcode result = Curl_reset_proxypwd(data); ++ result = Curl_reset_proxypwd(data); + if(result) { + free(newurl); + return result; + } + } ++ DEBUGASSERT(newurl); + + if(type == FOLLOW_FAKE) { + /* we are only figuring out the new URL if we would have followed locations +diff --git a/lib/urlapi-int.h b/lib/urlapi-int.h +index fcffab2..467f405 100644 +--- a/lib/urlapi-int.h ++++ b/lib/urlapi-int.h +@@ -35,4 +35,6 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme); + #endif + ++bool Curl_url_same_origin(CURLU *base, CURLU *href); ++ + #endif /* HEADER_CURL_URLAPI_INT_H */ +diff --git a/lib/urlapi.c b/lib/urlapi.c +index 98c8f6f..6533e8a 100644 +--- a/lib/urlapi.c ++++ b/lib/urlapi.c +@@ -2013,3 +2013,36 @@ nomem: + } + return CURLUE_OK; + } ++ ++bool Curl_url_same_origin(CURLU *base, CURLU *href) ++{ ++ const struct Curl_handler *s = NULL; ++ ++ /* base must be an absolute URL */ ++ if(!base->scheme || !base->host) ++ return FALSE; ++ if(href->scheme && !curl_strequal(base->scheme, href->scheme)) ++ return FALSE; ++ if(href->host) { ++ if(!curl_strequal(base->host, href->host)) ++ return FALSE; ++ if(!curl_strequal(base->port, href->port)) { ++ /* This may still match if only one has an explicit port ++ * and it is the default for the scheme. */ ++ if(base->port && href->port) ++ return FALSE; ++ ++ s = Curl_get_scheme_handler(base->scheme); ++ if(!s) /* Cannot match default port for unknown scheme */ ++ return FALSE; ++ ++ /* The port which is set must be the default one */ ++ if((base->port && (base->portnum != s->defport)) || ++ (href->port && (href->portnum != s->defport))) ++ return FALSE; ++ } ++ } ++ else if(href->port) /* no host in href, then there must be no port */ ++ return FALSE; ++ return TRUE; ++} +diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am +index 14dc997..3cdbdd7 100644 +--- a/tests/data/Makefile.am ++++ b/tests/data/Makefile.am +@@ -259,7 +259,7 @@ test2308 test2309 \ + \ + test2400 test2401 test2402 test2403 test2404 test2405 test2406 \ + \ +-test2500 test2501 test2502 test2503 test2504 \ ++test2500 test2501 test2502 test2503 test2504 test2506 \ + \ + test2600 test2601 test2602 test2603 test2604 \ + \ +diff --git a/tests/data/test2506 b/tests/data/test2506 +new file mode 100644 +index 0000000..9c65002 +--- /dev/null ++++ b/tests/data/test2506 +@@ -0,0 +1,64 @@ ++ ++ ++ ++ ++HTTP ++cookies ++ ++ ++ ++ ++ ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Content-Length: 3 ++Location: http://numbertwo.example/%TESTNUMBER0002 ++ ++ok ++ ++ ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Content-Length: 4 ++ ++yes ++ ++ ++ ++ ++ ++http ++ ++ ++proxy ++ ++ ++lib%TESTNUMBER ++ ++ ++netrc with redirect using proxy ++ ++ ++machine site.example login batman password robin ++ ++ ++http://%HOSTIP:%HTTPPORT http://site.example/ %LOGDIR/netrc2506 ++ ++ ++ ++ ++ ++GET http://site.example/ HTTP/1.1 ++Host: site.example ++Authorization: Basic %b64[batman:robin]b64% ++Accept: */* ++Proxy-Connection: Keep-Alive ++ ++GET http://numbertwo.example/25060002 HTTP/1.1 ++Host: numbertwo.example ++Accept: */* ++Proxy-Connection: Keep-Alive ++ ++ ++ ++ +diff --git a/tests/data/test998 b/tests/data/test998 +index c3a8f51..6b25f79 100644 +--- a/tests/data/test998 ++++ b/tests/data/test998 +@@ -72,21 +72,20 @@ HTTP with auth in URL redirected to another host + + QUIT + +- +-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 +- ++ ++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 ++User-Agent: curl/%VERSION ++Accept: */* ++Proxy-Connection: Keep-Alive ++ + + + +diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc +index 99cbc18..49e568e 100644 +--- a/tests/libtest/Makefile.inc ++++ b/tests/libtest/Makefile.inc +@@ -79,7 +79,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \ + lib1970 lib1971 lib1972 lib1973 lib1974 lib1975 \ + lib2301 lib2302 lib2304 lib2305 lib2306 lib2308 lib2309 \ + lib2402 lib2404 lib2405 \ +- lib2502 lib2504 \ ++ lib2502 lib2504 lib2506 \ + lib3010 lib3025 lib3026 lib3027 \ + lib3100 lib3101 lib3102 lib3103 lib3207 + +@@ -704,6 +704,9 @@ lib2502_LDADD = $(TESTUTIL_LIBS) + lib2504_SOURCES = lib2504.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) + lib2504_LDADD = $(TESTUTIL_LIBS) + ++lib2506_SOURCES = lib2506.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) ++lib2506_LDADD = $(TESTUTIL_LIBS) ++ + lib3010_SOURCES = lib3010.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) + lib3010_LDADD = $(TESTUTIL_LIBS) + +diff --git a/tests/libtest/lib2506.c b/tests/libtest/lib2506.c +new file mode 100644 +index 0000000..a09e16a +--- /dev/null ++++ b/tests/libtest/lib2506.c +@@ -0,0 +1,71 @@ ++/*************************************************************************** ++ * _ _ ____ _ ++ * Project ___| | | | _ \| | ++ * / __| | | | |_) | | ++ * | (__| |_| | _ <| |___ ++ * \___|\___/|_| \_\_____| ++ * ++ * Copyright (C) Linus Nielsen Feltzing ++ * ++ * 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" ++ ++static size_t sink2506(char *ptr, size_t size, size_t nmemb, void *ud) ++{ ++ (void)ptr; ++ (void)ud; ++ return size * nmemb; ++} ++ ++CURLcode test(char *URL) ++{ ++ CURL *curl; ++ CURLcode res = CURLE_OUT_OF_MEMORY; ++ ++ if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { ++ curl_mfprintf(stderr, "curl_global_init() failed\n"); ++ return TEST_ERR_MAJOR_BAD; ++ } ++ ++ curl = curl_easy_init(); ++ if(!curl) { ++ curl_mfprintf(stderr, "curl_easy_init() failed\n"); ++ curl_global_cleanup(); ++ return TEST_ERR_MAJOR_BAD; ++ } ++ ++ test_setopt(curl, CURLOPT_WRITEFUNCTION, sink2506); ++ test_setopt(curl, CURLOPT_PROXY, URL); ++ test_setopt(curl, CURLOPT_URL, libtest_arg2); ++ test_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); ++ test_setopt(curl, CURLOPT_NETRC_FILE, libtest_arg3); ++ test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); ++ test_setopt(curl, CURLOPT_VERBOSE, 1L); ++ ++ /* CURLOPT_UNRESTRICTED_AUTH should not make a difference because the ++ credentials come from netrc */ ++ test_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1L); ++ ++ res = curl_easy_perform(curl); ++ ++test_cleanup: ++ curl_easy_cleanup(curl); ++ curl_global_cleanup(); ++ ++ return res; ++} +-- +2.45.4 + diff --git a/SPECS/curl/curl.spec b/SPECS/curl/curl.spec index f164a0bb01a..3d7b38581ee 100644 --- a/SPECS/curl/curl.spec +++ b/SPECS/curl/curl.spec @@ -1,7 +1,7 @@ Summary: An URL retrieval utility and library Name: curl Version: 8.11.1 -Release: 7%{?dist} +Release: 8%{?dist} License: curl Vendor: Microsoft Corporation Distribution: Azure Linux @@ -19,6 +19,9 @@ Patch7: CVE-2026-3784.patch Patch8: CVE-2026-4873.patch Patch9: CVE-2026-6276.patch Patch10: CVE-2026-7168.patch +Patch11: CVE-2026-5545.patch +Patch12: CVE-2026-6253.patch +Patch13: CVE-2026-6429.patch BuildRequires: cmake BuildRequires: krb5-devel BuildRequires: libnghttp2-devel @@ -109,6 +112,9 @@ find %{buildroot} -type f -name "*.la" -delete -print %{_libdir}/libcurl.so.* %changelog +* Tue May 26 2026 Jyoti Kanase - 8.11.1-8 +- Patch for CVE-2026-6253, CVE-2026-6429, CVE-2026-5545 + * Thu May 14 2026 Azure Linux Security Servicing Account - 8.11.1-7 - Patch for CVE-2026-7168, CVE-2026-6276, CVE-2026-4873 diff --git a/toolkit/resources/manifests/package/pkggen_core_aarch64.txt b/toolkit/resources/manifests/package/pkggen_core_aarch64.txt index be154837446..8e165b6a37c 100644 --- a/toolkit/resources/manifests/package/pkggen_core_aarch64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_aarch64.txt @@ -199,9 +199,9 @@ krb5-1.21.3-4.azl3.aarch64.rpm krb5-devel-1.21.3-4.azl3.aarch64.rpm nghttp2-1.61.0-3.azl3.aarch64.rpm nghttp2-devel-1.61.0-3.azl3.aarch64.rpm -curl-8.11.1-7.azl3.aarch64.rpm -curl-devel-8.11.1-7.azl3.aarch64.rpm -curl-libs-8.11.1-7.azl3.aarch64.rpm +curl-8.11.1-8.azl3.aarch64.rpm +curl-devel-8.11.1-8.azl3.aarch64.rpm +curl-libs-8.11.1-8.azl3.aarch64.rpm createrepo_c-1.0.3-1.azl3.aarch64.rpm libxml2-2.11.5-9.azl3.aarch64.rpm libxml2-devel-2.11.5-9.azl3.aarch64.rpm diff --git a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt index ea28ddbf634..ac3640e9525 100644 --- a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt @@ -199,9 +199,9 @@ krb5-1.21.3-4.azl3.x86_64.rpm krb5-devel-1.21.3-4.azl3.x86_64.rpm nghttp2-1.61.0-3.azl3.x86_64.rpm nghttp2-devel-1.61.0-3.azl3.x86_64.rpm -curl-8.11.1-7.azl3.x86_64.rpm -curl-devel-8.11.1-7.azl3.x86_64.rpm -curl-libs-8.11.1-7.azl3.x86_64.rpm +curl-8.11.1-8.azl3.x86_64.rpm +curl-devel-8.11.1-8.azl3.x86_64.rpm +curl-libs-8.11.1-8.azl3.x86_64.rpm createrepo_c-1.0.3-1.azl3.x86_64.rpm libxml2-2.11.5-9.azl3.x86_64.rpm libxml2-devel-2.11.5-9.azl3.x86_64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_aarch64.txt b/toolkit/resources/manifests/package/toolchain_aarch64.txt index 0b6c7843bea..d18604dce9b 100644 --- a/toolkit/resources/manifests/package/toolchain_aarch64.txt +++ b/toolkit/resources/manifests/package/toolchain_aarch64.txt @@ -67,10 +67,10 @@ cracklib-lang-2.9.11-1.azl3.aarch64.rpm createrepo_c-1.0.3-1.azl3.aarch64.rpm createrepo_c-debuginfo-1.0.3-1.azl3.aarch64.rpm createrepo_c-devel-1.0.3-1.azl3.aarch64.rpm -curl-8.11.1-7.azl3.aarch64.rpm -curl-debuginfo-8.11.1-7.azl3.aarch64.rpm -curl-devel-8.11.1-7.azl3.aarch64.rpm -curl-libs-8.11.1-7.azl3.aarch64.rpm +curl-8.11.1-8.azl3.aarch64.rpm +curl-debuginfo-8.11.1-8.azl3.aarch64.rpm +curl-devel-8.11.1-8.azl3.aarch64.rpm +curl-libs-8.11.1-8.azl3.aarch64.rpm Cython-debuginfo-3.0.5-3.azl3.aarch64.rpm debugedit-5.0-2.azl3.aarch64.rpm debugedit-debuginfo-5.0-2.azl3.aarch64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_x86_64.txt b/toolkit/resources/manifests/package/toolchain_x86_64.txt index 46fd57f67aa..8dd8b03147e 100644 --- a/toolkit/resources/manifests/package/toolchain_x86_64.txt +++ b/toolkit/resources/manifests/package/toolchain_x86_64.txt @@ -72,10 +72,10 @@ createrepo_c-debuginfo-1.0.3-1.azl3.x86_64.rpm createrepo_c-devel-1.0.3-1.azl3.x86_64.rpm cross-binutils-common-2.41-13.azl3.noarch.rpm cross-gcc-common-13.2.0-7.azl3.noarch.rpm -curl-8.11.1-7.azl3.x86_64.rpm -curl-debuginfo-8.11.1-7.azl3.x86_64.rpm -curl-devel-8.11.1-7.azl3.x86_64.rpm -curl-libs-8.11.1-7.azl3.x86_64.rpm +curl-8.11.1-8.azl3.x86_64.rpm +curl-debuginfo-8.11.1-8.azl3.x86_64.rpm +curl-devel-8.11.1-8.azl3.x86_64.rpm +curl-libs-8.11.1-8.azl3.x86_64.rpm Cython-debuginfo-3.0.5-3.azl3.x86_64.rpm debugedit-5.0-2.azl3.x86_64.rpm debugedit-debuginfo-5.0-2.azl3.x86_64.rpm