-updating DNS code to use GID match instead of source port match, introduce source...
authorChristian Grothoff <christian@grothoff.org>
Tue, 7 Feb 2012 16:24:08 +0000 (16:24 +0000)
committerChristian Grothoff <christian@grothoff.org>
Tue, 7 Feb 2012 16:24:08 +0000 (16:24 +0000)
src/dns/gnunet-helper-dns.c
src/dns/gnunet-service-dns.c

index 5e603ceb8de05c1f13288d3e56c7f7413f0fbe3c..2b8ce2cc94f56ca09f18ebaa4aafd6d366990fe7 100644 (file)
@@ -35,7 +35,9 @@
  * 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).
+ * 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
@@ -659,7 +661,6 @@ PROCESS_BUFFER:
  *             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: PORT to not hijack ("55533")
  * @return 0 on success, otherwise code indicating type of error:
  *         1 wrong number of arguments
  *         2 invalid arguments (i.e. port number / prefix length wrong)
@@ -678,13 +679,12 @@ PROCESS_BUFFER:
 int
 main (int argc, char *const*argv)
 {
-  unsigned int port;
-  char localport[6];
   int r;
   char dev[IFNAMSIZ];
+  char mygid[32];
   int fd_tun;
 
-  if (7 != argc)
+  if (6 != argc)
   {
     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
     return 1;
@@ -714,20 +714,8 @@ main (int argc, char *const*argv)
     return 4;
   }
 
-  /* validate port number */
-  port = atoi (argv[6]);
-  if ( (port == 0) || (port >= 65536) )
-  {
-    fprintf (stderr, 
-            "Port `%u' is invalid\n",
-            port);
-    return 2;
-  }
-  /* print port number to string for command-line use*/
-  (void) snprintf (localport,
-                  sizeof (localport), 
-                  "%u", 
-                  port);
+  /* setup 'mygid' string */
+  snprintf (mygid, sizeof (mygid), "%d", (int) getegid());
 
   /* do not die on SIGPIPE */
   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
@@ -827,14 +815,15 @@ main (int argc, char *const*argv)
   }
   
   /* update routing tables -- next part why we need SUID! */
-  /* Forward everything from the given local port (with destination
-     to port 53, and only for UDP) without hijacking */
+  /* Forward everything from our EGID (which should only be held
+     by the 'gnunet-service-dns') and with destination
+     to port 53 on UDP, without hijacking */
   r = 8; /* failed to fully setup routing table */
   {
     char *const mangle_args[] = 
       {
-       "iptables", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
-       "udp", "--sport", localport, "--dport", DNS_PORT, "-j",
+       "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
+       "udp", "!", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
        "ACCEPT", NULL
       };
     if (0 != fork_and_exec (sbin_iptables, mangle_args))
@@ -948,8 +937,8 @@ main (int argc, char *const*argv)
   {
     char *const mangle_clean_args[] =
       {
-       "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
-       "--sport", localport, "--dport", DNS_PORT, "-j", "ACCEPT",
+       "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
+       "!", "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
        NULL
       };
     if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
index 74d3d25ac837a92312b87c3bd09aa9ff20b8ff6b..1c772d712a132edd44cd820f355e3115c2df37cd 100644 (file)
@@ -164,7 +164,6 @@ struct RequestRecord
 };
 
 
-
 /**
  * State we keep for each DNS tunnel that terminates at this node.
  */
@@ -235,12 +234,6 @@ static GNUNET_SCHEDULER_TaskIdentifier read4_task;
  */
 static GNUNET_SCHEDULER_TaskIdentifier read6_task;
 
-/**
- * The port bound to the socket dnsout (and/or dnsout6).  We always (try) to bind
- * both sockets to the same port.
- */
-static uint16_t dnsoutport;
-
 /**
  * The configuration to use
  */
@@ -259,7 +252,7 @@ static struct GNUNET_HELPER_Handle *hijacker;
 /**
  * Command-line arguments we are giving to the hijacker process.
  */
-static char *helper_argv[8];
+static char *helper_argv[7];
 
 /**
  * Head of DLL of clients we consult.
@@ -303,6 +296,11 @@ static char *dns_exit;
  */
 static struct GNUNET_MESH_Handle *mesh;
 
+/**
+ * Number of active DNS requests.
+ */
+static unsigned int dns_active;
+
 
 /**
  * We're done processing a DNS request, free associated memory.
@@ -335,7 +333,7 @@ cleanup_task (void *cls GNUNET_UNUSED,
 
   GNUNET_HELPER_stop (hijacker);
   hijacker = NULL;
-  for (i=0;i<8;i++)
+  for (i=0;i<7;i++)
     GNUNET_free_non_null (helper_argv[i]);
   if (NULL != dnsout4)
   {
@@ -560,6 +558,13 @@ send_request_to_client (struct RequestRecord *rr,
 }
 
 
+/**
+ * Try to change the source ports we are bound to.
+ */
+static void
+change_source_ports ();
+
+
 /**
  * A client has completed its processing for this
  * request.  Move on.
@@ -621,6 +626,7 @@ next_phase (struct RequestRecord *rr)
     return;
   case RP_QUERY:
     rr->phase = RP_INTERNET_DNS;
+    dns_active++;
     switch (rr->dst_addr.ss_family)
     {
     case AF_INET:
@@ -651,6 +657,9 @@ next_phase (struct RequestRecord *rr)
                                  salen);
     return;
   case RP_INTERNET_DNS:
+    dns_active--;
+    if (0 == dns_active)
+      change_source_ports ();
     rr->phase = RP_MODIFY;
     for (cr = clients_head; NULL != cr; cr = cr->next)
     {
@@ -904,7 +913,6 @@ static int
 open_port4 ()
 {
   struct sockaddr_in addr;
-  socklen_t addrlen;
 
   dnsout4 = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0);
   if (dnsout4 == NULL)
@@ -925,25 +933,6 @@ open_port4 ()
     dnsout4 = NULL;
     return GNUNET_SYSERR;
   }
-
-  /* Read the port we bound to */
-  addrlen = sizeof (struct sockaddr_in);
-  if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout4), 
-                       (struct sockaddr *) &addr,
-                       &addrlen))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
-               _("Could not determine port I got: %s\n"),
-               STRERROR (errno));
-    GNUNET_NETWORK_socket_close (dnsout4);
-    dnsout4 = NULL;
-    return GNUNET_SYSERR;
-  }
-  dnsoutport = htons (addr.sin_port);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
-             _("GNUnet DNS will exit on source port %u\n"),
-             (unsigned int) dnsoutport);
   read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 
                                              dnsout4,
                                              &read_response, dnsout4);
