GNUnet 0.22.2
gnunet-helper-dns.c File Reference

helper to install firewall rules to hijack all DNS traffic and send it to our virtual interface (except for DNS traffic that originates on the specified port). More...

#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_common.h"
#include "gnunet_protocols.h"
Include dependency graph for gnunet-helper-dns.c:

Go to the source code of this file.

Data Structures

struct  in6_ifreq
 This is in linux/include/net/ipv6.h, but not always exported... More...
 

Macros

#define MAX_SIZE   65536
 Need 'struct GNUNET_MessageHeader'. More...
 
#define DNS_PORT   "53"
 Port for DNS traffic. More...
 
#define DNS_MARK   "136708149"
 Marker we set for our hijacked DNS traffic. More...
 
#define DNS_TABLE   "53"
 Table we use for our DNS rules. More...
 

Functions

static void signal_handler (int signal)
 Signal handler called to initiate "nice" shutdown. More...
 
static void open_dev_null (int target_fd, int flags)
 Open '/dev/null' and make the result the given file descriptor. More...
 
static int fork_and_exec (const char *file, const char *const cmd[])
 Run the given command and wait for it to complete. More...
 
static int init_tun (char *dev)
 Creates a tun-interface called dev;. More...
 
static void set_address6 (const char *dev, const char *address, unsigned long prefix_len)
 Sets the IPv6-Address given in address on the interface dev. More...
 
static void set_address4 (const char *dev, const char *address, const char *mask)
 Sets the IPv4-Address given in address on the interface dev. More...
 
static void run (int fd_tun)
 Start forwarding to and from the tunnel. More...
 
int main (int argc, char *const *argv)
 Main function of "gnunet-helper-dns", which opens a VPN tunnel interface, redirects all outgoing DNS traffic (except from the specified port) to that interface and then passes traffic from and to the interface via stdin/stdout. More...
 

Variables

static const char * sbin_iptables
 Name and full path of IPTABLES binary. More...
 
static const char * sbin_ip6tables
 Name and full path of IPTABLES binary. More...
 
static const char * sbin_sysctl
 Name and full path of sysctl binary. More...
 
static const char * sbin_ip
 Name and full path of IPTABLES binary. More...
 
static int cpipe [2]
 Control pipe for shutdown via signal. More...
 

Detailed Description

helper to install firewall rules to hijack all DNS traffic and send it to our virtual interface (except for DNS traffic that originates on the specified port).

We then allow interacting with our virtual interface via stdin/stdout.

Author
Philipp Tölke
Christian Grothoff

This program alters the Linux firewall rules so that DNS traffic that ordinarily exits the system can be intercepted and managed by a virtual interface. In order to achieve this, DNS traffic is marked with the DNS_MARK given in below and re-routed to a custom table with the DNS_TABLE ID given below. Systems and administrators must take care to not cause conflicts with these values (it was deemed safest to hardcode them as passing these values as arguments might permit messing with arbitrary firewall rules, which would be dangerous). Traffic coming from the same group ID as the effective group ID that this process is running as is not intercepted.

The code first sets up the virtual interface, then begins to redirect the DNS traffic to it, and then on errors or SIGTERM shuts down the virtual interface and removes the rules for the traffic redirection.

Note that having this binary SUID is only partially safe: it will allow redirecting (and intercepting / mangling) of all DNS traffic originating from this system by any user who is able to run it. Furthermore, this code will make it trivial to DoS all DNS traffic originating from the current system, simply by sending it to nowhere (redirect stdout to /dev/null).

Naturally, neither of these problems can be helped as this is the fundamental purpose of the binary. Certifying that this code is "safe" thus only means that it doesn't allow anything else (such as local priv. escalation, etc.).

The following list of people have reviewed this code and considered it safe (within specifications) since the last modification (if you reviewed it, please have your name added to the list):

  • Christian Grothoff

Definition in file gnunet-helper-dns.c.

Macro Definition Documentation

◆ MAX_SIZE

#define MAX_SIZE   65536

Need 'struct GNUNET_MessageHeader'.

Need DNS message types. Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE)

Definition at line 86 of file gnunet-helper-dns.c.

◆ DNS_PORT

