]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Drop RFC 2535 special-casing of the KEY record type 12056/head
authorOndřej Surý <ondrej@isc.org>
Tue, 19 May 2026 13:58:54 +0000 (15:58 +0200)
committerOndřej Surý <ondrej@isc.org>
Thu, 28 May 2026 11:21:00 +0000 (13:21 +0200)
After SIG and NXT lost their special handling, KEY remained the only
RFC 2535-era type still receiving coexistence allowances: KEY
alongside CNAME at the same owner, KEY answered from the parent side
of a zone cut, KEY kept across CNAME eviction in the cache.  RFC 3755
retains type 25 only for SIG(0) and TKEY transaction signatures, and
neither relies on those allowances in practice.  The in-tree comment
that flagged the RFC 3007 parent-side carve-out as "unclear" predicted
this cleanup.

Zones that publish CNAME and KEY at the same owner — already invalid
under RFC 2181 — now fail to load.  System test fixtures are updated
accordingly, and a new test asserts that SIG, NXT, and KEY records
pick up covering RRSIGs when their zone is signed.

bin/tests/system/checkzone/zones/bad-cname-and-key.db [moved from bin/tests/system/checkzone/zones/good-cname-and-key.db with 100% similarity]
bin/tests/system/dnssec/ns3/secure.example.db.in
bin/tests/system/dnssec/ns3/sign.sh
bin/tests/system/dnssec/tests_validation.py
lib/dns/nsec.c
lib/dns/nsec3.c
lib/dns/qpcache.c
lib/dns/qpzone.c
lib/dns/rdata/generic/key_25.c
lib/dns/resolver.c
tests/dns/rdata_test.c

index 76d25e981b38dc92075ca3970973af4fa4a2896e..7c6a24d48663b7980dab80e34f7f595c4a53caa6 100644 (file)
@@ -39,9 +39,13 @@ normalthenrrsig              A       10.0.0.28
 rrsigonly              A       10.0.0.29
 
 cnameandkey            CNAME   @
-cnamenokey             CNAME   @
 dnameandkey            DNAME   @
 
+; Legacy DNSSEC types (RFC 3755) carried as opaque zone data and signed.
+sigrr                  SIG     A 6 2 86400 20260331170000 20260318160000 21831 . AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+nxtrr                  NXT     next.secure.example. A
+keyrr                  KEY     0 3 13 zJxo/L9JqctLZuL8CqocSmgHUhmQCrQQmRHjwhzXfjDgPoPjRo1nofU7yXHeli8myiulQLZk3h1CTayP8dOvkQ==
+
 mixedcase              A       10.0.0.30
 mixedCASE              TXT     "mixed case"
 MIXEDcase              AAAA    2002::
index ea81381eb233844d51fe5fe7478117cfa05eed3a..495d68e4564adf175b42c2b3f2054a594ffba188 100644 (file)
@@ -115,11 +115,10 @@ zone=secure.example.
 infile=secure.example.db.in
 zonefile=secure.example.db
 
-cnameandkey=$("$KEYGEN" -T KEY -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "cnameandkey.$zone")
 dnameandkey=$("$KEYGEN" -T KEY -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "dnameandkey.$zone")
 keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
 
-cat "$infile" dsset-badalg.secure.example. "$cnameandkey.key" "$dnameandkey.key" "$keyname.key" >"$zonefile"
+cat "$infile" dsset-badalg.secure.example. "$dnameandkey.key" "$keyname.key" >"$zonefile"
 
 "$SIGNER" -z -D -o "$zone" "$zonefile" >/dev/null
 cat "$zonefile" "$zonefile".signed >"$zonefile".tmp
index ed920749cf9547838a0f283641fdbf86e3ad04fb..781324fed268661467d31e196419d280e35187a8 100644 (file)
@@ -290,22 +290,6 @@ def test_chain_validation():
     isctest.check.adflag(res2)
     assert answer_has(res2, rdatatype.CNAME)
 
-    # check KEY lookup via CNAME
-    msg = isctest.query.create("cnameandkey.secure.example", "KEY")
-    res1 = isctest.query.tcp(msg, "10.53.0.3")
-    res2 = isctest.query.tcp(msg, "10.53.0.4")
-    isctest.check.same_answer(res1, res2)
-    isctest.check.adflag(res2)
-    assert not answer_has(res2, rdatatype.CNAME)
-
-    # check KEY lookup via CNAME (not present)
-    msg = isctest.query.create("cnamenokey.secure.example", "KEY")
-    res1 = isctest.query.tcp(msg, "10.53.0.3")
-    res2 = isctest.query.tcp(msg, "10.53.0.4")
-    isctest.check.same_answer(res1, res2)
-    isctest.check.adflag(res2)
-    assert not answer_has(res2, rdatatype.CNAME)
-
     # check DNSKEY lookup via DNAME
     msg = isctest.query.create("a.dnameandkey.secure.example", "DNSKEY")
     res1 = isctest.query.tcp(msg, "10.53.0.3")
