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