GNUnet  0.10.x
Data Structures | Macros | Functions | Variables
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 <linux/if_tun.h>
#include "gnunet_crypto_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, 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):

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 84 of file gnunet-helper-dns.c.

Referenced by run().

◆ DNS_PORT

#define DNS_PORT   "53"

Port for DNS traffic.

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

Referenced by main().

◆ 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 129 of file gnunet-helper-dns.c.

Referenced by main().

◆ 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 138 of file gnunet-helper-dns.c.

Referenced by main().

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 155 of file gnunet-helper-dns.c.

References cpipe.

Referenced by main().

156 {
157  /* ignore return value, as the signal handler could theoretically
158  be called many times before the shutdown can actually happen */
159  (void) write (cpipe[1], "K", 1);
160 }
static int cpipe[2]
Control pipe for shutdown via signal.
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 171 of file gnunet-helper-dns.c.

Referenced by fork_and_exec().

173 {
174  int fd;
175 
176  fd = open ("/dev/null", flags);
177  if (-1 == fd)
178  abort ();
179  if (fd == target_fd)
180  return;
181  if (-1 == dup2 (fd, target_fd))
182  {
183  (void) close (fd);
184  abort ();
185  }
186  (void) close (fd);
187 }
Here is the caller graph for this function:

◆ fork_and_exec()

static int fork_and_exec ( const char *  file,
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 198 of file gnunet-helper-dns.c.

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

Referenced by main().

200 {
201  int status;
202  pid_t pid;
203  pid_t ret;
204 
205  pid = fork ();
206  if (-1 == pid)
207  {
208  fprintf (stderr,
209  "fork failed: %s\n",
210  strerror (errno));
211  return 1;
212  }
213  if (0 == pid)
214  {
215  /* we are the child process */
216  /* close stdin/stdout to not cause interference
217  with the helper's main protocol! */
218  (void) close (0);
219  open_dev_null (0, O_RDONLY);
220  (void) close (1);
221  open_dev_null (1, O_WRONLY);
222  (void) execv (file, cmd);
223  /* can only get here on error */
224  fprintf (stderr,
225  "exec `%s' failed: %s\n",
226  file,
227  strerror (errno));
228  _exit (1);
229  }
230  /* keep running waitpid as long as the only error we get is 'EINTR' */
231  while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
232  (errno == EINTR) );
233  if (-1 == ret)
234  {
235  fprintf (stderr,
236  "waitpid failed: %s\n",
237  strerror (errno));
238  return 1;
239  }
240  if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
241  return 1;
242  /* child process completed and returned success, we're happy */
243  return 0;
244 }
static int ret
Final status code.
Definition: gnunet-arm.c:89
static void open_dev_null(int target_fd, int flags)
Open &#39;/dev/null&#39; and make the result the given file descriptor.
uint16_t status
See PRISM_STATUS_*-constants.
static struct GNUNET_PeerIdentity pid
Identity of the peer we transmit to / connect to.
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 asumed 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 255 of file gnunet-helper-dns.c.

References FD_SETSIZE.

Referenced by main().

256 {
257  struct ifreq ifr;
258  int fd;
259 
260  if (NULL == dev)
261  {
262  errno = EINVAL;
263  return -1;
264  }
265 
266  if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
267  {
268  fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
269  strerror (errno));
270  return -1;
271  }
272 
273  if (fd >= FD_SETSIZE)
274  {
275  fprintf (stderr, "File descriptor to large: %d", fd);
276  (void) close (fd);
277  return -1;
278  }
279 
280  memset (&ifr, 0, sizeof (ifr));
281  ifr.ifr_flags = IFF_TUN;
282 
283  if ('\0' != *dev)
284  strncpy (ifr.ifr_name, dev, IFNAMSIZ);
285 
286  if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
287  {
288  fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
289  strerror (errno));
290  (void) close (fd);
291  return -1;
292  }
293  strcpy (dev, ifr.ifr_name);
294  return fd;
295 }
#define FD_SETSIZE
Definition: winproc.h:39
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 306 of file gnunet-helper-dns.c.

References in6_ifreq::ifr6_addr, in6_ifreq::ifr6_ifindex, in6_ifreq::ifr6_prefixlen, and inet_pton().

Referenced by main().

