mirror of
https://github.com/vincentmli/bpfire.git
synced 2026-04-11 19:55:52 +02:00
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:
committed by
Michael Tremer
parent
78af2f67bb
commit
fbcc3cb784
@@ -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 \
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
134
src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
Normal file
134
src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user