#define DNS_PORT   "53"

Port for DNS traffic.

Definition at line 123 of file gnunet-helper-dns.c.

◆ DNS_MARK

#define DNS_MARK   "136708149"

Marker we set for our hijacked DNS traffic.

We use GNUnet's port (2086) plus the DNS port (53) in HEX to make a 32-bit mark (which is hopefully long enough to not collide); so 0x08260035 = 136708149 (hopefully unique enough...).

Definition at line 131 of file gnunet-helper-dns.c.

◆ DNS_TABLE

#define DNS_TABLE   "53"

Table we use for our DNS rules.

0-255 is the range and 0, 253, 254 and 255 are already reserved. As this is about DNS and as "53" is likely (fingers crossed!) high enough to not usually conflict with a normal user's setup, we use 53 to give a hint that this has something to do with DNS.

Definition at line 140 of file gnunet-helper-dns.c.

Function Documentation

◆ signal_handler()

static void signal_handler ( int  signal)
static

Signal handler called to initiate "nice" shutdown.

Signals select loop via non-bocking pipe 'cpipe'.

Parameters
signalsignal number of the signal (not used)

Definition at line 157 of file gnunet-helper-dns.c.

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}
static int cpipe[2]
Control pipe for shutdown via signal.

References cpipe.

Referenced by main().

Here is the caller graph for this function:

◆ open_dev_null()

static void open_dev_null ( int  target_fd,
int  flags 
)
static

Open '/dev/null' and make the result the given file descriptor.

Parameters
target_fddesired FD to point to /dev/null
flagsopen flags (O_RDONLY, O_WRONLY)

Definition at line 173 of file gnunet-helper-dns.c.

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}

Referenced by fork_and_exec().

Here is the caller graph for this function:

◆ fork_and_exec()

static int fork_and_exec ( const char *  file,
const char *const  cmd[] 
)
static

Run the given command and wait for it to complete.

Parameters
filename of the binary to run
cmdcommand line arguments (as given to 'execv')
Returns
0 on success, 1 on any error

Definition at line 200 of file gnunet-helper-dns.c.

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, (char *const*)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}
static int ret
Final status code.
Definition: gnunet-arm.c:93
static void open_dev_null(int target_fd, int flags)
Open '/dev/null' and make the result the given file descriptor.
static int status
The program status; 0 for success.
Definition: gnunet-nse.c:39
static struct GNUNET_PeerIdentity pid
Identity of the peer we transmit to / connect to.

References open_dev_null(), pid, ret, and status.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ init_tun()

static int init_tun ( char *  dev)
static

Creates a tun-interface called dev;.

Parameters
devis assumed to point to a char[IFNAMSIZ] if *dev == '\0', uses the name supplied by the kernel;
Returns
the fd to the tun or -1 on error

Definition at line 258 of file gnunet-helper-dns.c.

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}

Referenced by main().

Here is the caller graph for this function:

◆ set_address6()

static void set_address6 ( const char *  dev,
const char *  address,
unsigned long  prefix_len 
)
static

Sets the IPv6-Address given in address on the interface dev.

Parameters
devthe interface to configure
addressthe IPv6-Address
prefix_lenthe length of the network-prefix

Definition at line 309 of file gnunet-helper-dns.c.

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}
static char * address
GNS address for this phone.
This is in linux/include/net/ipv6.h, but not always exported...

References address, in6_ifreq::ifr6_addr, in6_ifreq::ifr6_ifindex, and in6_ifreq::ifr6_prefixlen.

Referenced by main().

Here is the caller graph for this function:

◆ set_address4()

static void set_address4 ( const char *  dev,
const char *  address,
const char *  mask 
)
static

Sets the IPv4-Address given in address on the interface dev.

Parameters
devthe interface to configure
addressthe IPv4-Address
maskthe netmask

Definition at line 406 of file gnunet-helper-dns.c.

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}

References address.

Referenced by main().

Here is the caller graph for this function:

◆ run()

static void run ( int  fd_tun)
static

Start forwarding to and from the tunnel.

This function runs with "reduced" privileges (saved UID is still 0, but effective UID is the real user ID).

