Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:42.3:Staging:A
bind
bind-CVE-2016-6170.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File bind-CVE-2016-6170.patch of Package bind
From 926433f157d9aee06e343515c1cedaa7758b0920 Mon Sep 17 00:00:00 2001 Message-Id: <926433f157d9aee06e343515c1cedaa7758b0920.1489674685.git.npajkovsky@suse.cz> From: Nikola Pajkovsky <npajkovsky@suse.cz> Date: Tue, 14 Mar 2017 13:26:57 +0100 Subject: [PATCH] 4504. [security] Allow the maximum number of records in a zone to be specified. This provides a control for issues raised in CVE-2016-6170. [RT #42143] Signed-off-by: Nikola Pajkovsky <npajkovsky@suse.cz> --- bin/named/config.c | 1 + bin/named/named.conf.docbook | 3 + bin/named/update.c | 16 +++ bin/named/zoneconf.c | 7 ++ bin/tests/system/nsupdate/clean.sh | 1 + bin/tests/system/nsupdate/ns3/named.conf | 7 ++ bin/tests/system/nsupdate/ns3/too-big.test.db.in | 10 ++ bin/tests/system/nsupdate/setup.sh | 2 + bin/tests/system/nsupdate/tests.sh | 15 +++ bin/tests/system/xfer/clean.sh | 1 + bin/tests/system/xfer/ns1/axfr-too-big.db | 10 ++ bin/tests/system/xfer/ns1/ixfr-too-big.db.in | 13 +++ bin/tests/system/xfer/ns1/named.conf | 11 +++ bin/tests/system/xfer/ns6/named.conf | 14 +++ bin/tests/system/xfer/setup.sh | 2 + bin/tests/system/xfer/tests.sh | 26 +++++ doc/arm/Bv9ARM-book.xml | 21 ++++ doc/arm/notes.xml | 6 +- lib/bind9/check.c | 2 + lib/dns/db.c | 13 +++ lib/dns/ecdb.c | 3 +- lib/dns/include/dns/db.h | 20 ++++ lib/dns/include/dns/rdataslab.h | 13 +++ lib/dns/include/dns/result.h | 3 +- lib/dns/include/dns/zone.h | 25 +++++ lib/dns/rbtdb.c | 121 ++++++++++++++++++++++- lib/dns/rdataslab.c | 13 +++ lib/dns/result.c | 1 + lib/dns/sdb.c | 3 +- lib/dns/sdlz.c | 3 +- lib/dns/xfrin.c | 22 ++++- lib/dns/zone.c | 23 ++++- lib/isccfg/namedconf.c | 1 + 33 files changed, 420 insertions(+), 12 deletions(-) create mode 100644 bin/tests/system/nsupdate/ns3/too-big.test.db.in create mode 100644 bin/tests/system/xfer/ns1/axfr-too-big.db create mode 100644 bin/tests/system/xfer/ns1/ixfr-too-big.db.in diff --git a/bin/named/config.c b/bin/named/config.c index 4798272ac353..c5ee16169a29 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -198,6 +198,7 @@ options {\n\ max-transfer-time-out 120;\n\ max-transfer-idle-in 60;\n\ max-transfer-idle-out 60;\n\ + max-records 0;\n\ max-retry-time 1209600; /* 2 weeks */\n\ min-retry-time 500;\n\ max-refresh-time 2419200; /* 4 weeks */\n\ diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook index 01cb62aaa009..2df51ded16f8 100644 --- a/bin/named/named.conf.docbook +++ b/bin/named/named.conf.docbook @@ -338,6 +338,7 @@ options { }; max-journal-size <replaceable>size_no_default</replaceable>; + max-records <replaceable>integer</replaceable>; max-transfer-time-in <replaceable>integer</replaceable>; max-transfer-time-out <replaceable>integer</replaceable>; max-transfer-idle-in <replaceable>integer</replaceable>; @@ -527,6 +528,7 @@ view <replaceable>string</replaceable> <replaceable>optional_class</replaceable> }; max-journal-size <replaceable>size_no_default</replaceable>; + max-records <replaceable>integer</replaceable>; max-transfer-time-in <replaceable>integer</replaceable>; max-transfer-time-out <replaceable>integer</replaceable>; max-transfer-idle-in <replaceable>integer</replaceable>; @@ -624,6 +626,7 @@ zone <replaceable>string</replaceable> <replaceable>optional_class</replaceable> }; max-journal-size <replaceable>size_no_default</replaceable>; + max-records <replaceable>integer</replaceable>; max-transfer-time-in <replaceable>integer</replaceable>; max-transfer-time-out <replaceable>integer</replaceable>; max-transfer-idle-in <replaceable>integer</replaceable>; diff --git a/bin/named/update.c b/bin/named/update.c index badf8fe1081f..ba4c37021cc0 100644 --- a/bin/named/update.c +++ b/bin/named/update.c @@ -2455,6 +2455,8 @@ update_action(isc_task_t *task, isc_event_t *event) { dns_rdata_dnskey_t dnskey; isc_boolean_t had_dnskey; dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); + isc_uint32_t maxrecords; + isc_uint64_t records; INSIST(event->ev_type == DNS_EVENT_UPDATE); @@ -3108,6 +3110,20 @@ update_action(isc_task_t *task, isc_event_t *event) { } } + maxrecords = dns_zone_getmaxrecords(zone); + if (maxrecords != 0U) { + result = dns_db_getsize(db, ver, &records, NULL); + if (result == ISC_R_SUCCESS && records > maxrecords) { + update_log(client, zone, ISC_LOG_ERROR, + "records in zone (%" + ISC_PRINT_QUADFORMAT + "u) exceeds max-records (%u)", + records, maxrecords); + result = DNS_R_TOOMANYRECORDS; + goto failure; + } + } + journalfile = dns_zone_getjournal(zone); if (journalfile != NULL) { update_log(client, zone, LOGLEVEL_DEBUG, diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 5b473d1b2951..26fe0b7e66a4 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -935,6 +935,13 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, INSIST(0); } + obj = NULL; + result = ns_config_get(maps, "max-records", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setmaxrecords(mayberaw, cfg_obj_asuint32(obj)); + if (zone != mayberaw) + dns_zone_setmaxrecords(zone, 0); + if (raw != NULL && filename != NULL) { #define SIGNED ".signed" size_t signedlen = strlen(filename) + sizeof(SIGNED); diff --git a/bin/tests/system/nsupdate/clean.sh b/bin/tests/system/nsupdate/clean.sh index dcb80d36eca6..57120a6f35ff 100644 --- a/bin/tests/system/nsupdate/clean.sh +++ b/bin/tests/system/nsupdate/clean.sh @@ -36,3 +36,4 @@ rm -f ns3/K* rm -f dig.out.* rm -f jp.out.ns3.* rm -f Kxxx.* +rm -f ns3/too-big.test.db diff --git a/bin/tests/system/nsupdate/ns3/named.conf b/bin/tests/system/nsupdate/ns3/named.conf index 4b43efe4f22d..f38a7daf564b 100644 --- a/bin/tests/system/nsupdate/ns3/named.conf +++ b/bin/tests/system/nsupdate/ns3/named.conf @@ -60,3 +60,10 @@ zone "dnskey.test" { allow-update { any; }; file "dnskey.test.db.signed"; }; + +zone "too-big.test" { + type master; + allow-update { any; }; + max-records 3; + file "too-big.test.db"; +}; diff --git a/bin/tests/system/nsupdate/ns3/too-big.test.db.in b/bin/tests/system/nsupdate/ns3/too-big.test.db.in new file mode 100644 index 000000000000..7ff1e4a514a4 --- /dev/null +++ b/bin/tests/system/nsupdate/ns3/too-big.test.db.in @@ -0,0 +1,10 @@ +; Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$TTL 10 +too-big.test. IN SOA too-big.test. hostmaster.too-big.test. 1 3600 900 2419200 3600 +too-big.test. IN NS too-big.test. +too-big.test. IN A 10.53.0.3 diff --git a/bin/tests/system/nsupdate/setup.sh b/bin/tests/system/nsupdate/setup.sh index 828255ee530b..43c40947de1b 100644 --- a/bin/tests/system/nsupdate/setup.sh +++ b/bin/tests/system/nsupdate/setup.sh @@ -27,12 +27,14 @@ test -r $RANDFILE || $GENRANDOM 400 $RANDFILE rm -f ns1/*.jnl ns1/example.db ns2/*.jnl ns2/example.bk rm -f ns2/update.bk ns2/update.alt.bk rm -f ns3/example.db.jnl +rm -f ns3/too-big.test.db.jnl cp -f ns1/example1.db ns1/example.db sed 's/example.nil/other.nil/g' ns1/example1.db > ns1/other.db sed 's/example.nil/unixtime.nil/g' ns1/example1.db > ns1/unixtime.db sed 's/example.nil/keytests.nil/g' ns1/example1.db > ns1/keytests.db cp -f ns3/example.db.in ns3/example.db +cp -f ns3/too-big.test.db.in ns3/too-big.test.db # update_test.pl has its own zone file because it # requires a specific NS record set. diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh index 799220d9c374..32840753db03 100644 --- a/bin/tests/system/nsupdate/tests.sh +++ b/bin/tests/system/nsupdate/tests.sh @@ -543,5 +543,20 @@ if [ $ret -ne 0 ]; then status=1 fi +n=`expr $n + 1` +echo "I:check that adding too many records is blocked ($n)" +ret=0 +$NSUPDATE -v << EOF > nsupdate.out-$n 2>&1 && ret=1 +server 10.53.0.3 5300 +zone too-big.test. +update add r1.too-big.test 3600 IN TXT r1.too-big.test +send +EOF +grep "update failed: SERVFAIL" nsupdate.out-$n > /dev/null || ret=1 +DIG +tcp @10.53.0.3 -p 5300 r1.too-big.test TXT > dig.out.ns3.test$n +grep "status: NXDOMAIN" dig.out.ns3.test$n > /dev/null || ret=1 +grep "records in zone (4) exceeds max-records (3)" ns3/named.run > /dev/null || ret=1 +[ $ret = 0 ] || { echo I:failed; status=1; } + echo "I:exit status: $status" exit $status diff --git a/bin/tests/system/xfer/clean.sh b/bin/tests/system/xfer/clean.sh index 58743ea9065e..4ee92d30bc5f 100644 --- a/bin/tests/system/xfer/clean.sh +++ b/bin/tests/system/xfer/clean.sh @@ -32,6 +32,7 @@ rm -f ns3/master.bk ns3/master.bk.jnl rm -f ns4/named.conf ns4/nil.db ns4/root.db rm -f ns6/*.db ns6/*.bk ns6/*.jnl rm -f ns7/*.db ns7/*.bk ns7/*.jnl +rm -f ns1/ixfr-too-big.db ns1/ixfr-too-big.db.jnl rm -f */named.memstats rm -f */named.run diff --git a/bin/tests/system/xfer/ns1/axfr-too-big.db b/bin/tests/system/xfer/ns1/axfr-too-big.db new file mode 100644 index 000000000000..d43760d9a8d9 --- /dev/null +++ b/bin/tests/system/xfer/ns1/axfr-too-big.db @@ -0,0 +1,10 @@ +; Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$TTL 3600 +@ IN SOA . . 0 0 0 0 0 +@ IN NS . +$GENERATE 1-29 host$ A 1.2.3.$ diff --git a/bin/tests/system/xfer/ns1/ixfr-too-big.db.in b/bin/tests/system/xfer/ns1/ixfr-too-big.db.in new file mode 100644 index 000000000000..318bb772af30 --- /dev/null +++ b/bin/tests/system/xfer/ns1/ixfr-too-big.db.in @@ -0,0 +1,13 @@ +; Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$TTL 3600 +@ IN SOA . . 0 0 0 0 0 +@ IN NS ns1 +@ IN NS ns6 +ns1 IN A 10.53.0.1 +ns6 IN A 10.53.0.6 +$GENERATE 1-25 host$ A 1.2.3.$ diff --git a/bin/tests/system/xfer/ns1/named.conf b/bin/tests/system/xfer/ns1/named.conf index 07dad85d9baa..1d292924c017 100644 --- a/bin/tests/system/xfer/ns1/named.conf +++ b/bin/tests/system/xfer/ns1/named.conf @@ -44,3 +44,14 @@ zone "slave" { type master; file "slave.db"; }; + +zone "axfr-too-big" { + type master; + file "axfr-too-big.db"; +}; + +zone "ixfr-too-big" { + type master; + allow-update { any; }; + file "ixfr-too-big.db"; +}; diff --git a/bin/tests/system/xfer/ns6/named.conf b/bin/tests/system/xfer/ns6/named.conf index c9421b1f6558..a12a92c2f6d5 100644 --- a/bin/tests/system/xfer/ns6/named.conf +++ b/bin/tests/system/xfer/ns6/named.conf @@ -52,3 +52,17 @@ zone "slave" { masters { 10.53.0.1; }; file "slave.bk"; }; + +zone "axfr-too-big" { + type slave; + max-records 30; + masters { 10.53.0.1; }; + file "axfr-too-big.bk"; +}; + +zone "ixfr-too-big" { + type slave; + max-records 30; + masters { 10.53.0.1; }; + file "ixfr-too-big.bk"; +}; diff --git a/bin/tests/system/xfer/setup.sh b/bin/tests/system/xfer/setup.sh index 56ca9018ec28..8f96b4e975e8 100644 --- a/bin/tests/system/xfer/setup.sh +++ b/bin/tests/system/xfer/setup.sh @@ -31,5 +31,7 @@ cp -f ns4/root.db.in ns4/root.db $PERL -e 'for ($i=0;$i<10000;$i++){ printf("x%u 0 in a 10.53.0.1\n", $i);}' >> ns4/root.db cp -f ns4/named.conf.base ns4/named.conf +cp -f ns1/ixfr-too-big.db.in ns1/ixfr-too-big.db + cp ns2/slave.db.in ns2/slave.db touch -t 200101010000 ns2/slave.db diff --git a/bin/tests/system/xfer/tests.sh b/bin/tests/system/xfer/tests.sh index 089b1c716fe9..89823c63e918 100644 --- a/bin/tests/system/xfer/tests.sh +++ b/bin/tests/system/xfer/tests.sh @@ -368,5 +368,31 @@ $DIGCMD nil. TXT | grep 'incorrect key AXFR' >/dev/null && { status=1 } +n=`expr $n + 1` +echo "I:test that a zone with too many records is rejected (AXFR) ($n)" +tmp=0 +grep "'axfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null || tmp=1 +if test $tmp != 0 ; then echo "I:failed"; fi +status=`expr $status + $tmp` + +n=`expr $n + 1` +echo "I:test that a zone with too many records is rejected (IXFR) ($n)" +tmp=0 +grep "'ixfr-too-big./IN.*: too many records" ns6/named.run >/dev/null && tmp=1 +$NSUPDATE << EOF +zone ixfr-too-big +server 10.53.0.1 5300 +update add the-31st-record.ixfr-too-big 0 TXT this is it +send +EOF +for i in 1 2 3 4 5 6 7 8 +do + grep "'ixfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null && break + sleep 1 +done +grep "'ixfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null || tmp=1 +if test $tmp != 0 ; then echo "I:failed"; fi +status=`expr $status + $tmp` + echo "I:exit status: $status" exit $status diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index fe479b029fa3..5a45766c52fd 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -4374,6 +4374,7 @@ badresp:1,adberr:0,findfail:0,valfail:0] <optional> use-queryport-pool <replaceable>yes_or_no</replaceable>; </optional> <optional> queryport-pool-ports <replaceable>number</replaceable>; </optional> <optional> queryport-pool-updateinterval <replaceable>number</replaceable>; </optional> + <optional> max-records <replaceable>number</replaceable>; </optional> <optional> max-transfer-time-in <replaceable>number</replaceable>; </optional> <optional> max-transfer-time-out <replaceable>number</replaceable>; </optional> <optional> max-transfer-idle-in <replaceable>number</replaceable>; </optional> @@ -7592,6 +7593,16 @@ avoid-v6-udp-ports { 40000; range 50000 60000; }; </varlistentry> <varlistentry> + <term><command>max-records</command></term> + <listitem> + <para> + The maximum number of records permitted in a zone. + The default is zero which means unlimited. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><command>host-statistics-max</command></term> <listitem> <para> @@ -11140,6 +11151,16 @@ zone <replaceable>zone_name</replaceable> <optional><replaceable>class</replacea </varlistentry> <varlistentry> + <term><command>max-records</command></term> + <listitem> + <para> + See the description of + <command>max-records</command> in <xref linkend="server_resource_limits"/>. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><command>max-transfer-time-in</command></term> <listitem> <para> diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 66ebc8b99a2d..106c7bf1966b 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -45,7 +45,11 @@ <itemizedlist> <listitem> <para> - None. + Added the ability to specify the maximum number of records + permitted in a zone (max-records #;). This provides a mechanism + to block overly large zone transfers, which is a potential risk + with slave zones from other parties, as described in CVE-2016-6170. + [RT #42143] </para> </listitem> </itemizedlist> diff --git a/lib/bind9/check.c b/lib/bind9/check.c index cbfa8301e3f4..5d541dfb3621 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1333,6 +1333,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, REDIRECTZONE }, { "masters", SLAVEZONE | STUBZONE | REDIRECTZONE }, { "max-ixfr-log-size", MASTERZONE | SLAVEZONE | STREDIRECTZONE }, + { "max-records", MASTERZONE | SLAVEZONE | STUBZONE | STREDIRECTZONE | + STATICSTUBZONE | REDIRECTZONE }, { "max-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "max-retry-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "max-transfer-idle-in", SLAVEZONE | STUBZONE | STREDIRECTZONE }, diff --git a/lib/dns/db.c b/lib/dns/db.c index bf4a5b37540c..55deed7975b1 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -983,6 +983,19 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, } isc_result_t +dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records, + isc_uint64_t *bytes) +{ + REQUIRE(DNS_DB_VALID(db)); + REQUIRE(dns_db_iszone(db) == ISC_TRUE); + + if (db->methods->getsize != NULL) + return ((db->methods->getsize)(db, version, records, bytes)); + + return (ISC_R_NOTFOUND); +} + +isc_result_t dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) { diff --git a/lib/dns/ecdb.c b/lib/dns/ecdb.c index 22cd810a773c..816489360fd3 100644 --- a/lib/dns/ecdb.c +++ b/lib/dns/ecdb.c @@ -583,7 +583,8 @@ static dns_dbmethods_t ecdb_methods = { NULL, /* rpz_enabled */ NULL, /* rpz_findips */ NULL, /* findnodeext */ - NULL /* findext */ + NULL, /* findext */ + NULL /* getsize */ }; static isc_result_t diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 66bc3e3481e1..dce6d1327581 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -194,6 +194,8 @@ typedef struct dns_dbmethods { dns_clientinfo_t *clientinfo, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset); + isc_result_t (*getsize)(dns_db_t *db, dns_dbversion_t *version, + isc_uint64_t *records, isc_uint64_t *bytes); } dns_dbmethods_t; typedef isc_result_t @@ -1445,6 +1447,24 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, */ isc_result_t +dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records, + isc_uint64_t *bytes); +/*%< + * Get the number of records in the given version of the database as well + * as the number bytes used to store those records. + * + * Requires: + * \li 'db' is a valid zone database. + * \li 'version' is NULL or a valid version. + * \li 'records' is NULL or a pointer to return the record count in. + * \li 'bytes' is NULL or a pointer to return the byte count in. + * + * Returns: + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOTIMPLEMENTED + */ + +isc_result_t dns_db_findnsec3node(dns_db_t *db, dns_name_t *name, isc_boolean_t create, dns_dbnode_t **nodep); /*%< diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h index 3ac44b879e03..2e1e7592a28d 100644 --- a/lib/dns/include/dns/rdataslab.h +++ b/lib/dns/include/dns/rdataslab.h @@ -104,6 +104,7 @@ dns_rdataslab_tordataset(unsigned char *slab, unsigned int reservelen, * Ensures: *\li 'rdataset' is associated and points to a valid rdataest. */ + unsigned int dns_rdataslab_size(unsigned char *slab, unsigned int reservelen); /*%< @@ -116,6 +117,18 @@ dns_rdataslab_size(unsigned char *slab, unsigned int reservelen); *\li The number of bytes in the slab, including the reservelen. */ +unsigned int +dns_rdataslab_count(unsigned char *slab, unsigned int reservelen); +/*%< + * Return the number of records in the rdataslab + * + * Requires: + *\li 'slab' points to a slab. + * + * Returns: + *\li The number of records in the slab. + */ + isc_result_t dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, unsigned int reservelen, isc_mem_t *mctx, diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index 7d11c2beb01e..de38dba7b4b4 100644 --- a/lib/dns/include/dns/result.h +++ b/lib/dns/include/dns/result.h @@ -157,8 +157,9 @@ #define DNS_R_BADCDS (ISC_RESULTCLASS_DNS + 111) #define DNS_R_BADCDNSKEY (ISC_RESULTCLASS_DNS + 112) #define DNS_R_OPTERR (ISC_RESULTCLASS_DNS + 113) +#define DNS_R_TOOMANYRECORDS (ISC_RESULTCLASS_DNS + 114) -#define DNS_R_NRESULTS 114 /*%< Number of results */ +#define DNS_R_NRESULTS 115 /*%< Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 987d06f70cec..ec9434bc11b9 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -288,6 +288,31 @@ dns_zone_getfile(dns_zone_t *zone); * Returns: *\li Pointer to null-terminated file name, or NULL. */ +void +dns_zone_setmaxrecords(dns_zone_t *zone, isc_uint32_t records); +/*%< + * Sets the maximim number of records permitted in a zone. + * 0 implies unlimited. + * + * Requires: + *\li 'zone' to be valid initialised zone. + * + * Returns: + *\li void + */ + +isc_uint32_t +dns_zone_getmaxrecords(dns_zone_t *zone); +/*%< + * Gets the maximim number of records permitted in a zone. + * 0 implies unlimited. + * + * Requires: + *\li 'zone' to be valid initialised zone. + * + * Returns: + *\li isc_uint32_t maxrecords. + */ isc_result_t dns_zone_load(dns_zone_t *zone); diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 80713da20dd9..45c635fe3efd 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -156,6 +156,7 @@ typedef isc_uint64_t rbtdb_serial_t; #define free_rbtdb_callback free_rbtdb_callback64 #define free_rdataset free_rdataset64 #define getnsec3parameters getnsec3parameters64 +#define getsize getsize64 #define getoriginnode getoriginnode64 #define getrrsetstats getrrsetstats64 #define getsigningtime getsigningtime64 @@ -521,6 +522,13 @@ typedef struct rbtdb_version { isc_uint16_t iterations; isc_uint8_t salt_length; unsigned char salt[DNS_NSEC3_SALTSIZE]; + + /* + * records and bytes are covered by rwlock. + */ + isc_rwlock_t rwlock; + isc_uint64_t records; + isc_uint64_t bytes; } rbtdb_version_t; typedef ISC_LIST(rbtdb_version_t) rbtdb_versionlist_t; @@ -993,6 +1001,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { INSIST(refs == 0); UNLINK(rbtdb->open_versions, rbtdb->current_version, link); isc_refcount_destroy(&rbtdb->current_version->references); + isc_rwlock_destroy(&rbtdb->current_version->rwlock); isc_mem_put(rbtdb->common.mctx, rbtdb->current_version, sizeof(rbtdb_version_t)); } @@ -1231,6 +1240,7 @@ allocate_version(isc_mem_t *mctx, rbtdb_serial_t serial, static isc_result_t newversion(dns_db_t *db, dns_dbversion_t **versionp) { + isc_result_t result; dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; rbtdb_version_t *version; @@ -1263,13 +1273,28 @@ newversion(dns_db_t *db, dns_dbversion_t **versionp) { version->salt_length = 0; memset(version->salt, 0, sizeof(version->salt)); } - rbtdb->next_serial++; - rbtdb->future_version = version; - } + result = isc_rwlock_init(&version->rwlock, 0, 0); + if (result != ISC_R_SUCCESS) { + isc_refcount_destroy(&version->references); + isc_mem_put(rbtdb->common.mctx, version, + sizeof(*version)); + version = NULL; + } else { + RWLOCK(&rbtdb->current_version->rwlock, + isc_rwlocktype_read); + version->records = rbtdb->current_version->records; + version->bytes = rbtdb->current_version->bytes; + RWUNLOCK(&rbtdb->current_version->rwlock, + isc_rwlocktype_read); + rbtdb->next_serial++; + rbtdb->future_version = version; + } + } else + result = ISC_R_NOMEMORY; RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); if (version == NULL) - return (ISC_R_NOMEMORY); + return (result); *versionp = version; @@ -2509,6 +2534,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { if (cleanup_version != NULL) { INSIST(EMPTY(cleanup_version->changed_list)); + isc_rwlock_destroy(&cleanup_version->rwlock); isc_mem_put(rbtdb->common.mctx, cleanup_version, sizeof(*cleanup_version)); } @@ -6388,6 +6414,26 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, else rbtnode->data = newheader; newheader->next = topheader->next; + if (rbtversion != NULL) + RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write); + if (rbtversion != NULL && !header_nx) { + rbtversion->records -= + dns_rdataslab_count((unsigned char *)header, + sizeof(*header)); + rbtversion->bytes -= + dns_rdataslab_size((unsigned char *)header, + sizeof(*header)); + } + if (rbtversion != NULL && !newheader_nx) { + rbtversion->records += + dns_rdataslab_count((unsigned char *)newheader, + sizeof(*newheader)); + rbtversion->bytes += + dns_rdataslab_size((unsigned char *)newheader, + sizeof(*newheader)); + } + if (rbtversion != NULL) + RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write); if (loading) { /* * There are no other references to 'header' when @@ -6490,6 +6536,16 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, newheader->down = NULL; rbtnode->data = newheader; } + if (rbtversion != NULL && !newheader_nx) { + RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write); + rbtversion->records += + dns_rdataslab_count((unsigned char *)newheader, + sizeof(*newheader)); + rbtversion->bytes += + dns_rdataslab_size((unsigned char *)newheader, + sizeof(*newheader)); + RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write); + } idx = newheader->node->locknum; if (IS_CACHE(rbtdb)) { ISC_LIST_PREPEND(rbtdb->rdatasets[idx], @@ -6944,6 +7000,12 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, */ newheader->additional_auth = NULL; newheader->additional_glue = NULL; + rbtversion->records += + dns_rdataslab_count((unsigned char *)newheader, + sizeof(*newheader)); + rbtversion->bytes += + dns_rdataslab_size((unsigned char *)newheader, + sizeof(*newheader)); } else if (result == DNS_R_NXRRSET) { /* * This subtraction would remove all of the rdata; @@ -6978,6 +7040,12 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, * topheader. */ INSIST(rbtversion->serial >= topheader->serial); + rbtversion->records -= + dns_rdataslab_count((unsigned char *)header, + sizeof(*header)); + rbtversion->bytes -= + dns_rdataslab_size((unsigned char *)header, + sizeof(*header)); if (topheader_prev != NULL) topheader_prev->next = newheader; else @@ -7509,6 +7577,33 @@ getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash, } static isc_result_t +getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records, + isc_uint64_t *bytes) +{ + dns_rbtdb_t *rbtdb; + isc_result_t result = ISC_R_SUCCESS; + rbtdb_version_t *rbtversion = version; + + rbtdb = (dns_rbtdb_t *)db; + + REQUIRE(VALID_RBTDB(rbtdb)); + INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb); + + if (rbtversion == NULL) + rbtversion = rbtdb->current_version; + + RWLOCK(&rbtversion->rwlock, isc_rwlocktype_read); + if (records != NULL) + *records = rbtversion->records; + + if (bytes != NULL) + *bytes = rbtversion->bytes; + RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_read); + + return (result); +} + +static isc_result_t setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; isc_stdtime_t oldresign; @@ -7694,7 +7789,8 @@ static dns_dbmethods_t zone_methods = { NULL, #endif NULL, - NULL + NULL, + getsize }; static dns_dbmethods_t cache_methods = { @@ -7737,6 +7833,7 @@ static dns_dbmethods_t cache_methods = { NULL, NULL, NULL, + NULL, NULL }; @@ -8025,6 +8122,20 @@ dns_rbtdb_create rbtdb->current_version->salt_length = 0; memset(rbtdb->current_version->salt, 0, sizeof(rbtdb->current_version->salt)); + result = isc_rwlock_init(&rbtdb->current_version->rwlock, 0, 0); + if (result != ISC_R_SUCCESS) { + isc_refcount_destroy(&rbtdb->current_version->references); + isc_mem_put(mctx, rbtdb->current_version, + sizeof(*rbtdb->current_version)); + rbtdb->current_version = NULL; + isc_refcount_decrement(&rbtdb->references, NULL); + isc_refcount_destroy(&rbtdb->references); + free_rbtdb(rbtdb, ISC_FALSE, NULL); + return (result); + } + + rbtdb->current_version->records = 0; + rbtdb->current_version->bytes = 0; rbtdb->future_version = NULL; ISC_LIST_INIT(rbtdb->open_versions); /* diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c index 9367127d9eaf..4dd38443c77c 100644 --- a/lib/dns/rdataslab.c +++ b/lib/dns/rdataslab.c @@ -522,6 +522,19 @@ dns_rdataslab_size(unsigned char *slab, unsigned int reservelen) { return ((unsigned int)(current - slab)); } +unsigned int +dns_rdataslab_count(unsigned char *slab, unsigned int reservelen) { + unsigned int count; + unsigned char *current; + + REQUIRE(slab != NULL); + + current = slab + reservelen; + count = *current++ * 256; + count += *current++; + return (count); +} + /* * Make the dns_rdata_t 'rdata' refer to the slab item * beginning at '*current', which is part of a slab of type diff --git a/lib/dns/result.c b/lib/dns/result.c index 7be4f577ed86..f03bbe31601c 100644 --- a/lib/dns/result.c +++ b/lib/dns/result.c @@ -168,6 +168,7 @@ static const char *text[DNS_R_NRESULTS] = { "bad CDS", /*%< 111 DNS_R_BADCSD */ "bad CDNSKEY", /*%< 112 DNS_R_BADCDNSKEY */ "malformed OPT option" /*%< 113 DNS_R_OPTERR */ + "too many records", /*%< 114 DNS_R_TOOMANYRECORDS */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c index f0ffc3d6f384..a0655ad1ee92 100644 --- a/lib/dns/sdb.c +++ b/lib/dns/sdb.c @@ -1296,7 +1296,8 @@ static dns_dbmethods_t sdb_methods = { NULL, /* rpz_enabled */ NULL, /* rpz_findips */ findnodeext, - findext + findext, + NULL /* getsize */ }; static isc_result_t diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c index e70532021719..ff96af434933 100644 --- a/lib/dns/sdlz.c +++ b/lib/dns/sdlz.c @@ -1267,7 +1267,8 @@ static dns_dbmethods_t sdlzdb_methods = { NULL, /* rpz_enabled */ NULL, /* rpz_findips */ findnodeext, - findext + findext, + NULL /* getsize */ }; /* diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index aec238d3fc1a..e71e42dbac56 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -147,6 +147,9 @@ struct dns_xfrin_ctx { unsigned int nrecs; /*%< Number of records recvd */ isc_uint64_t nbytes; /*%< Number of bytes received */ + unsigned int maxrecords; /*%< The maximum number of + records set for the zone */ + isc_time_t start; /*%< Start time of the transfer */ isc_time_t end; /*%< End time of the transfer */ @@ -312,11 +315,19 @@ axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, static isc_result_t axfr_apply(dns_xfrin_ctx_t *xfr) { isc_result_t result; + isc_uint64_t records; CHECK(dns_diff_load(&xfr->diff, xfr->axfr.add_func, xfr->axfr.add_private)); xfr->difflen = 0; dns_diff_clear(&xfr->diff); + if (xfr->maxrecords != 0U) { + result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL); + if (result == ISC_R_SUCCESS && records > xfr->maxrecords) { + result = DNS_R_TOOMANYRECORDS; + goto failure; + } + } result = ISC_R_SUCCESS; failure: return (result); @@ -403,6 +414,7 @@ ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, static isc_result_t ixfr_apply(dns_xfrin_ctx_t *xfr) { isc_result_t result; + isc_uint64_t records; if (xfr->ver == NULL) { CHECK(dns_db_newversion(xfr->db, &xfr->ver)); @@ -410,6 +422,13 @@ ixfr_apply(dns_xfrin_ctx_t *xfr) { CHECK(dns_journal_begin_transaction(xfr->ixfr.journal)); } CHECK(dns_diff_apply(&xfr->diff, xfr->db, xfr->ver)); + if (xfr->maxrecords != 0U) { + result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL); + if (result == ISC_R_SUCCESS && records > xfr->maxrecords) { + result = DNS_R_TOOMANYRECORDS; + goto failure; + } + } if (xfr->ixfr.journal != NULL) { result = dns_journal_writediff(xfr->ixfr.journal, &xfr->diff); if (result != ISC_R_SUCCESS) @@ -752,7 +771,7 @@ xfrin_reset(dns_xfrin_ctx_t *xfr) { static void xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg) { - if (result != DNS_R_UPTODATE) { + if (result != DNS_R_UPTODATE && result != DNS_R_TOOMANYRECORDS) { xfrin_log(xfr, ISC_LOG_ERROR, "%s: %s", msg, isc_result_totext(result)); if (xfr->is_ixfr) @@ -843,6 +862,7 @@ xfrin_create(isc_mem_t *mctx, xfr->nmsg = 0; xfr->nrecs = 0; xfr->nbytes = 0; + xfr->maxrecords = dns_zone_getmaxrecords(zone); isc_time_now(&xfr->start); xfr->tsigkey = NULL; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 490248a68910..c59ef3201ec6 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -248,6 +248,8 @@ struct dns_zone { isc_uint32_t maxretry; isc_uint32_t minretry; + isc_uint32_t maxrecords; + isc_sockaddr_t *masters; dns_name_t **masterkeynames; isc_boolean_t *mastersok; @@ -9689,6 +9691,20 @@ dns_zone_setmaxretrytime(dns_zone_t *zone, isc_uint32_t val) { zone->maxretry = val; } +isc_uint32_t +dns_zone_getmaxrecords(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + + return (zone->maxrecords); +} + +void +dns_zone_setmaxrecords(dns_zone_t *zone, isc_uint32_t val) { + REQUIRE(DNS_ZONE_VALID(zone)); + + zone->maxrecords = val; +} + static isc_boolean_t notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name, isc_sockaddr_t *addr, dns_tsigkey_t *key) @@ -13977,7 +13993,7 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR); TIME_NOW(&now); - switch (result) { + switch (xfrresult) { case ISC_R_SUCCESS: DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); /*FALLTHROUGH*/ @@ -14104,6 +14120,11 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { DNS_ZONE_SETFLAG(zone, DNS_ZONEFLAG_NOIXFR); goto same_master; + case DNS_R_TOOMANYRECORDS: + DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime); + inc_stats(zone, dns_zonestatscounter_xfrfail); + break; + default: next_master: /* diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index d95a03a61433..6f149602c05f 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1597,6 +1597,7 @@ zone_clauses[] = { { "masterfile-format", &cfg_type_masterformat, 0 }, { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE }, { "max-journal-size", &cfg_type_sizenodefault, 0 }, + { "max-records", &cfg_type_uint32, 0 }, { "max-refresh-time", &cfg_type_uint32, 0 }, { "max-retry-time", &cfg_type_uint32, 0 }, { "max-transfer-idle-in", &cfg_type_uint32, 0 }, -- 2.12.0
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor