From cc3455c6287134111b358dce19ea34ae8388fb28 Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Thu, 13 Aug 2026 07:24:26 -0400 Subject: [PATCH] import CS git bind9.16-9.16.23-0.22.el8_10.12 --- SOURCES/bind-9.16-CVE-2026-10723.patch | 63 +++++ SOURCES/bind-9.16-CVE-2026-11331-test.patch | 75 ++++++ SOURCES/bind-9.16-CVE-2026-11331.patch | 29 ++ SOURCES/bind-9.16-CVE-2026-11622.patch | 276 ++++++++++++++++++++ SOURCES/bind-9.16-CVE-2026-11721-test.patch | 148 +++++++++++ SOURCES/bind-9.16-CVE-2026-11721.patch | 269 +++++++++++++++++++ SOURCES/bind-9.16-CVE-2026-13204.patch | 154 +++++++++++ SOURCES/bind-9.16-CVE-2026-13321.patch | 85 ++++++ SPECS/bind9.16.spec | 54 +++- 9 files changed, 1152 insertions(+), 1 deletion(-) create mode 100644 SOURCES/bind-9.16-CVE-2026-10723.patch create mode 100644 SOURCES/bind-9.16-CVE-2026-11331-test.patch create mode 100644 SOURCES/bind-9.16-CVE-2026-11331.patch create mode 100644 SOURCES/bind-9.16-CVE-2026-11622.patch create mode 100644 SOURCES/bind-9.16-CVE-2026-11721-test.patch create mode 100644 SOURCES/bind-9.16-CVE-2026-11721.patch create mode 100644 SOURCES/bind-9.16-CVE-2026-13204.patch create mode 100644 SOURCES/bind-9.16-CVE-2026-13321.patch diff --git a/SOURCES/bind-9.16-CVE-2026-10723.patch b/SOURCES/bind-9.16-CVE-2026-10723.patch new file mode 100644 index 0000000..1f4bb19 --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2026-10723.patch @@ -0,0 +1,63 @@ +From 35e6c3ff81d289627c5b3d63a68187e450740e50 Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Thu, 21 May 2026 14:41:55 -0700 +Subject: [PATCH] Check NSEC3 signer matches the owning zone + +When validating NSEC3 records, reject any signature whose signer field +does not match the zone owning the NSEC3. + +This ensures that a child zone cannot impersonate its parent and forge +NXDOMAIN responses for sibling domains. + +Fixes: isc-projects/bind9#5874 +(cherry picked from commit 6e5066bb1f0f12d090e8707adb7d6ccf74f8012b) +--- + lib/dns/dnssec.c | 19 +++++++++++++++++-- + lib/dns/result.c | 2 +- + 2 files changed, 18 insertions(+), 3 deletions(-) + +diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c +index c168fed..a91cbe3 100644 +--- a/lib/dns/dnssec.c ++++ b/lib/dns/dnssec.c +@@ -426,10 +426,25 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + } + + /* +- * NS, SOA and DNSSKEY records are signed by their owner. +- * DS records are signed by the parent. ++ * NS, SOA and DNSKEY records are signed by their owners. ++ * NSEC3 records are signed by the apex, exactly one level up ++ * from their owner names. ++ * DS records are signed by the parent zone. + */ + switch (set->type) { ++ case dns_rdatatype_nsec3: { ++ dns_name_t apex = DNS_NAME_INITEMPTY; ++ labels = dns_name_countlabels(name); ++ if (labels <= 1) { ++ inc_stat(dns_dnssecstats_fail); ++ return DNS_R_INVALIDNSEC3; ++ } ++ dns_name_split(name, labels - 1, NULL, &apex); ++ if (!dns_name_equal(&apex, &sig.signer)) { ++ inc_stat(dns_dnssecstats_fail); ++ return DNS_R_SIGINVALID; ++ } ++ } break; + case dns_rdatatype_ns: + case dns_rdatatype_soa: + case dns_rdatatype_dnskey: +diff --git a/lib/dns/result.c b/lib/dns/result.c +index 01ce2b4..9ff2a3a 100644 +--- a/lib/dns/result.c ++++ b/lib/dns/result.c +@@ -146,7 +146,7 @@ static const char *text[DNS_R_NRESULTS] = { + "covering NSEC record returned", /*%< 101 DNS_R_COVERINGNSEC */ + "MX is an address", /*%< 102 DNS_R_MXISADDRESS */ + "duplicate query", /*%< 103 DNS_R_DUPLICATE */ +- "invalid NSEC3 owner name (wildcard)", /*%< 104 DNS_R_INVALIDNSEC3 */ ++ "invalid NSEC3 owner name", /*%< 104 DNS_R_INVALIDNSEC3 */ + + "not master", /*%< 105 DNS_R_NOTMASTER */ + "broken trust chain", /*%< 106 DNS_R_BROKENCHAIN */ diff --git a/SOURCES/bind-9.16-CVE-2026-11331-test.patch b/SOURCES/bind-9.16-CVE-2026-11331-test.patch new file mode 100644 index 0000000..ea8499a --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2026-11331-test.patch @@ -0,0 +1,75 @@ +From d4435021c0780353c66d860696a3ff40cfef5466 Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Fri, 10 Apr 2026 10:24:06 +1000 +Subject: [PATCH] Fix TTL extraction from A/AAAA record + +(cherry picked from commit 89c86e338db2492b92e6618c586f146c6928dc6d) +(cherry picked from commit e1c83d27984f10ff929bc54d6ed84b5152be96d5) + +Check rpz name too long wildcard CNAME expansion handling + +(cherry picked from commit 9345394e2097031b55b3ef34ceaadf5a7ebbeef2) +(cherry picked from commit 25b572a6d00f717d7992f154f28b43d2b2ffd0b3) +--- + bin/tests/system/rpz/ns2/tld2.db | 4 ++++ + bin/tests/system/rpz/ns4/tld4.db | 2 ++ + bin/tests/system/rpz/tests.sh | 5 ++++- + 3 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/bin/tests/system/rpz/ns2/tld2.db b/bin/tests/system/rpz/ns2/tld2.db +index a16d196e39..b6ab030182 100644 +--- a/bin/tests/system/rpz/ns2/tld2.db ++++ b/bin/tests/system/rpz/ns2/tld2.db +@@ -1,5 +1,7 @@ + ; 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 http://mozilla.org/MPL/2.0/. +@@ -118,3 +120,5 @@ a6-2 A 192.168.6.2 + + a7-1 A 192.168.7.1 + TXT "a7-1 tld2 text" ++ ++*.wild A 192.168.9.1 +diff --git a/bin/tests/system/rpz/ns4/tld4.db b/bin/tests/system/rpz/ns4/tld4.db +index a16481823d..a3ac815db4 100644 +--- a/bin/tests/system/rpz/ns4/tld4.db ++++ b/bin/tests/system/rpz/ns4/tld4.db +@@ -57,6 +57,8 @@ a3-6.tld2 A 56.56.56.56 + + a3-7.sub1.tld2 A 57.57.57.57 + ++*.wild.sub1.tld2 A 57.57.57.57 ++ + a3-8.tld2 A 58.58.58.58 + + a3-9.sub9.tld2 A 59.59.59.59 +diff --git a/bin/tests/system/rpz/tests.sh b/bin/tests/system/rpz/tests.sh +index fe72456f25..0df394bcb2 100644 +--- a/bin/tests/system/rpz/tests.sh ++++ b/bin/tests/system/rpz/tests.sh +@@ -380,7 +380,7 @@ addr () { + digcmd $2 >$DIGNM + #ckalive "$2" "server crashed by 'dig $2'" || return 1 + ADDR_ESC=`echo "$ADDR" | sed -e 's/\./\\\\./g'` +- ADDR_TTL=`tr -d '\r' < $DIGNM | sed -n -e "s/^[-.a-z0-9]\{1,\}[ ]*\([0-9]*\) IN AA* ${ADDR_ESC}\$/\1/p"` ++ ADDR_TTL=$(tr -d '\r' <$DIGNM | sed -n -e "s/^[-.a-z0-9]\{1,\}[ ]*\([0-9]*\)[ ]IN[ ]AA*[ ]${ADDR_ESC}\$/\1/p") + if test -z "$ADDR_TTL"; then + setret "'dig $2' wrong; no address $ADDR record in $DIGNM" + return 1 +@@ -534,6 +534,9 @@ for mode in native dnsrps; do + ;; status: SERVFAIL, x + EOF + addr 35.35.35.35 "x.servfail @$ns5" # 35 qname-wait-recurse no ++ here aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.wild.sub1.tld2 <<'EOF' # 36 wildcard CNAME name to long ++ ;; status: YXDOMAIN, x ++EOF + end_group + ckstats $ns3 test1 ns3 22 + ckstats $ns5 test1 ns5 1 +-- +2.55.0 + diff --git a/SOURCES/bind-9.16-CVE-2026-11331.patch b/SOURCES/bind-9.16-CVE-2026-11331.patch new file mode 100644 index 0000000..12d5b0a --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2026-11331.patch @@ -0,0 +1,29 @@ +From c7b516d590dd832ad9e9d85665da1a6970d0ecb3 Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Fri, 10 Apr 2026 10:26:14 +1000 +Subject: [PATCH] Properly handle rpz name to long wildcard expansion + +Previously a self referential CNAME and the original address +record were returned. We now return a YXDOMAIN response. + +(cherry picked from commit cfc4c4f69870ce492deaaa429453563d1621ded3) +--- + lib/ns/query.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/lib/ns/query.c b/lib/ns/query.c +index 4d6dd5d..bb0c629 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -7064,8 +7064,9 @@ query_rpzcname(query_ctx_t *qctx, dns_name_t *cname) { + qctx->fname, NULL); + if (result == DNS_R_NAMETOOLONG) { + client->message->rcode = dns_rcode_yxdomain; +- } else if (result != ISC_R_SUCCESS) { +- return (result); ++ } ++ if (result != ISC_R_SUCCESS) { ++ return result; + } + } else { + dns_name_copynf(cname, qctx->fname); diff --git a/SOURCES/bind-9.16-CVE-2026-11622.patch b/SOURCES/bind-9.16-CVE-2026-11622.patch new file mode 100644 index 0000000..23ee22c --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2026-11622.patch @@ -0,0 +1,276 @@ +From cedf1d41e0fb768d3a7252544be7ec1540444674 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Tue, 23 Jun 2026 10:59:38 +0200 +Subject: [PATCH] Make the dns_slabheaders in the cache reference counted + +Instead of only reference counting the enclosing qpcnode, add the +reference counting directly to the slabheaders. The reference is +incremented when an rdataset is bound to the header and decremented when +the rdataset is disassociated, so a stale slabheader can be removed from +the node's down chain as soon as its own reference count reaches zero, +instead of waiting for the whole qpcnode to become unreferenced. + +Building on that, clean up the ancient headers eagerly: mark_ancient() +is made idempotent, releases the header's own (container) reference and +reaps the stale headers from the node's down chain as soon as their +references reach zero. A header evicted over the per-name type limit is +expired only after the new rdataset has been bound, so the bind's +increment always precedes mark_ancient()'s decrement. + +Because a header can now be reclaimed independently of its node, the +rdataset iterators must keep the header they are positioned on alive: +each iterator takes a reference on its current header and releases it +when it advances or is destroyed. Iteration otherwise stays lazy and +re-reads the node on every step, so it still observes records added to +the node while the iterator is live, as zone signing requires. + +The slab headers are shared with the zone databases, so the matching +increment is added to every bind path. The noqname/closest proofs hand +out rdatasets backed by bare slabs that have no header, so they are +given a separate dns_rdataproof_rdatasetmethods that leaves the +reference count untouched. + +(cherry picked from commit 2dabf117e1264fd13fb33096f87e78a039fd1c6c) +--- + bin/tests/system/reclimit/tests.sh | 4 +- + lib/dns/include/dns/rdataslab.h | 1 + + lib/dns/rbtdb.c | 77 +++++++++++++++++++++++++----- + 3 files changed, 69 insertions(+), 13 deletions(-) + +diff --git a/bin/tests/system/reclimit/tests.sh b/bin/tests/system/reclimit/tests.sh +index ba574ad..112d2de 100644 +--- a/bin/tests/system/reclimit/tests.sh ++++ b/bin/tests/system/reclimit/tests.sh +@@ -313,13 +313,13 @@ echo_i "checking that NXDOMAIN names over the max-types-per-name limit don't get + + # Query for 10 NXDOMAIN types + for ntype in $(seq 65270 65279); do +- check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR big SOA 0 || ret=1 ++ check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR big SOA 60 || ret=1 + done + # Wait at least 1 second + sleep 1 + # Query for 10 NXDOMAIN types again - these should not be cached + for ntype in $(seq 65270 65279); do +- check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR big SOA 0 || ret=1 ++ check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR big SOA 60 || ret=1 + done + + if [ $ret -ne 0 ]; then echo_i "failed"; fi +diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h +index f2f3513..6e1a06a 100644 +--- a/lib/dns/include/dns/rdataslab.h ++++ b/lib/dns/include/dns/rdataslab.h +@@ -43,6 +43,7 @@ + #include + + #include ++#include + + #include + +diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c +index 7586e2a..5f50c79 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -199,6 +199,7 @@ struct noqname { + }; + + typedef struct rdatasetheader { ++ isc_refcount_t references; + /*% + * Locked by the owning node's lock. + */ +@@ -1514,6 +1515,7 @@ init_rdataset(dns_rbtdb_t *rbtdb, rdatasetheader_t *h) { + h->node_is_relative = 0; + atomic_init(&h->attributes, 0); + atomic_init(&h->last_refresh_fail_ts, 0); ++ isc_refcount_init(&h->references, 1); + + #ifndef ISC_MUTEX_ATOMICS + STATIC_ASSERT((sizeof(h->attributes) == 2), +@@ -1646,6 +1648,9 @@ rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) { + } + } + ++static void ++clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *top); ++ + static inline void + mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) { + uint_least16_t attributes = atomic_load_acquire(&header->attributes); +@@ -1671,8 +1676,12 @@ mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) { + update_rrsetstats(rbtdb, header->type, attributes, false); + header->node->dirty = 1; + ++ isc_refcount_decrement(&header->references); ++ + /* Increment the stats counter for the ancient RRtype. */ + update_rrsetstats(rbtdb, header->type, newattributes, true); ++ ++ clean_stale_headers(rbtdb, rbtdb->common.mctx, header); + } + + static inline void +@@ -1708,12 +1717,19 @@ static inline void + clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, + rdatasetheader_t *top) { + rdatasetheader_t *d, *down_next; ++ rdatasetheader_t *down_parent = top; + + for (d = top->down; d != NULL; d = down_next) { + down_next = d->down; +- free_rdataset(rbtdb, mctx, d); ++ d->next = down_parent; ++ ++ if (isc_refcount_current(&d->references) == 0) { ++ free_rdataset(rbtdb, mctx, d); ++ down_parent->down = down_next; ++ } else { ++ down_parent = d; ++ } + } +- top->down = NULL; + } + + static inline void +@@ -1729,6 +1745,7 @@ clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { + for (current = node->data; current != NULL; current = top_next) { + top_next = current->next; + clean_stale_headers(rbtdb, mctx, current); ++ INSIST(current->down == NULL); + /* + * If current is nonexistent, ancient, or stale and + * we are not keeping stale, we can clean it up. +@@ -3196,6 +3213,8 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header, + return; + } + ++ isc_refcount_increment(&header->references); ++ + new_reference(rbtdb, node, locktype); + + INSIST(rdataset->methods == NULL); /* We must be disassociated. */ +@@ -6258,6 +6277,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, + bool header_nx; + bool newheader_nx; + bool merge; ++ bool do_expireheader = false; + dns_rdatatype_t rdtype, covers; + rbtdb_rdatatype_t negtype, sigtype; + dns_trust_t trust; +@@ -6809,6 +6829,7 @@ find_header: + } + + if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) { ++ do_expireheader = true; + if (expireheader == NULL) { + expireheader = newheader; + } +@@ -6822,15 +6843,6 @@ find_header: + */ + expireheader = newheader; + } +- +- set_ttl(rbtdb, expireheader, 0); +- mark_header_ancient(rbtdb, expireheader); +- /* +- * FIXME: In theory, we should mark the RRSIG +- * and the header at the same time, but there is +- * no direct link between those two header, so +- * we would have to check the whole list again. +- */ + } + } + } +@@ -6853,6 +6865,15 @@ find_header: + isc_rwlocktype_write, addedrdataset); + } + ++ /* ++ * We need to delay the expiration of the header until we are bound to ++ * it to prevent decrement-then-increment on the header references. ++ */ ++ if (do_expireheader) { ++ set_ttl(rbtdb, expireheader, 0); ++ mark_header_ancient(rbtdb, expireheader); ++ } ++ + return (ISC_R_SUCCESS); + } + +@@ -9102,6 +9123,12 @@ rdataset_disassociate(dns_rdataset_t *rdataset) { + dns_db_t *db = rdataset->private1; + dns_dbnode_t *node = rdataset->private2; + ++ if (rdataset->methods == &rdataset_methods) { ++ rdatasetheader_t *header = rdataset->private3; ++ header--; ++ isc_refcount_decrement(&header->references); ++ } ++ + detachnode(db, &node); + } + +@@ -9215,6 +9242,11 @@ rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { + dns_dbnode_t *cloned_node = NULL; + + attachnode(db, node, &cloned_node); ++ if (source->methods == &rdataset_methods) { ++ rdatasetheader_t *header = source->private3; ++ header--; ++ isc_refcount_increment(&header->references); ++ } + INSIST(!ISC_LINK_LINKED(target, link)); + *target = *source; + ISC_LINK_INIT(target, link); +@@ -9378,6 +9410,11 @@ rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) { + + rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp); + ++ if (rbtiterator->current != NULL) { ++ isc_refcount_decrement(&rbtiterator->current->references); ++ rbtiterator->current = NULL; ++ } ++ + if (rbtiterator->common.version != NULL) { + closeversion(rbtiterator->common.db, + &rbtiterator->common.version, false); +@@ -9441,9 +9478,18 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) { + } + } + ++ if (header != NULL) { ++ isc_refcount_increment0(&header->references); ++ } ++ + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + ++ if (rbtiterator->current != NULL) { ++ isc_refcount_decrement(&rbtiterator->current->references); ++ rbtiterator->current = NULL; ++ } ++ + rbtiterator->current = header; + + if (header == NULL) { +@@ -9525,9 +9571,18 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) { + } + } + ++ if (header != NULL) { ++ isc_refcount_increment0(&header->references); ++ } ++ + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, + isc_rwlocktype_read); + ++ if (rbtiterator->current != NULL) { ++ isc_refcount_decrement(&rbtiterator->current->references); ++ rbtiterator->current = NULL; ++ } ++ + rbtiterator->current = header; + + if (header == NULL) { diff --git a/SOURCES/bind-9.16-CVE-2026-11721-test.patch b/SOURCES/bind-9.16-CVE-2026-11721-test.patch new file mode 100644 index 0000000..f7d3b58 --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2026-11721-test.patch @@ -0,0 +1,148 @@ +From 53123b5a9f9ce0d84f352452ff24907f5930d8df Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Tue, 14 Apr 2026 13:46:22 +1000 +Subject: [PATCH] Test RRSIG record parsing + +In particular test that labels and signer fields are consistent. + +(cherry picked from commit 5a95e64731afe63d348d272cc4d3b2f9847150c2) +(cherry picked from commit ebf391bdb8ea196dab43d0a1e1f639458cb15126) +--- + lib/dns/tests/rdata_test.c | 114 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 114 insertions(+) + +diff --git a/lib/dns/tests/rdata_test.c b/lib/dns/tests/rdata_test.c +index 0f9ddef82d..edf83c94cc 100644 +--- a/lib/dns/tests/rdata_test.c ++++ b/lib/dns/tests/rdata_test.c +@@ -2367,6 +2367,119 @@ rkey(void **state) { + dns_rdatatype_rkey, sizeof(dns_rdata_rkey_t)); + } + ++static void ++rrsig(void **state) { ++ text_ok_t text_ok[] = { ++ TEXT_VALID("SOA 8 0 86400 20260426170000 20260413160000 54393 " ++ ". " ++ "tFbcoVP8MnpecUquJ/aj+XeNgV7ts9GSHVkXaXRJrJ/" ++ "TEkOZApVG0F6E " ++ "9sYpxGk2ItweLL43ujioGj0HWwZDRR+vbur+O/" ++ "dIdheiig1VvU+9HXLi " ++ "QOViY9Kc64ixdyJhYCC5K+bO1qsHxd+" ++ "KJXOaxyHbqchYkDFy4PL6qftE " ++ "VaLkueRgjXgOsq/" ++ "NxvCXDgAa5xy0+3Sl0myxIs8rJ5KeXfJQFe7qxgaw " ++ "VjJsJTKw8neOTw2rQfLaigWu2LIWw+" ++ "IyVrLjZJdLqGkiLBGd1w4X3U12 " ++ "fFxoY3eqzNgBEtduoGKPZ/" ++ "NpP9cuKJORJ18283aV8hR4WO91VR0q1zcM jLwqUg=="), ++ /* labels too short for signer */ ++ TEXT_INVALID("SOA 8 0 86400 20260426170000 20260413160000 " ++ "54393 example. " ++ "tFbcoVP8MnpecUquJ/aj+XeNgV7ts9GSHVkXaXRJrJ/" ++ "TEkOZApVG0F6E " ++ "9sYpxGk2ItweLL43ujioGj0HWwZDRR+vbur+O/" ++ "dIdheiig1VvU+9HXLi " ++ "QOViY9Kc64ixdyJhYCC5K+bO1qsHxd+" ++ "KJXOaxyHbqchYkDFy4PL6qftE " ++ "VaLkueRgjXgOsq/" ++ "NxvCXDgAa5xy0+3Sl0myxIs8rJ5KeXfJQFe7qxgaw " ++ "VjJsJTKw8neOTw2rQfLaigWu2LIWw+" ++ "IyVrLjZJdLqGkiLBGd1w4X3U12 " ++ "fFxoY3eqzNgBEtduoGKPZ/" ++ "NpP9cuKJORJ18283aV8hR4WO91VR0q1zcM jLwqUg=="), ++ /* ++ * Sentinel. ++ */ ++ TEXT_SENTINEL() ++ }; ++ wire_ok_t wire_ok[] = { ++ WIRE_VALID(0x00, 0x06, 0x08, 0x00, 0x00, 0x01, 0x51, 0x80, 0x69, ++ 0xee, 0x44, 0x90, 0x69, 0xdd, 0x13, 0x00, 0xd4, 0x79, ++ 0x00, 0xb4, 0x56, 0xdc, 0xa1, 0x53, 0xfc, 0x32, 0x7a, ++ 0x5e, 0x71, 0x4a, 0xae, 0x27, 0xf6, 0xa3, 0xf9, 0x77, ++ 0x8d, 0x81, 0x5e, 0xed, 0xb3, 0xd1, 0x92, 0x1d, 0x59, ++ 0x17, 0x69, 0x74, 0x49, 0xac, 0x9f, 0xd3, 0x12, 0x43, ++ 0x99, 0x02, 0x95, 0x46, 0xd0, 0x5e, 0x84, 0xf6, 0xc6, ++ 0x29, 0xc4, 0x69, 0x36, 0x22, 0xdc, 0x1e, 0x2c, 0xbe, ++ 0x37, 0xba, 0x38, 0xa8, 0x1a, 0x3d, 0x07, 0x5b, 0x06, ++ 0x43, 0x45, 0x1f, 0xaf, 0x6e, 0xea, 0xfe, 0x3b, 0xf7, ++ 0x48, 0x76, 0x17, 0xa2, 0x8a, 0x0d, 0x55, 0xbd, 0x4f, ++ 0xbd, 0x1d, 0x72, 0xe2, 0x40, 0xe5, 0x62, 0x63, 0xd2, ++ 0x9c, 0xeb, 0x88, 0xb1, 0x77, 0x22, 0x61, 0x60, 0x20, ++ 0xb9, 0x2b, 0xe6, 0xce, 0xd6, 0xab, 0x07, 0xc5, 0xdf, ++ 0x8a, 0x25, 0x73, 0x9a, 0xc7, 0x21, 0xdb, 0xa9, 0xc8, ++ 0x58, 0x90, 0x31, 0x72, 0xe0, 0xf2, 0xfa, 0xa9, 0xfb, ++ 0x44, 0x55, 0xa2, 0xe4, 0xb9, 0xe4, 0x60, 0x8d, 0x78, ++ 0x0e, 0xb2, 0xaf, 0xcd, 0xc6, 0xf0, 0x97, 0x0e, 0x00, ++ 0x1a, 0xe7, 0x1c, 0xb4, 0xfb, 0x74, 0xa5, 0xd2, 0x6c, ++ 0xb1, 0x22, 0xcf, 0x2b, 0x27, 0x92, 0x9e, 0x5d, 0xf2, ++ 0x50, 0x15, 0xee, 0xea, 0xc6, 0x06, 0xb0, 0x56, 0x32, ++ 0x6c, 0x25, 0x32, 0xb0, 0xf2, 0x77, 0x8e, 0x4f, 0x0d, ++ 0xab, 0x41, 0xf2, 0xda, 0x8a, 0x05, 0xae, 0xd8, 0xb2, ++ 0x16, 0xc3, 0xe2, 0x32, 0x56, 0xb2, 0xe3, 0x64, 0x97, ++ 0x4b, 0xa8, 0x69, 0x22, 0x2c, 0x11, 0x9d, 0xd7, 0x0e, ++ 0x17, 0xdd, 0x4d, 0x76, 0x7c, 0x5c, 0x68, 0x63, 0x77, ++ 0xaa, 0xcc, 0xd8, 0x01, 0x12, 0xd7, 0x6e, 0xa0, 0x62, ++ 0x8f, 0x67, 0xf3, 0x69, 0x3f, 0xd7, 0x2e, 0x28, 0x93, ++ 0x91, 0x27, 0x5f, 0x36, 0xf3, 0x76, 0x95, 0xf2, 0x14, ++ 0x78, 0x58, 0xef, 0x75, 0x55, 0x1d, 0x2a, 0xd7, 0x37, ++ 0x0c, 0x8c, 0xbc, 0x2a, 0x52), ++ /* labels too short for signer */ ++ WIRE_INVALID( ++ 0x00, 0x06, 0x08, 0x00, 0x00, 0x01, 0x51, 0x80, 0x69, ++ 0xee, 0x44, 0x90, 0x69, 0xdd, 0x13, 0x00, 0xd4, 0x79, ++ 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, ++ 0xb4, 0x56, 0xdc, 0xa1, 0x53, 0xfc, 0x32, 0x7a, 0x5e, ++ 0x71, 0x4a, 0xae, 0x27, 0xf6, 0xa3, 0xf9, 0x77, 0x8d, ++ 0x81, 0x5e, 0xed, 0xb3, 0xd1, 0x92, 0x1d, 0x59, 0x17, ++ 0x69, 0x74, 0x49, 0xac, 0x9f, 0xd3, 0x12, 0x43, 0x99, ++ 0x02, 0x95, 0x46, 0xd0, 0x5e, 0x84, 0xf6, 0xc6, 0x29, ++ 0xc4, 0x69, 0x36, 0x22, 0xdc, 0x1e, 0x2c, 0xbe, 0x37, ++ 0xba, 0x38, 0xa8, 0x1a, 0x3d, 0x07, 0x5b, 0x06, 0x43, ++ 0x45, 0x1f, 0xaf, 0x6e, 0xea, 0xfe, 0x3b, 0xf7, 0x48, ++ 0x76, 0x17, 0xa2, 0x8a, 0x0d, 0x55, 0xbd, 0x4f, 0xbd, ++ 0x1d, 0x72, 0xe2, 0x40, 0xe5, 0x62, 0x63, 0xd2, 0x9c, ++ 0xeb, 0x88, 0xb1, 0x77, 0x22, 0x61, 0x60, 0x20, 0xb9, ++ 0x2b, 0xe6, 0xce, 0xd6, 0xab, 0x07, 0xc5, 0xdf, 0x8a, ++ 0x25, 0x73, 0x9a, 0xc7, 0x21, 0xdb, 0xa9, 0xc8, 0x58, ++ 0x90, 0x31, 0x72, 0xe0, 0xf2, 0xfa, 0xa9, 0xfb, 0x44, ++ 0x55, 0xa2, 0xe4, 0xb9, 0xe4, 0x60, 0x8d, 0x78, 0x0e, ++ 0xb2, 0xaf, 0xcd, 0xc6, 0xf0, 0x97, 0x0e, 0x00, 0x1a, ++ 0xe7, 0x1c, 0xb4, 0xfb, 0x74, 0xa5, 0xd2, 0x6c, 0xb1, ++ 0x22, 0xcf, 0x2b, 0x27, 0x92, 0x9e, 0x5d, 0xf2, 0x50, ++ 0x15, 0xee, 0xea, 0xc6, 0x06, 0xb0, 0x56, 0x32, 0x6c, ++ 0x25, 0x32, 0xb0, 0xf2, 0x77, 0x8e, 0x4f, 0x0d, 0xab, ++ 0x41, 0xf2, 0xda, 0x8a, 0x05, 0xae, 0xd8, 0xb2, 0x16, ++ 0xc3, 0xe2, 0x32, 0x56, 0xb2, 0xe3, 0x64, 0x97, 0x4b, ++ 0xa8, 0x69, 0x22, 0x2c, 0x11, 0x9d, 0xd7, 0x0e, 0x17, ++ 0xdd, 0x4d, 0x76, 0x7c, 0x5c, 0x68, 0x63, 0x77, 0xaa, ++ 0xcc, 0xd8, 0x01, 0x12, 0xd7, 0x6e, 0xa0, 0x62, 0x8f, ++ 0x67, 0xf3, 0x69, 0x3f, 0xd7, 0x2e, 0x28, 0x93, 0x91, ++ 0x27, 0x5f, 0x36, 0xf3, 0x76, 0x95, 0xf2, 0x14, 0x78, ++ 0x58, 0xef, 0x75, 0x55, 0x1d, 0x2a, 0xd7, 0x37, 0x0c, ++ 0x8c, 0xbc, 0x2a, 0x52), ++ ++ WIRE_SENTINEL() ++ }; ++ ++ UNUSED(state); ++ ++ check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in, ++ dns_rdatatype_rrsig, sizeof(dns_rdata_rrsig_t)); ++} ++ + /* SSHFP RDATA manipulations */ + static void + sshfp(void **state) { +@@ -3156,6 +3269,7 @@ main(int argc, char **argv) { + cmocka_unit_test_setup_teardown(nsec3, _setup, _teardown), + cmocka_unit_test_setup_teardown(nxt, _setup, _teardown), + cmocka_unit_test_setup_teardown(rkey, _setup, _teardown), ++ cmocka_unit_test_setup_teardown(rrsig, _setup, _teardown), + cmocka_unit_test_setup_teardown(sshfp, _setup, _teardown), + cmocka_unit_test_setup_teardown(wks, _setup, _teardown), + cmocka_unit_test_setup_teardown(zonemd, _setup, _teardown), +-- +2.55.0 + diff --git a/SOURCES/bind-9.16-CVE-2026-11721.patch b/SOURCES/bind-9.16-CVE-2026-11721.patch new file mode 100644 index 0000000..36b23dc --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2026-11721.patch @@ -0,0 +1,269 @@ +From 83650f725e274dcd00ccad155183f1ca491794ab Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Tue, 14 Apr 2026 12:24:33 +1000 +Subject: [PATCH 1/2] Invalid signed wildcard records were being accepted + +An RRSIG whose Labels field indicates fewer labels than its signer +name requires was being accepted. When such a record covers a +wildcard, the validator reconstructs a wildcard owner name above the +signer's zone and caches it as secure. RFC 8198 cache synthesis +(synth-from-dnssec) then serves that forged wildcard for unrelated +names, poisoning the cache. + +These records are now rejected, both when an RRSIG is parsed and when +its signature is verified. + +(cherry picked from commit 084ca5ee10515e461d46b63df9660b8394bc7de9) +--- + lib/dns/dnssec.c | 43 +++++++++++++++++++++++--------- + lib/dns/rdata/generic/rrsig_46.c | 37 ++++++++++++++++++++------- + 2 files changed, 59 insertions(+), 21 deletions(-) + +diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c +index c168fed..fcebd3d 100644 +--- a/lib/dns/dnssec.c ++++ b/lib/dns/dnssec.c +@@ -139,11 +139,11 @@ dns_dnssec_keyfromrdata(const dns_name_t *name, const dns_rdata_t *rdata, + isc_buffer_t b; + isc_region_t r; + +- INSIST(name != NULL); +- INSIST(rdata != NULL); +- INSIST(mctx != NULL); +- INSIST(key != NULL); +- INSIST(*key == NULL); ++ REQUIRE(name != NULL); ++ REQUIRE(rdata != NULL); ++ REQUIRE(mctx != NULL); ++ REQUIRE(key != NULL); ++ REQUIRE(*key == NULL); + REQUIRE(rdata->type == dns_rdatatype_key || + rdata->type == dns_rdatatype_dnskey); + +@@ -197,12 +197,14 @@ dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + isc_buffer_t *databuf = NULL; + char data[256 + 8]; + uint32_t flags; ++ unsigned int labels; + unsigned int sigsize; + dns_fixedname_t fnewname; + dns_fixedname_t fsigner; + + REQUIRE(name != NULL); +- REQUIRE(dns_name_countlabels(name) <= 255); ++ labels = dns_name_countlabels(name); ++ REQUIRE(labels <= 255 && labels > 0); + REQUIRE(set != NULL); + REQUIRE(key != NULL); + REQUIRE(inception != NULL); +@@ -242,7 +244,7 @@ dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + + sig.covered = set->type; + sig.algorithm = dst_key_alg(key); +- sig.labels = dns_name_countlabels(name) - 1; ++ sig.labels = labels - 1; + if (dns_name_iswildcard(name)) { + sig.labels--; + } +@@ -386,11 +388,14 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + isc_result_t ret; + unsigned char data[300]; + dst_context_t *ctx = NULL; +- int labels = 0; ++ unsigned int labels; ++ unsigned int siglabels; + uint32_t flags; + bool downcase = false; + + REQUIRE(name != NULL); ++ labels = dns_name_countlabels(name); ++ REQUIRE(labels > 0); + REQUIRE(set != NULL); + REQUIRE(key != NULL); + REQUIRE(mctx != NULL); +@@ -405,6 +410,21 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + return (DNS_R_SIGINVALID); + } + ++ /* ++ * The RRSIG labels field can't indicate fewer labels than the ++ * signer. Also the labels shouldn't be greater than that of ++ * the owner name. ++ * ++ * sig.labels doesn't include the root label, so add 1 to account ++ * for it. ++ */ ++ siglabels = sig.labels + 1; ++ if (siglabels < dns_name_countlabels(&sig.signer) || siglabels > labels) ++ { ++ inc_stat(dns_dnssecstats_fail); ++ return (DNS_R_SIGINVALID); ++ } ++ + if (isc_serial_lt(sig.timeexpire, sig.timesigned)) { + inc_stat(dns_dnssecstats_fail); + return (DNS_R_SIGINVALID); +@@ -484,10 +504,9 @@ again: + * If the name is an expanded wildcard, use the wildcard name. + */ + dns_fixedname_init(&fnewname); +- labels = dns_name_countlabels(name) - 1; + RUNTIME_CHECK(dns_name_downcase(name, dns_fixedname_name(&fnewname), + NULL) == ISC_R_SUCCESS); +- if (labels - sig.labels > 0) { ++ if (labels > siglabels) { + dns_name_split(dns_fixedname_name(&fnewname), sig.labels + 1, + NULL, dns_fixedname_name(&fnewname)); + } +@@ -498,7 +517,7 @@ again: + * Create an envelope for each rdata: . + */ + isc_buffer_init(&envbuf, data, sizeof(data)); +- if (labels - sig.labels > 0) { ++ if (labels > siglabels) { + isc_buffer_putuint8(&envbuf, 1); + isc_buffer_putuint8(&envbuf, '*'); + memmove(data + 2, r.base, r.length); +@@ -594,7 +613,7 @@ cleanup_struct: + inc_stat(dns_dnssecstats_fail); + } + +- if (ret == ISC_R_SUCCESS && labels - sig.labels > 0) { ++ if (ret == ISC_R_SUCCESS && labels > siglabels) { + if (wild != NULL) { + RUNTIME_CHECK(dns_name_concatenate( + dns_wildcardname, +diff --git a/lib/dns/rdata/generic/rrsig_46.c b/lib/dns/rdata/generic/rrsig_46.c +index 40506d7..053eef6 100644 +--- a/lib/dns/rdata/generic/rrsig_46.c ++++ b/lib/dns/rdata/generic/rrsig_46.c +@@ -21,12 +21,12 @@ + static inline isc_result_t + fromtext_rrsig(ARGS_FROMTEXT) { + isc_token_t token; +- unsigned char c; ++ unsigned char alg, labels; + long i; + dns_rdatatype_t covered; +- char *e; ++ char *e = NULL; + isc_result_t result; +- dns_name_t name; ++ dns_name_t signer; + isc_buffer_t buffer; + uint32_t time_signed, time_expire; + +@@ -59,8 +59,8 @@ fromtext_rrsig(ARGS_FROMTEXT) { + */ + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, + false)); +- RETTOK(dns_secalg_fromtext(&c, &token.value.as_textregion)); +- RETERR(mem_tobuffer(target, &c, 1)); ++ RETTOK(dns_secalg_fromtext(&alg, &token.value.as_textregion)); ++ RETERR(mem_tobuffer(target, &alg, 1)); + + /* + * Labels. +@@ -70,8 +70,8 @@ fromtext_rrsig(ARGS_FROMTEXT) { + if (token.value.as_ulong > 0xffU) { + RETTOK(ISC_R_RANGE); + } +- c = (unsigned char)token.value.as_ulong; +- RETERR(mem_tobuffer(target, &c, 1)); ++ labels = (unsigned char)token.value.as_ulong; ++ RETERR(mem_tobuffer(target, &labels, 1)); + + /* + * Original ttl. +@@ -142,12 +142,20 @@ fromtext_rrsig(ARGS_FROMTEXT) { + */ + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, + false)); +- dns_name_init(&name, NULL); ++ dns_name_init(&signer, NULL); + buffer_fromregion(&buffer, &token.value.as_region); + if (origin == NULL) { + origin = dns_rootname; + } +- RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target)); ++ RETTOK(dns_name_fromtext(&signer, &buffer, origin, options, target)); ++ ++ /* ++ * (RRSIG labels doesn't include the root label, so add one ++ * to normalize it before checking against the signer.) ++ */ ++ if ((unsigned int)(labels + 1) < dns_name_countlabels(&signer)) { ++ RETTOK(ISC_R_RANGE); ++ } + + /* + * Sig. +@@ -276,6 +284,7 @@ static inline isc_result_t + fromwire_rrsig(ARGS_FROMWIRE) { + isc_region_t sr; + dns_name_t name; ++ unsigned char labels; + + REQUIRE(type == dns_rdatatype_rrsig); + +@@ -298,6 +307,8 @@ fromwire_rrsig(ARGS_FROMWIRE) { + return (ISC_R_UNEXPECTEDEND); + } + ++ labels = sr.base[3]; ++ + isc_buffer_forward(source, 18); + RETERR(mem_tobuffer(target, sr.base, 18)); + +@@ -307,6 +318,14 @@ fromwire_rrsig(ARGS_FROMWIRE) { + dns_name_init(&name, NULL); + RETERR(dns_name_fromwire(&name, source, dctx, options, target)); + ++ /* ++ * (RRSIG labels doesn't include the root label, so add one ++ * to normalize it before checking against the signer.) ++ */ ++ if ((unsigned int)(labels + 1) < dns_name_countlabels(&name)) { ++ RETERR(DNS_R_FORMERR); ++ } ++ + /* + * Sig. + */ + +From c62a6deaeaf37c8f0867f2174e9dc62fb2b68389 Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Tue, 14 Apr 2026 15:14:06 +1000 +Subject: [PATCH 2/2] Don't sign out of zone records in dnssec-signzone + +dnssec-signzone was signing extraneous records that were not within +the namespace of the zone. This no longer occurs. + +(cherry picked from commit e45c9af7051421fd370f20ba8325199c606223fd) + +Don't sign out of zone records in dnssec-signzone + +dnssec-signzone was signing extraneous records that were not within +the namespace of the zone. This no longer occurs. + +(cherry picked from commit e45c9af7051421fd370f20ba8325199c606223fd) +--- + bin/dnssec/dnssec-signzone.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c +index 48aafdd..0ae6fae 100644 +--- a/bin/dnssec/dnssec-signzone.c ++++ b/bin/dnssec/dnssec-signzone.c +@@ -1624,6 +1624,11 @@ assignwork(isc_task_t *task, isc_task_t *worker) { + dns_db_detachnode(gdb, &node); + goto next; + } ++ if (!dns_name_issubdomain(name, gorigin)) { ++ dumpnode(name, node); ++ dns_db_detachnode(gdb, &node); ++ goto next; ++ } + /* + * Sort the zone data from the glue and out-of-zone data. + * For NSEC zones nodes with zone data have NSEC records. diff --git a/SOURCES/bind-9.16-CVE-2026-13204.patch b/SOURCES/bind-9.16-CVE-2026-13204.patch new file mode 100644 index 0000000..2e0a957 --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2026-13204.patch @@ -0,0 +1,154 @@ +From f22132f6faf946cfec02e9b51c58cab54695f42f Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Wed, 13 May 2026 20:45:57 -0700 +Subject: [PATCH] dns_rdataset_addnoqname() could find unsigned NSEC/NSEC3 + +The dns_rdatalist addnoqname() implementation searches for the first +NSEC or NSEC3 record in a message, then for the first RRSIG covering +that type in the same message. Previously, if no RRSIG for the type was +found, the function accepted the unsigned record. Now, it will instead +continue searching until an NSEC or NSEC3 that does have a matching +signature is found. + +When this function is called from validated() in resolver.c, a +non-success return code is now treated as an error instead of triggering +an assertion failure. + +Fixes: isc-projects/bind9#5985 +(cherry picked from commit 57cba571ee31311e54d8a11cb38094d439f04e09) +--- + lib/dns/rbtdb.c | 10 +++++++--- + lib/dns/rdatalist.c | 33 +++++++++++++++++---------------- + lib/dns/resolver.c | 4 +++- + lib/ns/query.c | 4 +++- + 4 files changed, 30 insertions(+), 21 deletions(-) + +diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c +index 7586e2a..ae478bf 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -6877,7 +6877,7 @@ delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, + static inline isc_result_t + addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, + uint32_t maxrrperset, dns_rdataset_t *rdataset) { +- struct noqname *noqname; ++ struct noqname *noqname = NULL; + isc_mem_t *mctx = rbtdb->common.mctx; + dns_name_t name; + dns_rdataset_t neg, negsig; +@@ -6889,7 +6889,9 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, + dns_rdataset_init(&negsig); + + result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig); +- RUNTIME_CHECK(result == ISC_R_SUCCESS); ++ if (result != ISC_R_SUCCESS) { ++ goto cleanup; ++ } + + noqname = isc_mem_get(mctx, sizeof(*noqname)); + dns_name_init(&noqname->name, NULL); +@@ -6915,7 +6917,9 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, + cleanup: + dns_rdataset_disassociate(&neg); + dns_rdataset_disassociate(&negsig); +- free_noqname(mctx, &noqname); ++ if (noqname != NULL) { ++ free_noqname(mctx, &noqname); ++ } + return (result); + } + +diff --git a/lib/dns/rdatalist.c b/lib/dns/rdatalist.c +index 9080015..cc0d190 100644 +--- a/lib/dns/rdatalist.c ++++ b/lib/dns/rdatalist.c +@@ -190,6 +190,7 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) { + dns_rdataset_t *neg = NULL; + dns_rdataset_t *negsig = NULL; + dns_rdataset_t *rdset; ++ dns_rdataset_t *sigset; + dns_ttl_t ttl; + + REQUIRE(rdataset != NULL); +@@ -197,28 +198,27 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) { + for (rdset = ISC_LIST_HEAD(name->list); rdset != NULL; + rdset = ISC_LIST_NEXT(rdset, link)) + { +- if (rdset->rdclass != rdataset->rdclass) { ++ if (rdset->rdclass != rdataset->rdclass || ++ (rdset->type != dns_rdatatype_nsec && ++ rdset->type != dns_rdatatype_nsec3)) ++ { + continue; + } +- if (rdset->type == dns_rdatatype_nsec || +- rdset->type == dns_rdatatype_nsec3) { +- neg = rdset; +- } +- } +- if (neg == NULL) { +- return (ISC_R_NOTFOUND); +- } + +- for (rdset = ISC_LIST_HEAD(name->list); rdset != NULL; +- rdset = ISC_LIST_NEXT(rdset, link)) +- { +- if (rdset->type == dns_rdatatype_rrsig && +- rdset->covers == neg->type) { +- negsig = rdset; ++ for (sigset = ISC_LIST_HEAD(name->list); sigset != NULL; ++ sigset = ISC_LIST_NEXT(sigset, link)) ++ { ++ if (sigset->type == dns_rdatatype_rrsig && ++ sigset->covers == rdset->type) ++ { ++ neg = rdset; ++ negsig = sigset; ++ break; ++ } + } + } + +- if (negsig == NULL) { ++ if (neg == NULL || negsig == NULL) { + return (ISC_R_NOTFOUND); + } + /* +@@ -234,6 +234,7 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) { + rdataset->ttl = neg->ttl = negsig->ttl = ttl; + rdataset->attributes |= DNS_RDATASETATTR_NOQNAME; + rdataset->private6 = name; ++ + return (ISC_R_SUCCESS); + } + +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index a44ae73..b73c8e0 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -6010,7 +6010,9 @@ validated(isc_task_t *task, isc_event_t *event) { + result = dns_rdataset_addnoqname( + vevent->rdataset, + vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]); +- RUNTIME_CHECK(result == ISC_R_SUCCESS); ++ if (result != ISC_R_SUCCESS) { ++ goto noanswer_response; ++ } + INSIST(vevent->sigrdataset != NULL); + vevent->sigrdataset->ttl = vevent->rdataset->ttl; + if (vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER] != NULL) { +diff --git a/lib/ns/query.c b/lib/ns/query.c +index 4d6dd5d..673fe43 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -7399,7 +7399,9 @@ query_addnoqnameproof(query_ctx_t *qctx) { + } + + result = dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig); +- RUNTIME_CHECK(result == ISC_R_SUCCESS); ++ if (result != ISC_R_SUCCESS) { ++ goto cleanup; ++ } + + query_addrrset(qctx, &fname, &neg, &negsig, dbuf, + DNS_SECTION_AUTHORITY); diff --git a/SOURCES/bind-9.16-CVE-2026-13321.patch b/SOURCES/bind-9.16-CVE-2026-13321.patch new file mode 100644 index 0000000..26a55da --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2026-13321.patch @@ -0,0 +1,85 @@ +From db08a828d3a2fae9bdcc901645d63adcd9d090eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ayd=C4=B1n=20Mercan?= +Date: Thu, 7 May 2026 18:59:20 +0300 +Subject: [PATCH] Reject out-of-zone NSEC next owner names + +When verifying DNSSEC records, make sure that a next owner name of +an NSEC record is a subdomain of the signer field. + +This follows the specification RFC 4034, section 4.1.1: + + Owner names of RRsets for which the given zone is not authoritative + (such as glue records) MUST NOT be listed in the Next Domain Name + unless at least one authoritative RRset exists at the same owner + name. + +While the above paragraph is intended for glue records, it also +applies to out-of-zone data. + +(cherry picked from commit 4065512d25b71605b9502bb69dfb903776d35aa9) +--- + lib/dns/dnssec.c | 17 +++++++++++++++++ + lib/dns/include/dns/dnssec.h | 6 ++++++ + 2 files changed, 23 insertions(+) + +diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c +index c168fed..2ab9c02 100644 +--- a/lib/dns/dnssec.c ++++ b/lib/dns/dnssec.c +@@ -376,8 +376,10 @@ isc_result_t + dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + bool ignoretime, unsigned int maxbits, isc_mem_t *mctx, + dns_rdata_t *sigrdata, dns_name_t *wild) { ++ dns_rdata_nsec_t nsec; + dns_rdata_rrsig_t sig; + dns_fixedname_t fnewname; ++ dns_rdata_t rdata = DNS_RDATA_INIT; + isc_region_t r; + isc_buffer_t envbuf; + dns_rdata_t *rdatas; +@@ -451,6 +453,21 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + } + break; + } ++ /* ++ * Check for out of zone NSEC entries. ++ */ ++ if (set->type == dns_rdatatype_nsec) { ++ if (dns_rdataset_first(set) != ISC_R_SUCCESS) { ++ return (DNS_R_NOVALIDNSEC); ++ } ++ dns_rdataset_current(set, &rdata); ++ if (dns_rdata_tostruct(&rdata, &nsec, NULL) != ISC_R_SUCCESS) { ++ return (DNS_R_NOVALIDNSEC); ++ } ++ if (!dns_name_issubdomain(&nsec.next, &sig.signer)) { ++ return (DNS_R_NOVALIDNSEC); ++ } ++ } + + /* + * Is the key allowed to sign data? +diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h +index e74ec47..c811e25 100644 +--- a/lib/dns/include/dns/dnssec.h ++++ b/lib/dns/include/dns/dnssec.h +@@ -133,6 +133,9 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + * this record, as this requires a resolver or database. + * If 'ignoretime' is true, temporal validity will not be checked. + * ++ * If 'set' is of type NSEC, this function also verifies that the ++ * Next Name is a subdomain of the Signer's Name from 'sigrdata'. ++ * + * 'maxbits' specifies the maximum number of rsa exponent bits accepted. + * + * Requires: +@@ -155,6 +158,9 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + *\li #DNS_R_KEYUNAUTHORIZED - the key cannot sign this data (either + * it is not a zone key or its flags prevent + * authentication) ++ * ++ *\li #DNS_R_NOVALIDNSEC - the NSEC rdata is not valid ++ *\li #DNS_R_KEYUNAUTHORIZED - the key cannot sign this data + *\li DST_R_* + */ + diff --git a/SPECS/bind9.16.spec b/SPECS/bind9.16.spec index fd11515..c74211e 100644 --- a/SPECS/bind9.16.spec +++ b/SPECS/bind9.16.spec @@ -62,7 +62,7 @@ Summary: The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) serv Name: bind9.16 License: MPLv2.0 Version: 9.16.23 -Release: 0.22%{?dist}.6 +Release: 0.22%{?dist}.12 Epoch: 32 Url: https://www.isc.org/downloads/bind/ # @@ -187,6 +187,24 @@ Patch230: bind-9.16-CVE-2026-3039.patch # https://gitlab.isc.org/isc-projects/bind9/-/commit/ec2c98181115bd5f6c7087fcc74d816490d4312e # https://gitlab.isc.org/isc-projects/bind9/-/commit/e5abd37cb2330af1fbfeba68eb32f2873390226d Patch231: bind-9.16-CVE-2026-5946.patch +# https://github.com/isc-projects/bind9/commit/1b90fbb4f9d3d923516ff7841171269b993cfd6f +Patch233: bind-9.16-CVE-2026-13204.patch +# https://github.com/isc-projects/bind9/commit/ee2ac186bc5f75f7f3f7049f1a21e9a2014cee59 +Patch234: bind-9.16-CVE-2026-11331.patch +# https://github.com/isc-projects/bind9/commit/15089066b15f826d7487c3d160b5872820f84b83 +# https://github.com/isc-projects/bind9/commit/1a4986e2533f87e80eb21da3f06708d335aff1e2 +Patch235: bind-9.16-CVE-2026-11721.patch +# https://github.com/isc-projects/bind9/commit/c57695da31a338092528283fde4f8ed9048b2087 +Patch236: bind-9.16-CVE-2026-11622.patch +# https://github.com/isc-projects/bind9/commit/af84538a2ce6722b89b8ef7f2a233a5c10d0207d +Patch237: bind-9.16-CVE-2026-13321.patch +# https://github.com/isc-projects/bind9/commit/238ec379e9bed56383ba2333e711e554b139ac13 +Patch238: bind-9.16-CVE-2026-10723.patch +# https://gitlab.isc.org/isc-projects/bind9/commit/ebf391bdb8ea196dab43d0a1e1f639458cb15126 +Patch239: bind-9.16-CVE-2026-11721-test.patch +# https://gitlab.isc.org/isc-projects/bind9/commit/e1c83d27984f10ff929bc54d6ed84b5152be96d5 +# https://gitlab.isc.org/isc-projects/bind9/commit/25b572a6d00f717d7992f154f28b43d2b2ffd0b3 +Patch240: bind-9.16-CVE-2026-11331-test.patch %{?systemd_ordering} Requires: coreutils @@ -529,6 +547,14 @@ in HTML and PDF format. %patch226 -p1 -b .CVE-2026-1519 %patch230 -p1 -b .CVE-2026-3039 %patch231 -p1 -b .CVE-2026-5946 +%patch233 -p1 -b .CVE-2026-13204 +%patch234 -p1 -b .CVE-2026-11331 +%patch235 -p1 -b .CVE-2026-11721 +%patch236 -p1 -b .CVE-2026-11622 +%patch237 -p1 -b .CVE-2026-13321 +%patch238 -p1 -b .CVE-2026-10723 +%patch239 -p1 -b .CVE-2026-11721-test +%patch240 -p1 -b .CVE-2026-11331-test %if %{with PKCS11} %patch135 -p1 -b .config-pkcs11 @@ -1271,6 +1297,32 @@ fi; %endif %changelog +* Thu Jul 23 2026 RHEL Packaging Agent - 32:9.16.23-0.22.12 +- Fix NSEC3 signer validation (CVE-2026-10723) +- Resolves: RHEL-213499 + +* Thu Jul 23 2026 RHEL Packaging Agent - 32:9.16.23-0.22.11 +- Reject out-of-zone NSEC next owner names (CVE-2026-13321) +- Resolves: RHEL-213313 + +* Thu Jul 23 2026 RHEL Packaging Agent - 32:9.16.23-0.22.10 +- Fix CVE-2026-11622: reference-counted DNS cache slab headers +- Resolves: RHEL-213396 + +* Thu Jul 23 2026 RHEL Packaging Agent - 32:9.16.23-0.22.9 +- Fix CVE-2026-11721: RRSIG labels validation and out-of-zone signing +- Add new unit test +- Resolves: RHEL-213406 + +* Thu Jul 23 2026 RHEL Packaging Agent - 32:9.16.23-0.22.8 +- Fix RPZ name-too-long wildcard expansion (CVE-2026-11331) +- Add upstream rpz system test +- Resolves: RHEL-213478 + +* Thu Jul 23 2026 RHEL Packaging Agent - 32:9.16.23-0.22.7 +- Fix CVE-2026-13204: ensure NSEC/NSEC3 has matching RRSIG +- Resolves: RHEL-213478 + * Mon May 25 2026 Petr Menšík - 32:9.16.23-0.22.6 - Fix GSS-API resource leak (CVE-2026-3039) - Invalid handling of CLASS != IN (CVE-2026-5946)