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 Closes #21304 --- lib/http.c | 12 +++++++ lib/transfer.c | 51 +++++++++++++++++++++--------- lib/transfer.h | 2 ++ tests/data/Makefile.am | 2 +- tests/data/test2009 | 70 +++++++++++++++++++++++++++++++++++++++++ tests/data/test2010 | 71 ++++++++++++++++++++++++++++++++++++++++++ tests/data/test2011 | 70 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 262 insertions(+), 16 deletions(-) create mode 100644 tests/data/test2009 create mode 100644 tests/data/test2010 create mode 100644 tests/data/test2011 diff --git a/lib/http.c b/lib/http.c index a1e58e886c..f976987111 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1278,12 +1278,24 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, curlx_free(scheme); } if(clear) { + CURLcode result = Curl_reset_userpwd(data); + if(result) { + curlx_free(follow_url); + return result; + } curlx_safefree(data->state.aptr.user); curlx_safefree(data->state.aptr.passwd); } } } DEBUGASSERT(follow_url); + { + CURLcode result = Curl_reset_proxypwd(data); + if(result) { + curlx_free(follow_url); + 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 dd4e16b7f5..a2fce9331b 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -438,6 +438,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 @@ -586,23 +620,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 41ec0357f6..b29e70b9ec 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); CURLcode Curl_sendrecv(struct Curl_easy *data); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 44ff75668f..3003dce03b 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -244,7 +244,7 @@ test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \ test1978 test1979 test1980 test1981 test1982 test1983 test1984 \ \ test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ -test2008 \ +test2008 test2009 test2010 test2011 \ \ test2023 \ test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \ diff --git a/tests/data/test2009 b/tests/data/test2009 new file mode 100644 index 0000000000..d2fd79e0d6 --- /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/ --follow --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 0000000000..443ae9d2f9 --- /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/ --follow --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 0000000000..dd4e534248 --- /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 + + +