-doxygen
[oweals/gnunet.git] / src / exit / gnunet-helper-exit.c
1 /*
2      This file is part of GNUnet.
3      (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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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_SERVER_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, "read-error: %s\n", 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, "write-error to stdout: %s\n", strerror (errno));
542           shutdown (fd_tun, SHUT_RD);
543           shutdown (1, SHUT_WR);
544           read_open = 0;
545           buftun_size = 0;
546         }
547         else if (0 == written)
548         {
549           fprintf (stderr, "write returned 0!?\n");
550           exit (1);
551         }
552         else
553         {
554           buftun_size -= written;
555           buftun_read += written;
556         }
557       }
558
559       if (FD_ISSET (0, &fds_r))
560       {
561         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
562         if (-1 == bufin_size)
563         {
564           fprintf (stderr, "read-error: %s\n", strerror (errno));
565           shutdown (0, SHUT_RD);
566           shutdown (fd_tun, SHUT_WR);
567           write_open = 0;
568           bufin_size = 0;
569         }
570         else if (0 == bufin_size)
571         {
572 #if DEBUG
573           fprintf (stderr, "EOF on stdin\n");
574 #endif
575           shutdown (0, SHUT_RD);
576           shutdown (fd_tun, SHUT_WR);
577           write_open = 0;
578           bufin_size = 0;
579         }
580         else
581         {
582           struct GNUNET_MessageHeader *hdr;
583
584 PROCESS_BUFFER:
585           bufin_rpos += bufin_size;
586           if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
587             continue;
588           hdr = (struct GNUNET_MessageHeader *) bufin;
589           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
590           {
591             fprintf (stderr, "protocol violation!\n");
592             exit (1);
593           }
594           if (ntohs (hdr->size) > bufin_rpos)
595             continue;
596           bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
597           bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
598           bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
599         }
600       }
601       else if (FD_ISSET (fd_tun, &fds_w))
602       {
603         ssize_t written = write (fd_tun, bufin_read, bufin_size);
604
605         if (-1 == written)
606         {
607           fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
608           shutdown (0, SHUT_RD);
609           shutdown (fd_tun, SHUT_WR);
610           write_open = 0;
611           bufin_size = 0;
612         }
613         else if (0 == written)
614         {
615           fprintf (stderr, "write returned 0!?\n");
616           exit (1);
617         }
618         else
619         {
620           bufin_size -= written;
621           bufin_read += written;
622           if (0 == bufin_size)
623           {
624             memmove (bufin, bufin_read, bufin_rpos);
625             bufin_read = NULL;  /* start reading again */
626             bufin_size = 0;
627             goto PROCESS_BUFFER;
628           }
629         }
630       }
631     }
632   }
633 }
634
635
636 /**
637  * Open VPN tunnel interface.
638  *
639  * @param argc must be 6
640  * @param argv 0: binary name ("gnunet-helper-exit")
641  *             1: tunnel interface name ("gnunet-exit")
642  *             2: IPv4 "physical" interface name ("eth0"), or "-" to not do IPv4 NAT
643  *             3: IPv6 address ("::1"), or "-" to skip IPv6
644  *             4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"]
645  *             5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4
646  *             6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"]
647  */
648 int
649 main (int argc, char **argv)
650 {
651   char dev[IFNAMSIZ];
652   int fd_tun;
653   int global_ret;
654
655   if (7 != argc)
656   {
657     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
658     return 1;
659   }
660   if ( (0 == strcmp (argv[3], "-")) &&
661        (0 == strcmp (argv[5], "-")) )
662   {
663     fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n");
664     return 1;
665   }
666   if (0 == access ("/sbin/iptables", X_OK))
667     sbin_iptables = "/sbin/iptables";
668   else if (0 == access ("/usr/sbin/iptables", X_OK))
669     sbin_iptables = "/usr/sbin/iptables";
670   else
671   {
672     fprintf (stderr,
673              "Fatal: executable iptables not found in approved directories: %s\n",
674              strerror (errno));
675     return 1;
676   }
677   if (0 == access ("/sbin/sysctl", X_OK))
678     sbin_sysctl = "/sbin/sysctl";
679   else if (0 == access ("/usr/sbin/sysctl", X_OK))
680     sbin_sysctl = "/usr/sbin/sysctl";
681   else
682   {
683     fprintf (stderr,
684              "Fatal: executable sysctl not found in approved directories: %s\n",
685              strerror (errno));
686     return 1;
687   }
688
689   strncpy (dev, argv[1], IFNAMSIZ);
690   dev[IFNAMSIZ - 1] = '\0';
691
692   if (-1 == (fd_tun = init_tun (dev)))
693   {
694     fprintf (stderr,
695              "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n",
696              dev,
697              argv[3],
698              argv[4],
699              argv[5],
700              argv[6]);
701     return 1;
702   }
703
704   if (0 != strcmp (argv[3], "-"))
705   {
706     {
707       const char *address = argv[3];
708       long prefix_len = atol (argv[4]);
709
710       if ((prefix_len < 1) || (prefix_len > 127))
711       {
712         fprintf (stderr, "Fatal: prefix_len out of range\n");
713         return 1;
714       }
715       set_address6 (dev, address, prefix_len);
716     }
717     {
718       char *const sysctl_args[] =
719         {
720           "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL
721         };
722       if (0 != fork_and_exec (sbin_sysctl,
723                               sysctl_args))
724       {
725         fprintf (stderr,
726                  "Failed to enable IPv6 forwarding.  Will continue anyway.\n");
727       }
728     }
729   }
730
731   if (0 != strcmp (argv[5], "-"))
732   {
733     {
734       const char *address = argv[5];
735       const char *mask = argv[6];
736
737       set_address4 (dev, address, mask);
738     }
739     {
740       char *const sysctl_args[] =
741         {
742           "sysctl", "-w", "net.ipv4.ip_forward=1", NULL
743         };
744       if (0 != fork_and_exec (sbin_sysctl,
745                               sysctl_args))
746       {
747         fprintf (stderr,
748                  "Failed to enable IPv4 forwarding.  Will continue anyway.\n");
749       }
750     }
751     if (0 != strcmp (argv[2], "-"))
752     {
753       char *const iptables_args[] =
754         {
755           "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", "MASQUERADE", NULL
756         };
757       if (0 != fork_and_exec (sbin_iptables,
758                               iptables_args))
759       {
760         fprintf (stderr,
761                  "Failed to enable IPv4 masquerading (NAT).  Will continue anyway.\n");
762       }
763     }
764   }
765
766   uid_t uid = getuid ();
767 #ifdef HAVE_SETRESUID
768   if (0 != setresuid (uid, uid, uid))
769   {
770     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
771     global_ret = 2;
772     goto cleanup;
773   }
774 #else
775   if (0 != (setuid (uid) | seteuid (uid)))
776   {
777     fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
778     global_ret = 2;
779     goto cleanup;
780   }
781 #endif
782
783   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
784   {
785     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
786              strerror (errno));
787     /* no exit, we might as well die with SIGPIPE should it ever happen */
788   }
789   run (fd_tun);
790   global_ret = 0;
791  cleanup:
792   (void) close (fd_tun);
793   return global_ret;
794 }
795
796 /* end of gnunet-helper-exit.c */