From: Ivan Romanov Date: Mon, 27 May 2019 16:42:14 +0000 (+0500) Subject: Add SOCKS5 username/password support X-Git-Tag: v_3_0_29~599 X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=commitdiff_plain;h=d01bb4028a9d19a62672a8d7d8d13f09ae270851;ds=sidebyside Add SOCKS5 username/password support Based on a patch by Sam. Closes Patch#141 and solves TODO#105. --- diff --git a/doc/source/p-config.sgml b/doc/source/p-config.sgml index 5eef4d2d..0ca50062 100644 --- a/doc/source/p-config.sgml +++ b/doc/source/p-config.sgml @@ -2226,7 +2226,7 @@ forward-socks4, forward-socks4a, forward-socks5 and forward-socks5t target_pattern - socks_proxy[:port] + [user:pass@]socks_proxy[:port] http_parent[:port] @@ -2239,7 +2239,8 @@ forward-socks4, forward-socks4a, forward-socks5 and forward-socks5t (http_parent may be . to denote no HTTP forwarding), and the optional port parameters are TCP ports, - i.e. integer values from 1 to 65535 + i.e. integer values from 1 to 65535. user and + pass can be used for SOCKS5 authentication if required. @@ -2315,6 +2316,13 @@ forward-socks4, forward-socks4a, forward-socks5 and forward-socks5t forward-socks4 / socks-gw.example.com:1080 . + + To connect SOCKS5 proxy which requires username/password authentication: + + + forward-socks5 / user:pass@socks-gw.example.com:1080 . + + To chain Privoxy and Tor, both running on the same system, you would use something like: diff --git a/filters.c b/filters.c index a0ddb939..9a8f1312 100644 --- a/filters.c +++ b/filters.c @@ -2466,10 +2466,11 @@ static const struct forward_spec *get_forward_override_settings(struct client_st if (NULL != socks_proxy) { - /* Parse the SOCKS proxy host[:port] */ + /* Parse the SOCKS proxy [user:pass@]host[:port] */ fwd->gateway_port = 1080; parse_forwarder_address(socks_proxy, - &fwd->gateway_host, &fwd->gateway_port); + &fwd->gateway_host, &fwd->gateway_port, + &fwd->auth_username, &fwd->auth_password); http_parent = vec[2]; } @@ -2487,7 +2488,8 @@ static const struct forward_spec *get_forward_override_settings(struct client_st { fwd->forward_port = 8000; parse_forwarder_address(http_parent, - &fwd->forward_host, &fwd->forward_port); + &fwd->forward_host, &fwd->forward_port, + NULL, NULL); } assert (NULL != fwd); diff --git a/gateway.c b/gateway.c index c84e3b28..03e56ee3 100644 --- a/gateway.c +++ b/gateway.c @@ -1036,7 +1036,16 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, client_pos = 0; cbuf[client_pos++] = '\x05'; /* Version */ - cbuf[client_pos++] = '\x01'; /* One authentication method supported */ + + if (fwd->auth_username && fwd->auth_password) + { + cbuf[client_pos++] = '\x02'; /* Two authentication methods supported */ + cbuf[client_pos++] = '\x02'; /* Username/password */ + } + else + { + cbuf[client_pos++] = '\x01'; /* One authentication method supported */ + } cbuf[client_pos++] = '\x00'; /* The no authentication authentication method */ if (write_socket(sfd, cbuf, client_pos)) @@ -1079,7 +1088,51 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, err = 1; } - if (!err && (sbuf[1] != '\x00')) + if (!err && (sbuf[1] == '\x02')) + { + /* check cbuf overflow */ + size_t auth_len = strlen(fwd->auth_username) + strlen(fwd->auth_password) + 3; + if (auth_len > sizeof(cbuf)) + { + errstr = "SOCKS5 username and/or password too long"; + err = 1; + } + + if (!err) + { + client_pos = 0; + cbuf[client_pos++] = '\x01'; /* Version */ + cbuf[client_pos++] = (char)strlen(fwd->auth_username); + + memcpy(cbuf + client_pos, fwd->auth_username, strlen(fwd->auth_username)); + client_pos += strlen(fwd->auth_username); + cbuf[client_pos++] = (char)strlen(fwd->auth_password); + memcpy(cbuf + client_pos, fwd->auth_password, strlen(fwd->auth_password)); + client_pos += strlen(fwd->auth_password); + + if (write_socket(sfd, cbuf, client_pos)) + { + errstr = "SOCKS5 negotiation auth write failed"; + csp->error_message = strdup(errstr); + log_error(LOG_LEVEL_CONNECT, "%s", errstr); + close_socket(sfd); + return(JB_INVALID_SOCKET); + } + + if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2) + { + errstr = "SOCKS5 negotiation auth read failed"; + err = 1; + } + } + + if (!err && (sbuf[1] != '\x00')) + { + errstr = "SOCKS5 authentication failed"; + err = 1; + } + } + else if (!err && (sbuf[1] != '\x00')) { errstr = "SOCKS5 negotiation protocol error"; err = 1; diff --git a/loadcfg.c b/loadcfg.c index 88379eb5..256dccfc 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -1163,8 +1163,9 @@ struct configuration_spec * load_config(void) if (strcmp(p, ".") != 0) { cur_fwd->forward_port = 8000; - parse_forwarder_address(p, &cur_fwd->forward_host, - &cur_fwd->forward_port); + parse_forwarder_address(p, + &cur_fwd->forward_host, &cur_fwd->forward_port, + NULL, NULL); } /* Add to list. */ @@ -1213,8 +1214,9 @@ struct configuration_spec * load_config(void) if (strcmp(p, ".") != 0) { cur_fwd->gateway_port = 1080; - parse_forwarder_address(p, &cur_fwd->gateway_host, - &cur_fwd->gateway_port); + parse_forwarder_address(p, + &cur_fwd->gateway_host, &cur_fwd->gateway_port, + NULL, NULL); } /* Parse the parent HTTP proxy host[:port] */ @@ -1223,8 +1225,9 @@ struct configuration_spec * load_config(void) if (strcmp(p, ".") != 0) { cur_fwd->forward_port = 8000; - parse_forwarder_address(p, &cur_fwd->forward_host, - &cur_fwd->forward_port); + parse_forwarder_address(p, + &cur_fwd->forward_host, &cur_fwd->forward_port, + NULL, NULL); } /* Add to list. */ @@ -1287,12 +1290,13 @@ struct configuration_spec * load_config(void) break; } - /* Parse the SOCKS proxy host[:port] */ + /* Parse the SOCKS proxy [user:pass@]host[:port] */ p = vec[1]; cur_fwd->gateway_port = 1080; - parse_forwarder_address(p, &cur_fwd->gateway_host, - &cur_fwd->gateway_port); + parse_forwarder_address(p, + &cur_fwd->gateway_host, &cur_fwd->gateway_port, + &cur_fwd->auth_username, &cur_fwd->auth_password); /* Parse the parent HTTP proxy host[:port] */ p = vec[2]; @@ -1300,8 +1304,9 @@ struct configuration_spec * load_config(void) if (strcmp(p, ".") != 0) { cur_fwd->forward_port = 8000; - parse_forwarder_address(p, &cur_fwd->forward_host, - &cur_fwd->forward_port); + parse_forwarder_address(p, + &cur_fwd->forward_host, &cur_fwd->forward_port, + NULL, NULL); } /* Add to list. */ diff --git a/project.h b/project.h index 78d53ce3..eed9f870 100644 --- a/project.h +++ b/project.h @@ -1136,6 +1136,12 @@ struct forward_spec /** SOCKS server port. */ int gateway_port; + /** SOCKS5 username. */ + char *auth_username; + + /** SOCKS5 password. */ + char *auth_password; + /** Parent HTTP proxy hostname, or NULL for none. */ char *forward_host; diff --git a/urlmatch.c b/urlmatch.c index dcef8787..f1742c89 100644 --- a/urlmatch.c +++ b/urlmatch.c @@ -1420,31 +1420,50 @@ int match_portlist(const char *portlist, int port) * * Function : parse_forwarder_address * - * Description : Parse out the host and port from a forwarder address. + * Description : Parse out the username, password, host and port from + * a forwarder address. * * Parameters : * 1 : address = The forwarder address to parse. * 2 : hostname = Used to return the hostname. NULL on error. * 3 : port = Used to return the port. Untouched if no port * is specified. + * 4 : username = Used to return the username if any. + * 5 : password = Used to return the password if any. * * Returns : JB_ERR_OK on success * JB_ERR_MEMORY on out of memory * JB_ERR_PARSE on malformed address. * *********************************************************************/ -jb_err parse_forwarder_address(char *address, char **hostname, int *port) +jb_err parse_forwarder_address(char *address, char **hostname, int *port, + char **username, char **password) { - char *p = address; + char *p; + *hostname = strdup_or_die(address); - if ((*address == '[') && (NULL == strchr(address, ']'))) + /* Parse username and password */ + if (username && password && (NULL != (p = strchr(*hostname, '@')))) + { + *p++ = '\0'; + *username = *hostname; + *hostname = p; + + if (NULL != (p = strchr(*username, ':'))) + { + *p++ = '\0'; + *password = strdup_or_die(p); + } + } + + /* Parse hostname and port */ + p = *hostname; + if ((*p == '[') && (NULL == strchr(p, ']'))) { /* XXX: Should do some more validity checks here. */ return JB_ERR_PARSE; } - *hostname = strdup_or_die(address); - if ((**hostname == '[') && (NULL != (p = strchr(*hostname, ']')))) { *p++ = '\0'; diff --git a/urlmatch.h b/urlmatch.h index ed3da548..ba86b5a3 100644 --- a/urlmatch.h +++ b/urlmatch.h @@ -55,7 +55,8 @@ extern int url_match(const struct pattern_spec *pattern, extern jb_err create_pattern_spec(struct pattern_spec *url, char *buf); extern void free_pattern_spec(struct pattern_spec *url); extern int match_portlist(const char *portlist, int port); -extern jb_err parse_forwarder_address(char *address, char **hostname, int *port); +extern jb_err parse_forwarder_address(char *address, char **hostname, int *port, + char **username, char **password); #endif /* ndef URLMATCH_H_INCLUDED */