Browse code

Adding support for SOCKS plain text authentication

This patch adds support for SOCKS plain text (username/password)
authentication as described in RFC 1929. It adds an optional third
parameter to the socks-proxy option, which is a file containing the
login credentials.

I've been using this patch for two weeks now and it does not seem to
cause any problem. The only modifications are in the SOCKS handshake
handling and the options parser.

Signed-Off-By: Pierre Bourdon <delroth@gmail.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Acked-by: David Sommerseth <dazo@users.sourceforge.net>
Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>

Pierre Bourdon authored on 2010/10/11 07:56:04
Showing 5 changed files
... ...
@@ -259,6 +259,7 @@ init_proxy_dowork (struct context *c)
259 259
     {
260 260
       c->c1.socks_proxy = socks_proxy_new (c->options.ce.socks_proxy_server,
261 261
 					   c->options.ce.socks_proxy_port,
262
+					   c->options.ce.socks_proxy_authfile,
262 263
 					   c->options.ce.socks_proxy_retry,
263 264
 					   c->options.auto_proxy_info);
264 265
       if (c->c1.socks_proxy)
... ...
@@ -120,8 +120,11 @@ static const char usage_message[] =
120 120
   "                  AGENT user-agent\n"
121 121
 #endif
122 122
 #ifdef ENABLE_SOCKS
123
-  "--socks-proxy s [p]: Connect to remote host through a Socks5 proxy at address\n"
124
-  "                  s and port p (default port = 1080).\n"
123
+  "--socks-proxy s [p] [up] : Connect to remote host through a Socks5 proxy at\n"
124
+  "                  address s and port p (default port = 1080).\n"
125
+  "                  If proxy authentication is required,\n"
126
+  "                  up is a file containing username/password on 2 lines, or\n"
127
+  "                  'stdin' to prompt for console.\n"
125 128
   "--socks-proxy-retry : Retry indefinitely on Socks proxy errors.\n"
126 129
 #endif
127 130
   "--resolv-retry n: If hostname resolve fails for --remote, retry\n"
... ...
@@ -4283,6 +4286,7 @@ add_option (struct options *options,
4283 4283
 	  options->ce.socks_proxy_port = 1080;
4284 4284
 	}
4285 4285
       options->ce.socks_proxy_server = p[1];
4286
+      options->ce.socks_proxy_authfile = p[3]; /* might be NULL */
4286 4287
     }
4287 4288
   else if (streq (p[0], "socks-proxy-retry"))
4288 4289
     {
... ...
@@ -95,6 +95,7 @@ struct connection_entry
95 95
 #ifdef ENABLE_SOCKS
96 96
   const char *socks_proxy_server;
97 97
   int socks_proxy_port;
98
+  const char *socks_proxy_authfile;
98 99
   bool socks_proxy_retry;
99 100
 #endif
100 101
 };
... ...
@@ -23,10 +23,11 @@
23 23
  */
24 24
 
25 25
 /*
26
- * 2004-01-30: Added Socks5 proxy support
26
+ * 2004-01-30: Added Socks5 proxy support, see RFC 1928
27 27
  *   (Christof Meerwald, http://cmeerw.org)
28 28
  *
29
- * see RFC 1928, only supports "no authentication"
29
+ * 2010-10-10: Added Socks5 plain text authentication support (RFC 1929)
30
+ *   (Pierre Bourdon <delroth@gmail.com>)
30 31
  */
31 32
 
32 33
 #include "syshead.h"
... ...
@@ -38,10 +39,12 @@
38 38
 #include "win32.h"
39 39
 #include "socket.h"
40 40
 #include "fdmisc.h"
41
+#include "misc.h"
41 42
 #include "proxy.h"
42 43
 
43 44
 #include "memdbg.h"
44 45
 
46
+#define UP_TYPE_SOCKS		"SOCKS Proxy"
45 47
 
46 48
 void
47 49
 socks_adjust_frame_parameters (struct frame *frame, int proto)
... ...
@@ -53,6 +56,7 @@ socks_adjust_frame_parameters (struct frame *frame, int proto)
53 53
 struct socks_proxy_info *
54 54
 socks_proxy_new (const char *server,
55 55
 		 int port,
56
+		 const char *authfile,
56 57
 		 bool retry,
57 58
 		 struct auto_proxy_info *auto_proxy_info)
