gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r18912 - gnunet/src/dns


From: gnunet
Subject: [GNUnet-SVN] r18912 - gnunet/src/dns
Date: Mon, 2 Jan 2012 04:51:28 +0100

Author: grothoff
Date: 2012-01-02 04:51:28 +0100 (Mon, 02 Jan 2012)
New Revision: 18912

Modified:
   gnunet/src/dns/gnunet-helper-hijack-dns.c
Log:
dns hijacker code review

Modified: gnunet/src/dns/gnunet-helper-hijack-dns.c
===================================================================
--- gnunet/src/dns/gnunet-helper-hijack-dns.c   2012-01-02 02:32:54 UTC (rev 
18911)
+++ gnunet/src/dns/gnunet-helper-hijack-dns.c   2012-01-02 03:51:28 UTC (rev 
18912)
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   (C) 2010 Christian Grothoff
+   (C) 2010, 2011, 2012 Christian Grothoff
 
    GNUnet is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published
@@ -19,138 +19,310 @@
    */
 
 /**
- * @file vpn/gnunet-helper-hijack-dns.c
- * @brief
+ * @file dns/gnunet-helper-hijack-dns.c
+ * @brief 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. 
  * @author Philipp Tölke
+ * @author 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).
+ *
+ * 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 can create a virtual
+ * interface (and this is again enabled by other GNUnet SUID
+ * binaries).  Furthermore, even without the ability to create a
+ * tunnel interface, this code will make it possible to DoS all DNS
+ * traffic originating from the current system, simply by sending it
+ * to nowhere.  
+ *
+ * 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 
  */
-#include <platform.h>
+#include "platform.h"
 
-#include "gnunet_common.h"
+/**
+ * Name and full path of IPTABLES binary.
+ */
+#define SBIN_IPTABLES "/sbin/iptables"
 
+/**
+ * Name and full path of IPTABLES binary.
+ */
+#define SBIN_IP "/sbin/ip"
+
+/**
+ * Port for DNS traffic.
+ */
+#define DNS_PORT "53"
+
+/**
+ * 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...).
+ */
+#define DNS_MARK "136708149"
+
+/**
+ * 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.
+ */
+#define DNS_TABLE "53"
+
+
+/**
+ * Run the given command and wait for it to complete.
+ * 
+ * @param file name of the binary to run
+ * @param cmd command line arguments (as given to 'execv')
+ * @return 0 on success, 1 on any error
+ */
 static int
-fork_and_exec (char *file, char *cmd[])
+fork_and_exec (const char *file, 
+              char *const cmd[])
 {
-  pid_t pid = fork ();
+  int status;
+  pid_t pid;
+  pid_t ret;
 
-  if (pid < 0)
+  pid = fork ();
+  if (-1 == pid)
   {
-    fprintf (stderr, "could not fork: %s\n", strerror (errno));
-    return GNUNET_SYSERR;
+    fprintf (stderr, 
+            "fork failed: %s\n", 
+            strerror (errno));
+    return 1;
   }
-
-  int st = 0;
-
-  if (pid == 0)
+  if (0 == pid)
   {
-    execv (file, cmd);
+    /* we are the child process */
+    (void) execv (file, cmd);
+    /* can only get here on error */
+    fprintf (stderr, 
+            "exec `%s' failed: %s\n", 
+            file,
+            strerror (errno));
+    _exit (1);
   }
-  else
+  /* keep running waitpid as long as the only error we get is 'EINTR' */
+  while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
+         (errno == EINTR) ); 
+  if (-1 == ret)
   {
-    waitpid (pid, &st, 0);
+    fprintf (stderr, 
+            "waitpid failed: %s\n", 
+            strerror (errno));
+    return 1;
   }
-  return WIFEXITED (st) && (WEXITSTATUS (st) == 0);
+  if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
+    return 1;
+  /* child process completed and returned success, we're happy */
+  return 0;
 }
 