@@ -1351,6 +1335,30 @@ def test_unknown_algorithms():
     isctest.check.noadflag(res2)
 
 
+def test_legacy_dnssec_types_are_signed():
+    """SIG (24), NXT (30) and KEY (25) records carry a covering RRSIG.
+
+    Per RFC 3755 SIG and NXT are obsolete and treated as opaque zone
+    data; KEY remains valid for SIG(0)/TKEY use.  All three are
+    ordinary zone data and must be signed like any other RRset.
+    """
+    for owner, rrtype in [
+        ("sigrr.secure.example", "SIG"),
+        ("nxtrr.secure.example", "NXT"),
+        ("keyrr.secure.example", "KEY"),
+    ]:
+        expected = rdatatype.from_text(rrtype)
+        msg = isctest.query.create(owner, rrtype, cd=True)
+        res = isctest.query.tcp(msg, "10.53.0.3")
+        isctest.check.noerror(res)
+        assert any(
+            rr.rdtype == expected for rr in res.answer
+        ), f"{rrtype} record missing in answer for {owner}: {res.answer}"
+        assert any(
+            rr.rdtype == rdatatype.RRSIG and rr.covers == expected for rr in res.answer
+        ), f"RRSIG({rrtype}) missing in answer for {owner}: {res.answer}"
+
+
 def test_rrsigs_for_glue():
     msg = isctest.query.create("ns3.secure.example", "A", cd=True)
     res = isctest.query.tcp(msg, "10.53.0.4")
