From: Mark Andrews Date: Thu, 4 Jun 2026 23:18:30 +0000 (+1000) Subject: POC for PRIVATEDNS DNSKEY overrun not being detected X-Git-Tag: v9.21.24~13^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9ce3bce8bc8b4e9c6a9b1e84b5849c33eb27830e;p=thirdparty%2Fbind9.git POC for PRIVATEDNS DNSKEY overrun not being detected 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. --- diff --git a/bin/tests/system/xfer/ans9/ans.py b/bin/tests/system/xfer/ans9/ans.py index 25da6b7d43d..a9e73953eef 100644 --- a/bin/tests/system/xfer/ans9/ans.py +++ b/bin/tests/system/xfer/ans9/ans.py @@ -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) diff --git a/bin/tests/system/xfer/ns6/named.conf.j2 b/bin/tests/system/xfer/ns6/named.conf.j2 index 28936ea31b3..ba27db1539a 100644 --- a/bin/tests/system/xfer/ns6/named.conf.j2 +++ b/bin/tests/system/xfer/ns6/named.conf.j2 @@ -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 +}; diff --git a/bin/tests/system/xfer/tests_xfer.py b/bin/tests/system/xfer/tests_xfer.py index dd95cff74a6..a75bcf8e53b 100644 --- a/bin/tests/system/xfer/tests_xfer.py +++ b/bin/tests/system/xfer/tests_xfer.py @@ -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(