global reindent, now with uncrustify hook enabled
[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     ;
234   if (-1 == ret)
235   {
236     fprintf (stderr,
237              "waitpid failed: %s\n",
238              strerror (errno));
239     return 1;
240   }
241   if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
242     return 1;
243   /* child process completed and returned success, we're happy */
244   return 0;
245 }
246
247
248 /**
249  * Creates a tun-interface called @a dev;
250  *
251  * @param dev is asumed to point to a char[IFNAMSIZ]
252  *        if *dev == '\\0', uses the name supplied by the kernel;
253  * @return the fd to the tun or -1 on error
254  */
255 static int
256 init_tun (char *dev)
257 {
258   struct ifreq ifr;
259   int fd;
260
261   if (NULL == dev)
262   {
263     errno = EINVAL;
264     return -1;
265   }
266
267   if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
268   {
269     fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
270              strerror (errno));
271     return -1;
272   }
273
274   if (fd >= FD_SETSIZE)
275   {
276     fprintf (stderr, "File descriptor to large: %d", fd);
277     (void) close (fd);
278     return -1;
279   }
280
281   memset (&ifr, 0, sizeof(ifr));
282   ifr.ifr_flags = IFF_TUN;
283
284   if ('\0' != *dev)
285     strncpy (ifr.ifr_name, dev, IFNAMSIZ);
286
287   if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
288   {
289     fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
290              strerror (errno));
291     (void) close (fd);
292     return -1;
293   }
294   strcpy (dev, ifr.ifr_name);
295   return fd;
296 }
297
298
299 /**
300  * @brief Sets the IPv6-Address given in @a address on the interface @a dev
301  *
302  * @param dev the interface to configure
303  * @param address the IPv6-Address
304  * @param prefix_len the length of the network-prefix
305  */
306 static void
307 set_address6 (const char *dev, const char *address, unsigned long prefix_len)
308 {
309   struct ifreq ifr;
310   struct in6_ifreq ifr6;
311   struct sockaddr_in6 sa6;
312   int fd;
313
314   /*
315    * parse the new address
316    */
317   memset (&sa6, 0, sizeof(struct sockaddr_in6));
318   sa6.sin6_family = AF_INET6;
319   if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
320   {
321     fprintf (stderr,
322              "Failed to parse IPv6 address `%s': %s\n",
323              address,
324              strerror (errno));
325     exit (1);
326   }
327
328   if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
329   {
330     fprintf (stderr,
331              "Error creating IPv6 socket: %s (ignored)\n",
332              strerror (errno));
333     /* ignore error, maybe only IPv4 works on this system! */
334     return;
335   }
336
337   memset (&ifr, 0, sizeof(struct ifreq));
338   /*
339    * Get the index of the if
340    */
341   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
342   if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
343   {
344     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
345     (void) close (fd);
346     exit (1);
347   }
348
349   memset (&ifr6, 0, sizeof(struct in6_ifreq));
350   ifr6.ifr6_addr = sa6.sin6_addr;
351   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
352   ifr6.ifr6_prefixlen = prefix_len;
353
354   /*
355    * Set the address
356    */
357   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
358   {
359     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
360              strerror (errno));
361     (void) close (fd);
362     exit (1);
363   }
364
365   /*
366    * Get the flags
367    */
368   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
369   {
370     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
371              strerror (errno));
372     (void) close (fd);
373     exit (1);
374   }
375
376   /*
377    * Add the UP and RUNNING flags
378    */
379   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
380   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
381   {
382     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
383              strerror (errno));
384     (void) close (fd);
385     exit (1);
386   }
387
388   if (0 != close (fd))
389   {
390     fprintf (stderr, "close failed: %s\n", strerror (errno));
391     exit (1);
392   }
393 }
394
395
396 /**
397  * @brief Sets the IPv4-Address given in @a address on the interface @a dev
398  *
399  * @param dev the interface to configure
400  * @param address the IPv4-Address
401  * @param mask the netmask
402  */
403 static void
404 set_address4 (const char *dev, const char *address, const char *mask)
405 {
406   int fd;
407   struct sockaddr_in *addr;
408   struct ifreq ifr;
409
410   memset (&ifr, 0, sizeof(struct ifreq));
411   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
412   addr->sin_family = AF_INET;
413
414   /*
415    * Parse the address
416    */
417   if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
418   {
419     fprintf (stderr,
420              "Failed to parse IPv4 address `%s': %s\n",
421              address,
422              strerror (errno));
423     exit (1);
424   }
425
426   if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
427   {
428     fprintf (stderr,
429              "Error creating IPv4 socket: %s\n",
430              strerror (errno));
431     exit (1);
432   }
433
434   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
435
436   /*
437    * Set the address
438    */
439   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
440   {
441     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
442     (void) close (fd);
443     exit (1);
444   }
445
446   /*
447    * Parse the netmask
448    */
449   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
450   if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
451   {
452     fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
453              strerror (errno));
454     (void) close (fd);
455     exit (1);
456   }
457
458   /*
459    * Set the netmask
460    */
461   if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
462   {
463     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
464              strerror (errno));
465     (void) close (fd);
466     exit (1);
467   }
468
469   /*
470    * Get the flags
471    */
472   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
473   {
474     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
475              strerror (errno));
476     (void) close (fd);
477     exit (1);
478   }
479
480   /*
481    * Add the UP and RUNNING flags
482    */
483   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
484   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
485   {
486     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
487              strerror (errno));
488     (void) close (fd);
489     exit (1);
490   }
491
492   if (0 != close (fd))
493   {
494     fprintf (stderr, "close failed: %s\n", strerror (errno));
495     (void) close (fd);
496     exit (1);
497   }
498 }
499
500
501 /**
502  * Start forwarding to and from the tunnel.  This function runs with
503  * "reduced" privileges (saved UID is still 0, but effective UID is
504  * the real user ID).
505  *
506  * @param fd_tun tunnel FD
507  */
508 static void
509 run (int fd_tun)
510 {
511   /*
512    * The buffer filled by reading from fd_tun
513    */
514   unsigned char buftun[MAX_SIZE];
515   ssize_t buftun_size = 0;
516   unsigned char *buftun_read = NULL;
517
518   /*
519    * The buffer filled by reading from stdin
520    */
521   unsigned char bufin[MAX_SIZE];
522   ssize_t bufin_size = 0;
523   size_t bufin_rpos = 0;
524   unsigned char *bufin_read = NULL;
525   fd_set fds_w;
526   fd_set fds_r;
527   int max;
528
529   while (1)
530   {
531     FD_ZERO (&fds_w);
532     FD_ZERO (&fds_r);
533
534     /*
535      * We are supposed to read and the buffer is empty
536      * -> select on read from tun
537      */
538     if (0 == buftun_size)
539       FD_SET (fd_tun, &fds_r);
540
541     /*
542      * We are supposed to read and the buffer is not empty
543      * -> select on write to stdout
544      */
545     if (0 < buftun_size)
546       FD_SET (1, &fds_w);
547
548     /*
549      * We are supposed to write and the buffer is empty
550      * -> select on read from stdin
551      */
552     if (NULL == bufin_read)
553       FD_SET (0, &fds_r);
554
555     /*
556      * We are supposed to write and the buffer is not empty
557      * -> select on write to tun
558      */
559     if (NULL != bufin_read)
560       FD_SET (fd_tun, &fds_w);
561
562     FD_SET (cpipe[0], &fds_r);
563     max = (fd_tun > cpipe[0]) ? fd_tun : cpipe[0];
564
565     int r = select (max + 1, &fds_r, &fds_w, NULL, NULL);
566
567     if (-1 == r)
568     {
569       if (EINTR == errno)
570         continue;
571       fprintf (stderr, "select failed: %s\n", strerror (errno));
572       return;
573     }
574
575     if (r > 0)
576     {
577       if (FD_ISSET (cpipe[0], &fds_r))
578         return;     /* aborted by signal */
579
580       if (FD_ISSET (fd_tun, &fds_r))
581       {
582         buftun_size =
583           read (fd_tun, buftun + sizeof(struct GNUNET_MessageHeader),
584                 MAX_SIZE - sizeof(struct GNUNET_MessageHeader));
585         if (-1 == buftun_size)
586         {
587           if ((errno == EINTR) ||
588               (errno == EAGAIN))
589           {
590             buftun_size = 0;
591             continue;
592           }
593           fprintf (stderr, "read-error: %s\n", strerror (errno));
594           return;
595         }
596         if (0 == buftun_size)
597         {
598           fprintf (stderr, "EOF on tun\n");
599           return;
600         }
601         buftun_read = buftun;
602         {
603           struct GNUNET_MessageHeader *hdr =
604             (struct GNUNET_MessageHeader *) buftun;
605           buftun_size += sizeof(struct GNUNET_MessageHeader);
606           hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
607           hdr->size = htons (buftun_size);
608         }
609       }
610       else if (FD_ISSET (1, &fds_w))
611       {
612         ssize_t written = write (1, buftun_read, buftun_size);
613
614         if (-1 == written)
615         {
616           if ((errno == EINTR) ||
617               (errno == EAGAIN))
618             continue;
619           fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
620           return;
621         }
622         if (0 == written)
623         {
624           fprintf (stderr, "write returned 0\n");
625           return;
626         }
627         buftun_size -= written;
628         buftun_read += written;
629       }
630
631       if (FD_ISSET (0, &fds_r))
632       {
633         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
634         if (-1 == bufin_size)
635         {
636           bufin_read = NULL;
637           if ((errno == EINTR) ||
638               (errno == EAGAIN))
639             continue;
640           fprintf (stderr, "read-error: %s\n", strerror (errno));
641           return;
642         }
643         if (0 == bufin_size)
644         {
645           bufin_read = NULL;
646           fprintf (stderr, "EOF on stdin\n");
647           return;
648         }
649         {
650           struct GNUNET_MessageHeader *hdr;
651
652 PROCESS_BUFFER:
653           bufin_rpos += bufin_size;
654           if (bufin_rpos < sizeof(struct GNUNET_MessageHeader))
655             continue;
656           hdr = (struct GNUNET_MessageHeader *) bufin;
657           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
658           {
659             fprintf (stderr, "protocol violation!\n");
660             return;
661           }
662           if (ntohs (hdr->size) > bufin_rpos)
663             continue;
664           bufin_read = bufin + sizeof(struct GNUNET_MessageHeader);
665           bufin_size = ntohs (hdr->size) - sizeof(struct GNUNET_MessageHeader);
666           bufin_rpos -= bufin_size + sizeof(struct GNUNET_MessageHeader);
667         }
668       }
669       else if (FD_ISSET (fd_tun, &fds_w))
670       {
671         ssize_t written = write (fd_tun, bufin_read, bufin_size);
672
673         if (-1 == written)
674         {
675           if ((errno == EINTR) ||
676               (errno == EAGAIN))
677             continue;
678           fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
679           return;
680         }
681         if (0 == written)
682         {
683           fprintf (stderr, "write returned 0\n");
684           return;
685         }
686         {
687           bufin_size -= written;
688           bufin_read += written;
689           if (0 == bufin_size)
690           {
691             memmove (bufin, bufin_read, bufin_rpos);
692             bufin_read = NULL;         /* start reading again */
693             bufin_size = 0;
694             goto PROCESS_BUFFER;
695           }
696         }
697       }
698     }
699   }
700 }
701
702
703 /**
704  * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface,
705  * redirects all outgoing DNS traffic (except from the specified port) to that
706  * interface and then passes traffic from and to the interface via stdin/stdout.
707  *
708  * Once stdin/stdout close or have other errors, the tunnel is closed and the
709  * DNS traffic redirection is stopped.
710  *
711  * @param argc number of arguments
712  * @param argv 0: binary name (should be "gnunet-helper-vpn")
713  *             1: tunnel interface name (typically "gnunet-dns")
714  *             2: IPv6 address for the tunnel ("FE80::1")
715  *             3: IPv6 netmask length in bits ("64")
716  *             4: IPv4 address for the tunnel ("1.2.3.4")
717  *             5: IPv4 netmask ("255.255.0.0")
718  *             6: skip sysctl, routing and iptables setup ("0")
719  * @return 0 on success, otherwise code indicating type of error:
720  *         1 wrong number of arguments
721  *         2 invalid arguments (i.e. port number / prefix length wrong)
722  *         3 iptables not executable
723  *         4 ip not executable
724  *         5 failed to initialize tunnel interface
725  *         6 failed to initialize control pipe
726  *         8 failed to change routing table, cleanup successful
727  *         9-23 failed to change routing table and failed to undo some changes to routing table
728  *         24 failed to drop privs
729  *         25-39 failed to drop privs and then failed to undo some changes to routing table
730  *         40 failed to regain privs
731  *         41-55 failed to regain prisv and then failed to undo some changes to routing table
732  *         254 insufficient privileges
733  *         255 failed to handle kill signal properly
734  */
735 int
736 main (int argc, char *const*argv)
737 {
738   int r;
739   char dev[IFNAMSIZ];
740   char mygid[32];
741   int fd_tun;
742   uid_t uid;
743   int nortsetup = 0;
744
745   if (7 != argc)
746   {
747     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
748     return 1;
749   }
750
751   /* assert privs so we can modify the firewall rules! */
752   uid = getuid ();
753 #ifdef HAVE_SETRESUID
754   if (0 != setresuid (uid, 0, 0))
755   {
756     fprintf (stderr, "Failed to setresuid to root: %s\n", strerror (errno));
757     return 254;
758   }
759 #else
760   if (0 != seteuid (0))
761   {
762     fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
763     return 254;
764   }
765 #endif
766   if (0 == strncmp (argv[6], "1", 2))
767     nortsetup = 1;
768
769   if (0 == nortsetup)
770   {
771     /* verify that the binaries we care about are executable */
772 #ifdef IPTABLES
773     if (0 == access (IPTABLES, X_OK))
774       sbin_iptables = IPTABLES;
775     else
776 #endif
777     if (0 == access ("/sbin/iptables", X_OK))
778       sbin_iptables = "/sbin/iptables";
779     else if (0 == access ("/usr/sbin/iptables", X_OK))
780       sbin_iptables = "/usr/sbin/iptables";
781     else
782     {
783       fprintf (stderr,
784                "Fatal: executable iptables not found in approved directories: %s\n",
785                strerror (errno));
786       return 3;
787     }
788 #ifdef IP6TABLES
789     if (0 == access (IP6TABLES, X_OK))
790       sbin_ip6tables = IP6TABLES;
791     else
792 #endif
793     if (0 == access ("/sbin/ip6tables", X_OK))
794       sbin_ip6tables = "/sbin/ip6tables";
795     else if (0 == access ("/usr/sbin/ip6tables", X_OK))
796       sbin_ip6tables = "/usr/sbin/ip6tables";
797     else
798     {
799       fprintf (stderr,
800                "Fatal: executable ip6tables not found in approved directories: %s\n",
801                strerror (errno));
802       return 3;
803     }
804 #ifdef PATH_TO_IP
805     if (0 == access (PATH_TO_IP, X_OK))
806       sbin_ip = PATH_TO_IP;
807     else
808 #endif
809     if (0 == access ("/sbin/ip", X_OK))
810       sbin_ip = "/sbin/ip";
811     else if (0 == access ("/usr/sbin/ip", X_OK))
812       sbin_ip = "/usr/sbin/ip";
813     else if (0 == access ("/bin/ip", X_OK))  /* gentoo has it there */
814       sbin_ip = "/bin/ip";
815     else
816     {
817       fprintf (stderr,
818                "Fatal: executable ip not found in approved directories: %s\n",
819                strerror (errno));
820       return 4;
821     }
822 #ifdef SYSCTL
823     if (0 == access (SYSCTL, X_OK))
824       sbin_sysctl = SYSCTL;
825     else
826 #endif
827     if (0 == access ("/sbin/sysctl", X_OK))
828       sbin_sysctl = "/sbin/sysctl";
829     else if (0 == access ("/usr/sbin/sysctl", X_OK))
830       sbin_sysctl = "/usr/sbin/sysctl";
831     else
832     {
833       fprintf (stderr,
834                "Fatal: executable sysctl not found in approved directories: %s\n",
835                strerror (errno));
836       return 5;
837     }
838   }
839
840   /* setup 'mygid' string */
841   snprintf (mygid, sizeof(mygid), "%d", (int) getegid ());
842
843   /* do not die on SIGPIPE */
844   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
845   {
846     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
847              strerror (errno));
848     return 7;
849   }
850
851   /* setup pipe to shutdown nicely on SIGINT */
852   if (0 != pipe (cpipe))
853   {
854     fprintf (stderr,
855              "Fatal: could not setup control pipe: %s\n",
856              strerror (errno));
857     return 6;
858   }
859   if (cpipe[0] >= FD_SETSIZE)
860   {
861     fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
862     (void) close (cpipe[0]);
863     (void) close (cpipe[1]);
864     return 6;
865   }
866   {
867     /* make pipe non-blocking, as we theoretically could otherwise block
868        in the signal handler */
869     int flags = fcntl (cpipe[1], F_GETFL);
870     if (-1 == flags)
871     {
872       fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
873       (void) close (cpipe[0]);
874       (void) close (cpipe[1]);
875       return 6;
876     }
877     flags |= O_NONBLOCK;
878     if (0 != fcntl (cpipe[1], F_SETFL, flags))
879     {
880       fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (
881                  errno));
882       (void) close (cpipe[0]);
883       (void) close (cpipe[1]);
884       return 6;
885     }
886   }
887   if ((SIG_ERR == signal (SIGTERM, &signal_handler)) ||
888 #if (SIGTERM != GNUNET_TERM_SIG)
889       (SIG_ERR == signal (GNUNET_TERM_SIG, &signal_handler)) ||
890 #endif
891       (SIG_ERR == signal (SIGINT, &signal_handler)) ||
892       (SIG_ERR == signal (SIGHUP, &signal_handler)))
893   {
894     fprintf (stderr,
895              "Fatal: could not initialize signal handler: %s\n",
896              strerror (errno));
897     (void) close (cpipe[0]);
898     (void) close (cpipe[1]);
899     return 7;
900   }
901
902
903   /* get interface name */
904   strncpy (dev, argv[1], IFNAMSIZ);
905   dev[IFNAMSIZ - 1] = '\0';
906
907   /* Disable rp filtering */
908   if (0 == nortsetup)
909   {
910     char *const sysctl_args[] = { "sysctl", "-w",
911                                   "net.ipv4.conf.all.rp_filter=0", NULL };
912     char *const sysctl_args2[] = { "sysctl", "-w",
913                                    "net.ipv4.conf.default.rp_filter=0", NULL };
914     if ((0 != fork_and_exec (sbin_sysctl, sysctl_args)) ||
915         (0 != fork_and_exec (sbin_sysctl, sysctl_args2)))
916     {
917       fprintf (stderr,
918                "Failed to disable rp filtering.\n");
919       return 5;
920     }
921   }
922
923
924   /* now open virtual interface (first part that requires root) */
925   if (-1 == (fd_tun = init_tun (dev)))
926   {
927     fprintf (stderr, "Fatal: could not initialize tun-interface\n");
928     (void) signal (SIGTERM, SIG_IGN);
929 #if (SIGTERM != GNUNET_TERM_SIG)
930     (void) signal (GNUNET_TERM_SIG, SIG_IGN);
931 #endif
932     (void) signal (SIGINT, SIG_IGN);
933     (void) signal (SIGHUP, SIG_IGN);
934     (void) close (cpipe[0]);
935     (void) close (cpipe[1]);
936     return 5;
937   }
938
939   /* now set interface addresses */
940   {
941     const char *address = argv[2];
942     long prefix_len = atol (argv[3]);
943
944     if ((prefix_len < 1) || (prefix_len > 127))
945     {
946       fprintf (stderr, "Fatal: prefix_len out of range\n");
947       (void) signal (SIGTERM, SIG_IGN);
948 #if (SIGTERM != GNUNET_TERM_SIG)
949       (void) signal (GNUNET_TERM_SIG, SIG_IGN);
950 #endif
951       (void) signal (SIGINT, SIG_IGN);
952       (void) signal (SIGHUP, SIG_IGN);
953       (void) close (cpipe[0]);
954       (void) close (cpipe[1]);
955       return 2;
956     }
957     set_address6 (dev, address, prefix_len);
958   }
959
960   {
961     const char *address = argv[4];
962     const char *mask = argv[5];
963
964     set_address4 (dev, address, mask);
965   }
966
967
968   /* update routing tables -- next part why we need SUID! */
969   /* Forward everything from our EGID (which should only be held
970      by the 'gnunet-service-dns') and with destination
971      to port 53 on UDP, without hijacking */
972   if (0 == nortsetup)
973   {
974     r = 8;   /* failed to fully setup routing table */
975     {
976       char *const mangle_args[] = {
977         "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
978         "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
979         "ACCEPT", NULL
980       };
981       if (0 != fork_and_exec (sbin_iptables, mangle_args))
982         goto cleanup_rest;
983     }
984     {
985       char *const mangle_args[] = {
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         "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
998         "udp", "--dport", DNS_PORT,
999         "-j", "MARK", "--set-mark", DNS_MARK,
1000         NULL
1001       };
1002       if (0 != fork_and_exec (sbin_iptables, mark_args))
1003         goto cleanup_mangle_1;
1004     }
1005     {
1006       char *const mark_args[] = {
1007         "ip6tables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
1008         "udp", "--dport", DNS_PORT,
1009         "!", "-s", "fe80::/10",   /* this line excludes link-local traffic */
1010         "-j", "MARK", "--set-mark", DNS_MARK,
1011         NULL
1012       };
1013       if (0 != fork_and_exec (sbin_ip6tables, mark_args))
1014         goto cleanup_mark_2b;
1015     }
1016     /* Forward all marked DNS traffic to our DNS_TABLE */
1017     {
1018       char *const forward_args[] = {
1019         "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1020       };
1021       if (0 != fork_and_exec (sbin_ip, forward_args))
1022         goto cleanup_mark_2;
1023     }
1024     {
1025       char *const forward_args[] = {
1026         "ip", "-6", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1027       };
1028       if (0 != fork_and_exec (sbin_ip, forward_args))
1029         goto cleanup_forward_3b;
1030     }
1031     /* Finally, add rule in our forwarding table to pass to our virtual interface */
1032     {
1033       char *const route_args[] = {
1034         "ip", "route", "add", "default", "dev", dev,
1035         "table", DNS_TABLE, NULL
1036       };
1037       if (0 != fork_and_exec (sbin_ip, route_args))
1038         goto cleanup_forward_3;
1039     }
1040     {
1041       char *const route_args[] = {
1042         "ip", "-6", "route", "add", "default", "dev", dev,
1043         "table", DNS_TABLE, NULL
1044       };
1045       if (0 != fork_and_exec (sbin_ip, route_args))
1046         goto cleanup_route_4b;
1047     }
1048   }
1049
1050   /* drop privs *except* for the saved UID; this is not perfect, but better
1051      than doing nothing */
1052 #ifdef HAVE_SETRESUID
1053   if (0 != setresuid (uid, uid, 0))
1054   {
1055     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
1056     r = 24;
1057     goto cleanup_route_4;
1058   }
1059 #else
1060   /* Note: no 'setuid' here as we must keep our saved UID as root */
1061   if (0 != seteuid (uid))
1062   {
1063     fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
1064     r = 24;
1065     goto cleanup_route_4;
1066   }
1067 #endif
1068
1069   r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
1070
1071   /* now forward until we hit a problem */
1072   run (fd_tun);
1073
1074   /* now need to regain privs so we can remove the firewall rules we added! */
1075 #ifdef HAVE_SETRESUID
1076   if (0 != setresuid (uid, 0, 0))
1077   {
1078     fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (
1079                errno));
1080     r = 40;
1081     goto cleanup_route_4;
1082   }
1083 #else
1084   if (0 != seteuid (0))
1085   {
1086     fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
1087     r = 40;
1088     goto cleanup_route_4;
1089   }
1090 #endif
1091
1092   /* update routing tables again -- this is why we could not fully drop privs */
1093   /* now undo updating of routing tables; normal exit or clean-up-on-error case */
1094 cleanup_route_4:
1095   if (0 == nortsetup)
1096   {
1097     char *const route_clean_args[] = {
1098       "ip", "-6", "route", "del", "default", "dev", dev,
1099       "table", DNS_TABLE, NULL
1100     };
1101     if (0 != fork_and_exec (sbin_ip, route_clean_args))
1102       r += 1;
1103   }
1104 cleanup_route_4b:
1105   if (0 == nortsetup)
1106   {
1107     char *const route_clean_args[] = {
1108       "ip", "route", "del", "default", "dev", dev,
1109       "table", DNS_TABLE, NULL
1110     };
1111     if (0 != fork_and_exec (sbin_ip, route_clean_args))
1112       r += 1;
1113   }
1114 cleanup_forward_3:
1115   if (0 == nortsetup)
1116   {
1117     char *const forward_clean_args[] = {
1118       "ip", "-6", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1119     };
1120     if (0 != fork_and_exec (sbin_ip, forward_clean_args))
1121       r += 2;
1122   }
1123 cleanup_forward_3b:
1124   if (0 == nortsetup)
1125   {
1126     char *const forward_clean_args[] = {
1127       "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1128     };
1129     if (0 != fork_and_exec (sbin_ip, forward_clean_args))
1130       r += 2;
1131   }
1132 cleanup_mark_2:
1133   if (0 == nortsetup)
1134   {
1135     char *const mark_clean_args[] = {
1136       "ip6tables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1137       "--dport", DNS_PORT,
1138       "!", "-s", "fe80::/10",   /* this line excludes link-local traffic */
1139       "-j", "MARK", "--set-mark", DNS_MARK, NULL
1140     };
1141     if (0 != fork_and_exec (sbin_ip6tables, mark_clean_args))
1142       r += 4;
1143   }
1144 cleanup_mark_2b:
1145   if (0 == nortsetup)
1146   {
1147     char *const mark_clean_args[] = {
1148       "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1149       "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
1150     };
1151     if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
1152       r += 4;
1153   }
1154 cleanup_mangle_1:
1155   if (0 == nortsetup)
1156   {
1157     char *const mangle_clean_args[] = {
1158       "ip6tables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1159       "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
1160       NULL
1161     };
1162     if (0 != fork_and_exec (sbin_ip6tables, mangle_clean_args))
1163       r += 8;
1164   }
1165 cleanup_mangle_1b:
1166   if (0 == nortsetup)
1167   {
1168     char *const mangle_clean_args[] = {
1169       "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1170       "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
1171       NULL
1172     };
1173     if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
1174       r += 8;
1175   }
1176
1177 cleanup_rest:
1178   /* close virtual interface */
1179   (void) close (fd_tun);
1180   /* remove signal handler so we can close the pipes */
1181   (void) signal (SIGTERM, SIG_IGN);
1182 #if (SIGTERM != GNUNET_TERM_SIG)
1183   (void) signal (GNUNET_TERM_SIG, SIG_IGN);
1184 #endif
1185   (void) signal (SIGINT, SIG_IGN);
1186   (void) signal (SIGHUP, SIG_IGN);
1187   (void) close (cpipe[0]);
1188   (void) close (cpipe[1]);
1189   return r;
1190 }
1191
1192 /* end of gnunet-helper-dns.c */