+
+/**
+ * Main function of "gnunet-helper-hijack-dns".  
+ * Use "-d" as the first argument to remove the firewall rules.
+ * The other arguments are the DNS source port to NOT affect
+ * by the rules, followed by the name of the virtual interface
+ * to redirect all of the remaining DNS traffic to.
+ *
+ * @param argc number of arguments
+ * @param argv ["-d"] PORT VTUN
+ * @return 0 on success, otherwise code indicating type of error:
+ *         1 wrong number of arguments
+ *         2 invalid port number
+ *         3 iptables not executable
+ *         4 ip not executable
+ *         8 failed to change routing table, cleanup successfull
+ *         16-31 failed to undo some changes to routing table
+ *         31-47 failed to fully change routing table and then might have 
failed to undo everything
+ */
 int
-main (int argc, char **argv)
+main (int argc, char *const*argv)
 {
-  int delete = 0;
-  int port = 0;
+  int delete;
+  unsigned int port;
   char *virt_dns;
+  char localport[6];
+  int r;
 
+  /* check command-line arguments */
   if (argc < 3)
-    return GNUNET_SYSERR;
-
-  if (strncmp (argv[1], "-d", 2) == 0)
   {
-    if (argc < 3)
-      return GNUNET_SYSERR;
+    fprintf (stderr, 
+            "Syntax: gnunet-helper-hijack-dns [-d] PORT INTERFACENAME\n");
+    return 1;
+  }
+  if (0 == strcmp (argv[1], "-d"))
     delete = 1;
-    port = atoi (argv[2]);
-    virt_dns = argv[3];
-  }
   else
+    delete = 0;
+  if (argc != 3 + delete)
   {
-    port = atoi (argv[1]);
-    virt_dns = argv[2];
+    fprintf (stderr, 
+            "Syntax: gnunet-helper-hijack-dns [-d] PORT INTERFACENAME\n");
+    return 1;
+  }  
+  port = atoi (argv[1 + delete]);
+  virt_dns = argv[2 + delete];
+  if ( (port == 0) || (port >= 65536) )
+  {
+    fprintf (stderr, 
+            "Port `%u' is invalid\n",
+            port);
+    return 2;
   }
-
-  if (port == 0)
-    return GNUNET_SYSERR;
-
-  struct stat s;
-
-  if (stat ("/sbin/iptables", &s) < 0)
+  /* verify that the binaries were care about are executable */
+  if (0 != access (SBIN_IPTABLES, X_OK))
   {
-    fprintf (stderr, "stat on /sbin/iptables failed: %s\n", strerror (errno));
-    return GNUNET_SYSERR;
+    fprintf (stderr, 
+            "`%s' is not executable: %s\n", 
+            SBIN_IPTABLES,
+            strerror (errno));
+    return 3;
   }
-  if (stat ("/sbin/ip", &s) < 0)
+  if (0 != access (SBIN_IP, X_OK))
   {
-    fprintf (stderr, "stat on /sbin/ip failed: %s\n", strerror (errno));
-    return GNUNET_SYSERR;
+    fprintf (stderr, 
+            "`%s' is not executable: %s\n", 
+            SBIN_IP,
+            strerror (errno));
+    return 4;
   }
 
-  char localport[7];
+  /* print port number to string for command-line use*/
+  (void) snprintf (localport,
+                  sizeof (localport), 
+                  "%u", 
+                  port);
 
