import UBI bind-9.18.33-15.el10_2.10
This commit is contained in:
parent
131c6587b0
commit
c9ad8bab07
63
bind-9.18-CVE-2026-10723.patch
Normal file
63
bind-9.18-CVE-2026-10723.patch
Normal file
@ -0,0 +1,63 @@
|
||||
From 01bb483cbf162b79dde64f7005f2cbc76aaee96e Mon Sep 17 00:00:00 2001
|
||||
From: Evan Hunt <each@isc.org>
|
||||
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/isc/result.c | 2 +-
|
||||
2 files changed, 18 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
|
||||
index 662136db87..d6259f939e 100644
|
||||
--- a/lib/dns/dnssec.c
|
||||
+++ b/lib/dns/dnssec.c
|
||||
@@ -424,10 +424,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 DNSSKEY 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/isc/result.c b/lib/isc/result.c
|
||||
index 493c1a5309..5e242ac285 100644
|
||||
--- a/lib/isc/result.c
|
||||
+++ b/lib/isc/result.c
|
||||
@@ -198,7 +198,7 @@ static const char *description[ISC_R_NRESULTS] = {
|
||||
[DNS_R_COVERINGNSEC] = "covering NSEC record returned",
|
||||
[DNS_R_MXISADDRESS] = "MX is an address",
|
||||
[DNS_R_DUPLICATE] = "duplicate query",
|
||||
- [DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name (wildcard)",
|
||||
+ [DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name",
|
||||
[DNS_R_NOTPRIMARY] = "not primary",
|
||||
[DNS_R_BROKENCHAIN] = "broken trust chain",
|
||||
[DNS_R_EXPIRED] = "expired",
|
||||
69
bind-9.18-CVE-2026-11331-test.patch
Normal file
69
bind-9.18-CVE-2026-11331-test.patch
Normal file
@ -0,0 +1,69 @@
|
||||
From 8a54e87eee8ccb80f6b96c211c5d655b64feb28e Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
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 adc8285d23e2eac6ec463f5dbc5a9596fdd36c60)
|
||||
|
||||
Check rpz name too long wildcard CNAME expansion handling
|
||||
|
||||
(cherry picked from commit 9345394e2097031b55b3ef34ceaadf5a7ebbeef2)
|
||||
(cherry picked from commit 095b11f20f911f5b8059bdc349b256d6c64ece30)
|
||||
---
|
||||
bin/tests/system/rpz/ns2/tld2.db | 2 ++
|
||||
bin/tests/system/rpz/ns4/tld4.db | 2 ++
|
||||
bin/tests/system/rpz/tests.sh | 7 +++++--
|
||||
3 files changed, 9 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/bin/tests/system/rpz/ns2/tld2.db b/bin/tests/system/rpz/ns2/tld2.db
|
||||
index c6f2556db5..c091ee27b7 100644
|
||||
--- a/bin/tests/system/rpz/ns2/tld2.db
|
||||
+++ b/bin/tests/system/rpz/ns2/tld2.db
|
||||
@@ -123,3 +123,5 @@ a7-1 A 192.168.7.1
|
||||
|
||||
a7-2 A 192.168.7.2
|
||||
TXT "a7-2 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 fca419c6dd..8accd76baf 100644
|
||||
--- a/bin/tests/system/rpz/ns4/tld4.db
|
||||
+++ b/bin/tests/system/rpz/ns4/tld4.db
|
||||
@@ -59,6 +59,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 2404c9f4ba..67b6710c16 100644
|
||||
--- a/bin/tests/system/rpz/tests.sh
|
||||
+++ b/bin/tests/system/rpz/tests.sh
|
||||
@@ -391,7 +391,7 @@ addr() {
|
||||
digcmd $2 >$DIGNM
|
||||
#ckalive "$2" "server crashed by 'dig $2'" || return 1
|
||||
ADDR_ESC=$(echo "$ADDR" | sed -e 's/\./\\./g')
|
||||
- ADDR_TTL=$(sed -n -e "s/^[-.a-z0-9]\{1,\}[ ]*\([0-9]*\) IN AA* ${ADDR_ESC}\$/\1/p" $DIGNM)
|
||||
+ ADDR_TTL=$(sed -n -e "s/^[-.a-z0-9]\{1,\}[ ]*\([0-9]*\)[ ]IN[ ]AA*[ ]${ADDR_ESC}\$/\1/p" $DIGNM)
|
||||
if test -z "$ADDR_TTL"; then
|
||||
setret "'dig $2' wrong; no address $ADDR record in $DIGNM"
|
||||
return 0
|
||||
@@ -516,7 +516,10 @@ nochange TCP a3-9.tld2 # 33 tcp-only
|
||||
here x.servfail <<'EOF' # 34 qname-wait-recurse yes
|
||||
;; status: SERVFAIL, x
|
||||
EOF
|
||||
-addr 35.35.35.35 "x.servfail @$ns5" # 35 qname-wait-recurse no
|
||||
+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
|
||||
|
||||
27
bind-9.18-CVE-2026-11331.patch
Normal file
27
bind-9.18-CVE-2026-11331.patch
Normal file
@ -0,0 +1,27 @@
|
||||
From 713060f54dd7da6aa18fdd57e2cf5f6603ddd6d0 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
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 | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/ns/query.c b/lib/ns/query.c
|
||||
index f7f0623713..609ef50739 100644
|
||||
--- a/lib/ns/query.c
|
||||
+++ b/lib/ns/query.c
|
||||
@@ -7575,7 +7575,8 @@ 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) {
|
||||
+ }
|
||||
+ if (result != ISC_R_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
279
bind-9.18-CVE-2026-11622.patch
Normal file
279
bind-9.18-CVE-2026-11622.patch
Normal file
@ -0,0 +1,279 @@
|
||||
From be220a81691c7625a59aa8036188a1207bc07377 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@sury.org>
|
||||
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 8cc8fe1..726b5c8 100644
|
||||
--- a/bin/tests/system/reclimit/tests.sh
|
||||
+++ b/bin/tests/system/reclimit/tests.sh
|
||||
@@ -337,13 +337,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 5729c00..6bd3b59 100644
|
||||
--- a/lib/dns/include/dns/rdataslab.h
|
||||
+++ b/lib/dns/include/dns/rdataslab.h
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/lang.h>
|
||||
+#include <isc/refcount.h>
|
||||
|
||||
#include <dns/types.h>
|
||||
|
||||
diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
|
||||
index dadb273..d7f55cd 100644
|
||||
--- a/lib/dns/rbtdb.c
|
||||
+++ b/lib/dns/rbtdb.c
|
||||
@@ -165,6 +165,7 @@ struct noqname {
|
||||
};
|
||||
|
||||
typedef struct rdatasetheader {
|
||||
+ isc_refcount_t references;
|
||||
/*%
|
||||
* Locked by the owning node's lock.
|
||||
*/
|
||||
@@ -1452,6 +1453,7 @@ init_rdataset(dns_rbtdb_t *rbtdb, rdatasetheader_t *h) {
|
||||
h->heap_index = 0;
|
||||
atomic_init(&h->attributes, 0);
|
||||
atomic_init(&h->last_refresh_fail_ts, 0);
|
||||
+ isc_refcount_init(&h->references, 1);
|
||||
|
||||
STATIC_ASSERT((sizeof(h->attributes) == 2),
|
||||
"The .attributes field of rdatasetheader_t needs to be "
|
||||
@@ -1564,6 +1566,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 void
|
||||
mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
|
||||
uint_least16_t attributes = atomic_load_acquire(&header->attributes);
|
||||
@@ -1589,8 +1594,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 void
|
||||
@@ -1626,12 +1635,19 @@ static 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 void
|
||||
@@ -1647,6 +1663,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.
|
||||
@@ -3101,6 +3118,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. */
|
||||
@@ -6290,6 +6309,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;
|
||||
@@ -6837,6 +6857,7 @@ find_header:
|
||||
}
|
||||
|
||||
if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) {
|
||||
+ do_expireheader = true;
|
||||
if (expireheader == NULL) {
|
||||
expireheader = newheader;
|
||||
}
|
||||
@@ -6850,15 +6871,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.
|
||||
- */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6882,6 +6894,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;
|
||||
}
|
||||
|
||||
@@ -8677,6 +8698,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);
|
||||
}
|
||||
|
||||
@@ -8791,6 +8818,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);
|
||||
@@ -8954,6 +8986,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);
|
||||
@@ -9031,9 +9068,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) {
|
||||
@@ -9125,9 +9171,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) {
|
||||
--
|
||||
2.55.0
|
||||
|
||||
40
bind-9.18-CVE-2026-11721-fix2.patch
Normal file
40
bind-9.18-CVE-2026-11721-fix2.patch
Normal file
@ -0,0 +1,40 @@
|
||||
From 0532ea83b5e806e0d5b0b1fa85760c8d680ac173 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
Date: Tue, 14 Apr 2026 15:14:06 +1000
|
||||
Subject: [PATCH] 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)
|
||||
(cherry picked from commit 1a4986e2533f87e80eb21da3f06708d335aff1e2)
|
||||
---
|
||||
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 b2c77d497a..d968f95d5e 100644
|
||||
--- a/bin/dnssec/dnssec-signzone.c
|
||||
+++ b/bin/dnssec/dnssec-signzone.c
|
||||
@@ -1643,6 +1643,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.
|
||||
--
|
||||
2.55.0
|
||||
|
||||
144
bind-9.18-CVE-2026-11721-test.patch
Normal file
144
bind-9.18-CVE-2026-11721-test.patch
Normal file
@ -0,0 +1,144 @@
|
||||
From 247b1944f3e658f9cf412dc986d3de39aa81e67f Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
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 19e496ca260b6a756ae1378e8ebcbdb666b7d9ed)
|
||||
---
|
||||
tests/dns/rdata_test.c | 110 +++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 110 insertions(+)
|
||||
|
||||
diff --git a/tests/dns/rdata_test.c b/tests/dns/rdata_test.c
|
||||
index 921f3f0591..26f1dd901f 100644
|
||||
--- a/tests/dns/rdata_test.c
|
||||
+++ b/tests/dns/rdata_test.c
|
||||
@@ -2302,6 +2302,115 @@ ISC_RUN_TEST_IMPL(rkey) {
|
||||
dns_rdatatype_rkey, sizeof(dns_rdata_rkey_t));
|
||||
}
|
||||
|
||||
+ISC_RUN_TEST_IMPL(rrsig) {
|
||||
+ 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()
|
||||
+ };
|
||||
+ check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in,
|
||||
+ dns_rdatatype_rrsig, sizeof(dns_rdata_rrsig_t));
|
||||
+}
|
||||
+
|
||||
ISC_RUN_TEST_IMPL(resinfo) {
|
||||
text_ok_t text_ok[] = {
|
||||
TEXT_VALID_CHANGED("qnamemin exterr=15,16,17 "
|
||||
@@ -3152,6 +3261,7 @@ ISC_TEST_ENTRY(nsec3)
|
||||
ISC_TEST_ENTRY(nxt)
|
||||
ISC_TEST_ENTRY(rkey)
|
||||
ISC_TEST_ENTRY(resinfo)
|
||||
+ISC_TEST_ENTRY(rrsig)
|
||||
ISC_TEST_ENTRY(sshfp)
|
||||
ISC_TEST_ENTRY(wallet)
|
||||
ISC_TEST_ENTRY(wks)
|
||||
--
|
||||
2.55.0
|
||||
|
||||
236
bind-9.18-CVE-2026-11721.patch
Normal file
236
bind-9.18-CVE-2026-11721.patch
Normal file
@ -0,0 +1,236 @@
|
||||
From 107fba6767a792bf9022dbfc8a75e3dc61c0d76d Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
Date: Tue, 14 Apr 2026 12:24:33 +1000
|
||||
Subject: [PATCH] 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)
|
||||
(cherry picked from commit 15089066b15f826d7487c3d160b5872820f84b83)
|
||||
---
|
||||
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 662136db87..86f03680e9 100644
|
||||
--- a/lib/dns/dnssec.c
|
||||
+++ b/lib/dns/dnssec.c
|
||||
@@ -137,11 +137,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);
|
||||
|
||||
@@ -195,12 +195,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);
|
||||
@@ -240,7 +242,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--;
|
||||
}
|
||||
@@ -384,11 +386,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);
|
||||
@@ -403,6 +408,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;
|
||||
@@ -482,10 +502,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));
|
||||
}
|
||||
@@ -496,7 +515,7 @@ again:
|
||||
* Create an envelope for each rdata: <name|type|class|ttl>.
|
||||
*/
|
||||
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);
|
||||
@@ -592,7 +611,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 2cc315bdea..53648f1bfa 100644
|
||||
--- a/lib/dns/rdata/generic/rrsig_46.c
|
||||
+++ b/lib/dns/rdata/generic/rrsig_46.c
|
||||
@@ -23,12 +23,12 @@
|
||||
static 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;
|
||||
|
||||
@@ -61,8 +61,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.
|
||||
@@ -72,8 +72,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.
|
||||
@@ -144,12 +144,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.
|
||||
@@ -278,6 +286,7 @@ static isc_result_t
|
||||
fromwire_rrsig(ARGS_FROMWIRE) {
|
||||
isc_region_t sr;
|
||||
dns_name_t name;
|
||||
+ unsigned char labels;
|
||||
|
||||
REQUIRE(type == dns_rdatatype_rrsig);
|
||||
|
||||
@@ -300,6 +309,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));
|
||||
|
||||
@@ -309,6 +320,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.
|
||||
*/
|
||||
--
|
||||
2.55.0
|
||||
|
||||
186
bind-9.18-CVE-2026-13204.patch
Normal file
186
bind-9.18-CVE-2026-13204.patch
Normal file
@ -0,0 +1,186 @@
|
||||
From d190532c319cf4e3b84000f4e389596f2af98fd4 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Hunt <each@isc.org>
|
||||
Date: Wed, 13 May 2026 20:45:57 -0700
|
||||
Subject: [PATCH 1/2] 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 | 3 +--
|
||||
4 files changed, 27 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
|
||||
index a341b007c7..dadb273bec 100644
|
||||
--- a/lib/dns/rbtdb.c
|
||||
+++ b/lib/dns/rbtdb.c
|
||||
@@ -6906,7 +6906,7 @@ delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
||||
static 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;
|
||||
@@ -6918,7 +6918,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);
|
||||
@@ -6944,7 +6946,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 971e4e656c..c6d77c75dd 100644
|
||||
--- a/lib/dns/rdatalist.c
|
||||
+++ b/lib/dns/rdatalist.c
|
||||
@@ -192,6 +192,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);
|
||||
@@ -199,30 +200,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) {
|
||||
- continue;
|
||||
- }
|
||||
- if (rdset->type == dns_rdatatype_nsec ||
|
||||
- rdset->type == dns_rdatatype_nsec3)
|
||||
+ if (rdset->rdclass != rdataset->rdclass ||
|
||||
+ (rdset->type != dns_rdatatype_nsec &&
|
||||
+ rdset->type != dns_rdatatype_nsec3))
|
||||
{
|
||||
- neg = rdset;
|
||||
+ continue;
|
||||
}
|
||||
- }
|
||||
- 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)
|
||||
+ for (sigset = ISC_LIST_HEAD(name->list); sigset != NULL;
|
||||
+ sigset = ISC_LIST_NEXT(sigset, link))
|
||||
{
|
||||
- negsig = rdset;
|
||||
+ 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;
|
||||
}
|
||||
/*
|
||||
@@ -238,6 +236,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 1326cb3428..a4efc9a847 100644
|
||||
--- a/lib/dns/resolver.c
|
||||
+++ b/lib/dns/resolver.c
|
||||
@@ -5831,7 +5831,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 f7f0623713..f0239b20b9 100644
|
||||
--- a/lib/ns/query.c
|
||||
+++ b/lib/ns/query.c
|
||||
@@ -7936,8 +7936,7 @@ query_addnoqnameproof(query_ctx_t *qctx) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
- result = dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig);
|
||||
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
+ CHECK(dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig));
|
||||
|
||||
query_addrrset(qctx, &fname, &neg, &negsig, dbuf,
|
||||
DNS_SECTION_AUTHORITY);
|
||||
|
||||
From 8051316fde01f4a4368e8a4cba24e6d2d483e5ce Mon Sep 17 00:00:00 2001
|
||||
From: RHEL Packaging Agent <redhat-ymir-agent@redhat.com>
|
||||
Date: Thu, 23 Jul 2026 10:26:08 +0000
|
||||
Subject: [PATCH 2/2] Fix: replace CHECK macro with inline equivalent in
|
||||
query.c
|
||||
|
||||
The CHECK macro is not defined in lib/ns/query.c (it is only defined
|
||||
locally in some other source files). Replace with inline equivalent
|
||||
that checks the result and goes to the cleanup label on failure.
|
||||
This preserves the security fix semantics: instead of crashing with
|
||||
RUNTIME_CHECK on error, gracefully jump to cleanup.
|
||||
---
|
||||
lib/ns/query.c | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/ns/query.c b/lib/ns/query.c
|
||||
index f0239b20b9..f265abfcef 100644
|
||||
--- a/lib/ns/query.c
|
||||
+++ b/lib/ns/query.c
|
||||
@@ -7936,7 +7936,10 @@ query_addnoqnameproof(query_ctx_t *qctx) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
- CHECK(dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig));
|
||||
+ result = dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig);
|
||||
+ if (result != ISC_R_SUCCESS) {
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
|
||||
query_addrrset(qctx, &fname, &neg, &negsig, dbuf,
|
||||
DNS_SECTION_AUTHORITY);
|
||||
766
bind-9.18-CVE-2026-13321.patch
Normal file
766
bind-9.18-CVE-2026-13321.patch
Normal file
@ -0,0 +1,766 @@
|
||||
From 384331c4c560e658ce5349afc7e556e939f5301e Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ayd=C4=B1n=20Mercan?= <aydin@isc.org>
|
||||
Date: Wed, 6 May 2026 16:54:57 +0300
|
||||
Subject: [PATCH 1/4] Add system test for out-of-zone nsec dnssec bypass
|
||||
|
||||
A malicious zone with out-of-zone NSEC entries can get a DNSSEC
|
||||
validating resolver's cache to cover the victim zone for non-existence
|
||||
and prevent nameserver queries without DNSSEC failure.
|
||||
|
||||
Test for this case with an `evil.test` zone that tries to cover the
|
||||
`victim.test` zone.
|
||||
|
||||
(cherry picked from commit 654f9773c0af59965c343bdfeb096b3dffe9dd53)
|
||||
---
|
||||
.../system/dnssec_bypass/ns1/named.conf.j2 | 31 ++++
|
||||
bin/tests/system/dnssec_bypass/ns1/root.db | 19 +++
|
||||
bin/tests/system/dnssec_bypass/ns1/test.db | 23 +++
|
||||
.../system/dnssec_bypass/ns2/named.conf.j2 | 26 +++
|
||||
bin/tests/system/dnssec_bypass/ns2/victim.db | 18 +++
|
||||
bin/tests/system/dnssec_bypass/ns3/evil.db | 23 +++
|
||||
.../system/dnssec_bypass/ns3/named.conf.j2 | 26 +++
|
||||
.../system/dnssec_bypass/ns4/named.conf.j2 | 35 ++++
|
||||
.../system/dnssec_bypass/tests_bypass.py | 152 ++++++++++++++++++
|
||||
9 files changed, 353 insertions(+)
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/ns1/named.conf.j2
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/ns1/root.db
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/ns1/test.db
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/ns2/named.conf.j2
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/ns2/victim.db
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/ns3/evil.db
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/ns3/named.conf.j2
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/ns4/named.conf.j2
|
||||
create mode 100644 bin/tests/system/dnssec_bypass/tests_bypass.py
|
||||
|
||||
diff --git a/bin/tests/system/dnssec_bypass/ns1/named.conf.j2 b/bin/tests/system/dnssec_bypass/ns1/named.conf.j2
|
||||
new file mode 100644
|
||||
index 0000000..59ced18
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/ns1/named.conf.j2
|
||||
@@ -0,0 +1,31 @@
|
||||
+/*
|
||||
+ * 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 https://mozilla.org/MPL/2.0/.
|
||||
+ *
|
||||
+ * See the COPYRIGHT file distributed with this work for additional
|
||||
+ * information regarding copyright ownership.
|
||||
+ */
|
||||
+
|
||||
+options {
|
||||
+ port @PORT@;
|
||||
+ pid-file "named.pid";
|
||||
+ listen-on { 10.53.0.1; };
|
||||
+ listen-on-v6 { none; };
|
||||
+ recursion no;
|
||||
+ dnssec-validation no;
|
||||
+};
|
||||
+
|
||||
+zone "test." {
|
||||
+ type primary;
|
||||
+ file "test.db.signed";
|
||||
+};
|
||||
+
|
||||
+zone "." {
|
||||
+ type primary;
|
||||
+ file "root.db.signed";
|
||||
+};
|
||||
diff --git a/bin/tests/system/dnssec_bypass/ns1/root.db b/bin/tests/system/dnssec_bypass/ns1/root.db
|
||||
new file mode 100644
|
||||
index 0000000..8d98a04
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/ns1/root.db
|
||||
@@ -0,0 +1,19 @@
|
||||
+; 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 https://mozilla.org/MPL/2.0/.
|
||||
+;
|
||||
+; See the COPYRIGHT file distributed with this work for additional
|
||||
+; information regarding copyright ownership.
|
||||
+
|
||||
+$TTL 3600
|
||||
+. IN SOA a.nil. a.nil. 1 3600 600 86400 300
|
||||
+. IN NS a.root-servers.nil.
|
||||
+
|
||||
+a.root-servers.nil. IN A 10.53.0.1
|
||||
+
|
||||
+test. IN NS ns1.test.
|
||||
+ns1.test. IN A 10.53.0.1
|
||||
diff --git a/bin/tests/system/dnssec_bypass/ns1/test.db b/bin/tests/system/dnssec_bypass/ns1/test.db
|
||||
new file mode 100644
|
||||
index 0000000..6efcd95
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/ns1/test.db
|
||||
@@ -0,0 +1,23 @@
|
||||
+; 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 https://mozilla.org/MPL/2.0/.
|
||||
+;
|
||||
+; See the COPYRIGHT file distributed with this work for additional
|
||||
+; information regarding copyright ownership.
|
||||
+
|
||||
+$ORIGIN test.
|
||||
+$TTL 3600
|
||||
+
|
||||
+@ IN SOA a a 1 3600 600 86400 300
|
||||
+ IN NS ns1.test.
|
||||
+ns1 IN A 10.53.0.1
|
||||
+
|
||||
+evil IN NS ns1.evil
|
||||
+ns1.evil IN A 10.53.0.3
|
||||
+
|
||||
+victim IN NS ns1.victim
|
||||
+ns1.victim IN A 10.53.0.2
|
||||
diff --git a/bin/tests/system/dnssec_bypass/ns2/named.conf.j2 b/bin/tests/system/dnssec_bypass/ns2/named.conf.j2
|
||||
new file mode 100644
|
||||
index 0000000..e81cee7
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/ns2/named.conf.j2
|
||||
@@ -0,0 +1,26 @@
|
||||
+/*
|
||||
+ * 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 https://mozilla.org/MPL/2.0/.
|
||||
+ *
|
||||
+ * See the COPYRIGHT file distributed with this work for additional
|
||||
+ * information regarding copyright ownership.
|
||||
+ */
|
||||
+
|
||||
+options {
|
||||
+ port @PORT@;
|
||||
+ pid-file "named.pid";
|
||||
+ listen-on { 10.53.0.2; };
|
||||
+ listen-on-v6 { none; };
|
||||
+ recursion no;
|
||||
+ dnssec-validation no;
|
||||
+};
|
||||
+
|
||||
+zone "victim.test." {
|
||||
+ type primary;
|
||||
+ file "victim.db.signed";
|
||||
+};
|
||||
diff --git a/bin/tests/system/dnssec_bypass/ns2/victim.db b/bin/tests/system/dnssec_bypass/ns2/victim.db
|
||||
new file mode 100644
|
||||
index 0000000..edcc234
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/ns2/victim.db
|
||||
@@ -0,0 +1,18 @@
|
||||
+; 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 https://mozilla.org/MPL/2.0/.
|
||||
+;
|
||||
+; See the COPYRIGHT file distributed with this work for additional
|
||||
+; information regarding copyright ownership.
|
||||
+
|
||||
+$ORIGIN victim.test.
|
||||
+$TTL 3600
|
||||
+
|
||||
+@ IN SOA ns1 hostmaster 1 3600 600 86400 2147483647
|
||||
+ IN NS ns1
|
||||
+
|
||||
+ns1 IN A 10.53.0.2
|
||||
diff --git a/bin/tests/system/dnssec_bypass/ns3/evil.db b/bin/tests/system/dnssec_bypass/ns3/evil.db
|
||||
new file mode 100644
|
||||
index 0000000..618f9d3
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/ns3/evil.db
|
||||
@@ -0,0 +1,23 @@
|
||||
+; 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 https://mozilla.org/MPL/2.0/.
|
||||
+;
|
||||
+; See the COPYRIGHT file distributed with this work for additional
|
||||
+; information regarding copyright ownership.
|
||||
+
|
||||
+$ORIGIN evil.test.
|
||||
+$TTL 300
|
||||
+
|
||||
+@ IN SOA ns1 hostmaster 1 3600 600 86400 300
|
||||
+ IN NS ns1
|
||||
+; Try to poison the victim zone in a resolver cache.
|
||||
+; If admitted, the aggressive NSEC cache will accept a range such as
|
||||
+; [evil.test, b.victim.test) and will cause the victim nameserver to
|
||||
+; be never queried.
|
||||
+ IN NSEC b.victim.test. NS SOA RRSIG NSEC DNSKEY
|
||||
+
|
||||
+ns1 IN A 10.53.0.3
|
||||
diff --git a/bin/tests/system/dnssec_bypass/ns3/named.conf.j2 b/bin/tests/system/dnssec_bypass/ns3/named.conf.j2
|
||||
new file mode 100644
|
||||
index 0000000..17d3e18
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/ns3/named.conf.j2
|
||||
@@ -0,0 +1,26 @@
|
||||
+/*
|
||||
+ * 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 https://mozilla.org/MPL/2.0/.
|
||||
+ *
|
||||
+ * See the COPYRIGHT file distributed with this work for additional
|
||||
+ * information regarding copyright ownership.
|
||||
+ */
|
||||
+
|
||||
+options {
|
||||
+ port @PORT@;
|
||||
+ pid-file "named.pid";
|
||||
+ listen-on { 10.53.0.3; };
|
||||
+ listen-on-v6 { none; };
|
||||
+ recursion no;
|
||||
+ dnssec-validation no;
|
||||
+};
|
||||
+
|
||||
+zone "evil.test." {
|
||||
+ type primary;
|
||||
+ file "evil.db.signed";
|
||||
+};
|
||||
diff --git a/bin/tests/system/dnssec_bypass/ns4/named.conf.j2 b/bin/tests/system/dnssec_bypass/ns4/named.conf.j2
|
||||
new file mode 100644
|
||||
index 0000000..039695d
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/ns4/named.conf.j2
|
||||
@@ -0,0 +1,35 @@
|
||||
+/*
|
||||
+ * 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 https://mozilla.org/MPL/2.0/.
|
||||
+ *
|
||||
+ * See the COPYRIGHT file distributed with this work for additional
|
||||
+ * information regarding copyright ownership.
|
||||
+ */
|
||||
+
|
||||
+options {
|
||||
+ query-source address 10.53.0.4;
|
||||
+ notify-source 10.53.0.4;
|
||||
+ transfer-source 10.53.0.4;
|
||||
+ port @PORT@;
|
||||
+ pid-file "named.pid";
|
||||
+ listen-on { 10.53.0.4; };
|
||||
+ listen-on-v6 { none; };
|
||||
+ allow-transfer { any; };
|
||||
+ recursion yes;
|
||||
+ dnssec-validation yes;
|
||||
+ synth-from-dnssec yes;
|
||||
+};
|
||||
+
|
||||
+trust-anchors {
|
||||
+ @root.domain@ @root.type@ @root.contents@;
|
||||
+};
|
||||
+
|
||||
+zone "." {
|
||||
+ type hint;
|
||||
+ file "../../_common/root.hint";
|
||||
+};
|
||||
diff --git a/bin/tests/system/dnssec_bypass/tests_bypass.py b/bin/tests/system/dnssec_bypass/tests_bypass.py
|
||||
new file mode 100644
|
||||
index 0000000..c41bb7e
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec_bypass/tests_bypass.py
|
||||
@@ -0,0 +1,152 @@
|
||||
+# 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 https://mozilla.org/MPL/2.0/.
|
||||
+#
|
||||
+# See the COPYRIGHT file distributed with this work for additional
|
||||
+# information regarding copyright ownership.
|
||||
+
|
||||
+from datetime import datetime, timedelta, timezone
|
||||
+
|
||||
+import shutil
|
||||
+
|
||||
+from cryptography.hazmat.primitives.asymmetric import ec
|
||||
+
|
||||
+import dns.dnssec
|
||||
+import dns.name
|
||||
+import dns.rdataclass
|
||||
+import dns.rdataset
|
||||
+import dns.rdatatype
|
||||
+import dns.rrset
|
||||
+import dns.zone
|
||||
+
|
||||
+from isctest.run import EnvCmd
|
||||
+
|
||||
+import isctest
|
||||
+
|
||||
+TTL = 3600
|
||||
+
|
||||
+
|
||||
+def bootstrap():
|
||||
+ keygen = EnvCmd("KEYGEN", "-q -a ECDSA256")
|
||||
+ signer = EnvCmd("SIGNER", "-S -g -O full")
|
||||
+
|
||||
+ def sign_regular_zone(ns: str, zone: str, database: str) -> isctest.kasp.Key:
|
||||
+ isctest.log.info(f"{zone}: generate keys")
|
||||
+ keygen(zone, cwd=ns).out.strip()
|
||||
+ ksk = keygen(f"-f KSK {zone}", cwd=ns).out.strip()
|
||||
+
|
||||
+ isctest.log.info(f"{zone}: sign zone")
|
||||
+ signer(f"-o {zone} {database}", cwd=ns)
|
||||
+
|
||||
+ if ns != "ns1":
|
||||
+ shutil.copy(f"{ns}/dsset-{zone}", f"ns1/dsset-{zone}")
|
||||
+ shutil.copy(f"{ns}/{ksk}.key", f"ns1/{ksk}.key")
|
||||
+
|
||||
+ return isctest.kasp.Key(ksk, keydir=ns)
|
||||
+
|
||||
+ # dnssec-signzone and `dns.dnssec.sign_zone` correctly disregard the invalid
|
||||
+ # NSEC entries when signing the zone. However, for this test we actualy *want*
|
||||
+ # to serve invalid yet signed zones. To accomplish this we sign the zone and then
|
||||
+ # replace the correct entries with the faulty ones accompanied by its RRSIG.
|
||||
+ #
|
||||
+ # TODO(aydin): move this to `isctest` to sign broken zones
|
||||
+ def sign_rogue_zone(ns: str, zone: str, database: str) -> None:
|
||||
+ # Read zone.
|
||||
+ origin = dns.name.from_text(zone)
|
||||
+ data = dns.zone.from_file(f"{ns}/{database}", origin=origin, relativize=False)
|
||||
+
|
||||
+ # Get key for signing.
|
||||
+ isctest.log.info(f"{zone}: generate keys")
|
||||
+ private_key = ec.generate_private_key(ec.SECP256R1())
|
||||
+ dnskey = dns.dnssec.make_dnskey(
|
||||
+ public_key=private_key.public_key(),
|
||||
+ algorithm=dns.dnssec.Algorithm.ECDSAP256SHA256,
|
||||
+ flags=257,
|
||||
+ )
|
||||
+
|
||||
+ # Sign zone.
|
||||
+ isctest.log.info(f"{zone}: sign zone")
|
||||
+ now = datetime.now(timezone.utc)
|
||||
+ inception = now - timedelta(hours=1)
|
||||
+ expiration = now + timedelta(days=30)
|
||||
+
|
||||
+ for name, node in data.nodes.items():
|
||||
+ owner = name.derelativize(origin)
|
||||
+ rdatasets = list(node.rdatasets)
|
||||
+
|
||||
+ for rdataset in rdatasets:
|
||||
+ rrset = dns.rrset.RRset(owner, rdataset.rdclass, rdataset.rdtype)
|
||||
+ rrset.update(rdataset)
|
||||
+
|
||||
+ rrsig = dns.dnssec.sign(
|
||||
+ rrset=rrset,
|
||||
+ private_key=private_key,
|
||||
+ signer=origin,
|
||||
+ dnskey=dnskey,
|
||||
+ inception=inception,
|
||||
+ expiration=expiration,
|
||||
+ deterministic=False,
|
||||
+ )
|
||||
+
|
||||
+ rdataset = dns.rdataset.Rdataset(rrset.rdclass, dns.rdatatype.RRSIG)
|
||||
+ rdataset.add(rrsig, rrset.ttl)
|
||||
+ node.replace_rdataset(rdataset)
|
||||
+
|
||||
+ # Sign DNSKEY RRset.
|
||||
+ dnskey_rrset = dns.rrset.RRset(origin, dns.rdataclass.IN, dns.rdatatype.DNSKEY)
|
||||
+ dnskey_rrset.add(dnskey, ttl=TTL)
|
||||
+
|
||||
+ apex_node = data.nodes[origin]
|
||||
+ apex_node.replace_rdataset(dnskey_rrset)
|
||||
+
|
||||
+ rrsig = dns.dnssec.sign(
|
||||
+ rrset=dnskey_rrset,
|
||||
+ private_key=private_key,
|
||||
+ signer=origin,
|
||||
+ dnskey=dnskey,
|
||||
+ inception=inception,
|
||||
+ expiration=expiration,
|
||||
+ deterministic=False,
|
||||
+ )
|
||||
+ rdataset = dns.rdataset.Rdataset(rrset.rdclass, dns.rdatatype.RRSIG)
|
||||
+ rdataset.add(rrsig, dnskey_rrset.ttl)
|
||||
+ apex_node.replace_rdataset(rdataset)
|
||||
+
|
||||
+ # Output zone.
|
||||
+ data.to_file(f"{ns}/{database}.signed", relativize=False)
|
||||
+
|
||||
+ # Output DS.
|
||||
+ ds = dns.dnssec.make_ds(name=origin, key=dnskey, algorithm="SHA256")
|
||||
+ with open(f"ns1/dsset-{zone}", "w", encoding="utf-8") as f:
|
||||
+ f.write(f"{zone} {TTL} IN DS {ds.to_text()}\n")
|
||||
+
|
||||
+ sign_rogue_zone("ns3", "evil.test.", "evil.db")
|
||||
+ sign_regular_zone("ns2", "victim.test.", "victim.db")
|
||||
+ sign_regular_zone("ns1", "test.", "test.db")
|
||||
+ root_ksk = sign_regular_zone("ns1", ".", "root.db")
|
||||
+
|
||||
+ return {
|
||||
+ "root": root_ksk.into_ta("static-key"),
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+def test_out_of_zone_nsec(ns4):
|
||||
+ isctest.log.info("trying to poison aggressive nsec cache")
|
||||
+ msg = isctest.query.create("nx.evil.test", "A")
|
||||
+ res = isctest.query.tcp(msg, ns4.ip)
|
||||
+ isctest.check.noadflag(res)
|
||||
+
|
||||
+ isctest.log.info("query victim from recursive")
|
||||
+ msg = isctest.query.create("victim.test", "SOA")
|
||||
+ res = isctest.query.tcp(msg, ns4.ip, attempts=1)
|
||||
+ isctest.check.noerror(res)
|
||||
+ isctest.check.adflag(res)
|
||||
+ isctest.check.rr_count_eq(res.answer, 2)
|
||||
+
|
||||
+ isctest.log.info("checking for query history on victim nameserver")
|
||||
+ with open("ns2/named.run", "r", encoding="utf-8") as f:
|
||||
+ assert "(victim.test): query 'victim.test/SOA/IN' approved" in f.read()
|
||||
--
|
||||
2.55.0
|
||||
|
||||
From a01a313653ad35eccf30e0001dfcdca6b66884fa Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ayd=C4=B1n=20Mercan?= <aydin@isc.org>
|
||||
Date: Thu, 7 May 2026 18:59:20 +0300
|
||||
Subject: [PATCH 2/4] 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 | 13 +++++++++++++
|
||||
lib/dns/include/dns/dnssec.h | 6 ++++++
|
||||
2 files changed, 19 insertions(+)
|
||||
|
||||
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
|
||||
index 86f0368..9f2105e 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;
|
||||
@@ -469,6 +471,17 @@ 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) {
|
||||
+ RETERR(dns_rdataset_first(set));
|
||||
+ dns_rdataset_current(set, &rdata);
|
||||
+ RETERR(dns_rdata_tostruct(&rdata, &nsec, NULL));
|
||||
+ 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 cb8fd9d..2be11b9 100644
|
||||
--- a/lib/dns/include/dns/dnssec.h
|
||||
+++ b/lib/dns/include/dns/dnssec.h
|
||||
@@ -151,6 +151,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:
|
||||
@@ -173,6 +176,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_*
|
||||
*/
|
||||
|
||||
--
|
||||
2.55.0
|
||||
|
||||
From 6d2f2273b904471f8e1713ef6946e6ce6abad577 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ayd=C4=B1n=20Mercan?= <aydin@isc.org>
|
||||
Date: Tue, 12 May 2026 14:54:09 +0300
|
||||
Subject: [PATCH 3/4] change dns_nsec_requiredtypespresent to dns_nsec_is_legal
|
||||
|
||||
Change `dns_nsec_requiredtypespresent` to `dns_nsec_is_legal` as a
|
||||
function for checking multiple NSEC validity rules.
|
||||
|
||||
Currently we now additionally check for out-of-zone NSEC entries.
|
||||
|
||||
(cherry picked from commit be2a6a497312469890b552907d039d2de0b44ccc)
|
||||
---
|
||||
lib/dns/include/dns/nsec.h | 18 ++++++++++----
|
||||
lib/dns/nsec.c | 17 ++++++++++----
|
||||
lib/dns/resolver.c | 48 ++++++++++++++++++++++++++++++++++++--
|
||||
lib/ns/query.c | 6 ++---
|
||||
4 files changed, 75 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/lib/dns/include/dns/nsec.h b/lib/dns/include/dns/nsec.h
|
||||
index e68ea35..047e3c4 100644
|
||||
--- a/lib/dns/include/dns/nsec.h
|
||||
+++ b/lib/dns/include/dns/nsec.h
|
||||
@@ -114,13 +114,21 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, const dns_name_t *name,
|
||||
*/
|
||||
|
||||
bool
|
||||
-dns_nsec_requiredtypespresent(dns_rdataset_t *rdataset);
|
||||
-/*
|
||||
- * Return true if all the NSEC records in rdataset have both
|
||||
- * NSEC and RRSIG present.
|
||||
+dns_nsec_is_legal(dns_rdataset_t *rdataset, const dns_name_t *name);
|
||||
+/**<
|
||||
+ * \brief
|
||||
+ * Validates a rdataset of type NSEC.
|
||||
*
|
||||
- * Requires:
|
||||
+ * This functions checks for the following in the given rdataset:
|
||||
+ * \li All NSEC records have both NSEC and RRSIG present
|
||||
+ * \li All NSEC entries are under the `name`
|
||||
+ *
|
||||
+ * \par Requires:
|
||||
* \li rdataset to be a NSEC rdataset.
|
||||
+ * \li `name` is a valid dns_name_t
|
||||
+ *
|
||||
+ * \retval true if all the checks pass
|
||||
+ * \retval false otherwise
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
diff --git a/lib/dns/nsec.c b/lib/dns/nsec.c
|
||||
index fe53e2a..9fb79f8 100644
|
||||
--- a/lib/dns/nsec.c
|
||||
+++ b/lib/dns/nsec.c
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <isc/util.h>
|
||||
|
||||
#include <dns/db.h>
|
||||
+#include <dns/name.h>
|
||||
#include <dns/nsec.h>
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdatalist.h>
|
||||
@@ -504,8 +505,9 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, const dns_name_t *name,
|
||||
}
|
||||
|
||||
bool
|
||||
-dns_nsec_requiredtypespresent(dns_rdataset_t *nsecset) {
|
||||
- dns_rdataset_t rdataset;
|
||||
+dns_nsec_is_legal(dns_rdataset_t *nsecset, const dns_name_t *name) {
|
||||
+ dns_rdataset_t rdataset = DNS_RDATASET_INIT;
|
||||
+ dns_rdata_nsec_t nsec;
|
||||
isc_result_t result;
|
||||
bool found = false;
|
||||
|
||||
@@ -520,12 +522,19 @@ dns_nsec_requiredtypespresent(dns_rdataset_t *nsecset) {
|
||||
{
|
||||
dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||
dns_rdataset_current(&rdataset, &rdata);
|
||||
- if (!dns_nsec_typepresent(&rdata, dns_rdatatype_nsec) ||
|
||||
- !dns_nsec_typepresent(&rdata, dns_rdatatype_rrsig))
|
||||
+
|
||||
+ /* must never fail */
|
||||
+ result = dns_rdata_tostruct(&rdata, &nsec, NULL);
|
||||
+ INSIST(result == ISC_R_SUCCESS);
|
||||
+
|
||||
+ if (!dns_name_issubdomain(&nsec.next, name) ||
|
||||
+ !dns_nsec_typepresent(&rdata, dns_rdatatype_rrsig) ||
|
||||
+ !dns_nsec_typepresent(&rdata, dns_rdatatype_nsec))
|
||||
{
|
||||
dns_rdataset_disassociate(&rdataset);
|
||||
return false;
|
||||
}
|
||||
+
|
||||
found = true;
|
||||
}
|
||||
dns_rdataset_disassociate(&rdataset);
|
||||
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
|
||||
index a4efc9a..40480c5 100644
|
||||
--- a/lib/dns/resolver.c
|
||||
+++ b/lib/dns/resolver.c
|
||||
@@ -63,7 +63,9 @@
|
||||
#include <dns/rootns.h>
|
||||
#include <dns/stats.h>
|
||||
#include <dns/tsig.h>
|
||||
+#include <dns/types.h>
|
||||
#include <dns/validator.h>
|
||||
+#include <dns/view.h>
|
||||
#include <dns/zone.h>
|
||||
|
||||
/* Detailed logging of fctx attach/detach */
|
||||
@@ -5540,6 +5542,36 @@ has_000_label(dns_rdataset_t *nsecset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+static bool
|
||||
+get_and_check_signer_name(dns_name_t *signer, dns_rdataset_t *sigrdataset) {
|
||||
+ dns_rdata_rrsig_t rrsig;
|
||||
+ isc_result_t result;
|
||||
+ dns_rdata_t rdata;
|
||||
+
|
||||
+ if (dns_rdataset_first(sigrdataset) != ISC_R_SUCCESS) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ rdata = (dns_rdata_t)DNS_RDATA_INIT;
|
||||
+ dns_rdataset_current(sigrdataset, &rdata);
|
||||
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
|
||||
+ INSIST(result == ISC_R_SUCCESS);
|
||||
+ dns_name_copy(&rrsig.signer, signer);
|
||||
+
|
||||
+ while (dns_rdataset_next(sigrdataset) == ISC_R_SUCCESS) {
|
||||
+ rdata = (dns_rdata_t)DNS_RDATA_INIT;
|
||||
+ dns_rdataset_current(sigrdataset, &rdata);
|
||||
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
|
||||
+ INSIST(result == ISC_R_SUCCESS);
|
||||
+
|
||||
+ if (!dns_name_equal(signer, &rrsig.signer)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* The validator has finished.
|
||||
*/
|
||||
@@ -5570,6 +5602,8 @@ validated(isc_task_t *task, isc_event_t *event) {
|
||||
dns_fixedname_t fwild;
|
||||
dns_name_t *wild = NULL;
|
||||
dns_message_t *message = NULL;
|
||||
+ dns_fixedname_t fsigner;
|
||||
+ dns_name_t *signer = NULL;
|
||||
|
||||
UNUSED(task); /* for now */
|
||||
|
||||
@@ -5959,10 +5993,20 @@ answer_response:
|
||||
}
|
||||
|
||||
/*
|
||||
- * Don't cache NSEC if missing NSEC or RRSIG types.
|
||||
+ * Don't cache if all the RRSIGs don't have the same
|
||||
+ * signer.
|
||||
+ */
|
||||
+ signer = dns_fixedname_initname(&fsigner);
|
||||
+ if (!get_and_check_signer_name(signer, sigrdataset)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Don't cache NSEC if missing NSEC or RRSIG
|
||||
+ * types.
|
||||
*/
|
||||
if (rdataset->type == dns_rdatatype_nsec &&
|
||||
- !dns_nsec_requiredtypespresent(rdataset))
|
||||
+ !dns_nsec_is_legal(rdataset, signer))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
diff --git a/lib/ns/query.c b/lib/ns/query.c
|
||||
index 0f61fe1..2ef4111 100644
|
||||
--- a/lib/ns/query.c
|
||||
+++ b/lib/ns/query.c
|
||||
@@ -10356,10 +10356,10 @@ query_coveringnsec(query_ctx_t *qctx) {
|
||||
}
|
||||
|
||||
/*
|
||||
- * If NSEC or RRSIG are missing from the type map
|
||||
- * reject the NSEC RRset.
|
||||
+ * Check that the NSEC entry is legal.
|
||||
+ * (NSEC + RRSIG present and the entry isn't out-of-zone)
|
||||
*/
|
||||
- if (!dns_nsec_requiredtypespresent(qctx->rdataset)) {
|
||||
+ if (!dns_nsec_is_legal(qctx->rdataset, signer)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
--
|
||||
2.55.0
|
||||
|
||||
From 88d9e228ca7496bb1f971163249eeae848f35ee7 Mon Sep 17 00:00:00 2001
|
||||
From: RHEL Packaging Agent <redhat-ymir-agent@redhat.com>
|
||||
Date: Thu, 23 Jul 2026 13:15:20 +0000
|
||||
Subject: [PATCH 4/4] Fix RETERR usage in dns_dnssec_verify - use ret variable
|
||||
instead of result
|
||||
|
||||
The dns_dnssec_verify function uses 'ret' as its local variable name and
|
||||
has no 'failure:' label, but the RETERR macro assumes 'result' and
|
||||
'goto failure'. Replace RETERR() calls with direct error checking using
|
||||
the 'ret' variable and immediate returns.
|
||||
---
|
||||
lib/dns/dnssec.c | 10 ++++++++--
|
||||
1 file changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
|
||||
index 9f2105e..546df26 100644
|
||||
--- a/lib/dns/dnssec.c
|
||||
+++ b/lib/dns/dnssec.c
|
||||
@@ -475,9 +475,15 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
|
||||
* Check for out of zone NSEC entries.
|
||||
*/
|
||||
if (set->type == dns_rdatatype_nsec) {
|
||||
- RETERR(dns_rdataset_first(set));
|
||||
+ ret = dns_rdataset_first(set);
|
||||
+ if (ret != ISC_R_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
dns_rdataset_current(set, &rdata);
|
||||
- RETERR(dns_rdata_tostruct(&rdata, &nsec, NULL));
|
||||
+ ret = dns_rdata_tostruct(&rdata, &nsec, NULL);
|
||||
+ if (ret != ISC_R_SUCCESS) {
|
||||
+ return ret;
|
||||
+ }
|
||||
if (!dns_name_issubdomain(&nsec.next, &sig.signer)) {
|
||||
return DNS_R_NOVALIDNSEC;
|
||||
}
|
||||
--
|
||||
2.55.0
|
||||
|
||||
43
bind.spec
43
bind.spec
@ -80,7 +80,7 @@ License: MPL-2.0 AND ISC AND MIT AND BSD-3-Clause AND BSD-2-Clause
|
||||
# Before rebasing bind, ensure bind-dyndb-ldap is ready to be rebuild and use side-tag with it.
|
||||
# Updating just bind will cause freeipa-dns-server package to be uninstallable.
|
||||
Version: 9.18.33
|
||||
Release: 15%{?dist}.2
|
||||
Release: 15%{?dist}.10
|
||||
Epoch: 32
|
||||
Url: https://www.isc.org/downloads/bind/
|
||||
#
|
||||
@ -164,6 +164,25 @@ Patch229: bind-9.18-CVE-2026-1519-test.patch
|
||||
Patch230: bind-9.18-CVE-2026-3039.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/commit/7ce6ce37b1b04af0953ed2d3211587465085600e
|
||||
Patch231: bind-9.18-CVE-2026-5946.patch
|
||||
# https://github.com/isc-projects/bind9/commit/48f5aa5fb3746d6194edcc57e8792a8b3cc3b454
|
||||
Patch233: bind-9.18-CVE-2026-13204.patch
|
||||
# https://github.com/isc-projects/bind9/commit/dc328a199f96222e0c30cc20b7b795bfc2c9b2e4
|
||||
Patch234: bind-9.18-CVE-2026-11331.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/15089066b15f826d7487c3d160b5872820f84b83
|
||||
Patch235: bind-9.18-CVE-2026-11721.patch
|
||||
# https://github.com/isc-projects/bind9/commit/231b1ca3edfb26389e1af39181aa6b4413e87ec4
|
||||
Patch236: bind-9.18-CVE-2026-11622.patch
|
||||
# https://github.com/isc-projects/bind9/commit/36f3d50f9c8ebc8d25ee033e707ca502e20b083f
|
||||
Patch238: bind-9.18-CVE-2026-13321.patch
|
||||
# https://github.com/isc-projects/bind9/commit/c9cb6a5e24e43489cf3fd4d4cc2193b6a74499cb
|
||||
Patch239: bind-9.18-CVE-2026-10723.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/adc8285d23e2eac6ec463f5dbc5a9596fdd36c60
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/095b11f20f911f5b8059bdc349b256d6c64ece30
|
||||
Patch240: bind-9.18-CVE-2026-11331-test.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/19e496ca260b6a756ae1378e8ebcbdb666b7d9ed
|
||||
Patch241: bind-9.18-CVE-2026-11721-test.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/1a4986e2533f87e80eb21da3f06708d335aff1e2
|
||||
Patch242: bind-9.18-CVE-2026-11721-fix2.patch
|
||||
|
||||
%{?systemd_ordering}
|
||||
# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
|
||||
@ -593,6 +612,7 @@ mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig
|
||||
install -m 644 %{SOURCE1} ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/named
|
||||
install -m 644 %{SOURCE49} ${RPM_BUILD_ROOT}%{_sysconfdir}/named-chroot.files
|
||||
|
||||
%if "%{_bindir}" != "%{_sbindir}"
|
||||
pushd ${RPM_BUILD_ROOT}%{_sbindir}
|
||||
# Compatibility with previous major versions, only for selected binaries
|
||||
for BIN in named-checkconf named-checkzone named-compilezone
|
||||
@ -600,6 +620,7 @@ do
|
||||
ln -s ../bin/$BIN $BIN
|
||||
done
|
||||
popd
|
||||
%endif
|
||||
|
||||
# Remove libtool .la files:
|
||||
find ${RPM_BUILD_ROOT}/%{_libdir} -name '*.la' -exec '/bin/rm' '-f' '{}' ';';
|
||||
@ -965,6 +986,26 @@ fi;
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon Jul 27 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.18.33-15.10
|
||||
- Validate NSEC3 signer matches owning zone (CVE-2026-10723)
|
||||
|
||||
* Fri Jul 24 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.18.33-15.9
|
||||
- Reject out-of-zone NSEC next owner names (CVE-2026-13321)
|
||||
|
||||
* Thu Jul 23 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.18.33-15.7
|
||||
- Fix reference-counted dns_slabheaders in cache (CVE-2026-11622)
|
||||
|
||||
* Thu Jul 23 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.18.33-15.6
|
||||
- Fix RRSIG label count validation for wildcard cache poisoning
|
||||
(CVE-2026-11721)
|
||||
|
||||
* Thu Jul 23 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.18.33-15.5
|
||||
- Fix RPZ name-too-long wildcard expansion (CVE-2026-11331)
|
||||
|
||||
* Thu Jul 23 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.18.33-15.4
|
||||
- Fix assertion failure on malformed NSEC/NSEC3 responses
|
||||
(CVE-2026-13204)
|
||||
|
||||
* Mon May 25 2026 Petr Menšík <pemensik@redhat.com> - 32:9.18.33-15.2
|
||||
- Fix GSS-API resource leak (CVE-2026-3039)
|
||||
- Invalid handling of CLASS != IN (CVE-2026-5946)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user