- fix
[oweals/gnunet.git] / src / transport / gnunet-helper-transport-wlan.c
index 3b5e0b1aba5903c3cbca13f870f39440319c3251..3ad4d64c424dbbf6433b4015d971e47328037e0e 100644 (file)
  * interface.  It will force traffic to be in 'ad-hoc' mode, use the
  * proper MAC address of the WLAN interface and use a GNUnet-specific
  * SSID (and a GNUnet-specific SNAP header).  It only takes a single
- * argument, which is the name of the interface to use.  Since it uses
- * RAW sockets, it must be installed SUID or run as 'root'.  In order
- * to keep the security risk of the resulting SUID binary minimal, the
- * program ONLY opens the RAW socket with root privileges, then drops
- * them and only then starts to process command line arguments.  The
- * code also does not link against any shared libraries (except libc)
- * and is strictly minimal (except for checking for errors).  The
- * following list of people have reviewed this code and considered it
- * safe since the last modification (if you reviewed it, please have
- * your name added to the list):
+ * argument, which is the name of the WLAN interface to use.  The
+ * program detects if the interface is not a WLAN interface and exits
+ * with an error in that case.
  *
- * - Christian Grothoff (Mar 16th 2012)
+ * Once initialized, the program will first send a 'struct
+ * GNUNET_TRANSPORT_WLAN_HelperControlMessage' to 'stdout'.  That
+ * message contains the MAC address of the WLAN interface.  It will
+ * then read messages from the WLAN interface and send them together
+ * with performance information as 'struct
+ * GNUNET_TRANSPORT_WLAN_RadiotapReceiveMessage' messages to 'stdout'.
+ * Furthermore, it will read a stream of messages from 'stdin' that
+ * have the format from 'struct
+ * GNUNET_TRANSPORT_WLAN_RadiotapSendMessage'.  Those messages will
+ * then be sent via the WLAN interface; however, the sender MAC
+ * address will be forced to be the correct address from our WLAN
+ * card.  If 'stdin' closes, receiving from the WLAN interface will
+ * continue.  If 'stdout' causes a SIGPIPE, the process dies from the
+ * signal.  Errors cause an error message to be reported to 'stderr',
+ * in most cases the process also exits (with status code '1').  The
+ * program never terminates normally; it is safe to kill the
+ * process with SIGTERM or SIGKILL at any time.
+ *
+ * Since it uses RAW sockets, the binary must be installed SUID or run
+ * as 'root'.  In order to keep the security risk of the resulting
+ * SUID binary minimal, the program ONLY opens the RAW socket with
+ * root privileges, then drops them and only then starts to process
+ * command line arguments.  The code also does not link against any
+ * shared libraries (except libc) and is strictly minimal (except for
+ * checking for errors).  The following list of people have reviewed
+ * this code and considered it safe since the last modification (if
+ * you reviewed it, please have your name added to the list):
+ *
+ * - Christian Grothoff (Apr 3rd 2012)
  */
 
 /*-
 /*
  * parts taken from aircrack-ng, parts changend.
  */
-
-#define _GNU_SOURCE
+#include "gnunet_config.h"
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include "gnunet_protocols.h"
 #include "plugin_transport_wlan.h"
 
+/**
+ * Packet format type for the messages we receive from 
+ * the kernel.  This is for Ethernet 10Mbps format (no
+ * performance information included).
+ */
+#define ARPHRD_ETHER        1 
+
+
 /**
  * Packet format type for the messages we receive from 
  * the kernel.  This is for plain messages (with no
  */
 #define ARPHRD_IEEE80211        801
 
+
 /**
  * Packet format type for the messages we receive from 
  * the kernel.  This is for the PRISM format.
@@ -1568,6 +1597,15 @@ linux_read (struct HardwareInfos *dev,
   case ARPHRD_IEEE80211:
     n = 0; /* no header */
     break;
+  case ARPHRD_ETHER:
+    {
+      if (sizeof (struct GNUNET_TRANSPORT_WLAN_Ieee8023Frame) > caplen)
+       return 0; /* invalid */
+      memcpy (&buf[sizeof (struct GNUNET_TRANSPORT_WLAN_Ieee80211Frame)],
+             tmpbuf + sizeof (struct GNUNET_TRANSPORT_WLAN_Ieee8023Frame),
+             caplen - sizeof (struct GNUNET_TRANSPORT_WLAN_Ieee8023Frame) - 4 /* 4 byte FCS */);
+      return caplen - sizeof (struct GNUNET_TRANSPORT_WLAN_Ieee8023Frame) - 4;
+    }      
   default:
     errno = ENOTSUP; /* unsupported format */
     return -1;
@@ -1631,6 +1669,16 @@ open_device_raw (struct HardwareInfos *dev)
              IFNAMSIZ, dev->iface, strerror (errno));
     return 1;
   }
