ac771d
From 04ef96f428beebd07b457b51b5d2f26d3099f1a5 Mon Sep 17 00:00:00 2001
ac771d
From: Simon Kelley <simon@thekelleys.org.uk>
ac771d
Date: Thu, 12 Nov 2020 18:49:23 +0000
ac771d
Subject: [PATCH 2/4] Check destination of DNS UDP query replies.
ac771d
ac771d
At any time, dnsmasq will have a set of sockets open, bound to
ac771d
random ports, on which it sends queries to upstream nameservers.
ac771d
This patch fixes the existing problem that a reply for ANY in-flight
ac771d
query would be accepted via ANY open port, which increases the
ac771d
chances of an attacker flooding answers "in the blind" in an
ac771d
attempt to poison the DNS cache. CERT VU#434904 refers.
ac771d
---
ac771d
 src/forward.c | 37 ++++++++++++++++++++++++++++---------
ac771d
 1 file changed, 28 insertions(+), 9 deletions(-)
ac771d
ac771d
diff --git a/src/forward.c b/src/forward.c
ac771d
index cdd11d3..85eab27 100644
ac771d
--- a/src/forward.c
ac771d
+++ b/src/forward.c
ac771d
@@ -16,7 +16,7 @@
ac771d
 
ac771d
 #include "dnsmasq.h"
ac771d
 
ac771d
-static struct frec *lookup_frec(unsigned short id, void *hash);
ac771d
+static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash);
ac771d
 static struct frec *lookup_frec_by_sender(unsigned short id,
ac771d
 					  union mysockaddr *addr,
ac771d
 					  void *hash);
ac771d
@@ -780,7 +780,7 @@ void reply_query(int fd, int family, time_t now)
ac771d
   crc = questions_crc(header, n, daemon->namebuff);
ac771d
 #endif
ac771d
   
ac771d
-  if (!(forward = lookup_frec(ntohs(header->id), hash)))
ac771d
+  if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash)))
ac771d
     return;
ac771d
   
ac771d
   /* log_query gets called indirectly all over the place, so 
ac771d
@@ -2195,14 +2195,25 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
ac771d
 }
ac771d
 
ac771d
 /* crc is all-ones if not known. */
ac771d
-static struct frec *lookup_frec(unsigned short id, void *hash)
ac771d
+static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash)
ac771d
 {
ac771d
   struct frec *f;
ac771d
 
ac771d
   for(f = daemon->frec_list; f; f = f->next)
ac771d
     if (f->sentto && f->new_id == id && 
ac771d
 	(!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
ac771d
-      return f;
ac771d
+      {
ac771d
+	/* sent from random port */
ac771d
+	if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd)
ac771d
+	  return f;
ac771d
+
ac771d
+	if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd)
ac771d
+	  return f;
ac771d
+
ac771d
+	/* sent to upstream from bound socket. */
ac771d
+	if (f->sentto->sfd && f->sentto->sfd->fd == fd)
ac771d
+	  return f;
ac771d
+      }
ac771d
       
ac771d
   return NULL;
ac771d
 }
ac771d
@@ -2263,12 +2274,20 @@ void server_gone(struct server *server)
ac771d
 static unsigned short get_id(void)
ac771d
 {
ac771d
   unsigned short ret = 0;
ac771d
+  struct frec *f;
ac771d
   
ac771d
-  do 
ac771d
-    ret = rand16();
ac771d
-  while (lookup_frec(ret, NULL));
ac771d
-  
ac771d
-  return ret;
ac771d
+  while (1)
ac771d
+    {
ac771d
+      ret = rand16();
ac771d
+
ac771d
+      /* ensure id is unique. */
ac771d
+      for (f = daemon->frec_list; f; f = f->next)
ac771d
+	if (f->sentto && f->new_id == ret)
ac771d
+	  break;
ac771d
+
ac771d
+      if (!f)
ac771d
+	return ret;
ac771d
+    }
ac771d
 }
ac771d
 
ac771d
 
ac771d
-- 
ac771d
2.26.2
ac771d