Signed-off-by: Matthias Fischer <matthias.fischer(a)ipfire.org>
---
lfs/dnsmasq | 4 +
...orwarding_to_private_servers_for_a_domain.patch | 41 ++++
...ors_and_check_we_have_a_root-trust_anchor.patch | 70 +++++++
...ze_calculation_when_hosts-file_read_fails.patch | 41 ++++
...main_servers_unless_trust-anchor_provided.patch | 217 +++++++++++++++++++++
5 files changed, 373 insertions(+)
create mode 100644 src/patches/dnsmasq/045-Inhibit_DNSSEC_validation_when_forwarding_to_private_servers_for_a_domain.patch
create mode 100644 src/patches/dnsmasq/046-DNSSEC_Handle_non-root_trust_anchors_and_check_we_have_a_root-trust_anchor.patch
create mode 100644 src/patches/dnsmasq/047-Fix_bad_cache-size_calculation_when_hosts-file_read_fails.patch
create mode 100644 src/patches/dnsmasq/048-Disable_DNSSEC_for_server_domain_servers_unless_trust-anchor_provided.patch
diff --git a/lfs/dnsmasq b/lfs/dnsmasq
index e145a39..0affcf5 100644
--- a/lfs/dnsmasq
+++ b/lfs/dnsmasq
@@ -117,6 +117,10 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/042-Handle_building_with_script_support_enabled_and_DHCP_disabled.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/043-Update_copyright_notices_Happy_new_year.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/044-Fix_FTBFS_when_scripts_excluded_at_compilation_time.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/045-Inhibit_DNSSEC_validation_when_forwarding_to_private_servers_for_a_domain.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/046-DNSSEC_Handle_non-root_trust_anchors_and_check_we_have_a_root-trust_anchor.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/047-Fix_bad_cache-size_calculation_when_hosts-file_read_fails.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/048-Disable_DNSSEC_for_server_domain_servers_unless_trust-anchor_provided.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/045-Inhibit_DNSSEC_validation_when_forwarding_to_private_servers_for_a_domain.patch b/src/patches/dnsmasq/045-Inhibit_DNSSEC_validation_when_forwarding_to_private_servers_for_a_domain.patch
new file mode 100644
index 0000000..11cf20a
--- /dev/null
+++ b/src/patches/dnsmasq/045-Inhibit_DNSSEC_validation_when_forwarding_to_private_servers_for_a_domain.patch
@@ -0,0 +1,41 @@
+From 5757371d43891e830abe19aacae5378a79c7851c Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon(a)thekelleys.org.uk>
+Date: Mon, 11 Jan 2016 22:50:00 +0000
+Subject: [PATCH] Inhibit DNSSEC validation when forwarding to private servers
+ for a domain.
+
+server=/example.com/<ip-of-server>
+
+The rationale is that the chain-of-trust will not be complete to
+private servers. If it was, it would not be necessary to access the
+server direct.
+---
+ src/forward.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/forward.c b/src/forward.c
+index 47c6ded..1458578 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -406,7 +406,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
+ }
+
+ #ifdef HAVE_DNSSEC
+- if (option_bool(OPT_DNSSEC_VALID))
++ if (option_bool(OPT_DNSSEC_VALID) && !(type & SERV_HAS_DOMAIN))
+ {
+ size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
+
+@@ -858,7 +858,8 @@ void reply_query(int fd, int family, time_t now)
+ no_cache_dnssec = 1;
+
+ #ifdef HAVE_DNSSEC
+- if (server && option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
++ if (server && !(server->flags & SERV_HAS_DOMAIN) &&
++ option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
+ {
+ int status = 0;
+
+--
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/046-DNSSEC_Handle_non-root_trust_anchors_and_check_we_have_a_root-trust_anchor.patch b/src/patches/dnsmasq/046-DNSSEC_Handle_non-root_trust_anchors_and_check_we_have_a_root-trust_anchor.patch
new file mode 100644
index 0000000..b58b6d2
--- /dev/null
+++ b/src/patches/dnsmasq/046-DNSSEC_Handle_non-root_trust_anchors_and_check_we_have_a_root-trust_anchor.patch
@@ -0,0 +1,70 @@
+From a63b8b89e66a097fca7cba3efc7923636574ec2c Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon(a)thekelleys.org.uk>
+Date: Tue, 12 Jan 2016 11:28:58 +0000
+Subject: [PATCH] DNSSEC: Handle non-root trust anchors, and check we have a
+ root trust anchor.
+
+---
+ src/dnsmasq.c | 12 ++++++++++--
+ src/dnssec.c | 19 ++++++++++++++++++-
+ 2 files changed, 28 insertions(+), 3 deletions(-)
+
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index 8032fc7..e993629 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -169,8 +169,16 @@ int main (int argc, char **argv)
+ if (option_bool(OPT_DNSSEC_VALID))
+ {
+ #ifdef HAVE_DNSSEC
+- if (!daemon->ds)
+- die(_("no trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
++ struct ds_config *ds;
++
++ /* Must have at least a root trust anchor, or the DNSSEC code
++ can loop forever. */
++ for (ds = daemon->ds; ds; ds = ds->next)
++ if (ds->name[0] == 0)
++ break;
++
++ if (!ds)
++ die(_("no root trust anchor provided for DNSSEC"), NULL, EC_BADCONF);
+
+ if (daemon->cachesize < CACHESIZ)
+ die(_("cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF);
+diff --git a/src/dnssec.c b/src/dnssec.c
+index a432ebf..18efa59 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -1873,10 +1873,27 @@ 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 name_start = strlen(name);
++ int name_start = strlen(name); /* for when TA is root */
+ struct crec *crecp;
+ char *p;
++
++ /* First, work towards the root, looking for a trust anchor.
++ This can either be one configured, or one previously cached.
++ We can assume, if we don't find one first, that there is
++ a trust anchor at the root. */
++ for (p = name; p; p = strchr(p, '.'))
++ {
++ if (*p == '.')
++ p++;
++
++ if (cache_find_by_name(NULL, p, now, F_DS))
++ {
++ name_start = p - name;
++ break;
++ }
++ }
+
++ /* Now work away from the trust anchor */
+ while (1)
+ {
+ strcpy(keyname, &name[name_start]);
+--
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/047-Fix_bad_cache-size_calculation_when_hosts-file_read_fails.patch b/src/patches/dnsmasq/047-Fix_bad_cache-size_calculation_when_hosts-file_read_fails.patch
new file mode 100644
index 0000000..71db7e7
--- /dev/null
+++ b/src/patches/dnsmasq/047-Fix_bad_cache-size_calculation_when_hosts-file_read_fails.patch
@@ -0,0 +1,41 @@
+From eddf3652845b30236a99187db19d13bc6d1f282d Mon Sep 17 00:00:00 2001
+From: =?utf8?q?Andr=C3=A9=20Gl=C3=BCpker?= <andre.gluepker(a)st.ovgu.de>
+Date: Tue, 12 Jan 2016 12:54:17 +0000
+Subject: [PATCH] Fix bad cache-size calculation when hosts-file read fails.
+
+---
+ CHANGELOG | 4 ++++
+ src/cache.c | 2 +-
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 93c73d0..dcaa699 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -18,6 +18,10 @@ version 2.76
+ that the same name is empty. Thanks to Edwin Török for
+ the patch.
+
++ Fix failure to correctly calculate cache-size when
++ reading a hosts-file fails. Thanks to André Glüpker
++ for the patch.
++
+
+ version 2.75
+ Fix reversion on 2.74 which caused 100% CPU use when a
+diff --git a/src/cache.c b/src/cache.c
+index d4b71a5..a9eaa65 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -919,7 +919,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
+ if (!f)
+ {
+ my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
+- return 0;
++ return cache_size;
+ }
+
+ eatspace(f);
+--
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/048-Disable_DNSSEC_for_server_domain_servers_unless_trust-anchor_provided.patch b/src/patches/dnsmasq/048-Disable_DNSSEC_for_server_domain_servers_unless_trust-anchor_provided.patch
new file mode 100644
index 0000000..62d4f8d
--- /dev/null
+++ b/src/patches/dnsmasq/048-Disable_DNSSEC_for_server_domain_servers_unless_trust-anchor_provided.patch
@@ -0,0 +1,217 @@
+From 367341f7456c33c66142d66b0e76c56d53bca4f2 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon(a)thekelleys.org.uk>
+Date: Tue, 12 Jan 2016 15:58:23 +0000
+Subject: [PATCH] Disable DNSSEC for server=/domain/.. servers unless
+ trust-anchor provided.
+
+---
+ src/dnsmasq.h | 1 +
+ src/dnssec.c | 2 +-
+ src/forward.c | 30 ++++++++++++++++++++++--------
+ src/network.c | 38 ++++++++++++++++++++++++++++++++++----
+ 4 files changed, 58 insertions(+), 13 deletions(-)
+
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index b2d1c5e..543481c 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -477,6 +477,7 @@ union mysockaddr {
+ #define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
+ #define SERV_FROM_FILE 4096 /* read from --servers-file */
+ #define SERV_LOOP 8192 /* server causes forwarding loop */
++#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
+
+ struct serverfd {
+ int fd;
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 18efa59..ebb9c93 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -1892,7 +1892,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
+ break;
+ }
+ }
+-
++
+ /* Now work away from the trust anchor */
+ while (1)
+ {
+diff --git a/src/forward.c b/src/forward.c
+index 1458578..11c0d45 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -106,8 +106,8 @@ int send_from(int fd, int nowild, char *packet, size_t len,
+ return 1;
+ }
+
+-static unsigned int search_servers(time_t now, struct all_addr **addrpp,
+- unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind)
++static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigned int qtype,
++ char *qdomain, int *type, char **domain, int *norebind)
+
+ {
+ /* If the query ends in the domain in one of our servers, set
+@@ -175,7 +175,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
+
+ if (domainlen >= matchlen)
+ {
+- *type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);
++ *type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_DO_DNSSEC);
+ *domain = serv->domain;
+ matchlen = domainlen;
+ if (serv->flags & SERV_NO_ADDR)
+@@ -233,12 +233,13 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
+ struct frec *forward, int ad_reqd, int do_bit)
+ {
+ char *domain = NULL;
+- int type = 0, norebind = 0;
++ int type = SERV_DO_DNSSEC, norebind = 0;
+ struct all_addr *addrp = NULL;
+ unsigned int flags = 0;
+ struct server *start = NULL;
+ #ifdef HAVE_DNSSEC
+ void *hash = hash_questions(header, plen, daemon->namebuff);
++ int do_dnssec = 0;
+ #else
+ unsigned int crc = questions_crc(header, plen, daemon->namebuff);
+ void *hash = &crc;
+@@ -315,6 +316,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
+ daemon->last_server = NULL;
+ }
+ type = forward->sentto->flags & SERV_TYPE;
++#ifdef HAVE_DNSSEC
++ do_dnssec = forward->sentto->flags & SERV_DO_DNSSEC;
++#endif
++
+ if (!(start = forward->sentto->next))
+ start = daemon->servers; /* at end of list, recycle */
+ header->id = htons(forward->new_id);
+@@ -324,6 +329,11 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
+ if (gotname)
+ flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
+
++#ifdef HAVE_DNSSEC
++ do_dnssec = type & SERV_DO_DNSSEC;
++ type &= ~SERV_DO_DNSSEC;
++#endif
++
+ if (!flags && !(forward = get_new_frec(now, NULL, 0)))
+ /* table full - server failure. */
+ flags = F_NEG;
+@@ -406,7 +416,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
+ }
+
+ #ifdef HAVE_DNSSEC
+- if (option_bool(OPT_DNSSEC_VALID) && !(type & SERV_HAS_DOMAIN))
++ if (option_bool(OPT_DNSSEC_VALID) && do_dnssec)
+ {
+ size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
+
+@@ -858,7 +868,7 @@ void reply_query(int fd, int family, time_t now)
+ no_cache_dnssec = 1;
+
+ #ifdef HAVE_DNSSEC
+- if (server && !(server->flags & SERV_HAS_DOMAIN) &&
++ if (server && (server->flags & SERV_DO_DNSSEC) &&
+ option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
+ {
+ int status = 0;
+@@ -1640,6 +1650,10 @@ unsigned char *tcp_request(int confd, time_t now,
+ if (gotname)
+ flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
+
++#ifdef HAVE_DNSSEC
++ type &= ~SERV_DO_DNSSEC;
++#endif
++
+ if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
+ last_server = daemon->servers;
+ else
+@@ -1711,7 +1725,7 @@ unsigned char *tcp_request(int confd, time_t now,
+ }
+
+ #ifdef HAVE_DNSSEC
+- if (option_bool(OPT_DNSSEC_VALID))
++ if (option_bool(OPT_DNSSEC_VALID) && (last_server->flags & SERV_DO_DNSSEC))
+ {
+ new_size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
+
+@@ -1757,7 +1771,7 @@ unsigned char *tcp_request(int confd, time_t now,
+ #endif
+
+ #ifdef HAVE_DNSSEC
+- if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
++ if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC))
+ {
+ int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
+ int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
+diff --git a/src/network.c b/src/network.c
+index 66b91ad..303ae50 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -1430,12 +1430,38 @@ void check_servers(void)
+ if (!option_bool(OPT_NOWILD))
+ enumerate_interfaces(0);
+
++#ifdef HAVE_DNSSEC
++ /* Disable DNSSEC validation when using server=/domain/.... servers
++ unless there's a configured trust anchor. */
++ for (serv = daemon->servers; serv; serv = serv->next)
++ serv->flags |= SERV_DO_DNSSEC;
++#endif
++
+ for (serv = daemon->servers; serv; serv = serv->next)
+ {
+- if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
++ if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
+ {
+- port = prettyprint_addr(&serv->addr, daemon->namebuff);
++#ifdef HAVE_DNSSEC
++ if (option_bool(OPT_DNSSEC_VALID) && (serv->flags & SERV_HAS_DOMAIN))
++ {
++ struct ds_config *ds;
++ char *domain = serv->domain;
++
++ /* .example.com is valid */
++ while (*domain == '.')
++ domain++;
++
++ for (ds = daemon->ds; ds; ds = ds->next)
++ if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
++ break;
+
++ if (!ds)
++ serv->flags &= ~SERV_DO_DNSSEC;
++ }
++#endif
++
++ port = prettyprint_addr(&serv->addr, daemon->namebuff);
++
+ /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
+ if (serv->addr.sa.sa_family == AF_INET &&
+ serv->addr.in.sin_addr.s_addr == 0)
+@@ -1471,7 +1497,11 @@ void check_servers(void)
+ {
+ if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
+ {
+- char *s1, *s2;
++ char *s1, *s2, *s3 = "";
++#ifdef HAVE_DNSSEC
++ if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
++ s3 = _("(no DNSSEC)");
++#endif
+ if (!(serv->flags & SERV_HAS_DOMAIN))
+ s1 = _("unqualified"), s2 = _("names");
+ else if (strlen(serv->domain) == 0)
+@@ -1484,7 +1514,7 @@ void check_servers(void)
+ else if (serv->flags & SERV_USE_RESOLV)
+ my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
+ else
+- my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
++ my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3);
+ }
+ #ifdef HAVE_LOOP
+ else if (serv->flags & SERV_LOOP)
+--
+1.7.10.4
+
--
2.7.0