ref bugnote
[oweals/gnunet.git] / src / util / socks.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2013 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21 /**
22  * @file util/socks.c
23  * @brief  SOCKS5 connection support
24  * @author Jeffrey Burdges
25  *
26  * These routines should be called only on newly active connections.
27  */
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "util-socks", __VA_ARGS__)
33
34 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-socks", syscall)
35
36
37 /* SOCKS5 authentication methods */
38 #define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */
39 #define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */
40 #define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */
41 #define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */
42 #define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */
43 #define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */
44 #define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */
45
46
47 /* SOCKS5 connection responces */
48 #define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */
49 #define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */
50 #define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */
51 #define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */
52 #define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */
53 #define SOCKS5_REP_REFUSED 0x05 /* connection refused */
54 #define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */
55 #define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */
56 #define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */
57 #define SOCKS5_REP_INVADDR 0x09 /* Inalid address */
58
59 const char * SOCKS5_REP_names(int rep)
60 {
61   switch (rep) {
62     case SOCKS5_REP_SUCCEEDED: return "succeeded";
63     case SOCKS5_REP_FAIL: return "general SOCKS server failure";
64     case SOCKS5_REP_NALLOWED: return "connection not allowed by ruleset";
65     case SOCKS5_REP_NUNREACH: return "Network unreachable";
66     case SOCKS5_REP_HUNREACH: return "Host unreachable";
67     case SOCKS5_REP_REFUSED: return "connection refused";
68     case SOCKS5_REP_EXPIRED: return "TTL expired";
69     case SOCKS5_REP_CNOTSUP: return "Command not supported";
70     case SOCKS5_REP_ANOTSUP: return "Address not supported";
71     case SOCKS5_REP_INVADDR: return "Invalid address";
72     default: return NULL;
73   }
74 };
75
76
77 /**
78  * Encode a string for the SOCKS5 protocol by prefixing it a byte stating its
79  * length and stipping the trailing zero byte.  Truncates any string longer
80  * than 255 bytes.
81  *
82  * @param b buffer to contain the encoded string
83  * @param s string to encode
84  * @return pointer to the end of the encoded string in the buffer
85  */
86 unsigned char *
87 SOCK5_proto_string(unsigned char * b,
88                    const char * s)
89 {
90   size_t l = strlen(s);
91
92   if (l > 255)
93   {
94     LOG (GNUNET_ERROR_TYPE_WARNING,
95          "SOCKS5 cannot handle hostnames, usernames, or passwords over 255 bytes, truncating.\n");
96     l=255;
97   }
98   *(b++) = (unsigned char) l;
99   strncpy ((char *)b, s, l);
100   return b+l;
101 }
102
103
104 #define SOCKS5_step_greet 0
105 #define SOCKS5_step_auth  1
106 #define SOCKS5_step_cmd   2
107 #define SOCKS5_step_done  3
108
109 /**
110  * State of the SOCKS5 handshake.
111  */
112 struct GNUNET_SOCKS_Handshake
113 {
114
115   /**
116    * Connection handle used for SOCKS5
117    */
118   struct GNUNET_CONNECTION_Handle *socks5_connection;
119
120   /**
121    * Connection handle initially returned to client
122    */
123   struct GNUNET_CONNECTION_Handle *target_connection;
124
125   /**
126    * Transmission handle on socks5_connection.
127    */
128   struct GNUNET_CONNECTION_TransmitHandle *th;
129
130   /**
131    * Our stage in the SOCKS5 handshake
132    */
133   int step;
134
135   /**
136    * Precomputed SOCKS5 handshake ouput buffer
137    */
138   unsigned char outbuf[1024];
139
140   /**
141    * Pointers delineating protoocol steps in the outbut buffer
142    */
143   unsigned char * (outstep[4]);
144
145   /**
146    * SOCKS5 handshake input buffer
147    */
148   unsigned char inbuf[1024];
149
150   /**
151    * Pointers delimiting the current step in the input buffer
152    */
153   unsigned char * instart;
154   unsigned char * inend;
155 };
156
157
158 /* Regitering prototypes */
159
160 void
161 register_reciever (struct GNUNET_SOCKS_Handshake *ih, int want);
162
163   /* In fact, the client sends first rule in GNUNet suggests one could take
164    * large mac read sizes without fear of screwing up the proxied protocol,
165    * but we make a proper SOCKS5 client. */
166 #define register_reciever_wants(ih) ((SOCKS5_step_cmd == ih->step) ? 10 : 2)
167
168
169 struct GNUNET_CONNECTION_TransmitHandle *
170 register_sender (struct GNUNET_SOCKS_Handshake *ih);
171
172
173 /**
174  * Conclude the SOCKS5 handshake successfully.
175  *
176  * @param ih SOCKS5 handshake, consumed here.
177  * @param c open unused connection, consumed here.
178  * @return Connection handle that becomes usable when the handshake completes.
179  */
180 void
181 SOCKS5_handshake_done(struct GNUNET_SOCKS_Handshake *ih)
182 {
183   GNUNET_CONNECTION_acivate_proxied (ih->target_connection);
184 }
185
186
187 /**
188  * Read one step in the SOCKS5 handshake.
189  *
190  * @param ih SOCKS5 Handshake
191  */
192 void
193 SOCKS5_handshake_step (struct GNUNET_SOCKS_Handshake *ih)
194 {
195   unsigned char * b = ih->instart;
196   size_t available = ih->inend - b;
197
198   int want = register_reciever_wants(ih);
199   if (available < want) {
200     register_reciever (ih, want - available);
201     return;
202   }
203   GNUNET_assert (SOCKS5_step_done > ih->step && ih->step >= 0);
204   switch (ih->step) {
205     case SOCKS5_step_greet:  /* SOCKS5 server's greeting */
206       if (b[0] != 5)
207       {
208         LOG (GNUNET_ERROR_TYPE_ERROR,
209              "Not a SOCKS5 server\n");
210         GNUNET_assert (0);
211       }
212       switch (b[1]) {
213         case SOCKS5_AUTH_NOAUTH:
214           ih->step=SOCKS5_step_cmd;  /* no authentication to do */
215           break;
216         case SOCKS5_AUTH_USERPASS:
217           ih->step=SOCKS5_step_auth;
218           break;
219         case SOCKS5_AUTH_REJECT:
220           LOG (GNUNET_ERROR_TYPE_ERROR,
221                "No authentication method accepted\n");
222           return;
223         default:
224           LOG (GNUNET_ERROR_TYPE_ERROR,
225                "Not a SOCKS5 server / Nonsensical authentication\n");
226           return;
227       }
228       b += 2;
229       break;
230     case SOCKS5_step_auth:  /* SOCKS5 server's responce to authentication */
231       if (b[1] != 0)
232       {
233         LOG (GNUNET_ERROR_TYPE_ERROR,
234              "SOCKS5 authentication failed\n");
235         GNUNET_assert (0);
236       }
237       ih->step=SOCKS5_step_cmd;
238       b += 2;
239       break;
240     case SOCKS5_step_cmd:  /* SOCKS5 server's responce to command */
241       if (b[0] != 5)
242       {
243         LOG (GNUNET_ERROR_TYPE_ERROR,
244              "SOCKS5 protocol error\n");
245         GNUNET_assert (0);
246       }
247       if (0 != b[1]) {
248         LOG (GNUNET_ERROR_TYPE_ERROR,
249              "SOCKS5 connection error : %s\n",
250              SOCKS5_REP_names(b[1]));
251         return;
252       }
253       b += 3;
254       /* There is no reason to verify host and port afaik. */
255       switch (*(b++)) {
256         case 1: /* IPv4 */
257           b += sizeof(struct in_addr);  /* 4 */
258           break;
259         case 4: /* IPv6 */
260           b += sizeof(struct in6_addr);  /* 16 */
261           break;
262         case 3: /* hostname */
263           b += *b;
264           break;
265       }
266       b += 2;  /* port */
267       if (b > ih->inend) {
268         register_reciever (ih, b - ih->inend);
269         return;
270       }
271       ih->step = SOCKS5_step_done;
272       LOG (GNUNET_ERROR_TYPE_DEBUG,
273            "SOCKS5 server : %s\n",
274            SOCKS5_REP_names(b[1]));
275       ih->instart = b;
276       SOCKS5_handshake_done (ih);
277       return;
278     case SOCKS5_step_done:
279       GNUNET_assert (0);
280   }
281   ih->instart = b;
282   /* Do not reschedule the sender unless we're done reading.
283    * I imagine this lets us avoid ever cancelling the transmit handle. */
284   register_sender (ih);
285 }
286
287
288 /**
289  * Callback to read from the SOCKS5 proxy.
290  *
291  * @param client the service
292  * @param handler function to call with the message
293  * @param handler_cls closure for @a handler
294  */
295 void
296 reciever (void *cls,
297           const void *buf, size_t available,
298           const struct sockaddr * addr,
299           socklen_t addrlen, int errCode)
300 {
301   struct GNUNET_SOCKS_Handshake * ih = cls;
302   GNUNET_assert (&ih->inend[available] < &ih->inbuf[1024]);
303   GNUNET_memcpy(ih->inend, buf, available);
304   ih->inend += available;
305   SOCKS5_handshake_step (ih);
306 }
307
308
309 /**
310  * Register callback to read from the SOCKS5 proxy.
311  *
312  * @param client the service
313  * @param handler function to call with the message
314  * @param handler_cls closure for @a handler
315  */
316 void
317 register_reciever (struct GNUNET_SOCKS_Handshake *ih, int want)
318 {
319   GNUNET_CONNECTION_receive (ih->socks5_connection,
320                              want,
321                              GNUNET_TIME_relative_get_minute_ (),
322                              &reciever,
323                              ih);
324 }
325
326
327 /**
328  * Register SOCKS5 handshake sender
329  *
330  * @param cls closure (SOCKS handshake)
331  * @param size number of bytes available in @a buf
332  * @param buf where the callee should write the message
333  * @return number of bytes written to @a buf
334  */
335 size_t
336 transmit_ready (void *cls,
337                 size_t size,
338                 void *buf)
339 {
340   struct GNUNET_SOCKS_Handshake * ih = cls;
341
342   /* connection.c has many routines that call us with buf == NULL :
343    * signal_transmit_error() - DNS, etc. active
344    *   connect_fail_continuation()
345    *     connect_probe_continuation() - timeout
346    *     try_connect_using_address() - DNS failure/timeout
347    *     transmit_timeout() - retry failed?
348    * GNUNET_CONNECTION_notify_transmit_ready() can schedule :
349    *   transmit_timeout() - DNS still working
350    *   connect_error() - DNS done but no socket?
351    * transmit_ready() - scheduler shutdown or timeout, or signal_transmit_error()
352    * We'd need to dig into the scheduler to guess at the reason, as
353    * connection.c tells us nothing itself, but mostly its timouts.
354    * Initially, we'll simply ignore this and leave massive timeouts, but
355    * maybe that should change for error handling pruposes.  It appears that
356    * successful operations, including DNS resolution, do not use this.  */
357   if (NULL == buf)
358   {
359     if (0 == ih->step)
360     {
361       LOG (GNUNET_ERROR_TYPE_WARNING,
362            "Timeout contacting SOCKS server, retrying indefinitely, but probably hopeless.\n");
363       register_sender (ih);
364     }
365     else
366     {
367       LOG (GNUNET_ERROR_TYPE_ERROR,
368            "Timeout during mid SOCKS handshake (step %u), probably not a SOCKS server.\n",
369            ih->step);
370       GNUNET_break (0);
371     }
372     return 0;
373   }
374
375   GNUNET_assert ( (1024 >= size) && (size > 0) );
376   GNUNET_assert ( (SOCKS5_step_done > ih->step) && (ih->step >= 0) );
377   unsigned char * b = ih->outstep[ih->step];
378   unsigned char * e = ih->outstep[ih->step+1];
379   GNUNET_assert (e <= &ih->outbuf[1024]);
380   unsigned int l = e - b;
381   GNUNET_assert (size >= l);
382   GNUNET_memcpy (buf,
383                  b,
384                  l);
385   register_reciever (ih,
386                      register_reciever_wants (ih));
387   return l;
388 }
389
390
391 /**
392  * Register SOCKS5 handshake sender
393  *
394  * @param ih handshake
395  * @return non-NULL if the notify callback was queued,
396  *         NULL if we are already going to notify someone else (busy)
397  */
398 struct GNUNET_CONNECTION_TransmitHandle *
399 register_sender (struct GNUNET_SOCKS_Handshake *ih)
400 {
401   struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_MINUTES;
402
403   GNUNET_assert (SOCKS5_step_done > ih->step);
404   GNUNET_assert (ih->step >= 0);
405   if (0 == ih->step)
406     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 3);
407   unsigned char * b = ih->outstep[ih->step];
408   unsigned char * e = ih->outstep[ih->step+1];
409   GNUNET_assert (ih->outbuf <= b && b < e && e < &ih->outbuf[1024]);
410   ih->th = GNUNET_CONNECTION_notify_transmit_ready (ih->socks5_connection,
411                                                     e - b,
412                                                     timeout,
413                                                     &transmit_ready,
414                                                     ih);
415   return ih->th;
416 }
417
418
419 /**
420  * Initialize a SOCKS5 handshake for authentication via username and
421  * password.  Tor uses SOCKS username and password authentication to assign
422  * programs unique circuits.
423  *
424  * @param user username for the proxy
425  * @param pass password for the proxy
426  * @return Valid SOCKS5 hanbdshake handle
427  */
428 struct GNUNET_SOCKS_Handshake *
429 GNUNET_SOCKS_init_handshake (const char *user, const char *pass)
430 {
431   struct GNUNET_SOCKS_Handshake *ih = GNUNET_new (struct GNUNET_SOCKS_Handshake);
432   unsigned char * b = ih->outbuf;
433
434   ih->outstep[SOCKS5_step_greet] = b;
435   *(b++) = 5; /* SOCKS5 */
436   unsigned char * n = b++;
437   *n = 1; /* Number of authentication methods */
438   /* We support no authentication even when requesting authentication,
439    * but this appears harmless, given the way that Tor uses authentication.
440    * And some SOCKS5 servers might require this.  */
441   *(b++) = SOCKS5_AUTH_NOAUTH;
442   if (NULL != user) {
443     *(b++) = SOCKS5_AUTH_USERPASS;
444     (*n)++;
445   }
446   /* There is no apperent reason to support authentication methods beyond
447    * username and password since afaik Tor does not support them. */
448
449   /* We authenticate with an empty username and password if the server demands
450    * them but we do not have any. */
451   if (user == NULL)
452     user = "";
453   if (pass == NULL)
454     pass = "";
455
456   ih->outstep[SOCKS5_step_auth] = b;
457   *(b++) = 1; /* subnegotiation ver.: 1 */
458   b = SOCK5_proto_string(b,user);
459   b = SOCK5_proto_string(b,pass);
460
461   ih->outstep[SOCKS5_step_cmd] = b;
462
463   ih->inend = ih->instart = ih->inbuf;
464
465   return ih;
466 }
467
468
469 /**
470  * Initialize a SOCKS5 handshake without authentication, thereby possibly
471  * sharing a Tor circuit with another process.
472  *
473  * @return Valid SOCKS5 hanbdshake handle
474  */
475 struct GNUNET_SOCKS_Handshake *
476 GNUNET_SOCKS_init_handshake_noauth ()
477 {
478   return GNUNET_SOCKS_init_handshake (NULL,NULL);
479 }
480
481
482 /**
483  * Build request that the SOCKS5 proxy open a TCP/IP stream to the given host
484  * and port.
485  *
486  * @param ih SOCKS5 handshake
487  * @param hostname
488  * @param port
489  */
490 void
491 GNUNET_SOCKS_set_handshake_destination (struct GNUNET_SOCKS_Handshake *ih,
492                                          const char *host, uint16_t port)
493 {
494   union {
495     struct in_addr in4;
496     struct in6_addr in6;
497   } ia;
498   unsigned char * b = ih->outstep[SOCKS5_step_cmd];
499
500   *(b++) = 5;  /* SOCKS5 */
501   *(b++) = 1;  /* Establish a TCP/IP stream */
502   *(b++) = 0;  /* reserved */
503
504   /* Specify destination */
505   if (1 == inet_pton(AF_INET,host,&ia.in4)) {
506     *(b++)= 1;  /* IPv4 */
507     GNUNET_memcpy (b, &ia.in4, sizeof(struct in_addr));
508     b += sizeof(struct in_addr);  /* 4 */
509   } else if (1 == inet_pton(AF_INET6,host,&ia.in6)) {
510     *(b++)= 4;  /* IPv6 */
511     GNUNET_memcpy (b, &ia.in6, sizeof(struct in6_addr));
512     b += sizeof(struct in6_addr);  /* 16 */
513   } else {
514     *(b++)= 3;  /* hostname */
515     b = SOCK5_proto_string (b, host);
516   }
517
518   /* Specify port */
519   *(uint16_t*)b = htons (port);
520   b += 2;
521
522   ih->outstep[SOCKS5_step_done] = b;
523 }
524
525
526 /**
527  * Run a SOCKS5 handshake on an open but unused TCP connection.
528  *
529  * @param ih SOCKS5 handshake, consumed here.
530  * @param c open unused connection, consumed here.
531  * @return Connection handle that becomes usable when the SOCKS5 handshake completes.
532  */
533 struct GNUNET_CONNECTION_Handle *
534 GNUNET_SOCKS_run_handshake(struct GNUNET_SOCKS_Handshake *ih,
535                             struct GNUNET_CONNECTION_Handle *c)
536 {
537   ih->socks5_connection=c;
538   ih->target_connection = GNUNET_CONNECTION_create_proxied_from_handshake (c);
539   register_sender (ih);
540
541   return ih->target_connection;
542 }
543
544
545 /**
546  * Check if a SOCKS proxy is required by a service.  Do not use local service
547  * if a SOCKS proxy port is configured as this could deanonymize a user.
548  *
549  * @param service_name name of service to connect to
550  * @param cfg configuration to use
551  * @return GNUNET_YES if so, GNUNET_NO if not
552  */
553 int
554 GNUNET_SOCKS_check_service (const char *service_name,
555                             const struct GNUNET_CONFIGURATION_Handle *cfg)
556 {
557   return GNUNET_CONFIGURATION_have_value (cfg, service_name, "SOCKSPORT") ||
558          GNUNET_CONFIGURATION_have_value (cfg, service_name, "SOCKSHOST");
559 }
560
561
562 /**
563  * Try to connect to a service configured to use a SOCKS5 proxy.
564  *
565  * @param service_name name of service to connect to
566  * @param cfg configuration to use
567  * @return Connection handle that becomes usable when the handshake completes.
568  *         NULL if SOCKS not configured or not configured properly
569  */
570 struct GNUNET_CONNECTION_Handle *
571 GNUNET_SOCKS_do_connect (const char *service_name,
572                          const struct GNUNET_CONFIGURATION_Handle *cfg)
573 {
574   struct GNUNET_SOCKS_Handshake *ih;
575   struct GNUNET_CONNECTION_Handle *socks5; /* *proxied */
576   char *host0;
577   char *host1;
578   char *user;
579   char *pass;
580   unsigned long long port0;
581   unsigned long long port1;
582
583   if (GNUNET_YES !=
584       GNUNET_SOCKS_check_service (service_name, cfg))
585     return NULL;
586   if (GNUNET_OK !=
587       GNUNET_CONFIGURATION_get_value_number (cfg,
588                                              service_name,
589                                              "SOCKSPORT",
590                                              &port0))
591     port0 = 9050;
592   /* A typical Tor client should usually try port 9150 for the TBB too, but
593    * GNUnet can probably assume a system Tor installation. */
594   if (port0 > 65535 || port0 <= 0)
595   {
596     LOG (GNUNET_ERROR_TYPE_WARNING,
597          _("Attempting to use invalid port %d as SOCKS proxy for service `%s'.\n"),
598          port0,
599          service_name);
600     return NULL;
601   }
602   if ( (GNUNET_OK !=
603         GNUNET_CONFIGURATION_get_value_number (cfg,
604                                                service_name,
605                                                "PORT",
606                                                &port1)) ||
607        (port1 > 65535) ||
608        (port1 <= 0) ||
609        (GNUNET_OK !=
610         GNUNET_CONFIGURATION_get_value_string (cfg,
611                                                service_name,
612                                                "HOSTNAME",
613                                                &host1)))
614   {
615     LOG (GNUNET_ERROR_TYPE_WARNING,
616          _("Attempting to proxy service `%s' to invalid port %d or hostname.\n"),
617          service_name,
618          port1);
619     return NULL;
620   }
621   /* Appeared to still work after host0 corrupted, so either test case is broken, or
622      this whole routine is not being called. */
623   if (GNUNET_OK !=
624       GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "SOCKSHOST", &host0))
625     host0 = NULL;
626   socks5 = GNUNET_CONNECTION_create_from_connect (cfg,
627                                                   (host0 != NULL)
628                                                   ? host0
629                                                   :"127.0.0.1",
630                                                   port0);
631   GNUNET_free_non_null (host0);
632
633   /* Sets to NULL if they do not exist */
634   (void) GNUNET_CONFIGURATION_get_value_string (cfg,
635                                                 service_name,
636                                                 "SOCKSUSER",
637                                                 &user);
638   (void) GNUNET_CONFIGURATION_get_value_string (cfg,
639                                                 service_name,
640                                                 "SOCKSPASS",
641                                                 &pass);
642   ih = GNUNET_SOCKS_init_handshake(user,pass);
643   GNUNET_free_non_null (user);
644   GNUNET_free_non_null (pass);
645
646   GNUNET_SOCKS_set_handshake_destination (ih,
647                                           host1,
648                                           port1);
649   GNUNET_free (host1);
650   return GNUNET_SOCKS_run_handshake (ih,
651                                      socks5);
652 }
653
654 /* socks.c */