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 > --- > 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 > +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 > +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 > +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 > +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 > +