From 5747971c870bb0a2ff37370df04cfba45cf79aff Mon Sep 17 00:00:00 2001 From: RHEL Packaging Agent Date: Thu, 23 Jul 2026 12:33:45 +0000 Subject: [PATCH] Fix CVE-2026-11721: RRSIG label count validation for wildcard cache poisoning Add patch for CVE-2026-11721: an RRSIG whose Labels field indicates fewer labels than its signer name requires was being accepted, allowing cache poisoning via forged wildcard records through RFC 8198 synthesis. The patch is a two-commit mbox: the original upstream fix plus an adaptation replacing the DNS_NAME_VALID() macro (BIND 9.20+) with the equivalent ISC_MAGIC_VALID() expression available in BIND 9.18. CVE: CVE-2026-11721 Upstream patches: - https://github.com/isc-projects/bind9/commit/8a46533cbeab78c215ee035d73967a3a59925375.patch Resolves: RHEL-213411 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir --- bind-9.18-CVE-2026-11721.patch | 260 +++++++++++++++++++++++++++++++++ bind.spec | 8 +- 2 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 bind-9.18-CVE-2026-11721.patch diff --git a/bind-9.18-CVE-2026-11721.patch b/bind-9.18-CVE-2026-11721.patch new file mode 100644 index 0000000..556ed08 --- /dev/null +++ b/bind-9.18-CVE-2026-11721.patch @@ -0,0 +1,260 @@ +From fad5a6422c34fee9f159b81b76a3926bcb8d1487 Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Tue, 14 Apr 2026 12:24:33 +1000 +Subject: [PATCH 1/2] Invalid signed wildcard records were being accepted + +An RRSIG whose Labels field indicates fewer labels than its signer +name requires was being accepted. When such a record covers a +wildcard, the validator reconstructs a wildcard owner name above the +signer's zone and caches it as secure. RFC 8198 cache synthesis +(synth-from-dnssec) then serves that forged wildcard for unrelated +names, poisoning the cache. + +These records are now rejected, both when an RRSIG is parsed and when +its signature is verified. + +(cherry picked from commit 084ca5ee10515e461d46b63df9660b8394bc7de9) +--- + lib/dns/dnssec.c | 39 ++++++++++++++++++++++++-------- + lib/dns/rdata/generic/rrsig_46.c | 33 +++++++++++++++++++++------ + 2 files changed, 55 insertions(+), 17 deletions(-) + +diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c +index 662136db87..ea8feedb18 100644 +--- a/lib/dns/dnssec.c ++++ b/lib/dns/dnssec.c +@@ -137,7 +137,7 @@ 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(DNS_NAME_VALID(name)); + INSIST(rdata != NULL); + INSIST(mctx != NULL); + INSIST(key != NULL); +@@ -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); ++ REQUIRE(DNS_NAME_VALID(name)); ++ 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); ++ REQUIRE(DNS_NAME_VALID(name)); ++ 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: . + */ + 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..3230eca78b 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 c, 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; + +@@ -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. + */ + +From 25fcb6ca775a2f3f047842fa89b185c582e19d30 Mon Sep 17 00:00:00 2001 +From: RHEL Packaging Agent +Date: Thu, 23 Jul 2026 12:17:07 +0000 +Subject: [PATCH 2/2] Replace DNS_NAME_VALID with ISC_MAGIC_VALID equivalent + for 9.18 compatibility + +DNS_NAME_VALID macro was introduced in BIND 9.20 and is not available in 9.18. +Replace with the equivalent ISC_MAGIC_VALID(name, DNS_NAME_MAGIC) expression +which is available in 9.18. +--- + lib/dns/dnssec.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c +index ea8feedb18..0b3b7fb2dc 100644 +--- a/lib/dns/dnssec.c ++++ b/lib/dns/dnssec.c +@@ -137,7 +137,7 @@ dns_dnssec_keyfromrdata(const dns_name_t *name, const dns_rdata_t *rdata, + isc_buffer_t b; + isc_region_t r; + +- INSIST(DNS_NAME_VALID(name)); ++ INSIST(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + INSIST(rdata != NULL); + INSIST(mctx != NULL); + INSIST(key != NULL); +@@ -200,7 +200,7 @@ dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + dns_fixedname_t fnewname; + dns_fixedname_t fsigner; + +- REQUIRE(DNS_NAME_VALID(name)); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + labels = dns_name_countlabels(name); + REQUIRE(labels <= 255 && labels > 0); + REQUIRE(set != NULL); +@@ -391,7 +391,7 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + uint32_t flags; + bool downcase = false; + +- REQUIRE(DNS_NAME_VALID(name)); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + labels = dns_name_countlabels(name); + REQUIRE(labels > 0); + REQUIRE(set != NULL); diff --git a/bind.spec b/bind.spec index a2bdcc1..69cefbc 100644 --- a/bind.spec +++ b/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: 21%{?dist} +Release: 22%{?dist} Epoch: 32 Url: https://www.isc.org/downloads/bind/ # @@ -174,6 +174,8 @@ Patch235: bind-9.18-CVE-2026-5946.patch Patch236: bind-9.18-CVE-2026-13204.patch # https://github.com/isc-projects/bind9/commit/dc328a199f96222e0c30cc20b7b795bfc2c9b2e4 Patch237: bind-9.18-CVE-2026-11331.patch +# https://github.com/isc-projects/bind9/commit/8a46533cbeab78c215ee035d73967a3a59925375 +Patch238: bind-9.18-CVE-2026-11721.patch %{?systemd_ordering} # https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers @@ -975,6 +977,10 @@ fi; %endif %changelog +* Thu Jul 23 2026 RHEL Packaging Agent - 32:9.18.33-22 +- Fix RRSIG label count validation for wildcard cache poisoning + (CVE-2026-11721) + * Thu Jul 23 2026 RHEL Packaging Agent - 32:9.18.33-21 - Fix RPZ name-too-long wildcard expansion (CVE-2026-11331)