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