mbox

dnsmasq 2.75: latest upstream patches ;-)

Message ID 1450447885-12407-1-git-send-email-matthias.fischer@ipfire.org
State Accepted
Commit 1e1b03d5819269184a85dc5bcc042c978666bc08
Headers

Message

Matthias Fischer Dec. 19, 2015, 1:11 a.m. UTC
  The neverending story continues...

Signed-off-by: Matthias Fischer <matthias.fischer@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
  

Comments

Michael Tremer Dec. 19, 2015, 2:30 a.m. UTC | #1
Hi,

I merged them both since we are still having stability issues with
dnsmasq. Some of the cleanup patches look quite promising.

All give that some good testing, please.

-Michael

On Fri, 2015-12-18 at 15:11 +0100, Matthias Fischer wrote:
> The neverending story continues...
> 
> Signed-off-by: Matthias Fischer <matthias.fischer@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@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@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@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@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
> +
  
Matthias Fischer Dec. 19, 2015, 3:58 a.m. UTC | #2
Hi,

On 18.12.2015 16:30, Michael Tremer wrote:
> All give that some good testing, please.

You can bet that I'll do that...! ;-))

Latest compiled version (Dec 18, 14:21) is running since then without any (known) problems:

...
14:35:03 dnsmasq[27964]: exiting on receipt of SIGTERM
14:35:24 dnsmasq[18002]: started, version 2.75 cachesize 2500
14:35:24 dnsmasq[18002]: compile time options: IPv6 GNU-getopt no-DBus no-i18n IDN no-DHCP no-TFTP no-con ntrack ipset auth DNSSEC loop-detect no-inotify
14:35:24 dnsmasq[18002]: DNSSEC validation enabled
14:35:24 dnsmasq[18002]: reading /var/ipfire/red/resolv.conf
14:35:24 dnsmasq[18002]: using nameserver 84.200.69.80#53
14:35:24 dnsmasq[18002]: using nameserver 84.200.70.40#53
14:35:24 dnsmasq[18002]: reading /var/state/dhcp/dhcpd.leases
14:35:24 dnsmasq[18002]: read /etc/hosts - 9 addresses
...

If anyone wants to test, too:

http://people.ipfire.org/~mfischer/dnsmasq_275_2015_12_18 (MD5: ba95b04fca60d99dfe7e2188e24d7fb3)

This is the compiled binary, nothing more is needed.

Copy to '/usr/sbin', stop 'dnsmasq', rename (don't forget backing up the *old* version!), restart
'dnsmasq'.

Best,
Matthias