bind/bind-9.18-CVE-2026-13204.patch
2026-08-17 01:00:41 -04:00

187 lines
6.1 KiB
Diff

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);