Parameters
fd_tuntunnel FD

Definition at line 511 of file gnunet-helper-dns.c.

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 int r;
531
532 while (1)
533 {
534 FD_ZERO (&fds_w);
535 FD_ZERO (&fds_r);
536
537 /*
538 * We are supposed to read and the buffer is empty
539 * -> select on read from tun
540 */
541 if (0 == buftun_size)
542 FD_SET (fd_tun, &fds_r);
543
544 /*
545 * We are supposed to read and the buffer is not empty
546 * -> select on write to stdout
547 */
548 if (0 < buftun_size)
549 FD_SET (1, &fds_w);
550
551 /*
552 * We are supposed to write and the buffer is empty
553 * -> select on read from stdin
554 */
555 if (NULL == bufin_read)
556 FD_SET (0, &fds_r);
557
558 /*
559 * We are supposed to write and the buffer is not empty
560 * -> select on write to tun
561 */
562 if (NULL != bufin_read)
563 FD_SET (fd_tun, &fds_w);
564
565 FD_SET (cpipe[0], &fds_r);
566 max = (fd_tun > cpipe[0]) ? fd_tun : cpipe[0];
567
568 r = select (max + 1, &fds_r, &fds_w, NULL, NULL);
569
570 if (-1 == r)
571 {
572 if (EINTR == errno)
573 continue;
574 fprintf (stderr, "select failed: %s\n", strerror (errno));
575 return;
576 }
577
578 if (r > 0)
579 {
580 if (FD_ISSET (cpipe[0], &fds_r))
581 return; /* aborted by signal */
582
583 if (FD_ISSET (fd_tun, &fds_r))
584 {
585 buftun_size =
586 read (fd_tun, buftun + sizeof(struct GNUNET_MessageHeader),
587 MAX_SIZE - sizeof(struct GNUNET_MessageHeader));
588 if (-1 == buftun_size)
589 {
590 if ((errno == EINTR) ||
591 (errno == EAGAIN))
592 {
593 buftun_size = 0;
594 continue;
595 }
596 fprintf (stderr, "read-error: %s\n", strerror (errno));
597 return;
598 }
599 if (0 == buftun_size)
600 {
601 fprintf (stderr, "EOF on tun\n");
602 return;
603 }
604 buftun_read = buftun;
605 {
606 struct GNUNET_MessageHeader *hdr =
607 (struct GNUNET_MessageHeader *) buftun;
608 buftun_size += sizeof(struct GNUNET_MessageHeader);
610 hdr->size = htons (buftun_size);
611 }
612 }
613 else if (FD_ISSET (1, &fds_w))
614 {
615 ssize_t written = write (1, buftun_read, buftun_size);
616
617 if (-1 == written)
618 {
619 if ((errno == EINTR) ||
620 (errno == EAGAIN))
621 continue;
622 fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
623 return;
624 }
625 if (0 == written)
626 {
627 fprintf (stderr, "write returned 0\n");
628 return;
629 }
630 buftun_size -= written;
631 buftun_read += written;
632 }
633
634 if (FD_ISSET (0, &fds_r))
635 {
636 bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
637 if (-1 == bufin_size)
638 {
639 bufin_read = NULL;
640 if ((errno == EINTR) ||
641 (errno == EAGAIN))
642 continue;
643 fprintf (stderr, "read-error: %s\n", strerror (errno));
644 return;
645 }
646 if (0 == bufin_size)
647 {
648 bufin_read = NULL;
649 fprintf (stderr, "EOF on stdin\n");
650 return;
651 }
652 {
653 struct GNUNET_MessageHeader *hdr;
654
655PROCESS_BUFFER:
656 bufin_rpos += bufin_size;
657 if (bufin_rpos < sizeof(struct GNUNET_MessageHeader))
658 continue;
659 hdr = (struct GNUNET_MessageHeader *) bufin;
660 if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
661 {
662 fprintf (stderr, "protocol violation!\n");
663 return;
664 }
665 if (ntohs (hdr->size) > bufin_rpos)
666 continue;
667 bufin_read = bufin + sizeof(struct GNUNET_MessageHeader);
668 bufin_size = ntohs (hdr->size) - sizeof(struct GNUNET_MessageHeader);
669 bufin_rpos -= bufin_size + sizeof(struct GNUNET_MessageHeader);
670 }
671 }
672 else if (FD_ISSET (fd_tun, &fds_w))
673 {
674 ssize_t written = write (fd_tun, bufin_read, bufin_size);
675
676 if (-1 == written)
677 {
678 if ((errno == EINTR) ||
679 (errno == EAGAIN))
680 continue;
681 fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
682 return;
683 }
684 if (0 == written)
685 {
686 fprintf (stderr, "write returned 0\n");
687 return;
688 }
689 {
690 bufin_size -= written;
691 bufin_read += written;
692 if (0 == bufin_size)
693 {
694 memmove (bufin, bufin_read, bufin_rpos);
695 bufin_read = NULL; /* start reading again */
696 bufin_size = 0;
697 goto PROCESS_BUFFER;
698 }
699 }
700 }
701 }
702 }
703}
#define MAX_SIZE
Need 'struct GNUNET_MessageHeader'.
uint16_t type
The type of the message (GNUNET_MESSAGE_TYPE_XXXX), in big-endian format.
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format.
#define GNUNET_MESSAGE_TYPE_DNS_HELPER
Type of messages between the gnunet-helper-dns and the service.
#define max(x, y)
Header for all communications.

