Blob Blame History Raw
From 04ef96f428beebd07b457b51b5d2f26d3099f1a5 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 12 Nov 2020 18:49:23 +0000
Subject: [PATCH 2/4] Check destination of DNS UDP query replies.

At any time, dnsmasq will have a set of sockets open, bound to
random ports, on which it sends queries to upstream nameservers.
This patch fixes the existing problem that a reply for ANY in-flight
query would be accepted via ANY open port, which increases the
chances of an attacker flooding answers "in the blind" in an
attempt to poison the DNS cache. CERT VU#434904 refers.
---
 src/forward.c | 37 ++++++++++++++++++++++++++++---------
 1 file changed, 28 insertions(+), 9 deletions(-)

diff --git a/src/forward.c b/src/forward.c
index cdd11d3..85eab27 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -16,7 +16,7 @@
 
 #include "dnsmasq.h"
 
-static struct frec *lookup_frec(unsigned short id, void *hash);
+static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash);
 static struct frec *lookup_frec_by_sender(unsigned short id,
 					  union mysockaddr *addr,
 					  void *hash);
@@ -780,7 +780,7 @@ void reply_query(int fd, int family, time_t now)
   crc = questions_crc(header, n, daemon->namebuff);
 #endif
   
-  if (!(forward = lookup_frec(ntohs(header->id), hash)))
+  if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash)))
     return;
   
   /* log_query gets called indirectly all over the place, so 
@@ -2195,14 +2195,25 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
 }
 
 /* crc is all-ones if not known. */
-static struct frec *lookup_frec(unsigned short id, void *hash)
+static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash)
 {
   struct frec *f;
 
   for(f = daemon->frec_list; f; f = f->next)
     if (f->sentto && f->new_id == id && 
 	(!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
-      return f;
+      {
+	/* sent from random port */
+	if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd)
+	  return f;
+
+	if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd)
+	  return f;
+
+	/* sent to upstream from bound socket. */
+	if (f->sentto->sfd && f->sentto->sfd->fd == fd)
+	  return f;
+      }
       
   return NULL;
 }
@@ -2263,12 +2274,20 @@ void server_gone(struct server *server)
 static unsigned short get_id(void)
 {
   unsigned short ret = 0;
+  struct frec *f;
   
-  do 
-    ret = rand16();
-  while (lookup_frec(ret, NULL));
-  
-  return ret;
+  while (1)
+    {
+      ret = rand16();
+
+      /* ensure id is unique. */
+      for (f = daemon->frec_list; f; f = f->next)
+	if (f->sentto && f->new_id == ret)
+	  break;
+
+      if (!f)
+	return ret;
+    }
 }
 
 
-- 
2.26.2