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 +