References cpipe, GNUNET_MESSAGE_TYPE_DNS_HELPER, max, MAX_SIZE, GNUNET_MessageHeader::size, and GNUNET_MessageHeader::type.

Referenced by main().

Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char *const *  argv 
)

Main function of "gnunet-helper-dns", which opens a VPN tunnel interface, redirects all outgoing DNS traffic (except from the specified port) to that interface and then passes traffic from and to the interface via stdin/stdout.

Once stdin/stdout close or have other errors, the tunnel is closed and the DNS traffic redirection is stopped.

Parameters
argcnumber of arguments
argv0: binary name (should be "gnunet-helper-vpn") 1: tunnel interface name (typically "gnunet-dns") 2: IPv6 address for the tunnel ("FE80::1") 3: IPv6 netmask length in bits ("64") 4: IPv4 address for the tunnel ("1.2.3.4") 5: IPv4 netmask ("255.255.0.0") 6: skip sysctl, routing and iptables setup ("0")
Returns
0 on success, otherwise code indicating type of error: 1 wrong number of arguments 2 invalid arguments (i.e. port number / prefix length wrong) 3 iptables not executable 4 ip not executable 5 failed to initialize tunnel interface 6 failed to initialize control pipe 8 failed to change routing table, cleanup successful 9-23 failed to change routing table and failed to undo some changes to routing table 24 failed to drop privs 25-39 failed to drop privs and then failed to undo some changes to routing table 40 failed to regain privs 41-55 failed to regain prisv and then failed to undo some changes to routing table 254 insufficient privileges 255 failed to handle kill signal properly

Definition at line 739 of file gnunet-helper-dns.c.