58 59
 {
... ...
@@ -77,6 +81,12 @@ socks_proxy_new (const char *server,
77 77
 
78 78
   strncpynt (p->server, server, sizeof (p->server));
79 79
   p->port = port;
80
+
81
+  if (authfile)
82
+    strncpynt (p->authfile, authfile, sizeof (p->authfile));
83
+  else
84
+    p->authfile[0] = 0;
85
+
80 86
   p->retry = retry;
81 87
   p->defined = true;
82 88
 
... ...
@@ -90,15 +100,99 @@ socks_proxy_close (struct socks_proxy_info *sp)
90 90
 }
91 91
 
92 92
 static bool
93
-socks_handshake (socket_descriptor_t sd, volatile int *signal_received)
93
+socks_username_password_auth (struct socks_proxy_info *p,
94
+                              socket_descriptor_t sd,
95
+                              volatile int *signal_received)
96
+{
97
+  char to_send[516];
98
+  char buf[2];
99
+  int len = 0;
100
+  const int timeout_sec = 5;
101
+  struct user_pass creds;
102
+  ssize_t size;
103
+
104
+  creds.defined = 0;
105
+
106
+  get_user_pass (&creds, p->authfile, UP_TYPE_SOCKS, GET_USER_PASS_MANAGEMENT);
107
+  snprintf (to_send, sizeof (to_send), "\x01%c%s%c%s", strlen(creds.username),
108
+            creds.username, strlen(creds.password), creds.password);
109
+  size = send (sd, to_send, strlen(to_send), MSG_NOSIGNAL);
110
+
111
+  if (size != strlen (to_send))
112
+    {
113
+      msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_username_password_auth: TCP port write failed on send()");
114
+      return false;
115
+    }
116
+
117
+  while (len < 2)
118
+    {
119
+      int status;
120
+      ssize_t size;
121
+      fd_set reads;
122
+      struct timeval tv;
123
+      char c;
124
+
125
+      FD_ZERO (&reads);
126
+      FD_SET (sd, &reads);
127
+      tv.tv_sec = timeout_sec;
128
+      tv.tv_usec = 0;
129
+
130
+      status = select (sd + 1, &reads, NULL, NULL, &tv);
131
+
132
+      get_signal (signal_received);
133
+      if (*signal_received)
134
+	return false;
135
+
136
+      /* timeout? */
137
+      if (status == 0)
138
+	{
139
+	  msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_username_password_auth: TCP port read timeout expired");
140
+	  return false;
141
+	}
142
+
143
+      /* error */
144
+      if (status < 0)
145
+	{
146
+	  msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_username_password_auth: TCP port read failed on select()");
147
+	  return false;
148
+	}
149
+
150
+      /* read single char */
151
+      size = recv(sd, &c, 1, MSG_NOSIGNAL);
152
+
153
+      /* error? */
154
+      if (size != 1)
155
+	{
156
+	  msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_username_password_auth: TCP port read failed on recv()");
157
+	  return false;
158
+	}
159
+
160
+      /* store char in buffer */
161
+      buf[len++] = c;
162
+    }
163
+
164
+  /* VER = 5, SUCCESS = 0 --> auth success */
165
+  if (buf[0] != 5 && buf[1] != 0)
166
+  {
167
+    msg (D_LINK_ERRORS, "socks_username_password_auth: server refused the authentication");
168
+    return false;
169
+  }
170
+
171
+  return true;
172
+}
173
+
174
+static bool
175
+socks_handshake (struct socks_proxy_info *p,
176
+                 socket_descriptor_t sd,
177
+                 volatile int *signal_received)
94 178
 {
95 179
   char buf[2];
96 180
   int len = 0;
97 181
   const int timeout_sec = 5;
98 182
 
99
-  /* VER = 5, NMETHODS = 1, METHODS = [0] */
100
-  const ssize_t size = send (sd, "\x05\x01\x00", 3, MSG_NOSIGNAL);
101
-  if (size != 3)
183
+  /* VER = 5, NMETHODS = 2, METHODS = [0 (no auth), 2 (plain login)] */
184
+  const ssize_t size = send (sd, "\x05\x02\x00\x02", 4, MSG_NOSIGNAL);
185
+  if (size != 4)
102 186
     {
103 187
       msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_handshake: TCP port write failed on send()");
104 188
       return false;
... ...
@@ -151,13 +245,37 @@ socks_handshake (socket_descriptor_t sd, volatile int *signal_received)
151 151
       buf[len++] = c;
152 152
     }
153 153
 
154
-  /* VER == 5 && METHOD == 0 */
155
-  if (buf[0] != '\x05' || buf[1] != '\x00')
154
+  /* VER == 5 */
155
+  if (buf[0] != '\x05')
156 156
     {
157 157
       msg (D_LINK_ERRORS, "socks_handshake: Socks proxy returned bad status");
158 158
       return false;
159 159
     }
160 160
 
161
+  /* select the appropriate authentication method */
162
+  switch (buf[1])
163
+    {
164
+    case 0: /* no authentication */
165
+      break;
166
+
167
+    case 2: /* login/password */
168
+      if (!p->authfile[0])
169
+      {
170
+	msg(D_LINK_ERRORS, "socks_handshake: server asked for username/login auth but we were "
171
+	                   "not provided any credentials");
172
+	return false;
173
+      }
174
+
175
+      if (!socks_username_password_auth(p, sd, signal_received))
176
+	return false;
177
+
178
+      break;
179
+
180
+    default: /* unknown auth method */
181
+      msg(D_LINK_ERRORS, "socks_handshake: unknown SOCKS auth method");
182
+      return false;
183
+    }
184
+
161 185
   return true;
162 186
 }
163 187
 
... ...
@@ -281,7 +399,7 @@ establish_socks_proxy_passthru (struct socks_proxy_info *p,
281 281
   char buf[128];
282 282
   size_t len;
283 283
 
284
-  if (!socks_handshake (sd, signal_received))
284
+  if (!socks_handshake (p, sd, signal_received))
285 285
     goto error;
286 286
 
287 287
   /* format Socks CONNECT message */
... ...
@@ -328,7 +446,7 @@ establish_socks_proxy_udpassoc (struct socks_proxy_info *p,
328 328
 				struct openvpn_sockaddr *relay_addr,
329 329
 			        volatile int *signal_received)
330 330
 {
331
-  if (!socks_handshake (ctrl_sd, signal_received))
331
+  if (!socks_handshake (p, ctrl_sd, signal_received))
332 332
     goto error;
333 333
 
334 334
   {
... ...
@@ -43,12 +43,14 @@ struct socks_proxy_info {
43 43
 
44 44
   char server[128];
45 45
   int port;
46
+  char authfile[256];
46 47
 };
47 48
 
48 49
 void socks_adjust_frame_parameters (struct frame *frame, int proto);
49 50
 
50 51
 struct socks_proxy_info *socks_proxy_new (const char *server,
51 52
 					  int port,
53
+					  const char *authfile,
52 54
 					  bool retry,
53 55
 					  struct auto_proxy_info *auto_proxy_info);
54 56