]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add dnssec_py/tests_sibling_ds: reject DS for sibling zones in referrals 11837/head
authorNicki Křížek <nicki@isc.org>
Mon, 13 Apr 2026 12:55:54 +0000 (12:55 +0000)
committerMark Andrews <marka@isc.org>
Thu, 25 Jun 2026 05:21:00 +0000 (15:21 +1000)
Add a system test that verifies the resolver rejects DS records whose
owner name does not match the delegation (NS) name in a referral
response.

A custom authoritative server (ans4) serves the parent zone sibling-ds.
from zone file with delegations for child and sibling subzones.  Its
DomainHandler injects a DS record for sibling.sibling-ds into referrals
for child.sibling-ds.  The resolver must detect the mismatch, log "DS
doesn't match referral (NS)", and return SERVFAIL.

Assisted-by: Claude:claude-opus-4-8
bin/tests/system/dnssec_py/ans4/ans.py [new file with mode: 0644]
bin/tests/system/dnssec_py/common.py
bin/tests/system/dnssec_py/tests_sibling_ds.py [new file with mode: 0644]

diff --git a/bin/tests/system/dnssec_py/ans4/ans.py b/bin/tests/system/dnssec_py/ans4/ans.py
new file mode 100644 (file)
index 0000000..47c4c0f
--- /dev/null
@@ -0,0 +1,65 @@
+# 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.
+
+"""
+Custom authoritative server for the sibling-ds test.
+
+When returning a referral for child.sibling-ds, this server injects a DS
+record for sibling.sibling-ds into the authority section.  The resolver
+should reject this because the DS owner name does not match the
+delegation (NS) name.
+"""
+
+from collections.abc import AsyncGenerator
+
+import dns.rdatatype
+import dns.rrset
+
+from isctest.asyncserver import (
+    AsyncDnsServer,
+    DnsResponseSend,
+    DomainHandler,
+    QueryContext,
+    ResponseAction,
+)
+
+
+class SiblingDsInjectionHandler(DomainHandler):
+    """Inject a DS record for sibling.sibling-ds into child.sibling-ds referrals."""
+
+    domains = ["child.sibling-ds."]
+
+    async def get_responses(
+        self, qctx: QueryContext
+    ) -> AsyncGenerator[ResponseAction, None]:
+        # The default zone-data response already has the NS delegation for
+        # child.sibling-ds. and glue.  Add a DS record for the *sibling* zone
+        # (wrong name for this referral).
+        sibling_ds = dns.rrset.from_text(
+            "sibling.sibling-ds.",
+            300,
+            qctx.qclass,
+            dns.rdatatype.DS,
+            "12345 8 2 "
+            "49FD46E6C4B45C55D4AC69CBD3CD34AC1AFE51DE7B2B585ABCDEABCDEABCDEAB",
+        )
+        qctx.response.authority.append(sibling_ds)
+        yield DnsResponseSend(qctx.response)
+
+
+def main() -> None:
+    server = AsyncDnsServer()
+    server.install_response_handler(SiblingDsInjectionHandler())
+    server.run()
+
+
+if __name__ == "__main__":
+    main()
index 8a9fbca4d24894b8e6d727f900817c33cf4ef63b..2d09eda4da611efadb2ffe68b9c170e87e26f0fa 100644 (file)
@@ -13,6 +13,8 @@ import pytest
 
 DNSSEC_PY_MARK = pytest.mark.extra_artifacts(
     [
+        "ans*/*.db",
+        "ans*/*.run",
         "ns*/dsset-*",
         "ns*/trusted.conf",
         "ns*/zones/*.db",
diff --git a/bin/tests/system/dnssec_py/tests_sibling_ds.py b/bin/tests/system/dnssec_py/tests_sibling_ds.py
new file mode 100644 (file)
index 0000000..7171194
--- /dev/null
@@ -0,0 +1,70 @@
+# 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.
+
+"""
+Test that the resolver rejects DS records for sibling zones in referrals.
+
+A custom authoritative server (ans4) returns a referral for
+child.sibling-ds that includes a DS record for sibling.sibling-ds.  The
+resolver must detect that the DS owner does not match the delegation NS
+name and treat the response as a form error.
+"""
+
+from re import compile as Re
+
+from dnssec_py.common import DNSSEC_PY_MARK
+from isctest.template import NS2, Nameserver, zones
+from isctest.zone import Zone, configure_root
+
+import isctest
+
+pytestmark = DNSSEC_PY_MARK
+
+ANS4 = Nameserver("ans4")
+
+
+def bootstrap():
+    # Child zone on ns2 — the test queries a.child.sibling-ds which
+    # resolves to the default template A record (10.0.0.1).
+    child = Zone("child.sibling-ds", NS2)
+    child.configure()
+
+    # Sibling zone on ns2 — exists so the sibling DS in the referral
+    # refers to a real delegation.
+    sibling = Zone("sibling.sibling-ds", NS2)
+    sibling.configure()
+
+    # Parent zone rendered into ans4/ (subdir=None puts the .db file
+    # directly in the ans4 directory where AsyncDnsServer loads it).
+    parent = Zone("sibling-ds", ANS4, subdir=None)
+    parent.delegations = [child, sibling]
+    parent.configure()
+
+    # Root zone delegates sibling-ds. to ans4.
+    root = configure_root([parent])
+
+    return {
+        "trust_anchors": root.trust_anchors(),
+        "zones": zones([root, child, sibling]),
+    }
+
+
+def test_sibling_ds_rejected(ns9):
+    """Resolver must reject a referral that contains DS for a sibling zone."""
+    log_ds_mismatch = Re(r"DS doesn't match the delegation owner name")
+
+    msg = isctest.query.create("a.child.sibling-ds.", "A")
+
+    with ns9.watch_log_from_here() as watcher:
+        res = isctest.query.tcp(msg, ns9.ip)
+        watcher.wait_for_line(log_ds_mismatch)
+
+    isctest.check.servfail(res)