3ed18bb7cb7b920d32f29b677965c9fc099c929b
[oweals/gnunet.git] / src / dns / gnunet-helper-dns.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 dns/gnunet-helper-dns.c
21  * @brief helper to install firewall rules to hijack all DNS traffic
22  *        and send it to our virtual interface (except for DNS traffic
23  *        that originates on the specified port).  We then
24  *        allow interacting with our virtual interface via stdin/stdout.
25  * @author Philipp Tölke
26  * @author Christian Grothoff
27  *
28  * This program alters the Linux firewall rules so that DNS traffic
29  * that ordinarily exits the system can be intercepted and managed by
30  * a virtual interface.  In order to achieve this, DNS traffic is
31  * marked with the DNS_MARK given in below and re-routed to a custom
32  * table with the DNS_TABLE ID given below.  Systems and
33  * administrators must take care to not cause conflicts with these
34  * values (it was deemed safest to hardcode them as passing these
35  * values as arguments might permit messing with arbitrary firewall
36  * rules, which would be dangerous).  Traffic coming from the same
37  * group ID as the effective group ID that this process is running
38  * as is not intercepted.
39  *
40  * The code first sets up the virtual interface, then begins to
41  * redirect the DNS traffic to it, and then on errors or SIGTERM shuts
42  * down the virtual interface and removes the rules for the traffic
43  * redirection.
44  *
45  *
46  * Note that having this binary SUID is only partially safe: it will
47  * allow redirecting (and intercepting / mangling) of all DNS traffic
48  * originating from this system by any user who is able to run it.
49  * Furthermore, this code will make it trivial to DoS all DNS traffic
50  * originating from the current system, simply by sending it to
51  * nowhere (redirect stdout to /dev/null).
52  *
53  * Naturally, neither of these problems can be helped as this is the
54  * fundamental purpose of the binary.  Certifying that this code is
55  * "safe" thus only means that it doesn't allow anything else (such
56  * as local priv. escalation, etc.).
57  *
58  * The following list of people have reviewed this code and considered
59  * it safe (within specifications) since the last modification (if you
60  * reviewed it, please have your name added to the list):
61  *
62  * - Christian Grothoff
63  */
64 #include "platform.h"
65
66 #include <linux/if_tun.h>
67
68 /**
69  * Need 'struct GNUNET_MessageHeader'.
70  */
71 #include "gnunet_crypto_lib.h"
72 #include "gnunet_common.h"
73
74 /**
75  * Need DNS message types.
76  */
77 #include "gnunet_protocols.h"
78
79 /**
80  * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE)
81  */
82 #define MAX_SIZE 65536
83
84 #ifndef _LINUX_IN6_H
85 /**
86  * This is in linux/include/net/ipv6.h, but not always exported...
87  */
88 struct in6_ifreq
89 {
90   struct in6_addr ifr6_addr;
91   uint32_t ifr6_prefixlen;
92   unsigned int ifr6_ifindex;
93 };
94 #endif
95
96 /**
97  * Name and full path of IPTABLES binary.
98  */
99 static const char *sbin_iptables;
100
101 /**
102  * Name and full path of IPTABLES binary.
103  */
104 static const char *sbin_ip6tables;
105
106 /**
107  * Name and full path of sysctl binary
108  */
109 static const char *sbin_sysctl;
110
111 /**
112  * Name and full path of IPTABLES binary.
113  */
114 static const char *sbin_ip;
115
116 /**
117  * Port for DNS traffic.
118  */
119 #define DNS_PORT "53"
120
121 /**
122  * Marker we set for our hijacked DNS traffic.  We use GNUnet's
123  * port (2086) plus the DNS port (53) in HEX to make a 32-bit mark
124  * (which is hopefully long enough to not collide); so
125  * 0x08260035 = 136708149 (hopefully unique enough...).
126  */
127 #define DNS_MARK "136708149"
128
129 /**
130  * Table we use for our DNS rules.  0-255 is the range and
131  * 0, 253, 254 and 255 are already reserved.  As this is about
132  * DNS and as "53" is likely (fingers crossed!) high enough to
133  * not usually conflict with a normal user's setup, we use 53
134  * to give a hint that this has something to do with DNS.
135  */
136 #define DNS_TABLE "53"
137
138
139 /**
140  * Control pipe for shutdown via signal. [0] is the read end,
141  * [1] is the write end.
142  */
143 static int cpipe[2];
144
145
146 /**
147  * Signal handler called to initiate "nice" shutdown.  Signals select
148  * loop via non-bocking pipe 'cpipe'.
149  *
150  * @param signal signal number of the signal (not used)
151  */
152 static void
153 signal_handler (int signal)
154 {
155   /* ignore return value, as the signal handler could theoretically
156      be called many times before the shutdown can actually happen */
157   (void) write (cpipe[1], "K", 1);
158 }
159
160
161 /**
162  * Open '/dev/null' and make the result the given
163  * file descriptor.
164  *
165  * @param target_fd desired FD to point to /dev/null
166  * @param flags open flags (O_RDONLY, O_WRONLY)
167  */
168 static void
169 open_dev_null (int target_fd,
170                int flags)
171 {
172   int fd;
173
174   fd = open ("/dev/null", flags);
175   if (-1 == fd)
176     abort ();
177   if (fd == target_fd)
178     return;
179   if (-1 == dup2 (fd, target_fd))
180   {
181     (void) close (fd);
182     abort ();
183   }
184   (void) close (fd);
185 }
186
187
188 /**
189  * Run the given command and wait for it to complete.
190  *
191  * @param file name of the binary to run
192  * @param cmd command line arguments (as given to 'execv')
193  * @return 0 on success, 1 on any error
194  */
195 static int
196 fork_and_exec (const char *file,
197                char *const cmd[])
198 {
199   int status;
200   pid_t pid;
201   pid_t ret;
202
203   pid = fork ();
204   if (-1 == pid)
205   {
206     fprintf (stderr,
207              "fork failed: %s\n",
208              strerror (errno));
209     return 1;
210   }
211   if (0 == pid)
212   {
213     /* we are the child process */
214     /* close stdin/stdout to not cause interference
215        with the helper's main protocol! */
216     (void) close (0);
217     open_dev_null (0, O_RDONLY);
218     (void) close (1);
219     open_dev_null (1, O_WRONLY);
220     (void) execv (file, cmd);
221     /* can only get here on error */
222     fprintf (stderr,
223              "exec `%s' failed: %s\n",
224              file,
225              strerror (errno));
226     _exit (1);
227   }
228   /* keep running waitpid as long as the only error we get is 'EINTR' */
229   while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
230           (errno == EINTR) );
231   if (-1 == ret)
232   {
233     fprintf (stderr,
234              "waitpid failed: %s\n",
235              strerror (errno));
236     return 1;
237   }
238   if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
239     return 1;
240   /* child process completed and returned success, we're happy */
241   return 0;
242 }
243
244
245 /**
246  * Creates a tun-interface called @a dev;
247  *
248  * @param dev is asumed to point to a char[IFNAMSIZ]
249  *        if *dev == '\\0', uses the name supplied by the kernel;
250  * @return the fd to the tun or -1 on error
251  */
252 static int
253 init_tun (char *dev)
254 {
255   struct ifreq ifr;
256   int fd;
257
258   if (NULL == dev)
259   {
260     errno = EINVAL;
261     return -1;
262   }
263
264   if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
265   {
266     fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
267              strerror (errno));
268     return -1;
269   }
270
271   if (fd >= FD_SETSIZE)
272   {
273     fprintf (stderr, "File descriptor to large: %d", fd);
274     (void) close (fd);
275     return -1;
276   }
277
278   memset (&ifr, 0, sizeof (ifr));
279   ifr.ifr_flags = IFF_TUN;
280
281   if ('\0' != *dev)
282     strncpy (ifr.ifr_name, dev, IFNAMSIZ);
283
284   if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
285   {
286     fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
287              strerror (errno));
288     (void) close (fd);
289     return -1;
290   }
291   strcpy (dev, ifr.ifr_name);
292   return fd;
293 }
294
295
296 /**
297  * @brief Sets the IPv6-Address given in @a address on the interface @a dev
298  *
299  * @param dev the interface to configure
300  * @param address the IPv6-Address
301  * @param prefix_len the length of the network-prefix
302  */
303 static void
304 set_address6 (const char *dev, const char *address, unsigned long prefix_len)
305 {
306   struct ifreq ifr;
307   struct in6_ifreq ifr6;
308   struct sockaddr_in6 sa6;
309   int fd;
310
311   /*
312    * parse the new address
313    */
314   memset (&sa6, 0, sizeof (struct sockaddr_in6));
315   sa6.sin6_family = AF_INET6;
316   if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
317   {
318     fprintf (stderr,
319              "Failed to parse IPv6 address `%s': %s\n",
320              address,
321              strerror (errno));
322     exit (1);
323   }
324
325   if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
326   {
327     fprintf (stderr,
328              "Error creating IPv6 socket: %s (ignored)\n",
329              strerror (errno));
330     /* ignore error, maybe only IPv4 works on this system! */
331     return;
332   }
333
334   memset (&ifr, 0, sizeof (struct ifreq));
335   /*
336    * Get the index of the if
337    */
338   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
339   if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
340   {
341     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
342     (void) close (fd);
343     exit (1);
344   }
345
346   memset (&ifr6, 0, sizeof (struct in6_ifreq));
347   ifr6.ifr6_addr = sa6.sin6_addr;
348   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
349   ifr6.ifr6_prefixlen = prefix_len;
350
351   /*
352    * Set the address
353    */
354   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
355   {
356     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
357              strerror (errno));
358     (void) close (fd);
359     exit (1);
360   }
361
362   /*
363    * Get the flags
364    */
365   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
366   {
367     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
368              strerror (errno));
369     (void) close (fd);
370     exit (1);
371   }
372
373   /*
374    * Add the UP and RUNNING flags
375    */
376   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
377   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
378   {
379     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
380              strerror (errno));
381     (void) close (fd);
382     exit (1);
383   }
384
385   if (0 != close (fd))
386   {
387     fprintf (stderr, "close failed: %s\n", strerror (errno));
388     exit (1);
389   }
390 }
391
392
393 /**
394  * @brief Sets the IPv4-Address given in @a address on the interface @a dev
395  *
396  * @param dev the interface to configure
397  * @param address the IPv4-Address
398  * @param mask the netmask
399  */
400 static void
401 set_address4 (const char *dev, const char *address, const char *mask)
402 {
403   int fd;
404   struct sockaddr_in *addr;
405   struct ifreq ifr;
406
407   memset (&ifr, 0, sizeof (struct ifreq));
408   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
409   addr->sin_family = AF_INET;
410
411   /*
412    * Parse the address
413    */
414   if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
415   {
416     fprintf (stderr,
417              "Failed to parse IPv4 address `%s': %s\n",
418              address,
419              strerror (errno));
420     exit (1);
421   }
422
423   if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
424   {
425     fprintf (stderr,
426              "Error creating IPv4 socket: %s\n",
427              strerror (errno));
428     exit (1);
429   }
430
431   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
432
433   /*
434    * Set the address
435    */
436   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
437   {
438     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
439     (void) close (fd);
440     exit (1);
441   }
442
443   /*
444    * Parse the netmask
445    */
446   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
447   if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
448   {
449     fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
450              strerror (errno));
451     (void) close (fd);
452     exit (1);
453   }
454
455   /*
456    * Set the netmask
457    */
458   if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
459   {
460     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
461              strerror (errno));
462     (void) close (fd);
463     exit (1);
464   }
465
466   /*
467    * Get the flags
468    */
469   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
470   {
471     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
472              strerror (errno));
473     (void) close (fd);
474     exit (1);
475   }
476
477   /*
478    * Add the UP and RUNNING flags
479    */
480   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
481   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
482   {
483     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
484              strerror (errno));
485     (void) close (fd);
486     exit (1);
487   }
488
489   if (0 != close (fd))
490   {
491     fprintf (stderr, "close failed: %s\n", strerror (errno));
492     (void) close (fd);
493     exit (1);
494   }
495 }
496
497
498 /**
499  * Start forwarding to and from the tunnel.  This function runs with
500  * "reduced" priviledges (saved UID is still 0, but effective UID is
501  * the real user ID).
502  *
503  * @param fd_tun tunnel FD
504  */
505 static void
506 run (int fd_tun)
507 {
508   /*
509    * The buffer filled by reading from fd_tun
510    */
511   unsigned char buftun[MAX_SIZE];
512   ssize_t buftun_size = 0;
513   unsigned char *buftun_read = NULL;
514
515   /*
516    * The buffer filled by reading from stdin
517    */
518   unsigned char bufin[MAX_SIZE];
519   ssize_t bufin_size = 0;
520   size_t bufin_rpos = 0;
521   unsigned char *bufin_read = NULL;
522   fd_set fds_w;
523   fd_set fds_r;
524   int max;
525
526   while (1)
527   {
528     FD_ZERO (&fds_w);
529     FD_ZERO (&fds_r);
530
531     /*
532      * We are supposed to read and the buffer is empty
533      * -> select on read from tun
534      */
535     if (0 == buftun_size)
536       FD_SET (fd_tun, &fds_r);
537
538     /*
539      * We are supposed to read and the buffer is not empty
540      * -> select on write to stdout
541      */
542     if (0 < buftun_size)
543       FD_SET (1, &fds_w);
544
545     /*
546      * We are supposed to write and the buffer is empty
547      * -> select on read from stdin
548      */
549     if (NULL == bufin_read)
550       FD_SET (0, &fds_r);
551
552     /*
553      * We are supposed to write and the buffer is not empty
554      * -> select on write to tun
555      */
556     if (NULL != bufin_read)
557       FD_SET (fd_tun, &fds_w);
558
559     FD_SET (cpipe[0], &fds_r);
560     max = (fd_tun > cpipe[0]) ? fd_tun : cpipe[0];
561
562     int r = select (max + 1, &fds_r, &fds_w, NULL, NULL);
563
564     if (-1 == r)
565     {
566       if (EINTR == errno)
567         continue;
568       fprintf (stderr, "select failed: %s\n", strerror (errno));
569       return;
570     }
571
572     if (r > 0)
573     {
574       if (FD_ISSET (cpipe[0], &fds_r))
575         return; /* aborted by signal */
576
577       if (FD_ISSET (fd_tun, &fds_r))
578       {
579         buftun_size =
580             read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
581                   MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
582         if (-1 == buftun_size)
583         {
584           if ( (errno == EINTR) ||
585                (errno == EAGAIN) )
586             {
587               buftun_size = 0;
588               continue;
589             }
590           fprintf (stderr, "read-error: %s\n", strerror (errno));
591           return;
592         }
593         if (0 == buftun_size)
594         {
595           fprintf (stderr, "EOF on tun\n");
596           return;
597         }
598         buftun_read = buftun;
599         {
600           struct GNUNET_MessageHeader *hdr =
601               (struct GNUNET_MessageHeader *) buftun;
602           buftun_size += sizeof (struct GNUNET_MessageHeader);
603           hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
604           hdr->size = htons (buftun_size);
605         }
606       }
607       else if (FD_ISSET (1, &fds_w))
608       {
609         ssize_t written = write (1, buftun_read, buftun_size);
610
611         if (-1 == written)
612         {
613           if ( (errno == EINTR) ||
614                (errno == EAGAIN) )
615             continue;
616           fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
617           return;
618         }
619         if (0 == written)
620         {
621           fprintf (stderr, "write returned 0\n");
622           return;
623         }
624         buftun_size -= written;
625         buftun_read += written;
626       }
627
628       if (FD_ISSET (0, &fds_r))
629       {
630         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
631         if (-1 == bufin_size)
632         {
633           bufin_read = NULL;
634           if ( (errno == EINTR) ||
635                (errno == EAGAIN) )
636             continue;
637           fprintf (stderr, "read-error: %s\n", strerror (errno));
638           return;
639         }
640         if (0 == bufin_size)
641         {
642           bufin_read = NULL;
643           fprintf (stderr, "EOF on stdin\n");
644           return;
645         }
646         {
647           struct GNUNET_MessageHeader *hdr;
648
649 PROCESS_BUFFER:
650           bufin_rpos += bufin_size;
651           if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
652             continue;
653           hdr = (struct GNUNET_MessageHeader *) bufin;
654           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
655           {
656             fprintf (stderr, "protocol violation!\n");
657             return;
658           }
659           if (ntohs (hdr->size) > bufin_rpos)
660             continue;
661           bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
662           bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
663           bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
664         }
665       }
666       else if (FD_ISSET (fd_tun, &fds_w))
667       {
668         ssize_t written = write (fd_tun, bufin_read, bufin_size);
669
670         if (-1 == written)
671         {
672           if ( (errno == EINTR) ||
673                (errno == EAGAIN) )
674             continue;
675           fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
676           return;
677         }
678         if (0 == written)
679         {
680           fprintf (stderr, "write returned 0\n");
681           return;
682         }
683         {
684           bufin_size -= written;
685           bufin_read += written;
686           if (0 == bufin_size)
687           {
688             memmove (bufin, bufin_read, bufin_rpos);
689             bufin_read = NULL;  /* start reading again */
690             bufin_size = 0;
691             goto PROCESS_BUFFER;
692           }
693         }
694       }
695     }
696   }
697 }
698
699
700 /**
701  * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface,
702  * redirects all outgoing DNS traffic (except from the specified port) to that
703  * interface and then passes traffic from and to the interface via stdin/stdout.
704  *
705  * Once stdin/stdout close or have other errors, the tunnel is closed and the
706  * DNS traffic redirection is stopped.
707  *
708  * @param argc number of arguments
709  * @param argv 0: binary name (should be "gnunet-helper-vpn")
710  *             1: tunnel interface name (typically "gnunet-dns")
711  *             2: IPv6 address for the tunnel ("FE80::1")
712  *             3: IPv6 netmask length in bits ("64")
713  *             4: IPv4 address for the tunnel ("1.2.3.4")
714  *             5: IPv4 netmask ("255.255.0.0")
715  *             6: skip sysctl, routing and iptables setup ("0")
716  * @return 0 on success, otherwise code indicating type of error:
717  *         1 wrong number of arguments
718  *         2 invalid arguments (i.e. port number / prefix length wrong)
719  *         3 iptables not executable
720  *         4 ip not executable
721  *         5 failed to initialize tunnel interface
722  *         6 failed to initialize control pipe
723  *         8 failed to change routing table, cleanup successful
724  *         9-23 failed to change routing table and failed to undo some changes to routing table
725  *         24 failed to drop privs
726  *         25-39 failed to drop privs and then failed to undo some changes to routing table
727  *         40 failed to regain privs
728  *         41-55 failed to regain prisv and then failed to undo some changes to routing table
729  *         254 insufficient priviledges
730  *         255 failed to handle kill signal properly
731  */
732 int
733 main (int argc, char *const*argv)
734 {
735   int r;
736   char dev[IFNAMSIZ];
737   char mygid[32];
738   int fd_tun;
739   uid_t uid;
740   int nortsetup = 0;
741
742   if (7 != argc)
743   {
744     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
745     return 1;
746   }
747
748   /* assert privs so we can modify the firewall rules! */
749   uid = getuid ();
750 #ifdef HAVE_SETRESUID
751   if (0 != setresuid (uid, 0, 0))
752   {
753     fprintf (stderr, "Failed to setresuid to root: %s\n", strerror (errno));
754     return 254;
755   }
756 #else
757   if (0 != seteuid (0))
758   {
759     fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
760     return 254;
761   }
762 #endif
763   if (0 == strncmp (argv[6], "1", 2))
764     nortsetup = 1;
765
766   if (0 == nortsetup)
767   {
768     /* verify that the binaries we care about are executable */
769     if (0 == access ("/sbin/iptables", X_OK))
770       sbin_iptables = "/sbin/iptables";
771     else if (0 == access ("/usr/sbin/iptables", X_OK))
772       sbin_iptables = "/usr/sbin/iptables";
773     else
774     {
775       fprintf (stderr,
776                "Fatal: executable iptables not found in approved directories: %s\n",
777                strerror (errno));
778       return 3;
779     }
780     if (0 == access ("/sbin/ip6tables", X_OK))
781       sbin_ip6tables = "/sbin/ip6tables";
782     else if (0 == access ("/usr/sbin/ip6tables", X_OK))
783       sbin_ip6tables = "/usr/sbin/ip6tables";
784     else
785     {
786       fprintf (stderr,
787                "Fatal: executable ip6tables not found in approved directories: %s\n",
788                strerror (errno));
789       return 3;
790     }
791     if (0 == access ("/sbin/ip", X_OK))
792       sbin_ip = "/sbin/ip";
793     else if (0 == access ("/usr/sbin/ip", X_OK))
794       sbin_ip = "/usr/sbin/ip";
795     else if (0 == access ("/bin/ip", X_OK)) /* gentoo has it there */
796       sbin_ip = "/bin/ip";
797     else
798     {
799       fprintf (stderr,
800                "Fatal: executable ip not found in approved directories: %s\n",
801                strerror (errno));
802       return 4;
803     }
804     if (0 == access ("/sbin/sysctl", X_OK))
805       sbin_sysctl = "/sbin/sysctl";
806     else if (0 == access ("/usr/sbin/sysctl", X_OK))
807       sbin_sysctl = "/usr/sbin/sysctl";
808     else
809     {
810       fprintf (stderr,
811                "Fatal: executable sysctl not found in approved directories: %s\n",
812                strerror (errno));
813       return 5;
814     }
815   }
816
817   /* setup 'mygid' string */
818   snprintf (mygid, sizeof (mygid), "%d", (int) getegid());
819
820   /* do not die on SIGPIPE */
821   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
822   {
823     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
824              strerror (errno));
825     return 7;
826   }
827
828   /* setup pipe to shutdown nicely on SIGINT */
829   if (0 != pipe (cpipe))
830   {
831     fprintf (stderr,
832              "Fatal: could not setup control pipe: %s\n",
833              strerror (errno));
834     return 6;
835   }
836   if (cpipe[0] >= FD_SETSIZE)
837   {
838     fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
839     (void) close (cpipe[0]);
840     (void) close (cpipe[1]);
841     return 6;
842   }
843   {
844     /* make pipe non-blocking, as we theoretically could otherwise block
845        in the signal handler */
846     int flags = fcntl (cpipe[1], F_GETFL);
847     if (-1 == flags)
848     {
849       fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
850       (void) close (cpipe[0]);
851       (void) close (cpipe[1]);
852       return 6;
853     }
854     flags |= O_NONBLOCK;
855     if (0 != fcntl (cpipe[1], F_SETFL, flags))
856     {
857       fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (errno));
858       (void) close (cpipe[0]);
859       (void) close (cpipe[1]);
860       return 6;
861     }
862   }
863   if ( (SIG_ERR == signal (SIGTERM, &signal_handler)) ||
864 #if (SIGTERM != GNUNET_TERM_SIG)
865        (SIG_ERR == signal (GNUNET_TERM_SIG, &signal_handler)) ||
866 #endif
867        (SIG_ERR == signal (SIGINT, &signal_handler)) ||
868        (SIG_ERR == signal (SIGHUP, &signal_handler)) )
869   {
870     fprintf (stderr,
871              "Fatal: could not initialize signal handler: %s\n",
872              strerror (errno));
873     (void) close (cpipe[0]);
874     (void) close (cpipe[1]);
875     return 7;
876   }
877
878
879   /* get interface name */
880   strncpy (dev, argv[1], IFNAMSIZ);
881   dev[IFNAMSIZ - 1] = '\0';
882
883   /* Disable rp filtering */
884   if (0 == nortsetup)
885   {
886     char *const sysctl_args[] = {"sysctl", "-w",
887       "net.ipv4.conf.all.rp_filter=0", NULL};
888     char *const sysctl_args2[] = {"sysctl", "-w",
889       "net.ipv4.conf.default.rp_filter=0", NULL};
890     if ((0 != fork_and_exec (sbin_sysctl, sysctl_args)) ||
891         (0 != fork_and_exec (sbin_sysctl, sysctl_args2)))
892     {
893       fprintf (stderr,
894                "Failed to disable rp filtering.\n");
895       return 5;
896     }
897   }
898
899
900   /* now open virtual interface (first part that requires root) */
901   if (-1 == (fd_tun = init_tun (dev)))
902   {
903     fprintf (stderr, "Fatal: could not initialize tun-interface\n");
904     (void) signal (SIGTERM, SIG_IGN);
905 #if (SIGTERM != GNUNET_TERM_SIG)
906     (void) signal (GNUNET_TERM_SIG, SIG_IGN);
907 #endif
908     (void) signal (SIGINT, SIG_IGN);
909     (void) signal (SIGHUP, SIG_IGN);
910     (void) close (cpipe[0]);
911     (void) close (cpipe[1]);
912     return 5;
913   }
914
915   /* now set interface addresses */
916   {
917     const char *address = argv[2];
918     long prefix_len = atol (argv[3]);
919
920     if ((prefix_len < 1) || (prefix_len > 127))
921     {
922       fprintf (stderr, "Fatal: prefix_len out of range\n");
923       (void) signal (SIGTERM, SIG_IGN);
924 #if (SIGTERM != GNUNET_TERM_SIG)
925     (void) signal (GNUNET_TERM_SIG, SIG_IGN);
926 #endif
927       (void) signal (SIGINT, SIG_IGN);
928       (void) signal (SIGHUP, SIG_IGN);
929       (void) close (cpipe[0]);
930       (void) close (cpipe[1]);
931       return 2;
932     }
933     set_address6 (dev, address, prefix_len);
934   }
935
936   {
937     const char *address = argv[4];
938     const char *mask = argv[5];
939
940     set_address4 (dev, address, mask);
941   }
942
943
944   /* update routing tables -- next part why we need SUID! */
945   /* Forward everything from our EGID (which should only be held
946      by the 'gnunet-service-dns') and with destination
947      to port 53 on UDP, without hijacking */
948   if (0 == nortsetup)
949   {
950     r = 8; /* failed to fully setup routing table */
951     {
952       char *const mangle_args[] =
953         {
954          "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
955          "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
956          "ACCEPT", NULL
957         };
958       if (0 != fork_and_exec (sbin_iptables, mangle_args))
959         goto cleanup_rest;
960     }
961     {
962       char *const mangle_args[] =
963         {
964          "ip6tables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
965          "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
966          "ACCEPT", NULL
967         };
968       if (0 != fork_and_exec (sbin_ip6tables, mangle_args))
969         goto cleanup_mangle_1b;
970     }
971     /* Mark all of the other DNS traffic using our mark DNS_MARK,
972        unless it is on a link-local IPv6 address, which we cannot support. */
973     {
974       char *const mark_args[] =
975         {
976          "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
977          "udp", "--dport", DNS_PORT,
978          "-j", "MARK", "--set-mark", DNS_MARK,
979          NULL
980         };
981       if (0 != fork_and_exec (sbin_iptables, mark_args))
982         goto cleanup_mangle_1;
983     }
984     {
985       char *const mark_args[] =
986         {
987          "ip6tables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
988          "udp", "--dport", DNS_PORT,
989          "!", "-s", "fe80::/10", /* this line excludes link-local traffic */
990          "-j", "MARK", "--set-mark", DNS_MARK,
991          NULL
992         };
993       if (0 != fork_and_exec (sbin_ip6tables, mark_args))
994         goto cleanup_mark_2b;
995     }
996     /* Forward all marked DNS traffic to our DNS_TABLE */
997     {
998       char *const forward_args[] =
999         {
1000          "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1001         };
1002       if (0 != fork_and_exec (sbin_ip, forward_args))
1003         goto cleanup_mark_2;
1004     }
1005     {
1006       char *const forward_args[] =
1007         {
1008           "ip", "-6", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1009         };
1010       if (0 != fork_and_exec (sbin_ip, forward_args))
1011         goto cleanup_forward_3b;
1012     }
1013     /* Finally, add rule in our forwarding table to pass to our virtual interface */
1014     {
1015       char *const route_args[] =
1016         {
1017          "ip", "route", "add", "default", "dev", dev,
1018          "table", DNS_TABLE, NULL
1019         };
1020       if (0 != fork_and_exec (sbin_ip, route_args))
1021         goto cleanup_forward_3;
1022     }
1023     {
1024       char *const route_args[] =
1025         {
1026           "ip", "-6", "route", "add", "default", "dev", dev,
1027           "table", DNS_TABLE, NULL
1028         };
1029       if (0 != fork_and_exec (sbin_ip, route_args))
1030         goto cleanup_route_4b;
1031     }
1032   }
1033
1034   /* drop privs *except* for the saved UID; this is not perfect, but better
1035      than doing nothing */
1036 #ifdef HAVE_SETRESUID
1037   if (0 != setresuid (uid, uid, 0))
1038   {
1039     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
1040     r = 24;
1041     goto cleanup_route_4;
1042   }
1043 #else
1044   /* Note: no 'setuid' here as we must keep our saved UID as root */
1045   if (0 != seteuid (uid))
1046   {
1047     fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
1048     r = 24;
1049     goto cleanup_route_4;
1050   }
1051 #endif
1052
1053   r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
1054
1055   /* now forward until we hit a problem */
1056   run (fd_tun);
1057
1058   /* now need to regain privs so we can remove the firewall rules we added! */
1059 #ifdef HAVE_SETRESUID
1060   if (0 != setresuid (uid, 0, 0))
1061   {
1062     fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (errno));
1063     r = 40;
1064     goto cleanup_route_4;
1065   }
1066 #else
1067   if (0 != seteuid (0))
1068   {
1069     fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
1070     r = 40;
1071     goto cleanup_route_4;
1072   }
1073 #endif
1074
1075   /* update routing tables again -- this is why we could not fully drop privs */
1076   /* now undo updating of routing tables; normal exit or clean-up-on-error case */
1077  cleanup_route_4:
1078   if (0 == nortsetup)
1079   {
1080     char *const route_clean_args[] =
1081       {
1082         "ip", "-6", "route", "del", "default", "dev", dev,
1083         "table", DNS_TABLE, NULL
1084       };
1085     if (0 != fork_and_exec (sbin_ip, route_clean_args))
1086       r += 1;
1087   }
1088  cleanup_route_4b:
1089   if (0 == nortsetup)
1090   {
1091     char *const route_clean_args[] =
1092       {
1093         "ip", "route", "del", "default", "dev", dev,
1094         "table", DNS_TABLE, NULL
1095       };
1096     if (0 != fork_and_exec (sbin_ip, route_clean_args))
1097       r += 1;
1098   }
1099  cleanup_forward_3:
1100   if (0 == nortsetup)
1101   {
1102     char *const forward_clean_args[] =
1103       {
1104         "ip", "-6", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1105       };
1106     if (0 != fork_and_exec (sbin_ip, forward_clean_args))
1107       r += 2;
1108   }
1109  cleanup_forward_3b:
1110   if (0 == nortsetup)
1111   {
1112     char *const forward_clean_args[] =
1113       {
1114         "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1115       };
1116     if (0 != fork_and_exec (sbin_ip, forward_clean_args))
1117       r += 2;
1118   }
1119  cleanup_mark_2:
1120   if (0 == nortsetup)
1121   {
1122     char *const mark_clean_args[] =
1123       {
1124         "ip6tables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1125         "--dport", DNS_PORT,
1126         "!", "-s", "fe80::/10", /* this line excludes link-local traffic */
1127         "-j", "MARK", "--set-mark", DNS_MARK, NULL
1128       };
1129     if (0 != fork_and_exec (sbin_ip6tables, mark_clean_args))
1130       r += 4;
1131   }
1132  cleanup_mark_2b:
1133   if (0 == nortsetup)
1134   {
1135     char *const mark_clean_args[] =
1136       {
1137         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1138         "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
1139       };
1140     if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
1141       r += 4;
1142   }
1143  cleanup_mangle_1:
1144   if (0 == nortsetup)
1145   {
1146     char *const mangle_clean_args[] =
1147       {
1148         "ip6tables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1149          "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
1150         NULL
1151       };
1152     if (0 != fork_and_exec (sbin_ip6tables, mangle_clean_args))
1153       r += 8;
1154   }
1155  cleanup_mangle_1b:
1156   if (0 == nortsetup)
1157   {
1158     char *const mangle_clean_args[] =
1159       {
1160         "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1161          "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
1162         NULL
1163       };
1164     if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
1165       r += 8;
1166   }
1167
1168  cleanup_rest:
1169   /* close virtual interface */
1170   (void) close (fd_tun);
1171   /* remove signal handler so we can close the pipes */
1172   (void) signal (SIGTERM, SIG_IGN);
1173 #if (SIGTERM != GNUNET_TERM_SIG)
1174     (void) signal (GNUNET_TERM_SIG, SIG_IGN);
1175 #endif
1176   (void) signal (SIGINT, SIG_IGN);
1177   (void) signal (SIGHUP, SIG_IGN);
1178   (void) close (cpipe[0]);
1179   (void) close (cpipe[1]);
1180   return r;
1181 }
1182
1183 /* end of gnunet-helper-dns.c */