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"); + } } } }