socks: support CURLAUTH_ONLY in CURLOPT_SOCKS5_AUTH
No binary (build failed)
—
(log yok)
| @@ -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 | |
| @@ -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; | |
| } | |
| @@ -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; | |
| } | |
| @@ -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 */ | |
| @@ -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 */ | |
| @@ -1734,6 +1734,9 @@ TESTCASES = \ | |
| test1725 \ | |
| test1740 \ | |
| test1741 \ | |
| + | test1742 \ |
| + | test1743 \ |
| + | test1744 \ |
| test1790 \ | |
| test1791 \ | |
| test1800 \ | |
| @@ -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> |
| @@ -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> |
| @@ -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> |
| @@ -243,6 +243,8 @@ TESTS_C = \ | |
| lib1662.c \ | |
| lib1678.c \ | |
| lib1686.c \ | |
| + | lib1742.c \ |
| + | lib1744.c \ |
| lib1791.c \ | |
| lib1900.c \ | |
| lib1901.c \ | |
| @@ -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; |
| + | } |
| @@ -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; |
| + | } |