Compare commits

..
56 Commits
Author SHA1 Message Date
Simon Kelley 81a883fda3 Format tweak. 2014-02-03 21:17:04 +00:00
Simon Kelley 40b695c1f1 Log NXDOMAIN correctly. 2014-02-03 17:07:51 +00:00
Simon Kelley 5f938534a9 Return configured DNSKEYs even though we don't have RRSIGS for them. 2014-02-03 16:44:32 +00:00
Simon Kelley 8d718cbb3e Nasty cache failure and memory leak with DNSSEC. 2014-02-03 16:27:37 +00:00
Simon Kelley f6a2b79310 Validate Ooops. 2014-02-01 14:54:26 +00:00
Simon Kelley 82e3f45a9f Blockdata fixes and tuning. 2014-01-31 21:05:48 +00:00
Simon Kelley 072e81b3c5 Blockdata leak. 2014-01-31 12:42:54 +00:00
Simon Kelley 1d97ac4fd2 copy-n-paste error. 2014-01-31 11:12:27 +00:00
Simon Kelley db73746620 Anounce DNSSEC at startup. 2014-01-31 10:32:45 +00:00
Simon Kelley 97bc798b05 Init ->dependent field in frec allocation. 2014-01-31 10:19:52 +00:00
Simon Kelley edc231bc58 Compiler warning. 2014-01-31 09:52:50 +00:00
Simon Kelley b85e092e23 Add a file containing current root trust anchors, for convenience. 2014-01-30 09:49:28 +00:00
Simon Kelley 583043f527 Crash in cache code when compiled with HAVE_DNSSEC. 2014-01-28 14:54:46 +00:00
Simon Kelley 8f6213cce9 Allow use of COPTS in Debian rules invokation for nefarious purposes. 2014-01-28 11:16:49 +00:00
Simon Kelley 00ec693db8 Debian package with DNSSEC now possible.
DNSSEC will eventually become opt-out and when that happens
I'll add libnettle build-depends. For now, build with

fakeroot debian/rules DEB_BUILD_OPTIONS=usednssec

to get DNSSEC support.
2014-01-28 11:08:57 +00:00
Simon Kelley 70b4a818ef Man page entries for DNSSEC flags. 2014-01-27 22:38:48 +00:00
Simon Kelley 7c28612a59 Trivial format fix. 2014-01-27 21:38:11 +00:00
Simon Kelley 6f4681034e Code tidy. 2014-01-26 23:39:17 +00:00
Simon Kelley 6938f3476e Don't mark answers as DNSEC validated if DNS-doctored. 2014-01-26 22:47:39 +00:00
Simon Kelley 17fb9ea763 Exclude CRC code in DNSSEC build - replaced with SHA1. 2014-01-26 09:36:54 +00:00
Simon Kelley 7d23a66ff0 Remove --dnssec-permissive, pointless if we don't set CD upstream. 2014-01-26 09:33:21 +00:00
Simon Kelley 703c7ff429 Fix to last commit. 2014-01-25 23:46:23 +00:00
Simon Kelley 8a9be9e493 Replace CRC32 with SHA1 for spoof detection in DNSSEC builds. 2014-01-25 23:17:21 +00:00
Simon Kelley c92f0083a2 Get AA flag right in DNSSEC answers from cache. 2014-01-25 18:43:59 +00:00
Simon Kelley b5dbfd142a RRSIG answer logging. 2014-01-25 18:19:51 +00:00
Simon Kelley cbf13a2a6d Class specifier in --dnskey, instead of hardwiring C_IN. 2014-01-25 17:59:14 +00:00
Simon Kelley 5b3bf92101 --dnssec-debug 2014-01-25 17:03:07 +00:00
Simon Kelley 0744ca66ad More DNSSEC caching logic, and avoid repeated validation of DS/DNSKEY 2014-01-25 16:40:15 +00:00
Simon Kelley 2d33bda2e6 RRSIGS for PTR records from cache. 2014-01-24 22:37:25 +00:00
Simon Kelley 32f90c0fad Tweak. 2014-01-24 10:37:36 +00:00
Simon Kelley bce6e1bc6d RRSIGs in DS and DNSKEY cached answers. 2014-01-23 22:02:19 +00:00
Simon Kelley 824202ef54 More DNSSEC cache readout. 2014-01-23 20:59:46 +00:00
Simon Kelley 9ebfca1e84 Compiler warning. 2014-01-23 12:11:43 +00:00
Simon Kelley 6429e421b3 Compiler warning. 2014-01-23 12:09:36 +00:00
Simon Kelley c9bfa948c3 remove redundant headerage 2014-01-22 22:32:33 +00:00
Simon Kelley e7829aefd8 Cache RRSIGS. 2014-01-22 22:21:51 +00:00
Simon Kelley 51ea3ca254 Caching of DNSSEC records. 2014-01-22 19:31:38 +00:00
Jonas Gorski 57ab36e77d Tweak definition of a permanent IPv6 address on Linux.
The linux kernel treats all addresses with a limited lifetime as being
non permanent, but when taking over the prefix livetimes from
upstream assigned prefixes through DHCP, addresses will always have a limited
lifetime.

Still reject temporary addresses, as they indicate autoconfigured
interfaces.

Contributed by T-Labs, Deutsche Telekom Innovation Laboratories

Signed-off-by: Jonas Gorski<jogo@openwrt.org>
2014-01-22 11:34:16 +00:00
Simon Kelley dd0e0a3995 Handle time_t wraparound more sanely. 2014-01-22 11:16:59 +00:00
Simon Kelley 6fd6dacb39 Fix loop in RR sort. 2014-01-21 20:17:40 +00:00
Simon Kelley 39048ad10b bug fix, avoids infinite loop in forwarding code. 2014-01-21 17:33:58 +00:00
Simon Kelley 979cdf9b64 Fix to hostname_cmp, and update to canonicalisation table. RFC 4034 LIES. 2014-01-21 16:26:41 +00:00
Simon Kelley dbf721235b Rationalise hostname_cmp() 2014-01-21 14:28:02 +00:00
Simon Kelley c979fa04a4 Provide for static library linking. 2014-01-21 13:45:17 +00:00
Simon Kelley c5f4ec7d23 NSEC proof-of-non-existence. 2014-01-20 22:37:55 +00:00
Simon Kelley 5d3b87a484 Better handling of truncated DNSSEC replies. 2014-01-20 11:57:23 +00:00
Simon Kelley 72ae2f3d56 Don't validate error returns. 2014-01-19 09:54:16 +00:00
Simon Kelley 6c0cb858c1 Trivial format fix 2014-01-17 14:40:46 +00:00
Simon Kelley e0c0ad3b5e UDP retries for DNSSEC 2014-01-16 22:42:07 +00:00
Simon Kelley 4619d94622 Fix SEGV and failure to validate on x86_64. 2014-01-16 19:53:06 +00:00
Simon Kelley 0975a58e9b Merge branch 'master' of ssh://central/var/cache/git/dnsmasq 2014-01-15 17:12:08 +00:00
Simon Kelley a25720a34a protocol handling for DNSSEC 2014-01-14 23:13:55 +00:00
Simon Kelley cc111e0bab Add ip6addr.h to Makefile list. 2014-01-13 21:38:19 +00:00
Simon Kelley 86bec2d399 Swap crypto library from openSSL to nettle. 2014-01-13 21:31:20 +00:00
Simon Kelley a59ff5f3df Merge branch 'master' of ssh://central/var/cache/git/dnsmasq 2014-01-12 22:36:12 +00:00
Simon Kelley 6ea1f23b3f Send correct O and M bits when advertising only deprecated prefixes. 2013-12-19 15:45:12 +00:00
20 changed files with 1519 additions and 795 deletions

No files matched your search

