Fix CVE-2026-13321: reject out-of-zone NSEC entries in DNSSEC validation
Add patch for CVE-2026-13321 which rejects out-of-zone NSEC next owner names during DNSSEC validation. The patch is based on ISC's 9.11-specific backport commits (3bc7280158 and 27ceccfe77) which add a dns_nsec_is_legal() function and integrate NSEC validation checks into the resolver, using 9.11-compatible APIs and coding conventions. CVE: CVE-2026-13321 Upstream patches: -058023c66f.patch -f751e19a30.patch Resolves: RHEL-213318 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir
This commit is contained in:
parent
3b30e917e8
commit
0f28e7dd60
270
bind-9.11-CVE-2026-13321.patch
Normal file
270
bind-9.11-CVE-2026-13321.patch
Normal file
@ -0,0 +1,270 @@
|
||||
From 338661714d508a810a903ae6fdaf522c9abc2d4b 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 1/2] 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 | 18 ++++++++++++++++++
|
||||
lib/dns/include/dns/dnssec.h | 6 ++++++
|
||||
2 files changed, 24 insertions(+)
|
||||
|
||||
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
|
||||
index b6b2405fac..b9bd374ed6 100644
|
||||
--- a/lib/dns/dnssec.c
|
||||
+++ b/lib/dns/dnssec.c
|
||||
@@ -380,8 +380,10 @@ dns_dnssec_verify3(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;
|
||||
@@ -454,6 +456,22 @@ dns_dnssec_verify3(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 31e2586c9b..1f13a08181 100644
|
||||
--- a/lib/dns/include/dns/dnssec.h
|
||||
+++ b/lib/dns/include/dns/dnssec.h
|
||||
@@ -138,6 +138,9 @@ dns_dnssec_verify3(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:
|
||||
@@ -160,6 +163,9 @@ dns_dnssec_verify3(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_*
|
||||
*/
|
||||
|
||||
|
||||
From dfb91ca4a45092c414a41cb5640cf4710f7ba0e9 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 2/2] 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 | 37 +++++++++++++++++++++++++++
|
||||
lib/dns/resolver.c | 51 ++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 106 insertions(+)
|
||||
|
||||
diff --git a/lib/dns/include/dns/nsec.h b/lib/dns/include/dns/nsec.h
|
||||
index 4e12cbea75..8caead6a1e 100644
|
||||
--- a/lib/dns/include/dns/nsec.h
|
||||
+++ b/lib/dns/include/dns/nsec.h
|
||||
@@ -106,6 +106,24 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name,
|
||||
* Return ISC_R_IGNORE when the NSEC is not the appropriate one.
|
||||
*/
|
||||
|
||||
+bool
|
||||
+dns_nsec_is_legal(dns_rdataset_t *rdataset, const dns_name_t *name);
|
||||
+/**<
|
||||
+ * \brief
|
||||
+ * Validates a rdataset of type NSEC.
|
||||
+ *
|
||||
+ * 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
|
||||
|
||||
#endif /* DNS_NSEC_H */
|
||||
diff --git a/lib/dns/nsec.c b/lib/dns/nsec.c
|
||||
index d90c38589e..fd4c529da0 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>
|
||||
@@ -445,3 +446,39 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name,
|
||||
*exists = false;
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
+
|
||||
+bool
|
||||
+dns_nsec_is_legal(dns_rdataset_t *nsecset, const dns_name_t *name) {
|
||||
+ dns_rdataset_t rdataset;
|
||||
+ dns_rdata_nsec_t nsec;
|
||||
+ isc_result_t result;
|
||||
+ bool found = false;
|
||||
+
|
||||
+ REQUIRE(nsecset != NULL && nsecset->type == dns_rdatatype_nsec);
|
||||
+
|
||||
+ dns_rdataset_init(&rdataset);
|
||||
+ dns_rdataset_clone(nsecset, &rdataset);
|
||||
+
|
||||
+ for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS;
|
||||
+ result = dns_rdataset_next(&rdataset))
|
||||
+ {
|
||||
+ dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||
+ dns_rdataset_current(&rdataset, &rdata);
|
||||
+
|
||||
+ /* 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);
|
||||
+ return (found);
|
||||
+}
|
||||
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
|
||||
index 0f16eadcdd..8ef35bb641 100644
|
||||
--- a/lib/dns/resolver.c
|
||||
+++ b/lib/dns/resolver.c
|
||||
@@ -62,8 +62,10 @@
|
||||
#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>
|
||||
|
||||
#ifdef WANT_QUERYTRACE
|
||||
#define RTRACE(m) isc_log_write(dns_lctx, \
|
||||
@@ -4879,6 +4881,36 @@ maybe_destroy(fetchctx_t *fctx, bool locked) {
|
||||
return (bucket_empty);
|
||||
}
|
||||
|
||||
+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, NULL);
|
||||
+
|
||||
+ 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.
|
||||
*/
|
||||
@@ -4907,6 +4939,8 @@ validated(isc_task_t *task, isc_event_t *event) {
|
||||
unsigned options;
|
||||
uint32_t bucketnum;
|
||||
dns_message_t *rmessage = NULL;
|
||||
+ dns_fixedname_t fsigner;
|
||||
+ dns_name_t *signer = NULL;
|
||||
|
||||
UNUSED(task); /* for now */
|
||||
|
||||
@@ -5240,6 +5274,23 @@ validated(isc_task_t *task, isc_event_t *event) {
|
||||
if (sigrdataset == NULL ||
|
||||
sigrdataset->trust != dns_trust_secure)
|
||||
continue;
|
||||
+ /*
|
||||
+ * 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_is_legal(rdataset, signer))
|
||||
+ {
|
||||
+ continue;
|
||||
+ }
|
||||
result = dns_db_findnode(fctx->cache, name, true,
|
||||
&nsnode);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
10
bind.spec
10
bind.spec
@ -68,7 +68,7 @@ Summary: The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) serv
|
||||
Name: bind
|
||||
License: MPLv2.0
|
||||
Version: 9.11.36
|
||||
Release: 16%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist}.10
|
||||
Release: 16%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist}.11
|
||||
Epoch: 32
|
||||
Url: https://www.isc.org/downloads/bind/
|
||||
#
|
||||
@ -217,6 +217,9 @@ Patch218: bind-9.11-CVE-2026-11622.patch
|
||||
# https://github.com/isc-projects/bind9/commit/06778424f0f58375f0f3d6632813558c59d705c6
|
||||
# https://github.com/isc-projects/bind9/commit/e5b16cfd1c0b7d7760108e1ca9e1fd9db30d9e6a
|
||||
Patch219: bind-9.11-CVE-2026-11721.patch
|
||||
# https://github.com/isc-projects/bind9/commit/058023c66f11d78590d4aa8c4f98946c4c965e21
|
||||
# https://github.com/isc-projects/bind9/commit/f751e19a30d107f04c2f644aff9f8dab8fed03ab
|
||||
Patch220: bind-9.11-CVE-2026-13321.patch
|
||||
|
||||
# SDB patches
|
||||
Patch11: bind-9.3.2b2-sdbsrc.patch
|
||||
@ -651,6 +654,7 @@ are used for building ISC DHCP.
|
||||
%patch -P 217 -p1 -b .CVE-2026-5946
|
||||
%patch -P 218 -p1 -b .CVE-2026-11622
|
||||
%patch -P 219 -p1 -b .CVE-2026-11721
|
||||
%patch -P 220 -p1 -b .CVE-2026-13321
|
||||
|
||||
mkdir lib/dns/tests/testdata/dstrandom
|
||||
cp -a %{SOURCE50} lib/dns/tests/testdata/dstrandom/random.data
|
||||
@ -1703,6 +1707,10 @@ rm -rf ${RPM_BUILD_ROOT}
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon Jul 27 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.11.36-16.11
|
||||
- Reject out-of-zone NSEC entries in DNSSEC validation
|
||||
(CVE-2026-13321)
|
||||
|
||||
* Mon Jul 27 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.11.36-16.10
|
||||
- Reject RRSIG records with invalid label counts (CVE-2026-11721)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user