]> git.ipfire.org Git - people/jschlag/ipfire-2.x.git/commitdiff
dnsmasq 2.75: latest upstream patches ;-)
authorMatthias Fischer <matthias.fischer@ipfire.org>
Fri, 18 Dec 2015 14:11:25 +0000 (15:11 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 18 Dec 2015 15:02:30 +0000 (15:02 +0000)
The neverending story continues...

Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
lfs/dnsmasq
src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch [new file with mode: 0644]
src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch [new file with mode: 0644]
src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch [new file with mode: 0644]
src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch [new file with mode: 0644]

index eeb7e037b267e73a3fba8be847c57902b1dff0d7..c8fd7db7555e4cb8e33b2d7aa34325ce3bf4d283 100644 (file)
@@ -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 (file)
index 0000000..c3c74cc
--- /dev/null
@@ -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 (file)
index 0000000..60503e9
--- /dev/null
@@ -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 (file)
index 0000000..eda6fbd
--- /dev/null
@@ -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Å\82 KÄ\99pieÅ\84 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 (file)
index 0000000..abcae5c
--- /dev/null
@@ -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
+