index a551d7d026f83d0f5ec7179a367bf0a9d3ac81e0..fabe279d504196996a0af009c7888ad792d836a5 100644 (file)
@@ -381,7 +381,6 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, const dns_name_t *name,
                        return ISC_R_IGNORE;
                }
                if (type == dns_rdatatype_cname || type == dns_rdatatype_nsec ||
-                   type == dns_rdatatype_key ||
                    !dns_nsec_typepresent(&rdata, dns_rdatatype_cname))
                {
                        *exists = true;
index 5713f21bfbc366ffe9d57ec49d0cfccfc86c7f81..64e49ad3f0fe30699d6592034ff53a1f374c6cb8 100644 (file)
@@ -1941,7 +1941,6 @@ dns_nsec3_noexistnodata(dns_rdatatype_t type, const dns_name_t *name,
                        }
                        if (type == dns_rdatatype_cname ||
                            type == dns_rdatatype_nsec ||
-                           type == dns_rdatatype_key ||
                            !dns_nsec3_typepresent(&rdata, dns_rdatatype_cname))
                        {
                                *exists = true;
index 690cf753941dbad7bb4f778552f35eb9625bdedb..eb85d5092c205d7c5ddf3bf772ea246d81feff00 100644 (file)
@@ -1578,11 +1578,9 @@ qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
 
        /*
         * Certain DNSSEC types are not subject to CNAME matching
-        * (RFC4035, section 2.5 and RFC3007).
+        * (RFC4035, section 2.5).
         */
-       if (type == dns_rdatatype_key || type == dns_rdatatype_nsec ||
-           type == dns_rdatatype_rrsig)
-       {
+       if (type == dns_rdatatype_nsec || type == dns_rdatatype_rrsig) {
                cname_ok = false;
        }
 
index ced8ce3cf75acc5d578eeb1b30dfca67ab5a3f59..dce80a32ff6ae26e63f50aa4e111d94f755d412e 100644 (file)
@@ -1828,8 +1828,7 @@ cname_and_other(qpznode_t *node, uint32_t serial) {
                        if (first_existing_header(top, serial) != NULL) {
                                cname = true;
                        }
-               } else if (rdtype != dns_rdatatype_key &&
-                          rdtype != dns_rdatatype_nsec &&
+               } else if (rdtype != dns_rdatatype_nsec &&
                           rdtype != dns_rdatatype_rrsig)
                {
                        if (first_existing_header(top, serial) != NULL) {
@@ -3638,12 +3637,12 @@ found:
 
        /*
         * Certain DNSSEC types are not subject to CNAME matching
-        * (RFC4035, section 2.5 and RFC3007).
+        * (RFC4035, section 2.5).
         *
         * We don't check for RRSIG, because we don't store RRSIG records
         * directly.
         */
-       if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) {
+       if (type == dns_rdatatype_nsec) {
                cname_ok = false;
        }
 
@@ -3684,15 +3683,9 @@ found:
                                search.need_cleanup = true;
                                maybe_zonecut = false;
                                at_zonecut = true;
-                               /*
-                                * It is not clear if KEY should still be
-                                * allowed at the parent side of the zone
-                                * cut or not.  It is needed for RFC3007
-                                * validated updates.
-                                */
+
                                if ((search.options & DNS_DBFIND_GLUEOK) == 0 &&
-                                   type != dns_rdatatype_nsec &&
-                                   type != dns_rdatatype_key)
+                                   type != dns_rdatatype_nsec)
                                {
                                        /*
                                         * Glue is not OK, but any answer we
@@ -3900,18 +3893,10 @@ found:
                /*
                 * If we're beneath a zone cut, we must indicate that the
                 * result is glue, unless we're actually at the zone cut
-                * and the type is NSEC or KEY.
+                * and the type is NSEC.
                 */
                if (search.zonecut == node) {
-                       /*
-                        * It is not clear if KEY should still be
-                        * allowed at the parent side of the zone
-                        * cut or not.  It is needed for RFC3007
-                        * validated updates.
-                        */
-                       if (dns_rdatatype_isnsec(type) ||
-                           type == dns_rdatatype_key)
-                       {
+                       if (dns_rdatatype_isnsec(type)) {
                                result = ISC_R_SUCCESS;
                        } else if (type == dns_rdatatype_any) {
                                result = DNS_R_ZONECUT;
index 2d375188a23e30949ca981060013e59e1c6597d3..0dfc0cf7e3f27469751c909df3869dc9c5e0b1e0 100644 (file)
@@ -18,8 +18,7 @@
 
 #include <dst/dst.h>
 
-#define RRTYPE_KEY_ATTRIBUTES \
-       (DNS_RDATATYPEATTR_ATCNAME | DNS_RDATATYPEATTR_ZONECUTAUTH)
+#define RRTYPE_KEY_ATTRIBUTES (0)
 
 static isc_result_t
 generic_fromtext_key(ARGS_FROMTEXT) {
index c5358705049b760d0d38e2f68eca9eb468fb9894..5c59014eb5efc9acf0f38c1fdc328a0e02554e01 100644 (file)
@@ -4571,7 +4571,6 @@ resume_qmin(void *arg) {
                 */
                if ((result == DNS_R_CNAME || result == DNS_R_DNAME) &&
                    fctx->qmin_labels == dns_name_countlabels(fctx->name) &&
-                   fctx->type != dns_rdatatype_key &&
                    fctx->type != dns_rdatatype_nsec &&
                    fctx->type != dns_rdatatype_any &&
                    fctx->type != dns_rdatatype_rrsig)
@@ -5576,11 +5575,9 @@ evict_cname_other(fetchctx_t *fctx, dns_name_t *name) {
                dns_typepair_t typepair = DNS_TYPEPAIR_VALUE(rdataset.type,
                                                             rdataset.covers);
                switch (typepair) {
-               /* KEY and NSEC records are allowed */
+               /* NSEC records are allowed */
                case DNS_TYPEPAIR(dns_rdatatype_nsec):
-               case DNS_TYPEPAIR(dns_rdatatype_key):
                case DNS_SIGTYPEPAIR(dns_rdatatype_nsec):
-               case DNS_SIGTYPEPAIR(dns_rdatatype_key):
                /* Keep the CNAME and its signature */
                case DNS_TYPEPAIR(dns_rdatatype_cname):
                case DNS_SIGTYPEPAIR(dns_rdatatype_cname):
@@ -5660,7 +5657,6 @@ cache_rrset(fetchctx_t *fctx, isc_stdtime_t now, dns_name_t *name,
         * along with the covered RRset in 'delete_rrset()'.
         */
        if (!dns_rdataset_matchestype(rdataset, dns_rdatatype_cname) &&
-           !dns_rdataset_matchestype(rdataset, dns_rdatatype_key) &&
            !dns_rdataset_matchestype(rdataset, dns_rdatatype_nsec))
        {
                delete_rrset(fctx, name, dns_rdatatype_cname);
@@ -8867,7 +8863,7 @@ rctx_answer_cname(respctx_t *rctx) {
        }
 
        if (rctx->type == dns_rdatatype_rrsig ||
-           rctx->type == dns_rdatatype_key || rctx->type == dns_rdatatype_nsec)
+           rctx->type == dns_rdatatype_nsec)
        {
                char buf[DNS_RDATATYPE_FORMATSIZE];
                dns_rdatatype_format(rctx->type, buf, sizeof(buf));
index b065b3e555baeaf5697985d58f321886dc13603c..940b54e2071567f75e99c713ecf091db20071408 100644 (file)
@@ -3365,7 +3365,6 @@ ISC_RUN_TEST_IMPL(atcname) {
                bool tf = dns_rdatatype_atcname((dns_rdatatype_t)i);
                switch (i) {
                case dns_rdatatype_nsec:
-               case dns_rdatatype_key:
                case dns_rdatatype_rrsig:
                        if (!tf) {
                                print_message(UNR, i);
@@ -3416,7 +3415,6 @@ ISC_RUN_TEST_IMPL(iszonecutauth) {
                case dns_rdatatype_ns:
                case dns_rdatatype_ds:
                case dns_rdatatype_nsec:
-               case dns_rdatatype_key:
                case dns_rdatatype_rrsig:
                        if (!tf) {
                                print_message(UNR, i);