From 99219efc5b031b8d3f61b1730ee31b66ab0ca9ba Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 09:26:55 +0800 Subject: [PATCH 1/9] fix(cve): CVE-2026-13204 - dns_rdataset_addnoqname() could find unsigned NSEC/NSEC3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE: CVE-2026-13204 (high) - dns_rdataset_addnoqname() 可能找到未签名的 NSEC/NSEC3 Upstream: https://github.com/isc-projects/bind9/commit/4730903dca9827c748c8f1555d0c99beb5696cfc Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++ debian/patches/CVE-2026-13204.patch | 132 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 139 insertions(+) create mode 100644 debian/patches/CVE-2026-13204.patch diff --git a/debian/changelog b/debian/changelog index 3a4f88b3..e17433c9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin2) unstable; urgency=medium + + * fix(cve): CVE-2026-13204 + + -- deepin-ci-robot Fri, 24 Jul 2026 09:26:55 +0800 + bind9 (1:9.20.23-1~deb13u1deepin1) unstable; urgency=medium [ lichenggang ] diff --git a/debian/patches/CVE-2026-13204.patch b/debian/patches/CVE-2026-13204.patch new file mode 100644 index 00000000..f41f614c --- /dev/null +++ b/debian/patches/CVE-2026-13204.patch @@ -0,0 +1,132 @@ +Description: CVE-2026-13204 - 安全修复 +Author: Evan Hunt +Origin: https://github.com/isc-projects/bind9/commit/4730903dca9827c748c8f1555d0c99beb5696cfc +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-13204 +Last-Update: 2026-05-13 20:45:57 -0700 +--- +diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c +index 1969d21..f138678 100644 +--- a/lib/dns/qpcache.c ++++ b/lib/dns/qpcache.c +@@ -3281,8 +3281,7 @@ addnoqname(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, + dns_rdataset_t neg = DNS_RDATASET_INIT, negsig = DNS_RDATASET_INIT; + isc_region_t r1 = { .base = NULL }, r2 = { .base = NULL }; + +- result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig); +- RUNTIME_CHECK(result == ISC_R_SUCCESS); ++ CHECK(dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig)); + + result = dns_rdataslab_fromrdataset(&neg, mctx, &r1, 0, maxrrperset); + if (result != ISC_R_SUCCESS) { +diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c +index 386b862..9b904de 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -3183,7 +3183,9 @@ addnoqname(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, + isc_region_t r1 = { .base = NULL }, r2 = { .base = NULL }; + + result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig); +- RUNTIME_CHECK(result == ISC_R_SUCCESS); ++ if (result != ISC_R_SUCCESS) { ++ goto cleanup; ++ } + + result = dns_rdataslab_fromrdataset(&neg, mctx, &r1, 0, maxrrperset); + if (result != ISC_R_SUCCESS) { +diff --git a/lib/dns/rdatalist.c b/lib/dns/rdatalist.c +index 6320f60..4fe11a2 100644 +--- a/lib/dns/rdatalist.c ++++ b/lib/dns/rdatalist.c +@@ -178,6 +178,7 @@ dns_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); +@@ -185,32 +186,30 @@ dns_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; + } ++ + /* + * Minimise ttl. + */ +@@ -224,6 +223,7 @@ dns_rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) { + rdataset->ttl = neg->ttl = negsig->ttl = ttl; + rdataset->attributes |= DNS_RDATASETATTR_NOQNAME; + rdataset->rdlist.noqname = name; ++ + return ISC_R_SUCCESS; + } + +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index 4b70963..b46df77 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -5516,7 +5516,9 @@ validated(void *arg) { + if (val->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) { + result = dns_rdataset_addnoqname( + val->rdataset, val->proofs[DNS_VALIDATOR_NOQNAMEPROOF]); +- RUNTIME_CHECK(result == ISC_R_SUCCESS); ++ if (result != ISC_R_SUCCESS) { ++ goto noanswer_response; ++ } + INSIST(val->sigrdataset != NULL); + val->sigrdataset->ttl = val->rdataset->ttl; + if (val->proofs[DNS_VALIDATOR_CLOSESTENCLOSER] != NULL) { +diff --git a/lib/ns/query.c b/lib/ns/query.c +index 231ad9f..8517b99 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -7884,8 +7884,7 @@ query_addnoqnameproof(query_ctx_t *qctx) { + neg = ns_client_newrdataset(client); + negsig = ns_client_newrdataset(client); + +- 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); diff --git a/debian/patches/series b/debian/patches/series index e2806182..88c2b1b2 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,2 +1,3 @@ 0001-Disable-treat-warnings-as-errors-in-sphinx-build.patch 0002-Disable-RTLD_DEEPBIND-in-Samba-DLZ-module.patch +CVE-2026-13204.patch From d7e9bac32f1ac416bfd4b3c07d5b5a28ea3048cf Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 09:37:32 +0800 Subject: [PATCH 2/9] fix(cve): CVE-2026-12617 - Do not assert in some CNAME/DNAME queries CVE: CVE-2026-12617 (high) - The issue is unexpected program termination based on ordering and/or specific content in responses to queries for CNAME or DNAME, and A records. Specifically, if a client queries for a DNAME and A record below the DNAME to the resolver, and the authoritative server responds positively to the A query but delays the DNAME response and later responds negatively, named may quit unexpectedly. Or, if a client queries for a CNAME and A record for the same name to the resolver, and the authoritative server responds positively to the A query but delays the CNAME response and later responds with a self-referential CNAME, the same failure may occur. This issue affects BIND 9 versions 9.18.0 through 9.18.50, 9.20.0 through 9.20.24, 9.18.11-S1 through 9.18.50-S1, and 9.20.9-S1 through 9.20.24-S1. Upstream: https://github.com/isc-projects/bind9/commit/773d46d58c693047a5945c8fe40512edd0ac214e Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 + debian/patches/CVE-2026-12617.patch | 206 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 213 insertions(+) create mode 100644 debian/patches/CVE-2026-12617.patch diff --git a/debian/changelog b/debian/changelog index e17433c9..1eee71c0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin3) unstable; urgency=medium + + * fix(cve): CVE-2026-12617 + + -- deepin-ci-robot Fri, 24 Jul 2026 09:37:32 +0800 + bind9 (1:9.20.23-1~deb13u1deepin2) unstable; urgency=medium * fix(cve): CVE-2026-13204 diff --git a/debian/patches/CVE-2026-12617.patch b/debian/patches/CVE-2026-12617.patch new file mode 100644 index 00000000..631fa086 --- /dev/null +++ b/debian/patches/CVE-2026-12617.patch @@ -0,0 +1,206 @@ +Description: CVE-2026-12617 - 安全修复 +Author: Colin Vidal +Origin: https://github.com/isc-projects/bind9/commit/773d46d58c693047a5945c8fe40512edd0ac214e +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-12617 +Last-Update: 2026-06-18 18:17:05 +0200 +--- +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index b46df77..4fe0282 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -675,10 +675,10 @@ fctx_minimize_qname(fetchctx_t *fctx); + static void + fctx_destroy(fetchctx_t *fctx); + static isc_result_t +-ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, +- dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl, +- dns_ttl_t maxttl, bool optout, bool secure, +- dns_rdataset_t *ardataset, isc_result_t *eresultp); ++ncache_adderesult(fetchctx_t *fctx, dns_message_t *message, dns_dbnode_t *node, ++ dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, ++ bool optout, bool secure, dns_rdataset_t *ardataset, ++ isc_result_t *eresultp); + static void + validated(void *arg); + static void +@@ -5254,6 +5254,46 @@ has_000_label(dns_rdataset_t *nsecset) { + return false; + } + ++/* ++ * After a (non-error) negative-cache add, 'rdataset' is bound to whatever ++ * rdataset the cache authoritatively holds for the queried name and type. ++ * Map that to the result code the fetch should report: ++ * ++ * - A negative cache entry (the one we just added, or a pre-existing one): ++ * DNS_R_NCACHENXDOMAIN or DNS_R_NCACHENXRRSET, depending on NXDOMAIN vs ++ * NODATA. ++ * ++ * - A positive rdataset that was already cached at higher trust, which ++ * caused our negative entry to be discarded (e.g. a CNAME or DNAME cached ++ * by a concurrent query): ISC_R_SUCCESS, because that cached positive ++ * answer is what gets returned. Note the specific case for CNAME and ++ * DNAME *if* the query type is not the same as the rdataset type. There ++ * is a chain to follow *only* if the query type doesn't ask for the CNAME ++ * or the DNAME. ++ */ ++static isc_result_t ++fctx_setresult(fetchctx_t *fctx, dns_rdataset_t *rdataset) { ++ isc_result_t result = ISC_R_SUCCESS; ++ ++ if (NEGATIVE(rdataset)) { ++ result = NXDOMAIN(rdataset) ? DNS_R_NCACHENXDOMAIN ++ : DNS_R_NCACHENXRRSET; ++ } else if (result == ISC_R_SUCCESS && rdataset->type != fctx->type) { ++ switch (rdataset->type) { ++ case dns_rdatatype_cname: ++ result = DNS_R_CNAME; ++ break; ++ case dns_rdatatype_dname: ++ result = DNS_R_DNAME; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return result; ++} ++ + /* + * The validator has finished. + */ +@@ -5499,8 +5539,7 @@ validated(void *arg) { + ttl = 0; + } + +- result = ncache_adderesult(message, fctx->cache, node, covers, +- now, fctx->res->view->minncachettl, ++ result = ncache_adderesult(fctx, message, node, covers, now, + ttl, val->optout, val->secure, + ardataset, &eresult); + if (result != ISC_R_SUCCESS) { +@@ -5748,23 +5787,7 @@ answer_response: + */ + INSIST(hresp->rdataset != NULL); + if (dns_rdataset_isassociated(hresp->rdataset)) { +- if (NEGATIVE(hresp->rdataset)) { +- INSIST(eresult == DNS_R_NCACHENXDOMAIN || +- eresult == DNS_R_NCACHENXRRSET); +- } else if (eresult == ISC_R_SUCCESS && +- hresp->rdataset->type != fctx->type) +- { +- switch (hresp->rdataset->type) { +- case dns_rdatatype_cname: +- eresult = DNS_R_CNAME; +- break; +- case dns_rdatatype_dname: +- eresult = DNS_R_DNAME; +- break; +- default: +- break; +- } +- } ++ eresult = fctx_setresult(fctx, hresp->rdataset); + } + + hresp->result = eresult; +@@ -6416,24 +6439,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message, + * resp->result. + */ + if (dns_rdataset_isassociated(resp->rdataset)) { +- if (NEGATIVE(resp->rdataset)) { +- INSIST(eresult == +- DNS_R_NCACHENXDOMAIN || +- eresult == DNS_R_NCACHENXRRSET); +- } else if (eresult == ISC_R_SUCCESS && +- resp->rdataset->type != fctx->type) +- { +- switch (resp->rdataset->type) { +- case dns_rdatatype_cname: +- eresult = DNS_R_CNAME; +- break; +- case dns_rdatatype_dname: +- eresult = DNS_R_DNAME; +- break; +- default: +- break; +- } +- } ++ eresult = fctx_setresult(fctx, resp->rdataset); + } + resp->result = eresult; + if (adbp != NULL && *adbp != NULL) { +@@ -6502,12 +6508,14 @@ cache_message(fetchctx_t *fctx, dns_message_t *message, + * eresult. + */ + static isc_result_t +-ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, +- dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl, +- dns_ttl_t maxttl, bool optout, bool secure, +- dns_rdataset_t *ardataset, isc_result_t *eresultp) { ++ncache_adderesult(fetchctx_t *fctx, dns_message_t *message, dns_dbnode_t *node, ++ dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, ++ bool optout, bool secure, dns_rdataset_t *ardataset, ++ isc_result_t *eresultp) { + isc_result_t result; + dns_rdataset_t rdataset; ++ dns_db_t *cache = fctx->cache; ++ dns_ttl_t minttl = fctx->res->view->minncachettl; + + if (ardataset == NULL) { + dns_rdataset_init(&rdataset); +@@ -6523,37 +6531,13 @@ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, + } + if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) { + /* +- * If the cache now contains a negative entry and we +- * care about whether it is DNS_R_NCACHENXDOMAIN or +- * DNS_R_NCACHENXRRSET then extract it. ++ * The cache settled successfully (DNS_R_UNCHANGED means our ++ * negative entry was discarded in favour of existing ++ * higher-trust data). Either way 'ardataset' is now bound to ++ * the rdataset the cache holds for this name and type; derive ++ * the result code from it. + */ +- if (NEGATIVE(ardataset)) { +- /* +- * The cache data is a negative cache entry. +- */ +- if (NXDOMAIN(ardataset)) { +- *eresultp = DNS_R_NCACHENXDOMAIN; +- } else { +- *eresultp = DNS_R_NCACHENXRRSET; +- } +- } else { +- /* +- * The attempt to add a negative cache entry +- * was rejected. Set *eresultp to reflect +- * the type of the dataset being returned. +- */ +- switch (ardataset->type) { +- case dns_rdatatype_cname: +- *eresultp = DNS_R_CNAME; +- break; +- case dns_rdatatype_dname: +- *eresultp = DNS_R_DNAME; +- break; +- default: +- *eresultp = ISC_R_SUCCESS; +- break; +- } +- } ++ *eresultp = fctx_setresult(fctx, ardataset); + result = ISC_R_SUCCESS; + } + if (ardataset == &rdataset && dns_rdataset_isassociated(ardataset)) { +@@ -6697,8 +6681,7 @@ ncache_message(fetchctx_t *fctx, dns_message_t *message, + ttl = 0; + } + +- result = ncache_adderesult(message, fctx->cache, node, covers, now, +- fctx->res->view->minncachettl, ttl, false, ++ result = ncache_adderesult(fctx, message, node, covers, now, ttl, false, + false, ardataset, &eresult); + if (result != ISC_R_SUCCESS) { + goto unlock; diff --git a/debian/patches/series b/debian/patches/series index 88c2b1b2..2a037b11 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,3 +1,4 @@ 0001-Disable-treat-warnings-as-errors-in-sphinx-build.patch 0002-Disable-RTLD_DEEPBIND-in-Samba-DLZ-module.patch CVE-2026-13204.patch +CVE-2026-12617.patch From c89a88813f106569b9e8c95ae9c090d8e4dd7961 Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 09:43:15 +0800 Subject: [PATCH 3/9] fix(cve): CVE-2026-11622 - Make the dns_slabheaders in the cache reference counted CVE: CVE-2026-11622 (high) - A DNSSEC validating resolver that is under a random subdomain attack against a DNSSEC-signed zone can suffer from runaway memory usage. The attacker needs to be able to send queries faster than the resolver can perform validation. The increased memory usage can be orders of magnitude beyond the limit configured in the parameter. Upstream: https://github.com/isc-projects/bind9/commit/2dabf117e1264fd13fb33096f87e78a039fd1c6c Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 + debian/patches/CVE-2026-11622.patch | 581 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 588 insertions(+) create mode 100644 debian/patches/CVE-2026-11622.patch diff --git a/debian/changelog b/debian/changelog index 1eee71c0..953e1afa 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin4) unstable; urgency=medium + + * fix(cve): CVE-2026-11622 + + -- deepin-ci-robot Fri, 24 Jul 2026 09:43:15 +0800 + bind9 (1:9.20.23-1~deb13u1deepin3) unstable; urgency=medium * fix(cve): CVE-2026-12617 diff --git a/debian/patches/CVE-2026-11622.patch b/debian/patches/CVE-2026-11622.patch new file mode 100644 index 00000000..1de9f7b9 --- /dev/null +++ b/debian/patches/CVE-2026-11622.patch @@ -0,0 +1,581 @@ +Description: CVE-2026-11622 - 安全修复 +Author: Ondřej Surý +Origin: https://github.com/isc-projects/bind9/commit/2dabf117e1264fd13fb33096f87e78a039fd1c6c +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-11622 +Last-Update: Tue, 23 Jun 2026 10:59:38 +0200 +--- +diff --git a/bin/tests/system/reclimit/tests.sh b/bin/tests/system/reclimit/tests.sh +index 10ef4c3..94dce0c 100644 +--- a/bin/tests/system/reclimit/tests.sh ++++ b/bin/tests/system/reclimit/tests.sh +@@ -340,13 +340,13 @@ echo_i "checking that NXDOMAIN names over the max-types-per-name limit don't get + + # Query for 10 NXDOMAIN types + for ntype in $(seq 65270 65279); do +- check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR big SOA 0 || ret=1 ++ check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR big SOA 120 || ret=1 + done + # Wait at least 1 second + sleep 1 + # Query for 10 NXDOMAIN types again - these should not be cached + for ntype in $(seq 65270 65279); do +- check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR big SOA 0 || ret=1 ++ check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR big SOA 120 || ret=1 + done + + if [ $ret -ne 0 ]; then echo_i "failed"; fi +diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h +index 8fc6832..6eeb55d 100644 +--- a/lib/dns/include/dns/rdataslab.h ++++ b/lib/dns/include/dns/rdataslab.h +@@ -46,6 +46,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -95,6 +96,8 @@ struct dns_slabheader { + isc_stdtime_t last_used; + _Atomic(uint32_t) last_refresh_fail_ts; + ++ isc_refcount_t references; ++ + dns_slabheader_proof_t *noqname; + dns_slabheader_proof_t *closest; + /*%< +diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c +index f138678..24b2f2c 100644 +--- a/lib/dns/qpcache.c ++++ b/lib/dns/qpcache.c +@@ -584,13 +584,19 @@ update_header(qpcache_t *qpdb, dns_slabheader_t *header, isc_stdtime_t now) { + + static void + clean_stale_headers(dns_slabheader_t *top) { +- dns_slabheader_t *d = NULL, *down_next = NULL; ++ dns_slabheader_t *d = NULL, *down_next = NULL, *down_parent = top; + + for (d = top->down; d != NULL; d = down_next) { + down_next = d->down; +- dns_slabheader_destroy(&d); ++ d->next = down_parent; ++ ++ if (isc_refcount_current(&d->references) == 0) { ++ dns_slabheader_destroy(&d); ++ down_parent->down = down_next; ++ } else { ++ down_parent = d; ++ } + } +- top->down = NULL; + } + + static void +@@ -604,6 +610,7 @@ clean_cache_node(qpcache_t *qpdb, qpcnode_t *node) { + for (current = node->data; current != NULL; current = top_next) { + top_next = current->next; + clean_stale_headers(current); ++ INSIST(current->down == NULL); + /* + * If current is nonexistent, ancient, or stale and + * we are not keeping stale, we can clean it up. +@@ -974,9 +981,13 @@ setttl(dns_slabheader_t *header, dns_ttl_t newttl) { + + static void + mark_ancient(dns_slabheader_t *header) { +- setttl(header, 0); +- mark(header, DNS_SLABHEADERATTR_ANCIENT); +- HEADERNODE(header)->dirty = 1; ++ if (!ANCIENT(header)) { ++ setttl(header, 0); ++ mark(header, DNS_SLABHEADERATTR_ANCIENT); ++ HEADERNODE(header)->dirty = 1; ++ isc_refcount_decrement(&header->references); ++ clean_stale_headers(header); ++ } + } + + /* +@@ -1065,6 +1076,8 @@ bindrdataset(qpcache_t *qpdb, qpcnode_t *node, dns_slabheader_t *header, + return; + } + ++ isc_refcount_increment(&header->references); ++ + qpcnode_acquire(qpdb, node, nlocktype, tlocktype DNS__DB_FLARG_PASS); + + INSIST(rdataset->methods == NULL); /* We must be disassociated. */ +@@ -2835,6 +2848,7 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, + dns_slabheader_t *prioheader = NULL, *expireheader = NULL; + bool header_nx; + bool newheader_nx; ++ bool do_expireheader = false; + dns_typepair_t negtype = 0; + dns_trust_t trust; + int idx; +@@ -3239,6 +3253,8 @@ find_header: + } + + if (overmaxtype(qpdb, ntypes)) { ++ do_expireheader = true; ++ + if (expireheader == NULL) { + expireheader = newheader; + } +@@ -3252,14 +3268,6 @@ find_header: + */ + expireheader = newheader; + } +- +- mark_ancient(expireheader); +- /* +- * FIXME: In theory, we should mark the RRSIG +- * and the header at the same time, but there is +- * no direct link between those two header, so +- * we would have to check the whole list again. +- */ + } + } + } +@@ -3269,6 +3277,14 @@ find_header: + addedrdataset DNS__DB_FLARG_PASS); + } + ++ /* ++ * We need to delay the expiration of the header until we are bound to ++ * it to prevent decrement-then-increment on the header references. ++ */ ++ if (do_expireheader) { ++ mark_ancient(expireheader); ++ } ++ + return ISC_R_SUCCESS; + } + +@@ -3721,6 +3737,11 @@ rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { + + iterator = (qpc_rditer_t *)(*iteratorp); + ++ if (iterator->current != NULL) { ++ isc_refcount_decrement(&iterator->current->references); ++ iterator->current = NULL; ++ } ++ + dns__db_detachnode(iterator->common.db, + &iterator->common.node DNS__DB_FLARG_PASS); + isc_mem_put(iterator->common.db->mctx, iterator, sizeof(*iterator)); +@@ -3790,8 +3811,17 @@ rdatasetiter_first(dns_rdatasetiter_t *it DNS__DB_FLARG) { + } + } + ++ if (header != NULL) { ++ isc_refcount_increment0(&header->references); ++ } ++ + NODE_UNLOCK(nlock, &nlocktype); + ++ if (iterator->current != NULL) { ++ isc_refcount_decrement(&iterator->current->references); ++ iterator->current = NULL; ++ } ++ + iterator->current = header; + + if (header == NULL) { +@@ -3879,8 +3909,17 @@ rdatasetiter_next(dns_rdatasetiter_t *it DNS__DB_FLARG) { + } + } + ++ if (header != NULL) { ++ isc_refcount_increment0(&header->references); ++ } ++ + NODE_UNLOCK(nlock, &nlocktype); + ++ if (iterator->current != NULL) { ++ isc_refcount_decrement(&iterator->current->references); ++ iterator->current = NULL; ++ } ++ + iterator->current = header; + + if (header == NULL) { +diff --git a/lib/dns/qpzone.c b/lib/dns/qpzone.c +index 1b69587..69954ae 100644 +--- a/lib/dns/qpzone.c ++++ b/lib/dns/qpzone.c +@@ -918,6 +918,8 @@ bindrdataset(qpzonedb_t *qpdb, qpznode_t *node, dns_slabheader_t *header, + return; + } + ++ isc_refcount_increment0(&header->references); ++ + qpznode_acquire(qpdb, node DNS__DB_FLARG_PASS); + + INSIST(rdataset->methods == NULL); /* We must be disassociated. */ +@@ -4050,6 +4052,11 @@ rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { + + qrditer = (qpdb_rdatasetiter_t *)(*iteratorp); + ++ if (qrditer->current != NULL) { ++ isc_refcount_decrement(&qrditer->current->references); ++ qrditer->current = NULL; ++ } ++ + if (qrditer->common.version != NULL) { + closeversion(qrditer->common.db, &qrditer->common.version, + false DNS__DB_FLARG_PASS); +@@ -4092,8 +4099,17 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) { + } + } + ++ if (header != NULL) { ++ isc_refcount_increment0(&header->references); ++ } ++ + NODE_UNLOCK(nlock, &nlocktype); + ++ if (qrditer->current != NULL) { ++ isc_refcount_decrement(&qrditer->current->references); ++ qrditer->current = NULL; ++ } ++ + qrditer->current = header; + + if (header == NULL) { +@@ -4164,8 +4180,17 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG) { + } + } + ++ if (header != NULL) { ++ isc_refcount_increment0(&header->references); ++ } ++ + NODE_UNLOCK(nlock, &nlocktype); + ++ if (qrditer->current != NULL) { ++ isc_refcount_decrement(&qrditer->current->references); ++ qrditer->current = NULL; ++ } ++ + qrditer->current = header; + + if (header == NULL) { +diff --git a/lib/dns/rbt-cachedb.c b/lib/dns/rbt-cachedb.c +index 09fc281..91a9ad8 100644 +--- a/lib/dns/rbt-cachedb.c ++++ b/lib/dns/rbt-cachedb.c +@@ -242,17 +242,6 @@ update_cachestats(dns_rbtdb_t *rbtdb, isc_result_t result) { + } + } + +-static void +-clean_stale_headers(dns_slabheader_t *top) { +- dns_slabheader_t *d = NULL, *down_next = NULL; +- +- for (d = top->down; d != NULL; d = down_next) { +- down_next = d->down; +- dns_slabheader_destroy(&d); +- } +- top->down = NULL; +-} +- + static isc_result_t + setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep, + dns_name_t *foundname, dns_rdataset_t *rdataset, +@@ -403,7 +392,7 @@ check_stale_header(dns_rbtnode_t *node, dns_slabheader_t *header, + * which case we need to purge the stale + * headers first. + */ +- clean_stale_headers(header); ++ dns__rbtdb_clean_stale_headers(header); + if (*header_prev != NULL) { + (*header_prev)->next = header->next; + } else { +@@ -412,6 +401,7 @@ check_stale_header(dns_rbtnode_t *node, dns_slabheader_t *header, + dns_slabheader_destroy(&header); + } else { + dns__rbtdb_mark_ancient(header); ++ dns__rbtdb_clean_stale_headers(header); + *header_prev = header; + } + } else { +@@ -1394,6 +1384,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + * argument to the function. + */ + dns__rbtdb_mark_ancient(header); ++ dns__rbtdb_clean_stale_headers(header); + } + } else if (EXISTS(header) && !ANCIENT(header)) { + if (header->type == matchtype) { +@@ -1580,6 +1571,7 @@ dns__cacherbt_expireheader(dns_slabheader_t *header, + isc_rwlocktype_t *tlocktypep, + dns_expire_t reason DNS__DB_FLARG) { + dns__rbtdb_mark_ancient(header); ++ dns__rbtdb_clean_stale_headers(header); + + if (isc_refcount_current(&RBTDB_HEADERNODE(header)->references) == 0) { + isc_rwlocktype_t nlocktype = isc_rwlocktype_write; +diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c +index 9b904de..e5c1db2 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -856,20 +856,29 @@ dns__rbtdb_mark(dns_slabheader_t *header, uint_least16_t flag) { + + void + dns__rbtdb_mark_ancient(dns_slabheader_t *header) { +- dns__rbtdb_setttl(header, 0); +- dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT); +- RBTDB_HEADERNODE(header)->dirty = 1; ++ if (!ANCIENT(header)) { ++ dns__rbtdb_setttl(header, 0); ++ dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT); ++ RBTDB_HEADERNODE(header)->dirty = 1; ++ isc_refcount_decrement(&header->references); ++ } + } + +-static void +-clean_stale_headers(dns_slabheader_t *top) { +- dns_slabheader_t *d = NULL, *down_next = NULL; ++void ++dns__rbtdb_clean_stale_headers(dns_slabheader_t *top) { ++ dns_slabheader_t *d = NULL, *down_next = NULL, *down_parent = top; + + for (d = top->down; d != NULL; d = down_next) { + down_next = d->down; +- dns_slabheader_destroy(&d); ++ d->next = down_parent; ++ ++ if (isc_refcount_current(&d->references) == 0) { ++ dns_slabheader_destroy(&d); ++ down_parent->down = down_next; ++ } else { ++ down_parent = d; ++ } + } +- top->down = NULL; + } + + static void +@@ -882,7 +891,8 @@ clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { + + for (current = node->data; current != NULL; current = top_next) { + top_next = current->next; +- clean_stale_headers(current); ++ dns__rbtdb_clean_stale_headers(current); ++ INSIST(current->down == NULL); + /* + * If current is nonexistent, ancient, or stale and + * we are not keeping stale, we can clean it up. +@@ -2131,6 +2141,8 @@ dns__rbtdb_bindrdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, + return; + } + ++ isc_refcount_increment(&header->references); ++ + dns__rbtnode_acquire(rbtdb, node, locktype DNS__DB_FLARG_PASS); + + INSIST(rdataset->methods == NULL); /* We must be disassociated. */ +@@ -2549,6 +2561,7 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, + bool header_nx; + bool newheader_nx; + bool merge; ++ bool do_expireheader = false; + dns_rdatatype_t rdtype, covers; + dns_typepair_t negtype = 0, sigtype; + dns_trust_t trust; +@@ -3108,6 +3121,7 @@ find_header: + } + + if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) { ++ do_expireheader = true; + if (expireheader == NULL) { + expireheader = newheader; + } +@@ -3121,14 +3135,6 @@ find_header: + */ + expireheader = newheader; + } +- +- dns__rbtdb_mark_ancient(expireheader); +- /* +- * FIXME: In theory, we should mark the RRSIG +- * and the header at the same time, but there is +- * no direct link between those two header, so +- * we would have to check the whole list again. +- */ + } + } + } +@@ -3153,6 +3159,14 @@ find_header: + addedrdataset DNS__DB_FLARG_PASS); + } + ++ /* ++ * We need to delay the expiration of the header until we are bound to ++ * it to prevent decrement-then-increment on the header references. ++ */ ++ if (do_expireheader) { ++ dns__rbtdb_mark_ancient(expireheader); ++ } ++ + return ISC_R_SUCCESS; + } + +@@ -4131,6 +4145,11 @@ rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { + + rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp); + ++ if (rbtiterator->current != NULL) { ++ isc_refcount_decrement(&rbtiterator->current->references); ++ rbtiterator->current = NULL; ++ } ++ + if (rbtiterator->common.version != NULL) { + dns__rbtdb_closeversion(rbtiterator->common.db, + &rbtiterator->common.version, +@@ -4210,8 +4229,17 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) { + } + } + ++ if (header != NULL) { ++ isc_refcount_increment0(&header->references); ++ } ++ + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); + ++ if (rbtiterator->current != NULL) { ++ isc_refcount_decrement(&rbtiterator->current->references); ++ rbtiterator->current = NULL; ++ } ++ + rbtiterator->current = header; + + if (header == NULL) { +@@ -4303,8 +4331,17 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG) { + } + } + ++ if (header != NULL) { ++ isc_refcount_increment0(&header->references); ++ } ++ + NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); + ++ if (rbtiterator->current != NULL) { ++ isc_refcount_decrement(&rbtiterator->current->references); ++ rbtiterator->current = NULL; ++ } ++ + rbtiterator->current = header; + + if (header == NULL) { +diff --git a/lib/dns/rbtdb_p.h b/lib/dns/rbtdb_p.h +index 1b2c7dc..7a71836 100644 +--- a/lib/dns/rbtdb_p.h ++++ b/lib/dns/rbtdb_p.h +@@ -494,5 +494,7 @@ dns__cacherbt_expireheader(dns_slabheader_t *header, + void + dns__cacherbt_overmem(dns_rbtdb_t *rbtdb, dns_slabheader_t *newheader, + isc_rwlocktype_t *tlocktypep DNS__DB_FLARG); ++void ++dns__rbtdb_clean_stale_headers(dns_slabheader_t *top); + + ISC_LANG_ENDDECLS +diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c +index ea2aa92..f059c6a 100644 +--- a/lib/dns/rdataslab.c ++++ b/lib/dns/rdataslab.c +@@ -1101,6 +1101,7 @@ dns_slabheader_reset(dns_slabheader_t *h, dns_db_t *db, dns_dbnode_t *node) { + + atomic_init(&h->attributes, 0); + atomic_init(&h->last_refresh_fail_ts, 0); ++ isc_refcount_init(&h->references, 1); + + STATIC_ASSERT(sizeof(h->attributes) == 2, + "The .attributes field of dns_slabheader_t needs to be " +@@ -1173,6 +1174,22 @@ dns_rdatasetmethods_t dns_rdataslab_rdatasetmethods = { + .getownercase = rdataset_getownercase, + }; + ++dns_rdatasetmethods_t dns_rdataproof_rdatasetmethods = { ++ .disassociate = rdataset_disassociate, ++ .first = rdataset_first, ++ .next = rdataset_next, ++ .current = rdataset_current, ++ .clone = rdataset_clone, ++ .count = rdataset_count, ++ .getnoqname = rdataset_getnoqname, ++ .getclosest = rdataset_getclosest, ++ .settrust = rdataset_settrust, ++ .expire = rdataset_expire, ++ .clearprefetch = rdataset_clearprefetch, ++ .setownercase = rdataset_setownercase, ++ .getownercase = rdataset_getownercase, ++}; ++ + /* Fixed RRSet helper macros */ + + #define DNS_RDATASET_LENGTH 2; +@@ -1190,6 +1207,11 @@ rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) { + dns_db_t *db = rdataset->slab.db; + dns_dbnode_t *node = rdataset->slab.node; + ++ if (rdataset->methods == &dns_rdataslab_rdatasetmethods) { ++ dns_slabheader_t *header = ++ dns_slabheader_fromrdataset(rdataset); ++ isc_refcount_decrement(&header->references); ++ } + dns__db_detachnode(db, &node DNS__DB_FLARG_PASS); + } + +@@ -1295,6 +1317,11 @@ rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) { + dns_dbnode_t *node = source->slab.node; + dns_dbnode_t *cloned_node = NULL; + ++ if (source->methods == &dns_rdataslab_rdatasetmethods) { ++ dns_slabheader_t *header = dns_slabheader_fromrdataset(source); ++ isc_refcount_increment(&header->references); ++ } ++ + dns__db_attachnode(db, node, &cloned_node DNS__DB_FLARG_PASS); + INSIST(!ISC_LINK_LINKED(target, link)); + *target = *source; +@@ -1334,7 +1361,7 @@ rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, + dns__db_attachnode(db, node, + &(dns_dbnode_t *){ NULL } DNS__DB_FLARG_PASS); + *nsec = (dns_rdataset_t){ +- .methods = &dns_rdataslab_rdatasetmethods, ++ .methods = &dns_rdataproof_rdatasetmethods, + .rdclass = db->rdclass, + .type = noqname->type, + .ttl = rdataset->ttl, +@@ -1351,7 +1378,7 @@ rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, + dns__db_attachnode(db, node, + &(dns_dbnode_t *){ NULL } DNS__DB_FLARG_PASS); + *nsecsig = (dns_rdataset_t){ +- .methods = &dns_rdataslab_rdatasetmethods, ++ .methods = &dns_rdataproof_rdatasetmethods, + .rdclass = db->rdclass, + .type = dns_rdatatype_rrsig, + .covers = noqname->type, +@@ -1387,7 +1414,7 @@ rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name, + dns__db_attachnode(db, node, + &(dns_dbnode_t *){ NULL } DNS__DB_FLARG_PASS); + *nsec = (dns_rdataset_t){ +- .methods = &dns_rdataslab_rdatasetmethods, ++ .methods = &dns_rdataproof_rdatasetmethods, + .rdclass = db->rdclass, + .type = closest->type, + .ttl = rdataset->ttl, +@@ -1404,7 +1431,7 @@ rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name, + dns__db_attachnode(db, node, + &(dns_dbnode_t *){ NULL } DNS__DB_FLARG_PASS); + *nsecsig = (dns_rdataset_t){ +- .methods = &dns_rdataslab_rdatasetmethods, ++ .methods = &dns_rdataproof_rdatasetmethods, + .rdclass = db->rdclass, + .type = dns_rdatatype_rrsig, + .covers = closest->type, diff --git a/debian/patches/series b/debian/patches/series index 2a037b11..8b8e9f40 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -2,3 +2,4 @@ 0002-Disable-RTLD_DEEPBIND-in-Samba-DLZ-module.patch CVE-2026-13204.patch CVE-2026-12617.patch +CVE-2026-11622.patch From 22491beec5b94c77f8dea607060416950a200822 Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 09:52:54 +0800 Subject: [PATCH 4/9] fix(cve): CVE-2026-13321 - change dns_nsec_requiredtypespresent to dns_nsec_is_legal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE: CVE-2026-13321 (high) - 修复 NSEC 记录验证逻辑,将 dns_nsec_requiredtypespresent 函数重命名为 dns_nsec_is_legal,并增加对区域外 NSEC 条目的检查 Upstream: https://github.com/isc-projects/bind9/commit/27ceccfe778d974840bc5267162bbac58049fe98 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 + debian/patches/CVE-2026-13321.patch | 184 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 191 insertions(+) create mode 100644 debian/patches/CVE-2026-13321.patch diff --git a/debian/changelog b/debian/changelog index 953e1afa..d0f82652 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin5) unstable; urgency=medium + + * fix(cve): CVE-2026-13321 + + -- deepin-ci-robot Fri, 24 Jul 2026 09:52:54 +0800 + bind9 (1:9.20.23-1~deb13u1deepin4) unstable; urgency=medium * fix(cve): CVE-2026-11622 diff --git a/debian/patches/CVE-2026-13321.patch b/debian/patches/CVE-2026-13321.patch new file mode 100644 index 00000000..2c123773 --- /dev/null +++ b/debian/patches/CVE-2026-13321.patch @@ -0,0 +1,184 @@ +Description: CVE-2026-13321 - 安全修复 +Author: Aydın Mercan +Origin: https://github.com/isc-projects/bind9/commit/27ceccfe778d974840bc5267162bbac58049fe98 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-13321 +Last-Update: 2026-05-12 +--- +diff --git a/lib/dns/include/dns/nsec.h b/lib/dns/include/dns/nsec.h +index 50df8e4..1e71bf1 100644 +--- a/lib/dns/include/dns/nsec.h ++++ b/lib/dns/include/dns/nsec.h +@@ -119,13 +119,21 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, const dns_name_t *name, + */ + + bool +-dns_nsec_requiredtypespresent(dns_rdataset_t *rdataset); +-/* +- * Return true if all the NSEC records in rdataset have both +- * NSEC and RRSIG present. ++dns_nsec_is_legal(dns_rdataset_t *rdataset, const dns_name_t *name); ++/**< ++ * \brief ++ * Validates a rdataset of type NSEC. + * +- * Requires: ++ * 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 +diff --git a/lib/dns/nsec.c b/lib/dns/nsec.c +index 255b9e9..dc48995 100644 +--- a/lib/dns/nsec.c ++++ b/lib/dns/nsec.c +@@ -21,6 +21,7 @@ + #include + + #include ++#include + #include + #include + #include +@@ -499,8 +500,9 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, const dns_name_t *name, + } + + bool +-dns_nsec_requiredtypespresent(dns_rdataset_t *nsecset) { +- dns_rdataset_t rdataset; ++dns_nsec_is_legal(dns_rdataset_t *nsecset, const dns_name_t *name) { ++ dns_rdataset_t rdataset = DNS_RDATASET_INIT; ++ dns_rdata_nsec_t nsec; + isc_result_t result; + bool found = false; + +@@ -515,12 +517,19 @@ dns_nsec_requiredtypespresent(dns_rdataset_t *nsecset) { + { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(&rdataset, &rdata); +- if (!dns_nsec_typepresent(&rdata, dns_rdatatype_nsec) || +- !dns_nsec_typepresent(&rdata, dns_rdatatype_rrsig)) ++ ++ /* 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); +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index 4fe0282..f819ed8 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -73,7 +73,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + + #ifdef WANT_QUERYTRACE +@@ -5294,6 +5296,36 @@ fctx_setresult(fetchctx_t *fctx, dns_rdataset_t *rdataset) { + return result; + } + ++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); ++ ++ 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. + */ +@@ -5323,6 +5355,8 @@ validated(void *arg) { + dns_fixedname_t fwild; + dns_name_t *wild = NULL; + dns_message_t *message = NULL; ++ dns_fixedname_t fsigner; ++ dns_name_t *signer = NULL; + bool done = false; + + valarg = val->arg; +@@ -5687,10 +5721,20 @@ answer_response: + } + + /* +- * Don't cache NSEC if missing NSEC or RRSIG types. ++ * 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_requiredtypespresent(rdataset)) ++ !dns_nsec_is_legal(rdataset, signer)) + { + continue; + } +diff --git a/lib/ns/query.c b/lib/ns/query.c +index 8517b99..bd763c6 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -10166,10 +10166,10 @@ query_coveringnsec(query_ctx_t *qctx) { + } + + /* +- * If NSEC or RRSIG are missing from the type map +- * reject the NSEC RRset. ++ * Check that the NSEC entry is legal. ++ * (NSEC + RRSIG present and the entry isn't out-of-zone) + */ +- if (!dns_nsec_requiredtypespresent(qctx->rdataset)) { ++ if (!dns_nsec_is_legal(qctx->rdataset, signer)) { + goto cleanup; + } + diff --git a/debian/patches/series b/debian/patches/series index 8b8e9f40..fe12f47c 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -3,3 +3,4 @@ CVE-2026-13204.patch CVE-2026-12617.patch CVE-2026-11622.patch +CVE-2026-13321.patch From ff4826141e6614d96bf4d34bee74430b916965ba Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 10:05:17 +0800 Subject: [PATCH 5/9] fix(cve): CVE-2026-11605 - Limit DNSSEC denial proof validation per fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE: CVE-2026-11605 (high) - DNSSEC 否认证明验证未限制每获取请求的验证计数,可能导致拒绝服务攻击 Upstream: https://github.com/isc-projects/bind9/commit/c3388378a7699c956603bc405f73e38d2c344dc8 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++++++ debian/patches/CVE-2026-11605.patch | 21 +++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 28 insertions(+) create mode 100644 debian/patches/CVE-2026-11605.patch diff --git a/debian/changelog b/debian/changelog index d0f82652..76d30e2a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin6) unstable; urgency=medium + + * fix(cve): CVE-2026-11605 + + -- deepin-ci-robot Fri, 24 Jul 2026 10:05:17 +0800 + bind9 (1:9.20.23-1~deb13u1deepin5) unstable; urgency=medium * fix(cve): CVE-2026-13321 diff --git a/debian/patches/CVE-2026-11605.patch b/debian/patches/CVE-2026-11605.patch new file mode 100644 index 00000000..1c160387 --- /dev/null +++ b/debian/patches/CVE-2026-11605.patch @@ -0,0 +1,21 @@ +Description: CVE-2026-11605 - 安全修复 +Author: Ondřej Surý +Origin: https://github.com/isc-projects/bind9/commit/c3388378a7699c956603bc405f73e38d2c344dc8 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-11605 +Last-Update: 2026-06-07 10:19:44 +0200 +--- +diff --git a/lib/dns/validator.c b/lib/dns/validator.c +index dc7b990..b8267b2 100644 +--- a/lib/dns/validator.c ++++ b/lib/dns/validator.c +@@ -898,6 +898,10 @@ validator_callback_nsec(void *arg) { + switch (eresult) { + case ISC_R_CANCELED: + case ISC_R_SHUTTINGDOWN: ++ case ISC_R_QUOTA: ++ val->attributes |= subvalidator->attributes & ++ (VALATTR_MAXVALIDATIONS | ++ VALATTR_MAXVALIDATIONFAILS); + result = eresult; + break; + case DNS_R_BROKENCHAIN: diff --git a/debian/patches/series b/debian/patches/series index fe12f47c..3cd13a93 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -4,3 +4,4 @@ CVE-2026-13204.patch CVE-2026-12617.patch CVE-2026-11622.patch CVE-2026-13321.patch +CVE-2026-11605.patch From 57f9f2d2e04ebe5a230dd1fc764d9c075aee49b6 Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 10:10:37 +0800 Subject: [PATCH 6/9] fix(cve): CVE-2026-11331 - [9.20] [CVE-2026-11331] sec: usr: Fix handling of rpz CNAME expansion th MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE: CVE-2026-11331 (high) - 修复 rpz CNAME 扩展返回名称过长时的处理问题 Upstream: https://github.com/isc-projects/bind9/commit/b950da5f625488e2b84575f75052c5f366a4cb06 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++++++ debian/patches/CVE-2026-11331.patch | 20 ++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 27 insertions(+) create mode 100644 debian/patches/CVE-2026-11331.patch diff --git a/debian/changelog b/debian/changelog index 76d30e2a..fc224edd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin7) unstable; urgency=medium + + * fix(cve): CVE-2026-11331 + + -- deepin-ci-robot Fri, 24 Jul 2026 10:10:36 +0800 + bind9 (1:9.20.23-1~deb13u1deepin6) unstable; urgency=medium * fix(cve): CVE-2026-11605 diff --git a/debian/patches/CVE-2026-11331.patch b/debian/patches/CVE-2026-11331.patch new file mode 100644 index 00000000..6d47d454 --- /dev/null +++ b/debian/patches/CVE-2026-11331.patch @@ -0,0 +1,20 @@ +Description: CVE-2026-11331 - 安全修复 +Author: Mark Andrews +Origin: https://github.com/isc-projects/bind9/commit/b950da5f625488e2b84575f75052c5f366a4cb06 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-11331 +Last-Update: 2026-06-12 +--- +diff --git a/lib/ns/query.c b/lib/ns/query.c +index bd763c6..08f0a1a 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -7544,7 +7544,8 @@ query_rpzcname(query_ctx_t *qctx, dns_name_t *cname) { + qctx->fname, NULL); + if (result == DNS_R_NAMETOOLONG) { + client->message->rcode = dns_rcode_yxdomain; +- } else if (result != ISC_R_SUCCESS) { ++ } ++ if (result != ISC_R_SUCCESS) { + return result; + } + } else { diff --git a/debian/patches/series b/debian/patches/series index 3cd13a93..98a014f8 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -5,3 +5,4 @@ CVE-2026-12617.patch CVE-2026-11622.patch CVE-2026-13321.patch CVE-2026-11605.patch +CVE-2026-11331.patch From 7dc359fd3893391f09848e299be8e886a99b91d8 Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 10:18:02 +0800 Subject: [PATCH 7/9] fix(cve): CVE-2026-10822 - [9.20] [CVE-2026-10822] sec: usr: Malformed DNSKEY records could trigger CVE: CVE-2026-10822 (medium) - Malformed DNSKEY records could trigger an assertion Upstream: https://github.com/isc-projects/bind9/commit/9a82af76e8b7249f512dd50a9a7925270475a22f Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++ debian/patches/CVE-2026-10822.patch | 156 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 163 insertions(+) create mode 100644 debian/patches/CVE-2026-10822.patch diff --git a/debian/changelog b/debian/changelog index fc224edd..e6958995 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin8) unstable; urgency=medium + + * fix(cve): CVE-2026-10822 + + -- deepin-ci-robot Fri, 24 Jul 2026 10:18:02 +0800 + bind9 (1:9.20.23-1~deb13u1deepin7) unstable; urgency=medium * fix(cve): CVE-2026-11331 diff --git a/debian/patches/CVE-2026-10822.patch b/debian/patches/CVE-2026-10822.patch new file mode 100644 index 00000000..f920c979 --- /dev/null +++ b/debian/patches/CVE-2026-10822.patch @@ -0,0 +1,156 @@ +Description: CVE-2026-10822 - 安全修复 +Author: Mark Andrews +Origin: https://github.com/isc-projects/bind9/commit/9a82af76e8b7249f512dd50a9a7925270475a22f +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-10822 +Last-Update: 2026-07-10 09:33:40 +0200 +--- +diff --git a/bin/tests/system/xfer/ans9/ans.py b/bin/tests/system/xfer/ans9/ans.py +index 25da6b7..a9e7395 100644 +--- a/bin/tests/system/xfer/ans9/ans.py ++++ b/bin/tests/system/xfer/ans9/ans.py +@@ -13,6 +13,7 @@ information regarding copyright ownership. + + from collections.abc import AsyncGenerator + ++import dns.name + import dns.rcode + import dns.rdatatype + import dns.rrset +@@ -33,7 +34,7 @@ class AXFRServer(DomainHandler): + version. + """ + +- domains = ["xfr-and-reconfig"] ++ domains = ["xfr-and-reconfig", "private-dns-overrun"] + + def __init__(self) -> None: + super().__init__() +@@ -97,6 +98,34 @@ class AXFRServer(DomainHandler): + + yield DnsResponseSend(txt_message) + ++ if qctx.qname == dns.name.from_text("private-dns-overrun"): ++ # A message where the malformed DNSKEY algorithm identifier ++ # finishes on a 00 byte in the next record. Assumes the ++ # next record starts with a compression pointer which is ++ # followed by the type which starts with 00. ++ ++ # Generate malformed PRIVATE DNS DNSKEY ++ dnskey_message = qctx.prepare_new_response() ++ dnskey_rrset = dns.rrset.from_text( ++ qctx.qname, ++ 300, ++ qctx.qclass, ++ dns.rdatatype.DNSKEY, ++ "\\# 12 00 00 00 fd 09 00 00 00 00 00 00 00", ++ ) ++ dnskey_message.answer.append(dnskey_rrset) ++ # Generate well formed PRIVATE DNS DNSKEY ++ dnskey_rrset = dns.rrset.from_text( ++ qctx.qname, ++ 300, ++ qctx.qclass, ++ dns.rdatatype.DNSKEY, ++ "\\# 12 00 00 00 fd 06 00 00 00 00 00 00 00", ++ ) ++ dnskey_message.answer.append(dnskey_rrset) ++ ++ yield DnsResponseSend(dnskey_message) ++ + # Finish the AXFR transaction by sending the second SOA RRset. + yield DnsResponseSend(soa_message) + +diff --git a/bin/tests/system/xfer/ns6/named.conf.j2 b/bin/tests/system/xfer/ns6/named.conf.j2 +index 80f4281..c262f9d 100644 +--- a/bin/tests/system/xfer/ns6/named.conf.j2 ++++ b/bin/tests/system/xfer/ns6/named.conf.j2 +@@ -118,3 +118,12 @@ zone "ixfr-race" { + primaries { 10.53.0.11; }; + file "ixfr-race.bk"; + }; ++ ++# GL#6004 ++zone "private-dns-overrun" { ++ type secondary; ++ primaries { 10.53.0.9; }; ++ file "private-dns-overrun.bk"; ++ masterfile-format text; # force bug to be exercised ++ request-ixfr no; # ans9 supports only axfr ++}; +diff --git a/bin/tests/system/xfer/tests_xfer.py b/bin/tests/system/xfer/tests_xfer.py +index b87a75f..1060a70 100644 +--- a/bin/tests/system/xfer/tests_xfer.py ++++ b/bin/tests/system/xfer/tests_xfer.py +@@ -544,6 +544,16 @@ def test_reconfiguration_when_zone_transfer_is_in_the_middle_of_soa_query(ns6): + watcher_transfer_started.wait_for_line("Transfer started") + + ++def test_malformed_private_dns_identifier_overrun(ns6): ++ isctest.log.info( ++ "Check that a malformed PRIVATEDNS DNSKEY which overruns the record is rejected" ++ ) ++ with ns6.watch_log_from_start(timeout=60) as watcher_transfer_completed: ++ watcher_transfer_completed.wait_for_line( ++ "zone private-dns-overrun/IN: zone transfer finished: unexpected end of input" ++ ) ++ ++ + # See #5767 + def test_ixfr_race(ns6): + isctest.log.info( +diff --git a/lib/dns/name.c b/lib/dns/name.c +index aede7da..9436220 100644 +--- a/lib/dns/name.c ++++ b/lib/dns/name.c +@@ -1492,7 +1492,7 @@ dns_name_fromwire(dns_name_t *const name, isc_buffer_t *const source, + * The amount of the source we consumed is set once. + */ + const uint8_t *const source_buf = isc_buffer_base(source); +- const uint8_t *const source_max = isc_buffer_used(source); ++ const uint8_t *const source_max = isc_buffer_active(source); + const uint8_t *const start = isc_buffer_current(source); + const uint8_t *marker = start; + const uint8_t *cursor = start; +diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c +index 42549ac..bd18777 100644 +--- a/lib/dns/rdata.c ++++ b/lib/dns/rdata.c +@@ -591,7 +591,7 @@ check_private(isc_buffer_t *source, dns_secalg_t alg) { + dns_fixedname_t fixed; + + RETERR(dns_name_fromwire(dns_fixedname_initname(&fixed), source, +- DNS_DECOMPRESS_DEFAULT, NULL)); ++ DNS_DECOMPRESS_NEVER, NULL)); + } else if (alg == DNS_KEYALG_PRIVATEOID) { + /* + * Check that we can extract the OID from the start of the +diff --git a/tests/dns/rdata_test.c b/tests/dns/rdata_test.c +index 6dd6e0c..61f3a41 100644 +--- a/tests/dns/rdata_test.c ++++ b/tests/dns/rdata_test.c +@@ -2276,6 +2276,25 @@ ISC_RUN_TEST_IMPL(key) { + + check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in, + dns_rdatatype_key, sizeof(dns_rdata_key_t)); ++ ++ /* ++ * A valid PRIVATEDNS record with an active region shorter than the ++ * actual record length. A bug in dns_name_fromwire meant that this ++ * was previously accepted. ++ */ ++ unsigned char key[] = { 0x00, 0x00, 0x00, 253, 0x07, 'e', 'x', ++ 'a', 'm', 'p', 'l', 'e', 0x00 }; ++ unsigned char buf[sizeof(key)]; ++ isc_buffer_t source, target; ++ isc_result_t result; ++ ++ isc_buffer_init(&source, key, sizeof(key)); ++ isc_buffer_add(&source, sizeof(key)); ++ isc_buffer_setactive(&source, sizeof(key) - 1); ++ isc_buffer_init(&target, buf, sizeof(buf)); ++ result = dns_rdata_fromwire(NULL, dns_rdataclass_in, dns_rdatatype_key, ++ &source, DNS_DECOMPRESS_ALWAYS, &target); ++ assert_int_not_equal(result, ISC_R_SUCCESS); + } + + /* diff --git a/debian/patches/series b/debian/patches/series index 98a014f8..67e8b183 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -6,3 +6,4 @@ CVE-2026-11622.patch CVE-2026-13321.patch CVE-2026-11605.patch CVE-2026-11331.patch +CVE-2026-10822.patch From a68de44aeabd9dd614bd4e8e6814f83aeb3120df Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 13:19:22 +0800 Subject: [PATCH 8/9] fix(cve): CVE-2026-10723 - Check NSEC3 signer matches the owning zone CVE: CVE-2026-10723 (medium) - BIND may accept incorrect child-zone NSEC3 records as valid, which could allow an attacker to forge authenticated NXDOMAIN responses. Upstream: https://gitlab.isc.org/isc-projects/bind9/commit/6965fa47edd3b45538db2b16488dbb6b4ad8066a Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++++ debian/patches/CVE-2026-10723.patch | 51 +++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 58 insertions(+) create mode 100644 debian/patches/CVE-2026-10723.patch diff --git a/debian/changelog b/debian/changelog index e6958995..fc078376 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin9) unstable; urgency=medium + + * fix(cve): CVE-2026-10723 + + -- deepin-ci-robot Fri, 24 Jul 2026 13:19:22 +0800 + bind9 (1:9.20.23-1~deb13u1deepin8) unstable; urgency=medium * fix(cve): CVE-2026-10822 diff --git a/debian/patches/CVE-2026-10723.patch b/debian/patches/CVE-2026-10723.patch new file mode 100644 index 00000000..696c06c6 --- /dev/null +++ b/debian/patches/CVE-2026-10723.patch @@ -0,0 +1,51 @@ +Description: CVE-2026-10723 - 安全修复 +Author: Evan Hunt +Origin: https://gitlab.isc.org/isc-projects/bind9/commit/6965fa47edd3b45538db2b16488dbb6b4ad8066a +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-10723 +Last-Update: Wed Jul 1 23:24:09 2026 -0700 +--- +diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c +index cf95f53622..3bd7069e8a 100644 +--- a/lib/dns/dnssec.c ++++ b/lib/dns/dnssec.c +@@ -402,10 +402,25 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + } + + /* +- * NS, SOA and DNSKEY records are signed by their owner. +- * DS records are signed by the parent. ++ * NS, SOA and DNSKEY records are signed by their owners. ++ * NSEC3 records are signed by the apex, exactly one level up ++ * from their owner names. ++ * DS records are signed by the parent zone. + */ + switch (set->type) { ++ case dns_rdatatype_nsec3: { ++ dns_name_t apex = DNS_NAME_INITEMPTY; ++ labels = dns_name_countlabels(name); ++ if (labels <= 1) { ++ inc_stat(dns_dnssecstats_fail); ++ return DNS_R_INVALIDNSEC3; ++ } ++ dns_name_split(name, labels - 1, NULL, &apex); ++ if (!dns_name_equal(&apex, &sig.signer)) { ++ inc_stat(dns_dnssecstats_fail); ++ return DNS_R_SIGINVALID; ++ } ++ } break; + case dns_rdatatype_ns: + case dns_rdatatype_soa: + case dns_rdatatype_dnskey: +diff --git a/lib/isc/result.c b/lib/isc/result.c +index 29b8bc65f4..3bce804809 100644 +--- a/lib/isc/result.c ++++ b/lib/isc/result.c +@@ -200,7 +200,7 @@ static const char *description[ISC_R_NRESULTS] = { + [DNS_R_COVERINGNSEC] = "covering NSEC record returned", + [DNS_R_MXISADDRESS] = "MX is an address", + [DNS_R_DUPLICATE] = "duplicate query", +- [DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name (wildcard)", ++ [DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name", + [DNS_R_NOTPRIMARY] = "not primary", + [DNS_R_BROKENCHAIN] = "broken trust chain", + [DNS_R_EXPIRED] = "expired", diff --git a/debian/patches/series b/debian/patches/series index 67e8b183..10405f17 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -7,3 +7,4 @@ CVE-2026-13321.patch CVE-2026-11605.patch CVE-2026-11331.patch CVE-2026-10822.patch +CVE-2026-10723.patch From cdc478767e9b8cc651a2e7625f9fa80e52af2a9e Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Fri, 24 Jul 2026 13:43:09 +0800 Subject: [PATCH 9/9] fix(cve): CVE-2026-11721 - Invalid signed wildcard records were being accepted CVE: CVE-2026-11721 (high) - It is possible for an attacker's zone to respond to a query with an RRSIG that has a smaller number of labels than the zone in which the RRSIG is contained. This causes to produce a wildcard name for a zone that is shorter than the attacker's zone, which can result in cache poisoning. For this attack to have any effect, the resolver under attack must have set (which is the default). Upstream: https://github.com/isc-projects/bind9/commit/8a46533cbeab78c215ee035d73967a3a59925375 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 + debian/patches/CVE-2026-11721.patch | 198 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 205 insertions(+) create mode 100644 debian/patches/CVE-2026-11721.patch diff --git a/debian/changelog b/debian/changelog index fc078376..14564868 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +bind9 (1:9.20.23-1~deb13u1deepin10) unstable; urgency=medium + + * fix(cve): CVE-2026-11721 + + -- deepin-ci-robot Fri, 24 Jul 2026 13:43:09 +0800 + bind9 (1:9.20.23-1~deb13u1deepin9) unstable; urgency=medium * fix(cve): CVE-2026-10723 diff --git a/debian/patches/CVE-2026-11721.patch b/debian/patches/CVE-2026-11721.patch new file mode 100644 index 00000000..4fab2672 --- /dev/null +++ b/debian/patches/CVE-2026-11721.patch @@ -0,0 +1,198 @@ +Description: CVE-2026-11721 - 安全修复 +Author: Mark Andrews +Origin: https://github.com/isc-projects/bind9/commit/8a46533cbeab78c215ee035d73967a3a59925375 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-11721 +Last-Update: 2026-04-14 +--- +diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c +index 3bd7069e8a..9f7e869971 100644 +--- a/lib/dns/dnssec.c ++++ b/lib/dns/dnssec.c +@@ -129,7 +129,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); +@@ -186,12 +186,14 @@ dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + isc_result_t ret; + isc_buffer_t *databuf = NULL; + char data[256 + 8]; ++ 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); +@@ -220,7 +222,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--; + } +@@ -363,10 +365,13 @@ 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; + 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); +@@ -381,6 +386,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; +@@ -462,10 +482,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)); + } +@@ -476,7 +495,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); +@@ -571,7 +590,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 80b6fbc711..75ea2e8e90 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 alg, 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; + unsigned int used; +@@ -73,8 +73,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. +@@ -145,12 +145,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. +@@ -297,6 +305,7 @@ fromwire_rrsig(ARGS_FROMWIRE) { + isc_region_t sr; + dns_name_t name; + unsigned char algorithm; ++ unsigned char labels; + + REQUIRE(type == dns_rdatatype_rrsig); + +@@ -320,6 +329,7 @@ fromwire_rrsig(ARGS_FROMWIRE) { + } + + algorithm = sr.base[2]; ++ labels = sr.base[3]; + + isc_buffer_forward(source, 18); + RETERR(mem_tobuffer(target, sr.base, 18)); +@@ -330,6 +340,14 @@ fromwire_rrsig(ARGS_FROMWIRE) { + dns_name_init(&name, NULL); + RETERR(dns_name_fromwire(&name, source, dctx, 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. + */ diff --git a/debian/patches/series b/debian/patches/series index 10405f17..649a1e8c 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -8,3 +8,4 @@ CVE-2026-11605.patch CVE-2026-11331.patch CVE-2026-10822.patch CVE-2026-10723.patch +CVE-2026-11721.patch