public inbox for development@lists.ipfire.org
 help / color / mirror / Atom feed
* [PATCH] dnsmasq 2.75: latest upstream patches ;-)
@ 2015-12-18 14:11 Matthias Fischer
  2015-12-18 15:30 ` Michael Tremer
  0 siblings, 1 reply; 3+ messages in thread
From: Matthias Fischer @ 2015-12-18 14:11 UTC (permalink / raw)
  To: development

[-- Attachment #1: Type: text/plain, Size: 31843 bytes --]

The neverending story continues...

Signed-off-by: Matthias Fischer <matthias.fischer(a)ipfire.org>
---
 lfs/dnsmasq                                        |   4 +
 ...1-Tweaks_to_EDNS0_handling_in_DNS_replies.patch | 133 +++++++
 ..._code_Check_zone_status_is_NSEC_proof_bad.patch | 409 +++++++++++++++++++++
 ...023-Fix_brace_botch_in_dnssec_validate_ds.patch |  98 +++++
 ...ning_which_DNSSEC_sig_algos_are_supported.patch | 145 ++++++++
 5 files changed, 789 insertions(+)
 create mode 100644 src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch
 create mode 100644 src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
 create mode 100644 src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
 create mode 100644 src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch

diff --git a/lfs/dnsmasq b/lfs/dnsmasq
index eeb7e03..c8fd7db 100644
--- a/lfs/dnsmasq
+++ b/lfs/dnsmasq
@@ -93,6 +93,10 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
 	cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/018-Move_code_which_caches_DS_records_to_a_more_logical_place.patch
 	cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/019-Generalise_RR-filtering_code_for_use_with_EDNS0.patch
 	cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
+	cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch
+	cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
+	cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
+	cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch
 	cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch
 
 	cd $(DIR_APP) && sed -i src/config.h \
diff --git a/src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch b/src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch
new file mode 100644
index 0000000..c3c74cc
--- /dev/null
+++ b/src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch
@@ -0,0 +1,133 @@
+From dd4ad9ac7ea6d51dcc34a1f2cd2da14efbb87714 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon(a)thekelleys.org.uk>
+Date: Thu, 17 Dec 2015 10:44:58 +0000
+Subject: [PATCH] Tweaks to EDNS0 handling in DNS replies.
+
+---
+ src/dnssec.c  |   20 +++++++++-----------
+ src/rfc1035.c |   57 +++++++++++++++++++++++++++++++++------------------------
+ 2 files changed, 42 insertions(+), 35 deletions(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index dc563e0..012b2a6 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -2129,18 +2129,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+ 	    /* Empty DS without NSECS */
+ 	    if (qtype == T_DS)
+ 	      return STAT_BOGUS;
+-	    else
++	    
++	    rc = zone_status(name, qclass, keyname, now);
++	    if (rc != STAT_SECURE)
+ 	      {
+-		rc = zone_status(name, qclass, keyname, now);
+-		if (rc != STAT_SECURE)
+-		  {
+-		    if (class)
+-		      *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
+-		    return rc;
+-		  } 
+-		
+-		return STAT_BOGUS; /* signed zone, no NSECs */
+-	      }
++		if (class)
++		  *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
++		return rc;
++	      } 
++	    
++	    return STAT_BOGUS; /* signed zone, no NSECs */
+ 	  }
+ 
+ 	  if (nsec_type == T_NSEC)
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index def8fa0..188d05f 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1539,7 +1539,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+   int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
+   struct mx_srv_record *rec;
+   size_t len;
+- 
++  
++  if (ntohs(header->ancount) != 0 ||
++      ntohs(header->nscount) != 0 ||
++      ntohs(header->qdcount) == 0 || 
++      OPCODE(header) != QUERY )
++    return 0;
++  
+   /* Don't return AD set if checking disabled. */
+   if (header->hb4 & HB4_CD)
+     sec_data = 0;
+@@ -1548,33 +1554,32 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+   *ad_reqd = header->hb4 & HB4_AD;
+   *do_bit = 0;
+ 
+-  /* If there is an RFC2671 pseudoheader then it will be overwritten by
++  /* If there is an  additional data section then it will be overwritten by
+      partial replies, so we have to do a dry run to see if we can answer
+-     the query. We check to see if the do bit is set, if so we always
+-     forward rather than answering from the cache, which doesn't include
+-     security information, unless we're in DNSSEC validation mode. */
++     the query. */
+ 
+-  if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
+-    { 
+-      unsigned short flags;
+-      
+-      have_pseudoheader = 1;
++  if (ntohs(header->arcount) != 0)
++    {
++      dryrun = 1;
+ 
+-      pheader += 4; /* udp size, ext_rcode */
+-      GETSHORT(flags, pheader);
+-      
+-      if ((sec_reqd = flags & 0x8000))
+-	{
+-	  *do_bit = 1;/* do bit */ 
+-	  *ad_reqd = 1;
++      /* If there's an additional section, there might be an EDNS(0) pseudoheader */
++      if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
++	{ 
++	  unsigned short flags;
++	  
++	  have_pseudoheader = 1;
++	  
++	  pheader += 4; /* udp size, ext_rcode */
++	  GETSHORT(flags, pheader);
++	  
++	  if ((sec_reqd = flags & 0x8000))
++	    {
++	      *do_bit = 1;/* do bit */ 
++	      *ad_reqd = 1;
++	    }
+ 	}
+-
+-      dryrun = 1;
+     }
+ 
+-  if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
+-    return 0;
+-  
+   for (rec = daemon->mxnames; rec; rec = rec->next)
+     rec->offset = 0;
+   
+@@ -1730,8 +1735,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+ 		}
+ 	      else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
+ 		{
+-		  /* Don't use cache when DNSSEC data required. */
+-		  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
++		  /* Don't use cache when DNSSEC data required, unless we know that
++		     the zone is unsigned, which implies that we're doing
++		     validation. */
++		  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || 
++		      !sec_reqd || 
++		      (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
+ 		    {
+ 		      do 
+ 			{ 
+-- 
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch b/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
new file mode 100644
index 0000000..60503e9
--- /dev/null
+++ b/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
@@ -0,0 +1,409 @@
+From b40f26c0199235073abc37e1e1d6ed93bed372f5 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon(a)thekelleys.org.uk>
+Date: Thu, 17 Dec 2015 11:57:26 +0000
+Subject: [PATCH] Tidy up DNSSEC non-existence code. Check zone status is NSEC
+ proof bad.
+
+---
+ src/dnssec.c |  207 +++++++++++++++++++++++++---------------------------------
+ 1 file changed, 90 insertions(+), 117 deletions(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 012b2a6..ddae497 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -1367,59 +1367,6 @@ static int hostname_cmp(const char *a, const char *b)
+     }
+ }
+ 
+-/* Find all the NSEC or NSEC3 records in a reply.
+-   return an array of pointers to them. */
+-static int find_nsec_records(struct dns_header *header, size_t plen, unsigned char ***nsecsetp, int *nsecsetl, int class_reqd)
+-{
+-  static unsigned char **nsecset = NULL;
+-  static int nsecset_sz = 0;
+-  
+-  int type_found = 0;
+-  unsigned char *p = skip_questions(header, plen);
+-  int type, class, rdlen, i, nsecs_found;
+-
+-  /* Move to NS section */
+-  if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
+-    return 0;
+-  
+-  for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
+-    {
+-      unsigned char *pstart = p;
+-      
+-      if (!(p = skip_name(p, header, plen, 10)))
+-	return 0;
+-      
+-      GETSHORT(type, p); 
+-      GETSHORT(class, p);
+-      p += 4; /* TTL */
+-      GETSHORT(rdlen, p);
+-
+-      if (class == class_reqd && (type == T_NSEC || type == T_NSEC3))
+-	{
+-	  /* No mixed NSECing 'round here, thankyouverymuch */
+-	  if (type_found == T_NSEC && type == T_NSEC3)
+-	    return 0;
+-	  if (type_found == T_NSEC3 && type == T_NSEC)
+-	    return 0;
+-
+-	  type_found = type;
+-
+-	  if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
+-	    return 0; 
+-	  
+-	  nsecset[nsecs_found++] = pstart;
+-	}
+-      
+-      if (!ADD_RDLEN(header, p, plen, rdlen))
+-	return 0;
+-    }
+-  
+-  *nsecsetp = nsecset;
+-  *nsecsetl = nsecs_found;
+-  
+-  return type_found;
+-}
+-
+ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
+ 				    char *workspace1, char *workspace2, char *name, int type, int *nons)
+ {
+@@ -1436,12 +1383,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
+     {
+       p = nsecs[i];
+       if (!extract_name(header, plen, &p, workspace1, 1, 10))
+-	return STAT_BOGUS;
++	return 0;
+       p += 8; /* class, type, TTL */
+       GETSHORT(rdlen, p);
+       psave = p;
+       if (!extract_name(header, plen, &p, workspace2, 1, 10))
+-	return STAT_BOGUS;
++	return 0;
+       
+       rc = hostname_cmp(workspace1, name);
+       
+@@ -1449,7 +1396,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
+ 	{
+ 	  /* 4035 para 5.4. Last sentence */
+ 	  if (type == T_NSEC || type == T_RRSIG)
+-	    return STAT_SECURE;
++	    return 1;
+ 
+ 	  /* NSEC with the same name as the RR we're testing, check
+ 	     that the type in question doesn't appear in the type map */
+@@ -1465,24 +1412,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
+ 	      /* A CNAME answer would also be valid, so if there's a CNAME is should 
+ 		 have been returned. */
+ 	      if ((p[2] & (0x80 >> T_CNAME)) != 0)
+-		return STAT_BOGUS;
++		return 0;
+ 	      
+ 	      /* If the SOA bit is set for a DS record, then we have the
+ 		 DS from the wrong side of the delegation. */
+ 	      if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
+-		return STAT_BOGUS;
++		return 0;
+ 	    }
+ 
+ 	  while (rdlen >= 2)
+ 	    {
+ 	      if (!CHECK_LEN(header, p, plen, rdlen))
+-		return STAT_BOGUS;
++		return 0;
+ 	      
+ 	      if (p[0] == type >> 8)
+ 		{
+ 		  /* Does the NSEC say our type exists? */
+ 		  if (offset < p[1] && (p[offset+2] & mask) != 0)
+-		    return STAT_BOGUS;
++		    return 0;
+ 		  
+ 		  break; /* finshed checking */
+ 		}
+@@ -1491,24 +1438,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
+ 	      p +=  p[1];
+ 	    }
+ 	  
+-	  return STAT_SECURE;
++	  return 1;
+ 	}
+       else if (rc == -1)
+ 	{
+ 	  /* Normal case, name falls between NSEC name and next domain name,
+ 	     wrap around case, name falls between NSEC name (rc == -1) and end */
+ 	  if (hostname_cmp(workspace2, name) >= 0 || hostname_cmp(workspace1, workspace2) >= 0)
+-	    return STAT_SECURE;
++	    return 1;
+ 	}
+       else 
+ 	{
+ 	  /* wrap around case, name falls between start and next domain name */
+ 	  if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 )
+-	    return STAT_SECURE;
++	    return 1;
+ 	}
+     }
+   
+-  return STAT_BOGUS;
++  return 0;
+ }
+ 
+ /* return digest length, or zero on error */
+@@ -1701,7 +1648,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+   for (i = 0; i < nsec_count; i++)
+     {
+       if (!(p = skip_name(nsecs[i], header, plen, 15)))
+-	return STAT_BOGUS; /* bad packet */
++	return 0; /* bad packet */
+       
+       p += 10; /* type, class, TTL, rdlen */
+       algo = *p++;
+@@ -1712,14 +1659,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+ 
+   /* No usable NSEC3s */
+   if (i == nsec_count)
+-    return STAT_BOGUS;
++    return 0;
+ 
+   p++; /* flags */
+   GETSHORT (iterations, p);
+   salt_len = *p++;
+   salt = p;
+   if (!CHECK_LEN(header, salt, plen, salt_len))
+-    return STAT_BOGUS; /* bad packet */
++    return 0; /* bad packet */
+     
+   /* Now prune so we only have NSEC3 records with same iterations, salt and algo */
+   for (i = 0; i < nsec_count; i++)
+@@ -1730,7 +1677,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+       nsecs[i] = NULL; /* Speculative, will be restored if OK. */
+       
+       if (!(p = skip_name(nsec3p, header, plen, 15)))
+-	return STAT_BOGUS; /* bad packet */
++	return 0; /* bad packet */
+       
+       p += 10; /* type, class, TTL, rdlen */
+       
+@@ -1747,7 +1694,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+ 	continue;
+       
+       if (!CHECK_LEN(header, p, plen, salt_len))
+-	return STAT_BOGUS; /* bad packet */
++	return 0; /* bad packet */
+ 
+       if (memcmp(p, salt, salt_len) != 0)
+ 	continue;
+@@ -1758,13 +1705,13 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+ 
+   /* Algo is checked as 1 above */
+   if (!(hash = hash_find("sha1")))
+-    return STAT_BOGUS;
++    return 0;
+ 
+   if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
+-    return STAT_BOGUS;
++    return 0;
+   
+   if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
+-    return STAT_SECURE;
++    return 1;
+ 
+   /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3" 
+      or an answer inferred from a wildcard record. */
+@@ -1780,14 +1727,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+ 	break;
+ 
+       if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
+-	return STAT_BOGUS;
++	return 0;
+       
+       for (i = 0; i < nsec_count; i++)
+ 	if ((p = nsecs[i]))
+ 	  {
+ 	    if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
+ 		!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
+-	      return STAT_BOGUS;
++	      return 0;
+ 	  
+ 	    if (digest_len == base32_len &&
+ 		memcmp(digest, workspace2, digest_len) == 0)
+@@ -1802,32 +1749,81 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+   while ((closest_encloser = strchr(closest_encloser, '.')));
+   
+   if (!closest_encloser)
+-    return STAT_BOGUS;
++    return 0;
+   
+   /* Look for NSEC3 that proves the non-existence of the next-closest encloser */
+   if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
+-    return STAT_BOGUS;
++    return 0;
+ 
+   if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
+-    return STAT_BOGUS;
++    return 0;
+   
+   /* Finally, check that there's no seat of wildcard synthesis */
+   if (!wildname)
+     {
+       if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
+-	return STAT_BOGUS;
++	return 0;
+       
+       wildcard--;
+       *wildcard = '*';
+       
+       if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
+-	return STAT_BOGUS;
++	return 0;
+       
+       if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
+-	return STAT_BOGUS;
++	return 0;
+     }
+   
+-  return STAT_SECURE;
++  return 1;
++}
++
++static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons)
++{
++  static unsigned char **nsecset = NULL;
++  static int nsecset_sz = 0;
++  
++  int type_found = 0;
++  unsigned char *p = skip_questions(header, plen);
++  int type, class, rdlen, i, nsecs_found;
++  
++  /* Move to NS section */
++  if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
++    return 0;
++  
++  for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
++    {
++      unsigned char *pstart = p;
++      
++      if (!(p = skip_name(p, header, plen, 10)))
++	return 0;
++      
++      GETSHORT(type, p); 
++      GETSHORT(class, p);
++      p += 4; /* TTL */
++      GETSHORT(rdlen, p);
++
++      if (class == qclass && (type == T_NSEC || type == T_NSEC3))
++	{
++	  /* No mixed NSECing 'round here, thankyouverymuch */
++	  if (type_found != 0 && type_found != type)
++	    return 0;
++
++	  type_found = type;
++
++	  if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
++	    return 0; 
++	  
++	  nsecset[nsecs_found++] = pstart;
++	}
++      
++      if (!ADD_RDLEN(header, p, plen, rdlen))
++	return 0;
++    }
++  
++  if (type_found == T_NSEC)
++    return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
++  else
++    return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
+ }
+ 
+ /* Check signing status of name.
+@@ -1925,10 +1921,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+   static unsigned char **targets = NULL;
+   static int target_sz = 0;
+ 
+-  unsigned char *ans_start, *p1, *p2, **nsecs;
++  unsigned char *ans_start, *p1, *p2;
+   int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx;
+-  int i, j, rc, nsec_count;
+-  int nsec_type;
++  int i, j, rc;
+ 
+   if (neganswer)
+     *neganswer = 0;
+@@ -2080,28 +2075,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+ 			  targets[j] = NULL;
+ 		      }
+ 			    
+-		  if (rc == STAT_SECURE_WILDCARD)
+-		    {
+-		      /* An attacker replay a wildcard answer with a different
+-			 answer and overlay a genuine RR. To prove this
+-			 hasn't happened, the answer must prove that
+-			 the gennuine record doesn't exist. Check that here. */
+-		      if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1)))
+-			return STAT_BOGUS; /* No NSECs or bad packet */
+-		      
+-		      /* Note that we may not yet have validated the NSEC/NSEC3 RRsets. Since the check
+-			 below returns either SECURE or BOGUS, that's not a problem. If the RRsets later fail
+-			 we'll return BOGUS then. */
+-
+-		      if (nsec_type == T_NSEC)
+-			rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL);
+-		      else
+-			rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, 
+-						       keyname, name, type1, wildname, NULL);
+-		      
+-		      if (rc == STAT_BOGUS)
+-			return rc;
+-		    } 
++		   /* An attacker replay a wildcard answer with a different
++		      answer and overlay a genuine RR. To prove this
++		      hasn't happened, the answer must prove that
++		      the gennuine record doesn't exist. Check that here. 
++		      Note that we may not yet have validated the NSEC/NSEC3 RRsets. 
++		      That's not a problem since if the RRsets later fail
++		      we'll return BOGUS then. */
++		  if (rc == STAT_SECURE_WILDCARD && !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL))
++		    return STAT_BOGUS;
+ 		}
+ 	    }
+ 	}
+@@ -2124,14 +2106,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+ 
+ 	/* For anything other than a DS record, this situation is OK if either
+ 	   the answer is in an unsigned zone, or there's a NSEC records. */
+-	if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass)))
++	if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons))
+ 	  {
+ 	    /* Empty DS without NSECS */
+ 	    if (qtype == T_DS)
+ 	      return STAT_BOGUS;
+ 	    
+-	    rc = zone_status(name, qclass, keyname, now);
+-	    if (rc != STAT_SECURE)
++	    if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
+ 	      {
+ 		if (class)
+ 		  *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
+@@ -2140,14 +2121,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+ 	    
+ 	    return STAT_BOGUS; /* signed zone, no NSECs */
+ 	  }
+-
+-	  if (nsec_type == T_NSEC)
+-	  rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
+-	else
+-	  rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
+-
+-	if (rc != STAT_SECURE)
+-	  return rc;
+       }
+   
+   return STAT_SECURE;
+-- 
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch b/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
new file mode 100644
index 0000000..eda6fbd
--- /dev/null
+++ b/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
@@ -0,0 +1,98 @@
+From 3b799c826db05fc2da1c6d15cbe372e394209d27 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon(a)thekelleys.org.uk>
+Date: Thu, 17 Dec 2015 16:58:04 +0000
+Subject: [PATCH] Fix brace botch in dnssec_validate_ds()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf8
+Content-Transfer-Encoding: 8bit
+
+Thanks to Michał Kępień for spotting this.
+---
+ src/dnssec.c |   34 +++++++++++++++++-----------------
+ 1 file changed, 17 insertions(+), 17 deletions(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index ddae497..1f8c954 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -923,11 +923,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
+ /* The DNS packet is expected to contain the answer to a DNSKEY query.
+    Put all DNSKEYs in the answer which are valid into the cache.
+    return codes:
+-         STAT_OK           Done, key(s) in cache.
+-	 STAT_BOGUS        No DNSKEYs found, which  can be validated with DS,
+-	                   or self-sign for DNSKEY RRset is not valid, bad packet.
+-	 STAT_NEED_DS      DS records to validate a key not found, name in keyname 
+-	 STAT_NEED_DNSKEY  DNSKEY records to validate a key not found, name in keyname 
++         STAT_OK        Done, key(s) in cache.
++	 STAT_BOGUS     No DNSKEYs found, which  can be validated with DS,
++	                or self-sign for DNSKEY RRset is not valid, bad packet.
++	 STAT_NEED_DS   DS records to validate a key not found, name in keyname 
++	 STAT_NEED_KEY  DNSKEY records to validate a key not found, name in keyname 
+ */
+ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
+ {
+@@ -1224,13 +1224,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
+ 		}
+ 	      
+ 	      p = psave;
+-	      
+-	      if (!ADD_RDLEN(header, p, plen, rdlen))
+-		return STAT_BOGUS; /* bad packet */
+ 	    }
+-	  
+-	  cache_end_insert();
++	  if (!ADD_RDLEN(header, p, plen, rdlen))
++	    return STAT_BOGUS; /* bad packet */
+ 	}
++
++      cache_end_insert();
++
+     }
+   else
+     {
+@@ -1828,10 +1828,10 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
+ 
+ /* Check signing status of name.
+    returns:
+-   STAT_SECURE      zone is signed.
+-   STAT_INSECURE    zone proved unsigned.
+-   STAT_NEED_DS     require DS record of name returned in keyname.
+-   STAT_NEED_DNSKEY require DNSKEY record of name returned in keyname.
++   STAT_SECURE   zone is signed.
++   STAT_INSECURE zone proved unsigned.
++   STAT_NEED_DS  require DS record of name returned in keyname.
++   STAT_NEED_KEY require DNSKEY record of name returned in keyname.
+    name returned unaltered.
+ */
+ static int zone_status(char *name, int class, char *keyname, time_t now)
+@@ -2028,7 +2028,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+ 		      if (rc == STAT_SECURE)
+ 			rc = STAT_BOGUS;
+ 		       if (class)
+-			 *class = class1; /* Class for NEED_DS or NEED_DNSKEY */
++			 *class = class1; /* Class for NEED_DS or NEED_KEY */
+ 		    }
+ 		  else 
+ 		    rc = STAT_INSECURE; 
+@@ -2045,7 +2045,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+ 		{
+ 		  /* Zone is insecure, don't need to validate RRset */
+ 		  if (class)
+-		    *class = class1; /* Class for NEED_DS or NEED_DNSKEY */
++		    *class = class1; /* Class for NEED_DS or NEED_KEY */
+ 		  return rc;
+ 		} 
+ 	      
+@@ -2115,7 +2115,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+ 	    if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
+ 	      {
+ 		if (class)
+-		  *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
++		  *class = qclass; /* Class for NEED_DS or NEED_KEY */
+ 		return rc;
+ 	      } 
+ 	    
+-- 
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch b/src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch
new file mode 100644
index 0000000..abcae5c
--- /dev/null
+++ b/src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch
@@ -0,0 +1,145 @@
+From 14a4ae883d51130d33da7133287e8867c64bab65 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon(a)thekelleys.org.uk>
+Date: Thu, 17 Dec 2015 17:23:03 +0000
+Subject: [PATCH] Do a better job of determining which DNSSEC sig algos are
+ supported.
+
+---
+ src/dnssec.c |   52 +++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 37 insertions(+), 15 deletions(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 1f8c954..82394ee 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -65,10 +65,9 @@ static char *algo_digest_name(int algo)
+     case 8: return "sha256";
+     case 10: return "sha512";
+     case 12: return "gosthash94";
+-#ifndef NO_NETTLE_ECC
+     case 13: return "sha256";
+     case 14: return "sha384";
+-#endif
++
+     default: return NULL;
+     }
+ }
+@@ -129,13 +128,15 @@ static int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char
+ }
+   
+ static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+-			      unsigned char *digest, int algo)
++			      unsigned char *digest, size_t digest_len, int algo)
+ {
+   unsigned char *p;
+   size_t exp_len;
+   
+   static struct rsa_public_key *key = NULL;
+   static mpz_t sig_mpz;
++
++  (void)digest_len;
+   
+   if (key == NULL)
+     {
+@@ -181,7 +182,7 @@ static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len,
+ }  
+ 
+ static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+-			      unsigned char *digest, int algo)
++			      unsigned char *digest, size_t digest_len, int algo)
+ {
+   unsigned char *p;
+   unsigned int t;
+@@ -189,6 +190,8 @@ static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len,
+   static struct dsa_public_key *key = NULL;
+   static struct dsa_signature *sig_struct;
+   
++  (void)digest_len;
++
+   if (key == NULL)
+     {
+       if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) || 
+@@ -292,26 +295,45 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
+ } 
+ #endif 
+ 
+-static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+-		  unsigned char *digest, size_t digest_len, int algo)
++static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
++				    unsigned char *digest, size_t digest_len, int algo)
+ {
+-  (void)digest_len;
+-
++    
++  /* Enure at runtime that we have support for this digest */
++  if (!hash_find(algo_digest_name(algo)))
++    return NULL;
++  
++  /* This switch defines which sig algorithms we support, can't introspect Nettle for that. */
+   switch (algo)
+     {
+     case 1: case 5: case 7: case 8: case 10:
+-      return dnsmasq_rsa_verify(key_data, key_len, sig, sig_len, digest, algo);
++      return dnsmasq_rsa_verify;
+       
+     case 3: case 6: 
+-      return dnsmasq_dsa_verify(key_data, key_len, sig, sig_len, digest, algo);
++      return dnsmasq_dsa_verify;
+  
+ #ifndef NO_NETTLE_ECC   
+     case 13: case 14:
+-      return dnsmasq_ecdsa_verify(key_data, key_len, sig, sig_len, digest, digest_len, algo);
++      return dnsmasq_ecdsa_verify;
+ #endif
+     }
+   
+-  return 0;
++  return NULL;
++}
++
++static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
++		  unsigned char *digest, size_t digest_len, int algo)
++{
++
++  int (*func)(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
++	      unsigned char *digest, size_t digest_len, int algo);
++  
++  func = verify_func(algo);
++  
++  if (!func)
++    return 0;
++
++  return (*func)(key_data, key_len, sig, sig_len, digest, digest_len, algo);
+ }
+ 
+ /* Convert from presentation format to wire format, in place.
+@@ -732,7 +754,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
+ 	      if (check_date_range(sig_inception, sig_expiration) &&
+ 		  labels <= name_labels &&
+ 		  type_covered == type && 
+-		  algo_digest_name(algo))
++		  verify_func(algo))
+ 		{
+ 		  if (!expand_workspace(&sigs, &sig_sz, sigidx))
+ 		    return 0; 
+@@ -1865,7 +1887,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
+ 		      if (crecp->flags & F_DNSSECOK)
+ 			return STAT_INSECURE; /* proved no DS here */
+ 		    }
+-		  else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
++		  else if (!hash_find(ds_digest_name(crecp->addr.ds.digest)) || !verify_func(crecp->addr.ds.algo))
+ 		    return STAT_INSECURE; /* algo we can't use - insecure */
+ 		  else
+ 		    secure_ds = 1;
+@@ -1887,7 +1909,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
+ 
+ 	  do 
+ 	    {
+-	      if (crecp->uid == (unsigned int)class && !algo_digest_name(crecp->addr.key.algo))
++	      if (crecp->uid == (unsigned int)class && !verify_func(crecp->addr.key.algo))
+ 		return STAT_INSECURE;
+ 	    }
+ 	  while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)));
+-- 
+1.7.10.4
+
-- 
2.6.4


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2015-12-18 16:58 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-18 14:11 [PATCH] dnsmasq 2.75: latest upstream patches ;-) Matthias Fischer
2015-12-18 15:30 ` Michael Tremer
2015-12-18 16:58   ` Matthias Fischer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox