Fix CVE-2026-11721: reject RRSIG records with invalid label counts
Backport fix for CVE-2026-11721 to bind 9.11 on c8s. The patch combines two upstream commits (06778424f0 and e5b16cfd1c) that prevent dnssec-signzone from signing out-of-zone records and reject RRSIG records whose labels field indicates fewer labels than the signer name requires, closing a cache poisoning vector via forged wildcard records. CVE: CVE-2026-11721 Upstream patches: -06778424f0.patch -e5b16cfd1c.patch Resolves: RHEL-213407 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir
This commit is contained in:
parent
4f8f15daec
commit
3b30e917e8
263
bind-9.11-CVE-2026-11721.patch
Normal file
263
bind-9.11-CVE-2026-11721.patch
Normal file
@ -0,0 +1,263 @@
|
||||
From 78ac82303bc3dd9d426d21fb268a5ed362d5535a Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
Date: Tue, 14 Apr 2026 15:14:06 +1000
|
||||
Subject: [PATCH 1/2] Don't sign out of zone records in dnssec-signzone
|
||||
|
||||
dnssec-signzone was signing extraneous records that were not within
|
||||
the namespace of the zone. This no longer occurs.
|
||||
|
||||
(cherry picked from commit e45c9af7051421fd370f20ba8325199c606223fd)
|
||||
---
|
||||
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 6567421..b7c8e55 100644
|
||||
--- a/bin/dnssec/dnssec-signzone.c
|
||||
+++ b/bin/dnssec/dnssec-signzone.c
|
||||
@@ -1482,6 +1482,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.
|
||||
|
||||
From 8c269a0269fdf12d5edd4698d08d663c6f64ddae Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
Date: Tue, 14 Apr 2026 12:24:33 +1000
|
||||
Subject: [PATCH 2/2] Invalid signed wildcard records were being accepted
|
||||
|
||||
An RRSIG whose Labels field indicates fewer labels than its signer
|
||||
name requires was being accepted. When such a record covers a
|
||||
wildcard, the validator reconstructs a wildcard owner name above the
|
||||
signer's zone and caches it as secure. RFC 8198 cache synthesis
|
||||
(synth-from-dnssec) then serves that forged wildcard for unrelated
|
||||
names, poisoning the cache.
|
||||
|
||||
These records are now rejected, both when an RRSIG is parsed and when
|
||||
its signature is verified.
|
||||
|
||||
(cherry picked from commit 084ca5ee10515e461d46b63df9660b8394bc7de9)
|
||||
---
|
||||
lib/dns/dnssec.c | 45 +++++++++++++++++++++++---------
|
||||
lib/dns/rdata/generic/rrsig_46.c | 37 +++++++++++++++++++-------
|
||||
2 files changed, 60 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
|
||||
index b6b2405..2516a9a 100644
|
||||
--- a/lib/dns/dnssec.c
|
||||
+++ b/lib/dns/dnssec.c
|
||||
@@ -142,11 +142,11 @@ dns_dnssec_keyfromrdata(dns_name_t *name, dns_rdata_t *rdata, isc_mem_t *mctx,
|
||||
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);
|
||||
|
||||
@@ -200,12 +200,14 @@ dns_dnssec_sign(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);
|
||||
@@ -243,7 +245,7 @@ dns_dnssec_sign(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--;
|
||||
sig.originalttl = set->ttl;
|
||||
@@ -390,11 +392,14 @@ dns_dnssec_verify3(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;
|
||||
uint32_t flags;
|
||||
+ unsigned int labels;
|
||||
+ unsigned int siglabels;
|
||||
bool downcase = false;
|
||||
|
||||
REQUIRE(name != NULL);
|
||||
+ labels = dns_name_countlabels(name);
|
||||
+ REQUIRE(labels > 0);
|
||||
REQUIRE(set != NULL);
|
||||
REQUIRE(key != NULL);
|
||||
REQUIRE(mctx != NULL);
|
||||
@@ -407,6 +412,21 @@ dns_dnssec_verify3(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
|
||||
if (set->type != sig.covered)
|
||||
return (DNS_R_SIGINVALID);
|
||||
|
||||
+ /*
|
||||
+ * The RRSIG labels field can't indicate fewer labels than the
|
||||
+ * signer. Also the labels shouldn't be greater than that of
|
||||
+ * the owner name.
|
||||
+ *
|
||||
+ * sig.labels doesn't include the root label, so add 1 to account
|
||||
+ * for it.
|
||||
+ */
|
||||
+ siglabels = sig.labels + 1;
|
||||
+ if (siglabels < dns_name_countlabels(&sig.signer) || siglabels > labels)
|
||||
+ {
|
||||
+ inc_stat(dns_dnssecstats_fail);
|
||||
+ return DNS_R_SIGINVALID;
|
||||
+ }
|
||||
+
|
||||
if (isc_serial_lt(sig.timeexpire, sig.timesigned)) {
|
||||
inc_stat(dns_dnssecstats_fail);
|
||||
return (DNS_R_SIGINVALID);
|
||||
@@ -484,11 +504,10 @@ dns_dnssec_verify3(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
|
||||
* 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)
|
||||
- dns_name_split(dns_fixedname_name(&fnewname), sig.labels + 1,
|
||||
+ if (labels > siglabels)
|
||||
+ dns_name_split(dns_fixedname_name(&fnewname), siglabels,
|
||||
NULL, dns_fixedname_name(&fnewname));
|
||||
|
||||
dns_name_toregion(dns_fixedname_name(&fnewname), &r);
|
||||
@@ -497,7 +516,7 @@ dns_dnssec_verify3(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
|
||||
* 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);
|
||||
@@ -583,7 +602,7 @@ cleanup_struct:
|
||||
if (ret != ISC_R_SUCCESS)
|
||||
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,
|
||||
dns_fixedname_name(&fnewname),
|
||||
diff --git a/lib/dns/rdata/generic/rrsig_46.c b/lib/dns/rdata/generic/rrsig_46.c
|
||||
index 0d4df67..35a1b4c 100644
|
||||
--- a/lib/dns/rdata/generic/rrsig_46.c
|
||||
+++ b/lib/dns/rdata/generic/rrsig_46.c
|
||||
@@ -21,12 +21,12 @@
|
||||
static inline isc_result_t
|
||||
fromtext_rrsig(ARGS_FROMTEXT) {
|
||||
isc_token_t token;
|
||||
- unsigned char c;
|
||||
+ unsigned char alg, labels;
|
||||
long i;
|
||||
dns_rdatatype_t covered;
|
||||
- char *e;
|
||||
+ char *e = NULL;
|
||||
isc_result_t result;
|
||||
- dns_name_t name;
|
||||
+ dns_name_t signer;
|
||||
isc_buffer_t buffer;
|
||||
uint32_t time_signed, time_expire;
|
||||
|
||||
@@ -57,8 +57,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.
|
||||
@@ -67,8 +67,8 @@ fromtext_rrsig(ARGS_FROMTEXT) {
|
||||
false));
|
||||
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.
|
||||
@@ -131,11 +131,19 @@ 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.
|
||||
@@ -260,6 +268,7 @@ static inline isc_result_t
|
||||
fromwire_rrsig(ARGS_FROMWIRE) {
|
||||
isc_region_t sr;
|
||||
dns_name_t name;
|
||||
+ unsigned char labels;
|
||||
|
||||
REQUIRE(type == dns_rdatatype_rrsig);
|
||||
|
||||
@@ -281,6 +290,8 @@ fromwire_rrsig(ARGS_FROMWIRE) {
|
||||
if (sr.length < 18)
|
||||
return (ISC_R_UNEXPECTEDEND);
|
||||
|
||||
+ labels = sr.base[3];
|
||||
+
|
||||
isc_buffer_forward(source, 18);
|
||||
RETERR(mem_tobuffer(target, sr.base, 18));
|
||||
|
||||
@@ -290,6 +301,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.
|
||||
*/
|
||||
@ -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}.9
|
||||
Release: 16%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist}.10
|
||||
Epoch: 32
|
||||
Url: https://www.isc.org/downloads/bind/
|
||||
#
|
||||
@ -214,6 +214,9 @@ Patch216: bind-9.11-CVE-2026-3039.patch
|
||||
Patch217: bind-9.11-CVE-2026-5946.patch
|
||||
# https://github.com/isc-projects/bind9/commit/0b72e907d10760c2a8b01fc1f5a1c1f0d1a4bfe0
|
||||
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
|
||||
|
||||
# SDB patches
|
||||
Patch11: bind-9.3.2b2-sdbsrc.patch
|
||||
@ -647,6 +650,7 @@ are used for building ISC DHCP.
|
||||
%patch -P 216 -p1 -b .CVE-2026-3039
|
||||
%patch -P 217 -p1 -b .CVE-2026-5946
|
||||
%patch -P 218 -p1 -b .CVE-2026-11622
|
||||
%patch -P 219 -p1 -b .CVE-2026-11721
|
||||
|
||||
mkdir lib/dns/tests/testdata/dstrandom
|
||||
cp -a %{SOURCE50} lib/dns/tests/testdata/dstrandom/random.data
|
||||
@ -1699,6 +1703,9 @@ rm -rf ${RPM_BUILD_ROOT}
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* 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)
|
||||
|
||||
* Mon Jul 27 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 32:9.11.36-16.9
|
||||
- Add reference counting to cache dns_slabheaders (CVE-2026-11622)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user