+8 -8
View File
@@ -59,8 +59,8 @@ ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFI
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack` ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1` lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1` lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
sec_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags libcrypto` nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed`
sec_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs libcrypto` nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs nettle hogweed`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi` sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"' version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
@@ -68,16 +68,16 @@ 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 \ 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 \ 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 \ dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o dnssec-openssl.o blockdata.o domain.o dnssec.o blockdata.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h dns-protocol.h radv-protocol.h ip6addr.h
all : $(BUILDDIR) all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \ @cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \ top="$(top)" \
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(sec_cflags)" \ build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(sec_libs)" \ build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs)" \
-f $(top)/Makefile dnsmasq -f $(top)/Makefile dnsmasq
mostly_clean : mostly_clean :
@@ -100,8 +100,8 @@ all-i18n : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \ @cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \ top="$(top)" \
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \ i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
build_cflags="$(version) $(dbus_cflags) $(ct_cflags) $(lua_cflags) `$(PKG_CONFIG) --cflags libidn`" \ build_cflags="$(version) $(dbus_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) `$(PKG_CONFIG) --cflags libidn`" \
build_libs="$(dbus_libs) $(ct_libs) $(lua_libs) $(sunos_libs) `$(PKG_CONFIG) --libs libidn`" \ build_libs="$(dbus_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) `$(PKG_CONFIG) --libs libidn`" \
-f $(top)/Makefile dnsmasq -f $(top)/Makefile dnsmasq
for f in `cd $(PO); echo *.po`; do \ for f in `cd $(PO); echo *.po`; do \
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \ cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
+19 -3
View File
@@ -2,10 +2,26 @@
search=$1 search=$1
shift shift
pkg=$1
shift
op=$1
shift
if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \ in=`cat`
grep $search >/dev/null 2>&1; then
exec $* if grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \
echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
if [ $op = "--libs" ]; then
pkg=`$pkg --static $op $*`
echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic"
exit 0
fi
fi fi
if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \
echo $in | grep $search >/dev/null 2>&1; then
pkg=`$pkg $op $*`
echo "$pkg"
fi
+18 -14
View File
@@ -17,7 +17,7 @@ CFLAGS += -Wall -W
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS) LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
COPTS = DEB_COPTS = $(COPTS)
TARGET = install-i18n TARGET = install-i18n
@@ -29,52 +29,56 @@ ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS)))
endif endif
ifeq (,$(filter nodbus,$(DEB_BUILD_OPTIONS))) ifeq (,$(filter nodbus,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_DBUS DEB_COPTS += -DHAVE_DBUS
endif endif
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS))) ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_BUILD_ARCH_OS),linux) ifeq ($(DEB_BUILD_ARCH_OS),linux)
COPTS += -DHAVE_CONNTRACK DEB_COPTS += -DHAVE_CONNTRACK
endif endif
endif endif
ifneq (,$(filter noipset,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter noipset,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_IPSET DEB_COPTS += -DNO_IPSET
endif endif
ifneq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP6 DEB_COPTS += -DNO_DHCP6
endif endif
ifneq (,$(filter noipv6,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter noipv6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_IPV6 DEB_COPTS += -DNO_IPV6
endif endif
ifneq (,$(filter notftp,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter notftp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_TFTP DEB_COPTS += -DNO_TFTP
endif endif
ifneq (,$(filter nodhcp,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter nodhcp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP DEB_COPTS += -DNO_DHCP
endif endif
ifneq (,$(filter noscript,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter noscript,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_SCRIPT DEB_COPTS += -DNO_SCRIPT
endif endif
ifneq (,$(filter nortc,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter nortc,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_BROKEN_RTC DEB_COPTS += -DHAVE_BROKEN_RTC
endif endif
ifneq (,$(filter noi18n,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter noi18n,$(DEB_BUILD_OPTIONS)))
TARGET = install TARGET = install
ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS))) ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_IDN DEB_COPTS += -DHAVE_IDN
endif endif
endif endif
ifneq (,$(filter uselua,$(DEB_BUILD_OPTIONS))) ifneq (,$(filter uselua,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_LUASCRIPT DEB_COPTS += -DHAVE_LUASCRIPT
endif
ifneq (,$(filter usednssec,$(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_DNSSEC
endif endif
clean: clean:
@@ -123,7 +127,7 @@ binary-arch: checkroot
-d debian/base/usr/share/doc/$(package)/examples \ -d debian/base/usr/share/doc/$(package)/examples \
-d debian/base/var/run \ -d debian/base/var/run \
-d debian/base/var/lib/misc -d debian/base/var/lib/misc
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS))) ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 644 doc.html debian/base/usr/share/doc/$(package)/. install -m 644 doc.html debian/base/usr/share/doc/$(package)/.
install -m 644 setup.html debian/base/usr/share/doc/$(package)/. install -m 644 setup.html debian/base/usr/share/doc/$(package)/.
@@ -167,7 +171,7 @@ ifeq ($(DEB_BUILD_ARCH_OS),linux)
-d debian/utils/usr/share/man/man1 \ -d debian/utils/usr/share/man/man1 \
-d debian/utils/usr/bin \ -d debian/utils/usr/bin \
-d debian/utils/usr/share/doc/dnsmasq-utils -d debian/utils/usr/share/doc/dnsmasq-utils
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1 install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
gzip -9 debian/utils/usr/share/man/man1/dhcp_release.1 gzip -9 debian/utils/usr/share/man/man1/dhcp_release.1
+22 -12
View File
@@ -579,19 +579,29 @@ Set the maximum number of concurrent DNS queries. The default value is
where this needs to be increased is when using web-server log file where this needs to be increased is when using web-server log file
resolvers, which can generate large numbers of concurrent queries. resolvers, which can generate large numbers of concurrent queries.
.TP .TP
.B --dnssec
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
DNSSEC records needed to validate the replies. The replies are validated and the result returned as
the Authenticated Data bit in the DNS packet. In addition the DNSSEC records are stored in the cache, making
validation by clients more efficient. Note that validation by clients is the most secure DNSSEC mode, but for
clients unable to do validation, use of the AD bit set by dnsmasq is useful, provided that the network between
the dnsmasq server and the client is trusted. Dnsmasq must be compiled with HAVE_DNSSEC enabled, and DNSSEC
trust anchors provided, see
.B --dnsskey.
Because the DNSSEC validation process uses the cache, it is not permitted to reduce the cache size below the default when DNSSEC is enabled.
.TP
.B --dnskey=[<class>],<domain>,<flags>,<algorithm>,<base64-key>
Provide DNSKEY records to act a trust anchors for DNSSEC validation. Typically these will be the keys for root zone,
but trust anchors for limited domains are also possible.
.TP
.B --proxy-dnssec .B --proxy-dnssec
A resolver on a client machine can do DNSSEC validation in two ways: it Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
can perform the cryptograhic operations on the reply it receives, or alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between
it can rely on the upstream recursive nameserver to do the validation dnsmasq and the upstream servers, and the trustworthiness of the upstream servers.
and set a bit in the reply if it succeeds. Dnsmasq is not a DNSSEC .TP
validator, so it cannot perform the validation role of the recursive nameserver, .B --dnssec-debug
but it can pass through the validation results from its own upstream Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries,
nameservers. This option enables this behaviour. You should only do and don't convert BOGUS replies to SERVFAIL responses.
this if you trust all the configured upstream nameservers
.I and the network between you and them.
If you use the first DNSSEC mode, validating resolvers in clients,
this option is not required. Dnsmasq always returns all the data
needed for a client to do validation itself.
.TP .TP
.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]] .B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
+63 -36
View File
@@ -18,13 +18,42 @@
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
static struct blockdata *keyblock_free = NULL; static struct blockdata *keyblock_free;
static unsigned int blockdata_count = 0, blockdata_hwm = 0; static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
static void blockdata_expand(int n)
{
struct blockdata *new = whine_malloc(n * sizeof(struct blockdata));
if (new)
{
int i;
new[n-1].next = keyblock_free;
keyblock_free = new;
for (i = 0; i < n - 1; i++)
new[i].next = &new[i+1];
blockdata_alloced += n;
}
}
/* Preallocate some blocks, proportional to cachesize, to reduce heap fragmentation. */
void blockdata_init(void)
{
keyblock_free = NULL;
blockdata_alloced = 0;
blockdata_count = 0;
blockdata_hwm = 0;
blockdata_expand((daemon->cachesize * 100) / sizeof(struct blockdata));
}
void blockdata_report(void) void blockdata_report(void)
{ {
my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u"), my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u, allocated %u"),
blockdata_count * sizeof(struct blockdata), blockdata_hwm * sizeof(struct blockdata)); blockdata_count * sizeof(struct blockdata), blockdata_hwm * sizeof(struct blockdata), blockdata_alloced * sizeof(struct blockdata));
} }
struct blockdata *blockdata_alloc(char *data, size_t len) struct blockdata *blockdata_alloc(char *data, size_t len)
@@ -35,26 +64,25 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
while (len > 0) while (len > 0)
{ {
if (!keyblock_free)
blockdata_expand(50);
if (keyblock_free) if (keyblock_free)
{ {
block = keyblock_free; block = keyblock_free;
keyblock_free = block->next; keyblock_free = block->next;
blockdata_count++; blockdata_count++;
} }
else if ((block = whine_malloc(sizeof(struct blockdata)))) else
{
blockdata_count++;
if (blockdata_hwm < blockdata_count)
blockdata_hwm = blockdata_count;
}
if (!block)
{ {
/* failed to alloc, free partial chain */ /* failed to alloc, free partial chain */
blockdata_free(ret); blockdata_free(ret);
return NULL; return NULL;
} }
if (blockdata_hwm < blockdata_count)
blockdata_hwm = blockdata_count;
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
memcpy(block->key, data, blen); memcpy(block->key, data, blen);
data += blen; data += blen;
@@ -67,21 +95,6 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
return ret; return ret;
} }
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt)
{
if (*p == NULL)
*p = (*key)->key;
else if (*p == (*key)->key + KEYBLOCK_LEN)
{
*key = (*key)->next;
if (*key == NULL)
return 0;
*p = (*key)->key;
}
return MIN(cnt, (size_t)((*key)->key + KEYBLOCK_LEN - (*p)));
}
void blockdata_free(struct blockdata *blocks) void blockdata_free(struct blockdata *blocks)
{ {
struct blockdata *tmp; struct blockdata *tmp;
@@ -96,24 +109,38 @@ void blockdata_free(struct blockdata *blocks)
} }
} }
/* copy blocks into data[], return 1 if data[] unchanged by so doing */ /* if data == NULL, return pointer to static block of sufficient size */
int blockdata_retrieve(struct blockdata *block, size_t len, void *data) void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
{ {
size_t blen; size_t blen;
struct blockdata *b; struct blockdata *b;
int match = 1; void *new, *d;
for (b = block; len > 0 && b; b = b->next) static unsigned int buff_len = 0;
static unsigned char *buff = NULL;
if (!data)
{
if (len > buff_len)
{
if (!(new = whine_malloc(len)))
return NULL;
if (buff)
free(buff);
buff = new;
}
data = buff;
}
for (d = data, b = block; len > 0 && b; b = b->next)
{ {
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
if (memcmp(data, b->key, blen) != 0) memcpy(d, b->key, blen);
match = 0; d += blen;
memcpy(data, b->key, blen);
data += blen;
len -= blen; len -= blen;
} }
return match; return data;
} }
#endif #endif
+124 -43
View File
@@ -171,6 +171,21 @@ static void cache_hash(struct crec *crecp)
*up = crecp; *up = crecp;
} }
#ifdef HAVE_DNSSEC
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);
}
else if (crecp->flags & F_DS)
blockdata_free(crecp->addr.ds.keydata);
}
#endif
static void cache_free(struct crec *crecp) static void cache_free(struct crec *crecp)
{ {
crecp->flags &= ~F_FORWARD; crecp->flags &= ~F_FORWARD;
@@ -195,9 +210,9 @@ static void cache_free(struct crec *crecp)
big_free = crecp->name.bname; big_free = crecp->name.bname;
crecp->flags &= ~F_BIGNAME; crecp->flags &= ~F_BIGNAME;
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
else if (crecp->flags & (F_DNSKEY | F_DS)) cache_blockdata_free(crecp);
blockdata_free(crecp->addr.key.keydata);
#endif #endif
} }
@@ -316,6 +331,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
if (flags & F_FORWARD) if (flags & F_FORWARD)
{ {
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next) for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
{
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp)) if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
{ {
*up = crecp->hash_next; *up = crecp->hash_next;
@@ -324,20 +340,44 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
cache_unlink(crecp); cache_unlink(crecp);
cache_free(crecp); cache_free(crecp);
} }
continue;
} }
else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) && if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
hostname_isequal(cache_get_name(crecp), name)) {
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) ||
(((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
{ {
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
return 0; return 0;
*up = crecp->hash_next; *up = crecp->hash_next;
cache_unlink(crecp); cache_unlink(crecp);
cache_free(crecp); cache_free(crecp);
continue;
}
#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))
{
if (crecp->flags & F_CONFIG)
return 0;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
continue;
}
#endif
} }
else
up = &crecp->hash_next; up = &crecp->hash_next;
} }
}
else else
{ {
int i; int i;
@@ -409,8 +449,8 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl) if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl; ttl = daemon->max_cache_ttl;
/* Don't log keys */ /* Don't log keys here, done elsewhere */
if (flags & (F_IPV4 | F_IPV6)) if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
log_query(flags | F_UPSTREAM, name, addr, NULL); log_query(flags | F_UPSTREAM, name, addr, NULL);
/* if previous insertion failed give up now. */ /* if previous insertion failed give up now. */
@@ -446,14 +486,32 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
insert. Once in this state, all inserts will probably fail. */ insert. Once in this state, all inserts will probably fail. */
if (free_avail) if (free_avail)
{ {
static int warned = 0;
if (!warned)
{
my_syslog(LOG_ERR, _("Internal error in cache."));
warned = 1;
}
insert_error = 1; insert_error = 1;
return NULL; return NULL;
} }
if (freed_all) if (freed_all)
{ {
struct all_addr free_addr = new->addr.addr;;
#ifdef HAVE_DNSSEC
/* For DNSSEC records, addr holds class and type_covered for RRSIG */
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;
}
#endif
free_avail = 1; /* Must be free space now. */ free_avail = 1; /* Must be free space now. */
cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags); cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
cache_live_freed++; cache_live_freed++;
} }
else else
@@ -465,7 +523,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
} }
/* Check if we need to and can allocate extra memory for a long name. /* Check if we need to and can allocate extra memory for a long name.
If that fails, give up now. */ If that fails, give up now, always succeed for DNSSEC records. */
if (name && (strlen(name) > SMALLDNAME-1)) if (name && (strlen(name) > SMALLDNAME-1))
{ {
if (big_free) if (big_free)
@@ -473,13 +531,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
big_name = big_free; big_name = big_free;
big_free = big_free->next; big_free = big_free->next;
} }
else if (!bignames_left || else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
!(big_name = (union bigname *)whine_malloc(sizeof(union bigname)))) !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
{ {
insert_error = 1; insert_error = 1;
return NULL; return NULL;
} }
else else if (bignames_left != 0)
bignames_left--; bignames_left--;
} }
@@ -554,6 +612,9 @@ 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 (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{ {
if ((crecp->flags & F_FORWARD) && if ((crecp->flags & F_FORWARD) &&
#ifdef HAVE_DNSSEC
((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
#endif
(crecp->flags & prot) && (crecp->flags & prot) &&
hostname_isequal(cache_get_name(crecp), name)) hostname_isequal(cache_get_name(crecp), name))
{ {
@@ -611,6 +672,9 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (ans && if (ans &&
(ans->flags & F_FORWARD) && (ans->flags & F_FORWARD) &&
#ifdef HAVE_DNSSEC
((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
#endif
(ans->flags & prot) && (ans->flags & prot) &&
hostname_isequal(cache_get_name(ans), name)) hostname_isequal(cache_get_name(ans), name))
return ans; return ans;
@@ -692,7 +756,7 @@ static void add_hosts_cname(struct crec *target)
if (hostname_isequal(cache_get_name(target), a->target) && if (hostname_isequal(cache_get_name(target), a->target) &&
(crec = whine_malloc(sizeof(struct crec)))) (crec = whine_malloc(sizeof(struct crec))))
{ {
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME | F_DNSSECOK; crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
crec->name.namep = a->alias; crec->name.namep = a->alias;
crec->addr.cname.target.cache = target; crec->addr.cname.target.cache = target;
crec->addr.cname.uid = target->uid; crec->addr.cname.uid = target->uid;
@@ -829,14 +893,14 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec
if (inet_pton(AF_INET, token, &addr) > 0) if (inet_pton(AF_INET, token, &addr) > 0)
{ {
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_DNSSECOK; flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ; addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4); domain_suffix = get_domain(addr.addr.addr4);
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, token, &addr) > 0) else if (inet_pton(AF_INET6, token, &addr) > 0)
{ {
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_DNSSECOK; flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ; addrlen = IN6ADDRSZ;
domain_suffix = get_domain6(&addr.addr.addr6); domain_suffix = get_domain6(&addr.addr.addr6);
} }
@@ -925,8 +989,7 @@ void cache_reload(void)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp) for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
{ {
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (cache->flags & (F_DNSKEY | F_DS)) cache_blockdata_free(cache);
blockdata_free(cache->addr.key.keydata);
#endif #endif
tmp = cache->hash_next; tmp = cache->hash_next;
if (cache->flags & (F_HOSTS | F_CONFIG)) if (cache->flags & (F_HOSTS | F_CONFIG))
@@ -954,7 +1017,7 @@ void cache_reload(void)
if (hostname_isequal(a->target, intr->name) && if (hostname_isequal(a->target, intr->name) &&
((cache = whine_malloc(sizeof(struct crec))))) ((cache = whine_malloc(sizeof(struct crec)))))
{ {
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG | F_DNSSECOK; cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
cache->name.namep = a->alias; cache->name.namep = a->alias;
cache->addr.cname.target.int_name = intr; cache->addr.cname.target.int_name = intr;
cache->addr.cname.uid = -1; cache->addr.cname.uid = -1;
@@ -969,9 +1032,11 @@ void cache_reload(void)
{ {
cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP; cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP;
cache->name.namep = key->name; cache->name.namep = key->name;
cache->uid = key->keylen; cache->addr.key.keylen = key->keylen;
cache->addr.key.algo = key->algo; cache->addr.key.algo = key->algo;
cache->addr.key.flags = key->flags;
cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen); cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
cache->uid = key->class;
cache_hash(cache); cache_hash(cache);
} }
#endif #endif
@@ -990,7 +1055,7 @@ void cache_reload(void)
(cache = whine_malloc(sizeof(struct crec)))) (cache = whine_malloc(sizeof(struct crec))))
{ {
cache->name.namep = nl->name; cache->name.namep = nl->name;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG | F_DNSSECOK; cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, 0, (struct crec **)daemon->packet, revhashsz); add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
@@ -998,7 +1063,7 @@ void cache_reload(void)
(cache = whine_malloc(sizeof(struct crec)))) (cache = whine_malloc(sizeof(struct crec))))
{ {
cache->name.namep = nl->name; cache->name.namep = nl->name;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG | F_DNSSECOK; cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, 0, (struct crec **)daemon->packet, revhashsz); add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
} }
#endif #endif
@@ -1068,7 +1133,7 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
if (aliasc) if (aliasc)
{ {
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG | F_DNSSECOK; aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG;
if (ttd == 0) if (ttd == 0)
aliasc->flags |= F_IMMORTAL; aliasc->flags |= F_IMMORTAL;
else else
@@ -1156,7 +1221,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
if (crec) /* malloc may fail */ if (crec) /* malloc may fail */
{ {
crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD | F_DNSSECOK; crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
if (ttd == 0) if (ttd == 0)
crec->flags |= F_IMMORTAL; crec->flags |= F_IMMORTAL;
else else
@@ -1175,6 +1240,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
void dump_cache(time_t now) void dump_cache(time_t now)
{ {
struct server *serv, *serv1; struct server *serv, *serv1;
char *t = "";
my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now); my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."), my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
@@ -1222,22 +1288,35 @@ void dump_cache(time_t now)
{ {
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache); char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
*a = 0; *a = 0;
if (strlen(n) == 0) if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>"; n = "<Root>";
p += sprintf(p, "%-40.40s ", n); p += sprintf(p, "%-40.40s ", n);
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
a = cache_get_cname_target(cache); a = cache_get_cname_target(cache);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
if (cache->flags & F_DNSKEY)
{
char tp[20];
/* RRSIG */
querystr("", tp, cache->addr.sig.type_covered);
a = daemon->addrbuff;
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, tp);
}
else
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
}
else if (cache->flags & F_DNSKEY) else if (cache->flags & F_DNSKEY)
{
a = daemon->addrbuff;
sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid);
}
else if (cache->flags & F_DS)
{ {
a = daemon->addrbuff; a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag, sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.digest); cache->addr.key.algo, cache->addr.key.flags);
} }
#endif #endif
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD)) else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
@@ -1251,12 +1330,21 @@ void dump_cache(time_t now)
#endif #endif
} }
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a, if (cache->flags & F_IPV4)
cache->flags & F_IPV4 ? "4" : "", t = "4";
cache->flags & F_IPV6 ? "6" : "", else if (cache->flags & F_IPV6)
cache->flags & F_DNSKEY ? "K" : "", t = "6";
cache->flags & F_DS ? "S" : "", else if (cache->flags & F_CNAME)
cache->flags & F_CNAME ? "C" : "", 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)
t = "K";
#endif
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s ", a, t,
cache->flags & F_FORWARD ? "F" : " ", cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ", cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ", cache->flags & F_IMMORTAL ? "I" : " ",
@@ -1335,14 +1423,7 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (flags & F_NEG) if (flags & F_NEG)
{ {
if (flags & F_NXDOMAIN) if (flags & F_NXDOMAIN)
{
if (flags & F_IPV4)
dest = "NXDOMAIN-IPv4";
else if (flags & F_IPV6)
dest = "NXDOMAIN-IPv6";
else
dest = "NXDOMAIN"; dest = "NXDOMAIN";
}
else else
{ {
if (flags & F_IPV4) if (flags & F_IPV4)
+13 -1
View File
@@ -124,6 +124,11 @@ RESOLVFILE
*/ */
/* Defining this builds a binary which handles time differently and works better on a system without a
stable RTC (it uses uptime, not epoch time) and writes the DHCP leases file less often to avoid flash wear.
*/
/* #define HAVE_BROKEN_RTC */
/* The default set of options to build. Built with these options, dnsmasq /* The default set of options to build. Built with these options, dnsmasq
has no library dependencies other than libc */ has no library dependencies other than libc */
@@ -134,8 +139,15 @@ RESOLVFILE
#define HAVE_SCRIPT #define HAVE_SCRIPT
#define HAVE_AUTH #define HAVE_AUTH
#define HAVE_IPSET #define HAVE_IPSET
/* Build options which require external libraries.
Defining HAVE_<opt>_STATIC as _well_ as HAVE_<opt> will link the library statically.
You can use "make COPTS=-DHAVE_<opt>" instead of editing these.
*/
/* #define HAVE_LUASCRIPT */ /* #define HAVE_LUASCRIPT */
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */ /* #define HAVE_DBUS */
/* #define HAVE_IDN */ /* #define HAVE_IDN */
/* #define HAVE_CONNTRACK */ /* #define HAVE_CONNTRACK */
+1 -19
View File
@@ -36,6 +36,7 @@
#define C_IN 1 /* the arpa internet */ #define C_IN 1 /* the arpa internet */
#define C_CHAOS 3 /* for chaos net (MIT) */ #define C_CHAOS 3 /* for chaos net (MIT) */
#define C_HESIOD 4 /* hesiod */
#define C_ANY 255 /* wildcard match */ #define C_ANY 255 /* wildcard match */
#define T_A 1 #define T_A 1
@@ -76,7 +77,6 @@
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */ #define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */ #define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
struct dns_header { struct dns_header {
u16 id; u16 id;
u8 hb3,hb4; u8 hb3,hb4;
@@ -136,24 +136,6 @@ struct dns_header {
(cp) += 4; \ (cp) += 4; \
} }
#define CHECKED_GETCHAR(var, ptr, len) do { \
if ((len) < 1) return 0; \
var = *ptr++; \
(len) -= 1; \
} while (0)
#define CHECKED_GETSHORT(var, ptr, len) do { \
if ((len) < 2) return 0; \
GETSHORT(var, ptr); \
(len) -= 2; \
} while (0)
#define CHECKED_GETLONG(var, ptr, len) do { \
if ((len) < 4) return 0; \
GETLONG(var, ptr); \
(len) -= 4; \
} while (0)
#define CHECK_LEN(header, pp, plen, len) \ #define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen)) ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
+19 -1
View File
@@ -141,10 +141,18 @@ int main (int argc, char **argv)
} }
#endif #endif
if (option_bool(OPT_DNSSEC_VALID))
{
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (daemon->cachesize <CACHESIZ && option_bool(OPT_DNSSEC_VALID)) if (!daemon->dnskeys)
die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
if (daemon->cachesize < CACHESIZ)
die(_("Cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF); die(_("Cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF);
#else
die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF);
#endif #endif
}
#ifndef HAVE_TFTP #ifndef HAVE_TFTP
if (option_bool(OPT_TFTP)) if (option_bool(OPT_TFTP))
@@ -296,7 +304,12 @@ int main (int argc, char **argv)
#endif #endif
if (daemon->port != 0) if (daemon->port != 0)
{
cache_init(); cache_init();
#ifdef HAVE_DNSSEC
blockdata_init();
#endif
}
if (option_bool(OPT_DBUS)) if (option_bool(OPT_DBUS))
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
@@ -646,6 +659,11 @@ int main (int argc, char **argv)
} }
#endif #endif
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
#endif
if (log_err != 0) if (log_err != 0)
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"), my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
daemon->log_file, strerror(log_err)); daemon->log_file, strerror(log_err));
+34 -13
View File
@@ -230,7 +230,9 @@ struct event_desc {
#define OPT_QUIET_DHCP6 43 #define OPT_QUIET_DHCP6 43
#define OPT_QUIET_RA 44 #define OPT_QUIET_RA 44
#define OPT_DNSSEC_VALID 45 #define OPT_DNSSEC_VALID 45
#define OPT_LAST 46 #define OPT_DNSSEC_PERMISS 46
#define OPT_DNSSEC_DEBUG 47
#define OPT_LAST 48
/* extra flags for my_syslog, we use a couple of facilities since they are known /* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */ not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -243,7 +245,12 @@ struct all_addr {
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
struct in6_addr addr6; struct in6_addr addr6;
#endif #endif
/* for log_query */
unsigned int keytag; unsigned int keytag;
/* for cache_insert if RRSIG, DNSKEY, DS */
struct {
unsigned short class, type;
} dnssec;
} addr; } addr;
}; };
@@ -290,7 +297,7 @@ struct cname {
struct dnskey { struct dnskey {
char *name, *key; char *name, *key;
int keylen, algo, flags; int keylen, class, algo, flags;
struct dnskey *next; struct dnskey *next;
}; };
@@ -362,13 +369,23 @@ struct crec {
} cname; } cname;
struct { struct {
struct blockdata *keydata; struct blockdata *keydata;
unsigned short keylen, flags, keytag;
unsigned char algo; unsigned char algo;
unsigned char digest; /* DS only */
unsigned short keytag;
} key; } key;
struct {
struct blockdata *keydata;
unsigned short keylen, keytag;
unsigned char algo;
unsigned char digest;
} ds;
struct {
struct blockdata *keydata;
unsigned short keylen, type_covered, keytag;
char algo;
} sig;
} addr; } addr;
time_t ttd; /* time to die */ time_t ttd; /* time to die */
/* used as keylen ifF_DNSKEY, index to source for F_HOSTS */ /* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
int uid; int uid;
unsigned short flags; unsigned short flags;
union { union {
@@ -407,10 +424,6 @@ struct crec {
#define F_KEYTAG (1u<<23) #define F_KEYTAG (1u<<23)
#define F_SECSTAT (1u<<24) #define F_SECSTAT (1u<<24)
/* composites */
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
/* struct sockaddr is not large enough to hold any address, /* struct sockaddr is not large enough to hold any address,
and specifically not big enough to hold an IPv6 address. and specifically not big enough to hold an IPv6 address.
@@ -518,6 +531,7 @@ struct hostsfile {
#define STAT_BOGUS 3 #define STAT_BOGUS 3
#define STAT_NEED_DS 4 #define STAT_NEED_DS 4
#define STAT_NEED_KEY 5 #define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define FREC_NOREBIND 1 #define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2 #define FREC_CHECKING_DISABLED 2
@@ -525,6 +539,12 @@ struct hostsfile {
#define FREC_DNSKEY_QUERY 8 #define FREC_DNSKEY_QUERY 8
#define FREC_DS_QUERY 16 #define FREC_DS_QUERY 16
#ifdef HAVE_DNSSEC
#define HASH_SIZE 20 /* SHA-1 digest size */
#else
#define HASH_SIZE sizeof(int)
#endif
struct frec { struct frec {
union mysockaddr source; union mysockaddr source;
struct all_addr dest; struct all_addr dest;
@@ -536,8 +556,8 @@ struct frec {
unsigned int iface; unsigned int iface;
unsigned short orig_id, new_id; unsigned short orig_id, new_id;
int fd, forwardall, flags; int fd, forwardall, flags;
unsigned int crc;
time_t time; time_t time;
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
int class; int class;
struct blockdata *stash; /* Saved reply, whilst we validate */ struct blockdata *stash; /* Saved reply, whilst we validate */
@@ -991,10 +1011,10 @@ struct crec *cache_enumerate(int init);
/* blockdata.c */ /* blockdata.c */
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
void blockdata_init(void);
void blockdata_report(void); void blockdata_report(void);
struct blockdata *blockdata_alloc(char *data, size_t len); struct blockdata *blockdata_alloc(char *data, size_t len);
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt); void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
int blockdata_retrieve(struct blockdata *block, size_t len, void *data);
void blockdata_free(struct blockdata *blocks); void blockdata_free(struct blockdata *blocks);
#endif #endif
@@ -1018,7 +1038,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
unsigned long local_ttl); unsigned long local_ttl);
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff, int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
time_t now, char **ipsets, int is_sign, int checkrebind, time_t now, char **ipsets, int is_sign, int checkrebind,
int no_cache, int secure); int no_cache, int secure, int *doctored);
size_t answer_request(struct dns_header *header, char *limit, size_t qlen, size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now); struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
@@ -1057,6 +1077,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class); int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class);
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen); int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
/* util.c */ /* util.c */
void rand_init(void); void rand_init(void);
-83
View File
@@ -1,83 +0,0 @@
/* dnssec-crypto.h is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
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/>.
*/
#ifndef DNSSEC_CRYPTO_H
#define DNSSEC_CRYPTO_H
struct blockdata;
/*
* vtable for a signature verification algorithm.
*
* Each algorithm verifies that a certain signature over a (possibly non-contigous)
* array of data has been made with the specified key.
*
* Sample of usage:
*
* // First, set the signature we need to check. Notice: data is not copied
* // nor consumed, so the pointer must stay valid.
* alg->set_signature(sig, 16);
*
* // Second, get push the data through the corresponding digest algorithm;
* // data is consumed immediately, so the buffers can be freed or modified.
* digestalg_begin(alg->get_digestalgo());
* digestalg_add_data(buf1, 123);
* digestalg_add_data(buf2, 45);
* digestalg_add_data(buf3, 678);
* alg->set_digest(digestalg_final());
*
* // Third, verify if we got the correct key for this signature.
* alg->verify(key1, 16);
* alg->verify(key2, 16);
*/
typedef struct VerifyAlgCtx VerifyAlgCtx;
typedef struct
{
int digest_algo;
int (*verify)(VerifyAlgCtx *ctx, struct blockdata *key, unsigned key_len);
} VerifyAlg;
struct VerifyAlgCtx
{
const VerifyAlg *vtbl;
unsigned char *sig;
size_t siglen;
unsigned char digest[64]; /* TODO: if memory problems, use VLA */
};
int verifyalg_supported(int algo);
VerifyAlgCtx* verifyalg_alloc(int algo);
void verifyalg_free(VerifyAlgCtx *a);
int verifyalg_algonum(VerifyAlgCtx *a);
/* Functions to calculate the digest of a key */
/* RFC4034 digest algorithms */
#define DIGESTALG_SHA1 1
#define DIGESTALG_SHA256 2
#define DIGESTALG_MD5 256
#define DIGESTALG_SHA512 257
int digestalg_supported(int algo);
void digestalg_begin(int algo);
void digestalg_add_data(void *data, unsigned len);
void digestalg_add_keydata(struct blockdata *key, size_t len);
unsigned char *digestalg_final(void);
int digestalg_len(void);
#endif /* DNSSEC_CRYPTO_H */
-316
View File
@@ -1,316 +0,0 @@
/* dnssec-openssl.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
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/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DNSSEC
#include "dnssec-crypto.h"
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/err.h>
#include <string.h>
#define POOL_SIZE 1
static union _Pool
{
VerifyAlgCtx ctx;
} Pool[POOL_SIZE];
static char pool_used = 0;
static void print_hex(unsigned char *data, unsigned len)
{
while (len > 0)
{
printf("%02x", *data++);
--len;
}
printf("\n");
}
static int keydata_to_bn(BIGNUM *ret, struct blockdata **key_data, unsigned char **p, unsigned len)
{
size_t cnt;
BIGNUM temp;
BN_init(ret);
cnt = blockdata_walk(key_data, p, len);
BN_bin2bn(*p, cnt, ret);
len -= cnt;
*p += cnt;
while (len > 0)
{
if (!(cnt = blockdata_walk(key_data, p, len)))
return 0;
BN_lshift(ret, ret, cnt*8);
BN_init(&temp);
BN_bin2bn(*p, cnt, &temp);
BN_add(ret, ret, &temp);
len -= cnt;
*p += cnt;
}
return 1;
}
static int rsasha1_parse_key(BIGNUM *exp, BIGNUM *mod, struct blockdata *key_data, unsigned key_len)
{
unsigned char *p = key_data->key;
size_t exp_len, mod_len;
CHECKED_GETCHAR(exp_len, p, key_len);
if (exp_len == 0)
CHECKED_GETSHORT(exp_len, p, key_len);
if (exp_len >= key_len)
return 0;
mod_len = key_len - exp_len;
return keydata_to_bn(exp, &key_data, &p, exp_len) &&
keydata_to_bn(mod, &key_data, &p, mod_len);
}
static int dsasha1_parse_key(BIGNUM *Q, BIGNUM *P, BIGNUM *G, BIGNUM *Y, struct blockdata *key_data, unsigned key_len)
{
unsigned char *p = key_data->key;
int T;
CHECKED_GETCHAR(T, p, key_len);
return
keydata_to_bn(Q, &key_data, &p, 20) &&
keydata_to_bn(P, &key_data, &p, 64+T*8) &&
keydata_to_bn(G, &key_data, &p, 64+T*8) &&
keydata_to_bn(Y, &key_data, &p, 64+T*8);
}
static int rsa_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len, int nid, int dlen)
{
int validated = 0;
RSA *rsa = RSA_new();
rsa->e = BN_new();
rsa->n = BN_new();
if (rsasha1_parse_key(rsa->e, rsa->n, key_data, key_len)
&& RSA_verify(nid, ctx->digest, dlen, ctx->sig, ctx->siglen, rsa))
validated = 1;
RSA_free(rsa);
return validated;
}
static int rsamd5_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_md5, 16);
}
static int rsasha1_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha1, 20);
}
static int rsasha256_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha256, 32);
}
static int rsasha512_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha512, 64);
}
static int dsasha1_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
static unsigned char asn1_signature[] =
{
0x30, 0x2E, // sequence
0x02, 21, // large integer (21 bytes)
0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // R
0x02, 21, // large integer (21 bytes)
0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // S
};
int validated = 0;
/* A DSA signature is made of 2 bignums (R & S). We could parse them manually with BN_bin2bn(),
but OpenSSL does not have an API to verify a DSA signature given R and S, and insists
in having a ASN.1 BER sequence (as per RFC3279).
We prepare a hard-coded ASN.1 sequence, and just fill in the R&S numbers in it. */
memcpy(asn1_signature+5, ctx->sig+1, 20);
memcpy(asn1_signature+28, ctx->sig+21, 20);
DSA *dsa = DSA_new();
dsa->q = BN_new();
dsa->p = BN_new();
dsa->g = BN_new();
dsa->pub_key = BN_new();
if (dsasha1_parse_key(dsa->q, dsa->p, dsa->g, dsa->pub_key, key_data, key_len)
&& DSA_verify(0, ctx->digest, 20, asn1_signature, countof(asn1_signature), dsa) > 0)
validated = 1;
DSA_free(dsa);
return validated;
}
#define VALG_UNSUPPORTED() { \
0,0 \
} /**/
#define VALG_VTABLE(alg, digest) { \
digest, \
alg ## _verify \
} /**/
/* Updated registry that merges various RFCs:
https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml */
static const VerifyAlg valgs[] =
{
VALG_UNSUPPORTED(), /* 0: reserved */
VALG_VTABLE(rsamd5, DIGESTALG_MD5), /* 1: RSAMD5 */
VALG_UNSUPPORTED(), /* 2: DH */
VALG_VTABLE(dsasha1, DIGESTALG_SHA1), /* 3: DSA */
VALG_UNSUPPORTED(), /* 4: ECC */
VALG_VTABLE(rsasha1, DIGESTALG_SHA1), /* 5: RSASHA1 */
VALG_VTABLE(dsasha1, DIGESTALG_SHA1), /* 6: DSA-NSEC3-SHA1 */
VALG_VTABLE(rsasha1, DIGESTALG_SHA1), /* 7: RSASHA1-NSEC3-SHA1 */
VALG_VTABLE(rsasha256, DIGESTALG_SHA256), /* 8: RSASHA256 */
VALG_UNSUPPORTED(), /* 9: unassigned */
VALG_VTABLE(rsasha512, DIGESTALG_SHA512), /* 10: RSASHA512 */
VALG_UNSUPPORTED(), /* 11: unassigned */
VALG_UNSUPPORTED(), /* 12: ECC-GOST */
VALG_UNSUPPORTED(), /* 13: ECDSAP256SHA256 */
VALG_UNSUPPORTED(), /* 14: ECDSAP384SHA384 */
};
/* TODO: remove if we don't need this anymore
(to be rechecked if we ever remove OpenSSL) */
static const int valgctx_size[] =
{
0, /* 0: reserved */
sizeof(VerifyAlgCtx), /* 1: RSAMD5 */
0, /* 2: DH */
sizeof(VerifyAlgCtx), /* 3: DSA */
0, /* 4: ECC */
sizeof(VerifyAlgCtx), /* 5: RSASHA1 */
sizeof(VerifyAlgCtx), /* 6: DSA-NSEC3-SHA1 */
sizeof(VerifyAlgCtx), /* 7: RSASHA1-NSEC3-SHA1 */
sizeof(VerifyAlgCtx), /* 8: RSASHA256 */
0, /* 9: unassigned */
sizeof(VerifyAlgCtx), /* 10: RSASHA512 */
0, /* 11: unassigned */
0, /* 12: ECC-GOST */
0, /* 13: ECDSAP256SHA256 */
0, /* 14: ECDSAP384SHA384 */
};
int verifyalg_supported(int algo)
{
return (algo < countof(valgctx_size) && valgctx_size[algo] != 0);
}
VerifyAlgCtx* verifyalg_alloc(int algo)
{
int i;
VerifyAlgCtx *ret = 0;
if (pool_used == (1<<POOL_SIZE)-1)
ret = whine_malloc(valgctx_size[algo]);
else
for (i = 0; i < POOL_SIZE; ++i)
if (!(pool_used & (1 << i)))
{
ret = (VerifyAlgCtx*)&Pool[i];
pool_used |= 1 << i;
break;
}
if (ret)
ret->vtbl = &valgs[algo];
return ret;
}
void verifyalg_free(VerifyAlgCtx *a)
{
int pool_idx = ((char*)a - (char*)&Pool[0]) / sizeof(Pool[0]);
if (pool_idx < 0 || pool_idx >= POOL_SIZE)
{
free(a);
return;
}
pool_used &= ~(1 << pool_idx);
}
int verifyalg_algonum(VerifyAlgCtx *a)
{
int num = a->vtbl - valgs;
if (num < 0 || num >= countof(valgs))
return -1;
return num;
}
static EVP_MD_CTX digctx;
int digestalg_supported(int algo)
{
return (algo == DIGESTALG_SHA1 ||
algo == DIGESTALG_SHA256 ||
algo == DIGESTALG_MD5 ||
algo == DIGESTALG_SHA512);
}
void digestalg_begin(int algo)
{
EVP_MD_CTX_init(&digctx);
if (algo == DIGESTALG_SHA1)
EVP_DigestInit_ex(&digctx, EVP_sha1(), NULL);
else if (algo == DIGESTALG_SHA256)
EVP_DigestInit_ex(&digctx, EVP_sha256(), NULL);
else if (algo == DIGESTALG_SHA512)
EVP_DigestInit_ex(&digctx, EVP_sha512(), NULL);
else if (algo == DIGESTALG_MD5)
EVP_DigestInit_ex(&digctx, EVP_md5(), NULL);
}
int digestalg_len()
{
return EVP_MD_CTX_size(&digctx);
}
void digestalg_add_data(void *data, unsigned len)
{
EVP_DigestUpdate(&digctx, data, len);
}
void digestalg_add_keydata(struct blockdata *key, size_t len)
{
size_t cnt; unsigned char *p = NULL;
while (len)
{
cnt = blockdata_walk(&key, &p, len);
EVP_DigestUpdate(&digctx, p, cnt);
p += cnt;
len -= cnt;
}
}
unsigned char* digestalg_final(void)
{
static unsigned char digest[32];
EVP_DigestFinal(&digctx, digest, NULL);
return digest;
}
#endif /* HAVE_DNSSEC */
+742 -146
View File
File diff suppressed because it is too large. Load diff
+159 -59
View File
@@ -16,11 +16,11 @@
#include "dnsmasq.h" #include "dnsmasq.h"
static struct frec *lookup_frec(unsigned short id, unsigned int crc); static struct frec *lookup_frec(unsigned short id, void *hash);
static struct frec *lookup_frec_by_sender(unsigned short id, static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr, union mysockaddr *addr,
unsigned int crc); void *hash);
static unsigned short get_id(unsigned int crc); static unsigned short get_id(void);
static void free_frec(struct frec *f); static void free_frec(struct frec *f);
static struct randfd *allocate_rfd(int family); static struct randfd *allocate_rfd(int family);
@@ -239,10 +239,15 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
char *domain = NULL; char *domain = NULL;
int type = 0, norebind = 0; int type = 0, norebind = 0;
struct all_addr *addrp = NULL; struct all_addr *addrp = NULL;
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
unsigned int flags = 0; unsigned int flags = 0;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
struct server *start = NULL; struct server *start = NULL;
#ifdef HAVE_DNSSEC
void *hash = hash_questions(header, plen, daemon->namebuff);
#else
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
void *hash = &crc;
#endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
/* RFC 4035: sect 4.6 para 2 */ /* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD; header->hb4 &= ~HB4_AD;
@@ -250,8 +255,48 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* may be no servers available. */ /* may be no servers available. */
if (!daemon->servers) if (!daemon->servers)
forward = NULL; forward = NULL;
else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc))) else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
{ {
#ifdef HAVE_DNSSEC
/* If we've already got an answer to this query, but we're awaiting keys for vaildation,
there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query)
{
int fd;
while (forward->blocking_query)
forward = forward->blocking_query;
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
if (forward->sentto->addr.sa.sa_family)
log_query(F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
#ifdef HAVE_IPV6
else
log_query(F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
#endif
if (forward->sentto->sfd)
fd = forward->sentto->sfd->fd;
else
{
#ifdef HAVE_IPV6
if (forward->sentto->addr.sa.sa_family == AF_INET6)
fd = forward->rfd6->fd;
else
#endif
fd = forward->rfd4->fd;
}
while (sendto(fd, (char *)header, plen, 0,
&forward->sentto->addr.sa,
sa_len(&forward->sentto->addr)) == -1 && retry_send());
return 1;
}
#endif
/* retry on existing query, send to all available servers */ /* retry on existing query, send to all available servers */
domain = forward->sentto->domain; domain = forward->sentto->domain;
forward->sentto->failed_queries++; forward->sentto->failed_queries++;
@@ -280,9 +325,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->dest = *dst_addr; forward->dest = *dst_addr;
forward->iface = dst_iface; forward->iface = dst_iface;
forward->orig_id = ntohs(header->id); forward->orig_id = ntohs(header->id);
forward->new_id = get_id(crc); forward->new_id = get_id();
forward->fd = udpfd; forward->fd = udpfd;
forward->crc = crc; memcpy(forward->hash, hash, HASH_SIZE);
forward->forwardall = 0; forward->forwardall = 0;
forward->flags = 0; forward->flags = 0;
if (norebind) if (norebind)
@@ -346,6 +391,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
{ {
plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz); plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD; header->hb4 |= HB4_CD;
} }
#endif #endif
@@ -473,7 +521,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
char *matchstart = daemon->namebuff + namelen - domainlen; char *matchstart = daemon->namebuff + namelen - domainlen;
if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) && if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
(domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) && (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
domainlen >= matchlen) { domainlen >= matchlen)
{
matchlen = domainlen; matchlen = domainlen;
sets = ipset_pos->sets; sets = ipset_pos->sets;
} }
@@ -507,14 +556,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY)) if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
header->hb4 &= ~HB4_AD; header->hb4 &= ~HB4_AD;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
header->hb4 &= ~HB4_AD;
if (cache_secure)
header->hb4 |= HB4_AD;
#endif
if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN)) if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
return n; return n;
@@ -534,9 +575,12 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
munged = 1; munged = 1;
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA; header->hb3 &= ~HB3_AA;
cache_secure = 0;
} }
else else
{ {
int doctored = 0;
if (RCODE(header) == NXDOMAIN && if (RCODE(header) == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) && extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now)) check_for_local_domain(daemon->namebuff, now))
@@ -547,15 +591,38 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
munged = 1; munged = 1;
header->hb3 |= HB3_AA; header->hb3 |= HB3_AA;
SET_RCODE(header, NOERROR); SET_RCODE(header, NOERROR);
cache_secure = 0;
} }
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure)) if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
{ {
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1; munged = 1;
cache_secure = 0;
}
if (doctored)
cache_secure = 0;
}
#ifdef HAVE_DNSSEC
if (no_cache && !(header->hb4 & HB4_CD))
{
if (!option_bool(OPT_DNSSEC_DEBUG))
{
/* Bogus reply, turn into SERVFAIL */
SET_RCODE(header, SERVFAIL);
munged = 1;
} }
} }
if (option_bool(OPT_DNSSEC_VALID))
header->hb4 &= ~HB4_AD;
if (!(header->hb4 & HB4_CD) && cache_secure)
header->hb4 |= HB4_AD;
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove /* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */ nameserver info. */
@@ -584,6 +651,10 @@ void reply_query(int fd, int family, time_t now)
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen); ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
size_t nn; size_t nn;
struct server *server; struct server *server;
void *hash;
#ifndef HAVE_DNSSEC
unsigned int crc;
#endif
/* packet buffer overwritten */ /* packet buffer overwritten */
daemon->srv_save = NULL; daemon->srv_save = NULL;
@@ -603,9 +674,16 @@ void reply_query(int fd, int family, time_t now)
header = (struct dns_header *)daemon->packet; header = (struct dns_header *)daemon->packet;
#ifdef HAVE_DNSSEC
hash = hash_questions(header, n, daemon->namebuff);
#else
hash = &crc;
crc = questions_crc(header, n, daemon->namebuff);
#endif
if (!server || if (!server ||
n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) || n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff)))) !(forward = lookup_frec(ntohs(header->id), hash)))
return; return;
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) && if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
@@ -679,20 +757,17 @@ void reply_query(int fd, int family, time_t now)
int status; int status;
/* We've had a reply already, which we're validating. Ignore this duplicate */ /* We've had a reply already, which we're validating. Ignore this duplicate */
if (forward->stash) if (forward->blocking_query)
return; return;
if (header->hb3 & HB3_TC) if (header->hb3 & HB3_TC)
{ {
/* Truncated answer can't be validated. /* Truncated answer can't be validated.
The client will retry over TCP, but if this is an answer to a If this is an answer to a DNSSEC-generated query, we still
DNSSEC-generated query, we have a problem. Should really re-send need to get the client to retry over TCP, so return
over TCP. No-one with any sense will make a DNSKEY or DS RRset an answer with the TC bit set, even if the actual answer fits.
exceed 4096, so this may not be a real problem. Just log */
for now. */ status = STAT_TRUNCATED;
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
my_syslog(LOG_ERR, _("Reply to DNSSEC query truncated - validation fails."));
status = STAT_INSECURE;
} }
else if (forward->flags & FREC_DNSKEY_QUERY) else if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
@@ -712,7 +787,6 @@ void reply_query(int fd, int family, time_t now)
struct frec *next = new->next; struct frec *next = new->next;
*new = *forward; /* copy everything, then overwrite */ *new = *forward; /* copy everything, then overwrite */
new->next = next; new->next = next;
new->stash = NULL;
new->blocking_query = NULL; new->blocking_query = NULL;
new->rfd4 = NULL; new->rfd4 = NULL;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
@@ -720,7 +794,14 @@ void reply_query(int fd, int family, time_t now)
#endif #endif
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY); new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
if ((forward->stash = blockdata_alloc((char *)header, n))) /* Free any saved query */
if (forward->stash)
blockdata_free(forward->stash);
/* Now save reply pending receipt of key data */
if (!(forward->stash = blockdata_alloc((char *)header, n)))
free_frec(new); /* malloc failure, unwind */
else
{ {
int fd; int fd;
@@ -735,15 +816,19 @@ void reply_query(int fd, int family, time_t now)
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz, nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
daemon->keyname, forward->class, T_DNSKEY, &server->addr); daemon->keyname, forward->class, T_DNSKEY, &server->addr);
} }
else if (status == STAT_NEED_DS) else
{ {
new->flags |= FREC_DS_QUERY; new->flags |= FREC_DS_QUERY;
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz, nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
daemon->keyname, forward->class, T_DS, &server->addr); daemon->keyname, forward->class, T_DS, &server->addr);
} }
new->crc = questions_crc(header, nn, daemon->namebuff); if ((hash = hash_questions(header, nn, daemon->namebuff)))
new->new_id = get_id(new->crc); memcpy(new->hash, hash, HASH_SIZE);
new->new_id = get_id();
header->id = htons(new->new_id); header->id = htons(new->new_id);
/* Save query for retransmission */
new->stash = blockdata_alloc((char *)header, nn);
new->stash_len = nn;
/* Don't resend this. */ /* Don't resend this. */
daemon->srv_save = NULL; daemon->srv_save = NULL;
@@ -783,11 +868,14 @@ void reply_query(int fd, int family, time_t now)
and validate them with the new data. Failure to find needed data here is an internal error. and validate them with the new data. Failure to find needed data here is an internal error.
Once we get to the original answer (FREC_DNSSEC_QUERY not set) and it validates, Once we get to the original answer (FREC_DNSSEC_QUERY not set) and it validates,
return it to the original requestor. */ return it to the original requestor. */
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
{
while (forward->dependent) while (forward->dependent)
{ {
struct frec *prev; struct frec *prev = forward->dependent;
free_frec(forward);
forward = prev;
forward->blocking_query = NULL; /* already gone */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
n = forward->stash_len;
if (status == STAT_SECURE) if (status == STAT_SECURE)
{ {
@@ -795,19 +883,7 @@ void reply_query(int fd, int family, time_t now)
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else if (forward->flags & FREC_DS_QUERY) else if (forward->flags & FREC_DS_QUERY)
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
} else
prev = forward->dependent;
free_frec(forward);
forward = prev;
forward->blocking_query = NULL; /* already gone */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
n = forward->stash_len;
}
/* All DNSKEY and DS records done and in cache, now finally validate original
answer, provided last DNSKEY is OK. */
if (status == STAT_SECURE)
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class); status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
if (status == STAT_NEED_DS || status == STAT_NEED_KEY) if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
@@ -816,7 +892,11 @@ void reply_query(int fd, int family, time_t now)
status = STAT_INSECURE; status = STAT_INSECURE;
} }
} }
}
if (status == STAT_TRUNCATED)
header->hb3 |= HB3_TC;
else
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, log_query(F_KEYTAG | F_SECSTAT, "result", NULL,
status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS")); status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
@@ -824,7 +904,6 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_SECURE) if (status == STAT_SECURE)
cache_secure = 1; cache_secure = 1;
/* TODO return SERVFAIL here */
else if (status == STAT_BOGUS) else if (status == STAT_BOGUS)
no_cache_dnssec = 1; no_cache_dnssec = 1;
@@ -1288,8 +1367,13 @@ unsigned char *tcp_request(int confd, time_t now,
if (!flags && last_server) if (!flags && last_server)
{ {
struct server *firstsendto = NULL; struct server *firstsendto = NULL;
#ifdef HAVE_DNSSEC
unsigned char *newhash, hash[HASH_SIZE];
if ((newhash = hash_questions(header, (unsigned int)size, daemon->keyname)))
memcpy(hash, newhash, HASH_SIZE);
#else
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff); unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
#endif
/* Loop round available servers until we succeed in connecting to one. /* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */ which can go to the same server, do so. */
@@ -1412,7 +1496,21 @@ unsigned char *tcp_request(int confd, time_t now,
/* If the crc of the question section doesn't match the crc we sent, then /* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */ sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff)) #ifdef HAVE_DNSSEC
newhash = hash_questions(header, (unsigned int)m, daemon->namebuff);
if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
{
m = 0;
break;
}
#else
if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff))
{
m = 0;
break;
}
#endif
m = process_reply(header, now, last_server, (unsigned int)m, m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
cache_secure, check_subnet, &peer_addr); cache_secure, check_subnet, &peer_addr);
@@ -1451,7 +1549,9 @@ static struct frec *allocate_frec(time_t now)
f->rfd6 = NULL; f->rfd6 = NULL;
#endif #endif
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
f->dependent = NULL;
f->blocking_query = NULL; f->blocking_query = NULL;
f->stash = NULL;
#endif #endif
daemon->frec_list = f; daemon->frec_list = f;
} }
@@ -1521,7 +1621,7 @@ static void free_frec(struct frec *f)
if (f->blocking_query) if (f->blocking_query)
free_frec(f->blocking_query); free_frec(f->blocking_query);
f->blocking_query = NULL; f->blocking_query = NULL;
f->dependent = NULL;
#endif #endif
} }
@@ -1604,13 +1704,13 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
} }
/* crc is all-ones if not known. */ /* crc is all-ones if not known. */
static struct frec *lookup_frec(unsigned short id, unsigned int crc) static struct frec *lookup_frec(unsigned short id, void *hash)
{ {
struct frec *f; struct frec *f;
for(f = daemon->frec_list; f; f = f->next) for(f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->new_id == id && if (f->sentto && f->new_id == id &&
(f->crc == crc || crc == 0xffffffff)) (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
return f; return f;
return NULL; return NULL;
@@ -1618,14 +1718,14 @@ static struct frec *lookup_frec(unsigned short id, unsigned int crc)
static struct frec *lookup_frec_by_sender(unsigned short id, static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr, union mysockaddr *addr,
unsigned int crc) void *hash)
{ {
struct frec *f; struct frec *f;
for(f = daemon->frec_list; f; f = f->next) for(f = daemon->frec_list; f; f = f->next)
if (f->sentto && if (f->sentto &&
f->orig_id == id && f->orig_id == id &&
f->crc == crc && memcmp(hash, f->hash, HASH_SIZE) == 0 &&
sockaddr_isequal(&f->source, addr)) sockaddr_isequal(&f->source, addr))
return f; return f;
@@ -1649,13 +1749,13 @@ void server_gone(struct server *server)
} }
/* return unique random ids. */ /* return unique random ids. */
static unsigned short get_id(unsigned int crc) static unsigned short get_id(void)
{ {
unsigned short ret = 0; unsigned short ret = 0;
do do
ret = rand16(); ret = rand16();
while (lookup_frec(ret, crc)); while (lookup_frec(ret, NULL));
return ret; return ret;
} }
+13 -4
View File
@@ -306,7 +306,7 @@ void lease_update_file(time_t now)
file_dirty = 0; file_dirty = 0;
} }
/* Set alarm for when the first lease expires + slop. */ /* Set alarm for when the first lease expires. */
next_event = 0; next_event = 0;
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
@@ -331,8 +331,8 @@ void lease_update_file(time_t now)
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if (lease->expires != 0 && if (lease->expires != 0 &&
(next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0)) (next_event == 0 || difftime(next_event, lease->expires) > 0.0))
next_event = lease->expires + 10; next_event = lease->expires;
if (err) if (err)
{ {
@@ -745,13 +745,22 @@ struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now) void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
{ {
time_t exp = now + (time_t)len; time_t exp;
if (len == 0xffffffff) if (len == 0xffffffff)
{ {
exp = 0; exp = 0;
len = 0; len = 0;
} }
else
{
exp = now + (time_t)len;
/* Check for 2038 overflow. Make the lease
inifinite in that case, as the least disruptive
thing we can do. */
if (difftime(exp, now) <= 0.0)
exp = 0;
}
if (exp != lease->expires) if (exp != lease->expires)
{ {
+1 -1
View File
@@ -265,7 +265,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (ifa->ifa_flags & IFA_F_DEPRECATED) if (ifa->ifa_flags & IFA_F_DEPRECATED)
flags |= IFACE_DEPRECATED; flags |= IFACE_DEPRECATED;
if (ifa->ifa_flags & IFA_F_PERMANENT) if (!(ifa->ifa_flags & IFA_F_TEMPORARY))
flags |= IFACE_PERMANENT; flags |= IFACE_PERMANENT;
if (addrp && callback_ok) if (addrp && callback_ok)
+26 -7
View File
@@ -140,7 +140,7 @@ struct myoption {
#define LOPT_QUIET_RA 328 #define LOPT_QUIET_RA 328
#define LOPT_SEC_VALID 329 #define LOPT_SEC_VALID 329
#define LOPT_DNSKEY 330 #define LOPT_DNSKEY 330
#define LOPT_DNSSEC_DEBUG 331
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
@@ -278,6 +278,7 @@ static const struct myoption opts[] =
{ "synth-domain", 1, 0, LOPT_SYNTH }, { "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID }, { "dnssec", 0, 0, LOPT_SEC_VALID },
{ "dnskey", 1, 0, LOPT_DNSKEY }, { "dnskey", 1, 0, LOPT_DNSKEY },
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
#ifdef OPTION6_PREFIX_CLASS #ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS }, { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif #endif
@@ -428,10 +429,9 @@ static struct {
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL }, { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL }, { LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL }, { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
#ifdef HAVE_DNSSEC
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL }, { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
{ LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), NULL }, { LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), NULL },
#endif { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
#ifdef OPTION6_PREFIX_CLASS #ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL }, { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif #endif
@@ -3678,16 +3678,35 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_DNSKEY: case LOPT_DNSKEY:
{ {
struct dnskey *new = opt_malloc(sizeof(struct dnskey)); struct dnskey *new = opt_malloc(sizeof(struct dnskey));
char *key64, *algo; char *key64, *algo = NULL;
if (!(comma = split(arg)) || !(algo = split(comma)) || !(key64 = split(algo)) || new->class = C_IN;
if ((comma = split(arg)) && (algo = split(comma)))
{
int class = 0;
if (strcmp(comma, "IN") == 0)
class = C_IN;
else if (strcmp(comma, "CH") == 0)
class = C_CHAOS;
else if (strcmp(comma, "HS") == 0)
class = C_HESIOD;
if (class != 0)
{
new->class = class;
comma = algo;
algo = split(comma);
}
}
if (!comma || !algo || !(key64 = split(algo)) ||
!atoi_check16(comma, &new->flags) || !atoi_check16(algo, &new->algo) || !atoi_check16(comma, &new->flags) || !atoi_check16(algo, &new->algo) ||
!(new->name = canonicalise_opt(arg))) !(new->name = canonicalise_opt(arg)))
ret_err(_("bad DNSKEY")); ret_err(_("bad DNSKEY"));
/* Upper bound on length */ /* Upper bound on length */
new->key = opt_malloc((3*strlen(key64)/4)); new->key = opt_malloc((3*strlen(key64)/4)+1);
unhide_metas(key64); unhide_metas(key64);
if ((new->keylen = parse_base64(key64, new->key)) == -1) if ((new->keylen = parse_base64(key64, new->key)) == -1)
ret_err(_("bad base64 in DNSKEY")); ret_err(_("bad base64 in DNSKEY"));
+18
View File
@@ -285,9 +285,27 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
/* zero net part of address */ /* zero net part of address */
setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU)); setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU));
if ((context->flags & if ((context->flags &
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS))) (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
{
do_slaac = 1; do_slaac = 1;
if (context->flags & CONTEXT_DHCP)
{
parm.other = 1;
if (!(context->flags & CONTEXT_RA_STATELESS))
parm.managed = 1;
}
}
else
{
/* don't do RA for non-ra-only unless --enable-ra is set */
if (option_bool(OPT_RA))
{
parm.managed = 1;
parm.other = 1;
}
}
if ((opt = expand(sizeof(struct prefix_opt)))) if ((opt = expand(sizeof(struct prefix_opt))))
{ {
+231 -29
View File
@@ -360,6 +360,7 @@ static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_he
than CRC the raw bytes, since replies might be compressed differently. than CRC the raw bytes, since replies might be compressed differently.
We ignore case in the names for the same reason. Return all-ones We ignore case in the names for the same reason. Return all-ones
if there is not question section. */ if there is not question section. */
#ifndef HAVE_DNSSEC
unsigned int questions_crc(struct dns_header *header, size_t plen, char *name) unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
{ {
int q; int q;
@@ -400,7 +401,7 @@ unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
return crc; return crc;
} }
#endif
size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen) size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
{ {
@@ -511,14 +512,17 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
int optno, unsigned char *opt, size_t optlen, int set_do) int optno, unsigned char *opt, size_t optlen, int set_do)
{ {
unsigned char *lenp, *datap, *p; unsigned char *lenp, *datap, *p;
int rdlen; int rdlen, is_sign;
if (ntohs(header->arcount) == 0) if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
{ {
if (is_sign)
return plen;
/* We are adding the pseudoheader */ /* We are adding the pseudoheader */
if (!(p = skip_questions(header, plen)) || if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p, !(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount), ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen))) header, plen)))
return plen; return plen;
*p++ = 0; /* empty name */ *p++ = 0; /* empty name */
@@ -531,16 +535,16 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
rdlen = 0; rdlen = 0;
if (((ssize_t)optlen) > (limit - (p + 4))) if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */ return plen; /* Too big */
header->arcount = htons(1); header->arcount = htons(ntohs(header->arcount) + 1);
datap = p; datap = p;
} }
else else
{ {
int i, is_sign; int i;
unsigned short code, len, flags; unsigned short code, len, flags;
/* Must be at the end, if exists */
if (ntohs(header->arcount) != 1 || if (ntohs(header->arcount) != 1 ||
!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
is_sign || is_sign ||
(!(p = skip_name(p, header, plen, 10)))) (!(p = skip_name(p, header, plen, 10))))
return plen; return plen;
@@ -750,7 +754,7 @@ int private_net(struct in_addr addr, int ban_localhost)
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ; ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
} }
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name) static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
{ {
int i, qtype, qclass, rdlen; int i, qtype, qclass, rdlen;
@@ -795,6 +799,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */ /* Since we munged the data, the server it came from is no longer authoritative */
header->hb3 &= ~HB3_AA; header->hb3 &= ~HB3_AA;
*doctored = 1;
memcpy(p, &addr, INADDRSZ); memcpy(p, &addr, INADDRSZ);
break; break;
} }
@@ -833,7 +838,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
return p; return p;
} }
static int find_soa(struct dns_header *header, size_t qlen, char *name) static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored)
{ {
unsigned char *p; unsigned char *p;
int qtype, qclass, rdlen; int qtype, qclass, rdlen;
@@ -842,7 +847,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
/* first move to NS section and find TTL from any SOA section */ /* first move to NS section and find TTL from any SOA section */
if (!(p = skip_questions(header, qlen)) || if (!(p = skip_questions(header, qlen)) ||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name))) !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored)))
return 0; /* bad packet */ return 0; /* bad packet */
for (i = ntohs(header->nscount); i != 0; i--) for (i = ntohs(header->nscount); i != 0; i--)
@@ -877,8 +882,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
return 0; /* bad packet */ return 0; /* bad packet */
} }
/* rewrite addresses in additioal section too */ /* rewrite addresses in additional section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL)) if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored))
return 0; return 0;
if (!found_soa) if (!found_soa)
@@ -892,7 +897,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
expired and cleaned out that way. expired and cleaned out that way.
Return 1 if we reject an address because it look like part of dns-rebinding attack. */ Return 1 if we reject an address because it look like part of dns-rebinding attack. */
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure) char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure, int *doctored)
{ {
unsigned char *p, *p1, *endrr, *namep; unsigned char *p, *p1, *endrr, *namep;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
@@ -907,10 +912,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
cache_start_insert(); cache_start_insert();
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */ /* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
if (daemon->doctors || option_bool(OPT_LOG)) if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID))
{ {
searched_soa = 1; searched_soa = 1;
ttl = find_soa(header, qlen, name); ttl = find_soa(header, qlen, name, doctored);
#ifdef HAVE_DNSSEC
if (*doctored)
secure = 0;
#endif
} }
/* go through the questions. */ /* go through the questions. */
@@ -981,6 +990,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{ {
if (!cname_count--) if (!cname_count--)
return 0; /* looped CNAMES */ return 0; /* looped CNAMES */
secflag = 0; /* no longer DNSSEC */
goto cname_loop; goto cname_loop;
} }
@@ -999,7 +1009,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!searched_soa) if (!searched_soa)
{ {
searched_soa = 1; searched_soa = 1;
ttl = find_soa(header, qlen, NULL); ttl = find_soa(header, qlen, NULL, doctored);
} }
if (ttl) if (ttl)
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag); cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);
@@ -1115,7 +1125,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (!searched_soa) if (!searched_soa)
{ {
searched_soa = 1; searched_soa = 1;
ttl = find_soa(header, qlen, NULL); ttl = find_soa(header, qlen, NULL, doctored);
} }
/* If there's no SOA to get the TTL from, but there is a CNAME /* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */ pointing at this, inherit its TTL */
@@ -1147,7 +1157,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
/* If the packet holds exactly one query /* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name */ return F_IPV4 or F_IPV6 and leave the name from the query in name */
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep) unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
{ {
unsigned char *p = (unsigned char *)(header+1); unsigned char *p = (unsigned char *)(header+1);
@@ -1366,6 +1375,11 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
p += INADDRSZ; p += INADDRSZ;
break; break;
case 'b':
usval = va_arg(ap, int);
*p++ = usval;
break;
case 's': case 's':
usval = va_arg(ap, int); usval = va_arg(ap, int);
PUTSHORT(usval, p); PUTSHORT(usval, p);
@@ -1447,23 +1461,30 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
int nameoffset; int nameoffset;
unsigned short flag; unsigned short flag;
int q, ans, anscount = 0, addncount = 0; int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0; int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
int is_sign; int is_sign;
struct crec *crecp; struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec; struct mx_srv_record *rec;
size_t len;
/* Don't return AD set even for local data if checking disabled. */
if (header->hb4 & HB4_CD)
sec_data = 0;
/* If there is an RFC2671 pseudoheader then it will be overwritten by /* If there is an RFC2671 pseudoheader then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer partial replies, so we have to do a dry run to see if we can answer
the query. We check to see if the do bit is set, if so we always the query. We check to see if the do bit is set, if so we always
forward rather than answering from the cache, which doesn't include forward rather than answering from the cache, which doesn't include
security information. */ security information, unless we're in DNSSEC validation mode. */
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign)) if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{ {
unsigned short udpsz, flags; unsigned short udpsz, flags;
unsigned char *psave = pheader; unsigned char *psave = pheader;
have_pseudoheader = 1;
GETSHORT(udpsz, pheader); GETSHORT(udpsz, pheader);
pheader += 2; /* ext_rcode */ pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader); GETSHORT(flags, pheader);
@@ -1529,6 +1550,99 @@ 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 || qtype == T_RRSIG))
{
int gotone = 0, have_rrsig = 0;
struct blockdata *keydata;
/* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && (qtype == T_RRSIG || crecp->addr.sig.type_covered == qtype))
{
have_rrsig = 1;
break;
}
if (qtype == T_RRSIG && have_rrsig)
{
ans = gotone = 1;
auth = 0;
}
else if (qtype == T_DS && have_rrsig)
{
auth = 0;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
if (crecp->uid == qclass)
{
ans = gotone = 1;
if (!dryrun && (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 if (qtype == T_DNSKEY)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
if (crecp->uid == qclass)
{
if ((crecp->flags & F_CONFIG) || have_rrsig) /* Return configured keys without an RRISG */
{
if (!(crecp->flags & F_CONFIG))
auth = 0, gotone = 1;
ans = 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)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && (qtype == T_RRSIG || (sec_reqd && crecp->addr.sig.type_covered == qtype)) &&
!dryrun &&
(keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
{
if (qtype == T_RRSIG)
{
char types[20];
querystr("rrsig", types, crecp->addr.sig.type_covered);
log_query(F_RRNAME, name, NULL, types);
}
if ((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) if (qclass == C_IN)
{ {
struct txt_record *t; struct txt_record *t;
@@ -1619,6 +1733,39 @@ 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))) 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;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS | F_DNSKEY)))
{
if (crecp->addr.sig.type_covered == T_PTR && crecp->uid == C_IN)
{
char *sigdata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL);
gotsig = 1;
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crecp->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
anscount++;
}
}
/* Need to re-run original cache search */
crecp = gotsig ? cache_find_by_addr(NULL, &addr, now, is_arpa) : NULL;
}
#endif
}
if (crecp)
{
do do
{ {
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
@@ -1637,7 +1784,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun) if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL); log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
} }
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
{ {
ans = 1; ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) if (!(crecp->flags & (F_HOSTS | F_DHCP)))
@@ -1654,6 +1801,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
} }
} }
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa))); } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
}
}
else if (is_rev_synth(is_arpa, &addr, name)) else if (is_rev_synth(is_arpa, &addr, name))
{ {
ans = 1; ans = 1;
@@ -1794,6 +1943,48 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
crecp = save; 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. */
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;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS | F_DNSKEY)))
{
if (crecp->addr.sig.type_covered == sigtype && crecp->uid == C_IN)
{
char *sigdata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL);
gotsig = 1;
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crecp->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
anscount++;
}
}
/* Need to re-run original cache search */
crecp = gotsig ? cache_find_by_name(NULL, name, now, flag | F_CNAME) : NULL;
}
#endif
}
if (crecp)
do do
{ {
/* don't answer wildcard queries with data not from /etc/hosts /* don't answer wildcard queries with data not from /etc/hosts
@@ -1826,6 +2017,10 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
} }
if (crecp->flags & F_NEG) if (crecp->flags & F_NEG)
{
/* We don't cache NSEC records, so if a DNSSEC-validated negative answer
is cached and the client wants DNSSEC, forward rather than answering from the cache */
if (!sec_reqd || !(crecp->flags & F_DNSSECOK))
{ {
ans = 1; ans = 1;
auth = 0; auth = 0;
@@ -1834,7 +2029,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun) if (!dryrun)
log_query(crecp->flags, name, NULL, NULL); log_query(crecp->flags, name, NULL, NULL);
} }
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) }
else
{ {
/* If we are returning local answers depending on network, /* If we are returning local answers depending on network,
filter here. */ filter here. */
@@ -2060,12 +2256,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (trunc) if (trunc)
header->hb3 |= HB3_TC; header->hb3 |= HB3_TC;
header->hb4 &= ~HB4_AD;
if (option_bool(OPT_DNSSEC_VALID) || option_bool(OPT_DNSSEC_PROXY))
if (sec_data)
header->hb4 |= HB4_AD;
if (nxdomain) if (nxdomain)
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);
else else
@@ -2073,6 +2263,18 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
header->ancount = htons(anscount); header->ancount = htons(anscount);
header->nscount = htons(0); header->nscount = htons(0);
header->arcount = htons(addncount); header->arcount = htons(addncount);
return ansp - (unsigned char *)header;
header->hb4 &= ~HB4_AD;
len = ansp - (unsigned char *)header;
if (have_pseudoheader)
{
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
if (sec_reqd && sec_data)
header->hb4 |= HB4_AD;
}
return len;
} }
+8
View File
@@ -0,0 +1,8 @@
# The root DNSSEC trust anchors, valid as at 30/01/2014
dnskey=.,257,8,AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=
dnskey=.,256,8,AwEAAb8sU6pbYMWRbkRnEuEZw9NSir707TkOcF+UL1XiK4NDJOvXRyX1 95Am5dQ7bRnnuySZ3daf37vvjUUhuIWUAQ4stht8nJfYxVQXDYjSpGH5 I6Hf/0CZEoNP6cNvrQ7AFmKkmv00xWExKQjbvnRPI4bqpMwtHVzn6Wyb BZ6kuqED
dnskey=.,256,8,AwEAAYRU41/8smgAvuSojEP4jaj5Yll7WPaUKpYvnz2pnX2VIvRn4jsy Jns80bloenG6X9ebJVy2CFtZQLKHP8DcKmIFotdgs2HolyocY1am/+33 4RtzusM2ojkhjn1FRGtuSE9s2TSz1ISv0yVnFyu+EP/ZkiWnDfWeVrJI SEWBEr4V