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