diff --git a/CHANGES b/CHANGES
index c4da40ae3152d838824c310c6a815f86e3ce4b83..1fa182cffc027de398020f3b4872bafbafce5048 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+4762. [func] "update-policy local" is now restricted to updates
+ from local addresses. (Previously, other addresses
+ were allowed so long as updates were signed by the
+ local session key.) [RT #45492]
+
4761. [protocol] Add support for DOA. [RT #45612]
4760. [func] Add glue cache statistics counters. [RT #46028]
diff --git a/bin/named/main.c b/bin/named/main.c
index 3f6034a2aa92444e8cbf660c08280f0790014ed3..979f81b5562feef3709f70f942843a26a461783d 100644
--- a/bin/named/main.c
+++ b/bin/named/main.c
@@ -127,6 +127,7 @@ static isc_boolean_t noaa = ISC_FALSE;
static unsigned int delay = 0;
static isc_boolean_t nonearest = ISC_FALSE;
static isc_boolean_t notcp = ISC_FALSE;
+static isc_boolean_t fixedlocal = ISC_FALSE;
/*
* -4 and -6
@@ -626,14 +627,21 @@ parse_command_line(int argc, char *argv[]) {
} else if (!strcmp(isc_commandline_argument, "notcp"))
notcp = ISC_TRUE;
else if (!strncmp(isc_commandline_argument, "tat=", 4))
+ {
named_g_tat_interval =
atoi(isc_commandline_argument + 4);
- else if (!strcmp(isc_commandline_argument,
+ } else if (!strcmp(isc_commandline_argument,
"keepstderr"))
+ {
named_g_keepstderr = ISC_TRUE;
- else
+ } else if (!strcmp(isc_commandline_argument,
+ "fixedlocal"))
+ {
+ fixedlocal = ISC_TRUE;
+ } else {
fprintf(stderr, "unknown -T flag '%s\n",
isc_commandline_argument);
+ }
break;
case 'U':
named_g_udpdisp = parse_int(isc_commandline_argument,
@@ -1193,6 +1201,8 @@ setup(void) {
ns_server_setoption(sctx, NS_SERVER_NONEAREST, ISC_TRUE);
if (notcp)
ns_server_setoption(sctx, NS_SERVER_NOTCP, ISC_TRUE);
+ if (fixedlocal)
+ ns_server_setoption(sctx, NS_SERVER_FIXEDLOCAL, ISC_TRUE);
if (disable4)
ns_server_setoption(sctx, NS_SERVER_DISABLE4, ISC_TRUE);
if (disable6)
diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
index 51325a4cf7f9b311050a9b3db788a9cb606b3473..610588ed9eae49203ad027b96b4d2e3b344bd439 100644
--- a/bin/named/zoneconf.c
+++ b/bin/named/zoneconf.c
@@ -218,7 +218,7 @@ configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
const char *str;
isc_boolean_t grant = ISC_FALSE;
isc_boolean_t usezone = ISC_FALSE;
- unsigned int mtype = DNS_SSUMATCHTYPE_NAME;
+ unsigned int mtype = dns_ssumatchtype_name;
dns_fixedname_t fname, fident;
isc_buffer_t b;
dns_rdatatype_t *types;
@@ -234,34 +234,34 @@ configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
str = cfg_obj_asstring(matchtype);
if (strcasecmp(str, "name") == 0)
- mtype = DNS_SSUMATCHTYPE_NAME;
+ mtype = dns_ssumatchtype_name;
else if (strcasecmp(str, "subdomain") == 0)
- mtype = DNS_SSUMATCHTYPE_SUBDOMAIN;
+ mtype = dns_ssumatchtype_subdomain;
else if (strcasecmp(str, "wildcard") == 0)
- mtype = DNS_SSUMATCHTYPE_WILDCARD;
+ mtype = dns_ssumatchtype_wildcard;
else if (strcasecmp(str, "self") == 0)
- mtype = DNS_SSUMATCHTYPE_SELF;
+ mtype = dns_ssumatchtype_self;
else if (strcasecmp(str, "selfsub") == 0)
- mtype = DNS_SSUMATCHTYPE_SELFSUB;
+ mtype = dns_ssumatchtype_selfsub;
else if (strcasecmp(str, "selfwild") == 0)
- mtype = DNS_SSUMATCHTYPE_SELFWILD;
+ mtype = dns_ssumatchtype_selfwild;
else if (strcasecmp(str, "ms-self") == 0)
- mtype = DNS_SSUMATCHTYPE_SELFMS;
+ mtype = dns_ssumatchtype_selfms;
else if (strcasecmp(str, "krb5-self") == 0)
- mtype = DNS_SSUMATCHTYPE_SELFKRB5;
+ mtype = dns_ssumatchtype_selfkrb5;
else if (strcasecmp(str, "ms-subdomain") == 0)
- mtype = DNS_SSUMATCHTYPE_SUBDOMAINMS;
+ mtype = dns_ssumatchtype_subdomainms;
else if (strcasecmp(str, "krb5-subdomain") == 0)
- mtype = DNS_SSUMATCHTYPE_SUBDOMAINKRB5;
+ mtype = dns_ssumatchtype_subdomainkrb5;
else if (strcasecmp(str, "tcp-self") == 0)
- mtype = DNS_SSUMATCHTYPE_TCPSELF;
+ mtype = dns_ssumatchtype_tcpself;
else if (strcasecmp(str, "6to4-self") == 0)
- mtype = DNS_SSUMATCHTYPE_6TO4SELF;
+ mtype = dns_ssumatchtype_6to4self;
else if (strcasecmp(str, "zonesub") == 0) {
- mtype = DNS_SSUMATCHTYPE_SUBDOMAIN;
+ mtype = dns_ssumatchtype_subdomain;
usezone = ISC_TRUE;
} else if (strcasecmp(str, "external") == 0)
- mtype = DNS_SSUMATCHTYPE_EXTERNAL;
+ mtype = dns_ssumatchtype_external;
else
INSIST(0);
@@ -373,7 +373,7 @@ configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
result = dns_ssutable_addrule(table, ISC_TRUE,
named_g_server->session_keyname,
- DNS_SSUMATCHTYPE_SUBDOMAIN,
+ dns_ssumatchtype_local,
dns_zone_getorigin(zone),
1, &any);
diff --git a/bin/tests/system/nsupdate/clean.sh b/bin/tests/system/nsupdate/clean.sh
index 5d90b312d61f0e13dbc3a3d63d52d8290979844a..71721ec469dd6ba70d7983b71fcf9a3313c394af 100644
--- a/bin/tests/system/nsupdate/clean.sh
+++ b/bin/tests/system/nsupdate/clean.sh
@@ -11,12 +11,12 @@
#
rm -f */named.memstats
-rm -f */named.run
+rm -f */named.run */ans.run
rm -f Kxxx.*
rm -f dig.out.*
rm -f jp.out.ns3.*
rm -f ns*/named.lock
-rm -f ns1/*.jnl ns2/*.jnl ns3/*.jnl
+rm -f */*.jnl
rm -f ns1/example.db ns1/unixtime.db ns1/yyyymmddvv.db ns1/update.db ns1/other.db ns1/keytests.db
rm -f ns1/many.test.db
rm -f ns1/maxjournal.db
@@ -33,6 +33,7 @@ rm -f ns3/example.db
rm -f ns3/many.test.bk
rm -f ns3/nsec3param.test.db
rm -f ns3/too-big.test.db
+rm -f ns5/local.db
rm -f nsupdate.out*
rm -f typelist.out.*
rm -f ns1/sample.db
diff --git a/bin/tests/system/nsupdate/ns5/local.db.in b/bin/tests/system/nsupdate/ns5/local.db.in
new file mode 100644
index 0000000000000000000000000000000000000000..707d1be598a0b675546afcc6ad265ff8a50bae11
--- /dev/null
+++ b/bin/tests/system/nsupdate/ns5/local.db.in
@@ -0,0 +1,20 @@
+; Copyright (C) 2017 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/.
+
+$ORIGIN .
+$TTL 300 ; 5 minutes
+local.nil IN SOA ns5.local.nil. hostmaster.local.nil. (
+ 1 ; serial
+ 2000 ; refresh (2000 seconds)
+ 2000 ; retry (2000 seconds)
+ 1814400 ; expire (3 weeks)
+ 3600 ; minimum (1 hour)
+ )
+local.nil. NS ns5.local.nil.
+ns5.local.nil. A 10.53.0.5
+
+$ORIGIN local.nil.
+a A 10.10.10.10
diff --git a/bin/tests/system/nsupdate/ns5/named.args b/bin/tests/system/nsupdate/ns5/named.args
new file mode 100644
index 0000000000000000000000000000000000000000..5d520debe17c090ad1eec450ab9a4ff41d0970ad
--- /dev/null
+++ b/bin/tests/system/nsupdate/ns5/named.args
@@ -0,0 +1 @@
+-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
diff --git a/bin/tests/system/nsupdate/ns5/named.conf b/bin/tests/system/nsupdate/ns5/named.conf
new file mode 100644
index 0000000000000000000000000000000000000000..3632c7cb99bcda4b3e8ea389d94bd1bfc7814b80
--- /dev/null
+++ b/bin/tests/system/nsupdate/ns5/named.conf
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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/.
+ */
+
+controls { /* empty */ };
+
+options {
+ query-source address 10.53.0.5;
+ notify-source 10.53.0.5;
+ transfer-source 10.53.0.5;
+ port 5300;
+ pid-file "named.pid";
+ session-keyfile "session.key";
+ listen-on { 10.53.0.5; };
+ recursion no;
+ notify yes;
+ minimal-responses no;
+};
+
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm hmac-sha256;
+};
+
+controls {
+ inet 10.53.0.5 port 9953 allow { any; } keys { rndc_key; };
+};
+
+zone "local.nil" {
+ type master;
+ file "local.db";
+ update-policy local;
+};
diff --git a/bin/tests/system/nsupdate/setup.sh b/bin/tests/system/nsupdate/setup.sh
index 872d26200abd67d44b5af0c5b855450f9426d969..1e12a1ca5fadaab23b3868cea4ea15411d1de129 100644
--- a/bin/tests/system/nsupdate/setup.sh
+++ b/bin/tests/system/nsupdate/setup.sh
@@ -64,3 +64,5 @@ cp ns2/sample.db.in ns2/sample.db
cp -f ns1/maxjournal.db.in ns1/maxjournal.db
rm -f ns1/maxjournal.db.jnl
+
+cp -f ns5/local.db.in ns5/local.db
diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh
index 707ffe156ebe3519a74f1ef7c0f7e8b4001724ca..30b8b7c29f2fba446273cf6bf082430be03ec531 100755
--- a/bin/tests/system/nsupdate/tests.sh
+++ b/bin/tests/system/nsupdate/tests.sh
@@ -464,6 +464,44 @@ then
echo "I:failed"; status=1
fi
+n=`expr $n + 1`
+ret=0
+echo "I:check that 'update-policy local' works from localhost address ($n)"
+$NSUPDATE -p 5300 -k ns5/session.key > nsupdate.out.$n 2>&1 << END || ret=1
+server 10.53.0.5 5300
+local 127.0.0.1 5300
+update add fromlocal.local.nil. 600 A 1.2.3.4
+send
+END
+grep REFUSED nsupdate.out.$n > /dev/null 2>&1 && ret=1
+$DIG @10.53.0.5 -p 5300 \
+ +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd \
+ fromlocal.local.nil. > dig.out.ns5.$n || ret=1
+grep fromlocal dig.out.ns5.$n > /dev/null 2>&1 || ret=1
+if test $ret -ne 0
+then
+echo "I:failed"; status=1
+fi
+
+n=`expr $n + 1`
+ret=0
+echo "I:check that 'update-policy local' fails from non-localhost address ($n)"
+$NSUPDATE -p 5300 -k ns5/session.key > nsupdate.out.$n 2>&1 << END && ret=1
+server 10.53.0.5 5300
+local 10.53.0.1 5300
+update add nonlocal.local.nil. 600 A 4.3.2.1
+send
+END
+grep REFUSED nsupdate.out.$n > /dev/null 2>&1 || ret=1
+$DIG @10.53.0.5 -p 5300 \
+ +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd \
+ nonlocal.local.nil. > dig.out.ns5.$n || ret=1
+grep nonlocal dig.out.ns5.$n > /dev/null 2>&1 && ret=1
+if test $ret -ne 0
+then
+echo "I:failed"; status=1
+fi
+
n=`expr $n + 1`
ret=0
echo "I:check that changes to the DNSKEY RRset TTL do not have side effects ($n)"
diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
index 455266e78582f5c905c188d08af8f0aa04829f6f..f2ed4a08a32167dec7fd3a4aefc72c39c0fbe415 100644
--- a/doc/arm/Bv9ARM-book.xml
+++ b/doc/arm/Bv9ARM-book.xml
@@ -13016,38 +13016,52 @@ example.com. NS ns2.example.net.
is present, it is a configuration error for the
allow-update statement to be
present. The update-policy statement
- only examines the signer of a message; the source
+ (except when set to local) only
+ examines the signer of a message; the source
address is not relevant.
- There is a pre-defined update-policy
- rule which can be switched on with the command
+ A pre-defined update-policy rule can be
+ switched on with the command
update-policy local;.
Switching on this rule in a zone causes
- named to generate a TSIG session
- key and place it in a file, and to allow that key
- to update the zone. (By default, the file is
- /var/run/named/session.key, the key
- name is "local-ddns" and the key algorithm is HMAC-SHA256,
- but these values are configurable with the
+ named to generate a TSIG session key and
+ place it in a file. That key will then be allowed to update
+ the zone, if the update request is sent from localhost.
+ By default, the session key is stored in the file
+ /var/run/named/session.key; the key name
+ is "local-ddns" and the key algorithm is HMAC-SHA256.
+ These values are configurable with the
session-keyfile,
session-keyname and
session-keyalg options, respectively).
- A client running on the local system, and with appropriate
- permissions, may read that file and use the key to sign update
- requests. The zone's update policy will be set to allow that
- key to change any record within the zone. Assuming the
- key name is "local-ddns", this policy is equivalent to:
+ A client on the local system, if it is run with appropriate
+ permissions, may read the session key from the key file and
+ use the key to sign update requests. The zone's update
+ policy will be set to allow that key to change any record
+ within the zone. Assuming the key name is "local-ddns",
+ this policy is:
update-policy { grant local-ddns zonesub any; };
- The command nsupdate -l sends update
- requests to localhost, and signs them using the session key.
+ ...with an additional restriction that only clients
+ connecting from the local system will be permitted to send
+ updates.
+
+
+ Note that only one session key is generated; all zones
+ configured to use update-policy local
+ will accept the same key.
+
+
+ The command nsupdate -l implements this
+ feature, sending requests to localhost and signing them using
+ the key retrieved from the session key file.
diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml
index 359881e9dca163537ccc746d653986d7136afd9e..d6f31ac27f1d43f230987247345ee7046b1cbc4c 100644
--- a/doc/arm/notes.xml
+++ b/doc/arm/notes.xml
@@ -471,6 +471,15 @@
anchor is now a fatal configuration error. [RT #46155]
+
+
+ Previously, update-policy local; accepted
+ updates from any source so long as they were signed by the
+ locally-generated session key. This has been further restricted;
+ updates are now only accepted from locally configured addresses.
+ [RT #45492]
+
+
The lightweight resolver daemon and library (lwresd
diff --git a/lib/dns/include/dns/ssu.h b/lib/dns/include/dns/ssu.h
index eda7ff8436a43162e26f8e1ac5da2c97e7e2df61..1b4510b050e39d5c1a3b980758ee92730bd01e53 100644
--- a/lib/dns/include/dns/ssu.h
+++ b/lib/dns/include/dns/ssu.h
@@ -6,8 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-/* $Id: ssu.h,v 1.28 2011/01/06 23:47:00 tbox Exp $ */
-
#ifndef DNS_SSU_H
#define DNS_SSU_H 1
@@ -15,26 +13,31 @@
#include
+#include
#include
#include
ISC_LANG_BEGINDECLS
-#define DNS_SSUMATCHTYPE_NAME 0
-#define DNS_SSUMATCHTYPE_SUBDOMAIN 1
-#define DNS_SSUMATCHTYPE_WILDCARD 2
-#define DNS_SSUMATCHTYPE_SELF 3
-#define DNS_SSUMATCHTYPE_SELFSUB 4
-#define DNS_SSUMATCHTYPE_SELFWILD 5
-#define DNS_SSUMATCHTYPE_SELFKRB5 6
-#define DNS_SSUMATCHTYPE_SELFMS 7
-#define DNS_SSUMATCHTYPE_SUBDOMAINMS 8
-#define DNS_SSUMATCHTYPE_SUBDOMAINKRB5 9
-#define DNS_SSUMATCHTYPE_TCPSELF 10
-#define DNS_SSUMATCHTYPE_6TO4SELF 11
-#define DNS_SSUMATCHTYPE_EXTERNAL 12
-#define DNS_SSUMATCHTYPE_DLZ 13
-#define DNS_SSUMATCHTYPE_MAX 12 /* max value */
+typedef enum {
+ dns_ssumatchtype_name = 0,
+ dns_ssumatchtype_subdomain = 1,
+ dns_ssumatchtype_wildcard = 2,
+ dns_ssumatchtype_self = 3,
+ dns_ssumatchtype_selfsub = 4,
+ dns_ssumatchtype_selfwild = 5,
+ dns_ssumatchtype_selfkrb5 = 6,
+ dns_ssumatchtype_selfms = 7,
+ dns_ssumatchtype_subdomainms = 8,
+ dns_ssumatchtype_subdomainkrb5 = 9,
+ dns_ssumatchtype_tcpself = 10,
+ dns_ssumatchtype_6to4self = 11,
+ dns_ssumatchtype_external = 12,
+ dns_ssumatchtype_local = 13,
+ dns_ssumatchtype_max = 13, /* max value */
+
+ dns_ssumatchtype_dlz = 14 /* intentionally higher than _max */
+} dns_ssumatchtype_t;
isc_result_t
dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **table);
@@ -56,7 +59,7 @@ dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
dns_dlzdb_t *dlzdatabase);
/*%<
* Create an SSU table that contains a dlzdatabase pointer, and a
- * single rule with matchtype DNS_SSUMATCHTYPE_DLZ. This type of SSU
+ * single rule with matchtype dns_ssumatchtype_dlz. This type of SSU
* table is used by writeable DLZ drivers to offload authorization for
* updates to the driver.
*/
@@ -90,7 +93,7 @@ dns_ssutable_detach(dns_ssutable_t **tablep);
isc_result_t
dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant,
- const dns_name_t *identity, unsigned int matchtype,
+ const dns_name_t *identity, dns_ssumatchtype_t matchtype,
const dns_name_t *name, unsigned int ntypes,
dns_rdatatype_t *types);
/*%<
@@ -123,7 +126,12 @@ dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant,
isc_boolean_t
dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
- const dns_name_t *name, const isc_netaddr_t *tcpaddr,
+ const dns_name_t *name, const isc_netaddr_t *addr,
+ dns_rdatatype_t type, const dst_key_t *key);
+isc_boolean_t
+dns_ssutable_checkrules2(dns_ssutable_t *table, const dns_name_t *signer,
+ const dns_name_t *name, const isc_netaddr_t *addr,
+ isc_boolean_t tcp, const dns_aclenv_t *env,
dns_rdatatype_t type, const dst_key_t *key);
/*%<
* Checks that the attempted update of (name, type) is allowed according
@@ -131,18 +139,26 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
* no rules are matched, access is denied.
*
* Notes:
- * 'tcpaddr' should only be set if the request received
- * via TCP. This provides a weak assurance that the
- * request was not spoofed. 'tcpaddr' is to to validate
- * DNS_SSUMATCHTYPE_TCPSELF and DNS_SSUMATCHTYPE_6TO4SELF
- * rules.
- *
- * For DNS_SSUMATCHTYPE_TCPSELF the addresses are mapped to
+ * In dns_ssutable_checkrules(), 'addr' should only be
+ * set if the request received via TCP. This provides a
+ * weak assurance that the request was not spoofed.
+ * 'addr' is to to validate dns_ssumatchtype_tcpself
+ * and dns_ssumatchtype_6to4self rules.
+ *
+ * In dns_ssutable_checkrules2(), 'addr' can also be passed for
+ * UDP requests and TCP is specified via the 'tcp' parameter.
+ * In addition to dns_ssumatchtype_tcpself and
+ * tcp_ssumatchtype_6to4self rules, the address
+ * also be used to check dns_ssumatchtype_local rules.
+ * If 'addr' is set then 'env' must also be set so that
+ * requests from non-localhost addresses can be rejected.
+ *
+ * For dns_ssumatchtype_tcpself the addresses are mapped to
* the standard reverse names under IN-ADDR.ARPA and IP6.ARPA.
* RFC 1035, Section 3.5, "IN-ADDR.ARPA domain" and RFC 3596,
* Section 2.5, "IP6.ARPA Domain".
*
- * For DNS_SSUMATCHTYPE_6TO4SELF, IPv4 address are converted
+ * For dns_ssumatchtype_6to4self, IPv4 address are converted
* to a 6to4 prefix (48 bits) per the rules in RFC 3056. Only
* the top 48 bits of the IPv6 address are mapped to the reverse
* name. This is independent of whether the most significant 16
@@ -151,8 +167,10 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
* Requires:
*\li 'table' is a valid SSU table
*\li 'signer' is NULL or a valid absolute name
- *\li 'tcpaddr' is NULL or a valid network address.
+ *\li 'addr' is NULL or a valid network address.
+ *\li 'aclenv' is NULL or a valid ACL environment.
*\li 'name' is a valid absolute name
+ *\li if 'addr' is not NULL, 'env' is not NULL.
*/
diff --git a/lib/dns/ssu.c b/lib/dns/ssu.c
index f86d41b57a41c57304e4bb774ab147aa2bfac7a0..b2f201b1995234f521ecfed66ebfdfe114f115cb 100644
--- a/lib/dns/ssu.c
+++ b/lib/dns/ssu.c
@@ -37,13 +37,14 @@
struct dns_ssurule {
unsigned int magic;
- isc_boolean_t grant; /*%< is this a grant or a deny? */
- unsigned int matchtype; /*%< which type of pattern match? */
- dns_name_t *identity; /*%< the identity to match */
- dns_name_t *name; /*%< the name being updated */
- unsigned int ntypes; /*%< number of data types covered */
- dns_rdatatype_t *types; /*%< the data types. Can include ANY, */
- /*%< defaults to all but SIG,SOA,NS if NULL */
+ isc_boolean_t grant; /*%< is this a grant or a deny? */
+ dns_ssumatchtype_t matchtype; /*%< which type of pattern match? */
+ dns_name_t *identity; /*%< the identity to match */
+ dns_name_t *name; /*%< the name being updated */
+ unsigned int ntypes; /*%< number of data types covered */
+ dns_rdatatype_t *types; /*%< the data types. Can include */
+ /* ANY. if NULL, defaults to all */
+ /* types except SIG, SOA, and NS */
ISC_LINK(dns_ssurule_t) link;
};
@@ -150,7 +151,7 @@ dns_ssutable_detach(dns_ssutable_t **tablep) {
isc_result_t
dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant,
- const dns_name_t *identity, unsigned int matchtype,
+ const dns_name_t *identity, dns_ssumatchtype_t matchtype,
const dns_name_t *name, unsigned int ntypes,
dns_rdatatype_t *types)
{
@@ -161,8 +162,8 @@ dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant,
REQUIRE(VALID_SSUTABLE(table));
REQUIRE(dns_name_isabsolute(identity));
REQUIRE(dns_name_isabsolute(name));
- REQUIRE(matchtype <= DNS_SSUMATCHTYPE_MAX);
- if (matchtype == DNS_SSUMATCHTYPE_WILDCARD)
+ REQUIRE(matchtype <= dns_ssumatchtype_max);
+ if (matchtype == dns_ssumatchtype_wildcard)
REQUIRE(dns_name_iswildcard(name));
if (ntypes > 0)
REQUIRE(types != NULL);
@@ -341,6 +342,17 @@ isc_boolean_t
dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
const dns_name_t *name, const isc_netaddr_t *tcpaddr,
dns_rdatatype_t type, const dst_key_t *key)
+{
+ return (dns_ssutable_checkrules2
+ (table, signer, name, tcpaddr,
+ tcpaddr == NULL ? ISC_FALSE : ISC_TRUE,
+ NULL, type, key));
+}
+isc_boolean_t
+dns_ssutable_checkrules2(dns_ssutable_t *table, const dns_name_t *signer,
+ const dns_name_t *name, const isc_netaddr_t *addr,
+ isc_boolean_t tcp, const dns_aclenv_t *env,
+ dns_rdatatype_t type, const dst_key_t *key)
{
dns_ssurule_t *rule;
unsigned int i;
@@ -349,12 +361,14 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
dns_name_t *tcpself;
dns_name_t *stfself;
isc_result_t result;
+ int match;
REQUIRE(VALID_SSUTABLE(table));
REQUIRE(signer == NULL || dns_name_isabsolute(signer));
REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(addr == NULL || env != NULL);
- if (signer == NULL && tcpaddr == NULL)
+ if (signer == NULL && addr == NULL)
return (ISC_FALSE);
for (rule = ISC_LIST_HEAD(table->rules);
@@ -362,12 +376,13 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
rule = ISC_LIST_NEXT(rule, link))
{
switch (rule->matchtype) {
- case DNS_SSUMATCHTYPE_NAME:
- case DNS_SSUMATCHTYPE_SUBDOMAIN:
- case DNS_SSUMATCHTYPE_WILDCARD:
- case DNS_SSUMATCHTYPE_SELF:
- case DNS_SSUMATCHTYPE_SELFSUB:
- case DNS_SSUMATCHTYPE_SELFWILD:
+ case dns_ssumatchtype_name:
+ case dns_ssumatchtype_local:
+ case dns_ssumatchtype_subdomain:
+ case dns_ssumatchtype_wildcard:
+ case dns_ssumatchtype_self:
+ case dns_ssumatchtype_selfsub:
+ case dns_ssumatchtype_selfwild:
if (signer == NULL)
continue;
if (dns_name_iswildcard(rule->identity)) {
@@ -379,42 +394,59 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
continue;
}
break;
- case DNS_SSUMATCHTYPE_SELFKRB5:
- case DNS_SSUMATCHTYPE_SELFMS:
- case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
- case DNS_SSUMATCHTYPE_SUBDOMAINMS:
+ case dns_ssumatchtype_selfkrb5:
+ case dns_ssumatchtype_selfms:
+ case dns_ssumatchtype_subdomainkrb5:
+ case dns_ssumatchtype_subdomainms:
if (signer == NULL)
continue;
break;
- case DNS_SSUMATCHTYPE_TCPSELF:
- case DNS_SSUMATCHTYPE_6TO4SELF:
- if (tcpaddr == NULL)
+ case dns_ssumatchtype_tcpself:
+ case dns_ssumatchtype_6to4self:
+ if (!tcp || addr == NULL)
continue;
break;
+ case dns_ssumatchtype_external:
+ case dns_ssumatchtype_dlz:
+ break;
}
switch (rule->matchtype) {
- case DNS_SSUMATCHTYPE_NAME:
+ case dns_ssumatchtype_name:
if (!dns_name_equal(name, rule->name))
continue;
break;
- case DNS_SSUMATCHTYPE_SUBDOMAIN:
+ case dns_ssumatchtype_subdomain:
if (!dns_name_issubdomain(name, rule->name))
continue;
break;
- case DNS_SSUMATCHTYPE_WILDCARD:
+ case dns_ssumatchtype_local:
+ if (addr == NULL) {
+ continue;
+ }
+ if (!dns_name_issubdomain(name, rule->name)) {
+
+ continue;
+ }
+ dns_acl_match(addr, NULL, env->localhost,
+ NULL, &match, NULL);
+ if (match == 0) {
+ continue;
+ }
+ break;
+ case dns_ssumatchtype_wildcard:
if (!dns_name_matcheswildcard(name, rule->name))
continue;
break;
- case DNS_SSUMATCHTYPE_SELF:
+ case dns_ssumatchtype_self:
if (!dns_name_equal(signer, name))
continue;
break;
- case DNS_SSUMATCHTYPE_SELFSUB:
+ case dns_ssumatchtype_selfsub:
if (!dns_name_issubdomain(name, signer))
continue;
break;
- case DNS_SSUMATCHTYPE_SELFWILD:
+ case dns_ssumatchtype_selfwild:
dns_fixedname_init(&fixed);
wildcard = dns_fixedname_name(&fixed);
result = dns_name_concatenate(dns_wildcardname, signer,
@@ -424,34 +456,34 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
if (!dns_name_matcheswildcard(name, wildcard))
continue;
break;
- case DNS_SSUMATCHTYPE_SELFKRB5:
+ case dns_ssumatchtype_selfkrb5:
if (!dst_gssapi_identitymatchesrealmkrb5(signer, name,
rule->identity))
continue;
break;
- case DNS_SSUMATCHTYPE_SELFMS:
+ case dns_ssumatchtype_selfms:
if (!dst_gssapi_identitymatchesrealmms(signer, name,
rule->identity))
continue;
break;
- case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
+ case dns_ssumatchtype_subdomainkrb5:
if (!dns_name_issubdomain(name, rule->name))
continue;
if (!dst_gssapi_identitymatchesrealmkrb5(signer, NULL,
rule->identity))
continue;
break;
- case DNS_SSUMATCHTYPE_SUBDOMAINMS:
+ case dns_ssumatchtype_subdomainms:
if (!dns_name_issubdomain(name, rule->name))
continue;
if (!dst_gssapi_identitymatchesrealmms(signer, NULL,
rule->identity))
continue;
break;
- case DNS_SSUMATCHTYPE_TCPSELF:
+ case dns_ssumatchtype_tcpself:
dns_fixedname_init(&fixed);
tcpself = dns_fixedname_name(&fixed);
- reverse_from_address(tcpself, tcpaddr);
+ reverse_from_address(tcpself, addr);
if (dns_name_iswildcard(rule->identity)) {
if (!dns_name_matcheswildcard(tcpself,
rule->identity))
@@ -463,10 +495,10 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
if (!dns_name_equal(tcpself, name))
continue;
break;
- case DNS_SSUMATCHTYPE_6TO4SELF:
+ case dns_ssumatchtype_6to4self:
dns_fixedname_init(&fixed);
stfself = dns_fixedname_name(&fixed);
- stf_from_address(stfself, tcpaddr);
+ stf_from_address(stfself, addr);
if (dns_name_iswildcard(rule->identity)) {
if (!dns_name_matcheswildcard(stfself,
rule->identity))
@@ -478,15 +510,15 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
if (!dns_name_equal(stfself, name))
continue;
break;
- case DNS_SSUMATCHTYPE_EXTERNAL:
+ case dns_ssumatchtype_external:
if (!dns_ssu_external_match(rule->identity, signer,
- name, tcpaddr, type, key,
+ name, addr, type, key,
table->mctx))
continue;
break;
- case DNS_SSUMATCHTYPE_DLZ:
+ case dns_ssumatchtype_dlz:
if (!dns_dlz_ssumatch(table->dlzdatabase, signer,
- name, tcpaddr, type, key))
+ name, addr, type, key))
continue;
break;
}
@@ -497,7 +529,7 @@ dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
* checks will have already checked
* the type.
*/
- if (rule->matchtype != DNS_SSUMATCHTYPE_DLZ &&
+ if (rule->matchtype != dns_ssumatchtype_dlz &&
!isusertype(type))
continue;
} else {
@@ -592,7 +624,7 @@ dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
rule->name = NULL;
rule->types = NULL;
rule->grant = ISC_TRUE;
- rule->matchtype = DNS_SSUMATCHTYPE_DLZ;
+ rule->matchtype = dns_ssumatchtype_dlz;
rule->ntypes = 0;
rule->types = NULL;
rule->magic = SSURULEMAGIC;
diff --git a/lib/isc/include/isc/netaddr.h b/lib/isc/include/isc/netaddr.h
index b333ec423f02f5c087264fb5e9c5d6381cfffd60..86cf6da2b482c718fc8ec2f20045e77ca6973861 100644
--- a/lib/isc/include/isc/netaddr.h
+++ b/lib/isc/include/isc/netaddr.h
@@ -178,6 +178,12 @@ isc_netaddr_prefixok(const isc_netaddr_t *na, unsigned int prefixlen);
* ISC_R_FAILURE extra bits.
*/
+isc_boolean_t
+isc_netaddr_isloopback(const isc_netaddr_t *na);
+/*
+ * Test whether the netaddr 'na' is a loopback IPv4 or IPv6 address (in
+ * 127.0.0.0/8 or ::1).
+ */
ISC_LANG_ENDDECLS
#endif /* ISC_NETADDR_H */
diff --git a/lib/isc/netaddr.c b/lib/isc/netaddr.c
index 2ecd992e8a62213d2a08ab5704439feb4a034f15..483d240fbd5cd9bf3190494b5cadd384f8622576 100644
--- a/lib/isc/netaddr.c
+++ b/lib/isc/netaddr.c
@@ -445,3 +445,16 @@ isc_netaddr_fromv4mapped(isc_netaddr_t *t, const isc_netaddr_t *s) {
memmove(&t->type.in, (char *)&src->type.in6 + 12, 4);
return;
}
+
+isc_boolean_t
+isc_netaddr_isloopback(const isc_netaddr_t *na) {
+ switch (na->family) {
+ case AF_INET:
+ return (ISC_TF((ntohl(na->type.in.s_addr) & 0xff000000U) ==
+ 0x7f000000U));
+ case AF_INET6:
+ return (IN6_IS_ADDR_LOOPBACK(&na->type.in6));
+ default:
+ return (ISC_FALSE);
+ }
+}
diff --git a/lib/ns/include/ns/server.h b/lib/ns/include/ns/server.h
index 32493d89ef6f9d1b4a995b9ef743131f7238e6e1..9e6c73edd89765ed56c4136474648f51215ed112 100644
--- a/lib/ns/include/ns/server.h
+++ b/lib/ns/include/ns/server.h
@@ -35,7 +35,8 @@
#define NS_SERVER_DROPEDNS 0x00000040U /*%< -T dropedns */
#define NS_SERVER_NOTCP 0x00000080U /*%< -T notcp */
#define NS_SERVER_DISABLE4 0x00000100U /*%< -6 */
-#define NS_SERVER_DISABLE6 0x00000100U /*%< -4 */
+#define NS_SERVER_DISABLE6 0x00000200U /*%< -4 */
+#define NS_SERVER_FIXEDLOCAL 0x00000400U /*%< -T fixedlocal */
/*%
* Type for callback function to get hostname.
diff --git a/lib/ns/interfacemgr.c b/lib/ns/interfacemgr.c
index 3c0e427dad5fda21776b8932ec6e777f544b7752..35d59c47d7bd19c0b4e717d08fee6cfd45b8d486 100644
--- a/lib/ns/interfacemgr.c
+++ b/lib/ns/interfacemgr.c
@@ -997,11 +997,23 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
}
if (adjusting == ISC_FALSE) {
+ /*
+ * If running with -T fixedlocal, then we only
+ * want 127.0.0.1 and ::1 in the localhost ACL.
+ */
+ if (((mgr->sctx->options &
+ NS_SERVER_FIXEDLOCAL) != 0) &&
+ !isc_netaddr_isloopback(&interface.address))
+ {
+ goto listenon;
+ }
+
result = setup_locals(mgr, &interface);
if (result != ISC_R_SUCCESS)
goto ignore_interface;
}
+ listenon:
ll = (family == AF_INET) ? mgr->listenon4 : mgr->listenon6;
dolistenon = ISC_TRUE;
for (le = ISC_LIST_HEAD(ll->elts);
diff --git a/lib/ns/update.c b/lib/ns/update.c
index f598204f8983ff2b733fe8445a475a4022247145..6dcb4a988ddf6038a7cd4ce0931651246650f475 100644
--- a/lib/ns/update.c
+++ b/lib/ns/update.c
@@ -42,6 +42,7 @@
#include
#include
+#include
#include
#include
#include
@@ -863,8 +864,14 @@ typedef struct {
/* The signature's name if the request was signed. */
dns_name_t *signer;
- /* The address of the client if the request was received via TCP. */
- isc_netaddr_t *tcpaddr;
+ /* The address of the client. */
+ isc_netaddr_t *addr;
+
+ /* The ACL environment */
+ dns_aclenv_t *aclenv;
+
+ /* Whether the request was sent via TCP. */
+ isc_boolean_t tcp;
/* The ssu table to check against. */
dns_ssutable_t *table;
@@ -885,16 +892,18 @@ ssu_checkrule(void *data, dns_rdataset_t *rrset) {
if (rrset->type == dns_rdatatype_rrsig ||
rrset->type == dns_rdatatype_nsec)
return (ISC_R_SUCCESS);
- result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer,
- ssuinfo->name, ssuinfo->tcpaddr,
- rrset->type, ssuinfo->key);
+ result = dns_ssutable_checkrules2(ssuinfo->table, ssuinfo->signer,
+ ssuinfo->name, ssuinfo->addr,
+ ssuinfo->tcp, ssuinfo->aclenv,
+ rrset->type, ssuinfo->key);
return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE);
}
static isc_boolean_t
ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
dns_ssutable_t *ssutable, dns_name_t *signer,
- isc_netaddr_t *tcpaddr, dst_key_t *key)
+ isc_netaddr_t *addr, dns_aclenv_t *aclenv, isc_boolean_t tcp,
+ dst_key_t *key)
{
isc_result_t result;
ssu_check_t ssuinfo;
@@ -902,7 +911,9 @@ ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
ssuinfo.name = name;
ssuinfo.table = ssutable;
ssuinfo.signer = signer;
- ssuinfo.tcpaddr = tcpaddr;
+ ssuinfo.addr = addr;
+ ssuinfo.aclenv = aclenv;
+ ssuinfo.tcp = tcp;
ssuinfo.key = key;
result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
return (ISC_TF(result == ISC_R_SUCCESS));
@@ -2497,6 +2508,7 @@ update_action(isc_task_t *task, isc_event_t *event) {
dns_ttl_t maxttl = 0;
isc_uint32_t maxrecords;
isc_uint64_t records;
+ dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
INSIST(event->ev_type == DNS_EVENT_UPDATE);
@@ -2565,7 +2577,8 @@ update_action(isc_task_t *task, isc_event_t *event) {
rdata.type, covers, &flag));
if (! flag) {
/* RRset does not exist. */
- PREREQFAILNT(DNS_R_NXRRSET, name, rdata.type,
+ PREREQFAILNT(DNS_R_NXRRSET,
+ name, rdata.type,
"'rrset exists (value independent)' "
"prerequisite not satisfied");
}
@@ -2726,38 +2739,32 @@ update_action(isc_task_t *task, isc_event_t *event) {
}
if (ssutable != NULL) {
- isc_netaddr_t *tcpaddr, netaddr;
+ isc_netaddr_t netaddr;
dst_key_t *tsigkey = NULL;
- /*
- * If this is a TCP connection then pass the
- * address of the client through for tcp-self
- * and 6to4-self otherwise pass NULL. This
- * provides weak address based authentication.
- */
- if (TCPCLIENT(client)) {
- isc_netaddr_fromsockaddr(&netaddr,
- &client->peeraddr);
- tcpaddr = &netaddr;
- } else
- tcpaddr = NULL;
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
if (client->message->tsigkey != NULL)
tsigkey = client->message->tsigkey->key;
if (rdata.type != dns_rdatatype_any) {
- if (!dns_ssutable_checkrules(ssutable,
- client->signer,
- name, tcpaddr,
- rdata.type,
- tsigkey))
+ if (!dns_ssutable_checkrules2
+ (ssutable, client->signer, name, &netaddr,
+ ISC_TF(TCPCLIENT(client)),
+ env, rdata.type, tsigkey))
+ {
FAILC(DNS_R_REFUSED,
"rejected by secure update");
+ }
} else {
if (!ssu_checkall(db, ver, name, ssutable,
- client->signer, tcpaddr,
+ client->signer,
+ &netaddr, env,
+ ISC_TF(TCPCLIENT(client)),
tsigkey))
+ {
FAILC(DNS_R_REFUSED,
"rejected by secure update");
+ }
}
}
}