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