Merge branch 'abe' into identity_abe
[oweals/gnunet.git] / src / exit / gnunet-helper-exit.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010, 2011, 2012 Christian Grothoff
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file exit/gnunet-helper-exit.c
23  *
24  * @brief the helper for exit nodes. Opens a virtual
25  * network-interface, sends data received on the if to stdout, sends
26  * data received on stdin to the interface.  The code also enables
27  * IPv4/IPv6 forwarding and NAT on the current system (the latter on
28  * an interface specified on the command-line); these changes to the
29  * network configuration are NOT automatically undone when the program
30  * is stopped (this is because we cannot be sure that some other
31  * application didn't enable them before or after us; also, these
32  * changes should be mostly harmless as it simply turns the system
33  * into a router).
34  *
35  * @author Philipp Tölke
36  * @author Christian Grothoff
37  *
38  * The following list of people have reviewed this code and considered
39  * it safe since the last modification (if you reviewed it, please
40  * have your name added to the list):
41  *
42  * - Philipp Tölke
43  */
44 #include "platform.h"
45 #include <linux/if_tun.h>
46
47 /**
48  * Need 'struct GNUNET_MessageHeader'.
49  */
50 #include "gnunet_crypto_lib.h"
51 #include "gnunet_common.h"
52
53 /**
54  * Need VPN message types.
55  */
56 #include "gnunet_protocols.h"
57
58 /**
59  * Should we print (interesting|debug) messages that can happen during
60  * normal operation?
61  */
62 #define DEBUG GNUNET_NO
63
64 /**
65  * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE)
66  */
67 #define MAX_SIZE 65536
68
69 /**
70  * Path to 'sysctl' binary.
71  */
72 static const char *sbin_sysctl;
73
74 /**
75  * Path to 'iptables' binary.
76  */
77 static const char *sbin_iptables;
78
79
80 #ifndef _LINUX_IN6_H
81 /**
82  * This is in linux/include/net/ipv6.h, but not always exported...
83  */
84 struct in6_ifreq
85 {
86   struct in6_addr ifr6_addr;
87   uint32_t ifr6_prefixlen; /* __u32 in the original */
88   int ifr6_ifindex;
89 };
90 #endif
91
92
93 /**
94  * Open '/dev/null' and make the result the given
95  * file descriptor.
96  *
97  * @param target_fd desired FD to point to /dev/null
98  * @param flags open flags (O_RDONLY, O_WRONLY)
99  */
100 static void
101 open_dev_null (int target_fd,
102                int flags)
103 {
104   int fd;
105
106   fd = open ("/dev/null", flags);
107   if (-1 == fd)
108     abort ();
109   if (fd == target_fd)
110     return;
111   if (-1 == dup2 (fd, target_fd))
112   {
113     (void) close (fd);
114     abort ();
115   }
116   (void) close (fd);
117 }
118
119
120 /**
121  * Run the given command and wait for it to complete.
122  *
123  * @param file name of the binary to run
124  * @param cmd command line arguments (as given to 'execv')
125  * @return 0 on success, 1 on any error
126  */
127 static int
128 fork_and_exec (const char *file,
129                char *const cmd[])
130 {
131   int status;
132   pid_t pid;
133   pid_t ret;
134
135   pid = fork ();
136   if (-1 == pid)
137   {
138     fprintf (stderr,
139              "fork failed: %s\n",
140              strerror (errno));
141     return 1;
142   }
143   if (0 == pid)
144   {
145     /* we are the child process */
146     /* close stdin/stdout to not cause interference
147        with the helper's main protocol! */
148     (void) close (0);
149     open_dev_null (0, O_RDONLY);
150     (void) close (1);
151     open_dev_null (1, O_WRONLY);
152     (void) execv (file, cmd);
153     /* can only get here on error */
154     fprintf (stderr,
155              "exec `%s' failed: %s\n",
156              file,
157              strerror (errno));
158     _exit (1);
159   }
160   /* keep running waitpid as long as the only error we get is 'EINTR' */
161   while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
162           (errno == EINTR) );
163   if (-1 == ret)
164   {
165     fprintf (stderr,
166              "waitpid failed: %s\n",
167              strerror (errno));
168     return 1;
169   }
170   if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
171     return 1;
172   /* child process completed and returned success, we're happy */
173   return 0;
174 }
175
176
177 /**
178  * Creates a tun-interface called dev;
179  *
180  * @param dev is asumed to point to a char[IFNAMSIZ]
181  *        if *dev == '\\0', uses the name supplied by the kernel;
182  * @return the fd to the tun or -1 on error
183  */
184 static int
185 init_tun (char *dev)
186 {
187   struct ifreq ifr;
188   int fd;
189
190   if (NULL == dev)
191   {
192     errno = EINVAL;
193     return -1;
194   }
195
196   if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
197   {
198     fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
199              strerror (errno));
200     return -1;
201   }
202
203   if (fd >= FD_SETSIZE)
204   {
205     fprintf (stderr, "File descriptor to large: %d", fd);
206     (void) close (fd);
207     return -1;
208   }
209
210   memset (&ifr, 0, sizeof (ifr));
211   ifr.ifr_flags = IFF_TUN;
212
213   if ('\0' != *dev)
214     strncpy (ifr.ifr_name, dev, IFNAMSIZ);
215
216   if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
217   {
218     fprintf (stderr,
219              "Error with ioctl on `%s': %s\n", "/dev/net/tun",
220              strerror (errno));
221     (void) close (fd);
222     return -1;
223   }
224   strcpy (dev, ifr.ifr_name);
225   return fd;
226 }
227
228
229 /**
230  * @brief Sets the IPv6-Address given in address on the interface dev
231  *
232  * @param dev the interface to configure
233  * @param address the IPv6-Address
234  * @param prefix_len the length of the network-prefix
235  */
236 static void
237 set_address6 (const char *dev, const char *address, unsigned long prefix_len)
238 {
239   struct ifreq ifr;
240   struct sockaddr_in6 sa6;
241   int fd;
242   struct in6_ifreq ifr6;
243
244   /*
245    * parse the new address
246    */
247   memset (&sa6, 0, sizeof (struct sockaddr_in6));
248   sa6.sin6_family = AF_INET6;
249   if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr))
250   {
251     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
252              strerror (errno));
253     exit (1);
254   }
255
256   if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
257   {
258     fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
259     exit (1);
260   }
261
262   memset (&ifr, 0, sizeof (struct ifreq));
263   /*
264    * Get the index of the if
265    */
266   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
267   if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
268   {
269     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
270     (void) close (fd);
271     exit (1);
272   }
273
274   memset (&ifr6, 0, sizeof (struct in6_ifreq));
275   ifr6.ifr6_addr = sa6.sin6_addr;
276   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
277   ifr6.ifr6_prefixlen = prefix_len;
278
279   /*
280    * Set the address
281    */
282   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
283   {
284     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
285              strerror (errno));
286     (void) close (fd);
287     exit (1);
288   }
289
290   /*
291    * Get the flags
292    */
293   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
294   {
295     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
296              strerror (errno));
297     (void) close (fd);
298     exit (1);
299   }
300
301   /*
302    * Add the UP and RUNNING flags
303    */
304   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
305   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
306   {
307     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
308              strerror (errno));
309     (void) close (fd);
310     exit (1);
311   }
312
313   if (0 != close (fd))
314   {
315     fprintf (stderr, "close failed: %s\n", strerror (errno));
316     exit (1);
317   }
318 }
319
320
321 /**
322  * @brief Sets the IPv4-Address given in address on the interface dev
323  *
324  * @param dev the interface to configure
325  * @param address the IPv4-Address
326  * @param mask the netmask
327  */
328 static void
329 set_address4 (const char *dev, const char *address, const char *mask)
330 {
331   int fd;
332   struct sockaddr_in *addr;
333   struct ifreq ifr;
334
335   memset (&ifr, 0, sizeof (struct ifreq));
336   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
337   addr->sin_family = AF_INET;
338
339   /*
340    * Parse the address
341    */
342   if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
343   {
344     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
345              strerror (errno));
346     exit (1);
347   }
348
349   if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
350   {
351     fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
352     exit (1);
353   }
354
355   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
356
357   /*
358    * Set the address
359    */
360   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
361   {
362     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
363     (void) close (fd);
364     exit (1);
365   }
366
367   /*
368    * Parse the netmask
369    */
370   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
371   if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
372   {
373     fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
374              strerror (errno));
375     (void) close (fd);
376     exit (1);
377   }
378
379   /*
380    * Set the netmask
381    */
382   if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
383   {
384     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
385              strerror (errno));
386     (void) close (fd);
387     exit (1);
388   }
389
390   /*
391    * Get the flags
392    */
393   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
394   {
395     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
396              strerror (errno));
397     (void) close (fd);
398     exit (1);
399   }
400
401   /*
402    * Add the UP and RUNNING flags
403    */
404   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
405   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
406   {
407     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
408              strerror (errno));
409     (void) close (fd);
410     exit (1);
411   }
412
413   if (0 != close (fd))
414   {
415     fprintf (stderr, "close failed: %s\n", strerror (errno));
416     (void) close (fd);
417     exit (1);
418   }
419 }
420
421
422 /**
423  * Start forwarding to and from the tunnel.
424  *
425  * @param fd_tun tunnel FD
426  */
427 static void
428 run (int fd_tun)
429 {
430   /*
431    * The buffer filled by reading from fd_tun
432    */
433   unsigned char buftun[MAX_SIZE];
434   ssize_t buftun_size = 0;
435   unsigned char *buftun_read = NULL;
436
437   /*
438    * The buffer filled by reading from stdin
439    */
440   unsigned char bufin[MAX_SIZE];
441   ssize_t bufin_size = 0;
442   size_t bufin_rpos = 0;
443   unsigned char *bufin_read = NULL;
444
445   fd_set fds_w;
446   fd_set fds_r;
447
448   /* read refers to reading from fd_tun, writing to stdout */
449   int read_open = 1;
450
451   /* write refers to reading from stdin, writing to fd_tun */
452   int write_open = 1;
453
454   while ((1 == read_open) && (1 == write_open))
455   {
456     FD_ZERO (&fds_w);
457     FD_ZERO (&fds_r);
458
459     /*
460      * We are supposed to read and the buffer is empty
461      * -> select on read from tun
462      */
463     if (read_open && (0 == buftun_size))
464       FD_SET (fd_tun, &fds_r);
465
466     /*
467      * We are supposed to read and the buffer is not empty
468      * -> select on write to stdout
469      */
470     if (read_open && (0 != buftun_size))
471       FD_SET (1, &fds_w);
472
473     /*
474      * We are supposed to write and the buffer is empty
475      * -> select on read from stdin
476      */
477     if (write_open && (NULL == bufin_read))
478       FD_SET (0, &fds_r);
479
480     /*
481      * We are supposed to write and the buffer is not empty
482      * -> select on write to tun
483      */
484     if (write_open && (NULL != bufin_read))
485       FD_SET (fd_tun, &fds_w);
486
487     int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL);
488
489     if (-1 == r)
490     {
491       if (EINTR == errno)
492         continue;
493       fprintf (stderr, "select failed: %s\n", strerror (errno));
494       exit (1);
495     }
496
497     if (r > 0)
498     {
499       if (FD_ISSET (fd_tun, &fds_r))
500       {
501         buftun_size =
502             read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
503                   MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
504         if (-1 == buftun_size)
505         {
506           fprintf (stderr,
507                    "read-error: %s\n",
508                    strerror (errno));
509           shutdown (fd_tun, SHUT_RD);
510           shutdown (1, SHUT_WR);
511           read_open = 0;
512           buftun_size = 0;
513         }
514         else if (0 == buftun_size)
515         {
516 #if DEBUG
517           fprintf (stderr, "EOF on tun\n");
518 #endif
519           shutdown (fd_tun, SHUT_RD);
520           shutdown (1, SHUT_WR);
521           read_open = 0;
522           buftun_size = 0;
523         }
524         else
525         {
526           buftun_read = buftun;
527           struct GNUNET_MessageHeader *hdr =
528               (struct GNUNET_MessageHeader *) buftun;
529           buftun_size += sizeof (struct GNUNET_MessageHeader);
530           hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
531           hdr->size = htons (buftun_size);
532         }
533       }
534       else if (FD_ISSET (1, &fds_w))
535       {
536         ssize_t written = write (1, buftun_read, buftun_size);
537
538         if (-1 == written)
539         {
540 #if !DEBUG
541           if (errno != EPIPE)
542 #endif
543             fprintf (stderr,
544                      "write-error to stdout: %s\n",
545                      strerror (errno));
546           shutdown (fd_tun, SHUT_RD);
547           shutdown (1, SHUT_WR);
548           read_open = 0;
549           buftun_size = 0;
550         }
551         else if (0 == written)
552         {
553           fprintf (stderr, "write returned 0!?\n");
554           exit (1);
555         }
556         else
557         {
558           buftun_size -= written;
559           buftun_read += written;
560         }
561       }
562
563       if (FD_ISSET (0, &fds_r))
564       {
565         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
566         if (-1 == bufin_size)
567         {
568           fprintf (stderr, "read-error: %s\n", strerror (errno));
569           shutdown (0, SHUT_RD);
570           shutdown (fd_tun, SHUT_WR);
571           write_open = 0;
572           bufin_size = 0;
573         }
574         else if (0 == bufin_size)
575         {
576 #if DEBUG
577           fprintf (stderr, "EOF on stdin\n");
578 #endif
579           shutdown (0, SHUT_RD);
580           shutdown (fd_tun, SHUT_WR);
581           write_open = 0;
582           bufin_size = 0;
583         }
584         else
585         {
586           struct GNUNET_MessageHeader *hdr;
587
588 PROCESS_BUFFER:
589           bufin_rpos += bufin_size;
590           if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
591             continue;
592           hdr = (struct GNUNET_MessageHeader *) bufin;
593           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
594           {
595             fprintf (stderr, "protocol violation!\n");
596             exit (1);
597           }
598           if (ntohs (hdr->size) > bufin_rpos)
599             continue;
600           bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
601           bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
602           bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
603         }
604       }
605       else if (FD_ISSET (fd_tun, &fds_w))
606       {
607         ssize_t written = write (fd_tun, bufin_read, bufin_size);
608
609         if (-1 == written)
610         {
611           fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
612           shutdown (0, SHUT_RD);
613           shutdown (fd_tun, SHUT_WR);
614           write_open = 0;
615           bufin_size = 0;
616         }
617         else if (0 == written)
618         {
619           fprintf (stderr, "write returned 0!?\n");
620           exit (1);
621         }
622         else
623         {
624           bufin_size -= written;
625           bufin_read += written;
626           if (0 == bufin_size)
627           {
628             memmove (bufin, bufin_read, bufin_rpos);
629             bufin_read = NULL;  /* start reading again */
630             bufin_size = 0;
631             goto PROCESS_BUFFER;
632           }
633         }
634       }
635     }
636   }
637 }
638
639
640 /**
641  * Open VPN tunnel interface.
642  *
643  * @param argc must be 6
644  * @param argv 0: binary name ("gnunet-helper-exit")
645  *             1: tunnel interface name ("gnunet-exit")
646  *             2: "physical" interface name ("eth0"), or "-" to not setup NAT
647  *                and routing
648  *             3: IPv6 address ("::1"), or "-" to skip IPv6
649  *             4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"]
650  *             5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4
651  *             6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"]
652  */
653 int
654 main (int argc, char **argv)
655 {
656   char dev[IFNAMSIZ];
657   int fd_tun;
658   int global_ret;
659
660   if (7 != argc)
661   {
662     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
663     return 1;
664   }
665   if ( (0 == strcmp (argv[3], "-")) &&
666        (0 == strcmp (argv[5], "-")) )
667   {
668     fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n");
669     return 1;
670   }
671   if (0 != strcmp (argv[2], "-"))
672   {
673     if (0 == access ("/sbin/iptables", X_OK))
674       sbin_iptables = "/sbin/iptables";
675     else if (0 == access ("/usr/sbin/iptables", X_OK))
676       sbin_iptables = "/usr/sbin/iptables";
677     else
678     {
679       fprintf (stderr,
680                "Fatal: executable iptables not found in approved directories: %s\n",
681                strerror (errno));
682       return 1;
683     }
684     if (0 == access ("/sbin/sysctl", X_OK))
685       sbin_sysctl = "/sbin/sysctl";
686     else if (0 == access ("/usr/sbin/sysctl", X_OK))
687       sbin_sysctl = "/usr/sbin/sysctl";
688     else
689     {
690       fprintf (stderr,
691                "Fatal: executable sysctl not found in approved directories: %s\n",
692                strerror (errno));
693       return 1;
694     }
695   }
696
697   strncpy (dev, argv[1], IFNAMSIZ);
698   dev[IFNAMSIZ - 1] = '\0';
699
700   if (-1 == (fd_tun = init_tun (dev)))
701   {
702     fprintf (stderr,
703              "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n",
704              dev,
705              argv[3],
706              argv[4],
707              argv[5],
708              argv[6]);
709     return 1;
710   }
711
712   if (0 != strcmp (argv[3], "-"))
713   {
714     {
715       const char *address = argv[3];
716       long prefix_len = atol (argv[4]);
717
718       if ((prefix_len < 1) || (prefix_len > 127))
719       {
720         fprintf (stderr, "Fatal: prefix_len out of range\n");
721         return 1;
722       }
723       set_address6 (dev, address, prefix_len);
724     }
725     if (0 != strcmp (argv[2], "-"))
726     {
727       char *const sysctl_args[] =
728         {
729           "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL
730         };
731       if (0 != fork_and_exec (sbin_sysctl,
732                               sysctl_args))
733       {
734         fprintf (stderr,
735                  "Failed to enable IPv6 forwarding.  Will continue anyway.\n");
736       }
737     }
738   }
739
740   if (0 != strcmp (argv[5], "-"))
741   {
742     {
743       const char *address = argv[5];
744       const char *mask = argv[6];
745
746       set_address4 (dev, address, mask);
747     }
748     if (0 != strcmp (argv[2], "-"))
749     {
750       {
751         char *const sysctl_args[] =
752           {
753             "sysctl", "-w", "net.ipv4.ip_forward=1", NULL
754           };
755         if (0 != fork_and_exec (sbin_sysctl,
756                                 sysctl_args))
757         {
758           fprintf (stderr,
759                    "Failed to enable IPv4 forwarding.  Will continue anyway.\n");
760         }
761       }
762       {
763         char *const iptables_args[] =
764           {
765             "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", "MASQUERADE", NULL
766           };
767         if (0 != fork_and_exec (sbin_iptables,
768                                 iptables_args))
769         {
770           fprintf (stderr,
771                    "Failed to enable IPv4 masquerading (NAT).  Will continue anyway.\n");
772         }
773       }
774     }
775   }
776
777   uid_t uid = getuid ();
778 #ifdef HAVE_SETRESUID
779   if (0 != setresuid (uid, uid, uid))
780   {
781     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
782     global_ret = 2;
783     goto cleanup;
784   }
785 #else
786   if (0 != (setuid (uid) | seteuid (uid)))
787   {
788     fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
789     global_ret = 2;
790     goto cleanup;
791   }
792 #endif
793
794   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
795   {
796     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
797              strerror (errno));
798     /* no exit, we might as well die with SIGPIPE should it ever happen */
799   }
800   run (fd_tun);
801   global_ret = 0;
802  cleanup:
803   (void) close (fd_tun);
804   return global_ret;
805 }
806
807 /* end of gnunet-helper-exit.c */