]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Test a policy zone holding a record from outside the zone
authorOndřej Surý <ondrej@sury.org>
Thu, 6 Aug 2026 18:35:29 +0000 (20:35 +0200)
committerOndřej Surý <ondrej@sury.org>
Mon, 10 Aug 2026 14:06:38 +0000 (16:06 +0200)
Getting an out-of-zone record into a policy zone does not need a crafted
transfer: lib/dns/master.c only drops such records for primaries, so a
secondary keeps whatever its own copy of the zone contains.  ns3 loads
one that already holds "com.", which is the state a secondary reaches
after a transfer that carried it, and is why the failure used to survive
a restart with the primary gone.

The checks read ns3's log rather than querying, so they do not depend on
when the zone expires; a secondary's expiry runs from the mtime of the
file it loaded, which for a file kept in the tree is whenever the tree
was last touched.

tests.sh fails on any "invalid rpz" complaint, which is how it catches
unexpected ones, so the message this zone exists to provoke is excluded
by name.

Assisted-by: Claude:claude-opus-5
bin/tests/system/rpz/ns3/named.conf.j2
bin/tests/system/rpz/ns3/outofzone.db.in [new file with mode: 0644]
bin/tests/system/rpz/setup.sh
bin/tests/system/rpz/tests.sh
bin/tests/system/rpz/tests_rpz_outofzone.py [new file with mode: 0644]
bin/tests/system/rpz/tests_sh_rpz.py

index a69dab160d69dd6d178ba5977b7617f83de34f32..2e5a916fe9c234b72f93361229d73b2e654b15b6 100644 (file)
@@ -37,6 +37,7 @@ options {
                zone "evil-cname"  policy cname a12.tld2. ede blocked;
                zone "wild-cname"  ede blocked;
                zone "slow-rpz";
+               zone "outofzone.tld2";
        }
        add-soa yes
        min-ns-dots 0
@@ -122,6 +123,18 @@ zone "fast-expire." {
        notify no;
 };
 
+/*
+ * A policy zone holding a record from outside the zone.  ns2 does not
+ * serve it, so "outofzone.db" is never replaced by a transfer.
+ */
+zone "outofzone.tld2." {
+       type secondary;
+       file "outofzone.db";
+       masterfile-format text;
+       primaries { 10.53.0.2; };
+       notify no;
+};
+
 zone "stub." {
        type stub;
        primaries { 10.53.0.2; };
diff --git a/bin/tests/system/rpz/ns3/outofzone.db.in b/bin/tests/system/rpz/ns3/outofzone.db.in
new file mode 100644 (file)
index 0000000..1bb3664
--- /dev/null
@@ -0,0 +1,29 @@
+; 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.
+
+; A policy zone as a secondary would have written it out after a transfer
+; that carried a record from outside the zone.  The zone is a secondary so
+; that this file is loaded the way a secondary loads its own copy of a
+; transferred zone; the out-of-zone check in lib/dns/master.c only applies
+; to primaries.
+
+$TTL 300
+@                      SOA     ns.tld3. hostmaster.ns.tld3. ( 1 3600 1200 2419200 60 )
+                       NS      ns.tld3.
+
+; Two labels, fewer than the three of the origin, so stripping the origin
+; from it used to underflow an unsigned label count in name2data().
+com.                   CNAME   .
+
+; An ordinary trigger, so the test can tell that the rest of the zone is
+; still loaded after the record above has been rejected.  It is never
+; queried, so it cannot disturb the other checks in this test.
+never-queried.example  CNAME   .
index eee949dd0763cb28d4d35c59c0b3bc6f3e3038d8..80d8ea4b7cae5d17c52484e14e71b22e0f3acfc1 100644 (file)
@@ -48,6 +48,9 @@ cp ns3/slow-rpz.db.in ns3/slow-rpz.db
 cp ns5/fast-expire.db.in ns5/fast-expire.db
 cp ns5/expire.conf.in ns5/expire.conf
 
+# a policy zone holding a record from outside the zone
+cp ns3/outofzone.db.in ns3/outofzone.db
+
 # $1=directory
 # $2=domain name
 # $3=input zone file
index 82a7135ca109375eb93b1aa5653d4a65db679035..ad2950010702410e252d6f4f0e24bf756016f28d 100644 (file)
@@ -659,12 +659,15 @@ if test -z "$HAVE_CORE"; then
   test -z "$HAVE_CORE" || setret "found $HAVE_CORE; memory leak?"
 fi
 
-# look for complaints from lib/dns/rpz.c and bin/name/query.c
+# look for complaints from lib/dns/rpz.c and bin/name/query.c, except the
+# one the outofzone.tld2 policy zone is there to provoke
+EXPECTED='invalid rpz owner name "com"'
 for runfile in ns*/named.run; do
-  EMSGS=$(nextpart $runfile | grep -E -l 'invalid rpz|rpz.*failed' || true)
+  EMSGS=$(nextpart $runfile | grep -Fv "$EXPECTED" \
+    | grep -E -l 'invalid rpz|rpz.*failed' || true)
   if test -n "$EMSGS"; then
     setret "error messages in $runfile starting with:"
-    grep -E 'invalid rpz|rpz.*failed' ns*/named.run \
+    grep -E 'invalid rpz|rpz.*failed' ns*/named.run | grep -Fv "$EXPECTED" \
       | sed -e '10,$d' -e 's/^//' | cat_i
   fi
 done
diff --git a/bin/tests/system/rpz/tests_rpz_outofzone.py b/bin/tests/system/rpz/tests_rpz_outofzone.py
new file mode 100644 (file)
index 0000000..1064acd
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python3
+
+# 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.
+
+from isctest.instance import NamedInstance
+
+
+def test_rpz_out_of_zone_owner_name(ns3: NamedInstance) -> None:
+    """
+    ns3 loads a policy zone holding "com.", whose two labels are fewer than
+    the three of the "outofzone.tld2." origin that gets stripped from an
+    owner name to build the trigger name.  That used to underflow an
+    unsigned label count and fail an assertion, taking named down as the
+    policy zone was loaded - so reaching this test at all is most of the
+    check.
+    """
+    assert 'invalid rpz owner name "com"; not within the policy zone' in ns3.log
+
+    # Only the record above was dropped; the rest of the zone still loads.
+    assert (
+        "rpz: outofzone.tld2: adding node never-queried.example.outofzone.tld2"
+        in ns3.log
+    )
index 348546f4c5098aa0ce635b5bd5f95e210405fbb0..c1ee8caf3c383a73bfae5373e36a6bdda24ba3a5 100644 (file)
@@ -45,6 +45,7 @@ pytestmark = pytest.mark.extra_artifacts(
         "ns3/mixed-case-rpz.db",
         "ns3/named.conf.tmp",
         "ns3/named.stats",
+        "ns3/outofzone.db",
         "ns3/slow-rpz.db",
         "ns3/wild-cname.db",
         "ns5/bl.db",