]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
POC for PRIVATEDNS DNSKEY overrun not being detected
authorMark Andrews <marka@isc.org>
Thu, 4 Jun 2026 23:18:30 +0000 (09:18 +1000)
committerMichał Kępień <michal@isc.org>
Fri, 10 Jul 2026 07:26:46 +0000 (09:26 +0200)
Construct a DNS message where a PRIVATEDNS DNSKEY identifier
overruns the record boundary by 3 byte so that the label ends
at the end of the compression pointer for the next record. The
next type is less than 256 so the next octet is 00 terminating
the identifier name.  The transfered zone is then written to
disk using master-format text triggering the assertion when the
truncated identier is discovered.

Note this test will produce a false result in versions of
BIND that do not check the PRIVATEDNS identifier as it looks
for the error message when the transfer is aborted.

bin/tests/system/xfer/ans9/ans.py
bin/tests/system/xfer/ns6/named.conf.j2
bin/tests/system/xfer/tests_xfer.py

index 25da6b7d43dbb32e444fc9a89bb1c61ce8568811..a9e73953eef3b53dab669100192227161ad2686d 100644 (file)
@@ -13,6 +13,7 @@ information regarding copyright ownership.
 
 from collections.abc import AsyncGenerator
 
+import dns.name
 import dns.rcode
 import dns.rdatatype
 import dns.rrset
@@ -33,7 +34,7 @@ class AXFRServer(DomainHandler):
     version.
     """
 
-    domains = ["xfr-and-reconfig"]
+    domains = ["xfr-and-reconfig", "private-dns-overrun"]
 
     def __init__(self) -> None:
         super().__init__()
@@ -97,6 +98,34 @@ class AXFRServer(DomainHandler):
 
         yield DnsResponseSend(txt_message)
 
+        if qctx.qname == dns.name.from_text("private-dns-overrun"):
+            # A message where the malformed DNSKEY algorithm identifier
+            # finishes on a 00 byte in the next record. Assumes the
+            # next record starts with a compression pointer which is
+            # followed by the type which starts with 00.
+
+            # Generate malformed PRIVATE DNS DNSKEY
+            dnskey_message = qctx.prepare_new_response()
+            dnskey_rrset = dns.rrset.from_text(
+                qctx.qname,
+                300,
+                qctx.qclass,
+                dns.rdatatype.DNSKEY,
+                "\\# 12 00 00 00 fd 09 00 00 00 00 00 00 00",
+            )
+            dnskey_message.answer.append(dnskey_rrset)
+            # Generate well formed PRIVATE DNS DNSKEY
+            dnskey_rrset = dns.rrset.from_text(
+                qctx.qname,
+                300,
+                qctx.qclass,
+                dns.rdatatype.DNSKEY,
+                "\\# 12 00 00 00 fd 06 00 00 00 00 00 00 00",
+            )
+            dnskey_message.answer.append(dnskey_rrset)
+
+            yield DnsResponseSend(dnskey_message)
+
         # Finish the AXFR transaction by sending the second SOA RRset.
         yield DnsResponseSend(soa_message)
 
index 28936ea31b3da5292212942a67e9026e60bca8de..ba27db1539a05bf74ff663d3f4189ff808ed1f26 100644 (file)
@@ -112,3 +112,12 @@ zone "ixfr-race" {
        primaries { 10.53.0.11; };
        file "ixfr-race.bk";
 };
+
+# GL#6004
+zone "private-dns-overrun" {
+       type secondary;
+       primaries { 10.53.0.9; };
+       file "private-dns-overrun.bk";
+       masterfile-format text; # force bug to be exercised
+       request-ixfr no; # ans9 supports only axfr
+};
index dd95cff74a620a5cb4c917e6339f5731f17d6e2a..a75bcf8e53ba03243f5a0b2c1c0595d5383bc41b 100644 (file)
@@ -622,6 +622,16 @@ def test_reconfiguration_when_zone_transfer_is_in_the_middle_of_soa_query(ns6):
         watcher_transfer_started.wait_for_line("Transfer started")
 
 
+def test_malformed_private_dns_identifier_overrun(ns6):
+    isctest.log.info(
+        "Check that a malformed PRIVATEDNS DNSKEY which overruns the record is rejected"
+    )
+    with ns6.watch_log_from_start(timeout=60) as watcher_transfer_completed:
+        watcher_transfer_completed.wait_for_line(
+            "zone private-dns-overrun/IN: zone transfer finished: unexpected end of input"
+        )
+
+
 # See #5767
 def test_ixfr_race(ns6):
     isctest.log.info(