Add test for SIG in prequisites of dynamic update

Make sure the nameserver correctly handles SIG records in the
prerequisites of the dynamic update. The first check is to ensure that
the prerequisites are not examined prior to checking the credentials.

The second test case checks that the SIG present prerequisite is
examined and therefore refuses the update. Also this should not trigger
an assertion failure in dns__db_findrdataset() (due to the REQUIRE()
only accepted dns_rdatatype_rrsig when the covers parameter was set).

(cherry picked from commit 51f27fda46)
This commit is contained in:
Matthijs Mekking 2026-04-16 16:17:34 +02:00 committed by Ondřej Surý
parent 8f75c7c3dd
commit 9c0e5da3aa
2 changed files with 68 additions and 35 deletions

View file

@ -58,6 +58,7 @@ pytestmark = pytest.mark.extra_artifacts(
"ns5/local.db",
"ns6/2.0.0.2.ip6.addr.db",
"ns6/in-addr.db",
"ns6/sigaxfr.bk",
"ns7/_default.tsigkeys",
"ns7/example.com.db",
"ns7/in-addr.db",

View file

@ -39,9 +39,29 @@ import dns.rdataclass
import dns.rdataset
import dns.rdatatype
import dns.update
import pytest
import isctest
pytestmark = pytest.mark.extra_artifacts(
[
"ans*/ans.run",
"ns*/*.bk",
"ns*/*.conf",
"ns*/*.db",
"ns*/*.db.jnl",
"ns*/*.db.signed",
"ns*/*.jnl",
"ns*/*.key",
"ns*/K*.key",
"ns*/K*.private",
"ns*/K*.state",
"ns*/dsset-*.",
"ns6/sigaxfr.bk",
"verylarge",
]
)
def _make_sig_rdata(text):
"""Create a SIG rdata from text.
@ -79,9 +99,9 @@ def test_tcp_self_sig_record(ns6):
crash-reproducing precondition.
"""
ptr_update = dns.update.UpdateMessage("in-addr.arpa.")
ptr_update.add("1.0.0.127.in-addr.arpa.", 600, "PTR", "localhost.")
ptr_update.add("1.0.53.10.in-addr.arpa.", 600, "PTR", "localhost.")
response = isctest.query.tcp(
ptr_update, ns6.ip, port=ns6.ports.dns, source="127.0.0.1"
ptr_update, ns6.ip, port=ns6.ports.dns, source="10.53.0.1"
)
assert response.rcode() == dns.rcode.NOERROR
@ -92,18 +112,58 @@ def test_tcp_self_sig_record(ns6):
sig_update = dns.update.UpdateMessage("in-addr.arpa.")
sig_update.add("1.0.0.127.in-addr.arpa.", rds)
response = isctest.query.tcp(
sig_update, ns6.ip, port=ns6.ports.dns, source="127.0.0.1"
)
assert response.rcode() == dns.rcode.REFUSED
with ns6.watch_log_from_here() as watcher:
response = isctest.query.tcp(
sig_update, ns6.ip, port=ns6.ports.dns, source="10.53.0.1"
)
assert response.rcode() == dns.rcode.REFUSED
watcher.wait_for_line(
"updating zone 'in-addr.arpa/IN': update failed: SIG updates are not allowed (REFUSED)"
)
# Confirm nothing of type SIG was stored.
msg = isctest.query.create("1.0.0.127.in-addr.arpa.", "SIG")
msg = isctest.query.create("1.0.53.10.in-addr.arpa.", "SIG")
res = isctest.query.tcp(msg, ns6.ip, port=ns6.ports.dns)
stored = any(rrset.rdtype == dns.rdatatype.SIG for rrset in res.answer)
assert not stored, "SIG record was stored despite REFUSED response"
def test_tcp_self_nxt_record(ns6):
"""NXT (type 30) updates must be refused at the front door.
NXT is the legacy DNSSEC denial-of-existence type, obsolete since
RFC 3755 replaced it with NSEC. Accepting it via dynamic update
would let an authorised updater inject records that the signing
and cut-point logic has no provision for.
"""
# A second owner under a source that also matches tcp-self.
source = "10.53.0.2"
owner = "2.0.53.10.in-addr.arpa."
ptr_update = dns.update.UpdateMessage("in-addr.arpa.")
ptr_update.add(owner, 600, "PTR", "localhost.")
response = isctest.query.tcp(ptr_update, ns6.ip, port=ns6.ports.dns, source=source)
assert response.rcode() == dns.rcode.NOERROR
nxt = _make_nxt_rdata()
rds = dns.rdataset.Rdataset(dns.rdataclass.IN, dns.rdatatype.NXT)
rds.update_ttl(600)
rds.add(nxt)
nxt_update = dns.update.UpdateMessage("in-addr.arpa.")
nxt_update.add(owner, rds)
with ns6.watch_log_from_here() as watcher:
response = isctest.query.tcp(
nxt_update, ns6.ip, port=ns6.ports.dns, source="10.53.0.1"
)
assert response.rcode() == dns.rcode.REFUSED
watcher.wait_for_line(
"updating zone 'in-addr.arpa/IN': update failed: NXT updates are not allowed (REFUSED)"
)
def test_sig_covers_preserved_via_axfr(ns6):
"""Regression test for GL#5818 Finding 1, reached via AXFR.
@ -170,31 +230,3 @@ def test_sig_covers_preserved_via_axfr(ns6):
"the Finding 1 bug both records are filed under typepair (SIG, 0) "
"and share the first-seen TTL (600)."
)
def test_tcp_self_nxt_record(ns6):
"""NXT (type 30) updates must be refused at the front door.
NXT is the legacy DNSSEC denial-of-existence type, obsolete since
RFC 3755 replaced it with NSEC. Accepting it via dynamic update
would let an authorised updater inject records that the signing
and cut-point logic has no provision for.
"""
# A second owner under a source that also matches tcp-self.
source = "127.0.0.2"
owner = "2.0.0.127.in-addr.arpa."
ptr_update = dns.update.UpdateMessage("in-addr.arpa.")
ptr_update.add(owner, 600, "PTR", "localhost.")
response = isctest.query.tcp(ptr_update, ns6.ip, port=ns6.ports.dns, source=source)
assert response.rcode() == dns.rcode.NOERROR
nxt = _make_nxt_rdata()
rds = dns.rdataset.Rdataset(dns.rdataclass.IN, dns.rdatatype.NXT)
rds.update_ttl(600)
rds.add(nxt)
nxt_update = dns.update.UpdateMessage("in-addr.arpa.")
nxt_update.add(owner, rds)
response = isctest.query.tcp(nxt_update, ns6.ip, port=ns6.ports.dns, source=source)
assert response.rcode() == dns.rcode.REFUSED