From: Daniel Stenberg Date: Mon, 18 May 2026 23:47:11 +0200 Subject: [PATCH] url: detect proxy changes read from environment When a proxy is set from an environment variable, detect if that proxy is not the same as previously and flush state. Verified by test1647: verify changing proxy with env variables and make sure Digest state is flushed in the second use Closes #21666 Backported-by: Rafael Passos * Refresh patch context for lib/url.c tests/data/Makefile.am tests/libtest/Makefile.inc * Replace lib imports in /tests/libtest/lib1647.c upstream changed the testing tools to a new package. The neede functions are in tesutil.h and test.h. * Updated section in test1647 to include the Proxy-Authorization header with the new credentials after proxy change * replace crlf=headers by crlf=yes in test file, add manual crlf termination in test expectation * Change expected error code from 7 to 56 in test1647. This changed in upstream in commit a186ecf4bf, not present in this version. Same as in tests like test217 and test1059 * remove the "digest" feature from the test1647, as done in previous backports. This feature gate is not available in this version of curl. * s/result/res/ in /tests/libtest/lib1647.c, this varable is used in macros. Signed-off-by: Rafael Passos --- lib/url.c | 14 ++++++ lib/urldata.h | 1 + tests/data/Makefile.am | 1 + tests/data/test1647 | 102 ++++++++++++++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 4 ++ tests/libtest/lib1647.c | 121 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 tests/data/test1647 create mode 100644 tests/libtest/lib1647.c diff --git a/lib/url.c b/lib/url.c index 2aa8ce8..5b7c11e 100644 --- a/lib/url.c +++ b/lib/url.c @@ -100,6 +100,7 @@ #include "telnet.h" #include "tftp.h" #include "http.h" +#include "vauth/vauth.h" #include "http2.h" #include "file.h" #include "curl_ldap.h" @@ -339,6 +340,9 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_freeset(data); Curl_headers_cleanup(data); Curl_netrc_cleanup(&data->state.netrc); +#ifndef CURL_DISABLE_DIGEST_AUTH + free(data->state.envproxy); +#endif free(data); return CURLE_OK; } @@ -2635,6 +2639,16 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, curl_proxytype ptype = (curl_proxytype)conn->http_proxy.proxytype; if(proxy) { result = parse_proxy(data, conn, proxy, ptype); + if(!result && conn->http_proxy.host.rawalloc) { +#ifndef CURL_DISABLE_DIGEST_AUTH + if(!Curl_safecmp(data->state.envproxy, proxy)) { + /* proxy changed */ + Curl_auth_digest_cleanup(&data->state.proxydigest); + free(data->state.envproxy); + data->state.envproxy = strdup(proxy); + } +#endif + } Curl_safefree(proxy); /* parse_proxy copies the proxy string */ if(result) goto out; diff --git a/lib/urldata.h b/lib/urldata.h index 4ddb0f7..82526b0 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1147,6 +1147,7 @@ struct UrlState { void (*prev_signal)(int sig); #endif #ifndef CURL_DISABLE_DIGEST_AUTH + char *envproxy; /* last proxy string used for proxy-related state */ struct digestdata digest; /* state data for host Digest auth */ struct digestdata proxydigest; /* state data for proxy Digest auth */ #endif diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index b7e60f7..91ba016 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -220,6 +220,7 @@ test1620 test1621 \ \ test1629 \ test1630 test1631 test1632 test1633 test1634 test1635 \ +test1647 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 \ diff --git a/tests/data/test1647 b/tests/data/test1647 new file mode 100644 index 0000000..d550264 --- /dev/null +++ b/tests/data/test1647 @@ -0,0 +1,102 @@ + + + + +HTTP +HTTP GET +HTTP proxy +HTTP proxy Digest auth +multi + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +And you should ignore this data. + + +# then this is returned when we get proxy-auth + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! + + + +HTTP/1.1 401 OK +Content-Length: 21 +Server: no + +Denied access. Leave + + + + +# Client-side + + +http +https-proxy +https + +# tool is what to use instead of 'curl' + +lib%TESTNUMBER + + +!SSPI +crypto +proxy +Debug + + +http_proxy=%HOSTIP:%HTTPPORT +https_proxy=https://%HOSTIP:%HTTPSPROXYPORT +CURL_ENTROPY=99376 + + +HTTP proxy auth Digest, then change proxy with env var and do it again + + +http://test.remote.example.com/path/%TESTNUMBER https://another.example.com:%HTTPSPORT/ daniel:monkey123 another:bump456 + + + +# Verify data after the test has been "shot" + + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Proxy-Authorization: Digest username="daniel", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="7a1672891aff03248887b1a6674b8096" +Accept: */* +Proxy-Connection: Keep-Alive + + + + +CONNECT another.example.com:%HTTPSPORT HTTP/1.1 +Host: another.example.com:%HTTPSPORT +Proxy-Connection: Keep-Alive + + + +# CONNECT fails + +56 + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 284be0c..8078d78 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -66,6 +66,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \ lib1576 lib1578 lib1588 \ lib1591 lib1592 lib1593 lib1594 lib1596 lib1597 lib1598 \ \ + lib1647 \ lib1662 \ \ lib1900 lib1901 \ @@ -566,6 +567,9 @@ lib1597_LDADD = $(TESTUTIL_LIBS) lib1598_SOURCES = lib1598.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1598_LDADD = $(TESTUTIL_LIBS) +lib1647_SOURCES = lib1647.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) +lib1647_LDADD = $(TESTUTIL_LIBS) + lib1662_SOURCES = lib1662.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1662_LDADD = $(TESTUTIL_LIBS) diff --git a/tests/libtest/lib1647.c b/tests/libtest/lib1647.c new file mode 100644 index 0000000..ea11e9c --- /dev/null +++ b/tests/libtest/lib1647.c @@ -0,0 +1,121 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , 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 + * + ***************************************************************************/ +/* + * argv1 = the first URL + * argv2 = URL2 + * argv3 = credentials 1 + * argv4 = credentials 2 + */ + +#include "testutil.h" +#include "test.h" + +/* this is meant to pick up the proxy from the environment variable */ +static CURLcode init1647(CURL *curl, const char *url, const char *userpwd) +{ + CURLcode res = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(res) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd); + if(res) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); + if(res) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + if(res) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + if(res) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L); + if(res) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L); + if(res) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(res) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return res; /* failure */ +} + +static CURLcode run1647(CURL *curl, const char *url, const char *userpwd) +{ + CURLcode res = CURLE_OK; + + res = init1647(curl, url, userpwd); + if(res) + return res; + + return curl_easy_perform(curl); +} + +CURLcode test(char *URL) +{ + CURLcode res = CURLE_OK; + CURL *curl = NULL; + + res_global_init(CURL_GLOBAL_ALL); + if(res) + return res; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + start_test_timing(); + + curl_mprintf("--- First get '%s'\n", URL); + res = run1647(curl, URL, libtest_arg3); + if(res) + goto test_cleanup; + + curl_mprintf("--- Then get '%s'\n", libtest_arg2); + res = run1647(curl, libtest_arg2, libtest_arg4); + +test_cleanup: + + /* proper cleanup sequence - type PB */ + + curl_easy_cleanup(curl); + curl_global_cleanup(); + return res; +}