From bfb027fecdcb0229eb515982f8293825f3ae2627 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Tue, 3 Mar 2026 14:00:38 -0800 Subject: [PATCH] Disable recursion for non-IN classes Force recursion off, and set allow-recursion/allow-recursion-on ACLs to none, for views with a class other than IN. Log a configuration warning if recursion is explicitly enabled for a non-IN view. This addresses YWH-PGM40640-74 and YWH-PGM40640-75 by preventing any attempt at recursive processing in a class-CHAOS view, ensuring that server addresses used for recursive queries and received in recursive responses are of the expected format. Fixes: isc-projects/bind9#5780 Fixes: isc-projects/bind9#5781 --- bin/named/server.c | 37 +++++-------------- bin/tests/system/checkconf/tests.sh | 1 + .../nohintswarn_bindchaos/ns1/named.conf.j2 | 23 ------------ .../tests_nohintswarn_bindchaos.py | 25 ------------- bin/tests/system/resolver/tests.sh | 8 ++-- lib/isccfg/check.c | 21 +++++++++-- 6 files changed, 33 insertions(+), 82 deletions(-) delete mode 100644 bin/tests/system/nohintswarn_bindchaos/ns1/named.conf.j2 delete mode 100644 bin/tests/system/nohintswarn_bindchaos/tests_nohintswarn_bindchaos.py diff --git a/bin/named/server.c b/bin/named/server.c index e9f2329b7c..1a98450e81 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -3926,7 +3926,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, obj = NULL; result = named_config_get(maps, "recursion", &obj); INSIST(result == ISC_R_SUCCESS); - view->recursion = cfg_obj_asboolean(obj); + view->recursion = (view->rdclass == dns_rdataclass_in && + cfg_obj_asboolean(obj)); max_cache_size = configure_max_cache_size(view, maps); @@ -4543,35 +4544,13 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, /* * We have default root hints for class IN if we need them. * Each view gets its own rootdb so a priming response only - * writes into that view's copy. + * writes into that view's copy. Other classes don't support + * recursion and don't need hints. */ if (view->rdclass == dns_rdataclass_in && view->rootdb == NULL) { CHECK(configure_rootdb(view, NULL)); } - /* - * If we still have no root hints, this is a non-IN view with no - * "hints zone" configured. Issue a warning, except if this - * is a root server. Root servers never need to consult - * their hints, so it's no point requiring users to configure - * them. - */ - if (view->rootdb == NULL) { - dns_zone_t *rootzone = NULL; - (void)dns_view_findzone(view, dns_rootname, DNS_ZTFIND_EXACT, - &rootzone); - if (rootzone != NULL) { - dns_zone_detach(&rootzone); - } else if (strcmp(view->name, "_bind") != 0 || - view->rdclass != dns_rdataclass_chaos) - { - isc_log_write(NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, - "no root hints for view '%s'", - view->name); - } - } - /* * Configure the view's transports (DoT/DoH) */ @@ -4794,9 +4773,11 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, CHECK(configure_view_acl(vconfig, config, "allow-proxy-on", NULL, aclctx, isc_g_mctx, &view->proxyonacl)); - if (strcmp(view->name, "_bind") != 0 && - view->rdclass != dns_rdataclass_chaos) - { + if (view->rdclass != dns_rdataclass_in) { + view->recursion = false; + dns_acl_none(isc_g_mctx, &view->recursionacl); + dns_acl_none(isc_g_mctx, &view->recursiononacl); + } else { CHECK(configure_view_acl(vconfig, config, "allow-recursion", NULL, aclctx, isc_g_mctx, &view->recursionacl)); diff --git a/bin/tests/system/checkconf/tests.sh b/bin/tests/system/checkconf/tests.sh index 69d96b7ea8..f927d78919 100644 --- a/bin/tests/system/checkconf/tests.sh +++ b/bin/tests/system/checkconf/tests.sh @@ -515,6 +515,7 @@ $CHECKCONF -l good.conf \ | grep -v "is not implemented" \ | grep -v "is not recommended" \ | grep -v "no longer exists" \ + | grep -v "recursion will be disabled" \ | grep -v "is obsolete" >checkconf.out$n || ret=1 diff good.zonelist checkconf.out$n >diff.out$n || ret=1 if [ $ret -ne 0 ]; then diff --git a/bin/tests/system/nohintswarn_bindchaos/ns1/named.conf.j2 b/bin/tests/system/nohintswarn_bindchaos/ns1/named.conf.j2 deleted file mode 100644 index 57389c0521..0000000000 --- a/bin/tests/system/nohintswarn_bindchaos/ns1/named.conf.j2 +++ /dev/null @@ -1,23 +0,0 @@ -options { - port @PORT@; - pid-file "named.pid"; - listen-on { 10.53.0.1; }; -}; - -key rndc_key { - secret "1234abcd8765"; - algorithm @DEFAULT_HMAC@; -}; - -controls { - inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; -}; - -view _bind { -}; - -view foo { -}; - -view bar ch { -}; diff --git a/bin/tests/system/nohintswarn_bindchaos/tests_nohintswarn_bindchaos.py b/bin/tests/system/nohintswarn_bindchaos/tests_nohintswarn_bindchaos.py deleted file mode 100644 index bb788d1708..0000000000 --- a/bin/tests/system/nohintswarn_bindchaos/tests_nohintswarn_bindchaos.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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. - -import isctest - - -def test_nohintswarn_bindchaos(ns1): - found = True - try: - with ns1.watch_log_from_start(timeout=1) as watcher: - watcher.wait_for_line("no root hints for view '_bind'") - except isctest.log.watchlog.WatchLogTimeout: - found = False - assert found is False - - with ns1.watch_log_from_start() as watcher: - watcher.wait_for_line("no root hints for view 'bar'") diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh index 18893edd69..06ef98f697 100755 --- a/bin/tests/system/resolver/tests.sh +++ b/bin/tests/system/resolver/tests.sh @@ -783,10 +783,12 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) n=$((n + 1)) -echo_i "checking NXDOMAIN is returned when querying non existing domain in CH class ($n)" +echo_i "checking REFUSED is returned when querying non existing domain in CH class ($n)" ret=0 -dig_with_opts @10.53.0.1 id.hostname txt ch >dig.ns1.out.${n} || ret=1 -grep "status: NXDOMAIN" dig.ns1.out.${n} >/dev/null || ret=1 +dig_with_opts @10.53.0.1 hostname.chaostest txt ch >dig.ns1.out.1.${n} || ret=1 +grep "status: NOERROR" dig.ns1.out.1.${n} >/dev/null || ret=1 +dig_with_opts @10.53.0.1 id.hostname txt ch >dig.ns1.out.2.${n} || ret=1 +grep "status: REFUSED" dig.ns1.out.2.${n} >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 232d4d1890..2995319627 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -2869,13 +2869,17 @@ check_mirror_zone_notify(const cfg_obj_t *zoptions, const char *znamestr) { */ static bool check_recursion(const cfg_obj_t *config, const cfg_obj_t *voptions, - const cfg_obj_t *goptions, cfg_aclconfctx_t *aclctx, - isc_mem_t *mctx) { + dns_rdataclass_t vclass, const cfg_obj_t *goptions, + cfg_aclconfctx_t *aclctx, isc_mem_t *mctx) { dns_acl_t *acl = NULL; const cfg_obj_t *obj; isc_result_t result = ISC_R_SUCCESS; bool retval = true; + if (vclass != dns_rdataclass_in) { + return false; + } + /* * Check the "recursion" option first. */ @@ -3827,7 +3831,7 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, * contradicts the purpose of the former. */ if (ztype == CFG_ZONE_MIRROR && - !check_recursion(config, voptions, goptions, aclctx, mctx)) + !check_recursion(config, voptions, zclass, goptions, aclctx, mctx)) { cfg_obj_log(zoptions, ISC_LOG_ERROR, "zone '%s': mirror zones cannot be used if " @@ -5646,6 +5650,17 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, cfg_aclconfctx_create(mctx, &aclctx); + if (vclass != dns_rdataclass_in) { + if (check_recursion(config, voptions, dns_rdataclass_in, + options, aclctx, mctx)) + { + cfg_obj_log(opts, ISC_LOG_WARNING, + "recursion will be disabled for " + "non-IN view '%s'", + viewname); + } + } + if (voptions != NULL) { (void)cfg_map_get(voptions, "zone", &zones); } else {