740{
741 int r;
742 char dev[IFNAMSIZ];
743 char mygid[32];
744 int fd_tun;
745 uid_t uid;
746 int nortsetup = 0;
747
748 if (7 != argc)
749 {
750 fprintf (stderr, "Fatal: must supply 6 arguments!\n");
751 return 1;
752 }
753
754 /* assert privs so we can modify the firewall rules! */
755 uid = getuid ();
756#ifdef HAVE_SETRESUID
757 if (0 != setresuid (uid, 0, 0))
758 {
759 fprintf (stderr, "Failed to setresuid to root: %s\n", strerror (errno));
760 return 254;
761 }
762#else
763 if (0 != seteuid (0))
764 {
765 fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
766 return 254;
767 }
768#endif
769 if (0 == strncmp (argv[6], "1", 2))
770 nortsetup = 1;
771
772 if (0 == nortsetup)
773 {
774 /* verify that the binaries we care about are executable */
775#ifdef IPTABLES
776 if (0 == access (IPTABLES, X_OK))
777 sbin_iptables = IPTABLES;
778 else
779#endif
780 if (0 == access ("/sbin/iptables", X_OK))
781 sbin_iptables = "/sbin/iptables";
782 else if (0 == access ("/usr/sbin/iptables", X_OK))
783 sbin_iptables = "/usr/sbin/iptables";
784 else
785 {
786 fprintf (stderr,
787 "Fatal: executable iptables not found in approved directories: %s\n",
788 strerror (errno));
789 return 3;
790 }
791#ifdef IP6TABLES
792 if (0 == access (IP6TABLES, X_OK))
793 sbin_ip6tables = IP6TABLES;
794 else
795#endif
796 if (0 == access ("/sbin/ip6tables", X_OK))
797 sbin_ip6tables = "/sbin/ip6tables";
798 else if (0 == access ("/usr/sbin/ip6tables", X_OK))
799 sbin_ip6tables = "/usr/sbin/ip6tables";
800 else
801 {
802 fprintf (stderr,
803 "Fatal: executable ip6tables not found in approved directories: %s\n",
804 strerror (errno));
805 return 3;
806 }
807#ifdef PATH_TO_IP
808 if (0 == access (PATH_TO_IP, X_OK))
809 sbin_ip = PATH_TO_IP;
810 else
811#endif
812 if (0 == access ("/sbin/ip", X_OK))
813 sbin_ip = "/sbin/ip";
814 else if (0 == access ("/usr/sbin/ip", X_OK))
815 sbin_ip = "/usr/sbin/ip";
816 else if (0 == access ("/bin/ip", X_OK)) /* gentoo has it there */
817 sbin_ip = "/bin/ip";
818 else
819 {
820 fprintf (stderr,
821 "Fatal: executable ip not found in approved directories: %s\n",
822 strerror (errno));
823 return 4;
824 }
825#ifdef SYSCTL
826 if (0 == access (SYSCTL, X_OK))
827 sbin_sysctl = SYSCTL;
828 else
829#endif
830 if (0 == access ("/sbin/sysctl", X_OK))
831 sbin_sysctl = "/sbin/sysctl";
832 else if (0 == access ("/usr/sbin/sysctl", X_OK))
833 sbin_sysctl = "/usr/sbin/sysctl";
834 else
835 {
836 fprintf (stderr,
837 "Fatal: executable sysctl not found in approved directories: %s\n",
838 strerror (errno));
839 return 5;
840 }
841 }
842
843 /* setup 'mygid' string */
844 snprintf (mygid, sizeof(mygid), "%d", (int) getegid ());
845
846 /* do not die on SIGPIPE */
847 if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
848 {
849 fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
850 strerror (errno));
851 return 7;
852 }
853
854 /* setup pipe to shutdown nicely on SIGINT */
855 if (0 != pipe (cpipe))
856 {
857 fprintf (stderr,
858 "Fatal: could not setup control pipe: %s\n",
859 strerror (errno));
860 return 6;
861 }
862 if (cpipe[0] >= FD_SETSIZE)
863 {
864 fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
865 (void) close (cpipe[0]);
866 (void) close (cpipe[1]);
867 return 6;
868 }
869 {
870 /* make pipe non-blocking, as we theoretically could otherwise block
871 in the signal handler */
872 int flags = fcntl (cpipe[1], F_GETFL);
873 if (-1 == flags)
874 {
875 fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
876 (void) close (cpipe[0]);
877 (void) close (cpipe[1]);
878 return 6;
879 }
880 flags |= O_NONBLOCK;
881 if (0 != fcntl (cpipe[1], F_SETFL, flags))
882 {
883 fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (
884 errno));
885 (void) close (cpipe[0]);
886 (void) close (cpipe[1]);
887 return 6;
888 }
889 }
890 if ((SIG_ERR == signal (SIGTERM, &signal_handler)) ||
891#if (SIGTERM != GNUNET_TERM_SIG)
892 (SIG_ERR == signal (GNUNET_TERM_SIG, &signal_handler)) ||
893#endif
894 (SIG_ERR == signal (SIGINT, &signal_handler)) ||
895 (SIG_ERR == signal (SIGHUP, &signal_handler)))
896 {
897 fprintf (stderr,
898 "Fatal: could not initialize signal handler: %s\n",
899 strerror (errno));
900 (void) close (cpipe[0]);
901 (void) close (cpipe[1]);
902 return 7;
903 }
904
905
906 /* get interface name */
907 strncpy (dev, argv[1], IFNAMSIZ);
908 dev[IFNAMSIZ - 1] = '\0';
909
910 /* Disable rp filtering */
911 if (0 == nortsetup)
912 {
913 const char *const sysctl_args[] = { "sysctl", "-w",
914 "net.ipv4.conf.all.rp_filter=0", NULL };
915 const char *const sysctl_args2[] = { "sysctl", "-w",
916 "net.ipv4.conf.default.rp_filter=0", NULL };
917 if ((0 != fork_and_exec (sbin_sysctl, sysctl_args)) ||
918 (0 != fork_and_exec (sbin_sysctl, sysctl_args2)))
919 {
920 fprintf (stderr,
921 "Failed to disable rp filtering.\n");
922 return 5;
923 }
924 }
925
926
927 /* now open virtual interface (first part that requires root) */
928 if (-1 == (fd_tun = init_tun (dev)))
929 {
930 fprintf (stderr, "Fatal: could not initialize tun-interface\n");
931 (void) signal (SIGTERM, SIG_IGN);
932#if (SIGTERM != GNUNET_TERM_SIG)
933 (void) signal (GNUNET_TERM_SIG, SIG_IGN);
934#endif
935 (void) signal (SIGINT, SIG_IGN);
936 (void) signal (SIGHUP, SIG_IGN);
937 (void) close (cpipe[0]);
938 (void) close (cpipe[1]);
939 return 5;
940 }
941
942 /* now set interface addresses */
943 {
944 const char *address = argv[2];
945 long prefix_len = atol (argv[3]);
946
947 if ((prefix_len < 1) || (prefix_len > 127))
948 {
949 fprintf (stderr, "Fatal: prefix_len out of range\n");
950 (void) signal (SIGTERM, SIG_IGN);
951#if (SIGTERM != GNUNET_TERM_SIG)
952 (void) signal (GNUNET_TERM_SIG, SIG_IGN);
953#endif
954 (void) signal (SIGINT, SIG_IGN);
955 (void) signal (SIGHUP, SIG_IGN);
956 (void) close (cpipe[0]);
957 (void) close (cpipe[1]);
958 return 2;
959 }
960 set_address6 (dev, address, prefix_len);
961 }
962
963 {
964 const char *address = argv[4];
965 const char *mask = argv[5];
966
967 set_address4 (dev, address, mask);
968 }
969
970
971 /* update routing tables -- next part why we need SUID! */
972 /* Forward everything from our EGID (which should only be held
973 by the 'gnunet-service-dns') and with destination
974 to port 53 on UDP, without hijacking */
975 if (0 == nortsetup)
976 {
977 r = 8; /* failed to fully setup routing table */
978 {
979 const char *const mangle_args[] = {
980 "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
981 "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
982 "ACCEPT", NULL
983 };
984 if (0 != fork_and_exec (sbin_iptables, mangle_args))
985 goto cleanup_rest;
986 }
987 {
988 const char *const mangle_args[] = {
989 "ip6tables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
990 "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
991 "ACCEPT", NULL
992 };
993 if (0 != fork_and_exec (sbin_ip6tables, mangle_args))
994 goto cleanup_mangle_1b;
995 }
996 /* Mark all of the other DNS traffic using our mark DNS_MARK,
997 unless it is on a link-local IPv6 address, which we cannot support. */
998 {
999 const char *const mark_args[] = {
1000 "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
1001 "udp", "--dport", DNS_PORT,
1002 "-j", "MARK", "--set-mark", DNS_MARK,
1003 NULL
1004 };
1005 if (0 != fork_and_exec (sbin_iptables, mark_args))
1006 goto cleanup_mangle_1;
1007 }
1008 {
1009 const char *const mark_args[] = {
1010 "ip6tables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
1011 "udp", "--dport", DNS_PORT,
1012 "!", "-s", "fe80::/10", /* this line excludes link-local traffic */
1013 "-j", "MARK", "--set-mark", DNS_MARK,
1014 NULL
1015 };
1016 if (0 != fork_and_exec (sbin_ip6tables, mark_args))
1017 goto cleanup_mark_2b;
1018 }
1019 /* Forward all marked DNS traffic to our DNS_TABLE */
1020 {
1021 const char *const forward_args[] = {
1022 "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1023 };
1024 if (0 != fork_and_exec (sbin_ip, forward_args))
1025 goto cleanup_mark_2;
1026 }
1027 {
1028 const char *const forward_args[] = {
1029 "ip", "-6", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1030 };
1031 if (0 != fork_and_exec (sbin_ip, forward_args))
1032 goto cleanup_forward_3b;
1033 }
1034 /* Finally, add rule in our forwarding table to pass to our virtual interface */
1035 {
1036 const char *const route_args[] = {
1037 "ip", "route", "add", "default", "dev", dev,
1038 "table", DNS_TABLE, NULL
1039 };
1040 if (0 != fork_and_exec (sbin_ip, route_args))
1041 goto cleanup_forward_3;
1042 }
1043 {
1044 const char *const route_args[] = {
1045 "ip", "-6", "route", "add", "default", "dev", dev,
1046 "table", DNS_TABLE, NULL
1047 };
1048 if (0 != fork_and_exec (sbin_ip, route_args))
1049 goto cleanup_route_4b;
1050 }
1051 }
1052
1053 /* drop privs *except* for the saved UID; this is not perfect, but better
1054 than doing nothing */
1055#ifdef HAVE_SETRESUID
1056 if (0 != setresuid (uid, uid, 0))
1057 {
1058 fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
1059 r = 24;
1060 goto cleanup_route_4;
1061 }
1062#else
1063 /* Note: no 'setuid' here as we must keep our saved UID as root */
1064 if (0 != seteuid (uid))
1065 {
1066 fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
1067 r = 24;
1068 goto cleanup_route_4;
1069 }
1070#endif
1071
1072 r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
1073
1074 /* now forward until we hit a problem */
1075 run (fd_tun);
1076
1077 /* now need to regain privs so we can remove the firewall rules we added! */
1078#ifdef HAVE_SETRESUID
1079 if (0 != setresuid (uid, 0, 0))
1080 {
1081 fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (
1082 errno));
1083 r = 40;
1084 goto cleanup_route_4;
1085 }
1086#else
1087 if (0 != seteuid (0))
1088 {
1089 fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
1090 r = 40;
1091 goto cleanup_route_4;
1092 }
1093#endif
1094
1095 /* update routing tables again -- this is why we could not fully drop privs */
1096 /* now undo updating of routing tables; normal exit or clean-up-on-error case */
1097cleanup_route_4:
1098 if (0 == nortsetup)
1099 {
1100 const char *const route_clean_args[] = {
1101 "ip", "-6", "route", "del", "default", "dev", dev,
1102 "table", DNS_TABLE, NULL
1103 };
1104 if (0 != fork_and_exec (sbin_ip, route_clean_args))
1105 r += 1;
1106 }
1107cleanup_route_4b:
1108 if (0 == nortsetup)
1109 {
1110 const char *const route_clean_args[] = {
1111 "ip", "route", "del", "default", "dev", dev,
1112 "table", DNS_TABLE, NULL
1113 };
1114 if (0 != fork_and_exec (sbin_ip, route_clean_args))
1115 r += 1;
1116 }
1117cleanup_forward_3:
1118 if (0 == nortsetup)
1119 {
1120 const char *const forward_clean_args[] = {
1121 "ip", "-6", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1122 };
1123 if (0 != fork_and_exec (sbin_ip, forward_clean_args))
1124 r += 2;
1125 }
1126cleanup_forward_3b:
1127 if (0 == nortsetup)
1128 {
1129 const char *const forward_clean_args[] = {
1130 "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1131 };
1132 if (0 != fork_and_exec (sbin_ip, forward_clean_args))
1133 r += 2;
1134 }
1135cleanup_mark_2:
1136 if (0 == nortsetup)
1137 {
1138 const char *const mark_clean_args[] = {
1139 "ip6tables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1140 "--dport", DNS_PORT,
1141 "!", "-s", "fe80::/10", /* this line excludes link-local traffic */
1142 "-j", "MARK", "--set-mark", DNS_MARK, NULL
1143 };
1144 if (0 != fork_and_exec (sbin_ip6tables, mark_clean_args))
1145 r += 4;
1146 }
1147cleanup_mark_2b:
1148 if (0 == nortsetup)
1149 {
1150 const char *const mark_clean_args[] = {
1151 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1152 "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
1153 };
1154 if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
1155 r += 4;
1156 }
1157cleanup_mangle_1:
1158 if (0 == nortsetup)
1159 {
1160 const char *const mangle_clean_args[] = {
1161 "ip6tables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1162 "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
1163 NULL
1164 };
1165 if (0 != fork_and_exec (sbin_ip6tables, mangle_clean_args))
1166 r += 8;
1167 }
1168cleanup_mangle_1b:
1169 if (0 == nortsetup)
1170 {
1171 const char *const mangle_clean_args[] = {
1172 "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1173 "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
1174 NULL
1175 };
1176 if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
1177 r += 8;
1178 }
1179
1180cleanup_rest:
1181 /* close virtual interface */
1182 (void) close (fd_tun);
1183 /* remove signal handler so we can close the pipes */
1184 (void) signal (SIGTERM, SIG_IGN);
1185#if (SIGTERM != GNUNET_TERM_SIG)
1186 (void) signal (GNUNET_TERM_SIG, SIG_IGN);
1187#endif
1188 (void) signal (SIGINT, SIG_IGN);
1189 (void) signal (SIGHUP, SIG_IGN);
1190 (void) close (cpipe[0]);
1191 (void) close (cpipe[1]);
1192 return r;
1193}
#define DNS_TABLE
Table we use for our DNS rules.
static const char * sbin_sysctl
Name and full path of sysctl binary.
static const char * sbin_ip
Name and full path of IPTABLES binary.
static void run(int fd_tun)
Start forwarding to and from the tunnel.
#define DNS_MARK
Marker we set for our hijacked DNS traffic.
static int fork_and_exec(const char *file, const char *const cmd[])
Run the given command and wait for it to complete.
static const char * sbin_iptables
Name and full path of IPTABLES binary.
static void signal_handler(int signal)
Signal handler called to initiate "nice" shutdown.
static int init_tun(char *dev)
Creates a tun-interface called dev;.
static void set_address4(const char *dev, const char *address, const char *mask)
Sets the IPv4-Address given in address on the interface dev.
static const char * sbin_ip6tables
Name and full path of IPTABLES binary.
#define DNS_PORT
Port for DNS traffic.
static void set_address6(const char *dev, const char *address, unsigned long prefix_len)
Sets the IPv6-Address given in address on the interface dev.
#define GNUNET_TERM_SIG
The termination signal.
Definition: platform.h:234