@@ -961,7 +950,6 @@ static int
 open_port6 ()
 {
   struct sockaddr_in6 addr;
-  socklen_t addrlen;
 
   dnsout6 = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_DGRAM, 0);
   if (dnsout6 == NULL)
@@ -973,7 +961,6 @@ open_port6 ()
   }
   memset (&addr, 0, sizeof (struct sockaddr_in6));
   addr.sin6_family = AF_INET6;
-  addr.sin6_port = htons (dnsoutport);
   int err = GNUNET_NETWORK_socket_bind (dnsout6,
                                         (struct sockaddr *) &addr,
                                         sizeof (struct sockaddr_in6));
@@ -981,29 +968,12 @@ open_port6 ()
   if (err != GNUNET_OK)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               _("Could not bind to port %u: %s\n"),
-               (unsigned int) dnsoutport,
+               _("Could not bind: %s\n"),
                STRERROR (errno));
     GNUNET_NETWORK_socket_close (dnsout6);
     dnsout6 = NULL;
     return GNUNET_SYSERR;
   }
-  if (0 == dnsoutport)
-  {
-    addrlen = sizeof (struct sockaddr_in6);
-    if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout6), 
-                         (struct sockaddr *) &addr,
-                         &addrlen))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
-                 _("Could not determine port I got: %s\n"),
-                 STRERROR (errno));
-      GNUNET_NETWORK_socket_close (dnsout6);
-      dnsout6 = NULL;
-      return GNUNET_SYSERR;
-    }
-  }
-  dnsoutport = htons (addr.sin6_port);
   read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
                                              dnsout6,
                                              &read_response, dnsout6);
@@ -1011,6 +981,54 @@ open_port6 ()
 }
 
 
+/**
+ * Try to change the source ports we are bound to.
+ */
+static void
+change_source_ports ()
+{
+  struct GNUNET_NETWORK_Handle *old4;
+  struct GNUNET_NETWORK_Handle *old6;
+
+  if (GNUNET_SCHEDULER_NO_TASK != read4_task)
+  {
+    GNUNET_SCHEDULER_cancel (read4_task);
+    read4_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  if (GNUNET_SCHEDULER_NO_TASK != read6_task)
+  {
+    GNUNET_SCHEDULER_cancel (read6_task);
+    read6_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  old4 = dnsout4;
+  if (GNUNET_OK != open_port4 ())
+  {
+    dnsout4 = old4;
+    read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                               dnsout4,
+                                               &read_response, dnsout4);
+  }
+  else
+  {
+    if (NULL != old4)
+      GNUNET_NETWORK_socket_close (old4);
+  }
+  old6 = dnsout6;
+  if (GNUNET_OK != open_port6 ())
+  {
+    dnsout6 = old6;
+    read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                               dnsout6,
+                                               &read_response, dnsout6);
+  }
+  else
+  {
+    if (NULL != old6)
+      GNUNET_NETWORK_socket_close (old6);
+  }
+}
+
+
 /**
  * We got a new client.  Make sure all new DNS requests pass by its desk.
  *
@@ -1216,6 +1234,12 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
 
   /* clean up from previous request */
   GNUNET_free_non_null (rr->payload);
+  if (RP_INTERNET_DNS == rr->phase)
+  {
+    dns_active--;
+    if (0 == dns_active)
+      change_source_ports ();
+  }
   rr->payload = NULL;
   GNUNET_array_grow (rr->client_wait_list,
                     rr->client_wait_list_length,
@@ -1435,7 +1459,6 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
     {&handle_client_response, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE, 0},
     {NULL, NULL, 0, 0}
   };
-  char port_s[6];
   char *ifc_name;
   char *ipv4addr;
   char *ipv4mask;
@@ -1514,12 +1537,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
     return;
   }
   helper_argv[5] = ipv4mask;
-  GNUNET_snprintf (port_s, 
-                  sizeof (port_s), 
-                  "%u", 
-                  (unsigned int) dnsoutport);
-  helper_argv[6] = GNUNET_strdup (port_s);
-  helper_argv[7] = NULL;
+  helper_argv[6] = NULL;
 
   if (NULL != dns_exit)
   {