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