diff --git a/CHANGES b/CHANGES index 8901b25c81..ccec1b2cf5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +5885. [bug] RPZ NSIP and NSDNAME rule processing didn't handle stub + and static-stub zones at or above the query name. This + has now been addressed. [GL #3232] + 5882. [contrib] Avoid name space collision in dlz modules by prefixing functions with 'dlz_'. [GL !5778] diff --git a/bin/tests/system/rpz/ns10/hints b/bin/tests/system/rpz/ns10/hints new file mode 100644 index 0000000000..b657c3980e --- /dev/null +++ b/bin/tests/system/rpz/ns10/hints @@ -0,0 +1,13 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +. 120 NS ns. +ns. 120 A 10.53.0.1 diff --git a/bin/tests/system/rpz/ns10/named.conf.in b/bin/tests/system/rpz/ns10/named.conf.in new file mode 100644 index 0000000000..b34ce79bb4 --- /dev/null +++ b/bin/tests/system/rpz/ns10/named.conf.in @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.10; + notify-source 10.53.0.10; + transfer-source 10.53.0.10; + port @PORT@; + pid-file "named.pid"; + session-keyfile "session.key"; + listen-on { 10.53.0.10; }; + listen-on-v6 { none; }; + notify no; + minimal-responses no; + recursion yes; + dnssec-validation yes; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; +controls { + inet 10.53.0.10 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +include "../trusted.conf"; +zone "." { type hint; file "hints"; }; + +# grafted on zones using stub and static-stub +zone "stub-nomatch." {type primary; file "stub.db"; }; +zone "static-stub-nomatch." {type primary; file "stub.db"; }; diff --git a/bin/tests/system/rpz/ns10/stub.db b/bin/tests/system/rpz/ns10/stub.db new file mode 100644 index 0000000000..8ecac8c2b2 --- /dev/null +++ b/bin/tests/system/rpz/ns10/stub.db @@ -0,0 +1,21 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +; RPZ rewrite responses from this zone + +$TTL 120 +@ SOA ns hostmaster.ns ( 1 3600 1200 604800 60 ) + NS ns +ns A 10.53.0.10 + +a3-1 A 10.53.99.99 + +a4-1 A 10.53.99.99 diff --git a/bin/tests/system/rpz/ns2/named.conf.in b/bin/tests/system/rpz/ns2/named.conf.in index 48ab311a97..1dde354562 100644 --- a/bin/tests/system/rpz/ns2/named.conf.in +++ b/bin/tests/system/rpz/ns2/named.conf.in @@ -49,3 +49,7 @@ zone "tld2s." {type primary; file "tld2s.db";}; zone "bl.tld2." {type primary; file "bl.tld2.db"; notify yes; notify-delay 0;}; + +# grafted on zones using stub and static-stub +zone "stub." {type primary; file "stub.db"; }; +zone "static-stub." {type primary; file "stub.db"; }; diff --git a/bin/tests/system/rpz/ns2/stub.db b/bin/tests/system/rpz/ns2/stub.db new file mode 100644 index 0000000000..e4b87817e7 --- /dev/null +++ b/bin/tests/system/rpz/ns2/stub.db @@ -0,0 +1,20 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +; RPZ rewrite responses from this zone + +$TTL 120 +@ SOA tld2. hostmaster.ns.tld2. ( 1 3600 1200 604800 60 ) + NS ns.sub1.tld2. + +a3-1 A 10.53.99.99 + +a4-1 A 10.53.99.99 diff --git a/bin/tests/system/rpz/ns3/named.conf.in b/bin/tests/system/rpz/ns3/named.conf.in index e5545a8720..30f08c804c 100644 --- a/bin/tests/system/rpz/ns3/named.conf.in +++ b/bin/tests/system/rpz/ns3/named.conf.in @@ -128,3 +128,23 @@ zone "fast-expire." { primaries { 10.53.0.5; }; notify no; }; + +zone "stub." { + type stub; + primaries { 10.53.0.2; }; +}; + +zone "static-stub." { + type static-stub; + server-addresses { 10.53.0.2; }; +}; + +zone "stub-nomatch." { + type stub; + primaries { 10.53.0.10; }; +}; + +zone "static-stub-nomatch." { + type static-stub; + server-addresses { 10.53.0.10; }; +}; diff --git a/bin/tests/system/rpz/setup.sh b/bin/tests/system/rpz/setup.sh index e679a8aab2..2f29ed45e8 100644 --- a/bin/tests/system/rpz/setup.sh +++ b/bin/tests/system/rpz/setup.sh @@ -53,6 +53,7 @@ copy_setports ns6/named.conf.in ns6/named.conf copy_setports ns7/named.conf.in ns7/named.conf copy_setports ns8/named.conf.in ns8/named.conf copy_setports ns9/named.conf.in ns9/named.conf +copy_setports ns10/named.conf.in ns10/named.conf copy_setports dnsrpzd.conf.in dnsrpzd.conf diff --git a/bin/tests/system/rpz/tests.sh b/bin/tests/system/rpz/tests.sh index 12fb767648..49a8316b08 100644 --- a/bin/tests/system/rpz/tests.sh +++ b/bin/tests/system/rpz/tests.sh @@ -27,6 +27,8 @@ ns5=$ns.5 # another rewriting resolver ns6=$ns.6 # a forwarding server ns7=$ns.7 # another rewriting resolver ns8=$ns.8 # another rewriting resolver +ns9=$ns.9 # another rewriting resolver +ns10=$ns.10 # authoritative server HAVE_CORE= @@ -405,6 +407,13 @@ nochange () { ckresult "$*" ${DIGNM}_OK && clean_result ${DIGNM}_OK } +nochange_ns10 () { + make_dignm + digcmd $* >$DIGNM + digcmd $* @$ns10 >${DIGNM}_OK + ckresult "$*" ${DIGNM}_OK && clean_result ${DIGNM}_OK +} + # check against a 'here document' here () { make_dignm @@ -620,6 +629,7 @@ EOF # these tests assume "min-ns-dots 0" start_group "NSDNAME rewrites" test3 + nextpart ns3/named.run > /dev/null nochange a3-1.tld2 # 1 nochange a3-1.tld2 +dnssec # 2 this once caused problems nxdomain a3-1.sub1.tld2 # 3 NXDOMAIN *.sub1.tld2 by NSDNAME @@ -632,25 +642,39 @@ EOF addr 127.0.0.1 a3-1.sub3.tld2 # 10 prefer policy for largest NSDNAME addr 127.0.0.2 a3-1.subsub.sub3.tld2 # 11 nxdomain xxx.crash1.tld2 # 12 dns_db_detachnode() crash + + nxdomain a3-1.stub # 13 + nxdomain a3-1.static-stub # 14 + nochange_ns10 a3-1.stub-nomatch # 15 + nochange_ns10 a3-1.static-stub-nomatch # 16 if [ "$mode" = dnsrps ]; then - addr 12.12.12.12 as-ns.tld5. # 13 qname-as-ns + addr 12.12.12.12 as-ns.tld5. # 17 qname-as-ns fi + nextpart ns3/named.run | grep -q "unrecognized NS rpz_rrset_find() failed: glue" && + setret "seen: unrecognized NS rpz_rrset_find() failed: glue" end_group if [ "$mode" = dnsrps ]; then - ckstats $ns3 test3 ns3 8 + ckstats $ns3 test3 ns3 10 else - ckstats $ns3 test3 ns3 7 + ckstats $ns3 test3 ns3 9 fi # these tests assume "min-ns-dots 0" start_group "NSIP rewrites" test4 + nextpart ns3/named.run > /dev/null nxdomain a3-1.tld2 # 1 NXDOMAIN for all of tld2 nochange a3-2.tld2. # 2 exempt rewrite by name nochange a0-1.tld2. # 3 exempt rewrite by address block nochange a3-1.tld4 # 4 different NS IP address + nxdomain a4-1.stub # 5 + nxdomain a4-1.static-stub # 6 + nochange_ns10 a4-1.stub-nomatch # 7 + nochange_ns10 a4-1.static-stub-nomatch # 8 if [ "$mode" = dnsrps ]; then - addr 12.12.12.12 as-ns.tld5. # 5 ip-as-ns + addr 12.12.12.12 as-ns.tld5. # 9 ip-as-ns fi + nextpart ns3/named.run | grep -q "unrecognized NS rpz_rrset_find() failed: glue" && + setret "seen: unrecognized NS rpz_rrset_find() failed: glue" end_group start_group "walled garden NSIP rewrites" test4a @@ -662,9 +686,9 @@ EOF EOF end_group if [ "$mode" = dnsrps ]; then - ckstats $ns3 test4 ns3 5 + ckstats $ns3 test4 ns3 7 else - ckstats $ns3 test4 ns3 4 + ckstats $ns3 test4 ns3 6 fi # policies in ./test5 overridden by response-policy{} in ns3/named.conf @@ -787,6 +811,7 @@ EOF fi # Ensure ns3 manages to transfer the fast-expire zone before shutdown. + nextpartreset ns3/named.run wait_for_log 20 "zone fast-expire/IN: transferred serial 1" ns3/named.run # reconfigure the ns5 primary server without the fast-expire zone, so diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c index 92eda66b9f..90832b34d1 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -279,6 +279,9 @@ dns_rpz_policy2str(dns_rpz_policy_t policy) { case DNS_RPZ_POLICY_DNS64: str = "DNS64"; break; + case DNS_RPZ_POLICY_ERROR: + str = "ERROR"; + break; default: UNREACHABLE(); } diff --git a/lib/ns/query.c b/lib/ns/query.c index 50babf26e4..8362f56a2b 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -2819,7 +2819,7 @@ query_rpzfetch(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t type) { */ static isc_result_t rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, - dns_rpz_type_t rpz_type, dns_db_t **dbp, + unsigned int options, dns_rpz_type_t rpz_type, dns_db_t **dbp, dns_dbversion_t *version, dns_rdataset_t **rdatasetp, bool resuming) { dns_rpz_st_t *st; @@ -2888,9 +2888,8 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, found = dns_fixedname_initname(&fixed); dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client, NULL, NULL); - result = dns_db_findext(*dbp, name, version, type, DNS_DBFIND_GLUEOK, - client->now, &node, found, &cm, &ci, *rdatasetp, - NULL); + result = dns_db_findext(*dbp, name, version, type, options, client->now, + &node, found, &cm, &ci, *rdatasetp, NULL); if (result == DNS_R_DELEGATION && is_zone && USECACHE(client)) { /* * Try the cache if we're authoritative for an @@ -3633,82 +3632,104 @@ rpz_rewrite_ip_rrset(ns_client_t *client, dns_name_t *name, struct in_addr ina; struct in6_addr in6a; isc_result_t result; + unsigned int options = DNS_DBFIND_GLUEOK; + bool done = false; CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip_rrset"); - zbits = rpz_get_zbits(client, ip_type, rpz_type); - if (zbits == 0) { - return (ISC_R_SUCCESS); - } + do { + zbits = rpz_get_zbits(client, ip_type, rpz_type); + if (zbits == 0) { + return (ISC_R_SUCCESS); + } - /* - * Get the A or AAAA rdataset. - */ - result = rpz_rrset_find(client, name, ip_type, rpz_type, ip_dbp, - ip_version, ip_rdatasetp, resuming); - switch (result) { - case ISC_R_SUCCESS: - case DNS_R_GLUE: - case DNS_R_ZONECUT: - break; - case DNS_R_EMPTYNAME: - case DNS_R_EMPTYWILD: - case DNS_R_NXDOMAIN: - case DNS_R_NCACHENXDOMAIN: - case DNS_R_NXRRSET: - case DNS_R_NCACHENXRRSET: - case ISC_R_NOTFOUND: - return (ISC_R_SUCCESS); - case DNS_R_DELEGATION: - case DNS_R_DUPLICATE: - case DNS_R_DROP: - return (result); - case DNS_R_CNAME: - case DNS_R_DNAME: - rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, name, rpz_type, - "NS address rewrite rrset", result); - return (ISC_R_SUCCESS); - default: - if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) { - client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR; - rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name, + /* + * Get the A or AAAA rdataset. + */ + result = rpz_rrset_find(client, name, ip_type, options, + rpz_type, ip_dbp, ip_version, + ip_rdatasetp, resuming); + switch (result) { + case ISC_R_SUCCESS: + case DNS_R_GLUE: + case DNS_R_ZONECUT: + break; + case DNS_R_EMPTYNAME: + case DNS_R_EMPTYWILD: + case DNS_R_NXDOMAIN: + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NXRRSET: + case DNS_R_NCACHENXRRSET: + case ISC_R_NOTFOUND: + return (ISC_R_SUCCESS); + case DNS_R_DELEGATION: + case DNS_R_DUPLICATE: + case DNS_R_DROP: + return (result); + case DNS_R_CNAME: + case DNS_R_DNAME: + rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, name, rpz_type, "NS address rewrite rrset", result); - } - CTRACE(ISC_LOG_ERROR, "rpz_rewrite_ip_rrset: unexpected " - "result"); - return (DNS_R_SERVFAIL); - } - - /* - * Check all of the IP addresses in the rdataset. - */ - for (result = dns_rdataset_first(*ip_rdatasetp); - result == ISC_R_SUCCESS; result = dns_rdataset_next(*ip_rdatasetp)) - { - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdataset_current(*ip_rdatasetp, &rdata); - switch (rdata.type) { - case dns_rdatatype_a: - INSIST(rdata.length == 4); - memmove(&ina.s_addr, rdata.data, 4); - isc_netaddr_fromin(&netaddr, &ina); - break; - case dns_rdatatype_aaaa: - INSIST(rdata.length == 16); - memmove(in6a.s6_addr, rdata.data, 16); - isc_netaddr_fromin6(&netaddr, &in6a); - break; + return (ISC_R_SUCCESS); default: - continue; + if (client->query.rpz_st->m.policy != + DNS_RPZ_POLICY_ERROR) { + client->query.rpz_st->m.policy = + DNS_RPZ_POLICY_ERROR; + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name, + rpz_type, + "NS address rewrite rrset", + result); + } + CTRACE(ISC_LOG_ERROR, + "rpz_rewrite_ip_rrset: unexpected " + "result"); + return (DNS_R_SERVFAIL); } - result = rpz_rewrite_ip(client, &netaddr, qtype, rpz_type, - zbits, p_rdatasetp); - if (result != ISC_R_SUCCESS) { - return (result); + /* + * If we are processing glue setup for the next loop + * otherwise we are done. + */ + if (result == DNS_R_GLUE) { + options = 0; + } else { + done = true; } - } + + /* + * Check all of the IP addresses in the rdataset. + */ + for (result = dns_rdataset_first(*ip_rdatasetp); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(*ip_rdatasetp)) + { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(*ip_rdatasetp, &rdata); + switch (rdata.type) { + case dns_rdatatype_a: + INSIST(rdata.length == 4); + memmove(&ina.s_addr, rdata.data, 4); + isc_netaddr_fromin(&netaddr, &ina); + break; + case dns_rdatatype_aaaa: + INSIST(rdata.length == 16); + memmove(in6a.s6_addr, rdata.data, 16); + isc_netaddr_fromin6(&netaddr, &in6a); + break; + default: + continue; + } + + result = rpz_rewrite_ip(client, &netaddr, qtype, + rpz_type, zbits, p_rdatasetp); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } while (!done && + client->query.rpz_st->m.policy == DNS_RPZ_POLICY_MISS); return (ISC_R_SUCCESS); } @@ -3982,6 +4003,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, dns_rpz_have_t have; dns_rpz_popt_t popt; int rpz_ver; + unsigned int options; #ifdef USE_DNSRPS librpz_emsg_t emsg; #endif /* ifdef USE_DNSRPS */ @@ -4232,7 +4254,9 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, dns_fixedname_init(&nsnamef); dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef)); + options = DNS_DBFIND_GLUEOK; while (st->r.label > st->popt.min_ns_labels) { + bool was_glue = false; /* * Get NS rrset for each domain in the current qname. */ @@ -4247,7 +4271,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, !dns_rdataset_isassociated(st->r.ns_rdataset)) { dns_db_t *db = NULL; result = rpz_rrset_find(client, nsname, - dns_rdatatype_ns, + dns_rdatatype_ns, options, DNS_RPZ_TYPE_NSDNAME, &db, NULL, &st->r.ns_rdataset, resuming); if (db != NULL) { @@ -4257,6 +4281,9 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, goto cleanup; } switch (result) { + case DNS_R_GLUE: + was_glue = true; + FALLTHROUGH; case ISC_R_SUCCESS: result = dns_rdataset_first(st->r.ns_rdataset); if (result != ISC_R_SUCCESS) { @@ -4296,6 +4323,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, continue; } } + /* * Check all NS names. */ @@ -4346,7 +4374,17 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, result = dns_rdataset_next(st->r.ns_rdataset); } while (result == ISC_R_SUCCESS); dns_rdataset_disassociate(st->r.ns_rdataset); - st->r.label--; + + /* + * If we just checked a glue NS RRset retry without allowing + * glue responses, otherwise setup for the next name. + */ + if (was_glue) { + options = 0; + } else { + options = DNS_DBFIND_GLUEOK; + st->r.label--; + } if (rpz_get_zbits(client, dns_rdatatype_any, DNS_RPZ_TYPE_NSDNAME) == 0 &&