uncrustify as demanded.
[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   struct in6_addr ifr6_addr;
92   uint32_t ifr6_prefixlen;
93   unsigned int ifr6_ifindex;
94 };
95 #endif
96
97 /**
98  * Name and full path of IPTABLES binary.
99  */
100 static const char *sbin_iptables;
101
102 /**
103  * Name and full path of IPTABLES binary.
104  */
105 static const char *sbin_ip6tables;
106
107 /**
108  * Name and full path of sysctl binary
109  */
110 static const char *sbin_sysctl;
111
112 /**
113  * Name and full path of IPTABLES binary.
114  */
115 static const char *sbin_ip;
116
117 /**
118  * Port for DNS traffic.
119  */
120 #define DNS_PORT "53"
121
122 /**
123  * Marker we set for our hijacked DNS traffic.  We use GNUnet's
124  * port (2086) plus the DNS port (53) in HEX to make a 32-bit mark
125  * (which is hopefully long enough to not collide); so
126  * 0x08260035 = 136708149 (hopefully unique enough...).
127  */
128 #define DNS_MARK "136708149"
129
130 /**
131  * Table we use for our DNS rules.  0-255 is the range and
132  * 0, 253, 254 and 255 are already reserved.  As this is about
133  * DNS and as "53" is likely (fingers crossed!) high enough to
134  * not usually conflict with a normal user's setup, we use 53
135  * to give a hint that this has something to do with DNS.
136  */
137 #define DNS_TABLE "53"
138
139
140 /**
141  * Control pipe for shutdown via signal. [0] is the read end,
142  * [1] is the write end.
143  */
144 static int cpipe[2];
145
146
147 /**
148  * Signal handler called to initiate "nice" shutdown.  Signals select
149  * loop via non-bocking pipe 'cpipe'.
150  *
151  * @param signal signal number of the signal (not used)
152  */
153 static void
154 signal_handler(int signal)
155 {
156   /* ignore return value, as the signal handler could theoretically
157      be called many times before the shutdown can actually happen */
158   (void)write(cpipe[1], "K", 1);
159 }
160
161
162 /**
163  * Open '/dev/null' and make the result the given
164  * file descriptor.
165  *
166  * @param target_fd desired FD to point to /dev/null
167  * @param flags open flags (O_RDONLY, O_WRONLY)
168  */
169 static void
170 open_dev_null(int target_fd,
171               int flags)
172 {
173   int fd;
174
175   fd = open("/dev/null", flags);
176   if (-1 == fd)
177     abort();
178   if (fd == target_fd)
179     return;
180   if (-1 == dup2(fd, target_fd))
181     {
182       (void)close(fd);
183       abort();
184     }
185   (void)close(fd);
186 }
187
188
189 /**
190  * Run the given command and wait for it to complete.
191  *
192  * @param file name of the binary to run
193  * @param cmd command line arguments (as given to 'execv')
194  * @return 0 on success, 1 on any error
195  */
196 static int
197 fork_and_exec(const char *file,
198               char *const cmd[])
199 {
200   int status;
201   pid_t pid;
202   pid_t ret;
203
204   pid = fork();
205   if (-1 == pid)
206     {
207       fprintf(stderr,
208               "fork failed: %s\n",
209               strerror(errno));
210       return 1;
211     }
212   if (0 == pid)
213     {
214       /* we are the child process */
215       /* close stdin/stdout to not cause interference
216          with the helper's main protocol! */
217       (void)close(0);
218       open_dev_null(0, O_RDONLY);
219       (void)close(1);
220       open_dev_null(1, O_WRONLY);
221       (void)execv(file, cmd);
222       /* can only get here on error */
223       fprintf(stderr,
224               "exec `%s' failed: %s\n",
225               file,
226               strerror(errno));
227       _exit(1);
228     }
229   /* keep running waitpid as long as the only error we get is 'EINTR' */
230   while ((-1 == (ret = waitpid(pid, &status, 0))) &&
231          (errno == EINTR))
232     ;
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 */