Blob Blame History Raw
diff -up dnsmasq-2.65/src/dnsmasq.c.CVE-2013-0198 dnsmasq-2.65/src/dnsmasq.c
--- dnsmasq-2.65/src/dnsmasq.c.CVE-2013-0198	2012-12-14 12:48:26.000000000 +0100
+++ dnsmasq-2.65/src/dnsmasq.c	2013-01-22 16:32:30.387640907 +0100
@@ -1384,7 +1384,7 @@ static void check_dns_listeners(fd_set *
 
       if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set))
 	{
-	  int confd;
+	  int confd, client_ok = 1;
 	  struct irec *iface = NULL;
 	  pid_t p;
 	  union mysockaddr tcp_addr;
@@ -1395,25 +1395,49 @@ static void check_dns_listeners(fd_set *
 	  if (confd == -1 ||
 	      getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
 	    continue;
-	  
-	  if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
+
+	   if (option_bool(OPT_NOWILD))
 	    iface = listener->iface; /* May be NULL */
-	  else
-	    {
-	      /* Check for allowed interfaces when binding the wildcard address:
-		 we do this by looking for an interface with the same address as 
-		 the local address of the TCP connection, then looking to see if that's
-		 an allowed interface. As a side effect, we get the netmask of the
-		 interface too, for localisation. */
-	      
-	      /* interface may be new since startup */
-	      if (enumerate_interfaces())
-		for (iface = daemon->interfaces; iface; iface = iface->next)
-		  if (sockaddr_isequal(&iface->addr, &tcp_addr))
-		    break;
-	    }
-	  
-	  if (!iface && !(option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)))
+	   else 
+	     {
+	       int if_index;
+
+	       /* In full wildcard mode, need to refresh interface list.
+		  This happens automagically in CLEVERBIND */
+	        if (!option_bool(OPT_CLEVERBIND))
+		  enumerate_interfaces();
+
+		/* if we can find the arrival interface, check it's one that's allowed */
+		if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0)
+		 {
+		   for (iface = daemon->interfaces; iface; iface = iface->next)
+		     if (iface->index == if_index)
+		       break;
+		   
+		   if (!iface)
+		     client_ok = 0;
+		 }
+	       
+	       if (option_bool(OPT_CLEVERBIND))
+		 iface = listener->iface; /* May be NULL */
+	       else
+		 {
+		   /* Check for allowed interfaces when binding the wildcard address:
+		      we do this by looking for an interface with the same address as 
+		      the local address of the TCP connection, then looking to see if that's
+		      an allowed interface. As a side effect, we get the netmask of the
+		      interface too, for localisation. */
+		   
+		   for (iface = daemon->interfaces; iface; iface = iface->next)
+		     if (sockaddr_isequal(&iface->addr, &tcp_addr))
+		       break;
+		   
+		   if (!iface)
+		     client_ok = 0;
+		 }
+	     }
+	  	  
+	  if (!client_ok)
 	    {
 	      shutdown(confd, SHUT_RDWR);
 	      close(confd);
diff -up dnsmasq-2.65/src/dnsmasq.h.CVE-2013-0198 dnsmasq-2.65/src/dnsmasq.h
--- dnsmasq-2.65/src/dnsmasq.h.CVE-2013-0198	2012-12-14 12:48:26.000000000 +0100
+++ dnsmasq-2.65/src/dnsmasq.h	2013-01-22 16:32:30.387640907 +0100
@@ -412,7 +412,7 @@ struct server {
 struct irec {
   union mysockaddr addr;
   struct in_addr netmask; /* only valid for IPv4 */
-  int tftp_ok, dhcp_ok, mtu, done, dad;
+  int tftp_ok, dhcp_ok, mtu, done, dad, index;
   char *name;
   struct irec *next;
 };
@@ -955,6 +955,7 @@ void create_bound_listeners(int die);
 int is_dad_listeners(void);
 int iface_check(int family, struct all_addr *addr, char *name);
 int fix_fd(int fd);
+int tcp_interface(int fd, int af);
 struct in_addr get_ifaddr(char *intr);
 #ifdef HAVE_IPV6
 int set_ipv6pktinfo(int fd);
diff -up dnsmasq-2.65/src/network.c.CVE-2013-0198 dnsmasq-2.65/src/network.c
--- dnsmasq-2.65/src/network.c.CVE-2013-0198	2012-12-14 12:48:26.000000000 +0100
+++ dnsmasq-2.65/src/network.c	2013-01-22 17:33:00.349128334 +0100
@@ -239,6 +239,7 @@ static int iface_allowed(struct irec **i
       iface->mtu = mtu;
       iface->dad = dad;
       iface->done = 0;
+      iface->index = if_index;
       if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
 	{
 	  strcpy(iface->name, ifr.ifr_name);
@@ -420,6 +421,77 @@ int set_ipv6pktinfo(int fd)
   return 0;
 }
 #endif
+
+
+/* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */
+int tcp_interface(int fd, int af)
+{ 
+  int if_index = 0;
+
+#ifdef HAVE_LINUX_NETWORK
+  int opt = 1;
+  struct cmsghdr *cmptr;
+  struct msghdr msg;
+  
+  /* use mshdr do that the CMSDG_* macros are available */
+  msg.msg_control = daemon->packet;
+  msg.msg_controllen = daemon->packet_buff_sz;
+  
+  /* we overwrote the buffer... */
+  daemon->srv_save = NULL;
+  
+  if (af == AF_INET)
+    {
+      if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
+	  getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
+	for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+	  if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
+            {
+              union {
+                unsigned char *c;
+                struct in_pktinfo *p;
+              } p;
+	      
+	      p.c = CMSG_DATA(cmptr);
+	      if_index = p.p->ipi_ifindex;
+	    }
+    }
+#ifdef HAVE_IPV6
+  else
+    {
+      /* Only the RFC-2292 API has the ability to find the interface for TCP connections,
+	 it was removed in RFC-3542 !!!! 
+
+	 Fortunately, Linux kept the 2292 ABI when it moved to 3542. The following code always
+	 uses the old ABI, and should work with pre- and post-3542 kernel headers */
+
+#ifdef IPV6_2292PKTOPTIONS   
+#  define PKTOPTIONS IPV6_2292PKTOPTIONS
+#else
+#  define PKTOPTIONS IPV6_PKTOPTIONS
+#endif
+
+      if (set_ipv6pktinfo(fd) &&
+	  getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
+	{
+          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+            if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
+              {
+                union {
+                  unsigned char *c;
+                  struct in6_pktinfo *p;
+                } p;
+                p.c = CMSG_DATA(cmptr);
+		
+		if_index = p.p->ipi6_ifindex;
+              }
+	}
+    }
+#endif /* IPV6 */
+#endif /* Linux */
+ 
+  return if_index;
+}
       
 static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, int dienow)
 {