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
This commit is contained in:
Daniel Stenberg 2026-04-13 17:17:23 +02:00
parent 9ceb3ff46a
commit 188c2f166a
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
7 changed files with 262 additions and 16 deletions

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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 \

70
tests/data/test2009 Normal file
View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
HTTP
HTTP proxy
http_proxy
</keywords>
</info>
# Server-side
<reply>
<connect>
HTTP/1.1 407 Denied
</connect>
<data crlf="headers" nocheck="yes">
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
</data>
</reply>
# Client-side
<client>
<features>
proxy
</features>
<server>
http
https
</server>
<name>
proxy credentials via env variables, redirect from http to https
</name>
<setenv>
http_proxy=http://user:secret@%HOSTIP:%HTTPPORT
https_proxy=https://%HOSTIP:%HTTPSPORT/
</setenv>
<command>
http://somewhere.example/ --follow --proxy-insecure
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol crlf="headers">
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
</protocol>
<errorcode>
7
</errorcode>
</verify>
</testcase>

71
tests/data/test2010 Normal file
View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
HTTP
HTTP proxy
http_proxy
</keywords>
</info>
# Server-side
<reply>
<connect>
HTTP/1.1 407 Denied
</connect>
<data crlf="headers" nocheck="yes">
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
</data>
</reply>
# Client-side
<client>
<features>
proxy
</features>
<server>
http
https
</server>
<name>
proxy credentials via options for two proxies, redirect from http to https
</name>
<setenv>
http_proxy=http://%HOSTIP:%HTTPPORT
https_proxy=https://%HOSTIP:%HTTPSPORT/
</setenv>
<command>
--proxy-user batman:robin http://somewhere.example/ --follow --proxy-insecure
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol crlf="headers">
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
</protocol>
<errorcode>
7
</errorcode>
</verify>
</testcase>

70
tests/data/test2011 Normal file
View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
HTTP
HTTP proxy
http_proxy
</keywords>
</info>
# Server-side
<reply>
<connect>
HTTP/1.1 407 Denied
</connect>
<data crlf="headers" nocheck="yes">
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
</data>
</reply>
# Client-side
<client>
<features>
proxy
</features>
<server>
http
https
</server>
<name>
proxy creds via env, cross-scheme redirect, --location-trusted
</name>
<setenv>
http_proxy=http://user:secret@%HOSTIP:%HTTPPORT
https_proxy=https://%HOSTIP:%HTTPSPORT/
</setenv>
<command>
http://somewhere.example/ --location-trusted --proxy-insecure
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol crlf="headers">
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
</protocol>
<errorcode>
7
</errorcode>
</verify>
</testcase>