dnsmasq 2.75: latest upstream patches

Since 'Makefile' was affected, I had to rewrite
'dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch', too.

Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
This commit is contained in:
Matthias Fischer
2015-12-16 21:42:41 +01:00
committed by Michael Tremer
parent 78af2f67bb
commit fbcc3cb784
7 changed files with 4017 additions and 50 deletions

View File

@@ -88,6 +88,11 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/013-Fix_crash_when_empty_address_from_DNS_overlays_A_record_from.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/014-Handle_unknown_DS_hash_algos_correctly.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/015-Fix_crash_at_start_up_with_conf-dir.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/016-Major_rationalisation_of_DNSSEC_validation.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/017-Abandon_caching_RRSIGs_and_returning_them_from_cache.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/018-Move_code_which_caches_DS_records_to_a_more_logical_place.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/019-Generalise_RR-filtering_code_for_use_with_EDNS0.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch
cd $(DIR_APP) && sed -i src/config.h \

View File

@@ -1,21 +1,5 @@
diff --git a/Makefile b/Makefile
index 4c87ea9..4e0ea10 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,8 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
- domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o
+ domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o \
+ isc.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
diff --git a/src/cache.c b/src/cache.c
index 178d654..6d0b131 100644
--- a/src/cache.c
+++ b/src/cache.c
--- a/src/cache.c Wed Dec 16 19:24:12 2015
+++ b/src/cache.c Wed Dec 16 19:37:37 2015
@@ -17,7 +17,7 @@
#include "dnsmasq.h"
@@ -25,7 +9,7 @@ index 178d654..6d0b131 100644
static struct crec *dhcp_spare = NULL;
#endif
static struct crec *new_chain = NULL;
@@ -222,6 +222,9 @@ static void cache_free(struct crec *crecp)
@@ -217,6 +217,9 @@
crecp->flags &= ~F_BIGNAME;
}
@@ -35,7 +19,7 @@ index 178d654..6d0b131 100644
#ifdef HAVE_DNSSEC
cache_blockdata_free(crecp);
#endif
@@ -1151,7 +1154,7 @@ void cache_reload(void)
@@ -1131,7 +1134,7 @@
}
@@ -44,16 +28,7 @@ index 178d654..6d0b131 100644
struct in_addr a_record_from_hosts(char *name, time_t now)
{
struct crec *crecp = NULL;
@@ -1229,7 +1232,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
addrlen = sizeof(struct in6_addr);
}
#endif
-
+
inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
@@ -1294,7 +1297,11 @@ void cache_add_dhcp_entry(char *host_name, int prot,
@@ -1274,7 +1277,11 @@
else
crec->ttd = ttd;
crec->addr.addr = *host_address;
@@ -65,11 +40,9 @@ index 178d654..6d0b131 100644
crec->uid = next_uid();
cache_hash(crec);
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 81254f6..ce2d1a7 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -982,6 +982,11 @@ int main (int argc, char **argv)
--- a/src/dnsmasq.c Thu Jul 30 20:59:06 2015
+++ b/src/dnsmasq.c Wed Dec 16 19:38:32 2015
@@ -982,6 +982,11 @@
poll_resolv(0, daemon->last_resolv != 0, now);
daemon->last_resolv = now;
@@ -81,11 +54,9 @@ index 81254f6..ce2d1a7 100644
}
#endif
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index cf1a782..30437aa 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1519,3 +1519,7 @@ int poll_check(int fd, short event);
--- a/src/dnsmasq.h Wed Dec 16 19:24:12 2015
+++ b/src/dnsmasq.h Wed Dec 16 19:40:11 2015
@@ -1513,8 +1513,12 @@
void poll_listen(int fd, short event);
int do_poll(int timeout);
@@ -93,11 +64,14 @@ index cf1a782..30437aa 100644
+#ifdef HAVE_ISC_READER
+void load_dhcp(time_t now);
+#endif
diff --git a/src/isc.c b/src/isc.c
new file mode 100644
index 0000000..5106442
--- /dev/null
+++ b/src/isc.c
+
/* rrfilter.c */
size_t rrfilter(struct dns_header *header, size_t plen, int mode);
u16 *rrfilter_desc(int type);
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
-
--- /dev/null Wed Dec 16 19:48:08 2015
+++ b/src/isc.c Wed Dec 16 19:41:35 2015
@@ -0,0 +1,251 @@
+/* dnsmasq is Copyright (c) 2014 John Volpe, Simon Kelley and
+ Michael Tremer
@@ -350,11 +324,9 @@ index 0000000..5106442
+}
+
+#endif
diff --git a/src/option.c b/src/option.c
index ecc2619..527c5aa 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1699,7 +1699,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
--- a/src/option.c Wed Dec 16 19:24:12 2015
+++ b/src/option.c Wed Dec 16 19:42:48 2015
@@ -1754,7 +1754,7 @@
ret_err(_("bad MX target"));
break;
@@ -363,3 +335,14 @@ index ecc2619..527c5aa 100644
case 'l': /* --dhcp-leasefile */
daemon->lease_file = opt_string_alloc(arg);
break;
--- a/Makefile Wed Dec 16 19:24:12 2015
+++ b/Makefile Wed Dec 16 19:28:45 2015
@@ -74,7 +74,7 @@
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
- poll.o rrfilter.o
+ poll.o rrfilter.o isc.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,612 @@
From 93be5b1e023b0c661e1ec2cd6d811a8ec9055c49 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 15 Dec 2015 12:04:40 +0000
Subject: [PATCH] Abandon caching RRSIGs and returning them from cache.
The list of exceptions to being able to locally answer
cached data for validated records when DNSSEC data is requested
was getting too long, so don't ever do that. This means
that the cache no longer has to hold RRSIGS and allows
us to lose lots of code. Note that cached validated
answers are still returned as long as do=0
---
src/cache.c | 38 ++---------
src/dnsmasq.h | 10 +--
src/dnssec.c | 94 ++++-----------------------
src/rfc1035.c | 197 ++++++---------------------------------------------------
4 files changed, 42 insertions(+), 297 deletions(-)
diff --git a/src/cache.c b/src/cache.c
index 1b76b67..51ba7cc 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -189,12 +189,7 @@ static void cache_hash(struct crec *crecp)
static void cache_blockdata_free(struct crec *crecp)
{
if (crecp->flags & F_DNSKEY)
- {
- if (crecp->flags & F_DS)
- blockdata_free(crecp->addr.sig.keydata);
- else
- blockdata_free(crecp->addr.key.keydata);
- }
+ blockdata_free(crecp->addr.key.keydata);
else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
blockdata_free(crecp->addr.ds.keydata);
}
@@ -369,13 +364,8 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
}
#ifdef HAVE_DNSSEC
- /* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also
- type-covered sensitive for RRSIG */
- if ((flags & (F_DNSKEY | F_DS)) &&
- (flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
- crecp->uid == addr->addr.dnssec.class &&
- (!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||
- crecp->addr.sig.type_covered == addr->addr.dnssec.type))
+ /* Deletion has to be class-sensitive for DS and DNSKEY */
+ if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == addr->addr.dnssec.class)
{
if (crecp->flags & F_CONFIG)
return crecp;
@@ -532,13 +522,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
struct all_addr free_addr = new->addr.addr;;
#ifdef HAVE_DNSSEC
- /* For DNSSEC records, addr holds class and type_covered for RRSIG */
+ /* For DNSSEC records, addr holds class. */
if (new->flags & (F_DS | F_DNSKEY))
- {
- free_addr.addr.dnssec.class = new->uid;
- if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
- free_addr.addr.dnssec.type = new->addr.sig.type_covered;
- }
+ free_addr.addr.dnssec.class = new->uid;
#endif
free_avail = 1; /* Must be free space now. */
@@ -653,9 +639,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{
if ((crecp->flags & F_FORWARD) &&
-#ifdef HAVE_DNSSEC
- (((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
-#endif
(crecp->flags & prot) &&
hostname_isequal(cache_get_name(crecp), name))
{
@@ -713,9 +696,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (ans &&
(ans->flags & F_FORWARD) &&
-#ifdef HAVE_DNSSEC
- (((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
-#endif
(ans->flags & prot) &&
hostname_isequal(cache_get_name(ans), name))
return ans;
@@ -1472,11 +1452,7 @@ void dump_cache(time_t now)
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
- if (cache->flags & F_DNSKEY)
- /* RRSIG */
- sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
- cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
- else if (!(cache->flags & F_NEG))
+ if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
@@ -1502,8 +1478,6 @@ void dump_cache(time_t now)
else if (cache->flags & F_CNAME)
t = "C";
#ifdef HAVE_DNSSEC
- else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
- t = "G"; /* DNSKEY and DS set -> RRISG */
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 023a1cf..4344cae 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -398,14 +398,9 @@ struct crec {
unsigned char algo;
unsigned char digest;
} ds;
- struct {
- struct blockdata *keydata;
- unsigned short keylen, type_covered, keytag;
- char algo;
- } sig;
} addr;
time_t ttd; /* time to die */
- /* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
+ /* used as class if DNSKEY/DS, index to source for F_HOSTS */
unsigned int uid;
unsigned short flags;
union {
@@ -445,8 +440,7 @@ struct crec {
#define F_SECSTAT (1u<<24)
#define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26)
-#define F_NSIGMATCH (1u<<27)
-#define F_NOEXTRA (1u<<28)
+#define F_NOEXTRA (1u<<27)
/* Values of uid in crecs with F_CONFIG bit set. */
#define SRC_INTERFACE 0
diff --git a/src/dnssec.c b/src/dnssec.c
index de7b335..1ae03a6 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1004,7 +1004,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
{
unsigned char *psave, *p = (unsigned char *)(header+1);
struct crec *crecp, *recp1;
- int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag, type_covered;
+ int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag;
struct blockdata *key;
struct all_addr a;
@@ -1115,7 +1115,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
if (valid)
{
- /* DNSKEY RRset determined to be OK, now cache it and the RRsigs that sign it. */
+ /* DNSKEY RRset determined to be OK, now cache it. */
cache_start_insert();
p = skip_questions(header, plen);
@@ -1155,7 +1155,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
if ((key = blockdata_alloc((char*)p, rdlen - 4)))
{
if (!(recp1 = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)))
- blockdata_free(key);
+ {
+ blockdata_free(key);
+ return STAT_BOGUS;
+ }
else
{
a.addr.keytag = keytag;
@@ -1169,38 +1172,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
}
}
}
- else if (qtype == T_RRSIG)
- {
- /* RRSIG, cache if covers DNSKEY RRset */
- if (rdlen < 18)
- return STAT_BOGUS; /* bad packet */
-
- GETSHORT(type_covered, p);
-
- if (type_covered == T_DNSKEY)
- {
- a.addr.dnssec.class = class;
- a.addr.dnssec.type = type_covered;
-
- algo = *p++;
- p += 13; /* labels, orig_ttl, expiration, inception */
- GETSHORT(keytag, p);
- if ((key = blockdata_alloc((char*)psave, rdlen)))
- {
- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
- blockdata_free(key);
- else
- {
- crecp->addr.sig.keydata = key;
- crecp->addr.sig.keylen = rdlen;
- crecp->addr.sig.keytag = keytag;
- crecp->addr.sig.type_covered = type_covered;
- crecp->addr.sig.algo = algo;
- }
- }
- }
- }
-
+
p = psave;
}
@@ -1326,7 +1298,8 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
cache_start_insert();
a.addr.dnssec.class = class;
- cache_insert(name, &a, now, ttl, flags);
+ if (!cache_insert(name, &a, now, ttl, flags))
+ return STAT_BOGUS;
cache_end_insert();
@@ -2028,14 +2001,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Not done, validate now */
if (j == i)
{
- int ttl, keytag, algo, digest, type_covered, sigcnt, rrcnt;
+ int ttl, keytag, algo, digest, sigcnt, rrcnt;
unsigned char *psave;
struct all_addr a;
struct blockdata *key;
struct crec *crecp;
char *wildname;
- int have_wildcard = 0;
-
+
if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
return STAT_BOGUS;
@@ -2096,8 +2068,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (rc == STAT_SECURE_WILDCARD)
{
- have_wildcard = 1;
-
/* An attacker replay a wildcard answer with a different
answer and overlay a genuine RR. To prove this
hasn't happened, the answer must prove that
@@ -2119,7 +2089,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
return rc;
}
- /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
+ /* If we just validated a DS RRset, cache it */
/* Also note if the RRset is the answer to the question, or the target of a CNAME */
cache_start_insert();
@@ -2168,45 +2138,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
}
}
}
- else if (type2 == T_RRSIG)
- {
- if (rdlen2 < 18)
- return STAT_BOGUS; /* bad packet */
-
- GETSHORT(type_covered, p2);
-
- if (type_covered == type1 &&
- (type_covered == T_A || type_covered == T_AAAA ||
- type_covered == T_CNAME || type_covered == T_DS ||
- type_covered == T_DNSKEY || type_covered == T_PTR))
- {
- a.addr.dnssec.type = type_covered;
- a.addr.dnssec.class = class1;
-
- algo = *p2++;
- p2 += 13; /* labels, orig_ttl, expiration, inception */
- GETSHORT(keytag, p2);
-
- /* We don't cache sigs for wildcard answers, because to reproduce the
- answer from the cache will require one or more NSEC/NSEC3 records
- which we don't cache. The lack of the RRSIG ensures that a query for
- this RRset asking for a secure answer will always be forwarded. */
- if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2)))
- {
- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
- blockdata_free(key);
- else
- {
- crecp->addr.sig.keydata = key;
- crecp->addr.sig.keylen = rdlen2;
- crecp->addr.sig.keytag = keytag;
- crecp->addr.sig.type_covered = type_covered;
- crecp->addr.sig.algo = algo;
- }
- }
- }
- }
-
+
p2 = psave;
}
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 4eb1772..def8fa0 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -1275,11 +1275,9 @@ int check_for_local_domain(char *name, time_t now)
struct naptr *naptr;
/* Note: the call to cache_find_by_name is intended to find any record which matches
- ie A, AAAA, CNAME, DS. Because RRSIG records are marked by setting both F_DS and F_DNSKEY,
- cache_find_by name ordinarily only returns records with an exact match on those bits (ie
- for the call below, only DS records). The F_NSIGMATCH bit changes this behaviour */
+ ie A, AAAA, CNAME. */
- if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR | F_NSIGMATCH)) &&
+ if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME |F_NO_RR)) &&
(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
return 1;
@@ -1566,9 +1564,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
GETSHORT(flags, pheader);
if ((sec_reqd = flags & 0x8000))
- *do_bit = 1;/* do bit */
+ {
+ *do_bit = 1;/* do bit */
+ *ad_reqd = 1;
+ }
- *ad_reqd = 1;
dryrun = 1;
}
@@ -1636,98 +1636,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
-#ifdef HAVE_DNSSEC
- if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
- {
- int gotone = 0;
- struct blockdata *keydata;
-
- /* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
- if (sec_reqd)
- {
- crecp = NULL;
- while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
- if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype)
- break;
- }
-
- if (!sec_reqd || crecp)
- {
- if (qtype == T_DS)
- {
- crecp = NULL;
- while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
- if (crecp->uid == qclass)
- {
- gotone = 1;
- if (!dryrun)
- {
- if (crecp->flags & F_NEG)
- {
- if (crecp->flags & F_NXDOMAIN)
- nxdomain = 1;
- log_query(F_UPSTREAM, name, NULL, "no DS");
- }
- else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
- {
- struct all_addr a;
- a.addr.keytag = crecp->addr.ds.keytag;
- log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
- if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- crec_ttl(crecp, now), &nameoffset,
- T_DS, qclass, "sbbt",
- crecp->addr.ds.keytag, crecp->addr.ds.algo,
- crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
- anscount++;
-
- }
- }
- }
- }
- else /* DNSKEY */
- {
- crecp = NULL;
- while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
- if (crecp->uid == qclass)
- {
- gotone = 1;
- if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
- {
- struct all_addr a;
- a.addr.keytag = crecp->addr.key.keytag;
- log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
- if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- crec_ttl(crecp, now), &nameoffset,
- T_DNSKEY, qclass, "sbbt",
- crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
- anscount++;
- }
- }
- }
- }
-
- /* Now do RRSIGs */
- if (gotone)
- {
- ans = 1;
- auth = 0;
- if (!dryrun && sec_reqd)
- {
- crecp = NULL;
- while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
- if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&
- (keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
- {
- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- crec_ttl(crecp, now), &nameoffset,
- T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
- anscount++;
- }
- }
- }
- }
-#endif
-
if (qclass == C_IN)
{
struct txt_record *t;
@@ -1736,6 +1644,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
{
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
@@ -1792,6 +1701,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (intr)
{
+ sec_data = 0;
ans = 1;
if (!dryrun)
{
@@ -1805,6 +1715,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (ptr)
{
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
@@ -1819,38 +1730,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
{
- if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
- {
- if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
- crecp = NULL;
-#ifdef HAVE_DNSSEC
- else if (crecp->flags & F_DNSSECOK)
- {
- int gotsig = 0;
- struct crec *rr_crec = NULL;
-
- while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
- {
- if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
- {
- char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
- gotsig = 1;
-
- if (!dryrun &&
- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- rr_crec->ttd - now, &nameoffset,
- T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
- anscount++;
- }
- }
-
- if (!gotsig)
- crecp = NULL;
- }
-#endif
- }
-
- if (crecp)
+ /* Don't use cache when DNSSEC data required. */
+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
{
do
{
@@ -1860,19 +1741,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
-
+
+ ans = 1;
+
if (crecp->flags & F_NEG)
{
- ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
}
- else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
+ else
{
- ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
if (!dryrun)
@@ -1892,6 +1773,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (is_rev_synth(is_arpa, &addr, name))
{
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);
@@ -1908,6 +1790,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
+ sec_data = 0;
nxdomain = 1;
if (!dryrun)
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
@@ -1955,6 +1838,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (i == 4)
{
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
addr.addr.addr4.s_addr = htonl(a);
@@ -1993,6 +1877,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
continue;
#endif
ans = 1;
+ sec_data = 0;
if (!dryrun)
{
gotit = 1;
@@ -2032,48 +1917,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
crecp = save;
}
- /* If the client asked for DNSSEC and we can't provide RRSIGs, either
- because we've not doing DNSSEC or the cached answer is signed by negative,
- don't answer from the cache, forward instead. */
- if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
- {
- if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
- crecp = NULL;
-#ifdef HAVE_DNSSEC
- else if (crecp->flags & F_DNSSECOK)
- {
- /* We're returning validated data, need to return the RRSIG too. */
- struct crec *rr_crec = NULL;
- int sigtype = type;
- /* The signature may have expired even though the data is still in cache,
- forward instead of answering from cache if so. */
- int gotsig = 0;
-
- if (crecp->flags & F_CNAME)
- sigtype = T_CNAME;
-
- while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
- {
- if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
- {
- char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
- gotsig = 1;
-
- if (!dryrun &&
- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
- rr_crec->ttd - now, &nameoffset,
- T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata))
- anscount++;
- }
- }
-
- if (!gotsig)
- crecp = NULL;
- }
-#endif
- }
-
- if (crecp)
+ /* 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))
do
{
/* don't answer wildcard queries with data not from /etc/hosts
--
1.7.10.4

View File

@@ -0,0 +1,269 @@
From d64c81fff7faf4392b688223ef3a617c5c07e7dc Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 15 Dec 2015 16:11:06 +0000
Subject: [PATCH] Move code which caches DS records to a more logical place.
---
src/dnssec.c | 179 +++++++++++++++++++++++++++++-----------------------------
1 file changed, 90 insertions(+), 89 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index 1ae03a6..359231f 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1204,7 +1204,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
unsigned char *p = (unsigned char *)(header+1);
- int qtype, qclass, val, i, neganswer, nons;
+ int qtype, qclass, rc, i, neganswer, nons;
+ int aclass, atype, rdlen;
+ unsigned long ttl;
+ struct all_addr a;
if (ntohs(header->qdcount) != 1 ||
!(p = skip_name(p, header, plen, 4)))
@@ -1214,40 +1217,100 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
GETSHORT(qclass, p);
if (qtype != T_DS || qclass != class)
- val = STAT_BOGUS;
+ rc = STAT_BOGUS;
else
- val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
+ rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
/* Note dnssec_validate_reply() will have cached positive answers */
- if (val == STAT_INSECURE)
- val = STAT_BOGUS;
-
+ if (rc == STAT_INSECURE)
+ rc = STAT_BOGUS;
+
p = (unsigned char *)(header+1);
extract_name(header, plen, &p, name, 1, 4);
p += 4; /* qtype, qclass */
- if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
- val = STAT_BOGUS;
-
/* If the key needed to validate the DS is on the same domain as the DS, we'll
loop getting nowhere. Stop that now. This can happen of the DS answer comes
from the DS's zone, and not the parent zone. */
- if (val == STAT_BOGUS || (val == STAT_NEED_KEY && hostname_isequal(name, keyname)))
+ if (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))
{
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
return STAT_BOGUS;
}
- if (val != STAT_SECURE)
- return val;
-
- /* By here, the answer is proved secure, and a positive answer has been cached. */
- if (neganswer)
+ if (rc != STAT_SECURE)
+ return rc;
+
+ if (!neganswer)
{
- int rdlen, flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
- unsigned long ttl, minttl = ULONG_MAX;
- struct all_addr a;
+ cache_start_insert();
+
+ for (i = 0; i < ntohs(header->ancount); i++)
+ {
+ if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
+ return STAT_BOGUS; /* bad packet */
+
+ GETSHORT(atype, p);
+ GETSHORT(aclass, p);
+ GETLONG(ttl, p);
+ GETSHORT(rdlen, p);
+
+ if (!CHECK_LEN(header, p, plen, rdlen))
+ return STAT_BOGUS; /* bad packet */
+
+ if (aclass == class && atype == T_DS && rc == 1)
+ {
+ int algo, digest, keytag;
+ unsigned char *psave = p;
+ struct blockdata *key;
+ struct crec *crecp;
+ if (rdlen < 4)
+ return STAT_BOGUS; /* bad packet */
+
+ GETSHORT(keytag, p);
+ algo = *p++;
+ digest = *p++;
+
+ /* Cache needs to known class for DNSSEC stuff */
+ a.addr.dnssec.class = class;
+
+ if ((key = blockdata_alloc((char*)p, rdlen - 4)))
+ {
+ if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
+ {
+ blockdata_free(key);
+ return STAT_BOGUS;
+ }
+ else
+ {
+ a.addr.keytag = keytag;
+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
+ crecp->addr.ds.digest = digest;
+ crecp->addr.ds.keydata = key;
+ crecp->addr.ds.algo = algo;
+ crecp->addr.ds.keytag = keytag;
+ crecp->addr.ds.keylen = rdlen - 4;
+ }
+ }
+
+ p = psave;
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return STAT_BOGUS; /* bad packet */
+ }
+
+ cache_end_insert();
+ }
+ }
+ else
+ {
+ int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
+ unsigned long minttl = ULONG_MAX;
+
+ if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
+ return STAT_BOGUS;
+
if (RCODE(header) == NXDOMAIN)
flags |= F_NXDOMAIN;
@@ -1261,20 +1324,20 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
if (!(p = skip_name(p, header, plen, 0)))
return STAT_BOGUS;
- GETSHORT(qtype, p);
- GETSHORT(qclass, p);
+ GETSHORT(atype, p);
+ GETSHORT(aclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
-
+
if (!CHECK_LEN(header, p, plen, rdlen))
return STAT_BOGUS; /* bad packet */
-
- if (qclass != class || qtype != T_SOA)
+
+ if (aclass != class || atype != T_SOA)
{
p += rdlen;
continue;
}
-
+
if (ttl < minttl)
minttl = ttl;
@@ -1306,7 +1369,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "no DS");
}
}
-
+
return STAT_OK;
}
@@ -2001,11 +2064,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Not done, validate now */
if (j == i)
{
- int ttl, keytag, algo, digest, sigcnt, rrcnt;
- unsigned char *psave;
- struct all_addr a;
- struct blockdata *key;
- struct crec *crecp;
+ int sigcnt, rrcnt;
char *wildname;
if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
@@ -2032,6 +2091,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
Can't overwrite name here. */
strcpy(daemon->workspacename, keyname);
rc = zone_status(daemon->workspacename, class1, keyname, now);
+
if (rc != STAT_SECURE)
{
/* Zone is insecure, don't need to validate RRset */
@@ -2088,65 +2148,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (rc == STAT_BOGUS)
return rc;
}
-
- /* If we just validated a DS RRset, cache it */
- /* Also note if the RRset is the answer to the question, or the target of a CNAME */
- cache_start_insert();
-
- for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
- {
- if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
- return STAT_BOGUS; /* bad packet */
-
- GETSHORT(type2, p2);
- GETSHORT(class2, p2);
- GETLONG(ttl, p2);
- GETSHORT(rdlen2, p2);
-
- if (!CHECK_LEN(header, p2, plen, rdlen2))
- return STAT_BOGUS; /* bad packet */
-
- if (class2 == class1 && rc == 1)
- {
- psave = p2;
-
- if (type1 == T_DS && type2 == T_DS)
- {
- if (rdlen2 < 4)
- return STAT_BOGUS; /* bad packet */
-
- GETSHORT(keytag, p2);
- algo = *p2++;
- digest = *p2++;
-
- /* Cache needs to known class for DNSSEC stuff */
- a.addr.dnssec.class = class2;
-
- if ((key = blockdata_alloc((char*)p2, rdlen2 - 4)))
- {
- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
- blockdata_free(key);
- else
- {
- a.addr.keytag = keytag;
- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
- crecp->addr.ds.digest = digest;
- crecp->addr.ds.keydata = key;
- crecp->addr.ds.algo = algo;
- crecp->addr.ds.keytag = keytag;
- crecp->addr.ds.keylen = rdlen2 - 4;
- }
- }
- }
-
- p2 = psave;
- }
-
- if (!ADD_RDLEN(header, p2, plen, rdlen2))
- return STAT_BOGUS; /* bad packet */
- }
-
- cache_end_insert();
}
}
}
--
1.7.10.4

View File

@@ -0,0 +1,755 @@
From c2bcd1e183bcc5fdd63811c045355fc57e36ecfd Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 15 Dec 2015 17:25:21 +0000
Subject: [PATCH] Generalise RR-filtering code, for use with EDNS0.
---
Makefile | 3 +-
bld/Android.mk | 2 +-
src/dnsmasq.h | 5 +
src/dnssec.c | 307 +-------------------------------------------------
src/forward.c | 2 +-
src/rrfilter.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 349 insertions(+), 309 deletions(-)
create mode 100644 src/rrfilter.c
diff --git a/Makefile b/Makefile
index 4c87ea9..b664160 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,8 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
- domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o
+ domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
+ poll.o rrfilter.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
diff --git a/bld/Android.mk b/bld/Android.mk
index 5364ee7..67b9c4b 100644
--- a/bld/Android.mk
+++ b/bld/Android.mk
@@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c tables.c \
- loop.c inotify.c poll.c
+ loop.c inotify.c poll.c rrfilter.c
LOCAL_MODULE := dnsmasq
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 4344cae..39a930c 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1513,3 +1513,8 @@ int poll_check(int fd, short event);
void poll_listen(int fd, short event);
int do_poll(int timeout);
+/* rrfilter.c */
+size_t rrfilter(struct dns_header *header, size_t plen, int mode);
+u16 *rrfilter_desc(int type);
+int expand_workspace(unsigned char ***wkspc, int *szp, int new);
+
diff --git a/src/dnssec.c b/src/dnssec.c
index 359231f..fa3eb81 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -507,50 +507,6 @@ static int check_date_range(unsigned long date_start, unsigned long date_end)
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
}
-static u16 *get_desc(int type)
-{
- /* List of RRtypes which include domains in the data.
- 0 -> domain
- integer -> no of plain bytes
- -1 -> end
-
- zero is not a valid RRtype, so the final entry is returned for
- anything which needs no mangling.
- */
-
- static u16 rr_desc[] =
- {
- T_NS, 0, -1,
- T_MD, 0, -1,
- T_MF, 0, -1,
- T_CNAME, 0, -1,
- T_SOA, 0, 0, -1,
- T_MB, 0, -1,
- T_MG, 0, -1,
- T_MR, 0, -1,
- T_PTR, 0, -1,
- T_MINFO, 0, 0, -1,
- T_MX, 2, 0, -1,
- T_RP, 0, 0, -1,
- T_AFSDB, 2, 0, -1,
- T_RT, 2, 0, -1,
- T_SIG, 18, 0, -1,
- T_PX, 2, 0, 0, -1,
- T_NXT, 0, -1,
- T_KX, 2, 0, -1,
- T_SRV, 6, 0, -1,
- T_DNAME, 0, -1,
- 0, -1 /* wildcard/catchall */
- };
-
- u16 *p = rr_desc;
-
- while (*p != type && *p != 0)
- while (*p++ != (u16)-1);
-
- return p+1;
-}
-
/* Return bytes of canonicalised rdata, when the return value is zero, the remaining
data, pointed to by *p, should be used raw. */
static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen,
@@ -594,34 +550,6 @@ static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end,
}
}
-static int expand_workspace(unsigned char ***wkspc, int *szp, int new)
-{
- unsigned char **p;
- int old = *szp;
-
- if (old >= new+1)
- return 1;
-
- if (new >= 100)
- return 0;
-
- new += 5;
-
- if (!(p = whine_malloc(new * sizeof(unsigned char **))))
- return 0;
-
- if (old != 0 && *wkspc)
- {
- memcpy(p, *wkspc, old * sizeof(unsigned char **));
- free(*wkspc);
- }
-
- *wkspc = p;
- *szp = new;
-
- return 1;
-}
-
/* Bubble sort the RRset into the canonical order.
Note that the byte-streams from two RRs may get unsynced: consider
RRs which have two domain-names at the start and then other data.
@@ -849,7 +777,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
int rdlen, j, name_labels;
struct crec *crecp = NULL;
int algo, labels, orig_ttl, key_tag;
- u16 *rr_desc = get_desc(type);
+ u16 *rr_desc = rrfilter_desc(type);
if (wildcard_out)
*wildcard_out = NULL;
@@ -2266,239 +2194,6 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
return ret;
}
-/* Go through a domain name, find "pointers" and fix them up based on how many bytes
- we've chopped out of the packet, or check they don't point into an elided part. */
-static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
-{
- unsigned char *ansp = *namep;
-
- while(1)
- {
- unsigned int label_type;
-
- if (!CHECK_LEN(header, ansp, plen, 1))
- return 0;
-
- label_type = (*ansp) & 0xc0;
-
- if (label_type == 0xc0)
- {
- /* pointer for compression. */
- unsigned int offset;
- int i;
- unsigned char *p;
-
- if (!CHECK_LEN(header, ansp, plen, 2))
- return 0;
-
- offset = ((*ansp++) & 0x3f) << 8;
- offset |= *ansp++;
-
- p = offset + (unsigned char *)header;
-
- for (i = 0; i < rr_count; i++)
- if (p < rrs[i])
- break;
- else
- if (i & 1)
- offset -= rrs[i] - rrs[i-1];
-
- /* does the pointer end up in an elided RR? */
- if (i & 1)
- return 0;
-
- /* No, scale the pointer */
- if (fixup)
- {
- ansp -= 2;
- *ansp++ = (offset >> 8) | 0xc0;
- *ansp++ = offset & 0xff;
- }
- break;
- }
- else if (label_type == 0x80)
- return 0; /* reserved */
- else if (label_type == 0x40)
- {
- /* Extended label type */
- unsigned int count;
-
- if (!CHECK_LEN(header, ansp, plen, 2))
- return 0;
-
- if (((*ansp++) & 0x3f) != 1)
- return 0; /* we only understand bitstrings */
-
- count = *(ansp++); /* Bits in bitstring */
-
- if (count == 0) /* count == 0 means 256 bits */
- ansp += 32;
- else
- ansp += ((count-1)>>3)+1;
- }
- else
- { /* label type == 0 Bottom six bits is length */
- unsigned int len = (*ansp++) & 0x3f;
-
- if (!ADD_RDLEN(header, ansp, plen, len))
- return 0;
-
- if (len == 0)
- break; /* zero length label marks the end. */
- }
- }
-
- *namep = ansp;
-
- return 1;
-}
-
-/* Go through RRs and check or fixup the domain names contained within */
-static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
-{
- int i, type, class, rdlen;
- unsigned char *pp;
-
- for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
- {
- pp = p;
-
- if (!(p = skip_name(p, header, plen, 10)))
- return 0;
-
- GETSHORT(type, p);
- GETSHORT(class, p);
- p += 4; /* TTL */
- GETSHORT(rdlen, p);
-
- if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
- {
- /* fixup name of RR */
- if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
- return 0;
-
- if (class == C_IN)
- {
- u16 *d;
-
- for (pp = p, d = get_desc(type); *d != (u16)-1; d++)
- {
- if (*d != 0)
- pp += *d;
- else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
- return 0;
- }
- }
- }
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
- return 0;
- }
-
- return 1;
-}
-
-
-size_t filter_rrsigs(struct dns_header *header, size_t plen)
-{
- static unsigned char **rrs;
- static int rr_sz = 0;
-
- unsigned char *p = (unsigned char *)(header+1);
- int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
-
- if (ntohs(header->qdcount) != 1 ||
- !(p = skip_name(p, header, plen, 4)))
- return plen;
-
- GETSHORT(qtype, p);
- GETSHORT(qclass, p);
-
- /* First pass, find pointers to start and end of all the records we wish to elide:
- records added for DNSSEC, unless explicity queried for */
- for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
- i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
- i++)
- {
- unsigned char *pstart = p;
- int type, class;
-
- if (!(p = skip_name(p, header, plen, 10)))
- return plen;
-
- GETSHORT(type, p);
- GETSHORT(class, p);
- p += 4; /* TTL */
- GETSHORT(rdlen, p);
-
- if ((type == T_NSEC || type == T_NSEC3 || type == T_RRSIG) &&
- (type != qtype || class != qclass))
- {
- if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
- return plen;
-
- rrs[rr_found++] = pstart;
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
- return plen;
-
- rrs[rr_found++] = p;
-
- if (i < ntohs(header->ancount))
- chop_an++;
- else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
- chop_ns++;
- else
- chop_ar++;
- }
- else if (!ADD_RDLEN(header, p, plen, rdlen))
- return plen;
- }
-
- /* Nothing to do. */
- if (rr_found == 0)
- return plen;
-
- /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
- point to records we're going to elide. This is theoretically possible, but unlikely. If
- it happens, we give up and leave the answer unchanged. */
- p = (unsigned char *)(header+1);
-
- /* question first */
- if (!check_name(&p, header, plen, 0, rrs, rr_found))
- return plen;
- p += 4; /* qclass, qtype */
-
- /* Now answers and NS */
- if (!check_rrs(p, header, plen, 0, rrs, rr_found))
- return plen;
-
- /* Third pass, elide records */
- 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;
-
- memmove(p, start, end-start);
- p += end-start;
- }
-
- plen = p - (unsigned char *)header;
- header->ancount = htons(ntohs(header->ancount) - chop_an);
- header->nscount = htons(ntohs(header->nscount) - chop_ns);
- header->arcount = htons(ntohs(header->arcount) - chop_ar);
-
- /* Fourth pass, fix up pointers in the remaining records */
- p = (unsigned char *)(header+1);
-
- check_name(&p, header, plen, 1, rrs, rr_found);
- p += 4; /* qclass, qtype */
-
- check_rrs(p, header, plen, 1, rrs, rr_found);
-
- return plen;
-}
-
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name)
{
int q;
diff --git a/src/forward.c b/src/forward.c
index dd22a62..3e801c8 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -662,7 +662,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
if (!do_bit)
- n = filter_rrsigs(header, n);
+ n = rrfilter(header, n, 1);
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
diff --git a/src/rrfilter.c b/src/rrfilter.c
new file mode 100644
index 0000000..ae12261
--- /dev/null
+++ b/src/rrfilter.c
@@ -0,0 +1,339 @@
+/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Code to safely remove RRs from an DNS answer */
+
+#include "dnsmasq.h"
+
+/* Go through a domain name, find "pointers" and fix them up based on how many bytes
+ we've chopped out of the packet, or check they don't point into an elided part. */
+static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
+{
+ unsigned char *ansp = *namep;
+
+ while(1)
+ {
+ unsigned int label_type;
+
+ if (!CHECK_LEN(header, ansp, plen, 1))
+ return 0;
+
+ label_type = (*ansp) & 0xc0;
+
+ if (label_type == 0xc0)
+ {
+ /* pointer for compression. */
+ unsigned int offset;
+ int i;
+ unsigned char *p;
+
+ if (!CHECK_LEN(header, ansp, plen, 2))
+ return 0;
+
+ offset = ((*ansp++) & 0x3f) << 8;
+ offset |= *ansp++;
+
+ p = offset + (unsigned char *)header;
+
+ for (i = 0; i < rr_count; i++)
+ if (p < rrs[i])
+ break;
+ else
+ if (i & 1)
+ offset -= rrs[i] - rrs[i-1];
+
+ /* does the pointer end up in an elided RR? */
+ if (i & 1)
+ return 0;
+
+ /* No, scale the pointer */
+ if (fixup)
+ {
+ ansp -= 2;
+ *ansp++ = (offset >> 8) | 0xc0;
+ *ansp++ = offset & 0xff;
+ }
+ break;
+ }
+ else if (label_type == 0x80)
+ return 0; /* reserved */
+ else if (label_type == 0x40)
+ {
+ /* Extended label type */
+ unsigned int count;
+
+ if (!CHECK_LEN(header, ansp, plen, 2))
+ return 0;
+
+ if (((*ansp++) & 0x3f) != 1)
+ return 0; /* we only understand bitstrings */
+
+ count = *(ansp++); /* Bits in bitstring */
+
+ if (count == 0) /* count == 0 means 256 bits */
+ ansp += 32;
+ else
+ ansp += ((count-1)>>3)+1;
+ }
+ else
+ { /* label type == 0 Bottom six bits is length */
+ unsigned int len = (*ansp++) & 0x3f;
+
+ if (!ADD_RDLEN(header, ansp, plen, len))
+ return 0;
+
+ if (len == 0)
+ break; /* zero length label marks the end. */
+ }
+ }
+
+ *namep = ansp;
+
+ return 1;
+}
+
+/* Go through RRs and check or fixup the domain names contained within */
+static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
+{
+ int i, j, type, class, rdlen;
+ unsigned char *pp;
+
+ for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
+ {
+ pp = p;
+
+ if (!(p = skip_name(p, header, plen, 10)))
+ return 0;
+
+ GETSHORT(type, p);
+ GETSHORT(class, p);
+ p += 4; /* TTL */
+ GETSHORT(rdlen, p);
+
+ /* If this RR is to be elided, don't fix up its contents */
+ for (j = 0; j < rr_count; j += 2)
+ if (rrs[j] == pp)
+ break;
+
+ if (j >= rr_count)
+ {
+ /* fixup name of RR */
+ if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
+ return 0;
+
+ if (class == C_IN)
+ {
+ u16 *d;
+
+ for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
+ {
+ if (*d != 0)
+ pp += *d;
+ else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
+ return 0;
+ }
+ }
+ }
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
+size_t rrfilter(struct dns_header *header, size_t plen, int mode)
+{
+ static unsigned char **rrs;
+ static int rr_sz = 0;
+
+ unsigned char *p = (unsigned char *)(header+1);
+ int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
+
+ if (ntohs(header->qdcount) != 1 ||
+ !(p = skip_name(p, header, plen, 4)))
+ return plen;
+
+ GETSHORT(qtype, p);
+ GETSHORT(qclass, p);
+
+ /* First pass, find pointers to start and end of all the records we wish to elide:
+ records added for DNSSEC, unless explicity queried for */
+ for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
+ i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
+ i++)
+ {
+ unsigned char *pstart = p;
+ int type, class;
+
+ if (!(p = skip_name(p, header, plen, 10)))
+ return plen;
+
+ GETSHORT(type, p);
+ GETSHORT(class, p);
+ p += 4; /* TTL */
+ GETSHORT(rdlen, p);
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return plen;
+
+ /* Don't remove the answer. */
+ if (i < ntohs(header->ancount) && type == qtype && class == qclass)
+ continue;
+
+ if (mode == 0) /* EDNS */
+ {
+ /* EDNS mode, remove T_OPT from additional section only */
+ if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
+ continue;
+ }
+ else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
+ /* DNSSEC mode, remove SIGs and NSECs from all three sections. */
+ continue;
+
+
+ if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
+ return plen;
+
+ rrs[rr_found++] = pstart;
+ rrs[rr_found++] = p;
+
+ if (i < ntohs(header->ancount))
+ chop_an++;
+ else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
+ chop_ns++;
+ else
+ chop_ar++;
+ }
+
+ /* Nothing to do. */
+ if (rr_found == 0)
+ return plen;
+
+ /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
+ point to records we're going to elide. This is theoretically possible, but unlikely. If
+ it happens, we give up and leave the answer unchanged. */
+ p = (unsigned char *)(header+1);
+
+ /* question first */
+ if (!check_name(&p, header, plen, 0, rrs, rr_found))
+ return plen;
+ p += 4; /* qclass, qtype */
+
+ /* Now answers and NS */
+ if (!check_rrs(p, header, plen, 0, rrs, rr_found))
+ return plen;
+
+ /* Third pass, elide records */
+ 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;
+
+ memmove(p, start, end-start);
+ p += end-start;
+ }
+
+ plen = p - (unsigned char *)header;
+ header->ancount = htons(ntohs(header->ancount) - chop_an);
+ header->nscount = htons(ntohs(header->nscount) - chop_ns);
+ header->arcount = htons(ntohs(header->arcount) - chop_ar);
+
+ /* Fourth pass, fix up pointers in the remaining records */
+ p = (unsigned char *)(header+1);
+
+ check_name(&p, header, plen, 1, rrs, rr_found);
+ p += 4; /* qclass, qtype */
+
+ check_rrs(p, header, plen, 1, rrs, rr_found);
+
+ return plen;
+}
+
+/* This is used in the DNSSEC code too, hence it's exported */
+u16 *rrfilter_desc(int type)
+{
+ /* List of RRtypes which include domains in the data.
+ 0 -> domain
+ integer -> no of plain bytes
+ -1 -> end
+
+ zero is not a valid RRtype, so the final entry is returned for
+ anything which needs no mangling.
+ */
+
+ static u16 rr_desc[] =
+ {
+ T_NS, 0, -1,
+ T_MD, 0, -1,
+ T_MF, 0, -1,
+ T_CNAME, 0, -1,
+ T_SOA, 0, 0, -1,
+ T_MB, 0, -1,
+ T_MG, 0, -1,
+ T_MR, 0, -1,
+ T_PTR, 0, -1,
+ T_MINFO, 0, 0, -1,
+ T_MX, 2, 0, -1,
+ T_RP, 0, 0, -1,
+ T_AFSDB, 2, 0, -1,
+ T_RT, 2, 0, -1,
+ T_SIG, 18, 0, -1,
+ T_PX, 2, 0, 0, -1,
+ T_NXT, 0, -1,
+ T_KX, 2, 0, -1,
+ T_SRV, 6, 0, -1,
+ T_DNAME, 0, -1,
+ 0, -1 /* wildcard/catchall */
+ };
+
+ u16 *p = rr_desc;
+
+ while (*p != type && *p != 0)
+ while (*p++ != (u16)-1);
+
+ return p+1;
+}
+
+int expand_workspace(unsigned char ***wkspc, int *szp, int new)
+{
+ unsigned char **p;
+ int old = *szp;
+
+ if (old >= new+1)
+ return 1;
+
+ if (new >= 100)
+ return 0;
+
+ new += 5;
+
+ if (!(p = whine_malloc(new * sizeof(unsigned char **))))
+ return 0;
+
+ if (old != 0 && *wkspc)
+ {
+ memcpy(p, *wkspc, old * sizeof(unsigned char **));
+ free(*wkspc);
+ }
+
+ *wkspc = p;
+ *szp = new;
+
+ return 1;
+}
--
1.7.10.4

View File

@@ -0,0 +1,134 @@
From 2dbba34b2c1289a108f876c78b84889f2a93115d Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 16 Dec 2015 13:41:58 +0000
Subject: [PATCH] DNSSEC validation tweak.
A zone which has at least one key with an algorithm we don't
support should be considered as insecure.
---
src/dnssec.c | 82 ++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 54 insertions(+), 28 deletions(-)
diff --git a/src/dnssec.c b/src/dnssec.c
index fa3eb81..dc563e0 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -763,10 +763,10 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
STAT_NEED_DS need DS to complete validation (name is returned in keyname)
- if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
+ If key is non-NULL, use that key, which has the algo and tag given in the params of those names,
otherwise find the key in the cache.
- name is unchanged on exit. keyname is used as workspace and trashed.
+ Name is unchanged on exit. keyname is used as workspace and trashed.
Call explore_rrset first to find and count RRs and sigs.
*/
@@ -919,6 +919,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
return STAT_BOGUS;
}
+
/* The DNS packet is expected to contain the answer to a DNSKEY query.
Put all DNSKEYs in the answer which are valid into the cache.
return codes:
@@ -1831,15 +1832,15 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
/* Check signing status of name.
returns:
- STAT_SECURE zone is signed.
- STAT_INSECURE zone proved unsigned.
- STAT_NEED_DS require DS record of name returned in keyname.
-
+ STAT_SECURE zone is signed.
+ STAT_INSECURE zone proved unsigned.
+ STAT_NEED_DS require DS record of name returned in keyname.
+ STAT_NEED_DNSKEY require DNSKEY record of name returned in keyname.
name returned unaltered.
*/
static int zone_status(char *name, int class, char *keyname, time_t now)
{
- int name_start = strlen(name);
+ int secure_ds, name_start = strlen(name);
struct crec *crecp;
char *p;
@@ -1850,27 +1851,52 @@ 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;
else
- 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 (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
- return STAT_INSECURE; /* algo we can't use - insecure */
- }
- }
- while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
-
+ {
+ secure_ds = 0;
+
+ 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 (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
+ return STAT_INSECURE; /* algo we can't use - insecure */
+ else
+ secure_ds = 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 && !algo_digest_name(crecp->addr.key.algo))
+ return STAT_INSECURE;
+ }
+ while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)));
+ }
+
if (name_start == 0)
break;
--
1.7.10.4