+  if (((ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211) &&
+       (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) &&
+       (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_PRISM) &&
+       (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_FULL)) )
+  {
+    fprintf (stderr, "Error: interface `%.*s' is not using a supported hardware address family (got %d)\n",
+             IFNAMSIZ, dev->iface,
+            ifr.ifr_hwaddr.sa_family);
+    return 1;
+  }
 
   /* lookup iw mode */
   memset (&wrq, 0, sizeof (struct iwreq));
@@ -1642,13 +1690,12 @@ open_device_raw (struct HardwareInfos *dev)
     wrq.u.mode = IW_MODE_MONITOR;
   }
 
-  if (((ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211) &&
-       (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_PRISM) &&
-       (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_FULL)) ||
-      (wrq.u.mode != IW_MODE_MONITOR))
+  if ( (wrq.u.mode != IW_MODE_MONITOR) &&
+       (wrq.u.mode != IW_MODE_ADHOC) ) 
   {
-    fprintf (stderr, "Error: interface `%.*s' is not in monitor mode\n",
-             IFNAMSIZ, dev->iface);
+    fprintf (stderr, "Error: interface `%.*s' is not in monitor or ad-hoc mode (got %d)\n",
+             IFNAMSIZ, dev->iface,
+            wrq.u.mode);
     return 1;
   }
 
@@ -1684,7 +1731,8 @@ open_device_raw (struct HardwareInfos *dev)
 
   memcpy (&dev->pl_mac, ifr.ifr_hwaddr.sa_data, MAC_ADDR_SIZE);
   dev->arptype_in = ifr.ifr_hwaddr.sa_family;