-  snprintf (localport, 7, "%d", port);
-
-  int r;
-
-  if (delete)
+  /* update routing tables -- this is why we are SUID! */
+  if (! delete)
   {
-e4:
-    r = fork_and_exec ("/sbin/ip", (char *[])
-                       {
-                       "ip", "route", "del", "default", "via", virt_dns,
-                       "table", "2", NULL});
-e3:
-    r = fork_and_exec ("/sbin/ip", (char *[])
-                       {
-                       "ip", "rule", "del", "fwmark", "3", "table", "2", 
NULL});
-e2:
-    r = fork_and_exec ("/sbin/iptables", (char *[])
-                       {
-                       "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
-                       "--dport", "53", "-j", "MARK", "--set-mark", "3", 
NULL});
-e1:
-    r = fork_and_exec ("/sbin/iptables", (char *[])
-                       {
-                       "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
-                       "--sport", localport, "--dport", "53", "-j", "ACCEPT",
-                       NULL});
-    if (!delete)
-      r = 0;
+    /* Forward everything from the given local port (with destination
+       to port 53, and only for UDP) without hijacking */
+    {
+      char *const mangle_args[] = 
+       {
+         "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
+         "udp", "--sport", localport, "--dport", DNS_PORT, "-j",
+         "ACCEPT", NULL
+       };
+      if (0 != fork_and_exec (SBIN_IPTABLES, mangle_args))
+       goto cleanup_mangle_1;
+    }    
+    /* Mark all of the other DNS traffic using our mark DNS_MARK */
+    {
+      char *const mark_args[] =
+       {
+         "iptables", "-t", "mangle", "-I", "OUTPUT", DNS_TABLE, "-p",
+         "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
+         NULL
+       };
+      if (0 != fork_and_exec (SBIN_IPTABLES, mark_args))
+       goto cleanup_mark_2;
+    }
+    /* Forward all marked DNS traffic to our DNS_TABLE */
+    {
+      char *const forward_args[] =
+       {
+         "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
+       };
+      if (0 != fork_and_exec (SBIN_IP, forward_args))
+       goto cleanup_forward_3;
+    }
+    /* Finally, add rule in our forwarding table to pass to our virtual 
interface */
+    {
+      char *const route_args[] =
+       {
+         "ip", "route", "add", "default", "via", virt_dns,
+         "table", DNS_TABLE, NULL
+       };
+      if (0 != fork_and_exec (SBIN_IP, route_args))
+       goto cleanup_route_4;
+    }
   }
   else
   {
-    r = fork_and_exec ("/sbin/iptables", (char *[])
-                       {
-                       "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
-                       "udp", "--sport", localport, "--dport", "53", "-j",
-                       "ACCEPT", NULL});
-    if (!r)
-      goto e1;
-    r = fork_and_exec ("/sbin/iptables", (char *[])
-                       {
-                       "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
-                       "udp", "--dport", "53", "-j", "MARK", "--set-mark", "3",
-                       NULL});
-    if (!r)
-      goto e2;
-    r = fork_and_exec ("/sbin/ip", (char *[])
-                       {
-                       "ip", "rule", "add", "fwmark", "3", "table", "2", 
NULL});
-    if (!r)
-      goto e3;
-    r = fork_and_exec ("/sbin/ip", (char *[])
-                       {
-                       "ip", "route", "add", "default", "via", virt_dns,
-                       "table", "2", NULL});
-    if (!r)
-      goto e4;
-  }
-  if (r)
-    return GNUNET_YES;
-  return GNUNET_SYSERR;
+    r = 0;
+    /* delete or clean-up-on-error case */
+cleanup_route_4:
+    {
+      char *const route_clean_args[] =                          
+       {
+         "ip", "route", "del", "default", "via", virt_dns,
+         "table", DNS_TABLE, NULL
+       };
+      if (0 != fork_and_exec (SBIN_IP, route_clean_args))
+       r += 1;
+    }
+cleanup_forward_3:
+    {
+      char *const forward_clean_args[] =
+       {
+         "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
+       };
+      if (0 != fork_and_exec (SBIN_IP, forward_clean_args))
+       r += 2; 
+    }
+cleanup_mark_2:
+    {
+      char *const mark_clean_args[] =
+       {
+         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
+         "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
+       };
+      if (0 != fork_and_exec (SBIN_IPTABLES, mark_clean_args))
+       r += 4;
+    }  
+cleanup_mangle_1:
+    {
+      char *const mangle_clean_args[] =
+       {
+         "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
+         "--sport", localport, "--dport", DNS_PORT, "-j", "ACCEPT",
+         NULL
+       };
+      if (0 != fork_and_exec (SBIN_IPTABLES, mangle_clean_args))
+       r += 8;
+    }
+    if (r != 0)
+    {
+      if (delete)
+       return 16 + r; /* failed to delete */
+      return 32 + r; /* first failed to install, then also failed to clean up! 
*/
+    }
+    if (! delete)
+    {
+      /* got here via goto to clean up handler, failed to install, succeeded 
with clean up */
+      return 8;
+    }
+  } 
+  /* success ! */
+  return 0;
 }
+
+/* end of gnunet-helper-hijack-dns.c */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]