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