-  if ((ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211) &&
+  if ((ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) &&
+      (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211) &&
       (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_PRISM) &&
       (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_FULL))
   {
@@ -1750,6 +1798,12 @@ static int
 mac_test (const struct GNUNET_TRANSPORT_WLAN_Ieee80211Frame *taIeeeHeader,
           const struct HardwareInfos *dev)
 {
+  static struct GNUNET_TRANSPORT_WLAN_MacAddress all_zeros;
+
+  if ( (0 == memcmp (&taIeeeHeader->addr3, &all_zeros, MAC_ADDR_SIZE)) ||
+       (0 == memcmp (&taIeeeHeader->addr1, &all_zeros, MAC_ADDR_SIZE)) )
+    return 0; /* some drivers set no Macs, then assume it is all for us! */
+
   if (0 != memcmp (&taIeeeHeader->addr3, &mac_bssid_gnunet, MAC_ADDR_SIZE))
     return 1; /* not a GNUnet ad-hoc package */
   if ( (0 == memcmp (&taIeeeHeader->addr1, &dev->pl_mac, MAC_ADDR_SIZE)) ||
@@ -1791,6 +1845,7 @@ stdin_send_hw (void *cls, const struct GNUNET_MessageHeader *hdr)
   struct GNUNET_TRANSPORT_WLAN_Ieee80211Frame *wlanheader;
   size_t sendsize;
   struct RadiotapTransmissionHeader rtheader;
+  struct GNUNET_TRANSPORT_WLAN_Ieee8023Frame etheader;
 
   sendsize = ntohs (hdr->size);
   if ( (sendsize <
@@ -1803,25 +1858,45 @@ stdin_send_hw (void *cls, const struct GNUNET_MessageHeader *hdr)
   sendsize -= (sizeof (struct GNUNET_TRANSPORT_WLAN_RadiotapSendMessage) - sizeof (struct GNUNET_TRANSPORT_WLAN_Ieee80211Frame));
   if (MAXLINE < sendsize)
   {
-    fprintf (stderr, "Function stdin_send_hw: Packet too big for buffer\n");
+    fprintf (stderr, "Packet too big for buffer\n");
     exit (1);
   }
   header = (const struct GNUNET_TRANSPORT_WLAN_RadiotapSendMessage *) hdr;
-  rtheader.header.it_version = 0;
-  rtheader.header.it_pad = 0; 
-  rtheader.header.it_len = GNUNET_htole16 (sizeof (rtheader));
-  rtheader.header.it_present = GNUNET_htole16 (IEEE80211_RADIOTAP_OUR_TRANSMISSION_HEADER_MASK);
-  rtheader.rate = header->rate; 
-  rtheader.pad1 = 0;
-  rtheader.txflags = GNUNET_htole16 (IEEE80211_RADIOTAP_F_TX_NOACK | IEEE80211_RADIOTAP_F_TX_NOSEQ);
-  memcpy (write_pout.buf, &rtheader, sizeof (rtheader));
-  memcpy (&write_pout.buf[sizeof (rtheader)], &header->frame, sendsize);
-  wlanheader = (struct GNUNET_TRANSPORT_WLAN_Ieee80211Frame *) &write_pout.buf[sizeof (rtheader)];
-
-  /* payload contains MAC address, but we don't trust it, so we'll
-   * overwrite it with OUR MAC address to prevent mischief */
-  mac_set (wlanheader, dev);
-  write_pout.size = sendsize + sizeof (rtheader);
+  switch (dev->arptype_in)
+  {
+  case ARPHRD_IEEE80211_PRISM:
+  case ARPHRD_IEEE80211_FULL:
+  case ARPHRD_IEEE80211:
+    rtheader.header.it_version = 0;
+    rtheader.header.it_pad = 0; 
+    rtheader.header.it_len = GNUNET_htole16 (sizeof (rtheader));
+    rtheader.header.it_present = GNUNET_htole16 (IEEE80211_RADIOTAP_OUR_TRANSMISSION_HEADER_MASK);
+    rtheader.rate = header->rate; 
+    rtheader.pad1 = 0;
+    rtheader.txflags = GNUNET_htole16 (IEEE80211_RADIOTAP_F_TX_NOACK | IEEE80211_RADIOTAP_F_TX_NOSEQ);
+    memcpy (write_pout.buf, &rtheader, sizeof (rtheader));
+    memcpy (&write_pout.buf[sizeof (rtheader)], &header->frame, sendsize);
+    wlanheader = (struct GNUNET_TRANSPORT_WLAN_Ieee80211Frame *) &write_pout.buf[sizeof (rtheader)];
+
+    /* payload contains MAC address, but we don't trust it, so we'll
+     * overwrite it with OUR MAC address to prevent mischief */
+    mac_set (wlanheader, dev);
+    write_pout.size = sendsize + sizeof (rtheader);
+    break;
+  case ARPHRD_ETHER:
+    etheader.dst = header->frame.addr1;
+    /* etheader.src = header->frame.addr2; --- untrusted input */
+    etheader.src = dev->pl_mac;
+    etheader.type = htons (ETH_P_IP);
+    memcpy (write_pout.buf, &etheader, sizeof (etheader));
+    memcpy (&write_pout.buf[sizeof (etheader)], &header[1], sendsize - sizeof (struct GNUNET_TRANSPORT_WLAN_Ieee80211Frame));
+    write_pout.size = sendsize - sizeof (struct GNUNET_TRANSPORT_WLAN_Ieee80211Frame) + sizeof (etheader);
+    break;
+  default:
+    fprintf (stderr,
+            "Unsupported ARPTYPE!\n");
+    break;
+  }
 }
 
 
@@ -1846,33 +1921,29 @@ main (int argc, char *argv[])
   int stdin_open;
   struct MessageStreamTokenizer *stdin_mst;
   int raw_eno;
+  uid_t uid;
 
-  memset (&dev, 0, sizeof (dev));
-  dev.fd_raw = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL));
-  raw_eno = errno; /* remember for later */
-
-  /* drop privs */
-  {
-    uid_t uid = getuid ();
+  /* assert privs so we can modify the firewall rules! */
+  uid = getuid ();
 #ifdef HAVE_SETRESUID
-    if (0 != setresuid (uid, uid, uid))
-    {
-      fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
-      if (-1 != dev.fd_raw)
-       (void) close (dev.fd_raw);
-      return 1;
-    }
+  if (0 != setresuid (uid, 0, 0))
+  {
+    fprintf (stderr, "Failed to setresuid to root: %s\n", strerror (errno));
+    return 254;
+  }
 #else
-    if (0 != (setuid (uid) | seteuid (uid)))
-    {
-      fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
-      if (-1 != dev.fd_raw)
-       (void) close (dev.fd_raw);
-      return 1;
-    }
+  if (0 != seteuid (0)) 
+  {
+    fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
+    return 254;
   }
 #endif
 
+  /* make use of SGID capabilities on POSIX */
+  memset (&dev, 0, sizeof (dev));
+  dev.fd_raw = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL));
+  raw_eno = errno; /* remember for later */
+
   /* now that we've dropped root rights, we can do error checking */
   if (2 != argc)
   {
@@ -1907,6 +1978,29 @@ main (int argc, char *argv[])
     return 1;
   }
 
+  /* drop privs */
+  {
+    uid_t uid = getuid ();
+#ifdef HAVE_SETRESUID
+    if (0 != setresuid (uid, uid, uid))
+    {
+      fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
+      if (-1 != dev.fd_raw)
+       (void) close (dev.fd_raw);
+      return 1;
+    }
+#else
+    if (0 != (setuid (uid) | seteuid (uid)))
+    {
+      fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
+      if (-1 != dev.fd_raw)
+       (void) close (dev.fd_raw);
+      return 1;
+    }
+#endif
+  }
+
+
   /* send MAC address of the WLAN interface to STDOUT first */
   {
     struct GNUNET_TRANSPORT_WLAN_HelperControlMessage macmsg;