Add SOCKS5 username/password support
authorIvan Romanov <drizt72@zoho.eu>
Mon, 27 May 2019 16:42:14 +0000 (21:42 +0500)
committerFabian Keil <fk@fabiankeil.de>
Fri, 6 Sep 2019 09:51:10 +0000 (11:51 +0200)
Based on a patch by Sam.

Closes Patch#141 and solves TODO#105.

doc/source/p-config.sgml
filters.c
gateway.c
loadcfg.c
project.h
urlmatch.c
urlmatch.h

index 5eef4d2..0ca5006 100644 (file)
@@ -2226,7 +2226,7 @@ forward-socks4, forward-socks4a, forward-socks5 and forward-socks5t</title>
   <listitem>
    <para>
     <replaceable class="parameter">target_pattern</replaceable>
-    <replaceable class="parameter">socks_proxy</replaceable>[:<replaceable class="parameter">port</replaceable>]
+    [<replaceable class="parameter">user</replaceable>:<replaceable class="parameter">pass</replaceable>@]<replaceable class="parameter">socks_proxy</replaceable>[:<replaceable class="parameter">port</replaceable>]
     <replaceable class="parameter">http_parent</replaceable>[:<replaceable class="parameter">port</replaceable>]
    </para>
    <para>
@@ -2239,7 +2239,8 @@ forward-socks4, forward-socks4a, forward-socks5 and forward-socks5t</title>
     (<replaceable class="parameter">http_parent</replaceable>
     may be <quote>.</quote> to denote <quote>no HTTP forwarding</quote>), and the optional
     <replaceable class="parameter">port</replaceable> parameters are TCP ports,
-    i.e. integer values from 1 to 65535
+    i.e. integer values from 1 to 65535. <replaceable class="parameter">user</replaceable> and
+    <replaceable class="parameter">pass</replaceable> can be used for SOCKS5 authentication if required.
    </para>
   </listitem>
  </varlistentry>
@@ -2315,6 +2316,13 @@ forward-socks4, forward-socks4a, forward-socks5 and forward-socks5t</title>
   forward-socks4   /               socks-gw.example.com:1080  .
 </screen>
 
+   <para>
+    To connect SOCKS5 proxy which requires username/password authentication:
+   </para>
+    <screen>
+  forward-socks5   /               user:pass@socks-gw.example.com:1080  .
+</screen>
+
     <para>
     To chain Privoxy and Tor, both running on the same system, you would use
     something like:
index a0ddb93..9a8f131 100644 (file)
--- 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);
index c84e3b2..03e56ee 100644 (file)
--- 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;
index 88379eb..256dccf 100644 (file)
--- 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. */
index 78d53ce..eed9f87 100644 (file)
--- 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;
 
index dcef878..f1742c8 100644 (file)
@@ -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';
index ed3da54..ba86b5a 100644 (file)
@@ -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 */