References address, cpipe, DNS_MARK, DNS_PORT, DNS_TABLE, fork_and_exec(), GNUNET_TERM_SIG, init_tun(), run(), sbin_ip, sbin_ip6tables, sbin_iptables, sbin_sysctl, set_address4(), set_address6(), and signal_handler().

Here is the call graph for this function:

Variable Documentation

◆ sbin_iptables

const char* sbin_iptables
static

Name and full path of IPTABLES binary.

Definition at line 103 of file gnunet-helper-dns.c.

Referenced by main().

◆ sbin_ip6tables

const char* sbin_ip6tables
static

Name and full path of IPTABLES binary.

Definition at line 108 of file gnunet-helper-dns.c.

Referenced by main().

◆ sbin_sysctl

const char* sbin_sysctl
static

Name and full path of sysctl binary.

Definition at line 113 of file gnunet-helper-dns.c.

Referenced by main().

◆ sbin_ip

const char* sbin_ip
static

Name and full path of IPTABLES binary.

Definition at line 118 of file gnunet-helper-dns.c.

Referenced by main(), and try_ip().

◆ cpipe

int cpipe[2]
static

Control pipe for shutdown via signal.

[0] is the read end, [1] is the write end.

Definition at line 147 of file gnunet-helper-dns.c.

Referenced by main(), run(), and signal_handler().