aboutsummaryrefslogtreecommitdiff
path: root/src/dns/gnunet-helper-dns.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-01-02 12:23:13 +0000
committerChristian Grothoff <christian@grothoff.org>2012-01-02 12:23:13 +0000
commit7d12168098849a5296a41661263131c1fec2ca8b (patch)
tree9f547a40b932c284d1750249c29b520ffd24356d /src/dns/gnunet-helper-dns.c
parentf5bee1c9c607cf39a3c22f9c8a7f52db447c1ee9 (diff)
downloadgnunet-7d12168098849a5296a41661263131c1fec2ca8b.tar.gz
gnunet-7d12168098849a5296a41661263131c1fec2ca8b.zip
DNS helper for DNS redesign
Diffstat (limited to 'src/dns/gnunet-helper-dns.c')
-rw-r--r--src/dns/gnunet-helper-dns.c949
1 files changed, 949 insertions, 0 deletions
diff --git a/src/dns/gnunet-helper-dns.c b/src/dns/gnunet-helper-dns.c
new file mode 100644
index 000000000..3660871d8
--- /dev/null
+++ b/src/dns/gnunet-helper-dns.c
@@ -0,0 +1,949 @@
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).
39 *
40 * The code first sets up the virtual interface, then begins to
41 * redirect the DNS traffic to it, and then on errors or SIGTERM shuts
42 * down the virtual interface and removes the rules for the traffic
43 * redirection.
44 *
45 *
46 * Note that having this binary SUID is only partially safe: it will
47 * allow redirecting (and intercepting / mangling) of all DNS traffic
48 * originating from this system by any user who is able to run it.
49 * Furthermore, this code will make it trivial to DoS all DNS traffic
50 * originating from the current system, simply by sending it to
51 * nowhere (redirect stdout to /dev/null).
52 *
53 * Naturally, neither of these problems can be helped as this is the
54 * fundamental purpose of the binary. Certifying that this code is
55 * "safe" thus only means that it doesn't allow anything else (such
56 * as local priv. escalation, etc.).
57 *
58 * The following list of people have reviewed this code and considered
59 * it safe (within specifications) since the last modification (if you
60 * reviewed it, please have your name added to the list):
61 *
62 * - Christian Grothoff
63 */
64#include "platform.h"
65
66#include <linux/if_tun.h>
67
68/**
69 * Need 'struct GNUNET_MessageHeader'.
70 */
71#include "gnunet_common.h"
72
73/**
74 * Need DNS message types.
75 */
76#include "gnunet_protocols.h"
77
78/**
79 * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
80 */
81#define MAX_SIZE 65536
82
83#ifndef _LINUX_IN6_H
84/**
85 * This is in linux/include/net/ipv6.h, but not always exported...
86 */
87struct in6_ifreq
88{
89 struct in6_addr ifr6_addr;
90 uint32_t ifr6_prefixlen;
91 unsigned int ifr6_ifindex;
92};
93#endif
94
95/**
96 * Name and full path of IPTABLES binary.
97 */
98#define SBIN_IPTABLES "/sbin/iptables"
99
100/**
101 * Name and full path of IPTABLES binary.
102 */
103#define SBIN_IP "/sbin/ip"
104
105/**
106 * Port for DNS traffic.
107 */
108#define DNS_PORT "53"
109
110/**
111 * Marker we set for our hijacked DNS traffic. We use GNUnet's
112 * port (2086) plus the DNS port (53) in HEX to make a 32-bit mark
113 * (which is hopefully long enough to not collide); so
114 * 0x08260035 = 136708149 (hopefully unique enough...).
115 */
116#define DNS_MARK "136708149"
117
118/**
119 * Table we use for our DNS rules. 0-255 is the range and
120 * 0, 253, 254 and 255 are already reserved. As this is about
121 * DNS and as "53" is likely (fingers crossed!) high enough to
122 * not usually conflict with a normal user's setup, we use 53
123 * to give a hint that this has something to do with DNS.
124 */
125#define DNS_TABLE "53"
126
127
128/**
129 * Control pipe for shutdown via signal. [0] is the read end,
130 * [1] is the write end.
131 */
132static int cpipe[2];
133
134
135/**
136 * Signal handler called to initiate "nice" shutdown. Signals select
137 * loop via non-bocking pipe 'cpipe'.
138 *
139 * @param signal signal number of the signal (not used)
140 */
141static void
142signal_handler (int signal)
143{
144 /* ignore return value, as the signal handler could theoretically
145 be called many times before the shutdown can actually happen */
146 (void) write (cpipe[1], "K", 1);
147}
148
149
150/**
151 * Run the given command and wait for it to complete.
152 *
153 * @param file name of the binary to run
154 * @param cmd command line arguments (as given to 'execv')
155 * @return 0 on success, 1 on any error
156 */
157static int
158fork_and_exec (const char *file,
159 char *const cmd[])
160{
161 int status;
162 pid_t pid;
163 pid_t ret;
164
165 pid = fork ();
166 if (-1 == pid)
167 {
168 fprintf (stderr,
169 "fork failed: %s\n",
170 strerror (errno));
171 return 1;
172 }
173 if (0 == pid)
174 {
175 /* we are the child process */
176 (void) execv (file, cmd);
177 /* can only get here on error */
178 fprintf (stderr,
179 "exec `%s' failed: %s\n",
180 file,
181 strerror (errno));
182 _exit (1);
183 }
184 /* keep running waitpid as long as the only error we get is 'EINTR' */
185 while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
186 (errno == EINTR) );
187 if (-1 == ret)
188 {
189 fprintf (stderr,
190 "waitpid failed: %s\n",
191 strerror (errno));
192 return 1;
193 }
194 if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
195 return 1;
196 /* child process completed and returned success, we're happy */
197 return 0;
198}
199
200
201/**
202 * Creates a tun-interface called dev;
203 *
204 * @param dev is asumed to point to a char[IFNAMSIZ]
205 * if *dev == '\\0', uses the name supplied by the kernel;
206 * @return the fd to the tun or -1 on error
207 */
208static int
209init_tun (char *dev)
210{
211 struct ifreq ifr;
212 int fd;
213
214 if (NULL == dev)
215 {
216 errno = EINVAL;
217 return -1;
218 }
219
220 if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
221 {
222 fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
223 strerror (errno));
224 return -1;
225 }
226
227 if (fd >= FD_SETSIZE)
228 {
229 fprintf (stderr, "File descriptor to large: %d", fd);
230 return -1;
231 }
232
233 memset (&ifr, 0, sizeof (ifr));
234 ifr.ifr_flags = IFF_TUN;
235
236 if ('\0' != *dev)
237 strncpy (ifr.ifr_name, dev, IFNAMSIZ);
238
239 if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
240 {
241 fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
242 strerror (errno));
243 (void) close (fd);
244 return -1;
245 }
246 strcpy (dev, ifr.ifr_name);
247 return fd;
248}
249
250
251/**
252 * @brief Sets the IPv6-Address given in address on the interface dev
253 *
254 * @param dev the interface to configure
255 * @param address the IPv6-Address
256 * @param prefix_len the length of the network-prefix
257 */
258static void
259set_address6 (const char *dev, const char *address, unsigned long prefix_len)
260{
261 struct ifreq ifr;
262 struct in6_ifreq ifr6;
263 struct sockaddr_in6 sa6;
264 int fd;
265
266 /*
267 * parse the new address
268 */
269 memset (&sa6, 0, sizeof (struct sockaddr_in6));
270 sa6.sin6_family = AF_INET6;
271 if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
272 {
273 fprintf (stderr, "Failed to parse address `%s': %s\n", address,
274 strerror (errno));
275 exit (1);
276 }
277
278 if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
279 {
280 fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
281 exit (1);
282 }
283
284 memset (&ifr, 0, sizeof (struct ifreq));
285 /*
286 * Get the index of the if
287 */
288 strncpy (ifr.ifr_name, dev, IFNAMSIZ);
289 if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
290 {
291 fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
292 (void) close (fd);
293 exit (1);
294 }
295
296 memset (&ifr6, 0, sizeof (struct in6_ifreq));
297 ifr6.ifr6_addr = sa6.sin6_addr;
298 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
299 ifr6.ifr6_prefixlen = prefix_len;
300
301 /*
302 * Set the address
303 */
304 if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
305 {
306 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
307 strerror (errno));
308 (void) close (fd);
309 exit (1);
310 }
311
312 /*
313 * Get the flags
314 */
315 if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
316 {
317 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
318 strerror (errno));
319 (void) close (fd);
320 exit (1);
321 }
322
323 /*
324 * Add the UP and RUNNING flags
325 */
326 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
327 if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
328 {
329 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
330 strerror (errno));
331 (void) close (fd);
332 exit (1);
333 }
334
335 if (0 != close (fd))
336 {
337 fprintf (stderr, "close failed: %s\n", strerror (errno));
338 exit (1);
339 }
340}
341
342
343/**
344 * @brief Sets the IPv4-Address given in address on the interface dev
345 *
346 * @param dev the interface to configure
347 * @param address the IPv4-Address
348 * @param mask the netmask
349 */
350static void
351set_address4 (const char *dev, const char *address, const char *mask)
352{
353 int fd;
354 struct sockaddr_in *addr;
355 struct ifreq ifr;
356
357 memset (&ifr, 0, sizeof (struct ifreq));
358 addr = (struct sockaddr_in *) &(ifr.ifr_addr);
359 addr->sin_family = AF_INET;
360
361 /*
362 * Parse the address
363 */
364 if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
365 {
366 fprintf (stderr, "Failed to parse address `%s': %s\n", address,
367 strerror (errno));
368 exit (1);
369 }
370
371 if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
372 {
373 fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
374 exit (1);
375 }
376
377 strncpy (ifr.ifr_name, dev, IFNAMSIZ);
378
379 /*
380 * Set the address
381 */
382 if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
383 {
384 fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
385 (void) close (fd);
386 exit (1);
387 }
388
389 /*
390 * Parse the netmask
391 */
392 addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
393 if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
394 {
395 fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
396 strerror (errno));
397 (void) close (fd);
398 exit (1);
399 }
400
401 /*
402 * Set the netmask
403 */
404 if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
405 {
406 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
407 strerror (errno));
408 (void) close (fd);
409 exit (1);
410 }
411
412 /*
413 * Get the flags
414 */
415 if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
416 {
417 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
418 strerror (errno));
419 (void) close (fd);
420 exit (1);
421 }
422
423 /*
424 * Add the UP and RUNNING flags
425 */
426 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
427 if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
428 {
429 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
430 strerror (errno));
431 (void) close (fd);
432 exit (1);
433 }
434
435 if (0 != close (fd))
436 {
437 fprintf (stderr, "close failed: %s\n", strerror (errno));
438 (void) close (fd);
439 exit (1);
440 }
441}
442
443
444/**
445 * Start forwarding to and from the tunnel. This function runs with
446 * "reduced" priviledges (saved UID is still 0, but effective UID is
447 * the real user ID).
448 *
449 * @param fd_tun tunnel FD
450 */
451static void
452run (int fd_tun)
453{
454 /*
455 * The buffer filled by reading from fd_tun
456 */
457 unsigned char buftun[MAX_SIZE];
458 ssize_t buftun_size = 0;
459 unsigned char *buftun_read = NULL;
460
461 /*
462 * The buffer filled by reading from stdin
463 */
464 unsigned char bufin[MAX_SIZE];
465 ssize_t bufin_size = 0;
466 size_t bufin_rpos = 0;
467 unsigned char *bufin_read = NULL;
468 fd_set fds_w;
469 fd_set fds_r;
470 int max;
471
472 while (1)
473 {
474 FD_ZERO (&fds_w);
475 FD_ZERO (&fds_r);
476
477 /*
478 * We are supposed to read and the buffer is empty
479 * -> select on read from tun
480 */
481 if (0 == buftun_size)
482 FD_SET (fd_tun, &fds_r);
483
484 /*
485 * We are supposed to read and the buffer is not empty
486 * -> select on write to stdout
487 */
488 if (0 != buftun_size)
489 FD_SET (1, &fds_w);
490
491 /*
492 * We are supposed to write and the buffer is empty
493 * -> select on read from stdin
494 */
495 if (NULL == bufin_read)
496 FD_SET (0, &fds_r);
497
498 /*
499 * We are supposed to write and the buffer is not empty
500 * -> select on write to tun
501 */
502 if (NULL != bufin_read)
503 FD_SET (fd_tun, &fds_w);
504
505 FD_SET (cpipe[0], &fds_r);
506 max = (fd_tun > cpipe[0]) ? fd_tun : cpipe[0];
507
508 int r = select (max + 1, &fds_r, &fds_w, NULL, NULL);
509
510 if (-1 == r)
511 {
512 if (EINTR == errno)
513 continue;
514 fprintf (stderr, "select failed: %s\n", strerror (errno));
515 return;
516 }
517
518 if (r > 0)
519 {
520 if (FD_ISSET (cpipe[0], &fds_r))
521 return; /* aborted by signal */
522
523 if (FD_ISSET (fd_tun, &fds_r))
524 {
525 buftun_size =
526 read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
527 MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
528 if (-1 == buftun_size)
529 {
530 if ( (errno == EINTR) ||
531 (errno == EAGAIN) )
532 continue;
533 fprintf (stderr, "read-error: %s\n", strerror (errno));
534 return;
535 }
536 if (0 == buftun_size)
537 {
538 fprintf (stderr, "EOF on tun\n");
539 return;
540 }
541 buftun_read = buftun;
542 {
543 struct GNUNET_MessageHeader *hdr =
544 (struct GNUNET_MessageHeader *) buftun;
545 buftun_size += sizeof (struct GNUNET_MessageHeader);
546 hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
547 hdr->size = htons (buftun_size);
548 }
549 }
550 else if (FD_ISSET (1, &fds_w))
551 {
552 ssize_t written = write (1, buftun_read, buftun_size);
553
554 if (-1 == written)
555 {
556 if ( (errno == EINTR) ||
557 (errno == EAGAIN) )
558 continue;
559 fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
560 return;
561 }
562 if (0 == written)
563 {
564 fprintf (stderr, "write returned 0\n");
565 return;
566 }
567 buftun_size -= written;
568 buftun_read += written;
569 }
570
571 if (FD_ISSET (0, &fds_r))
572 {
573 bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
574 if (-1 == bufin_size)
575 {
576 if ( (errno == EINTR) ||
577 (errno == EAGAIN) )
578 continue;
579 fprintf (stderr, "read-error: %s\n", strerror (errno));
580 return;
581 }
582 if (0 == bufin_size)
583 {
584 fprintf (stderr, "EOF on stdin\n");
585 return;
586 }
587 {
588 struct GNUNET_MessageHeader *hdr;
589
590PROCESS_BUFFER:
591 bufin_rpos += bufin_size;
592 if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
593 continue;
594 hdr = (struct GNUNET_MessageHeader *) bufin;
595 if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
596 {
597 fprintf (stderr, "protocol violation!\n");
598 return;
599 }
600 if (ntohs (hdr->size) > bufin_rpos)
601 continue;
602 bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
603 bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
604 bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
605 }
606 }
607 else if (FD_ISSET (fd_tun, &fds_w))
608 {
609 ssize_t written = write (fd_tun, bufin_read, bufin_size);
610
611 if (-1 == written)
612 {
613 if ( (errno == EINTR) ||
614 (errno == EAGAIN) )
615 continue;
616 fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
617 return;
618 }
619 if (0 == written)
620 {
621 fprintf (stderr, "write returned 0\n");
622 return;
623 }
624 {
625 bufin_size -= written;
626 bufin_read += written;
627 if (0 == bufin_size)
628 {
629 memmove (bufin, bufin_read, bufin_rpos);
630 bufin_read = NULL; /* start reading again */
631 bufin_size = 0;
632 goto PROCESS_BUFFER;
633 }
634 }
635 }
636 }
637 }
638}
639
640
641/**
642 * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface,
643 * redirects all outgoing DNS traffic (except from the specified port) to that
644 * interface and then passes traffic from and to the interface via stdin/stdout.
645 *
646 * Once stdin/stdout close or have other errors, the tunnel is closed and the
647 * DNS traffic redirection is stopped.
648 *
649 * @param argc number of arguments
650 * @param argv 0: binary name (should be "gnunet-helper-vpn")
651 * 1: tunnel interface name (typically "gnunet-dns")
652 * 2: IPv6 address for the tunnel ("FE80::1")
653 * 3: IPv6 netmask length in bits ("64")
654 * 4: IPv4 address for the tunnel ("1.2.3.4")
655 * 5: IPv4 netmask ("255.255.0.0")
656 * 6: PORT to not hijack ("55533")
657 * @return 0 on success, otherwise code indicating type of error:
658 * 1 wrong number of arguments
659 * 2 invalid arguments (i.e. port number / prefix length wrong)
660 * 3 iptables not executable
661 * 4 ip not executable
662 * 5 failed to initialize tunnel interface
663 * 6 failed to initialize control pipe
664 * 8 failed to change routing table, cleanup successful
665 * 9-23 failed to undo some changes to routing table
666 * 24 failed to drop privs
667 * 25-39 failed to drop privs and then failed to undo some changes to routing table
668 * 40 failed to regain privs
669 * 41-55 failed to regain prisv and then failed to undo some changes to routing table
670 * 255 failed to handle kill signal properly
671 */
672int
673main (int argc, char *const*argv)
674{
675 unsigned int port;
676 char localport[6];
677 int r;
678 char dev[IFNAMSIZ];
679 int fd_tun;
680
681 if (7 != argc)
682 {
683 fprintf (stderr, "Fatal: must supply 6 arguments!\n");
684 return 1;
685 }
686
687 /* verify that the binaries were care about are executable */
688 if (0 != access (SBIN_IPTABLES, X_OK))
689 {
690 fprintf (stderr,
691 "`%s' is not executable: %s\n",
692 SBIN_IPTABLES,
693 strerror (errno));
694 return 3;
695 }
696 if (0 != access (SBIN_IP, X_OK))
697 {
698 fprintf (stderr,
699 "`%s' is not executable: %s\n",
700 SBIN_IP,
701 strerror (errno));
702 return 4;
703 }
704
705 /* validate port number */
706 port = atoi (argv[6]);
707 if ( (port == 0) || (port >= 65536) )
708 {
709 fprintf (stderr,
710 "Port `%u' is invalid\n",
711 port);
712 return 2;
713 }
714 /* print port number to string for command-line use*/
715 (void) snprintf (localport,
716 sizeof (localport),
717 "%u",
718 port);
719
720 /* do not die on SIGPIPE */
721 if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
722 {
723 fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
724 strerror (errno));
725 return 7;
726 }
727
728 /* setup pipe to shutdown nicely on SIGINT */
729 if (0 != pipe (cpipe))
730 {
731 fprintf (stderr,
732 "Fatal: could not setup control pipe: %s\n",
733 strerror (errno));
734 return 6;
735 }
736 if (cpipe[0] >= FD_SETSIZE)
737 {
738 fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
739 (void) close (cpipe[0]);
740 (void) close (cpipe[1]);
741 return 6;
742 }
743 {
744 /* make pipe non-blocking, as we theoretically could otherwise block
745 in the signal handler */
746 int flags = fcntl (cpipe[1], F_GETFL);
747 if (-1 == flags)
748 {
749 fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
750 (void) close (cpipe[0]);
751 (void) close (cpipe[1]);
752 return 6;
753 }
754 flags |= O_NONBLOCK;
755 if (0 != fcntl (cpipe[1], F_SETFL, flags))
756 {
757 fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (errno));
758 (void) close (cpipe[0]);
759 (void) close (cpipe[1]);
760 return 6;
761 }
762 }
763 if (SIG_ERR == signal (SIGINT, &signal_handler))
764 {
765 fprintf (stderr,
766 "Fatal: could not initialize signal handler: %s\n",
767 strerror (errno));
768 (void) close (cpipe[0]);
769 (void) close (cpipe[1]);
770 return 7;
771 }
772
773
774 /* get interface name */
775 strncpy (dev, argv[1], IFNAMSIZ);
776 dev[IFNAMSIZ - 1] = '\0';
777
778 /* now open virtual interface (first part that requires root) */
779 if (-1 == (fd_tun = init_tun (dev)))
780 {
781 fprintf (stderr, "Fatal: could not initialize tun-interface\n");
782 (void) signal (SIGINT, SIG_IGN);
783 (void) close (cpipe[0]);
784 (void) close (cpipe[1]);
785 return 5;
786 }
787
788 /* now set interface addresses */
789 {
790 const char *address = argv[2];
791 long prefix_len = atol (argv[3]);
792
793 if ((prefix_len < 1) || (prefix_len > 127))
794 {
795 fprintf (stderr, "Fatal: prefix_len out of range\n");
796 (void) signal (SIGINT, SIG_IGN);
797 (void) close (cpipe[0]);
798 (void) close (cpipe[1]);
799 return 2;
800 }
801 set_address6 (dev, address, prefix_len);
802 }
803
804 {
805 const char *address = argv[4];
806 const char *mask = argv[5];
807
808 set_address4 (dev, address, mask);
809 }
810
811 /* update routing tables -- next part why we need SUID! */
812 /* Forward everything from the given local port (with destination
813 to port 53, and only for UDP) without hijacking */
814 r = 8; /* failed to fully setup routing table */
815 {
816 char *const mangle_args[] =
817 {
818 "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
819 "udp", "--sport", localport, "--dport", DNS_PORT, "-j",
820 "ACCEPT", NULL
821 };
822 if (0 != fork_and_exec (SBIN_IPTABLES, mangle_args))
823 goto cleanup_mangle_1;
824 }
825 /* Mark all of the other DNS traffic using our mark DNS_MARK */
826 {
827 char *const mark_args[] =
828 {
829 "iptables", "-t", "mangle", "-I", "OUTPUT", DNS_TABLE, "-p",
830 "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
831 NULL
832 };
833 if (0 != fork_and_exec (SBIN_IPTABLES, mark_args))
834 goto cleanup_mark_2;
835 }
836 /* Forward all marked DNS traffic to our DNS_TABLE */
837 {
838 char *const forward_args[] =
839 {
840 "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
841 };
842 if (0 != fork_and_exec (SBIN_IP, forward_args))
843 goto cleanup_forward_3;
844 }
845 /* Finally, add rule in our forwarding table to pass to our virtual interface */
846 {
847 char *const route_args[] =
848 {
849 "ip", "route", "add", "default", "via", dev,
850 "table", DNS_TABLE, NULL
851 };
852 if (0 != fork_and_exec (SBIN_IP, route_args))
853 goto cleanup_route_4;
854 }
855
856 /* drop privs *except* for the saved UID; this is not perfect, but better
857 than doing nothing */
858 uid_t uid = getuid ();
859#ifdef HAVE_SETRESUID
860 if (0 != setresuid (uid, uid, 0))
861 {
862 fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
863 r = 24;
864 goto cleanup_route_4;
865 }
866#else
867 /* Note: no 'setuid' here as we must keep our saved UID as root */
868 if (0 != seteuid (uid))
869 {
870 fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
871 r = 24;
872 goto cleanup_route_4;
873 }
874#endif
875
876 r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
877
878 /* now forward until we hit a problem */
879 run (fd_tun);
880 (void) close (fd_tun);
881
882 /* now need to regain privs so we can remove the firewall rules we added! */
883#ifdef HAVE_SETRESUID
884 if (0 != setresuid (uid, 0, 0))
885 {
886 fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (errno));
887 r = 40;
888 goto cleanup_route_4;
889 }
890#else
891 if (0 != seteuid (0))
892 {
893 fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
894 r = 40;
895 goto cleanup_route_4;
896 }
897#endif
898
899 /* update routing tables again -- this is why we could not fully drop privs */
900 /* now undo updating of routing tables; normal exit or clean-up-on-error case */
901 cleanup_route_4:
902 {
903 char *const route_clean_args[] =
904 {
905 "ip", "route", "del", "default", "via", dev,
906 "table", DNS_TABLE, NULL
907 };
908 if (0 != fork_and_exec (SBIN_IP, route_clean_args))
909 r += 1;
910 }
911 cleanup_forward_3:
912 {
913 char *const forward_clean_args[] =
914 {
915 "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
916 };
917 if (0 != fork_and_exec (SBIN_IP, forward_clean_args))
918 r += 2;
919 }
920 cleanup_mark_2:
921 {
922 char *const mark_clean_args[] =
923 {
924 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
925 "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
926 };
927 if (0 != fork_and_exec (SBIN_IPTABLES, mark_clean_args))
928 r += 4;
929 }
930 cleanup_mangle_1:
931 {
932 char *const mangle_clean_args[] =
933 {
934 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
935 "--sport", localport, "--dport", DNS_PORT, "-j", "ACCEPT",
936 NULL
937 };
938 if (0 != fork_and_exec (SBIN_IPTABLES, mangle_clean_args))
939 r += 8;
940 }
941
942 /* remove SIGINT handler so we can close the pipes */
943 (void) signal (SIGINT, SIG_IGN);
944 (void) close (cpipe[0]);
945 (void) close (cpipe[1]);
946 return r;
947}
948
949/* end of gnunet-helper-hijack-dns.c */