← Commit list

e47b56a762

Joshua Rogers
Joshua Rogers
2026-09-19
SUCCESS Duration: 393s

Subject

socks: support CURLAUTH_ONLY in CURLOPT_SOCKS5_AUTH

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

docs/libcurl/opts/CURLOPT_SOCKS5_AUTH.md+16 -3
@@ -29,9 +29,20 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SOCKS5_AUTH, long bitmask);
Pass a long as parameter, which is set to a bitmask, to tell libcurl which
authentication method(s) are allowed for SOCKS5 proxy authentication. The only
supported flags are *CURLAUTH_BASIC*, which allows username/password
-authentication, *CURLAUTH_GSSAPI*, which allows GSS-API authentication, and
-*CURLAUTH_NONE*, which allows no authentication. Set the actual username and
-password with the CURLOPT_PROXYUSERPWD(3) option.
+authentication, and *CURLAUTH_GSSAPI*, which allows GSS-API authentication.
+Set the actual username and password with the CURLOPT_PROXYUSERPWD(3) option.
+
+libcurl offers the SOCKS5 "no authentication" method in addition to the ones
+in the bitmask, and accepts it if the proxy selects it. *CURLAUTH_NONE* is zero
+and therefore cannot be combined with the other flags; setting the bitmask to
+*CURLAUTH_NONE* makes libcurl offer only the no authentication method.
+
+Add *CURLAUTH_ONLY* to the bitmask to require authentication. libcurl then does
+not offer the no authentication method, and rejects it with *CURLE_PROXY* if
+the proxy selects it anyway. A connection that was set up without SOCKS5
+authentication is also not reused for such a transfer. Setting *CURLAUTH_ONLY*
+without *CURLAUTH_BASIC* or *CURLAUTH_GSSAPI* makes curl_easy_setopt(3) return
+*CURLE_BAD_FUNCTION_ARGUMENT*.
The specific socks authentication method is an *access property*, it does not
change the security context. This means that this option changes how the
@@ -76,6 +87,8 @@ int main(void)
**CURLAUTH_*** macros became `long` types in 7.26.0, prior to this version
a `long` cast was necessary when passed to curl_easy_setopt(3).
+*CURLAUTH_ONLY* is supported since 8.23.0.
+
# %AVAILABILITY%
# RETURN VALUE
lib/setopt.c+8 -3
@@ -990,11 +990,16 @@ static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option,
#endif
s->proxytype = (unsigned char)arg;
break;
- case CURLOPT_SOCKS5_AUTH:
- if(arg & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
+ case CURLOPT_SOCKS5_AUTH: {
+ unsigned long auth = (unsigned long)arg;
+ if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI | CURLAUTH_ONLY))
return CURLE_NOT_BUILT_IN;
- s->socks5auth = (unsigned char)arg;
+ if((auth & CURLAUTH_ONLY) && !(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI)))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ s->socks5auth = (unsigned char)(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI));
+ s->socks5_auth_only = !!(auth & CURLAUTH_ONLY);
break;
+ }
default:
return CURLE_UNKNOWN_OPTION;
}
lib/socks.c+15 -2
@@ -587,8 +587,11 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf,
Curl_creds_unlink(&sx->creds);
req[0] = 5; /* version */
- nauths = 1;
- req[1 + nauths] = 0; /* 1. no authentication */
+ nauths = 0;
+ if(!data->set.socks5_auth_only) {
+ ++nauths;
+ req[1 + nauths] = 0; /* no authentication */
+ }
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
if(auth & CURLAUTH_GSSAPI) {
++nauths;
@@ -599,6 +602,10 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf,
++nauths;
req[1 + nauths] = 2; /* username/password */
}
+ if(!nauths) {
+ failf(data, "SOCKS5: no acceptable authentication method is available.");
+ return CURLPX_NO_AUTH;
+ }
req[1] = nauths;
req_len = 2 + nauths;
@@ -631,11 +638,16 @@ static CURLproxycode socks5_check_resp0(struct socks_ctx *sx,
switch(auth_mode) {
case 0:
+ if(data->set.socks5_auth_only) {
+ failf(data, "SOCKS5 proxy selected no authentication.");
+ return CURLPX_NO_AUTH;
+ }
/* DONE! No authentication needed. Send request. */
sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT);
return CURLPX_OK;
case 1:
if(data->set.socks5auth & CURLAUTH_GSSAPI) {
+ cf->conn->bits.socks5_authenticated = TRUE;
sxstate(sx, cf, data, SOCKS5_ST_GSSAPI_INIT);
return CURLPX_OK;
}
@@ -644,6 +656,7 @@ static CURLproxycode socks5_check_resp0(struct socks_ctx *sx,
case 2:
/* regular name + password authentication */
if(data->set.socks5auth & CURLAUTH_BASIC) {
+ cf->conn->bits.socks5_authenticated = TRUE;
sxstate(sx, cf, data, SOCKS5_ST_AUTH_INIT);
return CURLPX_OK;
}
lib/url.c+10 -0
@@ -749,6 +749,16 @@ static bool url_match_proxy_use(struct connectdata *conn,
if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
return FALSE;
+ if(m->data->set.socks5_auth_only && !conn->bits.socks5_authenticated &&
+ ((conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ||
+ (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5_HOSTNAME))) {
+ DEBUGF(infof(m->data,
+ "Connection #%" FMT_OFF_T
+ " was not SOCKS5 authenticated, cannot reuse",
+ conn->connection_id));
+ return FALSE;
+ }
+
if(CURL_PROXY_IS_HTTPS(m->needle->http_proxy.proxytype)) {
/* https proxies come in different types, http/1.1, h2, ... */
/* match SSL config to proxy */
lib/urldata.h+2 -0
@@ -184,6 +184,7 @@ struct ConnectBits {
BIT(connect_only);
#ifndef CURL_DISABLE_PROXY
BIT(origin_is_proxy); /* if set, the connection's origin is a proxy */
+ BIT(socks5_authenticated); /* SOCKS5 negotiated an authentication method */
#endif
/* always modify bits.close with the connclose() and connkeep() macros! */
BIT(close); /* if set, we close the connection after this request */
@@ -1107,6 +1108,7 @@ struct UserDefined {
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
BIT(socks5_gssapi_nec); /* Flag to support NEC SOCKS5 server */
#endif
+ BIT(socks5_auth_only); /* SOCKS5 must authenticate */
BIT(sasl_ir); /* Enable/disable SASL initial response */
BIT(tcp_keepalive); /* use TCP keepalives */
BIT(tcp_fastopen); /* use TCP Fast Open */
tests/data/Makefile.am+3 -0
@@ -1734,6 +1734,9 @@ TESTCASES = \
test1725 \
test1740 \
test1741 \
+ test1742 \
+ test1743 \
+ test1744 \
test1790 \
test1791 \
test1800 \
tests/data/test1742+62 -0
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+SOCKS5
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+
+-foo-
+</data>
+
+<datacheck>
+-foo-
+</datacheck>
+<servercmd>
+method 2
+user silly
+password person
+nmethods_min 1
+nmethods_max 1
+</servercmd>
+</reply>
+
+# Client-side
+<client>
+<features>
+proxy
+</features>
+<server>
+http
+socks5
+</server>
+<tool>
+lib%TESTNUMBER
+</tool>
+<name>
+SOCKS5 CURLAUTH_ONLY does not offer the no authentication method
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER %HOSTIP:%SOCKSPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<protocol crlf="headers">
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
tests/data/test1743+46 -0
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+SOCKS5
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+</data>
+<servercmd>
+method 0
+</servercmd>
+</reply>
+
+# Client-side
+<client>
+<features>
+proxy
+</features>
+<server>
+http
+socks5
+</server>
+<tool>
+lib1742
+</tool>
+<name>
+SOCKS5 CURLAUTH_ONLY rejects a proxy that selects no authentication
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER %HOSTIP:%SOCKSPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+97
+</errorcode>
+</verify>
+</testcase>
tests/data/test1744+60 -0
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+SOCKS5
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Content-Length: 6
+
+-foo-
+</data>
+
+<datacheck>
+-foo-
+</datacheck>
+<servercmd>
+method 0
+</servercmd>
+</reply>
+
+# Client-side
+<client>
+<features>
+proxy
+</features>
+<server>
+http
+socks5
+</server>
+<tool>
+lib%TESTNUMBER
+</tool>
+<name>
+SOCKS5 CURLAUTH_ONLY does not reuse an unauthenticated connection
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER %HOSTIP:%SOCKSPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+97
+</errorcode>
+<protocol crlf="headers">
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
tests/libtest/Makefile.inc+2 -0
@@ -243,6 +243,8 @@ TESTS_C = \
lib1662.c \
lib1678.c \
lib1686.c \
+ lib1742.c \
+ lib1744.c \
lib1791.c \
lib1900.c \
lib1901.c \
tests/libtest/lib1742.c+72 -0
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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 = URL
+ * argv2 = SOCKS5 proxy
+ */
+
+#include "first.h"
+
+static CURLcode test_lib1742(const char *URL)
+{
+ CURLcode result = CURLE_OK;
+ CURL *curl = NULL;
+
+ if(test_argc < 2)
+ return TEST_ERR_MAJOR_BAD;
+
+ 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;
+ }
+
+ if(curl_easy_setopt(curl, CURLOPT_SOCKS5_AUTH, (long)CURLAUTH_ONLY) !=
+ CURLE_BAD_FUNCTION_ARGUMENT) {
+ curl_mfprintf(stderr, "CURLAUTH_ONLY without a method was accepted\n");
+ result = TEST_ERR_FAILURE;
+ goto test_cleanup;
+ }
+
+ easy_setopt(curl, CURLOPT_URL, URL);
+ easy_setopt(curl, CURLOPT_PROXY, libtest_arg2);
+ easy_setopt(curl, CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5);
+ easy_setopt(curl, CURLOPT_PROXYUSERPWD, "silly:person");
+ easy_setopt(curl, CURLOPT_SOCKS5_AUTH,
+ (long)(CURLAUTH_BASIC | CURLAUTH_ONLY));
+ easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+ result = curl_easy_perform(curl);
+
+test_cleanup:
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+ return result;
+}
tests/libtest/lib1744.c+71 -0
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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 = URL
+ * argv2 = SOCKS5 proxy
+ */
+
+#include "first.h"
+
+static CURLcode test_lib1744(const char *URL)
+{
+ CURLcode result = CURLE_OK;
+ CURL *curl = NULL;
+
+ if(test_argc < 2)
+ return TEST_ERR_MAJOR_BAD;
+
+ 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;
+ }
+
+ easy_setopt(curl, CURLOPT_URL, URL);
+ easy_setopt(curl, CURLOPT_PROXY, libtest_arg2);
+ easy_setopt(curl, CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5);
+ easy_setopt(curl, CURLOPT_PROXYUSERPWD, "silly:person");
+ easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+ /* the proxy asks for no authentication and the connection is kept */
+ result = curl_easy_perform(curl);
+ if(result)
+ goto test_cleanup;
+
+ /* that connection must not be reused now that auth is required */
+ easy_setopt(curl, CURLOPT_SOCKS5_AUTH,
+ (long)(CURLAUTH_BASIC | CURLAUTH_ONLY));
+ result = curl_easy_perform(curl);
+
+test_cleanup:
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+ return result;
+}