* [scarthgap][PATCHv2 2/6] bind: fix for CVE-2026-10822
2026-10-12 4:38 [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
@ 2026-10-12 4:38 ` Hitendra Prajapati
2026-10-12 4:38 ` [scarthgap][PATCHv2 3/6] bind: fix for CVE-2026-11331 Hitendra Prajapati
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Hitendra Prajapati @ 2026-10-12 4:38 UTC (permalink / raw)
To: openembedded-core; +Cc: Hitendra Prajapati
Pick patch from [1], [2], [3], [4] & [5] also mentioned at Debian report in [6]
[1] https://github.com/isc-projects/bind9/commit/d413c9ac2e29a728531354a69c8c8234c01b7d1e
[2] https://github.com/isc-projects/bind9/commit/a5f1a9d0d2ec021618924b14202ac96ead8299c1
[3] https://github.com/isc-projects/bind9/commit/e73b70a64453e7d97a11cb5f0afe8bb02d34aaf8
[4] https://github.com/isc-projects/bind9/commit/19ac8b8e46aeb0a15e217bc7bdf485b31b87d9b4
[5] https://github.com/isc-projects/bind9/commit/8e066d3fc369e3346f22bb5cfb67a7ab08a74034
[6] https://security-tracker.debian.org/tracker/CVE-2026-10822
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
.../bind/bind/CVE-2026-10822-01.patch | 69 ++++++
.../bind/bind/CVE-2026-10822-02.patch | 33 +++
.../bind/bind/CVE-2026-10822-03.patch | 35 +++
.../bind/bind/CVE-2026-10822-04.patch | 53 +++++
.../bind/bind/CVE-2026-10822-05.patch | 223 ++++++++++++++++++
.../recipes-connectivity/bind/bind_9.18.49.bb | 5 +
6 files changed, 418 insertions(+)
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-10822-01.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-10822-02.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-10822-03.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-10822-04.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-10822-05.patch
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-10822-01.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-01.patch
new file mode 100644
index 0000000000..ab39a0e402
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-01.patch
@@ -0,0 +1,69 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 19 May 2026 10:44:04 +1000
+Subject: Check that dns_name_fromwire honours the active region
+
+When reading DNS records from the wire the active region of the
+source buffer is set to the end of the current record. dns_name_fromwire
+should fail if it attempts to read past this setting.
+
+(cherry picked from commit 3ed821d68b15fe4e6288e3054397d6bce7e65968)
+
+Origin: https://github.com/isc-projects/bind9/commit/d413c9ac2e29a728531354a69c8c8234c01b7d1e
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-10822
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-10822
+
+CVE: CVE-2026-10822
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/d413c9ac2e29a728531354a69c8c8234c01b7d1e]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ tests/dns/name_test.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+
+diff --git a/tests/dns/name_test.c b/tests/dns/name_test.c
+index fb34dca..95f6598 100644
+--- a/tests/dns/name_test.c
++++ b/tests/dns/name_test.c
+@@ -335,6 +335,35 @@ ISC_RUN_TEST_IMPL(fromregion) {
+ assert_false(dns_name_isabsolute(&name));
+ }
+
++ISC_RUN_TEST_IMPL(fromwire) {
++ dns_decompress_t dctx;
++ dns_fixedname_t fixed;
++ dns_name_t *name = dns_fixedname_initname(&fixed);
++ isc_buffer_t b;
++ unsigned char source[] = { 0x03, 'o', 'n', 'e', 0x00, 0x03,
++ 't', 'w', 'o', 0x00, 0x05, 't',
++ 'h', 'r', 'e', 'e', 0x00 };
++ isc_result_t result;
++
++ isc_buffer_init(&b, source, sizeof(source));
++ isc_buffer_add(&b, sizeof(source));
++ isc_buffer_setactive(&b, 10); /* names 'one.' and 'two.' */
++
++ /*
++ * We should only be able to read two names from the buffer
++ * as the active region has been set to cover only the first
++ * two.
++ */
++ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
++ dns_decompress_setmethods(&dctx, DNS_COMPRESS_NONE);
++ result = dns_name_fromwire(name, &b, &dctx, 0, NULL);
++ assert_int_equal(result, ISC_R_SUCCESS);
++ result = dns_name_fromwire(name, &b, &dctx, 0, NULL);
++ assert_int_equal(result, ISC_R_SUCCESS);
++ result = dns_name_fromwire(name, &b, &dctx, 0, NULL);
++ assert_int_not_equal(result, ISC_R_SUCCESS);
++}
++
+ /* is trust-anchor-telemetry test */
+ ISC_RUN_TEST_IMPL(istat) {
+ dns_fixedname_t fixed;
+@@ -778,6 +807,7 @@ ISC_TEST_LIST_START
+ ISC_TEST_ENTRY(fullcompare)
+ ISC_TEST_ENTRY(compression)
+ ISC_TEST_ENTRY(fromregion)
++ISC_TEST_ENTRY(fromwire)
+ ISC_TEST_ENTRY(istat)
+ ISC_TEST_ENTRY(init)
+ ISC_TEST_ENTRY(invalidate)
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-10822-02.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-02.patch
new file mode 100644
index 0000000000..9e2eb1d903
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-02.patch
@@ -0,0 +1,33 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 19 May 2026 15:00:17 +1000
+Subject: Fix the yaml query zone name code in dnstap-read
+
+When the buffer to read the query zone name was constructed
+isc_buffer_setactive was not called. This is now needed as
+dns_name_fromwire is being corrected to check the active region.
+
+(cherry picked from commit a25522c28c46655a81d2bf1d96374c81d834b157)
+
+Origin: https://github.com/isc-projects/bind9/commit/a5f1a9d0d2ec021618924b14202ac96ead8299c1
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-10822
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-10822
+
+CVE: CVE-2026-10822
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/a5f1a9d0d2ec021618924b14202ac96ead8299c1]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ bin/tools/dnstap-read.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/bin/tools/dnstap-read.c b/bin/tools/dnstap-read.c
+index a1d0243..bb78ae1 100644
+--- a/bin/tools/dnstap-read.c
++++ b/bin/tools/dnstap-read.c
+@@ -298,6 +298,7 @@ print_yaml(dns_dtdata_t *dt) {
+
+ isc_buffer_init(&b, m->query_zone.data, m->query_zone.len);
+ isc_buffer_add(&b, m->query_zone.len);
++ isc_buffer_setactive(&b, m->query_zone.len);
+
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE);
+ result = dns_name_fromwire(name, &b, &dctx, 0, NULL);
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-10822-03.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-03.patch
new file mode 100644
index 0000000000..cbd22a8492
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-03.patch
@@ -0,0 +1,35 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 19 May 2026 11:12:46 +1000
+Subject: Fix dns_name_fromwire to honour the active region
+
+dns_name_fromwire was not honouring the source buffer's active
+region when reading names from the wire. This allowed malformed
+records to be accepted when they shouldn't have been. This has
+been corrected.
+
+(cherry picked from commit 7c4f07a7ef6b571073327b02209df7f75b9363ff)
+
+Origin: https://github.com/isc-projects/bind9/commit/e73b70a64453e7d97a11cb5f0afe8bb02d34aaf8
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-10822
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-10822
+
+CVE: CVE-2026-10822
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/e73b70a64453e7d97a11cb5f0afe8bb02d34aaf8]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ lib/dns/name.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/dns/name.c b/lib/dns/name.c
+index cc0e30e..2ce868a 100644
+--- a/lib/dns/name.c
++++ b/lib/dns/name.c
+@@ -1833,7 +1833,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/meta/recipes-connectivity/bind/bind/CVE-2026-10822-04.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-04.patch
new file mode 100644
index 0000000000..27e1aa89c7
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-04.patch
@@ -0,0 +1,53 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 19 May 2026 12:03:23 +1000
+Subject: Check that a short PRIVATEDNS record is rejected
+
+A bug in dns_name_fromwire meant that short PRIVATEDNS key
+records where being accepted. Test that this is no longer
+the case.
+
+(cherry picked from commit f48d48027384d8c2210b5ce9e3eac7af101ead3d)
+
+Origin: https://github.com/isc-projects/bind9/commit/19ac8b8e46aeb0a15e217bc7bdf485b31b87d9b4
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-10822
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-10822
+
+CVE: CVE-2026-10822
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/19ac8b8e46aeb0a15e217bc7bdf485b31b87d9b4]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ tests/dns/rdata_test.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+diff --git a/tests/dns/rdata_test.c b/tests/dns/rdata_test.c
+index 6354819..7f0df6e 100644
+--- a/tests/dns/rdata_test.c
++++ b/tests/dns/rdata_test.c
+@@ -2199,6 +2199,27 @@ ISC_RUN_TEST_IMPL(key) {
+
+ check_rdata(NULL, 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.
++ */
++ dns_decompress_t dctx;
++ 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));
++ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
++ result = dns_rdata_fromwire(NULL, dns_rdataclass_in, dns_rdatatype_key,
++ &source, &dctx, 0, &target);
++ assert_int_not_equal(result, ISC_R_SUCCESS);
+ }
+
+ /*
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-10822-05.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-05.patch
new file mode 100644
index 0000000000..a4f2b04b9b
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-10822-05.patch
@@ -0,0 +1,223 @@
+From: Mark Andrews <marka@isc.org>
+Date: Fri, 5 Jun 2026 09:18:30 +1000
+Subject: POC for PRIVATEDNS DNSKEY overrun not being detected
+
+Construct a DNS message where a PRIVATEDNS DNSKEY identifier
+overruns the record boundary by 3 byte so that the label ends
+at the end of the compression pointer for the next record. The
+next type is less than 256 so the next octet is 00 terminating
+the identifier name. The transfered zone is then written to
+disk using master-format text triggering the assertion when the
+truncated identier is discovered.
+
+Note this test will produce a false result in versions of
+BIND that do not check the PRIVATEDNS identifier as it looks
+for the error message when the transfer is aborted.
+
+(cherry picked from commit 9ce3bce8bc8b4e9c6a9b1e84b5849c33eb27830e)
+
+Origin: https://github.com/isc-projects/bind9/commit/8e066d3fc369e3346f22bb5cfb67a7ab08a74034
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-10822
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-10822
+
+CVE: CVE-2026-10822
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/8e066d3fc369e3346f22bb5cfb67a7ab08a74034]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ bin/tests/system/xfer/ans9/ans.py | 142 ++++++++++++++++++++++++++++++++
+ bin/tests/system/xfer/ns6/named.conf.in | 9 ++
+ bin/tests/system/xfer/tests.sh | 16 ++++
+ 3 files changed, 167 insertions(+)
+ create mode 100644 bin/tests/system/xfer/ans9/ans.py
+
+diff --git a/bin/tests/system/xfer/ans9/ans.py b/bin/tests/system/xfer/ans9/ans.py
+new file mode 100644
+index 0000000..a9e7395
+--- /dev/null
++++ b/bin/tests/system/xfer/ans9/ans.py
+@@ -0,0 +1,142 @@
++"""
++Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++
++SPDX-License-Identifier: MPL-2.0
++
++This Source Code Form is subject to the terms of the Mozilla Public
++License, v. 2.0. If a copy of the MPL was not distributed with this
++file, you can obtain one at https://mozilla.org/MPL/2.0/.
++
++See the COPYRIGHT file distributed with this work for additional
++information regarding copyright ownership.
++"""
++
++from collections.abc import AsyncGenerator
++
++import dns.name
++import dns.rcode
++import dns.rdatatype
++import dns.rrset
++
++from isctest.asyncserver import (
++ ControllableAsyncDnsServer,
++ DnsResponseSend,
++ DomainHandler,
++ QueryContext,
++ ResponseAction,
++ ToggleResponsesCommand,
++)
++
++
++class AXFRServer(DomainHandler):
++ """
++ Yield SOA and AXFR responses. Every new AXFR response increments the SOA
++ version.
++ """
++
++ domains = ["xfr-and-reconfig", "private-dns-overrun"]
++
++ def __init__(self) -> None:
++ super().__init__()
++ self.soa_version = 0
++
++ async def get_responses(
++ self, qctx: QueryContext
++ ) -> AsyncGenerator[ResponseAction, None]:
++ # This is oversimplified because I am lazy - we are appending the SOA
++ # RRset to the ANSWER section for _every_ QTYPE. named is only
++ # expected to send a SOA query over UDP and then an AXFR query over
++ # TCP. Responses to both of those start with a SOA RRset in the ANSWER
++ # section :-)
++ soa_message = qctx.response
++ soa_rrset = dns.rrset.from_text(
++ qctx.qname,
++ 300,
++ qctx.qclass,
++ dns.rdatatype.SOA,
++ f". . {self.soa_version} 0 0 0 0",
++ )
++ soa_message.answer.append(soa_rrset)
++
++ yield DnsResponseSend(soa_message)
++
++ if qctx.qtype == dns.rdatatype.SOA:
++ # If QTYPE=SOA, the SOA record is the complete response.
++ return
++
++ if qctx.qtype != dns.rdatatype.AXFR:
++ # If QTYPE=AXFR, we will continue cramming RRsets into the ANSWER
++ # section of a subsequent DNS message below.
++ #
++ # If QTYPE was not SOA or AXFR, abort. Yeah, we just sent a broken
++ # response by yielding DnsResponseSend() with a SOA RRset in the
++ # ANSWER section above. We will have to carry that burden for the
++ # rest of our lives.
++ return
++
++ # Send just the obligatory NS RRset at zone apex in the next message.
++ # This is stupidly inefficient, but makes looping below simpler as we
++ # will already have been done with the mandatory stuff by then.
++ ns_message = qctx.prepare_new_response()
++ ns_rrset = dns.rrset.from_text(
++ qctx.qname, 300, qctx.qclass, dns.rdatatype.NS, "."
++ )
++ ns_message.answer.append(ns_rrset)
++
++ yield DnsResponseSend(ns_message)
++
++ # Generate the AXFR with a txt rrset.
++ txt_message = qctx.prepare_new_response()
++ txt_rrset = dns.rrset.from_text(
++ qctx.qname,
++ 300,
++ qctx.qclass,
++ dns.rdatatype.TXT,
++ "foo bar",
++ )
++ txt_message.answer.append(txt_rrset)
++
++ 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)
++
++ # This makes sure that the next SOA request causes a new zone transfer
++ self.soa_version += 1
++
++
++if __name__ == "__main__":
++ server = ControllableAsyncDnsServer(
++ default_aa=True, default_rcode=dns.rcode.NOERROR
++ )
++ server.install_control_command(ToggleResponsesCommand())
++ server.install_response_handler(AXFRServer())
++ server.run()
+diff --git a/bin/tests/system/xfer/ns6/named.conf.in b/bin/tests/system/xfer/ns6/named.conf.in
+index 142383c..6380944 100644
+--- a/bin/tests/system/xfer/ns6/named.conf.in
++++ b/bin/tests/system/xfer/ns6/named.conf.in
+@@ -83,3 +83,12 @@ zone "ixfr-too-big" {
+ primaries { 10.53.0.1; };
+ file "ixfr-too-big.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.sh b/bin/tests/system/xfer/tests.sh
+index a2c0adb..e08be17 100755
+--- a/bin/tests/system/xfer/tests.sh
++++ b/bin/tests/system/xfer/tests.sh
+@@ -622,5 +622,21 @@ if [ $tmp -eq 0 ]; then
+ fi
+ status=$((status + tmp))
+
++# 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"
++# )
++n=$((n + 1))
++echo_i "Check that a malformed PRIVATEDNS DNSKEY which overruns the record is rejected ($n)"
++tmp=0
++nextpartreset ns6/named.run
++retry 60 wait_for_message "zone private-dns-overrun/IN: zone transfer finished: unexpected end of input" || tmp=1
++if test $tmp != 0; then echo_i "failed"; fi
++status=$((status + tmp))
++
+ echo_i "exit status: $status"
+ [ $status -eq 0 ] || exit 1
diff --git a/meta/recipes-connectivity/bind/bind_9.18.49.bb b/meta/recipes-connectivity/bind/bind_9.18.49.bb
index 161736f9b1..42007383c4 100644
--- a/meta/recipes-connectivity/bind/bind_9.18.49.bb
+++ b/meta/recipes-connectivity/bind/bind_9.18.49.bb
@@ -21,6 +21,11 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.xz \
file://CVE-2026-10723-01.patch \
file://CVE-2026-10723-02.patch \
file://CVE-2026-10723-03.patch \
+ file://CVE-2026-10822-01.patch \
+ file://CVE-2026-10822-02.patch \
+ file://CVE-2026-10822-03.patch \
+ file://CVE-2026-10822-04.patch \
+ file://CVE-2026-10822-05.patch \
"
SRC_URI[sha256sum] = "c43ce4548ebed788cd9df63658a7de105ceafba43fcd63fa352b1093e525cd24"
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [scarthgap][PATCHv2 3/6] bind: fix for CVE-2026-11331
2026-10-12 4:38 [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
2026-10-12 4:38 ` [scarthgap][PATCHv2 2/6] bind: fix for CVE-2026-10822 Hitendra Prajapati
@ 2026-10-12 4:38 ` Hitendra Prajapati
2026-10-12 4:38 ` [scarthgap][PATCHv2 4/6] bind: fix for CVE-2026-11622, CVE-2026-11721 Hitendra Prajapati
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Hitendra Prajapati @ 2026-10-12 4:38 UTC (permalink / raw)
To: openembedded-core; +Cc: Hitendra Prajapati
Pick patch from [1], [2] & [3] also mentioned at Debian report in [4]
[1] https://github.com/isc-projects/bind9/commit/adc8285d23e2eac6ec463f5dbc5a9596fdd36c60
[2] https://github.com/isc-projects/bind9/commit/095b11f20f911f5b8059bdc349b256d6c64ece30
[3] https://github.com/isc-projects/bind9/commit/dc328a199f96222e0c30cc20b7b795bfc2c9b2e4
[4] https://security-tracker.debian.org/tracker/CVE-2026-11331
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
.../bind/bind/CVE-2026-11331-01.patch | 30 ++++++++++
.../bind/bind/CVE-2026-11331-02.patch | 58 +++++++++++++++++++
.../bind/bind/CVE-2026-11331-03.patch | 34 +++++++++++
.../recipes-connectivity/bind/bind_9.18.49.bb | 3 +
4 files changed, 125 insertions(+)
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-11331-01.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-11331-02.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-11331-03.patch
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-11331-01.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-11331-01.patch
new file mode 100644
index 0000000000..4ac06e9271
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-11331-01.patch
@@ -0,0 +1,30 @@
+From: Mark Andrews <marka@isc.org>
+Date: Fri, 10 Apr 2026 10:24:06 +1000
+Subject: Fix TTL extraction from A/AAAA record
+
+(cherry picked from commit 89c86e338db2492b92e6618c586f146c6928dc6d)
+
+Origin: https://github.com/isc-projects/bind9/commit/adc8285d23e2eac6ec463f5dbc5a9596fdd36c60
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-11331
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-11331
+
+CVE: CVE-2026-11331
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/adc8285d23e2eac6ec463f5dbc5a9596fdd36c60]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ bin/tests/system/rpz/tests.sh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/bin/tests/system/rpz/tests.sh b/bin/tests/system/rpz/tests.sh
+index 87e4118..90cf80c 100644
+--- a/bin/tests/system/rpz/tests.sh
++++ b/bin/tests/system/rpz/tests.sh
+@@ -391,7 +391,7 @@ addr() {
+ digcmd $2 >$DIGNM
+ #ckalive "$2" "server crashed by 'dig $2'" || return 1
+ ADDR_ESC=$(echo "$ADDR" | sed -e 's/\./\\./g')
+- ADDR_TTL=$(sed -n -e "s/^[-.a-z0-9]\{1,\}[ ]*\([0-9]*\) IN AA* ${ADDR_ESC}\$/\1/p" $DIGNM)
++ ADDR_TTL=$(sed -n -e "s/^[-.a-z0-9]\{1,\}[ ]*\([0-9]*\)[ ]IN[ ]AA*[ ]${ADDR_ESC}\$/\1/p" $DIGNM)
+ if test -z "$ADDR_TTL"; then
+ setret "'dig $2' wrong; no address $ADDR record in $DIGNM"
+ return 0
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-11331-02.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-11331-02.patch
new file mode 100644
index 0000000000..0291e98df3
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-11331-02.patch
@@ -0,0 +1,58 @@
+From: Mark Andrews <marka@isc.org>
+Date: Fri, 10 Apr 2026 10:24:40 +1000
+Subject: Check rpz name too long wildcard CNAME expansion handling
+
+(cherry picked from commit 9345394e2097031b55b3ef34ceaadf5a7ebbeef2)
+
+Origin: https://github.com/isc-projects/bind9/commit/095b11f20f911f5b8059bdc349b256d6c64ece30
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-11331
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-11331
+
+CVE: CVE-2026-11331
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/095b11f20f911f5b8059bdc349b256d6c64ece30]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ bin/tests/system/rpz/ns2/tld2.db | 2 ++
+ bin/tests/system/rpz/ns4/tld4.db | 2 ++
+ bin/tests/system/rpz/tests.sh | 5 ++++-
+ 3 files changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/bin/tests/system/rpz/ns2/tld2.db b/bin/tests/system/rpz/ns2/tld2.db
+index c6f2556..c091ee2 100644
+--- a/bin/tests/system/rpz/ns2/tld2.db
++++ b/bin/tests/system/rpz/ns2/tld2.db
+@@ -123,3 +123,5 @@ a7-1 A 192.168.7.1
+
+ a7-2 A 192.168.7.2
+ TXT "a7-2 tld2 text"
++
++*.wild A 192.168.9.1
+diff --git a/bin/tests/system/rpz/ns4/tld4.db b/bin/tests/system/rpz/ns4/tld4.db
+index fca419c..8accd76 100644
+--- a/bin/tests/system/rpz/ns4/tld4.db
++++ b/bin/tests/system/rpz/ns4/tld4.db
+@@ -59,6 +59,8 @@ a3-6.tld2 A 56.56.56.56
+
+ a3-7.sub1.tld2 A 57.57.57.57
+
++*.wild.sub1.tld2 A 57.57.57.57
++
+ a3-8.tld2 A 58.58.58.58
+
+ a3-9.sub9.tld2 A 59.59.59.59
+diff --git a/bin/tests/system/rpz/tests.sh b/bin/tests/system/rpz/tests.sh
+index 90cf80c..5297437 100644
+--- a/bin/tests/system/rpz/tests.sh
++++ b/bin/tests/system/rpz/tests.sh
+@@ -516,7 +516,10 @@ nochange TCP a3-9.tld2 # 33 tcp-only
+ here x.servfail <<'EOF' # 34 qname-wait-recurse yes
+ ;; status: SERVFAIL, x
+ EOF
+-addr 35.35.35.35 "x.servfail @$ns5" # 35 qname-wait-recurse no
++addr 35.35.35.35 "x.servfail @$ns5" # 35 qname-wait-recurse no
++here aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.wild.sub1.tld2 <<'EOF' # 36 wildcard CNAME name to long
++ ;; status: YXDOMAIN, x
++EOF
+ end_group
+ ckstats $ns3 test1 ns3 22
+ ckstats $ns5 test1 ns5 1
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-11331-03.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-11331-03.patch
new file mode 100644
index 0000000000..f02a9ebea0
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-11331-03.patch
@@ -0,0 +1,34 @@
+From: Mark Andrews <marka@isc.org>
+Date: Fri, 10 Apr 2026 10:26:14 +1000
+Subject: Properly handle rpz name to long wildcard expansion
+
+Previously a self referential CNAME and the original address
+record were returned. We now return a YXDOMAIN response.
+
+(cherry picked from commit cfc4c4f69870ce492deaaa429453563d1621ded3)
+
+Origin: https://github.com/isc-projects/bind9/commit/dc328a199f96222e0c30cc20b7b795bfc2c9b2e4
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-11331
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-11331
+
+CVE: CVE-2026-11331
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/dc328a199f96222e0c30cc20b7b795bfc2c9b2e4]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ lib/ns/query.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/lib/ns/query.c b/lib/ns/query.c
+index 86485b7..f0e5244 100644
+--- a/lib/ns/query.c
++++ b/lib/ns/query.c
+@@ -7579,7 +7579,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/meta/recipes-connectivity/bind/bind_9.18.49.bb b/meta/recipes-connectivity/bind/bind_9.18.49.bb
index 42007383c4..dc274f6076 100644
--- a/meta/recipes-connectivity/bind/bind_9.18.49.bb
+++ b/meta/recipes-connectivity/bind/bind_9.18.49.bb
@@ -26,6 +26,9 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.xz \
file://CVE-2026-10822-03.patch \
file://CVE-2026-10822-04.patch \
file://CVE-2026-10822-05.patch \
+ file://CVE-2026-11331-01.patch \
+ file://CVE-2026-11331-02.patch \
+ file://CVE-2026-11331-03.patch \
"
SRC_URI[sha256sum] = "c43ce4548ebed788cd9df63658a7de105ceafba43fcd63fa352b1093e525cd24"
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [scarthgap][PATCHv2 4/6] bind: fix for CVE-2026-11622, CVE-2026-11721
2026-10-12 4:38 [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
2026-10-12 4:38 ` [scarthgap][PATCHv2 2/6] bind: fix for CVE-2026-10822 Hitendra Prajapati
2026-10-12 4:38 ` [scarthgap][PATCHv2 3/6] bind: fix for CVE-2026-11331 Hitendra Prajapati
@ 2026-10-12 4:38 ` Hitendra Prajapati
2026-10-12 4:38 ` [scarthgap][PATCHv2 5/6] bind: fix for CVE-2026-12617 Hitendra Prajapati
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Hitendra Prajapati @ 2026-10-12 4:38 UTC (permalink / raw)
To: openembedded-core; +Cc: Hitendra Prajapati
Pick patch from [1], [2], [3] & [4] also mentioned at Debian report in [5] & [6]
[1] https://github.com/isc-projects/bind9/commit/231b1ca3edfb26389e1af39181aa6b4413e87ec4
[2] https://github.com/isc-projects/bind9/commit/1a4986e2533f87e80eb21da3f06708d335aff1e2
[3] https://github.com/isc-projects/bind9/commit/15089066b15f826d7487c3d160b5872820f84b83
[4] https://github.com/isc-projects/bind9/commit/19e496ca260b6a756ae1378e8ebcbdb666b7d9ed
[5] https://security-tracker.debian.org/tracker/CVE-2026-11721
[6] https://security-tracker.debian.org/tracker/CVE-2026-11622
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
.../bind/bind/CVE-2026-11622.patch | 283 ++++++++++++++++++
.../bind/bind/CVE-2026-11721-01.patch | 43 +++
.../bind/bind/CVE-2026-11721-02.patch | 238 +++++++++++++++
.../bind/bind/CVE-2026-11721-03.patch | 147 +++++++++
.../recipes-connectivity/bind/bind_9.18.49.bb | 4 +
5 files changed, 715 insertions(+)
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-11622.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-11721-01.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-11721-02.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-11721-03.patch
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-11622.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-11622.patch
new file mode 100644
index 0000000000..1168e67192
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-11622.patch
@@ -0,0 +1,283 @@
+From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@sury.org>
+Date: Tue, 23 Jun 2026 10:59:38 +0200
+Subject: Make the dns_slabheaders in the cache reference counted
+
+Instead of only reference counting the enclosing qpcnode, add the
+reference counting directly to the slabheaders. The reference is
+incremented when an rdataset is bound to the header and decremented when
+the rdataset is disassociated, so a stale slabheader can be removed from
+the node's down chain as soon as its own reference count reaches zero,
+instead of waiting for the whole qpcnode to become unreferenced.
+
+Building on that, clean up the ancient headers eagerly: mark_ancient()
+is made idempotent, releases the header's own (container) reference and
+reaps the stale headers from the node's down chain as soon as their
+references reach zero. A header evicted over the per-name type limit is
+expired only after the new rdataset has been bound, so the bind's
+increment always precedes mark_ancient()'s decrement.
+
+Because a header can now be reclaimed independently of its node, the
+rdataset iterators must keep the header they are positioned on alive:
+each iterator takes a reference on its current header and releases it
+when it advances or is destroyed. Iteration otherwise stays lazy and
+re-reads the node on every step, so it still observes records added to
+the node while the iterator is live, as zone signing requires.
+
+The slab headers are shared with the zone databases, so the matching
+increment is added to every bind path. The noqname/closest proofs hand
+out rdatasets backed by bare slabs that have no header, so they are
+given a separate dns_rdataproof_rdatasetmethods that leaves the
+reference count untouched.
+
+(cherry picked from commit 2dabf117e1264fd13fb33096f87e78a039fd1c6c)
+
+Origin: https://github.com/isc-projects/bind9/commit/231b1ca3edfb26389e1af39181aa6b4413e87ec4
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-11622
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-11622
+
+CVE: CVE-2026-11622
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/231b1ca3edfb26389e1af39181aa6b4413e87ec4]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ bin/tests/system/reclimit/tests.sh | 4 +-
+ lib/dns/include/dns/rdataslab.h | 1 +
+ lib/dns/rbtdb.c | 77 ++++++++++++++++++++++++++++++++------
+ 3 files changed, 69 insertions(+), 13 deletions(-)
+
+diff --git a/bin/tests/system/reclimit/tests.sh b/bin/tests/system/reclimit/tests.sh
+index 76889ec..efa7316 100644
+--- a/bin/tests/system/reclimit/tests.sh
++++ b/bin/tests/system/reclimit/tests.sh
+@@ -337,13 +337,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 5729c00..6bd3b59 100644
+--- a/lib/dns/include/dns/rdataslab.h
++++ b/lib/dns/include/dns/rdataslab.h
+@@ -44,6 +44,7 @@
+ #include <stdbool.h>
+
+ #include <isc/lang.h>
++#include <isc/refcount.h>
+
+ #include <dns/types.h>
+
+diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
+index 62bc97d..0b85479 100644
+--- a/lib/dns/rbtdb.c
++++ b/lib/dns/rbtdb.c
+@@ -158,6 +158,7 @@ struct noqname {
+ };
+
+ typedef struct rdatasetheader {
++ isc_refcount_t references;
+ /*%
+ * Locked by the owning node's lock.
+ */
+@@ -1447,6 +1448,7 @@ init_rdataset(dns_rbtdb_t *rbtdb, rdatasetheader_t *h) {
+ h->heap_index = 0;
+ 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 rdatasetheader_t needs to be "
+@@ -1559,6 +1561,9 @@ rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) {
+ }
+ }
+
++static void
++clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *top);
++
+ static void
+ mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
+ uint_least16_t attributes = atomic_load_acquire(&header->attributes);
+@@ -1584,8 +1589,12 @@ mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
+ update_rrsetstats(rbtdb, header->type, attributes, false);
+ header->node->dirty = 1;
+
++ isc_refcount_decrement(&header->references);
++
+ /* Increment the stats counter for the ancient RRtype. */
+ update_rrsetstats(rbtdb, header->type, newattributes, true);
++
++ clean_stale_headers(rbtdb, rbtdb->common.mctx, header);
+ }
+
+ static void
+@@ -1621,12 +1630,19 @@ static void
+ clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx,
+ rdatasetheader_t *top) {
+ rdatasetheader_t *d, *down_next;
++ rdatasetheader_t *down_parent = top;
+
+ for (d = top->down; d != NULL; d = down_next) {
+ down_next = d->down;
+- free_rdataset(rbtdb, mctx, d);
++ d->next = down_parent;
++
++ if (isc_refcount_current(&d->references) == 0) {
++ free_rdataset(rbtdb, mctx, d);
++ down_parent->down = down_next;
++ } else {
++ down_parent = d;
++ }
+ }
+- top->down = NULL;
+ }
+
+ static void
+@@ -1642,6 +1658,7 @@ 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(rbtdb, mctx, current);
++ INSIST(current->down == NULL);
+ /*
+ * If current is nonexistent, ancient, or stale and
+ * we are not keeping stale, we can clean it up.
+@@ -3114,6 +3131,8 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header,
+ return;
+ }
+
++ isc_refcount_increment(&header->references);
++
+ dns__rbtnode_acquire(rbtdb, node, locktype);
+
+ INSIST(rdataset->methods == NULL); /* We must be disassociated. */
+@@ -6307,6 +6326,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename,
+ bool header_nx;
+ bool newheader_nx;
+ bool merge;
++ bool do_expireheader = false;
+ dns_rdatatype_t rdtype, covers;
+ rbtdb_rdatatype_t negtype, sigtype;
+ dns_trust_t trust;
+@@ -6856,6 +6876,7 @@ find_header:
+ }
+
+ if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) {
++ do_expireheader = true;
+ if (expireheader == NULL) {
+ expireheader = newheader;
+ }
+@@ -6869,15 +6890,6 @@ find_header:
+ */
+ expireheader = newheader;
+ }
+-
+- set_ttl(rbtdb, expireheader, 0);
+- mark_header_ancient(rbtdb, 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.
+- */
+ }
+ }
+ }
+@@ -6901,6 +6913,15 @@ find_header:
+ isc_rwlocktype_write, addedrdataset);
+ }
+
++ /*
++ * 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) {
++ set_ttl(rbtdb, expireheader, 0);
++ mark_header_ancient(rbtdb, expireheader);
++ }
++
+ return ISC_R_SUCCESS;
+ }
+
+@@ -8692,6 +8713,12 @@ rdataset_disassociate(dns_rdataset_t *rdataset) {
+ dns_db_t *db = rdataset->private1;
+ dns_dbnode_t *node = rdataset->private2;
+
++ if (rdataset->methods == &rdataset_methods) {
++ rdatasetheader_t *header = rdataset->private3;
++ header--;
++ isc_refcount_decrement(&header->references);
++ }
++
+ detachnode(db, &node);
+ }
+
+@@ -8806,6 +8833,11 @@ rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ dns_dbnode_t *cloned_node = NULL;
+
+ attachnode(db, node, &cloned_node);
++ if (source->methods == &rdataset_methods) {
++ rdatasetheader_t *header = source->private3;
++ header--;
++ isc_refcount_increment(&header->references);
++ }
+ INSIST(!ISC_LINK_LINKED(target, link));
+ *target = *source;
+ ISC_LINK_INIT(target, link);
+@@ -8969,6 +9001,11 @@ rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+
+ rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp);
+
++ if (rbtiterator->current != NULL) {
++ isc_refcount_decrement(&rbtiterator->current->references);
++ rbtiterator->current = NULL;
++ }
++
+ if (rbtiterator->common.version != NULL) {
+ closeversion(rbtiterator->common.db,
+ &rbtiterator->common.version, false);
+@@ -9046,9 +9083,18 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+ }
+ }
+
++ if (header != NULL) {
++ isc_refcount_increment0(&header->references);
++ }
++
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
++ if (rbtiterator->current != NULL) {
++ isc_refcount_decrement(&rbtiterator->current->references);
++ rbtiterator->current = NULL;
++ }
++
+ rbtiterator->current = header;
+
+ if (header == NULL) {
+@@ -9140,9 +9186,18 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) {
+ }
+ }
+
++ if (header != NULL) {
++ isc_refcount_increment0(&header->references);
++ }
++
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
++ if (rbtiterator->current != NULL) {
++ isc_refcount_decrement(&rbtiterator->current->references);
++ rbtiterator->current = NULL;
++ }
++
+ rbtiterator->current = header;
+
+ if (header == NULL) {
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-11721-01.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-11721-01.patch
new file mode 100644
index 0000000000..bdb7ff0d9a
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-11721-01.patch
@@ -0,0 +1,43 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 14 Apr 2026 15:14:06 +1000
+Subject: 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)
+
+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)
+
+Origin: https://github.com/isc-projects/bind9/commit/1a4986e2533f87e80eb21da3f06708d335aff1e2
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-11721
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-11721
+
+CVE: CVE-2026-11721
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/1a4986e2533f87e80eb21da3f06708d335aff1e2]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ 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 73855e6..9e3a48a 100644
+--- a/bin/dnssec/dnssec-signzone.c
++++ b/bin/dnssec/dnssec-signzone.c
+@@ -1643,6 +1643,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.
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-11721-02.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-11721-02.patch
new file mode 100644
index 0000000000..b76ef17b96
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-11721-02.patch
@@ -0,0 +1,238 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 14 Apr 2026 12:24:33 +1000
+Subject: 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)
+
+Origin: https://github.com/isc-projects/bind9/commit/15089066b15f826d7487c3d160b5872820f84b83
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-11721
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-11721
+
+CVE: CVE-2026-11721
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/15089066b15f826d7487c3d160b5872820f84b83]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ lib/dns/dnssec.c | 43 +++++++++++++++++++++++++++++-----------
+ lib/dns/rdata/generic/rrsig_46.c | 37 +++++++++++++++++++++++++---------
+ 2 files changed, 59 insertions(+), 21 deletions(-)
+
+diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
+index 1725de3..9b9b1f2 100644
+--- a/lib/dns/dnssec.c
++++ b/lib/dns/dnssec.c
+@@ -130,11 +130,11 @@ 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(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);
+
+@@ -187,12 +187,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);
++ labels = dns_name_countlabels(name);
++ REQUIRE(labels <= 255 && labels > 0);
+ REQUIRE(set != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(inception != NULL);
+@@ -221,7 +223,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--;
+ }
+@@ -365,10 +367,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);
++ labels = dns_name_countlabels(name);
++ REQUIRE(labels > 0);
+ REQUIRE(set != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(mctx != NULL);
+@@ -383,6 +388,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;
+@@ -464,10 +484,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));
+ }
+@@ -478,7 +497,7 @@ again:
+ * 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);
+@@ -574,7 +593,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 10bc039..4cf4259 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 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;
+
+@@ -61,8 +61,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.
+@@ -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.
+ */
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-11721-03.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-11721-03.patch
new file mode 100644
index 0000000000..659951fb4d
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-11721-03.patch
@@ -0,0 +1,147 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 14 Apr 2026 13:46:22 +1000
+Subject: Test RRSIG record parsing
+
+In particular test that labels and signer fields are consistent.
+
+(cherry picked from commit 5a95e64731afe63d348d272cc4d3b2f9847150c2)
+
+Origin: https://github.com/isc-projects/bind9/commit/19e496ca260b6a756ae1378e8ebcbdb666b7d9ed
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-11721
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-11721
+
+CVE: CVE-2026-11721
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/19e496ca260b6a756ae1378e8ebcbdb666b7d9ed]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ tests/dns/rdata_test.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 110 insertions(+)
+
+diff --git a/tests/dns/rdata_test.c b/tests/dns/rdata_test.c
+index 7f0df6e..c704d98 100644
+--- a/tests/dns/rdata_test.c
++++ b/tests/dns/rdata_test.c
+@@ -2504,6 +2504,115 @@ ISC_RUN_TEST_IMPL(rkey) {
+ dns_rdatatype_rkey, sizeof(dns_rdata_rkey_t));
+ }
+
++ISC_RUN_TEST_IMPL(rrsig) {
++ text_ok_t text_ok[] = {
++ TEXT_VALID("SOA 8 0 86400 20260426170000 20260413160000 54393 "
++ ". "
++ "tFbcoVP8MnpecUquJ/aj+XeNgV7ts9GSHVkXaXRJrJ/"
++ "TEkOZApVG0F6E "
++ "9sYpxGk2ItweLL43ujioGj0HWwZDRR+vbur+O/"
++ "dIdheiig1VvU+9HXLi "
++ "QOViY9Kc64ixdyJhYCC5K+bO1qsHxd+"
++ "KJXOaxyHbqchYkDFy4PL6qftE "
++ "VaLkueRgjXgOsq/"
++ "NxvCXDgAa5xy0+3Sl0myxIs8rJ5KeXfJQFe7qxgaw "
++ "VjJsJTKw8neOTw2rQfLaigWu2LIWw+"
++ "IyVrLjZJdLqGkiLBGd1w4X3U12 "
++ "fFxoY3eqzNgBEtduoGKPZ/"
++ "NpP9cuKJORJ18283aV8hR4WO91VR0q1zcM jLwqUg=="),
++ /* labels too short for signer */
++ TEXT_INVALID("SOA 8 0 86400 20260426170000 20260413160000 "
++ "54393 example. "
++ "tFbcoVP8MnpecUquJ/aj+XeNgV7ts9GSHVkXaXRJrJ/"
++ "TEkOZApVG0F6E "
++ "9sYpxGk2ItweLL43ujioGj0HWwZDRR+vbur+O/"
++ "dIdheiig1VvU+9HXLi "
++ "QOViY9Kc64ixdyJhYCC5K+bO1qsHxd+"
++ "KJXOaxyHbqchYkDFy4PL6qftE "
++ "VaLkueRgjXgOsq/"
++ "NxvCXDgAa5xy0+3Sl0myxIs8rJ5KeXfJQFe7qxgaw "
++ "VjJsJTKw8neOTw2rQfLaigWu2LIWw+"
++ "IyVrLjZJdLqGkiLBGd1w4X3U12 "
++ "fFxoY3eqzNgBEtduoGKPZ/"
++ "NpP9cuKJORJ18283aV8hR4WO91VR0q1zcM jLwqUg=="),
++ /*
++ * Sentinel.
++ */
++ TEXT_SENTINEL()
++ };
++ wire_ok_t wire_ok[] = {
++ WIRE_VALID(0x00, 0x06, 0x08, 0x00, 0x00, 0x01, 0x51, 0x80, 0x69,
++ 0xee, 0x44, 0x90, 0x69, 0xdd, 0x13, 0x00, 0xd4, 0x79,
++ 0x00, 0xb4, 0x56, 0xdc, 0xa1, 0x53, 0xfc, 0x32, 0x7a,
++ 0x5e, 0x71, 0x4a, 0xae, 0x27, 0xf6, 0xa3, 0xf9, 0x77,
++ 0x8d, 0x81, 0x5e, 0xed, 0xb3, 0xd1, 0x92, 0x1d, 0x59,
++ 0x17, 0x69, 0x74, 0x49, 0xac, 0x9f, 0xd3, 0x12, 0x43,
++ 0x99, 0x02, 0x95, 0x46, 0xd0, 0x5e, 0x84, 0xf6, 0xc6,
++ 0x29, 0xc4, 0x69, 0x36, 0x22, 0xdc, 0x1e, 0x2c, 0xbe,
++ 0x37, 0xba, 0x38, 0xa8, 0x1a, 0x3d, 0x07, 0x5b, 0x06,
++ 0x43, 0x45, 0x1f, 0xaf, 0x6e, 0xea, 0xfe, 0x3b, 0xf7,
++ 0x48, 0x76, 0x17, 0xa2, 0x8a, 0x0d, 0x55, 0xbd, 0x4f,
++ 0xbd, 0x1d, 0x72, 0xe2, 0x40, 0xe5, 0x62, 0x63, 0xd2,
++ 0x9c, 0xeb, 0x88, 0xb1, 0x77, 0x22, 0x61, 0x60, 0x20,
++ 0xb9, 0x2b, 0xe6, 0xce, 0xd6, 0xab, 0x07, 0xc5, 0xdf,
++ 0x8a, 0x25, 0x73, 0x9a, 0xc7, 0x21, 0xdb, 0xa9, 0xc8,
++ 0x58, 0x90, 0x31, 0x72, 0xe0, 0xf2, 0xfa, 0xa9, 0xfb,
++ 0x44, 0x55, 0xa2, 0xe4, 0xb9, 0xe4, 0x60, 0x8d, 0x78,
++ 0x0e, 0xb2, 0xaf, 0xcd, 0xc6, 0xf0, 0x97, 0x0e, 0x00,
++ 0x1a, 0xe7, 0x1c, 0xb4, 0xfb, 0x74, 0xa5, 0xd2, 0x6c,
++ 0xb1, 0x22, 0xcf, 0x2b, 0x27, 0x92, 0x9e, 0x5d, 0xf2,
++ 0x50, 0x15, 0xee, 0xea, 0xc6, 0x06, 0xb0, 0x56, 0x32,
++ 0x6c, 0x25, 0x32, 0xb0, 0xf2, 0x77, 0x8e, 0x4f, 0x0d,
++ 0xab, 0x41, 0xf2, 0xda, 0x8a, 0x05, 0xae, 0xd8, 0xb2,
++ 0x16, 0xc3, 0xe2, 0x32, 0x56, 0xb2, 0xe3, 0x64, 0x97,
++ 0x4b, 0xa8, 0x69, 0x22, 0x2c, 0x11, 0x9d, 0xd7, 0x0e,
++ 0x17, 0xdd, 0x4d, 0x76, 0x7c, 0x5c, 0x68, 0x63, 0x77,
++ 0xaa, 0xcc, 0xd8, 0x01, 0x12, 0xd7, 0x6e, 0xa0, 0x62,
++ 0x8f, 0x67, 0xf3, 0x69, 0x3f, 0xd7, 0x2e, 0x28, 0x93,
++ 0x91, 0x27, 0x5f, 0x36, 0xf3, 0x76, 0x95, 0xf2, 0x14,
++ 0x78, 0x58, 0xef, 0x75, 0x55, 0x1d, 0x2a, 0xd7, 0x37,
++ 0x0c, 0x8c, 0xbc, 0x2a, 0x52),
++ /* labels too short for signer */
++ WIRE_INVALID(
++ 0x00, 0x06, 0x08, 0x00, 0x00, 0x01, 0x51, 0x80, 0x69,
++ 0xee, 0x44, 0x90, 0x69, 0xdd, 0x13, 0x00, 0xd4, 0x79,
++ 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00,
++ 0xb4, 0x56, 0xdc, 0xa1, 0x53, 0xfc, 0x32, 0x7a, 0x5e,
++ 0x71, 0x4a, 0xae, 0x27, 0xf6, 0xa3, 0xf9, 0x77, 0x8d,
++ 0x81, 0x5e, 0xed, 0xb3, 0xd1, 0x92, 0x1d, 0x59, 0x17,
++ 0x69, 0x74, 0x49, 0xac, 0x9f, 0xd3, 0x12, 0x43, 0x99,
++ 0x02, 0x95, 0x46, 0xd0, 0x5e, 0x84, 0xf6, 0xc6, 0x29,
++ 0xc4, 0x69, 0x36, 0x22, 0xdc, 0x1e, 0x2c, 0xbe, 0x37,
++ 0xba, 0x38, 0xa8, 0x1a, 0x3d, 0x07, 0x5b, 0x06, 0x43,
++ 0x45, 0x1f, 0xaf, 0x6e, 0xea, 0xfe, 0x3b, 0xf7, 0x48,
++ 0x76, 0x17, 0xa2, 0x8a, 0x0d, 0x55, 0xbd, 0x4f, 0xbd,
++ 0x1d, 0x72, 0xe2, 0x40, 0xe5, 0x62, 0x63, 0xd2, 0x9c,
++ 0xeb, 0x88, 0xb1, 0x77, 0x22, 0x61, 0x60, 0x20, 0xb9,
++ 0x2b, 0xe6, 0xce, 0xd6, 0xab, 0x07, 0xc5, 0xdf, 0x8a,
++ 0x25, 0x73, 0x9a, 0xc7, 0x21, 0xdb, 0xa9, 0xc8, 0x58,
++ 0x90, 0x31, 0x72, 0xe0, 0xf2, 0xfa, 0xa9, 0xfb, 0x44,
++ 0x55, 0xa2, 0xe4, 0xb9, 0xe4, 0x60, 0x8d, 0x78, 0x0e,
++ 0xb2, 0xaf, 0xcd, 0xc6, 0xf0, 0x97, 0x0e, 0x00, 0x1a,
++ 0xe7, 0x1c, 0xb4, 0xfb, 0x74, 0xa5, 0xd2, 0x6c, 0xb1,
++ 0x22, 0xcf, 0x2b, 0x27, 0x92, 0x9e, 0x5d, 0xf2, 0x50,
++ 0x15, 0xee, 0xea, 0xc6, 0x06, 0xb0, 0x56, 0x32, 0x6c,
++ 0x25, 0x32, 0xb0, 0xf2, 0x77, 0x8e, 0x4f, 0x0d, 0xab,
++ 0x41, 0xf2, 0xda, 0x8a, 0x05, 0xae, 0xd8, 0xb2, 0x16,
++ 0xc3, 0xe2, 0x32, 0x56, 0xb2, 0xe3, 0x64, 0x97, 0x4b,
++ 0xa8, 0x69, 0x22, 0x2c, 0x11, 0x9d, 0xd7, 0x0e, 0x17,
++ 0xdd, 0x4d, 0x76, 0x7c, 0x5c, 0x68, 0x63, 0x77, 0xaa,
++ 0xcc, 0xd8, 0x01, 0x12, 0xd7, 0x6e, 0xa0, 0x62, 0x8f,
++ 0x67, 0xf3, 0x69, 0x3f, 0xd7, 0x2e, 0x28, 0x93, 0x91,
++ 0x27, 0x5f, 0x36, 0xf3, 0x76, 0x95, 0xf2, 0x14, 0x78,
++ 0x58, 0xef, 0x75, 0x55, 0x1d, 0x2a, 0xd7, 0x37, 0x0c,
++ 0x8c, 0xbc, 0x2a, 0x52),
++
++ WIRE_SENTINEL()
++ };
++ check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in,
++ dns_rdatatype_rrsig, sizeof(dns_rdata_rrsig_t));
++}
++
+ ISC_RUN_TEST_IMPL(resinfo) {
+ text_ok_t text_ok[] = {
+ TEXT_VALID_CHANGED("qnamemin exterr=15,16,17 "
+@@ -3357,6 +3466,7 @@ ISC_TEST_ENTRY(nsec3)
+ ISC_TEST_ENTRY(nxt)
+ ISC_TEST_ENTRY(resinfo)
+ ISC_TEST_ENTRY(rkey)
++ISC_TEST_ENTRY(rrsig)
+ ISC_TEST_ENTRY(sshfp)
+ ISC_TEST_ENTRY(wallet)
+ ISC_TEST_ENTRY(wks)
diff --git a/meta/recipes-connectivity/bind/bind_9.18.49.bb b/meta/recipes-connectivity/bind/bind_9.18.49.bb
index dc274f6076..8218772531 100644
--- a/meta/recipes-connectivity/bind/bind_9.18.49.bb
+++ b/meta/recipes-connectivity/bind/bind_9.18.49.bb
@@ -29,6 +29,10 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.xz \
file://CVE-2026-11331-01.patch \
file://CVE-2026-11331-02.patch \
file://CVE-2026-11331-03.patch \
+ file://CVE-2026-11622.patch \
+ file://CVE-2026-11721-01.patch \
+ file://CVE-2026-11721-02.patch \
+ file://CVE-2026-11721-03.patch \
"
SRC_URI[sha256sum] = "c43ce4548ebed788cd9df63658a7de105ceafba43fcd63fa352b1093e525cd24"
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [scarthgap][PATCHv2 5/6] bind: fix for CVE-2026-12617
2026-10-12 4:38 [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
` (2 preceding siblings ...)
2026-10-12 4:38 ` [scarthgap][PATCHv2 4/6] bind: fix for CVE-2026-11622, CVE-2026-11721 Hitendra Prajapati
@ 2026-10-12 4:38 ` Hitendra Prajapati
2026-10-12 4:38 ` [scarthgap][PATCHv2 6/6] bind: fix for CVE-2026-13204, CVE-2026-13321 Hitendra Prajapati
2026-10-14 9:56 ` [OE-core] [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723 Yoann Congal
5 siblings, 0 replies; 9+ messages in thread
From: Hitendra Prajapati @ 2026-10-12 4:38 UTC (permalink / raw)
To: openembedded-core; +Cc: Hitendra Prajapati
Pick patch from [1] & [2] also mentioned at Debian report in [2]
[1] https://github.com/isc-projects/bind9/commit/c740c37689f234e21a9b0ef760471ef2cf1133f5
[2] https://github.com/isc-projects/bind9/commit/bb92832fb6ae899bee7206c2d8966258461c2f71
[3] https://security-tracker.debian.org/tracker/CVE-2026-12617
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
.../bind/bind/CVE-2026-12617-01.patch | 283 +++++++++++++++++
.../bind/bind/CVE-2026-12617-02.patch | 292 ++++++++++++++++++
.../recipes-connectivity/bind/bind_9.18.49.bb | 2 +
3 files changed, 577 insertions(+)
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-12617-01.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-12617-02.patch
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-12617-01.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-12617-01.patch
new file mode 100644
index 0000000000..15ffca2b63
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-12617-01.patch
@@ -0,0 +1,283 @@
+From: Colin Vidal <colin@isc.org>
+Date: Thu, 18 Jun 2026 18:17:05 +0200
+Subject: Do not assert in some CNAME/DNAME queries
+
+Fix a `named` crash because of a fail assertion for certains types of
+CNAME and DNAME queries:
+
+- If a client queries for a DNAME and A record to the resolver, and the
+ authoritative server responds positively to the A query but delay the
+ DNAME response and respond later negatively;
+
+- If a client queries for a CNAME and A record to the resolver, and the
+ authoritative server responds positively to the A query but delay the
+ CNAME response and respond later with a self-referential CNAME.
+
+The first scenario consists of sending two queries: `foo.test./DNAME`
+and `a.foo.test./A`. The authoritative server delays the answer for
+`foo.test./DNAME` but immediately answers the DNAME record for the
+second query: `foo.test. DNAME bar.test.`. The resolver caches it,
+follows the DNAME, and resolves `a.bar.test./A`. The authoritative
+server eventually answers negatively for `foo.test./DNAME`
+(NOERROR/NODATA, with only an SOA in the authority section). The
+resolver pulls out the previously cached rdataset (because it has a
+higher trust level than the received negative answer), and wrongly (this
+is the first bug) sets the result to `DNS_R_DNAME` instead of
+`ISC_R_SUCCESS`. The code in `ns/query.c` that handles the resolver
+result interprets this as "this is a non-DNAME query and we got a DNAME
+rdataset, so follow the chain". It goes into the `query_dname()`
+function, which asserts that the qname is a subdomain of the owner name
+in the rdataset. That assertion fails because the qname (`foo.test.`) is
+exactly equal to the owner name of the DNAME (`foo.test.`), rather than
+being a subdomain of it. `DNS_R_DNAME` must only be set when the qtype
+is something other than DNAME and the resolver has obtained a DNAME that
+needs to be followed.
+
+The second scenario consists of sending two queries:
+`cname.foo.test./CNAME` and `cname.foo.test./A`. The authoritative
+server delays the answer for `cname.foo.test./CNAME` but immediately
+answers the CNAME record for the second query: `cname.foo.test. CNAME
+cname.foo.test.`. Note that the CNAME is self-referential. The resolver
+caches it and sets the result code to `DNS_R_CNAME`. Then `ns/query.c`
+interprets this as "this is a non-CNAME query and we got a CNAME
+rdataset, so follow the chain" (which is correct in this case; however,
+because the CNAME rdataset is self-referential, the resolver responds
+with SERVFAIL, which is expected). The authoritative server eventually
+answers negatively for `cname.foo.test./CNAME`. The resolver then pulls
+out the previously cached CNAME rdataset (obtained from the A answer,
+even though it was self-referential, the resolver cached it) and wrongly
+sets the result to `DNS_R_CNAME` (this is the second bug). As noted
+above, `ns/query.c` interprets this as "this is a non-CNAME query and we
+got a CNAME rdataset, so follow the chain". The internals here are
+slightly more subtle: it first goes into `query_cname()` and sets the
+CNAME rdataset in the message answer section, then restarts the query to
+follow the CNAME. The restart retrieves the CNAME rdataset from the
+cache directly (without going to the resolver), and this time the query
+context result is `ISC_R_SUCCESS` (since it was found) and
+`qctx->rdataset` points to the same CNAME again (as it is
+self-referential), so it goes directly into the
+`query_prepresponse()/query_respond()` flow, which attempts to add the
+rdataset to the message answer again. However, this fails because the
+rdataset is already in the message, and the assertion which expects that
+operation to succeed fails (due to `qctx->rdataset` being set to `NULL`
+when ownership of the rdataset was transferred). `DNS_R_CNAME` must only
+be set when the qtype is something other than CNAME and the resolver has
+obtained a CNAME that needs to be followed.
+
+In both cases, the correct answer from the resolver should have been
+`ISC_R_SUCCESS` (instead of respectively `DNS_R_DNAME` and
+`DNS_R_CNAME`) becuase the rdataset that has been looked up was found.
+
+(cherry picked from commit 773d46d58c693047a5945c8fe40512edd0ac214e)
+
+Origin: https://github.com/isc-projects/bind9/commit/c740c37689f234e21a9b0ef760471ef2cf1133f5
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-12617
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-12617
+
+CVE: CVE-2026-12617
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/c740c37689f234e21a9b0ef760471ef2cf1133f5]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ lib/dns/resolver.c | 137 +++++++++++++++++++++++------------------------------
+ 1 file changed, 60 insertions(+), 77 deletions(-)
+
+diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
+index 9d46126..06c779e 100644
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -692,10 +692,10 @@ fctx_destroy(fetchctx_t *fctx, bool exiting);
+ static void
+ send_shutdown_events(dns_resolver_t *res);
+ 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(isc_task_t *task, isc_event_t *event);
+ static void
+@@ -5580,6 +5580,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.
+ */
+@@ -5853,8 +5893,7 @@ validated(isc_task_t *task, isc_event_t *event) {
+ ttl = 0;
+ }
+
+- result = ncache_adderesult(message, fctx->cache, node, covers,
+- now, fctx->res->view->minncachettl,
++ result = ncache_adderesult(fctx, message, node, covers, now,
+ ttl, vevent->optout, vevent->secure,
+ ardataset, &eresult);
+ if (result != ISC_R_SUCCESS) {
+@@ -6098,23 +6137,7 @@ answer_response:
+ */
+ INSIST(hevent->rdataset != NULL);
+ if (dns_rdataset_isassociated(hevent->rdataset)) {
+- if (NEGATIVE(hevent->rdataset)) {
+- INSIST(eresult == DNS_R_NCACHENXDOMAIN ||
+- eresult == DNS_R_NCACHENXRRSET);
+- } else if (eresult == ISC_R_SUCCESS &&
+- hevent->rdataset->type != fctx->type)
+- {
+- switch (hevent->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, hevent->rdataset);
+ }
+
+ hevent->result = eresult;
+@@ -6764,24 +6787,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
+ * event->result.
+ */
+ if (dns_rdataset_isassociated(event->rdataset)) {
+- if (NEGATIVE(event->rdataset)) {
+- INSIST(eresult ==
+- DNS_R_NCACHENXDOMAIN ||
+- eresult == DNS_R_NCACHENXRRSET);
+- } else if (eresult == ISC_R_SUCCESS &&
+- event->rdataset->type != fctx->type)
+- {
+- switch (event->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, event->rdataset);
+ }
+ event->result = eresult;
+ if (adbp != NULL && *adbp != NULL) {
+@@ -6850,12 +6856,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);
+@@ -6871,37 +6879,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)) {
+@@ -7046,8 +7030,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/meta/recipes-connectivity/bind/bind/CVE-2026-12617-02.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-12617-02.patch
new file mode 100644
index 0000000000..48ca53a641
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-12617-02.patch
@@ -0,0 +1,292 @@
+From: Colin Vidal <colin@isc.org>
+Date: Mon, 15 Jun 2026 11:34:08 +0200
+Subject: Reproducer for #5946 (assertion in some CNAME/DNAME queries)
+
+Add a system test reproducing the issue reported by #5946, which
+is also CVE-2026-12617. There are two scenarios:
+
+- A client send queries for a DNAME and A record to the resolver (ns3),
+ and the authoritative server (ans2) responds positively to the A query
+ but delay the DNAME response and respond later negatively;
+
+- A client send queries for a CNAME and A record to the resolver (ns3),
+ and the authoritative server (ans2) responds positively to the A query
+ but delay the CNAME response and respond later with a self-referential
+ CNAME.
+
+The test does not check the results of the queries, however, it expects
+the resolver to correctly handle those and do not assert.
+
+(cherry picked from commit e88271f2e584010157b068cc998dd76451273562)
+
+Origin: https://github.com/isc-projects/bind9/commit/bb92832fb6ae899bee7206c2d8966258461c2f71
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-12617
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-12617
+
+CVE: CVE-2026-12617
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/bb92832fb6ae899bee7206c2d8966258461c2f71]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ bin/tests/system/cname_dname_negcache/ans2/ans.py | 98 ++++++++++++++++++++++
+ .../system/cname_dname_negcache/ns1/bar.test.db | 5 ++
+ .../system/cname_dname_negcache/ns1/named.conf.j2 | 24 ++++++
+ bin/tests/system/cname_dname_negcache/ns1/root.db | 6 ++
+ bin/tests/system/cname_dname_negcache/ns1/test.db | 8 ++
+ .../system/cname_dname_negcache/ns3/named.conf.j2 | 11 +++
+ .../tests_cname_dname_negcache.py | 53 ++++++++++++
+ 7 files changed, 205 insertions(+)
+ create mode 100644 bin/tests/system/cname_dname_negcache/ans2/ans.py
+ create mode 100644 bin/tests/system/cname_dname_negcache/ns1/bar.test.db
+ create mode 100644 bin/tests/system/cname_dname_negcache/ns1/named.conf.j2
+ create mode 100644 bin/tests/system/cname_dname_negcache/ns1/root.db
+ create mode 100644 bin/tests/system/cname_dname_negcache/ns1/test.db
+ create mode 100644 bin/tests/system/cname_dname_negcache/ns3/named.conf.j2
+ create mode 100644 bin/tests/system/cname_dname_negcache/tests_cname_dname_negcache.py
+
+diff --git a/bin/tests/system/cname_dname_negcache/ans2/ans.py b/bin/tests/system/cname_dname_negcache/ans2/ans.py
+new file mode 100644
+index 0000000..eec5c90
+--- /dev/null
++++ b/bin/tests/system/cname_dname_negcache/ans2/ans.py
+@@ -0,0 +1,98 @@
++"""
++Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++
++SPDX-License-Identifier: MPL-2.0
++
++This Source Code Form is subject to the terms of the Mozilla Public
++License, v. 2.0. If a copy of the MPL was not distributed with this
++file, you can obtain one at https://mozilla.org/MPL/2.0/.
++
++See the COPYRIGHT file distributed with this work for additional
++information regarding copyright ownership.
++"""
++
++from collections.abc import AsyncGenerator
++
++from dns import name, rcode, rdataclass, rdatatype, rrset
++
++from isctest.asyncserver import (
++ AsyncDnsServer,
++ DnsResponseSend,
++ QnameQtypeHandler,
++ QueryContext,
++ StaticResponseHandler,
++)
++
++
++def build_rrset(
++ qname: name.Name | str,
++ rtype: rdatatype.RdataType,
++ rdata: str,
++ ttl: int = 300,
++) -> rrset.RRset:
++ return rrset.from_text(qname, ttl, rdataclass.IN, rtype, rdata)
++
++
++class FooTestNsHandler(QnameQtypeHandler, StaticResponseHandler):
++ qnames = ["foo.test."]
++ qtypes = [rdatatype.NS]
++ answer = [build_rrset("foo.test.", rdatatype.NS, "ns.foo.test.")]
++ additional = [build_rrset("ns.foo.test.", rdatatype.A, "10.53.0.2")]
++
++
++class DelayedDnameNegHandler(QnameQtypeHandler, StaticResponseHandler):
++ qnames = ["foo.test."]
++ qtypes = [rdatatype.DNAME]
++ authority = [
++ build_rrset(
++ "foo.test.",
++ rdatatype.SOA,
++ "ns.test. op.ns.test. 2081509183 86400 3600 3600000 300",
++ )
++ ]
++ delay = 1
++
++
++class DnamePosHandler(QnameQtypeHandler, StaticResponseHandler):
++ qnames = ["a.foo.test."]
++ qtypes = [rdatatype.A]
++ answer = [
++ build_rrset("foo.test.", rdatatype.DNAME, "bar.test."),
++ build_rrset("a.foo.test.", rdatatype.CNAME, "a.bar.test."),
++ ]
++
++
++class CnameHandler(QnameQtypeHandler):
++ qnames = ["cname.foo.test."]
++ qtypes = [rdatatype.CNAME, rdatatype.A]
++ answer = [build_rrset("cname.foo.test.", rdatatype.CNAME, "cname.foo.test.")]
++ authority = [
++ build_rrset(
++ "cname.foo.test.",
++ rdatatype.SOA,
++ "ns.test. op.ns.test. 2081509183 86400 3600 3600000 300",
++ )
++ ]
++
++ async def get_responses(
++ self, qctx: QueryContext
++ ) -> AsyncGenerator[DnsResponseSend, None]:
++ qctx.prepare_new_response(with_zone_data=False)
++ if qctx.qtype == rdatatype.CNAME:
++ qctx.response.authority.extend(self.authority)
++ yield DnsResponseSend(qctx.response, authoritative=True, delay=1)
++ else:
++ qctx.response.answer.extend(self.answer)
++ yield DnsResponseSend(qctx.response, authoritative=True)
++
++
++def main() -> None:
++ server = AsyncDnsServer(default_aa=True, default_rcode=rcode.NOERROR)
++ server.install_response_handlers(
++ FooTestNsHandler(), DelayedDnameNegHandler(), DnamePosHandler(), CnameHandler()
++ )
++ server.run()
++
++
++if __name__ == "__main__":
++ main()
+diff --git a/bin/tests/system/cname_dname_negcache/ns1/bar.test.db b/bin/tests/system/cname_dname_negcache/ns1/bar.test.db
+new file mode 100644
+index 0000000..840b9c3
+--- /dev/null
++++ b/bin/tests/system/cname_dname_negcache/ns1/bar.test.db
+@@ -0,0 +1,5 @@
++$TTL 300
++bar.test. IN SOA ns.bar.test. hostmaster.bar.test. 1 600 600 1200 600
++bar.test. NS ns.bar.test.
++ns A 10.53.0.1
++a A 10.0.0.1
+diff --git a/bin/tests/system/cname_dname_negcache/ns1/named.conf.j2 b/bin/tests/system/cname_dname_negcache/ns1/named.conf.j2
+new file mode 100644
+index 0000000..d72dd11
+--- /dev/null
++++ b/bin/tests/system/cname_dname_negcache/ns1/named.conf.j2
+@@ -0,0 +1,24 @@
++options {
++ query-source address @ns.ip@;
++ port @PORT@;
++ pid-file "named.pid";
++ listen-on { @ns.ip@; };
++ listen-on-v6 { none; };
++ recursion no;
++ dnssec-validation no;
++};
++
++zone "." {
++ type primary;
++ file "root.db";
++};
++
++zone "test." {
++ type primary;
++ file "test.db";
++};
++
++zone "bar.test." {
++ type primary;
++ file "bar.test.db";
++};
+diff --git a/bin/tests/system/cname_dname_negcache/ns1/root.db b/bin/tests/system/cname_dname_negcache/ns1/root.db
+new file mode 100644
+index 0000000..c456c45
+--- /dev/null
++++ b/bin/tests/system/cname_dname_negcache/ns1/root.db
+@@ -0,0 +1,6 @@
++$TTL 300
++. IN SOA ns. hostmaster. 1 600 600 1200 600
++. NS a.root-servers.nil.
++a.root-servers.nil. A 10.53.0.1
++test NS ns.test
++ns.test A 10.53.0.1
+diff --git a/bin/tests/system/cname_dname_negcache/ns1/test.db b/bin/tests/system/cname_dname_negcache/ns1/test.db
+new file mode 100644
+index 0000000..acb68e0
+--- /dev/null
++++ b/bin/tests/system/cname_dname_negcache/ns1/test.db
+@@ -0,0 +1,8 @@
++$TTL 300
++test. IN SOA ns.test. hostmaster.test. 1 600 600 1200 600
++test. NS ns.test.
++ns A 10.53.0.1
++bar NS ns.bar
++ns.bar A 10.53.0.1
++foo NS ns.foo
++ns.foo A 10.53.0.2
+diff --git a/bin/tests/system/cname_dname_negcache/ns3/named.conf.j2 b/bin/tests/system/cname_dname_negcache/ns3/named.conf.j2
+new file mode 100644
+index 0000000..197d727
+--- /dev/null
++++ b/bin/tests/system/cname_dname_negcache/ns3/named.conf.j2
+@@ -0,0 +1,11 @@
++options {
++ query-source address @ns.ip@;
++ port @PORT@;
++ pid-file "named.pid";
++ listen-on { @ns.ip@; };
++ listen-on-v6 { none; };
++ recursion yes;
++ dnssec-validation no;
++};
++
++{% include "_common/root.hint.conf" %}
+diff --git a/bin/tests/system/cname_dname_negcache/tests_cname_dname_negcache.py b/bin/tests/system/cname_dname_negcache/tests_cname_dname_negcache.py
+new file mode 100644
+index 0000000..5d21c5b
+--- /dev/null
++++ b/bin/tests/system/cname_dname_negcache/tests_cname_dname_negcache.py
+@@ -0,0 +1,53 @@
++# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++#
++# SPDX-License-Identifier: MPL-2.0
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License, v. 2.0. If a copy of the MPL was not distributed with this
++# file, you can obtain one at https://mozilla.org/MPL/2.0/.
++#
++# See the COPYRIGHT file distributed with this work for additional
++# information regarding copyright ownership.
++
++from os import environ
++from re import compile as Re
++from socket import AF_INET, SOCK_DGRAM, socket
++
++import isctest
++
++
++def run_attack(ns, name1, type1, name2, type2):
++ msg1 = isctest.query.create(name1, type1, cd=True)
++ msg2 = isctest.query.create(name2, type2, cd=True)
++ port = int(environ["PORT"])
++
++ with socket(AF_INET, SOCK_DGRAM) as sock:
++ # The order the request does out doesn't matter. What is important is
++ # the first query starts recursion before the second query returns the
++ # answer, and the second query returns the answer before the first
++ # query returns the answer. (So, when the NOERROR/NODATA cames back
++ # from the first query, the cache is queried and we get the positive
++ # response cached from the second query attached to the fresp rdataset
++ # of the response of the first query.)
++ # Therefore, the logic is really baked into ans2, which has a 3 seconds
++ # delay to answer the first query.
++ sock.sendto(msg1.to_wire(), (ns.ip, port))
++ sock.sendto(msg2.to_wire(), (ns.ip, port))
++
++ # The second query come back immediately, the resolver caches the DNAME.
++ # The first query come back after 3s (because of intentional ans2 latency
++ # on foo.test./DNAME answer) and should not crash the server.
++ with ns.watch_log_from_start(timeout=15) as watcher:
++ watcher.wait_for_sequence(
++ [
++ Re(r"foo\.test\..*IN\s+SOA\s+ns\.test\.\s+op\.ns\.test\."),
++ ]
++ )
++
++
++def test_dname_negcache(ns3):
++ run_attack(ns3, "foo.test.", "DNAME", "a.foo.test.", "A")
++
++
++def test_cname_negcache(ns3):
++ run_attack(ns3, "cname.foo.test.", "CNAME", "cname.foo.test.", "A")
diff --git a/meta/recipes-connectivity/bind/bind_9.18.49.bb b/meta/recipes-connectivity/bind/bind_9.18.49.bb
index 8218772531..b048ba6559 100644
--- a/meta/recipes-connectivity/bind/bind_9.18.49.bb
+++ b/meta/recipes-connectivity/bind/bind_9.18.49.bb
@@ -33,6 +33,8 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.xz \
file://CVE-2026-11721-01.patch \
file://CVE-2026-11721-02.patch \
file://CVE-2026-11721-03.patch \
+ file://CVE-2026-12617-01.patch \
+ file://CVE-2026-12617-02.patch \
"
SRC_URI[sha256sum] = "c43ce4548ebed788cd9df63658a7de105ceafba43fcd63fa352b1093e525cd24"
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [scarthgap][PATCHv2 6/6] bind: fix for CVE-2026-13204, CVE-2026-13321
2026-10-12 4:38 [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
` (3 preceding siblings ...)
2026-10-12 4:38 ` [scarthgap][PATCHv2 5/6] bind: fix for CVE-2026-12617 Hitendra Prajapati
@ 2026-10-12 4:38 ` Hitendra Prajapati
2026-10-14 9:56 ` [OE-core] [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723 Yoann Congal
5 siblings, 0 replies; 9+ messages in thread
From: Hitendra Prajapati @ 2026-10-12 4:38 UTC (permalink / raw)
To: openembedded-core; +Cc: Hitendra Prajapati
Pick patch from [1,2,3] & [4,5,6] also mentioned at Debian report in [7] & [8]
[1] https://github.com/isc-projects/bind9/commit/0cf010c153518f1f9831e201891ecba8d8ba65e1
[2] https://github.com/isc-projects/bind9/commit/6c0e599ea85c0c53a4af09742e64e193da089bb4
[3] https://github.com/isc-projects/bind9/commit/48f5aa5fb3746d6194edcc57e8792a8b3cc3b454
[4] https://github.com/isc-projects/bind9/commit/c969ad2c17b43dd999e358bfeb280d3df6fab822
[5] https://github.com/isc-projects/bind9/commit/058023c66f11d78590d4aa8c4f98946c4c965e21
[6] https://github.com/isc-projects/bind9/commit/f751e19a30d107f04c2f644aff9f8dab8fed03ab
[7] https://security-tracker.debian.org/tracker/CVE-2026-13204
[8] https://security-tracker.debian.org/tracker/CVE-2026-13321
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
.../bind/bind/CVE-2026-13204-01.patch | 395 +++++++++++++++++
.../bind/bind/CVE-2026-13204-02.patch | 215 ++++++++++
.../bind/bind/CVE-2026-13204-03.patch | 161 +++++++
.../bind/bind/CVE-2026-13321-01.patch | 401 ++++++++++++++++++
.../bind/bind/CVE-2026-13321-02.patch | 88 ++++
.../bind/bind/CVE-2026-13321-03.patch | 203 +++++++++
.../recipes-connectivity/bind/bind_9.18.49.bb | 6 +
7 files changed, 1469 insertions(+)
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-13204-01.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-13204-02.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-13204-03.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-13321-01.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-13321-02.patch
create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-13321-03.patch
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-13204-01.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-13204-01.patch
new file mode 100644
index 0000000000..2c80be8315
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-13204-01.patch
@@ -0,0 +1,395 @@
+From: Alessio Podda <alessio@isc.org>
+Date: Fri, 12 Jun 2026 11:16:01 +0200
+Subject: Reproducer for #5985 addnoqname mismatch
+
+LLM generated.
+
+(cherry picked from commit 5f4de929b3e4749b6e32c51660be11c47c2514e6)
+
+Origin: https://github.com/isc-projects/bind9/commit/0cf010c153518f1f9831e201891ecba8d8ba65e1
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-13204
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-13204
+
+CVE: CVE-2026-13204
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/0cf010c153518f1f9831e201891ecba8d8ba65e1]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ .../ans1/ans.py | 11 ++
+ .../ns2/named.conf.j2 | 7 +-
+ .../repro_5985_findnoqname_runtime_check/server.py | 189 +++++++++++++++++++++
+ .../tests_repro_5985_findnoqname_runtime_check.py | 120 +++++++++++++
+ 4 files changed, 325 insertions(+), 2 deletions(-)
+ create mode 100644 bin/tests/system/repro_5985_findnoqname_runtime_check/ans1/ans.py
+ copy bin/tests/system/{nsec3_impersonation => repro_5985_findnoqname_runtime_check}/ns2/named.conf.j2 (77%)
+ create mode 100644 bin/tests/system/repro_5985_findnoqname_runtime_check/server.py
+ create mode 100644 bin/tests/system/repro_5985_findnoqname_runtime_check/tests_repro_5985_findnoqname_runtime_check.py
+
+diff --git a/bin/tests/system/repro_5985_findnoqname_runtime_check/ans1/ans.py b/bin/tests/system/repro_5985_findnoqname_runtime_check/ans1/ans.py
+new file mode 100644
+index 0000000..cb01c8a
+--- /dev/null
++++ b/bin/tests/system/repro_5985_findnoqname_runtime_check/ans1/ans.py
+@@ -0,0 +1,11 @@
++#!/usr/bin/python3
++
++# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++#
++# SPDX-License-Identifier: MPL-2.0
++
++from server import main
++
++
++if __name__ == "__main__":
++ main()
+diff --git a/bin/tests/system/nsec3_impersonation/ns2/named.conf.j2 b/bin/tests/system/repro_5985_findnoqname_runtime_check/ns2/named.conf.j2
+similarity index 77%
+copy from bin/tests/system/nsec3_impersonation/ns2/named.conf.j2
+copy to bin/tests/system/repro_5985_findnoqname_runtime_check/ns2/named.conf.j2
+index 2c9b0bb..7d6fc84 100644
+--- a/bin/tests/system/nsec3_impersonation/ns2/named.conf.j2
++++ b/bin/tests/system/repro_5985_findnoqname_runtime_check/ns2/named.conf.j2
+@@ -10,6 +10,9 @@ options {
+ listen-on-v6 { none; };
+ recursion yes;
+ dnssec-validation yes;
++ trust-anchor-telemetry no;
++ resolver-query-timeout 5000;
++ qname-minimization off;
+ };
+
+ controls {
+@@ -23,11 +26,11 @@ zone "." {
+ file "../../_common/root.hint";
+ };
+
+-zone "tld.test" {
++zone "f217.test" {
+ type static-stub;
+ server-addresses { 10.53.0.1; };
+ };
+
+ trust-anchors {
+- tld.test. static-key 257 3 13 "@TLD_DNSKEY@";
++ f217.test. static-key 257 3 13 "@ZONE_DNSKEY@";
+ };
+diff --git a/bin/tests/system/repro_5985_findnoqname_runtime_check/server.py b/bin/tests/system/repro_5985_findnoqname_runtime_check/server.py
+new file mode 100644
+index 0000000..18d0ac1
+--- /dev/null
++++ b/bin/tests/system/repro_5985_findnoqname_runtime_check/server.py
+@@ -0,0 +1,189 @@
++#!/usr/bin/python3
++
++# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++#
++# SPDX-License-Identifier: MPL-2.0
++
++from collections.abc import AsyncGenerator
++from dataclasses import dataclass
++from datetime import datetime, timedelta, timezone
++from pathlib import Path
++
++import base64
++import json
++
++from cryptography.hazmat.primitives import serialization
++
++import dns.dnssec
++import dns.flags
++import dns.message
++import dns.name
++import dns.rdata
++import dns.rdataclass
++import dns.rcode
++import dns.rdatatype
++import dns.rrset
++
++from isctest.asyncserver import (
++ AsyncDnsServer,
++ DnsResponseSend,
++ QueryContext,
++ ResponseHandler,
++)
++
++TTL = 300
++ZONE = "f217.test."
++CHILD = f"evil.{ZONE}"
++ATTACK = f"www.{CHILD}"
++NSEC_OWNER = f"00000000.{CHILD}"
++NSEC_NEXT = f"zzz.{CHILD}"
++FORGED_A = "192.0.2.217"
++
++
++@dataclass(frozen=True)
++class Key:
++ zone: dns.name.Name
++ private_key: object
++ dnskey: dns.rdata.Rdata
++
++
++def name(text: str) -> dns.name.Name:
++ return dns.name.from_text(text)
++
++
++def load_key() -> Key:
++ path = Path(__file__).resolve().parent / "keys.json"
++ with path.open(encoding="utf-8") as keys_file:
++ raw_key = json.load(keys_file)[ZONE]
++
++ private_key = serialization.load_pem_private_key(
++ raw_key["private_pem"].encode("ascii"),
++ password=None,
++ )
++ dnskey = dns.rdata.from_text(
++ dns.rdataclass.IN, dns.rdatatype.DNSKEY, raw_key["dnskey"]
++ )
++ return Key(name(ZONE), private_key, dnskey)
++
++
++def rrset(
++ owner: str, rdtype: dns.rdatatype.RdataType, *rdatas: str
++) -> dns.rrset.RRset:
++ return dns.rrset.from_text(owner, TTL, dns.rdataclass.IN, rdtype, *rdatas)
++
++
++def rrset_from_rdata(owner: str, rdata: dns.rdata.Rdata) -> dns.rrset.RRset:
++ return dns.rrset.from_rdata(name(owner), TTL, rdata)
++
++
++def add_signed(
++ section: list[dns.rrset.RRset], covered: dns.rrset.RRset, signer: Key
++) -> None:
++ rrsig = dns.dnssec.sign(
++ covered,
++ signer.private_key,
++ signer.zone,
++ signer.dnskey,
++ lifetime=86400,
++ verify=True,
++ )
++ section.append(covered)
++ section.append(dns.rrset.from_rdata(covered.name, covered.ttl, rrsig))
++
++
++def soa_rrset(zone: str) -> dns.rrset.RRset:
++ return rrset(
++ zone,
++ dns.rdatatype.SOA,
++ f"ns.{ZONE} hostmaster.{ZONE} 1 7200 3600 1209600 300",
++ )
++
++
++def garbage_rrsig(
++ owner: str, covered: dns.rdatatype.RdataType, labels: int, signer: str
++) -> dns.rrset.RRset:
++ now = datetime.now(timezone.utc)
++ inception = (now - timedelta(hours=1)).strftime("%Y%m%d%H%M%S")
++ expiration = (now + timedelta(days=1)).strftime("%Y%m%d%H%M%S")
++ signature = base64.b64encode(bytes(64)).decode("ascii")
++ text = (
++ f"{dns.rdatatype.to_text(covered)} 13 {labels} {TTL} "
++ f"{expiration} {inception} 12345 {signer} {signature}"
++ )
++ rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.RRSIG, text)
++ return dns.rrset.from_rdata(name(owner), TTL, rdata)
++
++
++def add_ds_denial(response: dns.message.Message, key: Key) -> None:
++ add_signed(response.authority, soa_rrset(ZONE), key)
++ nsec = rrset(CHILD, dns.rdatatype.NSEC, f"ns.{ZONE} NS RRSIG NSEC")
++ add_signed(response.authority, nsec, key)
++
++
++def add_attack_answer(response: dns.message.Message) -> None:
++ response.answer.append(rrset(ATTACK, dns.rdatatype.A, FORGED_A))
++ response.answer.append(garbage_rrsig(ATTACK, dns.rdatatype.A, 1, CHILD))
++
++ nsec = rrset(
++ NSEC_OWNER,
++ dns.rdatatype.NSEC,
++ f"{NSEC_NEXT} A RRSIG NSEC",
++ )
++ nsec3 = rrset(
++ NSEC_OWNER,
++ dns.rdatatype.NSEC3,
++ "1 0 0 - VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV A RRSIG",
++ )
++ response.authority.append(nsec)
++ response.authority.append(
++ garbage_rrsig(
++ NSEC_OWNER,
++ dns.rdatatype.NSEC,
++ len(name(NSEC_OWNER).labels) - 1,
++ CHILD,
++ )
++ )
++ response.authority.append(nsec3)
++
++
++class RuntimeCheckHandler(ResponseHandler):
++ def __init__(self, key: Key) -> None:
++ self.key = key
++ self.zone = name(ZONE)
++ self.child = name(CHILD)
++ self.attack = name(ATTACK)
++
++ def match(self, qctx: QueryContext) -> bool:
++ return qctx.qname.is_subdomain(self.zone)
++
++ async def get_responses(
++ self, qctx: QueryContext
++ ) -> AsyncGenerator[DnsResponseSend, None]:
++ qctx.prepare_new_response(with_zone_data=False)
++ qctx.response.flags |= dns.flags.AA
++ qctx.response.set_rcode(dns.rcode.NOERROR)
++
++ if qctx.qname == self.zone and qctx.qtype == dns.rdatatype.DNSKEY:
++ add_signed(
++ qctx.response.answer,
++ rrset_from_rdata(ZONE, self.key.dnskey),
++ self.key,
++ )
++ elif qctx.qname == self.zone and qctx.qtype == dns.rdatatype.SOA:
++ add_signed(qctx.response.answer, soa_rrset(ZONE), self.key)
++ elif qctx.qname == self.child and qctx.qtype == dns.rdatatype.DS:
++ add_ds_denial(qctx.response, self.key)
++ elif qctx.qname == self.child and qctx.qtype == dns.rdatatype.DNSKEY:
++ qctx.response.authority.append(soa_rrset(CHILD))
++ elif qctx.qname == self.attack and qctx.qtype == dns.rdatatype.A:
++ add_attack_answer(qctx.response)
++ else:
++ add_signed(qctx.response.authority, soa_rrset(ZONE), self.key)
++
++ yield DnsResponseSend(qctx.response, authoritative=True)
++
++
++def main() -> None:
++ server = AsyncDnsServer(default_aa=True)
++ server.install_response_handlers(RuntimeCheckHandler(load_key()))
++ server.run()
+diff --git a/bin/tests/system/repro_5985_findnoqname_runtime_check/tests_repro_5985_findnoqname_runtime_check.py b/bin/tests/system/repro_5985_findnoqname_runtime_check/tests_repro_5985_findnoqname_runtime_check.py
+new file mode 100644
+index 0000000..0e7d71c
+--- /dev/null
++++ b/bin/tests/system/repro_5985_findnoqname_runtime_check/tests_repro_5985_findnoqname_runtime_check.py
+@@ -0,0 +1,120 @@
++#!/usr/bin/python3
++
++# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++#
++# SPDX-License-Identifier: MPL-2.0
++
++from pathlib import Path
++
++import json
++
++from cryptography.hazmat.primitives import serialization
++from cryptography.hazmat.primitives.asymmetric import ec
++
++import dns.dnssec
++import dns.name
++import dns.rdataclass
++import dns.rdatatype
++import pytest
++
++import isctest
++
++ZONE = "f217.test."
++CHILD = f"evil.{ZONE}"
++ATTACK = f"www.{CHILD}"
++NSEC_OWNER = f"00000000.{CHILD}"
++FORGED_A = "192.0.2.217"
++AUTH = "10.53.0.1"
++RESOLVER = "10.53.0.2"
++
++pytestmark = pytest.mark.extra_artifacts(
++ [
++ "ans*/ans.run",
++ "keys.json",
++ ]
++)
++
++
++def _make_key():
++ private_key = ec.generate_private_key(ec.SECP256R1())
++ dnskey = dns.dnssec.make_dnskey(
++ private_key.public_key(),
++ algorithm="ECDSAP256SHA256",
++ flags=257,
++ )
++ private_pem = private_key.private_bytes(
++ encoding=serialization.Encoding.PEM,
++ format=serialization.PrivateFormat.PKCS8,
++ encryption_algorithm=serialization.NoEncryption(),
++ ).decode("ascii")
++ return {
++ "private_pem": private_pem,
++ "dnskey": dnskey.to_text(),
++ }
++
++
++def bootstrap():
++ keys = {ZONE: _make_key()}
++ Path("keys.json").write_text(json.dumps(keys, indent=2), encoding="ascii")
++ zone_dnskey = "".join(keys[ZONE]["dnskey"].split()[3:])
++ return {"ZONE_DNSKEY": zone_dnskey}
++
++
++def _query(server, qname, qtype):
++ query = isctest.query.create(qname, qtype)
++ return isctest.query.tcp(query, server, attempts=1, timeout=5)
++
++
++def _rrset(response, section, owner, rdtype, covers=None):
++ if covers is None:
++ return response.get_rrset(
++ section, dns.name.from_text(owner), dns.rdataclass.IN, rdtype
++ )
++ return response.get_rrset(
++ section,
++ dns.name.from_text(owner),
++ dns.rdataclass.IN,
++ rdtype,
++ covers=covers,
++ )
++
++
++def _has_a(response, section, owner, address):
++ rrset = _rrset(response, section, owner, dns.rdatatype.A)
++ return rrset is not None and any(rdata.address == address for rdata in rrset)
++
++
++def _check_rrsig(response, section, owner, rdtype, signer, labels=None):
++ rrsig = _rrset(response, section, owner, dns.rdatatype.RRSIG, covers=rdtype)
++ assert rrsig is not None, response.to_text()
++ assert rrsig[0].signer == dns.name.from_text(signer), response.to_text()
++ if labels is not None:
++ assert rrsig[0].labels == labels, response.to_text()
++
++
++def test_repro_5985_direct_findnoqname_addnoqname_mismatch_fixture():
++ response = _query(AUTH, ATTACK, "A")
++ isctest.check.noerror(response)
++ assert _has_a(response, response.answer, ATTACK, FORGED_A), response.to_text()
++ _check_rrsig(response, response.answer, ATTACK, dns.rdatatype.A, CHILD, labels=1)
++
++ assert _rrset(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC)
++ _check_rrsig(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC, CHILD)
++ assert _rrset(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC3)
++ assert (
++ _rrset(
++ response,
++ response.authority,
++ NSEC_OWNER,
++ dns.rdatatype.RRSIG,
++ covers=dns.rdatatype.NSEC3,
++ )
++ is None
++ )
++
++
++def test_repro_5985_resolver_does_not_abort_on_noqname_type_mismatch():
++ _query(RESOLVER, ATTACK, "A")
++
++ response = _query(RESOLVER, ZONE, "SOA")
++ isctest.check.noerror(response)
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-13204-02.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-13204-02.patch
new file mode 100644
index 0000000000..a7ae3d1ee7
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-13204-02.patch
@@ -0,0 +1,215 @@
+From: Matthijs Mekking <matthijs@isc.org>
+Date: Mon, 15 Jun 2026 14:55:29 +0200
+Subject: Update reproducer #5985
+
+Update the llm generated reproducer:
+- Move server.py into ans/ans1.py
+- Remove unncessary named.conf configuration options
+- Add comments describing the steps
+- Rename system test
+
+(cherry picked from commit fd539807829dd7d2eb76c8b503083f5d84fec6f0)
+
+Origin: https://github.com/isc-projects/bind9/commit/6c0e599ea85c0c53a4af09742e64e193da089bb4
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-13204
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-13204
+
+CVE: CVE-2026-13204
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/6c0e599ea85c0c53a4af09742e64e193da089bb4]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ .../ans1/ans.py} | 36 ++++++++++++++++------
+ .../ns2/named.conf.j2 | 3 --
+ .../tests_findnoqname_mismatch.py} | 26 ++++++++++------
+ .../ans1/ans.py | 11 -------
+ 4 files changed, 43 insertions(+), 33 deletions(-)
+ rename bin/tests/system/{repro_5985_findnoqname_runtime_check/server.py => dnssec_findnoqname_mismatch/ans1/ans.py} (85%)
+ rename bin/tests/system/{repro_5985_findnoqname_runtime_check => dnssec_findnoqname_mismatch}/ns2/named.conf.j2 (87%)
+ rename bin/tests/system/{repro_5985_findnoqname_runtime_check/tests_repro_5985_findnoqname_runtime_check.py => dnssec_findnoqname_mismatch/tests_findnoqname_mismatch.py} (87%)
+ delete mode 100644 bin/tests/system/repro_5985_findnoqname_runtime_check/ans1/ans.py
+
+diff --git a/bin/tests/system/repro_5985_findnoqname_runtime_check/server.py b/bin/tests/system/dnssec_findnoqname_mismatch/ans1/ans.py
+similarity index 85%
+rename from bin/tests/system/repro_5985_findnoqname_runtime_check/server.py
+rename to bin/tests/system/dnssec_findnoqname_mismatch/ans1/ans.py
+index 18d0ac1..b36fc83 100644
+--- a/bin/tests/system/repro_5985_findnoqname_runtime_check/server.py
++++ b/bin/tests/system/dnssec_findnoqname_mismatch/ans1/ans.py
+@@ -66,9 +66,7 @@ def load_key() -> Key:
+ return Key(name(ZONE), private_key, dnskey)
+
+
+-def rrset(
+- owner: str, rdtype: dns.rdatatype.RdataType, *rdatas: str
+-) -> dns.rrset.RRset:
++def rrset(owner: str, rdtype: dns.rdatatype.RdataType, *rdatas: str) -> dns.rrset.RRset:
+ return dns.rrset.from_text(owner, TTL, dns.rdataclass.IN, rdtype, *rdatas)
+
+
+@@ -121,20 +119,30 @@ def add_ds_denial(response: dns.message.Message, key: Key) -> None:
+
+
+ def add_attack_answer(response: dns.message.Message) -> None:
++ """
++ Crafted authoritative response to <q>.evil.f217.hack./A
++
++ ;; ANSWER
++ <q>.evil.f217.hack. 300 IN A 192.0.2.217
++ <q>.evil.f217.hack. 300 IN RRSIG A 13 1 300 <exp> <inc> 12345 evil.f217.hack. <base64 of 64×0x00>
++ ^^^ Labels = 1, qname has 4 labels, wildcard heuristic fires
++
++ ;; AUTHORITY (single owner, three rdatasets in this wire order)
++ 00000000.evil.f217.hack. 300 IN NSEC zzz.evil.f217.hack. A RRSIG NSEC
++ 00000000.evil.f217.hack. 300 IN RRSIG NSEC 13 4 300 <exp> <inc> 12345 evil.f217.hack. <base64 of 64×0x00>
++ 00000000.evil.f217.hack. 300 IN NSEC3 1 0 0 - VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV A RRSIG
++ """
++ # A + RRSIG
+ response.answer.append(rrset(ATTACK, dns.rdatatype.A, FORGED_A))
+ response.answer.append(garbage_rrsig(ATTACK, dns.rdatatype.A, 1, CHILD))
+-
++ # NSEC
+ nsec = rrset(
+ NSEC_OWNER,
+ dns.rdatatype.NSEC,
+ f"{NSEC_NEXT} A RRSIG NSEC",
+ )
+- nsec3 = rrset(
+- NSEC_OWNER,
+- dns.rdatatype.NSEC3,
+- "1 0 0 - VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV A RRSIG",
+- )
+ response.authority.append(nsec)
++ # RRSIG(NSEC)
+ response.authority.append(
+ garbage_rrsig(
+ NSEC_OWNER,
+@@ -143,6 +151,12 @@ def add_attack_answer(response: dns.message.Message) -> None:
+ CHILD,
+ )
+ )
++ # NSEC3
++ nsec3 = rrset(
++ NSEC_OWNER,
++ dns.rdatatype.NSEC3,
++ "1 0 0 - VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV A RRSIG",
++ )
+ response.authority.append(nsec3)
+
+
+@@ -187,3 +201,7 @@ def main() -> None:
+ server = AsyncDnsServer(default_aa=True)
+ server.install_response_handlers(RuntimeCheckHandler(load_key()))
+ server.run()
++
++
++if __name__ == "__main__":
++ main()
+diff --git a/bin/tests/system/repro_5985_findnoqname_runtime_check/ns2/named.conf.j2 b/bin/tests/system/dnssec_findnoqname_mismatch/ns2/named.conf.j2
+similarity index 87%
+rename from bin/tests/system/repro_5985_findnoqname_runtime_check/ns2/named.conf.j2
+rename to bin/tests/system/dnssec_findnoqname_mismatch/ns2/named.conf.j2
+index 7d6fc84..f4fbd8a 100644
+--- a/bin/tests/system/repro_5985_findnoqname_runtime_check/ns2/named.conf.j2
++++ b/bin/tests/system/dnssec_findnoqname_mismatch/ns2/named.conf.j2
+@@ -10,9 +10,6 @@ options {
+ listen-on-v6 { none; };
+ recursion yes;
+ dnssec-validation yes;
+- trust-anchor-telemetry no;
+- resolver-query-timeout 5000;
+- qname-minimization off;
+ };
+
+ controls {
+diff --git a/bin/tests/system/repro_5985_findnoqname_runtime_check/tests_repro_5985_findnoqname_runtime_check.py b/bin/tests/system/dnssec_findnoqname_mismatch/tests_findnoqname_mismatch.py
+similarity index 87%
+rename from bin/tests/system/repro_5985_findnoqname_runtime_check/tests_repro_5985_findnoqname_runtime_check.py
+rename to bin/tests/system/dnssec_findnoqname_mismatch/tests_findnoqname_mismatch.py
+index 0e7d71c..f3e332a 100644
+--- a/bin/tests/system/repro_5985_findnoqname_runtime_check/tests_repro_5985_findnoqname_runtime_check.py
++++ b/bin/tests/system/dnssec_findnoqname_mismatch/tests_findnoqname_mismatch.py
+@@ -18,6 +18,7 @@ import dns.rdatatype
+ import pytest
+
+ import isctest
++import isctest.mark
+
+ ZONE = "f217.test."
+ CHILD = f"evil.{ZONE}"
+@@ -27,12 +28,15 @@ FORGED_A = "192.0.2.217"
+ AUTH = "10.53.0.1"
+ RESOLVER = "10.53.0.2"
+
+-pytestmark = pytest.mark.extra_artifacts(
+- [
+- "ans*/ans.run",
+- "keys.json",
+- ]
+-)
++pytestmark = [
++ isctest.mark.with_ecdsa_deterministic,
++ pytest.mark.extra_artifacts(
++ [
++ "ans1/ans.run",
++ "ans1/keys.json",
++ ]
++ ),
++]
+
+
+ def _make_key():
+@@ -55,7 +59,7 @@ def _make_key():
+
+ def bootstrap():
+ keys = {ZONE: _make_key()}
+- Path("keys.json").write_text(json.dumps(keys, indent=2), encoding="ascii")
++ Path("ans1/keys.json").write_text(json.dumps(keys, indent=2), encoding="ascii")
+ zone_dnskey = "".join(keys[ZONE]["dnskey"].split()[3:])
+ return {"ZONE_DNSKEY": zone_dnskey}
+
+@@ -92,14 +96,16 @@ def _check_rrsig(response, section, owner, rdtype, signer, labels=None):
+ assert rrsig[0].labels == labels, response.to_text()
+
+
+-def test_repro_5985_direct_findnoqname_addnoqname_mismatch_fixture():
++def test_malicious_findnoqname_addnoqname_mismatch():
+ response = _query(AUTH, ATTACK, "A")
+ isctest.check.noerror(response)
+ assert _has_a(response, response.answer, ATTACK, FORGED_A), response.to_text()
+ _check_rrsig(response, response.answer, ATTACK, dns.rdatatype.A, CHILD, labels=1)
+
++ # Has NSEC
+ assert _rrset(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC)
+ _check_rrsig(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC, CHILD)
++ # Has NSEC3
+ assert _rrset(response, response.authority, NSEC_OWNER, dns.rdatatype.NSEC3)
+ assert (
+ _rrset(
+@@ -113,8 +119,8 @@ def test_repro_5985_direct_findnoqname_addnoqname_mismatch_fixture():
+ )
+
+
+-def test_repro_5985_resolver_does_not_abort_on_noqname_type_mismatch():
++def test_resolver_findnoqname_addnoqname_mismatch():
++ # Send one trigger query
+ _query(RESOLVER, ATTACK, "A")
+-
+ response = _query(RESOLVER, ZONE, "SOA")
+ isctest.check.noerror(response)
+diff --git a/bin/tests/system/repro_5985_findnoqname_runtime_check/ans1/ans.py b/bin/tests/system/repro_5985_findnoqname_runtime_check/ans1/ans.py
+deleted file mode 100644
+index cb01c8a..0000000
+--- a/bin/tests/system/repro_5985_findnoqname_runtime_check/ans1/ans.py
++++ /dev/null
+@@ -1,11 +0,0 @@
+-#!/usr/bin/python3
+-
+-# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+-#
+-# SPDX-License-Identifier: MPL-2.0
+-
+-from server import main
+-
+-
+-if __name__ == "__main__":
+- main()
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-13204-03.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-13204-03.patch
new file mode 100644
index 0000000000..f3447e3457
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-13204-03.patch
@@ -0,0 +1,161 @@
+From: Evan Hunt <each@isc.org>
+Date: Wed, 13 May 2026 20:45:57 -0700
+Subject: dns_rdataset_addnoqname() could find unsigned NSEC/NSEC3
+
+The dns_rdatalist addnoqname() implementation searches for the first
+NSEC or NSEC3 record in a message, then for the first RRSIG covering
+that type in the same message. Previously, if no RRSIG for the type was
+found, the function accepted the unsigned record. Now, it will instead
+continue searching until an NSEC or NSEC3 that does have a matching
+signature is found.
+
+When this function is called from validated() in resolver.c, a
+non-success return code is now treated as an error instead of triggering
+an assertion failure.
+
+Fixes: isc-projects/bind9#5985
+(cherry picked from commit 57cba571ee31311e54d8a11cb38094d439f04e09)
+
+Origin: https://github.com/isc-projects/bind9/commit/48f5aa5fb3746d6194edcc57e8792a8b3cc3b454
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-13204
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-13204
+
+CVE: CVE-2026-13204
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/48f5aa5fb3746d6194edcc57e8792a8b3cc3b454]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ lib/dns/rbtdb.c | 10 +++++++---
+ lib/dns/rdatalist.c | 33 ++++++++++++++++-----------------
+ lib/dns/resolver.c | 4 +++-
+ lib/ns/query.c | 3 +--
+ 4 files changed, 27 insertions(+), 23 deletions(-)
+
+diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
+index 0b85479..c922df5 100644
+--- a/lib/dns/rbtdb.c
++++ b/lib/dns/rbtdb.c
+@@ -6946,7 +6946,7 @@ delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ static isc_result_t
+ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
+ uint32_t maxrrperset, dns_rdataset_t *rdataset) {
+- struct noqname *noqname;
++ struct noqname *noqname = NULL;
+ isc_mem_t *mctx = rbtdb->common.mctx;
+ dns_name_t name;
+ dns_rdataset_t neg, negsig;
+@@ -6958,7 +6958,9 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
+ dns_rdataset_init(&negsig);
+
+ result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig);
+- RUNTIME_CHECK(result == ISC_R_SUCCESS);
++ if (result != ISC_R_SUCCESS) {
++ goto cleanup;
++ }
+
+ noqname = isc_mem_get(mctx, sizeof(*noqname));
+ dns_name_init(&noqname->name, NULL);
+@@ -6984,7 +6986,9 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
+ cleanup:
+ dns_rdataset_disassociate(&neg);
+ dns_rdataset_disassociate(&negsig);
+- free_noqname(mctx, &noqname);
++ if (noqname != NULL) {
++ free_noqname(mctx, &noqname);
++ }
+ return result;
+ }
+
+diff --git a/lib/dns/rdatalist.c b/lib/dns/rdatalist.c
+index 98036f9..2cca8d6 100644
+--- a/lib/dns/rdatalist.c
++++ b/lib/dns/rdatalist.c
+@@ -192,6 +192,7 @@ isc__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);
+@@ -199,30 +200,27 @@ isc__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;
+ }
+ /*
+@@ -238,6 +236,7 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) {
+ rdataset->ttl = neg->ttl = negsig->ttl = ttl;
+ rdataset->attributes |= DNS_RDATASETATTR_NOQNAME;
+ rdataset->private6 = name;
++
+ return ISC_R_SUCCESS;
+ }
+
+diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
+index 06c779e..01c4a00 100644
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -5910,7 +5910,9 @@ validated(isc_task_t *task, isc_event_t *event) {
+ result = dns_rdataset_addnoqname(
+ vevent->rdataset,
+ vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]);
+- RUNTIME_CHECK(result == ISC_R_SUCCESS);
++ if (result != ISC_R_SUCCESS) {
++ goto noanswer_response;
++ }
+ INSIST(vevent->sigrdataset != NULL);
+ vevent->sigrdataset->ttl = vevent->rdataset->ttl;
+ if (vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER] != NULL) {
+diff --git a/lib/ns/query.c b/lib/ns/query.c
+index f0e5244..c4fe7c8 100644
+--- a/lib/ns/query.c
++++ b/lib/ns/query.c
+@@ -7941,8 +7941,7 @@ query_addnoqnameproof(query_ctx_t *qctx) {
+ goto cleanup;
+ }
+
+- 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/meta/recipes-connectivity/bind/bind/CVE-2026-13321-01.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-13321-01.patch
new file mode 100644
index 0000000000..ea8e03b808
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-13321-01.patch
@@ -0,0 +1,401 @@
+From: =?utf-8?q?Ayd=C4=B1n_Mercan?= <aydin@isc.org>
+Date: Wed, 6 May 2026 16:54:57 +0300
+Subject: Add system test for out-of-zone nsec dnssec bypass
+
+A malicious zone with out-of-zone NSEC entries can get a DNSSEC
+validating resolver's cache to cover the victim zone for non-existence
+and prevent nameserver queries without DNSSEC failure.
+
+Test for this case with an `evil.test` zone that tries to cover the
+`victim.test` zone.
+
+(cherry picked from commit 654f9773c0af59965c343bdfeb096b3dffe9dd53)
+
+Origin: https://github.com/isc-projects/bind9/commit/c969ad2c17b43dd999e358bfeb280d3df6fab822
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-13321
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-13321
+
+CVE: CVE-2026-13321
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/c969ad2c17b43dd999e358bfeb280d3df6fab822]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ .../ns1/named.conf.j2} | 7 +-
+ .../ns1/root.db} | 11 +-
+ .../ns1/test.db} | 15 +-
+ .../ns2/named.conf.j2} | 6 +-
+ .../ns2/victim.db} | 8 +-
+ .../template.db.in => dnssec_bypass/ns3/evil.db} | 22 ++-
+ .../ns3/named.conf.j2} | 6 +-
+ .../ns4/named.conf.j2} | 9 +-
+ bin/tests/system/dnssec_bypass/tests_bypass.py | 152 +++++++++++++++++++++
+ 9 files changed, 202 insertions(+), 34 deletions(-)
+ copy bin/tests/system/{allow_query/ns1/named.conf.in => dnssec_bypass/ns1/named.conf.j2} (87%)
+ copy bin/tests/system/{dupsigs/ns1/signing.test.db.in => dnssec_bypass/ns1/root.db} (72%)
+ copy bin/tests/system/{dupsigs/ns1/signing.test.db.in => dnssec_bypass/ns1/test.db} (69%)
+ copy bin/tests/system/{runtime/ns2/named-alt4.conf.in => dnssec_bypass/ns2/named.conf.j2} (89%)
+ copy bin/tests/system/{dnssec/ns2/cdnskey.secure.db.in => dnssec_bypass/ns2/victim.db} (79%)
+ copy bin/tests/system/{checkds/ns9/template.db.in => dnssec_bypass/ns3/evil.db} (54%)
+ copy bin/tests/system/{allow_query/ns1/named.conf.in => dnssec_bypass/ns3/named.conf.j2} (88%)
+ copy bin/tests/system/{rrsetorder/ns4/named.conf.in => dnssec_bypass/ns4/named.conf.j2} (86%)
+ create mode 100644 bin/tests/system/dnssec_bypass/tests_bypass.py
+
+diff --git a/bin/tests/system/allow_query/ns1/named.conf.in b/bin/tests/system/dnssec_bypass/ns1/named.conf.j2
+similarity index 87%
+copy from bin/tests/system/allow_query/ns1/named.conf.in
+copy to bin/tests/system/dnssec_bypass/ns1/named.conf.j2
+index dd786e2..59ced18 100644
+--- a/bin/tests/system/allow_query/ns1/named.conf.in
++++ b/bin/tests/system/dnssec_bypass/ns1/named.conf.j2
+@@ -20,7 +20,12 @@ options {
+ dnssec-validation no;
+ };
+
++zone "test." {
++ type primary;
++ file "test.db.signed";
++};
++
+ zone "." {
+ type primary;
+- file "root.db";
++ file "root.db.signed";
+ };
+diff --git a/bin/tests/system/dupsigs/ns1/signing.test.db.in b/bin/tests/system/dnssec_bypass/ns1/root.db
+similarity index 72%
+copy from bin/tests/system/dupsigs/ns1/signing.test.db.in
+copy to bin/tests/system/dnssec_bypass/ns1/root.db
+index b522b6f..8d98a04 100644
+--- a/bin/tests/system/dupsigs/ns1/signing.test.db.in
++++ b/bin/tests/system/dnssec_bypass/ns1/root.db
+@@ -10,9 +10,10 @@
+ ; information regarding copyright ownership.
+
+ $TTL 3600
+-@ IN SOA ns root.ns 1996072700 3600 1800 86400 60
+-@ NS ns
+-ns A 127.0.0.1
+-ns AAAA ::1
++. IN SOA a.nil. a.nil. 1 3600 600 86400 300
++. IN NS a.root-servers.nil.
+
+-$GENERATE 0-499 a${0,4,d} AAAA ::$
++a.root-servers.nil. IN A 10.53.0.1
++
++test. IN NS ns1.test.
++ns1.test. IN A 10.53.0.1
+diff --git a/bin/tests/system/dupsigs/ns1/signing.test.db.in b/bin/tests/system/dnssec_bypass/ns1/test.db
+similarity index 69%
+copy from bin/tests/system/dupsigs/ns1/signing.test.db.in
+copy to bin/tests/system/dnssec_bypass/ns1/test.db
+index b522b6f..6efcd95 100644
+--- a/bin/tests/system/dupsigs/ns1/signing.test.db.in
++++ b/bin/tests/system/dnssec_bypass/ns1/test.db
+@@ -9,10 +9,15 @@
+ ; See the COPYRIGHT file distributed with this work for additional
+ ; information regarding copyright ownership.
+
++$ORIGIN test.
+ $TTL 3600
+-@ IN SOA ns root.ns 1996072700 3600 1800 86400 60
+-@ NS ns
+-ns A 127.0.0.1
+-ns AAAA ::1
+
+-$GENERATE 0-499 a${0,4,d} AAAA ::$
++@ IN SOA a a 1 3600 600 86400 300
++ IN NS ns1.test.
++ns1 IN A 10.53.0.1
++
++evil IN NS ns1.evil
++ns1.evil IN A 10.53.0.3
++
++victim IN NS ns1.victim
++ns1.victim IN A 10.53.0.2
+diff --git a/bin/tests/system/runtime/ns2/named-alt4.conf.in b/bin/tests/system/dnssec_bypass/ns2/named.conf.j2
+similarity index 89%
+copy from bin/tests/system/runtime/ns2/named-alt4.conf.in
+copy to bin/tests/system/dnssec_bypass/ns2/named.conf.j2
+index 4c0312a..e81cee7 100644
+--- a/bin/tests/system/runtime/ns2/named-alt4.conf.in
++++ b/bin/tests/system/dnssec_bypass/ns2/named.conf.j2
+@@ -12,7 +12,6 @@
+ */
+
+ options {
+- directory "./nope";
+ port @PORT@;
+ pid-file "named.pid";
+ listen-on { 10.53.0.2; };
+@@ -20,3 +19,8 @@ options {
+ recursion no;
+ dnssec-validation no;
+ };
++
++zone "victim.test." {
++ type primary;
++ file "victim.db.signed";
++};
+diff --git a/bin/tests/system/dnssec/ns2/cdnskey.secure.db.in b/bin/tests/system/dnssec_bypass/ns2/victim.db
+similarity index 79%
+copy from bin/tests/system/dnssec/ns2/cdnskey.secure.db.in
+copy to bin/tests/system/dnssec_bypass/ns2/victim.db
+index aa3aaab..edcc234 100644
+--- a/bin/tests/system/dnssec/ns2/cdnskey.secure.db.in
++++ b/bin/tests/system/dnssec_bypass/ns2/victim.db
+@@ -9,6 +9,10 @@
+ ; See the COPYRIGHT file distributed with this work for additional
+ ; information regarding copyright ownership.
+
++$ORIGIN victim.test.
+ $TTL 3600
+-@ SOA ns2.example. . 1 3600 1200 86400 1200
+-@ NS ns2.example.
++
++@ IN SOA ns1 hostmaster 1 3600 600 86400 2147483647
++ IN NS ns1
++
++ns1 IN A 10.53.0.2
+diff --git a/bin/tests/system/checkds/ns9/template.db.in b/bin/tests/system/dnssec_bypass/ns3/evil.db
+similarity index 54%
+copy from bin/tests/system/checkds/ns9/template.db.in
+copy to bin/tests/system/dnssec_bypass/ns3/evil.db
+index cf06015..618f9d3 100644
+--- a/bin/tests/system/checkds/ns9/template.db.in
++++ b/bin/tests/system/dnssec_bypass/ns3/evil.db
+@@ -9,19 +9,15 @@
+ ; See the COPYRIGHT file distributed with this work for additional
+ ; information regarding copyright ownership.
+
++$ORIGIN evil.test.
+ $TTL 300
+-@ IN SOA mname1. . (
+- 1 ; serial
+- 20 ; refresh (20 seconds)
+- 20 ; retry (20 seconds)
+- 1814400 ; expire (3 weeks)
+- 3600 ; minimum (1 hour)
+- )
+
+- NS ns9
+-ns9 A 10.53.0.9
+-
+-a A 10.0.0.1
+-b A 10.0.0.2
+-c A 10.0.0.3
++@ IN SOA ns1 hostmaster 1 3600 600 86400 300
++ IN NS ns1
++; Try to poison the victim zone in a resolver cache.
++; If admitted, the aggressive NSEC cache will accept a range such as
++; [evil.test, b.victim.test) and will cause the victim nameserver to
++; be never queried.
++ IN NSEC b.victim.test. NS SOA RRSIG NSEC DNSKEY
+
++ns1 IN A 10.53.0.3
+diff --git a/bin/tests/system/allow_query/ns1/named.conf.in b/bin/tests/system/dnssec_bypass/ns3/named.conf.j2
+similarity index 88%
+copy from bin/tests/system/allow_query/ns1/named.conf.in
+copy to bin/tests/system/dnssec_bypass/ns3/named.conf.j2
+index dd786e2..17d3e18 100644
+--- a/bin/tests/system/allow_query/ns1/named.conf.in
++++ b/bin/tests/system/dnssec_bypass/ns3/named.conf.j2
+@@ -14,13 +14,13 @@
+ options {
+ port @PORT@;
+ pid-file "named.pid";
+- listen-on { 10.53.0.1; };
++ listen-on { 10.53.0.3; };
+ listen-on-v6 { none; };
+ recursion no;
+ dnssec-validation no;
+ };
+
+-zone "." {
++zone "evil.test." {
+ type primary;
+- file "root.db";
++ file "evil.db.signed";
+ };
+diff --git a/bin/tests/system/rrsetorder/ns4/named.conf.in b/bin/tests/system/dnssec_bypass/ns4/named.conf.j2
+similarity index 86%
+copy from bin/tests/system/rrsetorder/ns4/named.conf.in
+copy to bin/tests/system/dnssec_bypass/ns4/named.conf.j2
+index d5fc527..039695d 100644
+--- a/bin/tests/system/rrsetorder/ns4/named.conf.in
++++ b/bin/tests/system/dnssec_bypass/ns4/named.conf.j2
+@@ -19,13 +19,14 @@ options {
+ pid-file "named.pid";
+ listen-on { 10.53.0.4; };
+ listen-on-v6 { none; };
++ allow-transfer { any; };
+ recursion yes;
+ dnssec-validation yes;
+- notify yes;
+- rrset-order {
+- class IN type A name "host.example.com" order random;
+- };
++ synth-from-dnssec yes;
++};
+
++trust-anchors {
++ @root.domain@ @root.type@ @root.contents@;
+ };
+
+ zone "." {
+diff --git a/bin/tests/system/dnssec_bypass/tests_bypass.py b/bin/tests/system/dnssec_bypass/tests_bypass.py
+new file mode 100644
+index 0000000..c41bb7e
+--- /dev/null
++++ b/bin/tests/system/dnssec_bypass/tests_bypass.py
+@@ -0,0 +1,152 @@
++# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++#
++# SPDX-License-Identifier: MPL-2.0
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License, v. 2.0. If a copy of the MPL was not distributed with this
++# file, you can obtain one at https://mozilla.org/MPL/2.0/.
++#
++# See the COPYRIGHT file distributed with this work for additional
++# information regarding copyright ownership.
++
++from datetime import datetime, timedelta, timezone
++
++import shutil
++
++from cryptography.hazmat.primitives.asymmetric import ec
++
++import dns.dnssec
++import dns.name
++import dns.rdataclass
++import dns.rdataset
++import dns.rdatatype
++import dns.rrset
++import dns.zone
++
++from isctest.run import EnvCmd
++
++import isctest
++
++TTL = 3600
++
++
++def bootstrap():
++ keygen = EnvCmd("KEYGEN", "-q -a ECDSA256")
++ signer = EnvCmd("SIGNER", "-S -g -O full")
++
++ def sign_regular_zone(ns: str, zone: str, database: str) -> isctest.kasp.Key:
++ isctest.log.info(f"{zone}: generate keys")
++ keygen(zone, cwd=ns).out.strip()
++ ksk = keygen(f"-f KSK {zone}", cwd=ns).out.strip()
++
++ isctest.log.info(f"{zone}: sign zone")
++ signer(f"-o {zone} {database}", cwd=ns)
++
++ if ns != "ns1":
++ shutil.copy(f"{ns}/dsset-{zone}", f"ns1/dsset-{zone}")
++ shutil.copy(f"{ns}/{ksk}.key", f"ns1/{ksk}.key")
++
++ return isctest.kasp.Key(ksk, keydir=ns)
++
++ # dnssec-signzone and `dns.dnssec.sign_zone` correctly disregard the invalid
++ # NSEC entries when signing the zone. However, for this test we actualy *want*
++ # to serve invalid yet signed zones. To accomplish this we sign the zone and then
++ # replace the correct entries with the faulty ones accompanied by its RRSIG.
++ #
++ # TODO(aydin): move this to `isctest` to sign broken zones
++ def sign_rogue_zone(ns: str, zone: str, database: str) -> None:
++ # Read zone.
++ origin = dns.name.from_text(zone)
++ data = dns.zone.from_file(f"{ns}/{database}", origin=origin, relativize=False)
++
++ # Get key for signing.
++ isctest.log.info(f"{zone}: generate keys")
++ private_key = ec.generate_private_key(ec.SECP256R1())
++ dnskey = dns.dnssec.make_dnskey(
++ public_key=private_key.public_key(),
++ algorithm=dns.dnssec.Algorithm.ECDSAP256SHA256,
++ flags=257,
++ )
++
++ # Sign zone.
++ isctest.log.info(f"{zone}: sign zone")
++ now = datetime.now(timezone.utc)
++ inception = now - timedelta(hours=1)
++ expiration = now + timedelta(days=30)
++
++ for name, node in data.nodes.items():
++ owner = name.derelativize(origin)
++ rdatasets = list(node.rdatasets)
++
++ for rdataset in rdatasets:
++ rrset = dns.rrset.RRset(owner, rdataset.rdclass, rdataset.rdtype)
++ rrset.update(rdataset)
++
++ rrsig = dns.dnssec.sign(
++ rrset=rrset,
++ private_key=private_key,
++ signer=origin,
++ dnskey=dnskey,
++ inception=inception,
++ expiration=expiration,
++ deterministic=False,
++ )
++
++ rdataset = dns.rdataset.Rdataset(rrset.rdclass, dns.rdatatype.RRSIG)
++ rdataset.add(rrsig, rrset.ttl)
++ node.replace_rdataset(rdataset)
++
++ # Sign DNSKEY RRset.
++ dnskey_rrset = dns.rrset.RRset(origin, dns.rdataclass.IN, dns.rdatatype.DNSKEY)
++ dnskey_rrset.add(dnskey, ttl=TTL)
++
++ apex_node = data.nodes[origin]
++ apex_node.replace_rdataset(dnskey_rrset)
++
++ rrsig = dns.dnssec.sign(
++ rrset=dnskey_rrset,
++ private_key=private_key,
++ signer=origin,
++ dnskey=dnskey,
++ inception=inception,
++ expiration=expiration,
++ deterministic=False,
++ )
++ rdataset = dns.rdataset.Rdataset(rrset.rdclass, dns.rdatatype.RRSIG)
++ rdataset.add(rrsig, dnskey_rrset.ttl)
++ apex_node.replace_rdataset(rdataset)
++
++ # Output zone.
++ data.to_file(f"{ns}/{database}.signed", relativize=False)
++
++ # Output DS.
++ ds = dns.dnssec.make_ds(name=origin, key=dnskey, algorithm="SHA256")
++ with open(f"ns1/dsset-{zone}", "w", encoding="utf-8") as f:
++ f.write(f"{zone} {TTL} IN DS {ds.to_text()}\n")
++
++ sign_rogue_zone("ns3", "evil.test.", "evil.db")
++ sign_regular_zone("ns2", "victim.test.", "victim.db")
++ sign_regular_zone("ns1", "test.", "test.db")
++ root_ksk = sign_regular_zone("ns1", ".", "root.db")
++
++ return {
++ "root": root_ksk.into_ta("static-key"),
++ }
++
++
++def test_out_of_zone_nsec(ns4):
++ isctest.log.info("trying to poison aggressive nsec cache")
++ msg = isctest.query.create("nx.evil.test", "A")
++ res = isctest.query.tcp(msg, ns4.ip)
++ isctest.check.noadflag(res)
++
++ isctest.log.info("query victim from recursive")
++ msg = isctest.query.create("victim.test", "SOA")
++ res = isctest.query.tcp(msg, ns4.ip, attempts=1)
++ isctest.check.noerror(res)
++ isctest.check.adflag(res)
++ isctest.check.rr_count_eq(res.answer, 2)
++
++ isctest.log.info("checking for query history on victim nameserver")
++ with open("ns2/named.run", "r", encoding="utf-8") as f:
++ assert "(victim.test): query 'victim.test/SOA/IN' approved" in f.read()
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-13321-02.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-13321-02.patch
new file mode 100644
index 0000000000..a4e8ed2dc8
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-13321-02.patch
@@ -0,0 +1,88 @@
+From: =?utf-8?q?Ayd=C4=B1n_Mercan?= <aydin@isc.org>
+Date: Thu, 7 May 2026 18:59:20 +0300
+Subject: Reject out-of-zone NSEC next owner names
+
+When verifying DNSSEC records, make sure that a next owner name of
+an NSEC record is a subdomain of the signer field.
+
+This follows the specification RFC 4034, section 4.1.1:
+
+ Owner names of RRsets for which the given zone is not authoritative
+ (such as glue records) MUST NOT be listed in the Next Domain Name
+ unless at least one authoritative RRset exists at the same owner
+ name.
+
+While the above paragraph is intended for glue records, it also
+applies to out-of-zone data.
+
+(cherry picked from commit 4065512d25b71605b9502bb69dfb903776d35aa9)
+
+Origin: https://github.com/isc-projects/bind9/commit/058023c66f11d78590d4aa8c4f98946c4c965e21
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-13321
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-13321
+
+CVE: CVE-2026-13321
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/058023c66f11d78590d4aa8c4f98946c4c965e21]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ lib/dns/dnssec.c | 13 +++++++++++++
+ lib/dns/include/dns/dnssec.h | 6 ++++++
+ 2 files changed, 19 insertions(+)
+
+diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
+index 9b9b1f2..5acaea9 100644
+--- a/lib/dns/dnssec.c
++++ b/lib/dns/dnssec.c
+@@ -357,8 +357,10 @@ isc_result_t
+ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ bool ignoretime, unsigned int maxbits, isc_mem_t *mctx,
+ dns_rdata_t *sigrdata, dns_name_t *wild) {
++ dns_rdata_nsec_t nsec;
+ dns_rdata_rrsig_t sig;
+ dns_fixedname_t fnewname;
++ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t r;
+ isc_buffer_t envbuf;
+ dns_rdata_t *rdatas;
+@@ -464,6 +466,17 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ }
+ break;
+ }
++ /*
++ * Check for out of zone NSEC entries.
++ */
++ if (set->type == dns_rdatatype_nsec) {
++ RETERR(dns_rdataset_first(set));
++ dns_rdataset_current(set, &rdata);
++ RETERR(dns_rdata_tostruct(&rdata, &nsec, NULL));
++ if (!dns_name_issubdomain(&nsec.next, &sig.signer)) {
++ return DNS_R_NOVALIDNSEC;
++ }
++ }
+
+ again:
+ ret = dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, false,
+diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h
+index cb8fd9d..2be11b9 100644
+--- a/lib/dns/include/dns/dnssec.h
++++ b/lib/dns/include/dns/dnssec.h
+@@ -151,6 +151,9 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ * this record, as this requires a resolver or database.
+ * If 'ignoretime' is true, temporal validity will not be checked.
+ *
++ * If 'set' is of type NSEC, this function also verifies that the
++ * Next Name is a subdomain of the Signer's Name from 'sigrdata'.
++ *
+ * 'maxbits' specifies the maximum number of rsa exponent bits accepted.
+ *
+ * Requires:
+@@ -173,6 +176,9 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ *\li #DNS_R_KEYUNAUTHORIZED - the key cannot sign this data (either
+ * it is not a zone key or its flags prevent
+ * authentication)
++ *
++ *\li #DNS_R_NOVALIDNSEC - the NSEC rdata is not valid
++ *\li #DNS_R_KEYUNAUTHORIZED - the key cannot sign this data
+ *\li DST_R_*
+ */
+
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-13321-03.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-13321-03.patch
new file mode 100644
index 0000000000..a2ec87efe1
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-13321-03.patch
@@ -0,0 +1,203 @@
+From: =?utf-8?q?Ayd=C4=B1n_Mercan?= <aydin@isc.org>
+Date: Tue, 12 May 2026 14:54:09 +0300
+Subject: change dns_nsec_requiredtypespresent to dns_nsec_is_legal
+
+Change `dns_nsec_requiredtypespresent` to `dns_nsec_is_legal` as a
+function for checking multiple NSEC validity rules.
+
+Currently we now additionally check for out-of-zone NSEC entries.
+
+(cherry picked from commit be2a6a497312469890b552907d039d2de0b44ccc)
+
+Origin: https://github.com/isc-projects/bind9/commit/f751e19a30d107f04c2f644aff9f8dab8fed03ab
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-13321
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-13321
+
+CVE: CVE-2026-13321
+Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/f751e19a30d107f04c2f644aff9f8dab8fed03ab]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ lib/dns/include/dns/nsec.h | 18 ++++++++++++-----
+ lib/dns/nsec.c | 17 ++++++++++++----
+ lib/dns/resolver.c | 48 ++++++++++++++++++++++++++++++++++++++++++++--
+ lib/ns/query.c | 6 +++---
+ 4 files changed, 75 insertions(+), 14 deletions(-)
+
+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 80ee8d7..5abcce5 100644
+--- a/lib/dns/nsec.c
++++ b/lib/dns/nsec.c
+@@ -21,6 +21,7 @@
+ #include <isc/util.h>
+
+ #include <dns/db.h>
++#include <dns/name.h>
+ #include <dns/nsec.h>
+ #include <dns/rdata.h>
+ #include <dns/rdatalist.h>
+@@ -497,8 +498,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;
+
+@@ -513,12 +515,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 01c4a00..1bfd8bb 100644
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -65,7 +65,9 @@
+ #include <dns/rootns.h>
+ #include <dns/stats.h>
+ #include <dns/tsig.h>
++#include <dns/types.h>
+ #include <dns/validator.h>
++#include <dns/view.h>
+ #include <dns/zone.h>
+
+ /* Detailed logging of fctx attach/detach */
+@@ -5620,6 +5622,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.
+ */
+@@ -5650,6 +5682,8 @@ validated(isc_task_t *task, isc_event_t *event) {
+ dns_fixedname_t fwild;
+ dns_name_t *wild = NULL;
+ dns_message_t *message = NULL;
++ dns_fixedname_t fsigner;
++ dns_name_t *signer = NULL;
+
+ UNUSED(task); /* for now */
+
+@@ -6038,10 +6072,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 c4fe7c8..1985f4e 100644
+--- a/lib/ns/query.c
++++ b/lib/ns/query.c
+@@ -10356,10 +10356,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/meta/recipes-connectivity/bind/bind_9.18.49.bb b/meta/recipes-connectivity/bind/bind_9.18.49.bb
index b048ba6559..32205e4104 100644
--- a/meta/recipes-connectivity/bind/bind_9.18.49.bb
+++ b/meta/recipes-connectivity/bind/bind_9.18.49.bb
@@ -35,6 +35,12 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.xz \
file://CVE-2026-11721-03.patch \
file://CVE-2026-12617-01.patch \
file://CVE-2026-12617-02.patch \
+ file://CVE-2026-13204-01.patch \
+ file://CVE-2026-13204-02.patch \
+ file://CVE-2026-13204-03.patch \
+ file://CVE-2026-13321-01.patch \
+ file://CVE-2026-13321-02.patch \
+ file://CVE-2026-13321-03.patch \
"
SRC_URI[sha256sum] = "c43ce4548ebed788cd9df63658a7de105ceafba43fcd63fa352b1093e525cd24"
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [OE-core] [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723
2026-10-12 4:38 [scarthgap][PATCHv2 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
` (4 preceding siblings ...)
2026-10-12 4:38 ` [scarthgap][PATCHv2 6/6] bind: fix for CVE-2026-13204, CVE-2026-13321 Hitendra Prajapati
@ 2026-10-14 9:56 ` Yoann Congal
2026-10-14 13:09 ` Hitendra Prajapati
5 siblings, 1 reply; 9+ messages in thread
From: Yoann Congal @ 2026-10-14 9:56 UTC (permalink / raw)
To: hprajapati, openembedded-core
Hello,
On Mon Oct 12, 2026 at 6:38 AM CEST, Hitendra Prajapati via lists.openembedded.org wrote:
> Pick patch from [1], [2] & [3] also mentioned at Debian report in [4]
>
> [1] https://github.com/isc-projects/bind9/commit/c9cb6a5e24e43489cf3fd4d4cc2193b6a74499cb
> [2] https://github.com/isc-projects/bind9/commit/35e3d49d2222c13786a06021c7ed583d2a656e51
> [3] https://github.com/isc-projects/bind9/commit/833dd3b230b92596074e8da15b12298f46c939f2
> [4] https://security-tracker.debian.org/tracker/CVE-2026-10723
>
> Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
> ---
> .../bind/bind/CVE-2026-10723-01.patch | 70 +++
> .../bind/bind/CVE-2026-10723-02.patch | 496 ++++++++++++++++++
> .../bind/bind/CVE-2026-10723-03.patch | 310 +++++++++++
> .../recipes-connectivity/bind/bind_9.18.49.bb | 3 +
> 4 files changed, 879 insertions(+)
> create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-10723-01.patch
> create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-10723-02.patch
> create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2026-10723-03.patch
>
> diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-10723-02.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-10723-02.patch
> new file mode 100644
> index 0000000000..b2ecf1a669
> --- /dev/null
> +++ b/meta/recipes-connectivity/bind/bind/CVE-2026-10723-02.patch
> @@ -0,0 +1,496 @@
> +From: Alessio Podda <alessio@isc.org>
> +Date: Wed, 3 Jun 2026 10:42:43 +0200
> +Subject: Reproducer for #5874 NSEC3 impersonation
> +
> +LLM generated.
> +
> +(cherry picked from commit f3e2eb333be3ac636f745aa13cfb8d9ee8af87d8)
> +
> +Origin: https://github.com/isc-projects/bind9/commit/35e3d49d2222c13786a06021c7ed583d2a656e51
> +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-10723
> +Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-10723
> +
> +CVE: CVE-2026-10723
> +Upstream-Status: Backport [https://github.com/isc-projects/bind9/commit/35e3d49d2222c13786a06021c7ed583d2a656e51]
> +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
> +---
> + .../ans1/ans.py} | 8 +-
> + .../repro_5874_nsec3_parent/ns2/named.conf.j2 | 35 +++
> + bin/tests/system/repro_5874_nsec3_parent/server.py | 249 +++++++++++++++++++++
> + .../tests_repro_5874_nsec3_parent.py | 144 ++++++++++++
> + 4 files changed, 433 insertions(+), 3 deletions(-)
> + copy bin/tests/system/{glue/setup.sh => repro_5874_nsec3_parent/ans1/ans.py} (83%)
> + create mode 100644 bin/tests/system/repro_5874_nsec3_parent/ns2/named.conf.j2
> + create mode 100644 bin/tests/system/repro_5874_nsec3_parent/server.py
> + create mode 100644 bin/tests/system/repro_5874_nsec3_parent/tests_repro_5874_nsec3_parent.py
> +
> +diff --git a/bin/tests/system/glue/setup.sh b/bin/tests/system/repro_5874_nsec3_parent/ans1/ans.py
> +similarity index 83%
> +copy from bin/tests/system/glue/setup.sh
> +copy to bin/tests/system/repro_5874_nsec3_parent/ans1/ans.py
> +index 82240a7..614f26a 100644
> +--- a/bin/tests/system/glue/setup.sh
> ++++ b/bin/tests/system/repro_5874_nsec3_parent/ans1/ans.py
> +@@ -1,4 +1,4 @@
> +-#!/bin/sh
> ++#!/usr/bin/python3
> +
> + # Copyright (C) Internet Systems Consortium, Inc. ("ISC")
> + #
> +@@ -11,6 +11,8 @@
> + # See the COPYRIGHT file distributed with this work for additional
> + # information regarding copyright ownership.
> +
> +-. ../conf.sh
> ++from server import main
> +
> +-copy_setports ns1/named.conf.in ns1/named.conf
> ++
> ++if __name__ == "__main__":
> ++ main()
The above change does not look right to me (and is not in the upstream
patch).
But, before sending a v3: This series looks like patches from
the 9.18 branch.
Do you know when the 9.18.51 is expected?
Is there a reason to rush/backport these patches instead of waiting for
the release (like we usually do for bind)?
Regards,
--
Yoann Congal
Smile ECS
^ permalink raw reply [flat|nested] 9+ messages in thread