From 5c225384b8d52c67ce8259c6e4203bc57aacb567 Mon Sep 17 00:00:00 2001 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 --- lib/url.c | 11 ++++ lib/urldata.h | 1 + tests/data/Makefile.am | 8 +-- tests/data/test1647 | 103 +++++++++++++++++++++++++++++++ tests/libtest/Makefile.inc | 1 + tests/libtest/lib1647.c | 120 +++++++++++++++++++++++++++++++++++++ 6 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 tests/data/test1647 create mode 100644 tests/libtest/lib1647.c diff --git a/lib/url.c b/lib/url.c index a569c3e4bc..31f5d948d8 100644 --- a/lib/url.c +++ b/lib/url.c @@ -305,6 +305,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 + curlx_free(data->state.envproxy); +#endif curlx_free(data); return CURLE_OK; } @@ -2007,6 +2010,14 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, result = CURLE_UNSUPPORTED_PROTOCOL; goto out; #else +#ifndef CURL_DISABLE_DIGEST_AUTH + if(!Curl_safecmp(data->state.envproxy, proxy)) { + /* proxy changed */ + Curl_auth_digest_cleanup(&data->state.proxydigest); + curlx_free(data->state.envproxy); + data->state.envproxy = curlx_strdup(proxy); + } +#endif /* force this connection's protocol to become HTTP if compatible */ if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) && diff --git a/lib/urldata.h b/lib/urldata.h index 8b85c674a9..85630b05b7 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -671,6 +671,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 b330af3b90..a3778bdad1 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -214,11 +214,9 @@ test1596 test1597 test1598 test1599 test1600 test1601 test1602 test1603 \ test1604 test1605 test1606 test1607 test1608 test1609 test1610 test1611 \ test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 \ test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 \ -test1628 test1629 \ -\ -test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \ -test1638 test1639 test1640 test1641 test1642 test1643 test1644 test1645 \ -test1646 \ +test1628 test1629 test1630 test1631 test1632 test1633 test1634 test1635 \ +test1636 test1637 test1638 test1639 test1640 test1641 test1642 test1643 \ +test1644 test1645 test1646 test1647 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ diff --git a/tests/data/test1647 b/tests/data/test1647 new file mode 100644 index 0000000000..a87487fa9f --- /dev/null +++ b/tests/data/test1647 @@ -0,0 +1,103 @@ + + + + +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 +digest +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 + +7 + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 734e7f30e9..586db5a2c9 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -99,6 +99,7 @@ TESTS_C = \ lib1576.c lib1582.c lib1587.c lib1588.c lib1589.c \ lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ lib1598.c lib1599.c \ + lib1647.c \ lib1662.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ diff --git a/tests/libtest/lib1647.c b/tests/libtest/lib1647.c new file mode 100644 index 0000000000..8060e1bfe9 --- /dev/null +++ b/tests/libtest/lib1647.c @@ -0,0 +1,120 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "first.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 result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1647(CURL *curl, const char *url, const char *userpwd) +{ + CURLcode result = CURLE_OK; + + result = init1647(curl, url, userpwd); + if(result) + return result; + + return curl_easy_perform(curl); +} + +static CURLcode test_lib1647(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + 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); + result = run1647(curl, URL, libtest_arg3); + if(result) + goto test_cleanup; + + curl_mprintf("--- Then get '%s'\n", libtest_arg2); + result = run1647(curl, libtest_arg2, libtest_arg4); + +test_cleanup: + + /* proper cleanup sequence - type PB */ + + curl_easy_cleanup(curl); + curl_global_cleanup(); + return result; +}