Same procedure as... :-)
Best to all for xmas and 2016!
Matthias
Signed-off-by: Matthias Fischer matthias.fischer@ipfire.org --- lfs/dnsmasq | 5 + ...EDNS0_handling_and_computation_use_of_udp.patch | 643 +++++++++++++++++++++ ...aks_in_handling_unknown_DNSSEC_algorithms.patch | 262 +++++++++ ...obscure_off-by-one_in_DNSSEC_hostname_cmp.patch | 27 + .../028-Minor_tweak_to_previous_commit.patch | 39 ++ .../dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch | 39 ++ 6 files changed, 1015 insertions(+) create mode 100644 src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch create mode 100644 src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch create mode 100644 src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch create mode 100644 src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch create mode 100644 src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch
diff --git a/lfs/dnsmasq b/lfs/dnsmasq index c8fd7db..8058663 100644 --- a/lfs/dnsmasq +++ b/lfs/dnsmasq @@ -97,6 +97,11 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) 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/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.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/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch b/src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch new file mode 100644 index 0000000..c016e73 --- /dev/null +++ b/src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch @@ -0,0 +1,643 @@ +From fa14bec83b2db010fd076910fddab56957b9375d Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 17:12:16 +0000 +Subject: [PATCH] Major tidy up of EDNS0 handling and computation/use of udp + packet size. + +--- + src/auth.c | 8 ++- + src/dnsmasq.h | 7 ++- + src/dnssec.c | 1 - + src/forward.c | 184 ++++++++++++++++++++++++++++++++++++++++---------------- + src/netlink.c | 3 +- + src/rfc1035.c | 81 +++++++------------------ + src/rrfilter.c | 2 +- + 7 files changed, 168 insertions(+), 118 deletions(-) + +diff --git a/src/auth.c b/src/auth.c +index 2b0b7d6..85bd5e7 100644 +--- a/src/auth.c ++++ b/src/auth.c +@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut) + } + + +-size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query) ++size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, ++ int local_query, int do_bit, int have_pseudoheader) + { + char *name = daemon->namebuff; + unsigned char *p, *ansp; +@@ -820,6 +821,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n + header->ancount = htons(anscount); + header->nscount = htons(authcount); + header->arcount = htons(0); ++ ++ /* Advertise our packet size limit in our reply */ ++ if (have_pseudoheader) ++ return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit); ++ + return ansp - (unsigned char *)header; + } + +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 39a930c..abb34c5 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1113,7 +1113,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff, + int no_cache, int secure, int *doctored); + size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + struct in_addr local_addr, struct in_addr local_netmask, +- time_t now, int *ad_reqd, int *do_bit); ++ time_t now, int ad_reqd, int do_bit, int have_pseudoheader); + int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, + struct bogus_addr *addr, time_t now); + int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr); +@@ -1123,6 +1123,8 @@ int check_for_local_domain(char *name, time_t now); + unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff); + size_t resize_packet(struct dns_header *header, size_t plen, + unsigned char *pheader, size_t hlen); ++size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, ++ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do); + size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3); + size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source); + #ifdef HAVE_DNSSEC +@@ -1141,7 +1143,8 @@ int private_net(struct in_addr addr, int ban_localhost); + /* auth.c */ + #ifdef HAVE_AUTH + size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, +- time_t now, union mysockaddr *peer_addr, int local_query); ++ time_t now, union mysockaddr *peer_addr, int local_query, ++ int do_bit, int have_pseudoheader); + int in_zone(struct auth_zone *zone, char *name, char **cut); + #endif + +diff --git a/src/dnssec.c b/src/dnssec.c +index 82394ee..299ca64 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -67,7 +67,6 @@ static char *algo_digest_name(int algo) + case 12: return "gosthash94"; + case 13: return "sha256"; + case 14: return "sha384"; +- + default: return NULL; + } + } +diff --git a/src/forward.c b/src/forward.c +index 3e801c8..041353c 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -244,7 +244,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + void *hash = &crc; + #endif + unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); +- unsigned char *pheader; + + (void)do_bit; + +@@ -264,7 +263,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + there's no point retrying the query, retry the key query instead...... */ + if (forward->blocking_query) + { +- int fd; ++ int fd, is_sign; ++ unsigned char *pheader; + + forward->flags &= ~FREC_TEST_PKTSZ; + +@@ -276,8 +276,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); + plen = forward->stash_len; + +- if (find_pseudoheader(header, plen, NULL, &pheader, NULL)) +- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader); ++ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign) ++ PUTSHORT(SAFE_PKTSZ, pheader); + + if (forward->sentto->addr.sa.sa_family == AF_INET) + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); +@@ -394,32 +394,40 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + forward->log_id = daemon->log_id; + + if (option_bool(OPT_ADD_MAC)) +- plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); +- ++ { ++ size_t new = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); ++ if (new != plen) ++ { ++ plen = new; ++ forward->flags |= FREC_ADDED_PHEADER; ++ } ++ } ++ + if (option_bool(OPT_CLIENT_SUBNET)) + { + size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); + if (new != plen) + { + plen = new; +- forward->flags |= FREC_HAS_SUBNET; ++ forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER; + } + } + + #ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID)) + { +- size_t new_plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz); ++ size_t new = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz); + ++ if (new != plen) ++ forward->flags |= FREC_ADDED_PHEADER; ++ ++ plen = new; ++ + /* For debugging, set Checking Disabled, otherwise, have the upstream check too, + this allows it to select auth servers when one is returning bad data. */ + if (option_bool(OPT_DNSSEC_DEBUG)) + header->hb4 |= HB4_CD; + +- if (new_plen != plen) +- forward->flags |= FREC_ADDED_PHEADER; +- +- plen = new_plen; + } + #endif + +@@ -469,10 +477,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + } + #endif + } +- +- if (find_pseudoheader(header, plen, NULL, &pheader, NULL)) +- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader); + ++#ifdef HAVE_DNSSEC ++ if (option_bool(OPT_DNSSEC_VALID) && !do_bit) ++ { ++ /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP ++ packet size to 512. But that won't provide space for the RRSIGS in many cases. ++ The RRSIGS will be stripped out before the answer goes back, so the packet should ++ shrink again. So, if we added a do-bit, bump the udp packet size to the value ++ known to be OK for this server. Maybe check returned size after stripping and set ++ the truncated bit? */ ++ unsigned char *pheader; ++ int is_sign; ++ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign)) ++ PUTSHORT(start->edns_pktsz, pheader); ++ } ++#endif ++ + if (retry_send(sendto(fd, (char *)header, plen, 0, + &start->addr.sa, + sa_len(&start->addr)))) +@@ -563,30 +584,34 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server + } + #endif + +- /* If upstream is advertising a larger UDP packet size +- than we allow, trim it so that we don't get overlarge +- requests for the client. We can't do this for signed packets. */ +- + if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign))) + { +- unsigned short udpsz; +- unsigned char *psave = sizep; +- +- GETSHORT(udpsz, sizep); +- +- if (!is_sign && udpsz > daemon->edns_pktsz) +- PUTSHORT(daemon->edns_pktsz, psave); +- + if (check_subnet && !check_source(header, plen, pheader, query_source)) + { + my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); + return 0; + } + +- if (added_pheader) ++ if (!is_sign) + { +- pheader = 0; +- header->arcount = htons(0); ++ if (added_pheader) ++ { ++ /* client didn't send EDNS0, we added one, strip it off before returning answer. */ ++ n = rrfilter(header, n, 0); ++ pheader = NULL; ++ } ++ else ++ { ++ /* If upstream is advertising a larger UDP packet size ++ than we allow, trim it so that we don't get overlarge ++ requests for the client. We can't do this for signed packets. */ ++ unsigned short udpsz; ++ unsigned char *psave = sizep; ++ ++ GETSHORT(udpsz, sizep); ++ if (udpsz > daemon->edns_pktsz) ++ PUTSHORT(daemon->edns_pktsz, psave); ++ } + } + } + +@@ -655,14 +680,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server + } + + if (option_bool(OPT_DNSSEC_VALID)) +- header->hb4 &= ~HB4_AD; +- +- if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) +- header->hb4 |= HB4_AD; +- +- /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ +- if (!do_bit) +- n = rrfilter(header, n, 1); ++ { ++ header->hb4 &= ~HB4_AD; ++ ++ if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) ++ header->hb4 |= HB4_AD; ++ ++ /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ ++ if (!do_bit) ++ n = rrfilter(header, n, 1); ++ } + #endif + + /* do this after extract_addresses. Ensure NODATA reply and remove +@@ -761,8 +788,14 @@ void reply_query(int fd, int family, time_t now) + if ((nn = resize_packet(header, (size_t)n, pheader, plen))) + { + header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC); +- header->hb4 &= ~(HB4_RA | HB4_RCODE); +- forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0); ++ header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD); ++ if (forward->flags |= FREC_CHECKING_DISABLED) ++ header->hb4 |= HB4_CD; ++ if (forward->flags |= FREC_AD_QUESTION) ++ header->hb4 |= HB4_AD; ++ if (forward->flags & FREC_DO_QUESTION) ++ add_do_bit(header, nn, (char *)pheader + plen); ++ forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION); + return; + } + } +@@ -1007,12 +1040,13 @@ void receive_query(struct listener *listen, time_t now) + { + struct dns_header *header = (struct dns_header *)daemon->packet; + union mysockaddr source_addr; +- unsigned short type; ++ unsigned char *pheader; ++ unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */ + struct all_addr dst_addr; + struct in_addr netmask, dst_addr_4; + size_t m; + ssize_t n; +- int if_index = 0, auth_dns = 0; ++ int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0; + #ifdef HAVE_AUTH + int local_auth = 0; + #endif +@@ -1279,10 +1313,30 @@ void receive_query(struct listener *listen, time_t now) + #endif + } + ++ if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL)) ++ { ++ unsigned short flags; ++ ++ have_pseudoheader = 1; ++ GETSHORT(udp_size, pheader); ++ pheader += 2; /* ext_rcode */ ++ GETSHORT(flags, pheader); ++ ++ if (flags & 0x8000) ++ do_bit = 1;/* do bit */ ++ ++ /* If the client provides an EDNS0 UDP size, use that to limit our reply. ++ (bounded by the maximum configured). If no EDNS0, then it ++ defaults to 512 */ ++ if (udp_size > daemon->edns_pktsz) ++ udp_size = daemon->edns_pktsz; ++ } ++ + #ifdef HAVE_AUTH + if (auth_dns) + { +- m = answer_auth(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, now, &source_addr, local_auth); ++ m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr, ++ local_auth, do_bit, have_pseudoheader); + if (m >= 1) + { + send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), +@@ -1293,9 +1347,13 @@ void receive_query(struct listener *listen, time_t now) + else + #endif + { +- int ad_reqd, do_bit; +- m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, +- dst_addr_4, netmask, now, &ad_reqd, &do_bit); ++ int ad_reqd = do_bit; ++ /* RFC 6840 5.7 */ ++ if (header->hb4 & HB4_AD) ++ ad_reqd = 1; ++ ++ m = answer_request(header, ((char *) header) + udp_size, (size_t)n, ++ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader); + + if (m >= 1) + { +@@ -1397,7 +1455,7 @@ unsigned char *tcp_request(int confd, time_t now, + #ifdef HAVE_AUTH + int local_auth = 0; + #endif +- int checking_disabled, ad_question, do_bit, added_pheader = 0; ++ int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0; + int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; + size_t m; + unsigned short qtype; +@@ -1414,6 +1472,7 @@ unsigned char *tcp_request(int confd, time_t now, + union mysockaddr peer_addr; + socklen_t peer_len = sizeof(union mysockaddr); + int query_count = 0; ++ unsigned char *pheader; + + if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) + return packet; +@@ -1508,15 +1567,35 @@ unsigned char *tcp_request(int confd, time_t now, + else + dst_addr_4.s_addr = 0; + ++ do_bit = 0; ++ ++ if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL)) ++ { ++ unsigned short flags; ++ ++ have_pseudoheader = 1; ++ pheader += 4; /* udp_size, ext_rcode */ ++ GETSHORT(flags, pheader); ++ ++ if (flags & 0x8000) ++ do_bit = 1;/* do bit */ ++ } ++ + #ifdef HAVE_AUTH + if (auth_dns) +- m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth); ++ m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, ++ local_auth, do_bit, have_pseudoheader); + else + #endif + { +- /* m > 0 if answered from cache */ +- m = answer_request(header, ((char *) header) + 65536, (size_t)size, +- dst_addr_4, netmask, now, &ad_question, &do_bit); ++ int ad_reqd = do_bit; ++ /* RFC 6840 5.7 */ ++ if (header->hb4 & HB4_AD) ++ ad_reqd = 1; ++ ++ /* m > 0 if answered from cache */ ++ m = answer_request(header, ((char *) header) + 65536, (size_t)size, ++ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader); + + /* Do this by steam now we're not in the select() loop */ + check_log_writer(1); +@@ -1615,6 +1694,7 @@ unsigned char *tcp_request(int confd, time_t now, + } + + #ifdef HAVE_DNSSEC ++ added_pheader = 0; + if (option_bool(OPT_DNSSEC_VALID)) + { + size_t new_size = add_do_bit(header, size, ((char *) header) + 65536); +@@ -1719,7 +1799,7 @@ unsigned char *tcp_request(int confd, time_t now, + + m = process_reply(header, now, last_server, (unsigned int)m, + option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, +- ad_question, do_bit, added_pheader, check_subnet, &peer_addr); ++ ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr); + + break; + } +diff --git a/src/netlink.c b/src/netlink.c +index 753784d..3376d68 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + rta = RTA_NEXT(rta, len1); + } + +- if (inaddr && mac && callback_ok) ++ if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) && ++ inaddr && mac && callback_ok) + if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm))) + callback_ok = 0; + } +diff --git a/src/rfc1035.c b/src/rfc1035.c +index 188d05f..18858a8 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -489,8 +489,8 @@ struct macparm { + union mysockaddr *l3; + }; + +-static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, +- int optno, unsigned char *opt, size_t optlen, int set_do) ++size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, ++ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do) + { + unsigned char *lenp, *datap, *p; + int rdlen, is_sign; +@@ -508,7 +508,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned + return plen; + *p++ = 0; /* empty name */ + PUTSHORT(T_OPT, p); +- PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be overwritten */ ++ PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */ + PUTSHORT(0, p); /* extended RCODE and version */ + PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */ + lenp = p; +@@ -594,7 +594,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p + if (!match) + return 1; /* continue */ + +- parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0); ++ parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, PACKETSZ, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0); + + return 0; /* done */ + } +@@ -603,12 +603,6 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock + { + struct macparm parm; + +-/* Must have an existing pseudoheader as the only ar-record, +- or have no ar-records. Must also not be signed */ +- +- if (ntohs(header->arcount) > 1) +- return plen; +- + parm.header = header; + parm.limit = (unsigned char *)limit; + parm.plen = plen; +@@ -699,13 +693,13 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio + struct subnet_opt opt; + + len = calc_subnet_opt(&opt, source); +- return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0); ++ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0); + } + + #ifdef HAVE_DNSSEC + size_t add_do_bit(struct dns_header *header, size_t plen, char *limit) + { +- return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1); ++ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1); + } + #endif + +@@ -1525,16 +1519,16 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now) + /* return zero if we can't answer from cache, or packet size if we can */ + size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + struct in_addr local_addr, struct in_addr local_netmask, +- time_t now, int *ad_reqd, int *do_bit) ++ time_t now, int ad_reqd, int do_bit, int have_pseudoheader) + { + char *name = daemon->namebuff; +- unsigned char *p, *ansp, *pheader; ++ unsigned char *p, *ansp; + unsigned int qtype, qclass; + struct all_addr addr; + int nameoffset; + unsigned short flag; + int q, ans, anscount = 0, addncount = 0; +- int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0; ++ int dryrun = 0; + struct crec *crecp; + int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; + struct mx_srv_record *rec; +@@ -1550,35 +1544,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + if (header->hb4 & HB4_CD) + sec_data = 0; + +- /* RFC 6840 5.7 */ +- *ad_reqd = header->hb4 & HB4_AD; +- *do_bit = 0; +- + /* 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. */ +- + if (ntohs(header->arcount) != 0) +- { +- dryrun = 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; + + for (rec = daemon->mxnames; rec; rec = rec->next) + rec->offset = 0; +@@ -1603,11 +1573,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + GETSHORT(qtype, p); + GETSHORT(qclass, p); + +- /* Don't filter RRSIGS from answers to ANY queries, even if do-bit +- not set. */ +- if (qtype == T_ANY) +- *do_bit = 1; +- + ans = 0; /* have we answered this question */ + + if (qtype == T_TXT || qtype == T_ANY) +@@ -1739,7 +1704,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + the zone is unsigned, which implies that we're doing + validation. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || +- !sec_reqd || ++ !do_bit || + (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) + { + do +@@ -1927,7 +1892,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + } + + /* If the client asked for DNSSEC don't use cached data. */ +- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK)) ++ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK)) + do + { + /* don't answer wildcard queries with data not from /etc/hosts +@@ -1961,17 +1926,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + + if (crecp->flags & F_NEG) + { +- /* We don't cache NSEC records, so if a DNSSEC-validated negative answer +- is cached and the client wants DNSSEC, forward rather than answering from the cache */ +- if (!sec_reqd || !(crecp->flags & F_DNSSECOK)) +- { +- ans = 1; +- auth = 0; +- if (crecp->flags & F_NXDOMAIN) +- nxdomain = 1; +- if (!dryrun) +- log_query(crecp->flags, name, NULL, NULL); +- } ++ ans = 1; ++ auth = 0; ++ if (crecp->flags & F_NXDOMAIN) ++ nxdomain = 1; ++ if (!dryrun) ++ log_query(crecp->flags, name, NULL, NULL); + } + else + { +@@ -2209,10 +2169,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + + len = ansp - (unsigned char *)header; + ++ /* Advertise our packet size limit in our reply */ + if (have_pseudoheader) +- len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd); ++ len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit); + +- if (*ad_reqd && sec_data) ++ if (ad_reqd && sec_data) + header->hb4 |= HB4_AD; + else + header->hb4 &= ~HB4_AD; +diff --git a/src/rrfilter.c b/src/rrfilter.c +index ae12261..b26b39f 100644 +--- a/src/rrfilter.c ++++ b/src/rrfilter.c +@@ -243,7 +243,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) + for (p = rrs[0], i = 1; i < rr_found; i += 2) + { + unsigned char *start = rrs[i]; +- unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen; ++ unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen; + + memmove(p, start, end-start); + p += end-start; +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch b/src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch new file mode 100644 index 0000000..910921b --- /dev/null +++ b/src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch @@ -0,0 +1,262 @@ +From d67ecac59d58f249707d26e38d49c29b552af4d8 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 20:44:23 +0000 +Subject: [PATCH] More tweaks in handling unknown DNSSEC algorithms. + +--- + src/dnssec.c | 128 +++++++++++++++++++++++++++++----------------------------- + 1 file changed, 63 insertions(+), 65 deletions(-) + +diff --git a/src/dnssec.c b/src/dnssec.c +index 299ca64..e09f304 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -70,7 +70,17 @@ static char *algo_digest_name(int algo) + default: return NULL; + } + } +- ++ ++/* http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-paramet... */ ++static char *nsec3_digest_name(int digest) ++{ ++ switch (digest) ++ { ++ case 1: return "sha1"; ++ default: return NULL; ++ } ++} ++ + /* Find pointer to correct hash function in nettle library */ + static const struct nettle_hash *hash_find(char *name) + { +@@ -667,7 +677,6 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int + static int rrset_sz = 0, sig_sz = 0; + unsigned char *p; + int rrsetidx, sigidx, j, rdlen, res; +- int name_labels = count_labels(name); /* For 4035 5.3.2 check */ + int gotkey = 0; + + if (!(p = skip_questions(header, plen))) +@@ -678,7 +687,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int + j != 0; j--) + { + unsigned char *pstart, *pdata; +- int stype, sclass, algo, type_covered, labels, sig_expiration, sig_inception; ++ int stype, sclass, type_covered; + + pstart = p; + +@@ -712,12 +721,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int + return 0; /* bad packet */ + + GETSHORT(type_covered, p); +- algo = *p++; +- labels = *p++; +- p += 4; /* orig_ttl */ +- GETLONG(sig_expiration, p); +- GETLONG(sig_inception, p); +- p += 2; /* key_tag */ ++ p += 16; /* algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag */ + + if (gotkey) + { +@@ -749,11 +753,8 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int + } + } + +- /* Don't count signatures for algos we don't support */ +- if (check_date_range(sig_inception, sig_expiration) && +- labels <= name_labels && +- type_covered == type && +- verify_func(algo)) ++ ++ if (type_covered == type) + { + if (!expand_workspace(&sigs, &sig_sz, sigidx)) + return 0; +@@ -795,7 +796,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in + char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in) + { + unsigned char *p; +- int rdlen, j, name_labels; ++ int rdlen, j, name_labels, sig_expiration, sig_inception; + struct crec *crecp = NULL; + int algo, labels, orig_ttl, key_tag; + u16 *rr_desc = rrfilter_desc(type); +@@ -828,13 +829,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in + algo = *p++; + labels = *p++; + GETLONG(orig_ttl, p); +- p += 8; /* sig_expiration, sig_inception already checked */ ++ GETLONG(sig_expiration, p); ++ GETLONG(sig_inception, p); + GETSHORT(key_tag, p); + + if (!extract_name(header, plen, &p, keyname, 1, 0)) + return STAT_BOGUS; + +- if (!(hash = hash_find(algo_digest_name(algo))) || ++ if (!check_date_range(sig_inception, sig_expiration) || ++ labels > name_labels || ++ !(hash = hash_find(algo_digest_name(algo))) || + !hash_init(hash, &ctx, &digest)) + continue; + +@@ -1112,7 +1116,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch + else + { + a.addr.keytag = keytag; +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u"); ++ if (verify_func(algo)) ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u"); ++ else ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u (not supported)"); + + recp1->addr.key.keylen = rdlen - 4; + recp1->addr.key.keydata = key; +@@ -1235,7 +1242,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char + else + { + a.addr.keytag = keytag; +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); ++ if (hash_find(ds_digest_name(digest)) && verify_func(algo)) ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); ++ else ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u (not supported)"); ++ + crecp->addr.ds.digest = digest; + crecp->addr.ds.keydata = key; + crecp->addr.ds.algo = algo; +@@ -1660,7 +1671,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + *nons = 1; + + /* Look though the NSEC3 records to find the first one with +- an algorithm we support (currently only algo == 1). ++ an algorithm we support. + + Take the algo, iterations, and salt of that record + as the ones we're going to use, and prune any +@@ -1674,7 +1685,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + p += 10; /* type, class, TTL, rdlen */ + algo = *p++; + +- if (algo == 1) ++ if ((hash = hash_find(nsec3_digest_name(algo)))) + break; /* known algo */ + } + +@@ -1724,10 +1735,6 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + nsecs[i] = nsec3p; + } + +- /* Algo is checked as 1 above */ +- if (!(hash = hash_find("sha1"))) +- return 0; +- + if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0) + return 0; + +@@ -1843,8 +1850,10 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key + + if (type_found == T_NSEC) + return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons); +- else ++ else if (type_found == T_NSEC3) + return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons); ++ else ++ return 0; + } + + /* Check signing status of name. +@@ -1857,7 +1866,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key + */ + static int zone_status(char *name, int class, char *keyname, time_t now) + { +- int secure_ds, name_start = strlen(name); ++ int name_start = strlen(name); + struct crec *crecp; + char *p; + +@@ -1867,51 +1876,40 @@ static int zone_status(char *name, int class, char *keyname, time_t now) + + if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS))) + return STAT_NEED_DS; ++ ++ /* F_DNSSECOK misused in DS cache records to non-existance of NS record. ++ F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here, ++ but that's because there's no NS record either, ie this isn't the start ++ of a zone. We only prove that the DNS tree below a node is unsigned when ++ we prove that we're at a zone cut AND there's no DS record. */ ++ if (crecp->flags & F_NEG) ++ { ++ if (crecp->flags & F_DNSSECOK) ++ return STAT_INSECURE; /* proved no DS here */ ++ } + else + { +- secure_ds = 0; +- ++ int gotone = 0; ++ ++ /* If all the DS records have digest and/or sig algos we don't support, ++ then the zone is insecure. Note that if an algo ++ appears in the DS, then RRSIGs for that algo MUST ++ exist for each RRset: 4035 para 2.2 So if we find ++ a DS here with digest and sig we can do, we're entitled ++ to assume we can validate the zone and if we can't later, ++ because an RRSIG is missing we return BOGUS. ++ */ + do + { +- if (crecp->uid == (unsigned int)class) +- { +- /* F_DNSSECOK misused in DS cache records to non-existance of NS record. +- F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here, +- but that's because there's no NS record either, ie this isn't the start +- of a zone. We only prove that the DNS tree below a node is unsigned when +- we prove that we're at a zone cut AND there's no DS record. +- */ +- if (crecp->flags & F_NEG) +- { +- if (crecp->flags & F_DNSSECOK) +- return STAT_INSECURE; /* proved no DS here */ +- } +- 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; +- } ++ if (crecp->uid == (unsigned int)class && ++ hash_find(ds_digest_name(crecp->addr.ds.digest)) && ++ verify_func(crecp->addr.ds.algo)) ++ gotone = 1; + } + while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS))); +- } +- +- if (secure_ds) +- { +- /* We've found only DS records that attest to the DNSKEY RRset in the zone, so we believe +- that RRset is good. Furthermore the DNSKEY whose hash is proved by the DS record is +- one we can use. However the DNSKEY RRset may contain more than one key and +- one of the other keys may use an algorithm we don't support. If that's +- the case the zone is insecure for us. */ +- +- if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY))) +- return STAT_NEED_KEY; + +- do +- { +- 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))); ++ if (!gotone) ++ return STAT_INSECURE; + } + + if (name_start == 0) +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch b/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch new file mode 100644 index 0000000..031339e --- /dev/null +++ b/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch @@ -0,0 +1,27 @@ +From 3e86d316c4bb406ed813aa5256615c8a95cac6d8 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 20:50:05 +0000 +Subject: [PATCH] Nasty, rare and obscure off-by-one in DNSSEC hostname_cmp(). + +--- + src/dnssec.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/dnssec.c b/src/dnssec.c +index e09f304..29848e1 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1394,8 +1394,8 @@ static int hostname_cmp(const char *a, const char *b) + if (sb == b) + return 1; + +- ea = sa--; +- eb = sb--; ++ ea = --sa; ++ eb = --sb; + } + } + +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch b/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch new file mode 100644 index 0000000..f3758fc --- /dev/null +++ b/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch @@ -0,0 +1,39 @@ +From a86fdf437ecc29398f9715ceb5240442a17ac014 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 21:19:20 +0000 +Subject: [PATCH] Minor tweak to previous commit. + +--- + src/dnssec.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/dnssec.c b/src/dnssec.c +index 29848e1..9fa64b6 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1889,8 +1889,6 @@ static int zone_status(char *name, int class, char *keyname, time_t now) + } + else + { +- int gotone = 0; +- + /* If all the DS records have digest and/or sig algos we don't support, + then the zone is insecure. Note that if an algo + appears in the DS, then RRSIGs for that algo MUST +@@ -1904,11 +1902,11 @@ static int zone_status(char *name, int class, char *keyname, time_t now) + if (crecp->uid == (unsigned int)class && + hash_find(ds_digest_name(crecp->addr.ds.digest)) && + verify_func(crecp->addr.ds.algo)) +- gotone = 1; ++ break; + } + while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS))); + +- if (!gotone) ++ if (!crecp) + return STAT_INSECURE; + } + +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch b/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch new file mode 100644 index 0000000..33219d2 --- /dev/null +++ b/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch @@ -0,0 +1,39 @@ +From ce5732e84fc46d7f99c152f736cfb4ef5ec98a01 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 21:39:19 +0000 +Subject: [PATCH] NSEC3 check: RFC5155 para 8.2 + +--- + src/dnssec.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/dnssec.c b/src/dnssec.c +index 9fa64b6..486e422 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1704,7 +1704,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + for (i = 0; i < nsec_count; i++) + { + unsigned char *nsec3p = nsecs[i]; +- int this_iter; ++ int this_iter, flags; + + nsecs[i] = NULL; /* Speculative, will be restored if OK. */ + +@@ -1716,8 +1716,12 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + if (*p++ != algo) + continue; + +- p++; /* flags */ ++ flags = *p++; /* flags */ + ++ /* 5155 8.2 */ ++ if (flags != 0 && flags != 1) ++ continue; ++ + GETSHORT(this_iter, p); + if (this_iter != iterations) + continue; +-- +1.7.10.4 +
Merged.
Can we have these updates like once per Core Update?
Best, -Michael
On Thu, 2015-12-24 at 10:17 +0100, Matthias Fischer wrote:
Same procedure as... :-)
Best to all for xmas and 2016!
Matthias
Signed-off-by: Matthias Fischer matthias.fischer@ipfire.org
lfs/dnsmasq | 5 + ...EDNS0_handling_and_computation_use_of_udp.patch | 643 +++++++++++++++++++++ ...aks_in_handling_unknown_DNSSEC_algorithms.patch | 262 +++++++++ ...obscure_off-by-one_in_DNSSEC_hostname_cmp.patch | 27 + .../028-Minor_tweak_to_previous_commit.patch | 39 ++ .../dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch | 39 ++ 6 files changed, 1015 insertions(+) create mode 100644 src/patches/dnsmasq/025- Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch create mode 100644 src/patches/dnsmasq/026- More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch create mode 100644 src/patches/dnsmasq/027- Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch create mode 100644 src/patches/dnsmasq/028- Minor_tweak_to_previous_commit.patch create mode 100644 src/patches/dnsmasq/029- NSEC3_check_RFC5155_para_8_2.patch
diff --git a/lfs/dnsmasq b/lfs/dnsmasq index c8fd7db..8058663 100644 --- a/lfs/dnsmasq +++ b/lfs/dnsmasq @@ -97,6 +97,11 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) 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.p atch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/025- Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/026- More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by- one_in_DNSSEC_hostname_cmp.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/028- Minor_tweak_to_previous_commit.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.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/025- Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch b/src/patches/dnsmasq/025- Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch new file mode 100644 index 0000000..c016e73 --- /dev/null +++ b/src/patches/dnsmasq/025- Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch @@ -0,0 +1,643 @@ +From fa14bec83b2db010fd076910fddab56957b9375d Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 17:12:16 +0000 +Subject: [PATCH] Major tidy up of EDNS0 handling and computation/use of udp
- packet size.
+---
- src/auth.c | 8 ++-
- src/dnsmasq.h | 7 ++-
- src/dnssec.c | 1 -
- src/forward.c | 184 ++++++++++++++++++++++++++++++++++++++++-----
- src/netlink.c | 3 +-
- src/rfc1035.c | 81 +++++++------------------
- src/rrfilter.c | 2 +-
- 7 files changed, 168 insertions(+), 118 deletions(-)
+diff --git a/src/auth.c b/src/auth.c +index 2b0b7d6..85bd5e7 100644 +--- a/src/auth.c ++++ b/src/auth.c +@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut)
- }
+ + +-size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query) ++size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, ++ int local_query, int do_bit, int have_pseudoheader)
- {
+ char *name = daemon->namebuff; + unsigned char *p, *ansp; +@@ -820,6 +821,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n + header->ancount = htons(anscount); + header->nscount = htons(authcount); + header->arcount = htons(0); ++ ++ /* Advertise our packet size limit in our reply */ ++ if (have_pseudoheader) ++ return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit); ++ + return ansp - (unsigned char *)header;
- }
+ +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 39a930c..abb34c5 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1113,7 +1113,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff, + int no_cache, int secure, int *doctored);
- size_t answer_request(struct dns_header *header, char *limit,
size_t qlen, + struct in_addr local_addr, struct in_addr local_netmask, +- time_t now, int *ad_reqd, int *do_bit); ++ time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
- int check_for_bogus_wildcard(struct dns_header *header, size_t
qlen, char *name, + struct bogus_addr *addr, time_t now);
- int check_for_ignored_address(struct dns_header *header, size_t
qlen, struct bogus_addr *baddr); +@@ -1123,6 +1123,8 @@ int check_for_local_domain(char *name, time_t now);
- unsigned int questions_crc(struct dns_header *header, size_t plen,
char *buff);
- size_t resize_packet(struct dns_header *header, size_t plen,
+ unsigned char *pheader, size_t hlen); ++size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, ++ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do);
- size_t add_mac(struct dns_header *header, size_t plen, char *limit,
union mysockaddr *l3);
- size_t add_source_addr(struct dns_header *header, size_t plen, char
*limit, union mysockaddr *source);
- #ifdef HAVE_DNSSEC
+@@ -1141,7 +1143,8 @@ int private_net(struct in_addr addr, int ban_localhost);
- /* auth.c */
- #ifdef HAVE_AUTH
- size_t answer_auth(struct dns_header *header, char *limit, size_t
qlen, +- time_t now, union mysockaddr *peer_addr, int local_query); ++ time_t now, union mysockaddr *peer_addr, int local_query, ++ int do_bit, int have_pseudoheader);
- int in_zone(struct auth_zone *zone, char *name, char **cut);
- #endif
+ +diff --git a/src/dnssec.c b/src/dnssec.c +index 82394ee..299ca64 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -67,7 +67,6 @@ static char *algo_digest_name(int algo) + case 12: return "gosthash94"; + case 13: return "sha256"; + case 14: return "sha384"; +- + default: return NULL; + }
- }
+diff --git a/src/forward.c b/src/forward.c +index 3e801c8..041353c 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -244,7 +244,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + void *hash = &crc;
- #endif
+ unsigned int gotname = extract_request(header, plen, daemon-
namebuff, NULL);
+- unsigned char *pheader; + + (void)do_bit; + +@@ -264,7 +263,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + there's no point retrying the query, retry the key query instead...... */ + if (forward->blocking_query) + { +- int fd; ++ int fd, is_sign; ++ unsigned char *pheader; + + forward->flags &= ~FREC_TEST_PKTSZ; + +@@ -276,8 +276,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); + plen = forward->stash_len; + +- if (find_pseudoheader(header, plen, NULL, &pheader, NULL)) +- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader); ++ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign) ++ PUTSHORT(SAFE_PKTSZ, pheader); + + if (forward->sentto->addr.sa.sa_family == AF_INET) + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); +@@ -394,32 +394,40 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + forward->log_id = daemon->log_id; + + if (option_bool(OPT_ADD_MAC)) +- plen = add_mac(header, plen, ((char *) header) + daemon-
packet_buff_sz, &forward->source);
+- ++ { ++ size_t new = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); ++ if (new != plen) ++ { ++ plen = new; ++ forward->flags |= FREC_ADDED_PHEADER; ++ } ++ } ++ + if (option_bool(OPT_CLIENT_SUBNET)) + { + size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); + if (new != plen) + { + plen = new; +- forward->flags |= FREC_HAS_SUBNET; ++ forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER; + } + } +
- #ifdef HAVE_DNSSEC
+ if (option_bool(OPT_DNSSEC_VALID)) + { +- size_t new_plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz); ++ size_t new = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz); + ++ if (new != plen) ++ forward->flags |= FREC_ADDED_PHEADER; ++ ++ plen = new; ++ + /* For debugging, set Checking Disabled, otherwise, have the upstream check too, + this allows it to select auth servers when one is returning bad data. */ + if (option_bool(OPT_DNSSEC_DEBUG)) + header->hb4 |= HB4_CD; + +- if (new_plen != plen) +- forward->flags |= FREC_ADDED_PHEADER; +- +- plen = new_plen; + }
- #endif
+ +@@ -469,10 +477,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + }
- #endif
+ } +- +- if (find_pseudoheader(header, plen, NULL, &pheader, NULL)) +- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader); + ++#ifdef HAVE_DNSSEC ++ if (option_bool(OPT_DNSSEC_VALID) && !do_bit) ++ { ++ /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP ++ packet size to 512. But that won't provide space for the RRSIGS in many cases. ++ The RRSIGS will be stripped out before the answer goes back, so the packet should ++ shrink again. So, if we added a do-bit, bump the udp packet size to the value ++ known to be OK for this server. Maybe check returned size after stripping and set ++ the truncated bit? */ ++ unsigned char *pheader; ++ int is_sign; ++ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign)) ++ PUTSHORT(start->edns_pktsz, pheader); ++ } ++#endif ++ + if (retry_send(sendto(fd, (char *)header, plen, 0, + &start->addr.sa, + sa_len(&start->addr)))) +@@ -563,30 +584,34 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server + }
- #endif
+ +- /* If upstream is advertising a larger UDP packet size +- than we allow, trim it so that we don't get overlarge +- requests for the client. We can't do this for signed packets. */ +- + if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign))) + { +- unsigned short udpsz; +- unsigned char *psave = sizep; +- +- GETSHORT(udpsz, sizep); +- +- if (!is_sign && udpsz > daemon->edns_pktsz) +- PUTSHORT(daemon->edns_pktsz, psave); +- + if (check_subnet && !check_source(header, plen, pheader, query_source)) + { + my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); + return 0; + } + +- if (added_pheader) ++ if (!is_sign) + { +- pheader = 0; +- header->arcount = htons(0); ++ if (added_pheader) ++ { ++ /* client didn't send EDNS0, we added one, strip it off before returning answer. */ ++ n = rrfilter(header, n, 0); ++ pheader = NULL; ++ } ++ else ++ { ++ /* If upstream is advertising a larger UDP packet size ++ than we allow, trim it so that we don't get overlarge ++ requests for the client. We can't do this for signed packets. */ ++ unsigned short udpsz; ++ unsigned char *psave = sizep; ++ ++ GETSHORT(udpsz, sizep); ++ if (udpsz > daemon->edns_pktsz) ++ PUTSHORT(daemon->edns_pktsz, psave); ++ } + } + } + +@@ -655,14 +680,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server + } + + if (option_bool(OPT_DNSSEC_VALID)) +- header->hb4 &= ~HB4_AD; +- +- if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) +- header->hb4 |= HB4_AD; +- +- /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ +- if (!do_bit) +- n = rrfilter(header, n, 1); ++ { ++ header->hb4 &= ~HB4_AD; ++ ++ if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) ++ header->hb4 |= HB4_AD; ++ ++ /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ ++ if (!do_bit) ++ n = rrfilter(header, n, 1); ++ }
- #endif
+ + /* do this after extract_addresses. Ensure NODATA reply and remove +@@ -761,8 +788,14 @@ void reply_query(int fd, int family, time_t now) + if ((nn = resize_packet(header, (size_t)n, pheader, plen))) + { + header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC); +- header->hb4 &= ~(HB4_RA | HB4_RCODE); +- forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0); ++ header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD); ++ if (forward->flags |= FREC_CHECKING_DISABLED) ++ header->hb4 |= HB4_CD; ++ if (forward->flags |= FREC_AD_QUESTION) ++ header->hb4 |= HB4_AD; ++ if (forward->flags & FREC_DO_QUESTION) ++ add_do_bit(header, nn, (char *)pheader + plen); ++ forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION); + return; + } + } +@@ -1007,12 +1040,13 @@ void receive_query(struct listener *listen, time_t now)
- {
+ struct dns_header *header = (struct dns_header *)daemon->packet; + union mysockaddr source_addr; +- unsigned short type; ++ unsigned char *pheader; ++ unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */ + struct all_addr dst_addr; + struct in_addr netmask, dst_addr_4; + size_t m; + ssize_t n; +- int if_index = 0, auth_dns = 0; ++ int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0;
- #ifdef HAVE_AUTH
+ int local_auth = 0;
- #endif
+@@ -1279,10 +1313,30 @@ void receive_query(struct listener *listen, time_t now)
- #endif
+ } + ++ if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL)) ++ { ++ unsigned short flags; ++ ++ have_pseudoheader = 1; ++ GETSHORT(udp_size, pheader); ++ pheader += 2; /* ext_rcode */ ++ GETSHORT(flags, pheader); ++ ++ if (flags & 0x8000) ++ do_bit = 1;/* do bit */ ++ ++ /* If the client provides an EDNS0 UDP size, use that to limit our reply. ++ (bounded by the maximum configured). If no EDNS0, then it ++ defaults to 512 */ ++ if (udp_size > daemon->edns_pktsz) ++ udp_size = daemon->edns_pktsz; ++ } ++
- #ifdef HAVE_AUTH
+ if (auth_dns) + { +- m = answer_auth(header, ((char *) header) + daemon-
packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
++ m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr, ++ local_auth, do_bit, have_pseudoheader); + if (m >= 1) + { + send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), +@@ -1293,9 +1347,13 @@ void receive_query(struct listener *listen, time_t now) + else
- #endif
+ { +- int ad_reqd, do_bit; +- m = answer_request(header, ((char *) header) + daemon-
packet_buff_sz, (size_t)n,
+- dst_addr_4, netmask, now, &ad_reqd, &do_bit); ++ int ad_reqd = do_bit; ++ /* RFC 6840 5.7 */ ++ if (header->hb4 & HB4_AD) ++ ad_reqd = 1; ++ ++ m = answer_request(header, ((char *) header) + udp_size, (size_t)n, ++ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader); + + if (m >= 1) + { +@@ -1397,7 +1455,7 @@ unsigned char *tcp_request(int confd, time_t now,
- #ifdef HAVE_AUTH
+ int local_auth = 0;
- #endif
+- int checking_disabled, ad_question, do_bit, added_pheader = 0; ++ int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0; + int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; + size_t m; + unsigned short qtype; +@@ -1414,6 +1472,7 @@ unsigned char *tcp_request(int confd, time_t now, + union mysockaddr peer_addr; + socklen_t peer_len = sizeof(union mysockaddr); + int query_count = 0; ++ unsigned char *pheader; + + if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) + return packet; +@@ -1508,15 +1567,35 @@ unsigned char *tcp_request(int confd, time_t now, + else + dst_addr_4.s_addr = 0; + ++ do_bit = 0; ++ ++ if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL)) ++ { ++ unsigned short flags; ++ ++ have_pseudoheader = 1; ++ pheader += 4; /* udp_size, ext_rcode */ ++ GETSHORT(flags, pheader); ++ ++ if (flags & 0x8000) ++ do_bit = 1;/* do bit */ ++ } ++
- #ifdef HAVE_AUTH
+ if (auth_dns) +- m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth); ++ m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, ++ local_auth, do_bit, have_pseudoheader); + else
- #endif
+ { +- /* m > 0 if answered from cache */ +- m = answer_request(header, ((char *) header) + 65536, (size_t)size, +- dst_addr_4, netmask, now, &ad_question, &do_bit); ++ int ad_reqd = do_bit; ++ /* RFC 6840 5.7 */ ++ if (header->hb4 & HB4_AD) ++ ad_reqd = 1; ++ ++ /* m > 0 if answered from cache */ ++ m = answer_request(header, ((char *) header) + 65536, (size_t)size, ++ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader); + + /* Do this by steam now we're not in the select() loop */ + check_log_writer(1); +@@ -1615,6 +1694,7 @@ unsigned char *tcp_request(int confd, time_t now, + } +
- #ifdef HAVE_DNSSEC
++ added_pheader = 0; + if (option_bool(OPT_DNSSEC_VALID)) + { + size_t new_size = add_do_bit(header, size, ((char *) header) + 65536); +@@ -1719,7 +1799,7 @@ unsigned char *tcp_request(int confd, time_t now, + + m = process_reply(header, now, last_server, (unsigned int)m, + option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, +- ad_question, do_bit, added_pheader, check_subnet, &peer_addr); ++ ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr); + + break; + } +diff --git a/src/netlink.c b/src/netlink.c +index 753784d..3376d68 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + rta = RTA_NEXT(rta, len1); + } + +- if (inaddr && mac && callback_ok) ++ if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) && ++ inaddr && mac && callback_ok) + if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm))) + callback_ok = 0; + } +diff --git a/src/rfc1035.c b/src/rfc1035.c +index 188d05f..18858a8 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -489,8 +489,8 @@ struct macparm { + union mysockaddr *l3;
- };
+ +-static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, +- int optno, unsigned char *opt, size_t optlen, int set_do) ++size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, ++ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
- {
+ unsigned char *lenp, *datap, *p; + int rdlen, is_sign; +@@ -508,7 +508,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned + return plen; + *p++ = 0; /* empty name */ + PUTSHORT(T_OPT, p); +- PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be overwritten */ ++ PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */ + PUTSHORT(0, p); /* extended RCODE and version */ + PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */ + lenp = p; +@@ -594,7 +594,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p + if (!match) + return 1; /* continue */ + +- parm->plen = add_pseudoheader(parm->header, parm->plen, parm-
limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
++ parm->plen = add_pseudoheader(parm->header, parm->plen, parm-
limit, PACKETSZ, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
+ + return 0; /* done */
- }
+@@ -603,12 +603,6 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock
- {
+ struct macparm parm; + +-/* Must have an existing pseudoheader as the only ar-record, +- or have no ar-records. Must also not be signed */ +- +- if (ntohs(header->arcount) > 1) +- return plen; +- + parm.header = header; + parm.limit = (unsigned char *)limit; + parm.plen = plen; +@@ -699,13 +693,13 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio + struct subnet_opt opt; + + len = calc_subnet_opt(&opt, source); +- return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0); ++ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
- }
+
- #ifdef HAVE_DNSSEC
- size_t add_do_bit(struct dns_header *header, size_t plen, char
*limit)
- {
+- return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1); ++ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1);
- }
- #endif
+ +@@ -1525,16 +1519,16 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
- /* return zero if we can't answer from cache, or packet size if we
can */
- size_t answer_request(struct dns_header *header, char *limit,
size_t qlen, + struct in_addr local_addr, struct in_addr local_netmask, +- time_t now, int *ad_reqd, int *do_bit) ++ time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
- {
+ char *name = daemon->namebuff; +- unsigned char *p, *ansp, *pheader; ++ unsigned char *p, *ansp; + unsigned int qtype, qclass; + struct all_addr addr; + int nameoffset; + unsigned short flag; + int q, ans, anscount = 0, addncount = 0; +- int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0; ++ int dryrun = 0; + struct crec *crecp; + int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; + struct mx_srv_record *rec; +@@ -1550,35 +1544,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + if (header->hb4 & HB4_CD) + sec_data = 0; + +- /* RFC 6840 5.7 */ +- *ad_reqd = header->hb4 & HB4_AD; +- *do_bit = 0; +- + /* 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. */ +- + if (ntohs(header->arcount) != 0) +- { +- dryrun = 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; + + for (rec = daemon->mxnames; rec; rec = rec->next) + rec->offset = 0; +@@ -1603,11 +1573,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + GETSHORT(qtype, p); + GETSHORT(qclass, p); + +- /* Don't filter RRSIGS from answers to ANY queries, even if do-bit +- not set. */ +- if (qtype == T_ANY) +- *do_bit = 1; +- + ans = 0; /* have we answered this question */ + + if (qtype == T_TXT || qtype == T_ANY) +@@ -1739,7 +1704,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + the zone is unsigned, which implies that we're doing + validation. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || +- !sec_reqd || ++ !do_bit || + (option_bool(OPT_DNSSEC_VALID) && !(crecp-
flags & F_DNSSECOK)))
+ { + do +@@ -1927,7 +1892,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + } + + /* If the client asked for DNSSEC don't use cached data. */ +- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK)) ++ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK)) + do + { + /* don't answer wildcard queries with data not from /etc/hosts +@@ -1961,17 +1926,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + + if (crecp->flags & F_NEG) + { +- /* We don't cache NSEC records, so if a DNSSEC-validated negative answer +- is cached and the client wants DNSSEC, forward rather than answering from the cache */ +- if (!sec_reqd || !(crecp->flags & F_DNSSECOK)) +- { +- ans = 1; +- auth = 0; +- if (crecp->flags & F_NXDOMAIN) +- nxdomain = 1; +- if (!dryrun) +- log_query(crecp->flags, name, NULL, NULL); +- } ++ ans = 1; ++ auth = 0; ++ if (crecp->flags & F_NXDOMAIN) ++ nxdomain = 1; ++ if (!dryrun) ++ log_query(crecp->flags, name, NULL, NULL); + } + else + { +@@ -2209,10 +2169,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + + len = ansp - (unsigned char *)header; + ++ /* Advertise our packet size limit in our reply */ + if (have_pseudoheader) +- len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd); ++ len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit); + +- if (*ad_reqd && sec_data) ++ if (ad_reqd && sec_data) + header->hb4 |= HB4_AD; + else + header->hb4 &= ~HB4_AD; +diff --git a/src/rrfilter.c b/src/rrfilter.c +index ae12261..b26b39f 100644 +--- a/src/rrfilter.c ++++ b/src/rrfilter.c +@@ -243,7 +243,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) + for (p = rrs[0], i = 1; i < rr_found; i += 2) + { + unsigned char *start = rrs[i]; +- unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen; ++ unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen; + + memmove(p, start, end-start); + p += end-start; +-- +1.7.10.4
diff --git a/src/patches/dnsmasq/026- More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch b/src/patches/dnsmasq/026- More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch new file mode 100644 index 0000000..910921b --- /dev/null +++ b/src/patches/dnsmasq/026- More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch @@ -0,0 +1,262 @@ +From d67ecac59d58f249707d26e38d49c29b552af4d8 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 20:44:23 +0000 +Subject: [PATCH] More tweaks in handling unknown DNSSEC algorithms.
+---
- src/dnssec.c | 128 +++++++++++++++++++++++++++++----------------
- 1 file changed, 63 insertions(+), 65 deletions(-)
+diff --git a/src/dnssec.c b/src/dnssec.c +index 299ca64..e09f304 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -70,7 +70,17 @@ static char *algo_digest_name(int algo) + default: return NULL; + }
- }
+- ++ ++/* http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-n sec3-parameters.xhtml */ ++static char *nsec3_digest_name(int digest) ++{ ++ switch (digest) ++ { ++ case 1: return "sha1"; ++ default: return NULL; ++ } ++} ++
- /* Find pointer to correct hash function in nettle library */
- static const struct nettle_hash *hash_find(char *name)
- {
+@@ -667,7 +677,6 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int + static int rrset_sz = 0, sig_sz = 0; + unsigned char *p; + int rrsetidx, sigidx, j, rdlen, res; +- int name_labels = count_labels(name); /* For 4035 5.3.2 check */ + int gotkey = 0; + + if (!(p = skip_questions(header, plen))) +@@ -678,7 +687,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int + j != 0; j--) + { + unsigned char *pstart, *pdata; +- int stype, sclass, algo, type_covered, labels, sig_expiration, sig_inception; ++ int stype, sclass, type_covered; + + pstart = p; + +@@ -712,12 +721,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int + return 0; /* bad packet */ + + GETSHORT(type_covered, p); +- algo = *p++; +- labels = *p++; +- p += 4; /* orig_ttl */ +- GETLONG(sig_expiration, p); +- GETLONG(sig_inception, p); +- p += 2; /* key_tag */ ++ p += 16; /* algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag */ + + if (gotkey) + { +@@ -749,11 +753,8 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int + } + } + +- /* Don't count signatures for algos we don't support */ +- if (check_date_range(sig_inception, sig_expiration) && +- labels <= name_labels && +- type_covered == type && +- verify_func(algo)) ++ ++ if (type_covered == type) + { + if (!expand_workspace(&sigs, &sig_sz, sigidx)) + return 0; +@@ -795,7 +796,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in + char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
- {
+ unsigned char *p; +- int rdlen, j, name_labels; ++ int rdlen, j, name_labels, sig_expiration, sig_inception; + struct crec *crecp = NULL; + int algo, labels, orig_ttl, key_tag; + u16 *rr_desc = rrfilter_desc(type); +@@ -828,13 +829,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in + algo = *p++; + labels = *p++; + GETLONG(orig_ttl, p); +- p += 8; /* sig_expiration, sig_inception already checked */ ++ GETLONG(sig_expiration, p); ++ GETLONG(sig_inception, p); + GETSHORT(key_tag, p); + + if (!extract_name(header, plen, &p, keyname, 1, 0)) + return STAT_BOGUS; + +- if (!(hash = hash_find(algo_digest_name(algo))) || ++ if (!check_date_range(sig_inception, sig_expiration) || ++ labels > name_labels || ++ !(hash = hash_find(algo_digest_name(algo))) || + !hash_init(hash, &ctx, &digest)) + continue; + +@@ -1112,7 +1116,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch + else + { + a.addr.keytag = keytag; +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u"); ++ if (verify_func(algo)) ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u"); ++ else ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u (not supported)"); + + recp1->addr.key.keylen = rdlen - 4; + recp1->addr.key.keydata = key; +@@ -1235,7 +1242,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char + else + { + a.addr.keytag = keytag; +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); ++ if (hash_find(ds_digest_name(digest)) && verify_func(algo)) ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); ++ else ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u (not supported)"); ++ + crecp->addr.ds.digest = digest; + crecp->addr.ds.keydata = key; + crecp->addr.ds.algo = algo; +@@ -1660,7 +1671,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + *nons = 1; + + /* Look though the NSEC3 records to find the first one with +- an algorithm we support (currently only algo == 1). ++ an algorithm we support. + + Take the algo, iterations, and salt of that record + as the ones we're going to use, and prune any +@@ -1674,7 +1685,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + p += 10; /* type, class, TTL, rdlen */ + algo = *p++; + +- if (algo == 1) ++ if ((hash = hash_find(nsec3_digest_name(algo)))) + break; /* known algo */ + } + +@@ -1724,10 +1735,6 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + nsecs[i] = nsec3p; + } + +- /* Algo is checked as 1 above */ +- if (!(hash = hash_find("sha1"))) +- return 0; +- + if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0) + return 0; + +@@ -1843,8 +1850,10 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key + + if (type_found == T_NSEC) + return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons); +- else ++ else if (type_found == T_NSEC3) + return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons); ++ else ++ return 0;
- }
+
- /* Check signing status of name.
+@@ -1857,7 +1866,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
- */
- static int zone_status(char *name, int class, char *keyname, time_t
now)
- {
+- int secure_ds, name_start = strlen(name); ++ int name_start = strlen(name); + struct crec *crecp; + char *p; + +@@ -1867,51 +1876,40 @@ static int zone_status(char *name, int class, char *keyname, time_t now) + + if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS))) + return STAT_NEED_DS; ++ ++ /* F_DNSSECOK misused in DS cache records to non-existance of NS record. ++ F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here, ++ but that's because there's no NS record either, ie this isn't the start ++ of a zone. We only prove that the DNS tree below a node is unsigned when ++ we prove that we're at a zone cut AND there's no DS record. */ ++ if (crecp->flags & F_NEG) ++ { ++ if (crecp->flags & F_DNSSECOK) ++ return STAT_INSECURE; /* proved no DS here */ ++ } + else + { +- secure_ds = 0; +- ++ int gotone = 0; ++ ++ /* If all the DS records have digest and/or sig algos we don't support, ++ then the zone is insecure. Note that if an algo ++ appears in the DS, then RRSIGs for that algo MUST ++ exist for each RRset: 4035 para 2.2 So if we find ++ a DS here with digest and sig we can do, we're entitled ++ to assume we can validate the zone and if we can't later, ++ because an RRSIG is missing we return BOGUS. ++ */ + do + { +- if (crecp->uid == (unsigned int)class) +- { +- /* F_DNSSECOK misused in DS cache records to non- existance of NS record. +- F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here, +- but that's because there's no NS record either, ie this isn't the start +- of a zone. We only prove that the DNS tree below a node is unsigned when +- we prove that we're at a zone cut AND there's no DS record. +- */ +- if (crecp->flags & F_NEG) +- { +- if (crecp->flags & F_DNSSECOK) +- return STAT_INSECURE; /* proved no DS here */ +- } +- 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; +- } ++ if (crecp->uid == (unsigned int)class && ++ hash_find(ds_digest_name(crecp->addr.ds.digest)) && ++ verify_func(crecp->addr.ds.algo)) ++ gotone = 1; + } + while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS))); +- } +- +- if (secure_ds) +- { +- /* We've found only DS records that attest to the DNSKEY RRset in the zone, so we believe +- that RRset is good. Furthermore the DNSKEY whose hash is proved by the DS record is +- one we can use. However the DNSKEY RRset may contain more than one key and +- one of the other keys may use an algorithm we don't support. If that's +- the case the zone is insecure for us. */ +- +- if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY))) +- return STAT_NEED_KEY; + +- do +- { +- 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))); ++ if (!gotone) ++ return STAT_INSECURE; + } + + if (name_start == 0) +-- +1.7.10.4
diff --git a/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by- one_in_DNSSEC_hostname_cmp.patch b/src/patches/dnsmasq/027- Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch new file mode 100644 index 0000000..031339e --- /dev/null +++ b/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by- one_in_DNSSEC_hostname_cmp.patch @@ -0,0 +1,27 @@ +From 3e86d316c4bb406ed813aa5256615c8a95cac6d8 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 20:50:05 +0000 +Subject: [PATCH] Nasty, rare and obscure off-by-one in DNSSEC hostname_cmp().
+---
- src/dnssec.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
+diff --git a/src/dnssec.c b/src/dnssec.c +index e09f304..29848e1 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1394,8 +1394,8 @@ static int hostname_cmp(const char *a, const char *b) + if (sb == b) + return 1; + +- ea = sa--; +- eb = sb--; ++ ea = --sa; ++ eb = --sb; + }
- }
+ +-- +1.7.10.4
diff --git a/src/patches/dnsmasq/028- Minor_tweak_to_previous_commit.patch b/src/patches/dnsmasq/028- Minor_tweak_to_previous_commit.patch new file mode 100644 index 0000000..f3758fc --- /dev/null +++ b/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch @@ -0,0 +1,39 @@ +From a86fdf437ecc29398f9715ceb5240442a17ac014 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 21:19:20 +0000 +Subject: [PATCH] Minor tweak to previous commit.
+---
- src/dnssec.c | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
+diff --git a/src/dnssec.c b/src/dnssec.c +index 29848e1..9fa64b6 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1889,8 +1889,6 @@ static int zone_status(char *name, int class, char *keyname, time_t now) + } + else + { +- int gotone = 0; +- + /* If all the DS records have digest and/or sig algos we don't support, + then the zone is insecure. Note that if an algo + appears in the DS, then RRSIGs for that algo MUST +@@ -1904,11 +1902,11 @@ static int zone_status(char *name, int class, char *keyname, time_t now) + if (crecp->uid == (unsigned int)class && + hash_find(ds_digest_name(crecp->addr.ds.digest)) && + verify_func(crecp->addr.ds.algo)) +- gotone = 1; ++ break; + } + while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS))); + +- if (!gotone) ++ if (!crecp) + return STAT_INSECURE; + } + +-- +1.7.10.4
diff --git a/src/patches/dnsmasq/029- NSEC3_check_RFC5155_para_8_2.patch b/src/patches/dnsmasq/029- NSEC3_check_RFC5155_para_8_2.patch new file mode 100644 index 0000000..33219d2 --- /dev/null +++ b/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch @@ -0,0 +1,39 @@ +From ce5732e84fc46d7f99c152f736cfb4ef5ec98a01 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Sun, 20 Dec 2015 21:39:19 +0000 +Subject: [PATCH] NSEC3 check: RFC5155 para 8.2
+---
- src/dnssec.c | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
+diff --git a/src/dnssec.c b/src/dnssec.c +index 9fa64b6..486e422 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1704,7 +1704,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + for (i = 0; i < nsec_count; i++) + { + unsigned char *nsec3p = nsecs[i]; +- int this_iter; ++ int this_iter, flags; + + nsecs[i] = NULL; /* Speculative, will be restored if OK. */ + +@@ -1716,8 +1716,12 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + if (*p++ != algo) + continue; + +- p++; /* flags */ ++ flags = *p++; /* flags */ + ++ /* 5155 8.2 */ ++ if (flags != 0 && flags != 1) ++ continue; ++ + GETSHORT(this_iter, p); + if (this_iter != iterations) + continue; +-- +1.7.10.4