diff --git a/bind-9.18.21-CVE-2026-80274.patch b/bind-9.18.21-CVE-2026-80274.patch new file mode 100644 index 0000000000000000000000000000000000000000..7bfe0676d87b33ee90782435b769437f68b92b68 --- /dev/null +++ b/bind-9.18.21-CVE-2026-80274.patch @@ -0,0 +1,423 @@ +From ff259b9f4279aa2ead2fc49bd071c6039f265c19 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Wed, 26 Aug 2026 09:36:19 +0200 +Subject: [PATCH] Keep NOQNAME proof selection consistent + +The add and get paths of the rdatalist noqname proof could select +different denial types when an owner carried both NSEC and NSEC3 +records, so a proof accepted during validation could fail to be +cached. Worse, both paths picked the NSEC/NSEC3 and RRSIG pair by its +position in the message, with no knowledge of which type the validator +(or findnoqname()) had actually accepted, so the choice silently +depended on the order the authoritative server sent the records in and +could cache a proof that had been rejected. + +Pass the accepted type down through dns_rdataset_addnoqname() and +record it with the rdataset, so one lookup serves both paths and the +get path returns exactly the pair that was added. + +(cherry picked from commit 5c9ef5314cc4fea2cba10d4e30b01c4de0d3221e) +Adapted-by: PkgAgent/deepseek-v4 (modified to adapt to opencloudos-stream) +--- + lib/dns/include/dns/rdataset.h | 22 ++++++--- + lib/dns/include/dns/validator.h | 4 ++ + lib/dns/rdatalist.c | 106 ++++++++++++++++++---------------------- + lib/dns/rdatalist_p.h | 3 +- + lib/dns/rdataset.c | 5 +- + lib/dns/resolver.c | 34 +++++++++---- + lib/dns/validator.c | 4 ++ + 7 files changed, 100 insertions(+), 78 deletions(-) +diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h +index 566ea44cf9396d7f23a5ef2e9cc8fecdd6f19da0..ac452fbbbbeec4684df8c6abf4dc5b5b84d41850 100644 +--- a/lib/dns/include/dns/rdataset.h ++++ b/lib/dns/include/dns/rdataset.h +@@ -70,7 +70,8 @@ typedef struct dns_rdatasetmethods { + void (*clone)(dns_rdataset_t *source, dns_rdataset_t *target); + unsigned int (*count)(dns_rdataset_t *rdataset); + isc_result_t (*addnoqname)(dns_rdataset_t *rdataset, +- const dns_name_t *name); ++ const dns_name_t *name, ++ dns_rdatatype_t type); + isc_result_t (*getnoqname)(dns_rdataset_t *rdataset, dns_name_t *name, + dns_rdataset_t *neg, dns_rdataset_t *negsig); + isc_result_t (*addclosest)(dns_rdataset_t *rdataset, +@@ -493,17 +494,26 @@ dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, + */ + + isc_result_t +-dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name); ++dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name, ++ dns_rdatatype_t type); + /*%< +- * Associate a noqname proof with this record. ++ * Associate a noqname proof with this record: the rdataset of 'type' ++ * (NSEC or NSEC3) at 'name' together with the RRSIG rdataset covering it. + * Sets #DNS_RDATASETATTR_NOQNAME if successful. + * Adjusts the 'rdataset->ttl' to minimum of the 'rdataset->ttl' and + * the 'nsec'/'nsec3' and 'rrsig(nsec)'/'rrsig(nsec3)' ttl. + * + * Requires: +- *\li 'rdataset' to be valid and #DNS_RDATASETATTR_NOQNAME to be set. +- *\li 'name' to be valid and have NSEC or NSEC3 and associated RRSIG +- * rdatasets. ++ *\li 'rdataset' to be valid. ++ *\li 'name' to be valid. ++ *\li 'type' to be dns_rdatatype_nsec or dns_rdatatype_nsec3. ++ * ++ * Returns: ++ *\li #ISC_R_SUCCESS ++ *\li #ISC_R_NOTFOUND if 'name' has no rdataset of 'type' or no RRSIG ++ * rdataset covering it. ++ *\li #ISC_R_NOTIMPLEMENTED if the rdataset implementation does not ++ * support noqname proofs. + */ + + isc_result_t +diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h +index 383dcb46e400423f6f368431186b25cda868ccfd..0fde73e082e30d814cfc409c0a1435018920c295 100644 +--- a/lib/dns/include/dns/validator.h ++++ b/lib/dns/include/dns/validator.h +@@ -93,6 +93,10 @@ typedef struct dns_validatorevent { + * Proofs to be cached. + */ + dns_name_t *proofs[4]; ++ /* ++ * The denial type (NSEC or NSEC3) of the NOQNAME proof. ++ */ ++ dns_rdatatype_t noqnametype; + /* + * Optout proof seen. + */ +diff --git a/lib/dns/rdatalist.c b/lib/dns/rdatalist.c +index a2643c749faa291c743c8785675637409429a709..d79861f13cbe9123ac01def735105b89d7a4eb7d 100644 +--- a/lib/dns/rdatalist.c ++++ b/lib/dns/rdatalist.c +@@ -19,7 +19,6 @@ + #include + + #include +-#include + #include + #include + #include +@@ -187,64 +186,77 @@ isc__rdatalist_count(dns_rdataset_t *rdataset) { + return (count); + } + +-isc_result_t +-isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) { ++/* ++ * Find the denial of existence proof of the given type at 'name': the ++ * NSEC or NSEC3 rdataset together with the RRSIG rdataset covering it. ++ */ ++static bool ++findproof(const dns_name_t *name, dns_rdataclass_t rdclass, ++ dns_rdatatype_t type, dns_rdataset_t **negp, ++ dns_rdataset_t **negsigp) { + dns_rdataset_t *neg = NULL; + dns_rdataset_t *negsig = NULL; +- dns_rdataset_t *rdset; +- dns_ttl_t ttl; ++ dns_rdataset_t *rdset = NULL; + +- REQUIRE(rdataset != NULL); ++ REQUIRE(type == dns_rdatatype_nsec || type == dns_rdatatype_nsec3); + + for (rdset = ISC_LIST_HEAD(name->list); rdset != NULL; + rdset = ISC_LIST_NEXT(rdset, link)) + { +- if (rdset->rdclass != rdataset->rdclass) { ++ if (rdset->rdclass != rdclass) { + continue; + } +- if (rdset->type == dns_rdatatype_nsec || +- rdset->type == dns_rdatatype_nsec3) +- { +- neg = rdset; +- } +- } +- 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) ++ if (rdset->type == type) { ++ neg = rdset; ++ } else if (rdset->type == dns_rdatatype_rrsig && ++ rdset->covers == type) + { + negsig = rdset; + } ++ ++ if (neg != NULL && negsig != NULL) { ++ *negp = neg; ++ *negsigp = negsig; ++ return true; ++ } + } + +- if (negsig == NULL) { +- return (ISC_R_NOTFOUND); ++ return false; ++} ++ ++isc_result_t ++isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name, ++ dns_rdatatype_t type) { ++ dns_rdataset_t *neg = NULL; ++ dns_rdataset_t *negsig = NULL; ++ dns_ttl_t ttl; ++ ++ REQUIRE(rdataset != NULL); ++ REQUIRE(name != NULL); ++ ++ if (!findproof(name, rdataset->rdclass, type, &neg, &negsig)) { ++ return ISC_R_NOTFOUND; + } + /* + * Minimise ttl. + */ +- ttl = rdataset->ttl; +- if (neg->ttl < ttl) { +- ttl = neg->ttl; +- } +- if (negsig->ttl < ttl) { +- ttl = negsig->ttl; +- } ++ ttl = ISC_MIN(rdataset->ttl, ISC_MIN(neg->ttl, negsig->ttl)); + rdataset->ttl = neg->ttl = negsig->ttl = ttl; + rdataset->attributes |= DNS_RDATASETATTR_NOQNAME; ++ /* ++ * The proof owner and the denial type selected there; rdatalist ++ * does not otherwise use 'privateuint4'. ++ */ + rdataset->private6 = name; +- return (ISC_R_SUCCESS); ++ rdataset->privateuint4 = type; ++ ++ return ISC_R_SUCCESS; + } + + isc_result_t + isc__rdatalist_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, + dns_rdataset_t *neg, dns_rdataset_t *negsig) { +- dns_rdataclass_t rdclass; + dns_rdataset_t *tneg = NULL; + dns_rdataset_t *tnegsig = NULL; + const dns_name_t *noqname; +@@ -252,38 +264,14 @@ isc__rdatalist_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, + REQUIRE(rdataset != NULL); + REQUIRE((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0); + +- rdclass = rdataset->rdclass; + noqname = rdataset->private6; + + (void)dns_name_dynamic(noqname); /* Sanity Check. */ + +- for (rdataset = ISC_LIST_HEAD(noqname->list); rdataset != NULL; +- rdataset = ISC_LIST_NEXT(rdataset, link)) +- { +- if (rdataset->rdclass != rdclass) { +- continue; +- } +- if (rdataset->type == dns_rdatatype_nsec || +- rdataset->type == dns_rdatatype_nsec3) +- { +- tneg = rdataset; +- } +- } +- if (tneg == NULL) { +- return (ISC_R_NOTFOUND); +- } +- +- for (rdataset = ISC_LIST_HEAD(noqname->list); rdataset != NULL; +- rdataset = ISC_LIST_NEXT(rdataset, link)) ++ if (!findproof(noqname, rdataset->rdclass, rdataset->privateuint4, ++ &tneg, &tnegsig)) + { +- if (rdataset->type == dns_rdatatype_rrsig && +- rdataset->covers == tneg->type) +- { +- tnegsig = rdataset; +- } +- } +- if (tnegsig == NULL) { +- return (ISC_R_NOTFOUND); ++ return ISC_R_NOTFOUND; + } + + dns_name_clone(noqname, name); +diff --git a/lib/dns/rdatalist_p.h b/lib/dns/rdatalist_p.h +index f27a084dfb0b7c58f49516a06304d51e3f5c2f34..33477f960079f11ab26f69e6ef295a9ce078ce2f 100644 +--- a/lib/dns/rdatalist_p.h ++++ b/lib/dns/rdatalist_p.h +@@ -40,7 +40,8 @@ unsigned int + isc__rdatalist_count(dns_rdataset_t *rdataset); + + isc_result_t +-isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name); ++isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name, ++ dns_rdatatype_t type); + + isc_result_t + isc__rdatalist_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, +diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c +index 4d482030af208677cd799719e234b1a8031aa5ff..cbe0b9f008c84457f8b85763388e84d65cbdc426 100644 +--- a/lib/dns/rdataset.c ++++ b/lib/dns/rdataset.c +@@ -611,13 +611,14 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset, + } + + isc_result_t +-dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) { ++dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name, ++ dns_rdatatype_t type) { + REQUIRE(DNS_RDATASET_VALID(rdataset)); + REQUIRE(rdataset->methods != NULL); + if (rdataset->methods->addnoqname == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } +- return ((rdataset->methods->addnoqname)(rdataset, name)); ++ return ((rdataset->methods->addnoqname)(rdataset, name, type)); + } + + isc_result_t +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index 4b3d1c0b40a284ad99929c4125550b9567e2db89..8d014d804a70428bd1d8efe8e944ce38a38480c4 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -683,7 +683,8 @@ add_bad(fetchctx_t *fctx, dns_message_t *rmessage, dns_adbaddrinfo_t *addrinfo, + isc_result_t reason, badnstype_t badtype); + static isc_result_t + findnoqname(fetchctx_t *fctx, dns_message_t *message, dns_name_t *name, +- dns_rdatatype_t type, dns_name_t **noqname); ++ dns_rdatatype_t type, dns_name_t **noqnamep, ++ dns_rdatatype_t *noqnametypep); + + #define fctx_attach(fctx, fctxp) \ + fctx__attach(fctx, fctxp, __FILE__, __LINE__, __func__) +@@ -5717,7 +5718,8 @@ validated(isc_task_t *task, isc_event_t *event) { + if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) { + result = dns_rdataset_addnoqname( + vevent->rdataset, +- vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]); ++ vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF], ++ vevent->noqnametype); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + INSIST(vevent->sigrdataset != NULL); + vevent->sigrdataset->ttl = vevent->rdataset->ttl; +@@ -5732,11 +5734,13 @@ validated(isc_task_t *task, isc_event_t *event) { + { + isc_result_t tresult; + dns_name_t *noqname = NULL; ++ dns_rdatatype_t noqnametype = dns_rdatatype_none; + tresult = findnoqname(fctx, message, vevent->name, +- vevent->rdataset->type, &noqname); ++ vevent->rdataset->type, &noqname, ++ &noqnametype); + if (tresult == ISC_R_SUCCESS && noqname != NULL) { + tresult = dns_rdataset_addnoqname(vevent->rdataset, +- noqname); ++ noqname, noqnametype); + RUNTIME_CHECK(tresult == ISC_R_SUCCESS); + } + } +@@ -5991,7 +5995,8 @@ fctx_log(void *arg, int level, const char *fmt, ...) { + + static isc_result_t + findnoqname(fetchctx_t *fctx, dns_message_t *message, dns_name_t *name, +- dns_rdatatype_t type, dns_name_t **noqnamep) { ++ dns_rdatatype_t type, dns_name_t **noqnamep, ++ dns_rdatatype_t *noqnametypep) { + dns_rdataset_t *nrdataset, *next, *sigrdataset; + dns_rdata_rrsig_t rrsig; + isc_result_t result; +@@ -6009,6 +6014,7 @@ findnoqname(fetchctx_t *fctx, dns_message_t *message, dns_name_t *name, + FCTXTRACE("findnoqname"); + + REQUIRE(noqnamep != NULL && *noqnamep == NULL); ++ REQUIRE(noqnametypep != NULL); + + /* + * Find the SIG for this rdataset, if we have it. +@@ -6119,6 +6125,7 @@ findnoqname(fetchctx_t *fctx, dns_message_t *message, dns_name_t *name, + } + if (sigrdataset != NULL) { + *noqnamep = noqname; ++ *noqnametypep = found; + } + } + return (result); +@@ -6361,14 +6368,18 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message, + { + isc_result_t tresult; + dns_name_t *noqname = NULL; ++ dns_rdatatype_t noqnametype = ++ dns_rdatatype_none; + tresult = findnoqname( + fctx, message, name, +- rdataset->type, &noqname); ++ rdataset->type, &noqname, ++ &noqnametype); + if (tresult == ISC_R_SUCCESS && + noqname != NULL) + { + (void)dns_rdataset_addnoqname( +- rdataset, noqname); ++ rdataset, noqname, ++ noqnametype); + } + } + if ((fctx->options & DNS_FETCHOPT_PREFETCH) != +@@ -6524,12 +6535,15 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message, + { + isc_result_t tresult; + dns_name_t *noqname = NULL; ++ dns_rdatatype_t noqnametype = ++ dns_rdatatype_none; + tresult = findnoqname(fctx, message, name, +- rdataset->type, &noqname); ++ rdataset->type, &noqname, ++ &noqnametype); + if (tresult == ISC_R_SUCCESS && noqname != NULL) + { +- (void)dns_rdataset_addnoqname(rdataset, +- noqname); ++ (void)dns_rdataset_addnoqname( ++ rdataset, noqname, noqnametype); + } + } + +diff --git a/lib/dns/validator.c b/lib/dns/validator.c +index 56a0ced7b7580ff5e8f0c41a91c262c7de279f30..c0a99a5570de6d95215aadcef57babc469f354da 100644 +--- a/lib/dns/validator.c ++++ b/lib/dns/validator.c +@@ -920,6 +920,8 @@ validator_callback_nsec(isc_task_t *task, isc_event_t *event) { + if (NEEDNOQNAME(val)) { + proofs[DNS_VALIDATOR_NOQNAMEPROOF] = + devent->name; ++ val->event->noqnametype = ++ dns_rdatatype_nsec; + } + } + } +@@ -2287,6 +2289,7 @@ findnsec3proofs(dns_validator_t *val) { + proofs[DNS_VALIDATOR_NOQNAMEPROOF] == NULL) + { + proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name; ++ val->event->noqnametype = dns_rdatatype_nsec3; + } else if (setclosest) { + proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name; + } else if (NEEDNODATA(val) && +@@ -2314,6 +2317,7 @@ findnsec3proofs(dns_validator_t *val) { + if (!exists && setnearest) { + val->attributes |= VALATTR_FOUNDNOQNAME; + proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name; ++ val->event->noqnametype = dns_rdatatype_nsec3; + if (optout) { + val->attributes |= VALATTR_FOUNDOPTOUT; + } diff --git a/bind.spec b/bind.spec index 7bc3478eeeeca9ed104a3149bffc03310dcecae0..e3dd35469fd2f804e4cba6d197901c7161dd917e 100644 --- a/bind.spec +++ b/bind.spec @@ -6,7 +6,7 @@ Summary: BIND (Berkeley Internet Name Domain), implementation of the Domain Name System (DNS) protocol. Name: bind Version: 9.18.21 -Release: 6%{?dist} +Release: 7%{?dist} License: MPLv2.0 URL: https://www.isc.org/bind/ Source0: https://downloads.isc.org/isc/bind9/%{version}/bind-%{version}.tar.xz @@ -43,6 +43,7 @@ Patch0014: bind-9.18.21-CVE-2026-5946-5.patch Patch0015: bind-9.18.21-CVE-2026-12617.patch Patch0016: bind-9.18.21-CVE-2026-11721-1.patch Patch0017: bind-9.18.21-CVE-2026-11721-2.patch +Patch0018: bind-9.18.21-CVE-2026-80274.patch Patch3000: 3000-CVE-2024-0760.patch Patch3001: 3001-optimize-the-slabheader-placement-for-certain-RRtypes.patch @@ -466,6 +467,10 @@ fi %changelog +* Thu Sep 17 2026 PkgAgent Robot - 9.18.21-7 +- [Type] security +- [DESC] Fix CVE-2026-80274: keep NOQNAME proof selection consistent + * Mon Jul 27 2026 PkgAgent Robot - 9.18.21-6 - [Type] security - [DESC] Fix CVE-2026-12617, CVE-2026-11721