307 {
308  struct ifreq ifr;
309  struct in6_ifreq ifr6;
310  struct sockaddr_in6 sa6;
311  int fd;
312 
313  /*
314  * parse the new address
315  */
316  memset (&sa6, 0, sizeof (struct sockaddr_in6));
317  sa6.sin6_family = AF_INET6;
318  if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
319  {
320  fprintf (stderr,
321  "Failed to parse IPv6 address `%s': %s\n",
322  address,
323  strerror (errno));
324  exit (1);
325  }
326 
327  if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
328  {
329  fprintf (stderr,
330  "Error creating IPv6 socket: %s (ignored)\n",
331  strerror (errno));
332  /* ignore error, maybe only IPv4 works on this system! */
333  return;
334  }
335 
336  memset (&ifr, 0, sizeof (struct ifreq));
337  /*
338  * Get the index of the if
339  */
340  strncpy (ifr.ifr_name, dev, IFNAMSIZ);
341  if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
342  {
343  fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
344  (void) close (fd);
345  exit (1);
346  }
347 
348  memset (&ifr6, 0, sizeof (struct in6_ifreq));
349  ifr6.ifr6_addr = sa6.sin6_addr;
350  ifr6.ifr6_ifindex = ifr.ifr_ifindex;
351  ifr6.ifr6_prefixlen = prefix_len;
352 
353  /*
354  * Set the address
355  */
356  if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
357  {
358  fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
359  strerror (errno));
360  (void) close (fd);
361  exit (1);
362  }
363 
364  /*
365  * Get the flags
366  */
367  if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
368  {
369  fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
370  strerror (errno));
371  (void) close (fd);
372  exit (1);
373  }
374 
375  /*
376  * Add the UP and RUNNING flags
377  */
378  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
379  if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
380  {
381  fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
382  strerror (errno));
383  (void) close (fd);
384  exit (1);
385  }
386 
387  if (0 != close (fd))
388  {
389  fprintf (stderr, "close failed: %s\n", strerror (errno));
390  exit (1);
391  }
392 }
This is in linux/include/net/ipv6.h, but not always exported...
static int inet_pton(int af, const char *cp, struct in_addr *buf)
Convert IPv4 address from text to binary form.
static char * address
GNS address for this phone.
Here is the call graph for this function:
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 403 of file gnunet-helper-dns.c.

References inet_pton().

Referenced by main().

404 {
405  int fd;
406  struct sockaddr_in *addr;
407  struct ifreq ifr;
408 
409  memset (&ifr, 0, sizeof (struct ifreq));
410  addr = (struct sockaddr_in *) &(ifr.ifr_addr);
411  addr->sin_family = AF_INET;
412 
413  /*
414  * Parse the address
415  */
416  if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
417  {
418  fprintf (stderr,
419  "Failed to parse IPv4 address `%s': %s\n",
420  address,
421  strerror (errno));
422  exit (1);
423  }
424 
425  if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
426  {
427  fprintf (stderr,
428  "Error creating IPv4 socket: %s\n",
429  strerror (errno));
430  exit (1);
431  }
432 
433  strncpy (ifr.ifr_name, dev, IFNAMSIZ);
434 
435  /*
436  * Set the address
437  */
438  if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
439  {
440  fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
441  (void) close (fd);
442  exit (1);
443  }
444 
445  /*
446  * Parse the netmask
447  */
448  addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
449  if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
450  {
451  fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
452  strerror (errno));
453  (void) close (fd);
454  exit (1);
455  }
456 
457  /*
458  * Set the netmask
459  */
460  if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
461  {
462  fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
463  strerror (errno));
464  (void) close (fd);
465  exit (1);
466  }
467 
468  /*
469  * Get the flags
470  */
471  if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
472  {
473  fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
474  strerror (errno));
475  (void) close (fd);
476  exit (1);
477  }
478 
479  /*
480  * Add the UP and RUNNING flags
481  */
482  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
483  if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
484  {
485  fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
486  strerror (errno));
487  (void) close (fd);
488  exit (1);
489  }
490 
491  if (0 != close (fd))
492  {
493  fprintf (stderr, "close failed: %s\n", strerror (errno));
494  (void) close (fd);
495  exit (1);
496  }
497 }
static int inet_pton(int af, const char *cp, struct in_addr *buf)
Convert IPv4 address from text to binary form.
static char * address
GNS address for this phone.
Here is the call graph for this function:
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" priviledges (saved UID is still 0, but effective UID is the real user ID).

Parameters
fd_tuntunnel FD

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

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

Referenced by main().

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

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

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

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

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