Openembedded Core Discussions
 help / color / mirror / Atom feed
* [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723
@ 2026-09-28  7:11 Hitendra Prajapati
  2026-09-28  7:11 ` [scarthgap][PATCH 2/6] bind: fix for CVE-2026-10822 Hitendra Prajapati
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Hitendra Prajapati @ 2026-09-28  7:11 UTC (permalink / raw)
  To: openembedded-core; +Cc: Hitendra Prajapati

Pick patch from [1], [2] & [3] also mentioned at Debian report in [4]

[1] https://gitlab.isc.org/isc-projects/bind9/-/commit/c9cb6a5e24e43489cf3fd4d4cc2193b6a74499cb
[2] https://gitlab.isc.org/isc-projects/bind9/-/commit/35e3d49d2222c13786a06021c7ed583d2a656e51
[3] https://gitlab.isc.org/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-01.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-10723-01.patch
new file mode 100644
index 0000000000..ffec38aea2
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-10723-01.patch
@@ -0,0 +1,70 @@
+From: Evan Hunt <each@isc.org>
+Date: Thu, 21 May 2026 14:41:55 -0700
+Subject: Check NSEC3 signer matches the owning zone
+
+When validating NSEC3 records, reject any signature whose signer field
+does not match the zone owning the NSEC3.
+
+This ensures that a child zone cannot impersonate its parent and forge
+NXDOMAIN responses for sibling domains.
+
+Fixes: isc-projects/bind9#5874
+(cherry picked from commit 6e5066bb1f0f12d090e8707adb7d6ccf74f8012b)
+
+Origin: https://gitlab.isc.org/isc-projects/bind9/-/commit/c9cb6a5e24e43489cf3fd4d4cc2193b6a74499cb
+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://gitlab.isc.org/isc-projects/bind9/-/commit/c9cb6a5e24e43489cf3fd4d4cc2193b6a74499cb]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ lib/dns/dnssec.c | 19 +++++++++++++++++--
+ lib/isc/result.c |  2 +-
+ 2 files changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
+index c7e9224..1725de3 100644
+--- a/lib/dns/dnssec.c
++++ b/lib/dns/dnssec.c
+@@ -404,10 +404,25 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ 	}
+ 
+ 	/*
+-	 * NS, SOA and DNSKEY records are signed by their owner.
+-	 * DS records are signed by the parent.
++	 * NS, SOA and DNSKEY records are signed by their owners.
++	 * NSEC3 records are signed by the apex, exactly one level up
++	 * from their owner names.
++	 * DS records are signed by the parent zone.
+ 	 */
+ 	switch (set->type) {
++	case dns_rdatatype_nsec3: {
++		dns_name_t apex = DNS_NAME_INITEMPTY;
++		labels = dns_name_countlabels(name);
++		if (labels <= 1) {
++			inc_stat(dns_dnssecstats_fail);
++			return DNS_R_INVALIDNSEC3;
++		}
++		dns_name_split(name, labels - 1, NULL, &apex);
++		if (!dns_name_equal(&apex, &sig.signer)) {
++			inc_stat(dns_dnssecstats_fail);
++			return DNS_R_SIGINVALID;
++		}
++	} break;
+ 	case dns_rdatatype_ns:
+ 	case dns_rdatatype_soa:
+ 	case dns_rdatatype_dnskey:
+diff --git a/lib/isc/result.c b/lib/isc/result.c
+index dbd0431..f78e689 100644
+--- a/lib/isc/result.c
++++ b/lib/isc/result.c
+@@ -198,7 +198,7 @@ static const char *description[ISC_R_NRESULTS] = {
+ 	[DNS_R_COVERINGNSEC] = "covering NSEC record returned",
+ 	[DNS_R_MXISADDRESS] = "MX is an address",
+ 	[DNS_R_DUPLICATE] = "duplicate query",
+-	[DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name (wildcard)",
++	[DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name",
+ 	[DNS_R_NOTPRIMARY] = "not primary",
+ 	[DNS_R_BROKENCHAIN] = "broken trust chain",
+ 	[DNS_R_EXPIRED] = "expired",
diff --git a/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..a3ff47b8bf
--- /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://gitlab.isc.org/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://gitlab.isc.org/isc-projects/bind9/-/commit/35e3d49d2222c13786a06021c7ed583d2a656e51b]
+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()
+diff --git a/bin/tests/system/repro_5874_nsec3_parent/ns2/named.conf.j2 b/bin/tests/system/repro_5874_nsec3_parent/ns2/named.conf.j2
+new file mode 100644
+index 0000000..9358866
+--- /dev/null
++++ b/bin/tests/system/repro_5874_nsec3_parent/ns2/named.conf.j2
+@@ -0,0 +1,35 @@
++// validating resolver
++
++options {
++	query-source address 10.53.0.2;
++	notify-source 10.53.0.2;
++	transfer-source 10.53.0.2;
++	port @PORT@;
++	pid-file "named.pid";
++	listen-on { 10.53.0.2; };
++	listen-on-v6 { none; };
++	recursion yes;
++	dnssec-validation yes;
++	trust-anchor-telemetry no;
++	resolver-query-timeout 5000;
++};
++
++controls {
++	inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
++};
++
++include "../../_common/rndc.key";
++
++zone "." {
++	type hint;
++	file "../../_common/root.hint";
++};
++
++zone "tld.test" {
++	type static-stub;
++	server-addresses { 10.53.0.1; };
++};
++
++trust-anchors {
++	tld.test. static-key 257 3 13 "@TLD_DNSKEY@";
++};
+diff --git a/bin/tests/system/repro_5874_nsec3_parent/server.py b/bin/tests/system/repro_5874_nsec3_parent/server.py
+new file mode 100644
+index 0000000..c6856bb
+--- /dev/null
++++ b/bin/tests/system/repro_5874_nsec3_parent/server.py
+@@ -0,0 +1,249 @@
++#!/usr/bin/python3
++
++# 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 dataclasses import dataclass
++from pathlib import Path
++
++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
++TLD = "tld.test."
++APEX_HASH = "1B40241KFORIOG780N4IKSCRLVETPCTQ"
++ATTACKER = f"{APEX_HASH.lower()}.{TLD}"
++VICTIM = f"victim.{TLD}"
++AUTH_IP = "10.53.0.1"
++
++
++@dataclass(frozen=True)
++class Key:
++    zone: dns.name.Name
++    private_key: object
++    dnskey: dns.rdata.Rdata
++    ds: dns.rdata.Rdata
++
++
++def name(text: str) -> dns.name.Name:
++    return dns.name.from_text(text)
++
++
++def load_keys() -> dict[str, Key]:
++    path = Path(__file__).resolve().parent / "keys.json"
++    with path.open(encoding="utf-8") as keys_file:
++        raw_keys = json.load(keys_file)
++
++    keys = {}
++    for zone, raw_key in raw_keys.items():
++        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"]
++        )
++        ds = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, raw_key["ds"])
++        keys[zone] = Key(name(zone), private_key, dnskey, ds)
++
++    return keys
++
++
++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 rrsig_rrset(covered: dns.rrset.RRset, signer: Key) -> dns.rrset.RRset:
++    rrsig = dns.dnssec.sign(
++        covered,
++        signer.private_key,
++        signer.zone,
++        signer.dnskey,
++        lifetime=86400,
++        verify=True,
++    )
++    return dns.rrset.from_rdata(covered.name, covered.ttl, rrsig)
++
++
++def add_signed(
++    section: list[dns.rrset.RRset], covered: dns.rrset.RRset, signer: Key
++) -> None:
++    section.append(covered)
++    section.append(rrsig_rrset(covered, signer))
++
++
++def dnskey_rrset(zone: str, zone_key: Key) -> dns.rrset.RRset:
++    return rrset_from_rdata(zone, zone_key.dnskey)
++
++
++def ds_rrset(zone: str, zone_key: Key) -> dns.rrset.RRset:
++    return rrset_from_rdata(zone, zone_key.ds)
++
++
++def soa_rrset(zone: str) -> dns.rrset.RRset:
++    return rrset(
++        zone,
++        dns.rdatatype.SOA,
++        f"ns.{zone} hostmaster.{zone} 1 3600 600 86400 300",
++    )
++
++
++def ns_rrset(zone: str, ns_target: str) -> dns.rrset.RRset:
++    return rrset(zone, dns.rdatatype.NS, ns_target)
++
++
++def glue_rrset(ns_target: str, address: str) -> dns.rrset.RRset:
++    return rrset(ns_target, dns.rdatatype.A, address)
++
++
++def answer_dnskey(response: dns.message.Message, zone: str, zone_key: Key) -> None:
++    add_signed(response.answer, dnskey_rrset(zone, zone_key), zone_key)
++
++
++def answer_soa(response: dns.message.Message, zone: str, zone_key: Key) -> None:
++    add_signed(response.answer, soa_rrset(zone), zone_key)
++
++
++def answer_ns(
++    response: dns.message.Message, zone: str, ns_target: str, zone_key: Key
++) -> None:
++    add_signed(response.answer, ns_rrset(zone, ns_target), zone_key)
++
++
++def answer_ds(
++    response: dns.message.Message, zone: str, child_key: Key, parent_key: Key
++) -> None:
++    add_signed(response.answer, ds_rrset(zone, child_key), parent_key)
++
++
++def child_nsec3_rrset() -> dns.rrset.RRset:
++    rdata = dns.rdata.from_text(
++        dns.rdataclass.IN,
++        dns.rdatatype.NSEC3,
++        f"1 0 0 - {APEX_HASH} NS SOA RRSIG DNSKEY NSEC3PARAM",
++    )
++    return dns.rrset.from_rdata(name(f"{APEX_HASH}.{TLD}"), TTL, rdata)
++
++
++def forged_nxdomain(response: dns.message.Message, keys: dict[str, Key]) -> None:
++    response.set_rcode(dns.rcode.NXDOMAIN)
++
++    add_signed(response.authority, soa_rrset(TLD), keys[TLD])
++
++    # The owner name derives zone "tld.test.", but the RRSIG signer is the
++    # secure child zone "1b40241kforiog780n4ikscrlvetpctq.tld.test.".
++    add_signed(response.authority, child_nsec3_rrset(), keys[ATTACKER])
++
++
++class SignedResponseHandler(ResponseHandler):
++    def __init__(self, keys: dict[str, Key]) -> None:
++        self.keys = keys
++
++    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)
++        self.respond(qctx)
++        yield DnsResponseSend(qctx.response, authoritative=True)
++
++    def respond(self, qctx: QueryContext) -> None:
++        raise NotImplementedError
++
++
++class VictimForgedNxdomainHandler(SignedResponseHandler):
++    def match(self, qctx: QueryContext) -> bool:
++        return qctx.qname == name(VICTIM) and qctx.qtype == dns.rdatatype.A
++
++    def respond(self, qctx: QueryContext) -> None:
++        forged_nxdomain(qctx.response, self.keys)
++
++
++class ChildDsHandler(SignedResponseHandler):
++    def match(self, qctx: QueryContext) -> bool:
++        return qctx.qname == name(ATTACKER) and qctx.qtype == dns.rdatatype.DS
++
++    def respond(self, qctx: QueryContext) -> None:
++        answer_ds(qctx.response, ATTACKER, self.keys[ATTACKER], self.keys[TLD])
++
++
++class AttackerZoneHandler(SignedResponseHandler):
++    def match(self, qctx: QueryContext) -> bool:
++        return qctx.qname.is_subdomain(name(ATTACKER))
++
++    def respond(self, qctx: QueryContext) -> None:
++        if qctx.qname == name(ATTACKER):
++            if qctx.qtype == dns.rdatatype.DNSKEY:
++                answer_dnskey(qctx.response, ATTACKER, self.keys[ATTACKER])
++            elif qctx.qtype == dns.rdatatype.SOA:
++                answer_soa(qctx.response, ATTACKER, self.keys[ATTACKER])
++            else:
++                answer_ns(qctx.response, ATTACKER, f"ns.{ATTACKER}", self.keys[ATTACKER])
++                qctx.response.additional.append(glue_rrset(f"ns.{ATTACKER}", AUTH_IP))
++            return
++
++        qctx.response.set_rcode(dns.rcode.NXDOMAIN)
++        add_signed(qctx.response.authority, soa_rrset(ATTACKER), self.keys[ATTACKER])
++
++
++class TldZoneHandler(SignedResponseHandler):
++    def match(self, qctx: QueryContext) -> bool:
++        return qctx.qname.is_subdomain(name(TLD))
++
++    def respond(self, qctx: QueryContext) -> None:
++        if qctx.qname == name(TLD):
++            if qctx.qtype == dns.rdatatype.DNSKEY:
++                answer_dnskey(qctx.response, TLD, self.keys[TLD])
++            elif qctx.qtype == dns.rdatatype.SOA:
++                answer_soa(qctx.response, TLD, self.keys[TLD])
++            else:
++                answer_ns(qctx.response, TLD, "ns.tld.test.", self.keys[TLD])
++                qctx.response.additional.append(glue_rrset("ns.tld.test.", AUTH_IP))
++            return
++
++        qctx.response.set_rcode(dns.rcode.NXDOMAIN)
++        add_signed(qctx.response.authority, soa_rrset(TLD), self.keys[TLD])
++
++
++def main() -> None:
++    keys = load_keys()
++    server = AsyncDnsServer(default_aa=True)
++    server.install_response_handlers(
++        VictimForgedNxdomainHandler(keys),
++        ChildDsHandler(keys),
++        AttackerZoneHandler(keys),
++        TldZoneHandler(keys),
++    )
++    server.run()
+diff --git a/bin/tests/system/repro_5874_nsec3_parent/tests_repro_5874_nsec3_parent.py b/bin/tests/system/repro_5874_nsec3_parent/tests_repro_5874_nsec3_parent.py
+new file mode 100644
+index 0000000..5fbeff5
+--- /dev/null
++++ b/bin/tests/system/repro_5874_nsec3_parent/tests_repro_5874_nsec3_parent.py
+@@ -0,0 +1,144 @@
++#!/usr/bin/python3
++
++# 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 pathlib import Path
++
++import json
++
++from cryptography.hazmat.primitives import serialization
++from cryptography.hazmat.primitives.asymmetric import ec
++
++import dns.dnssec
++import dns.flags
++import dns.name
++import dns.rdataclass
++import dns.rdatatype
++import pytest
++
++import isctest
++
++APEX_HASH = "1B40241KFORIOG780N4IKSCRLVETPCTQ"
++ATTACKER = f"{APEX_HASH.lower()}.tld.test."
++VICTIM = "victim.tld.test."
++AUTH = "10.53.0.1"
++RESOLVER = "10.53.0.2"
++
++pytestmark = pytest.mark.extra_artifacts(
++    [
++        "ans*/ans.run",
++        "keys.json",
++    ]
++)
++
++
++def _make_key(zone):
++    private_key = ec.generate_private_key(ec.SECP256R1())
++    dnskey = dns.dnssec.make_dnskey(
++        private_key.public_key(),
++        algorithm="ECDSAP256SHA256",
++        flags=257,
++    )
++    ds = dns.dnssec.make_ds(dns.name.from_text(zone), dnskey, "SHA256")
++    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(),
++        "ds": ds.to_text(),
++    }
++
++
++def bootstrap():
++    zones = ["tld.test.", ATTACKER]
++    keys = {zone: _make_key(zone) for zone in zones}
++
++    Path("keys.json").write_text(json.dumps(keys, indent=2), encoding="ascii")
++
++    tld_dnskey = "".join(keys["tld.test."]["dnskey"].split()[3:])
++    return {"TLD_DNSKEY": tld_dnskey}
++
++
++def _check_direct_dnskey_response(zone):
++    query = isctest.query.create(zone, "DNSKEY")
++    response = isctest.query.tcp(query, AUTH)
++
++    isctest.check.noerror(response)
++    assert response.flags & dns.flags.AA
++    assert (
++        response.get_rrset(
++            response.answer,
++            dns.name.from_text(zone),
++            dns.rdataclass.IN,
++            dns.rdatatype.DNSKEY,
++        )
++        is not None
++    ), response
++
++
++def _check_direct_ds_response(zone):
++    query = isctest.query.create(zone, "DS")
++    response = isctest.query.tcp(query, AUTH)
++
++    isctest.check.noerror(response)
++    assert response.flags & dns.flags.AA
++    assert (
++        response.get_rrset(
++            response.answer,
++            dns.name.from_text(zone),
++            dns.rdataclass.IN,
++            dns.rdatatype.DS,
++        )
++        is not None
++    ), response
++
++
++def test_repro_5874_direct_forged_nsec3_response_has_child_signer():
++    _check_direct_dnskey_response("tld.test.")
++    _check_direct_dnskey_response(ATTACKER)
++    _check_direct_ds_response(ATTACKER)
++
++    query = isctest.query.create(VICTIM, "A")
++    response = isctest.query.tcp(query, AUTH)
++
++    isctest.check.nxdomain(response)
++    assert response.flags & dns.flags.AA
++
++    nsec3_owner = dns.name.from_text(f"{APEX_HASH}.tld.test.")
++    nsec3 = response.get_rrset(
++        response.authority,
++        nsec3_owner,
++        dns.rdataclass.IN,
++        dns.rdatatype.NSEC3,
++    )
++    rrsig = response.get_rrset(
++        response.authority,
++        nsec3_owner,
++        dns.rdataclass.IN,
++        dns.rdatatype.RRSIG,
++        covers=dns.rdatatype.NSEC3,
++    )
++
++    assert nsec3 is not None, response
++    assert rrsig is not None, response
++    assert rrsig[0].signer == dns.name.from_text(ATTACKER)
++
++
++def test_repro_5874_resolver_rejects_child_signed_nsec3_parent_proof():
++    query = isctest.query.create(VICTIM, "A")
++    response = isctest.query.tcp(query, RESOLVER)
++
++    isctest.check.servfail(response)
++    isctest.check.noadflag(response)
diff --git a/meta/recipes-connectivity/bind/bind/CVE-2026-10723-03.patch b/meta/recipes-connectivity/bind/bind/CVE-2026-10723-03.patch
new file mode 100644
index 0000000000..5461b3c87e
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2026-10723-03.patch
@@ -0,0 +1,310 @@
+From: Matthijs Mekking <matthijs@isc.org>
+Date: Thu, 4 Jun 2026 16:12:50 +0200
+Subject: Update reproducer #5874
+
+Update the llm generated reproducer:
+- Move server.py into ans1/ans.py
+- Remove unnecessary named.conf configuration options
+- Add comments describing the steps (copied from GL issue)
+- Rename system test
+
+(cherry picked from commit c1321fef165a2ef8c2bff971901c58941e8e694c)
+
+Origin: https://gitlab.isc.org/isc-projects/bind9/-/commit/833dd3b230b92596074e8da15b12298f46c939f2
+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://gitlab.isc.org/isc-projects/bind9/-/commit/833dd3b230b92596074e8da15b12298f46c939f2]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ .../server.py => nsec3_impersonation/ans1/ans.py}  | 81 +++++++++++++++-------
+ .../ns2/named.conf.j2                              |  2 -
+ .../tests_nsec3_impersonation.py}                  | 38 ++++++----
+ .../system/repro_5874_nsec3_parent/ans1/ans.py     | 18 -----
+ 4 files changed, 79 insertions(+), 60 deletions(-)
+ rename bin/tests/system/{repro_5874_nsec3_parent/server.py => nsec3_impersonation/ans1/ans.py} (83%)
+ rename bin/tests/system/{repro_5874_nsec3_parent => nsec3_impersonation}/ns2/named.conf.j2 (90%)
+ rename bin/tests/system/{repro_5874_nsec3_parent/tests_repro_5874_nsec3_parent.py => nsec3_impersonation/tests_nsec3_impersonation.py} (84%)
+ delete mode 100644 bin/tests/system/repro_5874_nsec3_parent/ans1/ans.py
+
+diff --git a/bin/tests/system/repro_5874_nsec3_parent/server.py b/bin/tests/system/nsec3_impersonation/ans1/ans.py
+similarity index 83%
+rename from bin/tests/system/repro_5874_nsec3_parent/server.py
+rename to bin/tests/system/nsec3_impersonation/ans1/ans.py
+index c6856bb..177e79c 100644
+--- a/bin/tests/system/repro_5874_nsec3_parent/server.py
++++ b/bin/tests/system/nsec3_impersonation/ans1/ans.py
+@@ -23,9 +23,9 @@ import dns.dnssec
+ import dns.flags
+ import dns.message
+ import dns.name
++import dns.rcode
+ import dns.rdata
+ import dns.rdataclass
+-import dns.rcode
+ import dns.rdatatype
+ import dns.rrset
+ 
+@@ -141,10 +141,21 @@ def answer_ns(
+     add_signed(response.answer, ns_rrset(zone, ns_target), zone_key)
+ 
+ 
+-def answer_ds(
+-    response: dns.message.Message, zone: str, child_key: Key, parent_key: Key
+-) -> None:
+-    add_signed(response.answer, ds_rrset(zone, child_key), parent_key)
++class SignedResponseHandler(ResponseHandler):
++    def __init__(self, keys: dict[str, Key]) -> None:
++        self.keys = keys
++
++    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)
++        self.respond(qctx)
++        yield DnsResponseSend(qctx.response, authoritative=True)
++
++    def respond(self, qctx: QueryContext) -> None:
++        raise NotImplementedError
+ 
+ 
+ def child_nsec3_rrset() -> dns.rrset.RRset:
+@@ -162,28 +173,15 @@ def forged_nxdomain(response: dns.message.Message, keys: dict[str, Key]) -> None
+     add_signed(response.authority, soa_rrset(TLD), keys[TLD])
+ 
+     # The owner name derives zone "tld.test.", but the RRSIG signer is the
+-    # secure child zone "1b40241kforiog780n4ikscrlvetpctq.tld.test.".
++    # malicious child zone "1b40241kforiog780n4ikscrlvetpctq.tld.test.".
+     add_signed(response.authority, child_nsec3_rrset(), keys[ATTACKER])
+ 
+ 
+-class SignedResponseHandler(ResponseHandler):
+-    def __init__(self, keys: dict[str, Key]) -> None:
+-        self.keys = keys
+-
+-    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)
+-        self.respond(qctx)
+-        yield DnsResponseSend(qctx.response, authoritative=True)
+-
+-    def respond(self, qctx: QueryContext) -> None:
+-        raise NotImplementedError
+-
+-
+ class VictimForgedNxdomainHandler(SignedResponseHandler):
++    """
++    This serves the forged response for the victim's domain.
++    """
++
+     def match(self, qctx: QueryContext) -> bool:
+         return qctx.qname == name(VICTIM) and qctx.qtype == dns.rdatatype.A
+ 
+@@ -192,14 +190,31 @@ class VictimForgedNxdomainHandler(SignedResponseHandler):
+ 
+ 
+ class ChildDsHandler(SignedResponseHandler):
++    """
++    This will spoof the response for the malicious zone when qtype is DS.
++    It is actually a validly signed DS response.
++    """
++
+     def match(self, qctx: QueryContext) -> bool:
+         return qctx.qname == name(ATTACKER) and qctx.qtype == dns.rdatatype.DS
+ 
+     def respond(self, qctx: QueryContext) -> None:
+-        answer_ds(qctx.response, ATTACKER, self.keys[ATTACKER], self.keys[TLD])
++        response = qctx.response
++        zone = ATTACKER
++        child_key = self.keys[ATTACKER]
++        parent_key = self.keys[TLD]
++
++        add_signed(response.answer, ds_rrset(zone, child_key), parent_key)
+ 
+ 
+ class AttackerZoneHandler(SignedResponseHandler):
++    """
++    Acts as the malicious authoritative name server. The zone being served
++    is the hashed label of the parent zone (tld.test). This will respond
++    for all queries qtype SOA, DNSKEY, NS at the apex. Any names below
++    the apex are answered with an NXDOMAIN with no NSEC or NSEC3 present.
++    """
++
+     def match(self, qctx: QueryContext) -> bool:
+         return qctx.qname.is_subdomain(name(ATTACKER))
+ 
+@@ -210,7 +225,9 @@ class AttackerZoneHandler(SignedResponseHandler):
+             elif qctx.qtype == dns.rdatatype.SOA:
+                 answer_soa(qctx.response, ATTACKER, self.keys[ATTACKER])
+             else:
+-                answer_ns(qctx.response, ATTACKER, f"ns.{ATTACKER}", self.keys[ATTACKER])
++                answer_ns(
++                    qctx.response, ATTACKER, f"ns.{ATTACKER}", self.keys[ATTACKER]
++                )
+                 qctx.response.additional.append(glue_rrset(f"ns.{ATTACKER}", AUTH_IP))
+             return
+ 
+@@ -219,6 +236,16 @@ class AttackerZoneHandler(SignedResponseHandler):
+ 
+ 
+ class TldZoneHandler(SignedResponseHandler):
++    """
++    Acts as the TLD who is being used in the attack, but is not a standard
++    name server. It only responds with validly signed records for DNSKEY, SOA
++    and NS on the apex. Any names below the apex are answered with an NXDOMAIN
++    with no NSEC or NSEC3 present.
++
++    If we turn this into a regular name server than the attack won't work.
++    The attack assumes that the adversary can inject these responses on-path.
++    """
++
+     def match(self, qctx: QueryContext) -> bool:
+         return qctx.qname.is_subdomain(name(TLD))
+ 
+@@ -247,3 +274,7 @@ def main() -> None:
+         TldZoneHandler(keys),
+     )
+     server.run()
++
++
++if __name__ == "__main__":
++    main()
+diff --git a/bin/tests/system/repro_5874_nsec3_parent/ns2/named.conf.j2 b/bin/tests/system/nsec3_impersonation/ns2/named.conf.j2
+similarity index 90%
+rename from bin/tests/system/repro_5874_nsec3_parent/ns2/named.conf.j2
+rename to bin/tests/system/nsec3_impersonation/ns2/named.conf.j2
+index 9358866..2c9b0bb 100644
+--- a/bin/tests/system/repro_5874_nsec3_parent/ns2/named.conf.j2
++++ b/bin/tests/system/nsec3_impersonation/ns2/named.conf.j2
+@@ -10,8 +10,6 @@ options {
+ 	listen-on-v6 { none; };
+ 	recursion yes;
+ 	dnssec-validation yes;
+-	trust-anchor-telemetry no;
+-	resolver-query-timeout 5000;
+ };
+ 
+ controls {
+diff --git a/bin/tests/system/repro_5874_nsec3_parent/tests_repro_5874_nsec3_parent.py b/bin/tests/system/nsec3_impersonation/tests_nsec3_impersonation.py
+similarity index 84%
+rename from bin/tests/system/repro_5874_nsec3_parent/tests_repro_5874_nsec3_parent.py
+rename to bin/tests/system/nsec3_impersonation/tests_nsec3_impersonation.py
+index 5fbeff5..bd9bd27 100644
+--- a/bin/tests/system/repro_5874_nsec3_parent/tests_repro_5874_nsec3_parent.py
++++ b/bin/tests/system/nsec3_impersonation/tests_nsec3_impersonation.py
+@@ -26,6 +26,7 @@ import dns.rdatatype
+ import pytest
+ 
+ import isctest
++import isctest.mark
+ 
+ APEX_HASH = "1B40241KFORIOG780N4IKSCRLVETPCTQ"
+ ATTACKER = f"{APEX_HASH.lower()}.tld.test."
+@@ -33,12 +34,15 @@ VICTIM = "victim.tld.test."
+ 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(
++        [
++            "ans*/ans.run",
++            "ans*/keys.json",
++        ]
++    ),
++]
+ 
+ 
+ def _make_key(zone):
+@@ -65,13 +69,13 @@ def bootstrap():
+     zones = ["tld.test.", ATTACKER]
+     keys = {zone: _make_key(zone) for zone in zones}
+ 
+-    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")
+ 
+     tld_dnskey = "".join(keys["tld.test."]["dnskey"].split()[3:])
+     return {"TLD_DNSKEY": tld_dnskey}
+ 
+ 
+-def _check_direct_dnskey_response(zone):
++def check_dnskey_response(zone):
+     query = isctest.query.create(zone, "DNSKEY")
+     response = isctest.query.tcp(query, AUTH)
+ 
+@@ -88,7 +92,7 @@ def _check_direct_dnskey_response(zone):
+     ), response
+ 
+ 
+-def _check_direct_ds_response(zone):
++def check_ds_response(zone):
+     query = isctest.query.create(zone, "DS")
+     response = isctest.query.tcp(query, AUTH)
+ 
+@@ -105,10 +109,10 @@ def _check_direct_ds_response(zone):
+     ), response
+ 
+ 
+-def test_repro_5874_direct_forged_nsec3_response_has_child_signer():
+-    _check_direct_dnskey_response("tld.test.")
+-    _check_direct_dnskey_response(ATTACKER)
+-    _check_direct_ds_response(ATTACKER)
++def test_attack_responses():
++    check_dnskey_response("tld.test.")
++    check_dnskey_response(ATTACKER)
++    check_ds_response(ATTACKER)
+ 
+     query = isctest.query.create(VICTIM, "A")
+     response = isctest.query.tcp(query, AUTH)
+@@ -136,9 +140,13 @@ def test_repro_5874_direct_forged_nsec3_response_has_child_signer():
+     assert rrsig[0].signer == dns.name.from_text(ATTACKER)
+ 
+ 
+-def test_repro_5874_resolver_rejects_child_signed_nsec3_parent_proof():
++def test_nsec3_impersonation():
++    """
++    Reproducer for #5874:
++    F-006 DNSSEC Validation Bypass NSEC3 Apex Hash Label Parent Impersonation
++    """
+     query = isctest.query.create(VICTIM, "A")
+     response = isctest.query.tcp(query, RESOLVER)
+ 
+-    isctest.check.servfail(response)
+     isctest.check.noadflag(response)
++    isctest.check.servfail(response)
+diff --git a/bin/tests/system/repro_5874_nsec3_parent/ans1/ans.py b/bin/tests/system/repro_5874_nsec3_parent/ans1/ans.py
+deleted file mode 100644
+index 614f26a..0000000
+--- a/bin/tests/system/repro_5874_nsec3_parent/ans1/ans.py
++++ /dev/null
+@@ -1,18 +0,0 @@
+-#!/usr/bin/python3
+-
+-# 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 server import main
+-
+-
+-if __name__ == "__main__":
+-    main()
diff --git a/meta/recipes-connectivity/bind/bind_9.18.49.bb b/meta/recipes-connectivity/bind/bind_9.18.49.bb
index 723a09e739..161736f9b1 100644
--- a/meta/recipes-connectivity/bind/bind_9.18.49.bb
+++ b/meta/recipes-connectivity/bind/bind_9.18.49.bb
@@ -18,6 +18,9 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.xz \
            file://bind-ensure-searching-for-json-headers-searches-sysr.patch \
            file://0001-named-lwresd-V-and-start-log-hide-build-options.patch \
            file://0001-avoid-start-failure-with-bind-user.patch \
+           file://CVE-2026-10723-01.patch \
+           file://CVE-2026-10723-02.patch \
+           file://CVE-2026-10723-03.patch \
            "
 
 SRC_URI[sha256sum] = "c43ce4548ebed788cd9df63658a7de105ceafba43fcd63fa352b1093e525cd24"
-- 
2.50.1



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [scarthgap][PATCH 2/6] bind: fix for CVE-2026-10822
  2026-09-28  7:11 [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
@ 2026-09-28  7:11 ` Hitendra Prajapati
  2026-09-28  7:11 ` [scarthgap][PATCH 3/6] bind: fix for CVE-2026-11331 Hitendra Prajapati
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Hitendra Prajapati @ 2026-09-28  7:11 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://gitlab.isc.org/isc-projects/bind9/-/commit/d413c9ac2e29a728531354a69c8c8234c01b7d1e
[2] https://gitlab.isc.org/isc-projects/bind9/-/commit/a5f1a9d0d2ec021618924b14202ac96ead8299c1
[3] https://gitlab.isc.org/isc-projects/bind9/-/commit/e73b70a64453e7d97a11cb5f0afe8bb02d34aaf8
[4] https://gitlab.isc.org/isc-projects/bind9/-/commit/19ac8b8e46aeb0a15e217bc7bdf485b31b87d9b4
[5] https://gitlab.isc.org/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..1c330418a6
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..0735bafadd
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..5708c06d9f
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..ff5a8811f0
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..41a6588045
--- /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://gitlab.isc.org/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://gitlab.isc.org/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] 8+ messages in thread

* [scarthgap][PATCH 3/6] bind: fix for CVE-2026-11331
  2026-09-28  7:11 [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
  2026-09-28  7:11 ` [scarthgap][PATCH 2/6] bind: fix for CVE-2026-10822 Hitendra Prajapati
@ 2026-09-28  7:11 ` Hitendra Prajapati
  2026-09-28  7:11 ` [scarthgap][PATCH 4/6] bind: fix for CVE-2026-11622, CVE-2026-11721 Hitendra Prajapati
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Hitendra Prajapati @ 2026-09-28  7:11 UTC (permalink / raw)
  To: openembedded-core; +Cc: Hitendra Prajapati

Pick patch from [1], [2] & [3] also mentioned at Debian report in [4]

[1] https://gitlab.isc.org/isc-projects/bind9/-/commit/adc8285d23e2eac6ec463f5dbc5a9596fdd36c60
[2] https://gitlab.isc.org/isc-projects/bind9/-/commit/095b11f20f911f5b8059bdc349b256d6c64ece30
[3] https://gitlab.isc.org/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..1aaca7392a
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..8f45d7021b
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..2d57122632
--- /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://gitlab.isc.org/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://gitlab.isc.org/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] 8+ messages in thread

* [scarthgap][PATCH 4/6] bind: fix for CVE-2026-11622, CVE-2026-11721
  2026-09-28  7:11 [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
  2026-09-28  7:11 ` [scarthgap][PATCH 2/6] bind: fix for CVE-2026-10822 Hitendra Prajapati
  2026-09-28  7:11 ` [scarthgap][PATCH 3/6] bind: fix for CVE-2026-11331 Hitendra Prajapati
@ 2026-09-28  7:11 ` Hitendra Prajapati
  2026-09-28  7:11 ` [scarthgap][PATCH 5/6] bind: fix for CVE-2026-12617 Hitendra Prajapati
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Hitendra Prajapati @ 2026-09-28  7:11 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://gitlab.isc.org/isc-projects/bind9/-/commit/231b1ca3edfb26389e1af39181aa6b4413e87ec4
[2] https://gitlab.isc.org/isc-projects/bind9/-/commit/1a4986e2533f87e80eb21da3f06708d335aff1e2
[3] https://gitlab.isc.org/isc-projects/bind9/-/commit/15089066b15f826d7487c3d160b5872820f84b83
[4] https://gitlab.isc.org/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..9698762029
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..77579de2d2
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..125ecf468b
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..63fae84da1
--- /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://gitlab.isc.org/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://gitlab.isc.org/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] 8+ messages in thread

* [scarthgap][PATCH 5/6] bind: fix for CVE-2026-12617
  2026-09-28  7:11 [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
                   ` (2 preceding siblings ...)
  2026-09-28  7:11 ` [scarthgap][PATCH 4/6] bind: fix for CVE-2026-11622, CVE-2026-11721 Hitendra Prajapati
@ 2026-09-28  7:11 ` Hitendra Prajapati
  2026-09-28  7:11 ` [scarthgap][PATCH 6/6] bind: fix for CVE-2026-13204, CVE-2026-13321 Hitendra Prajapati
  2026-10-02 10:22 ` [OE-core] [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Yoann Congal
  5 siblings, 0 replies; 8+ messages in thread
From: Hitendra Prajapati @ 2026-09-28  7:11 UTC (permalink / raw)
  To: openembedded-core; +Cc: Hitendra Prajapati

Pick patch from [1] & [2] also mentioned at Debian report in [2]

[1] https://gitlab.isc.org/isc-projects/bind9/-/commit/c740c37689f234e21a9b0ef760471ef2cf1133f5
[2] https://gitlab.isc.org/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..35d357374b
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..7dfaafb80d
--- /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://gitlab.isc.org/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://gitlab.isc.org/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] 8+ messages in thread

* [scarthgap][PATCH 6/6] bind: fix for CVE-2026-13204, CVE-2026-13321
  2026-09-28  7:11 [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
                   ` (3 preceding siblings ...)
  2026-09-28  7:11 ` [scarthgap][PATCH 5/6] bind: fix for CVE-2026-12617 Hitendra Prajapati
@ 2026-09-28  7:11 ` Hitendra Prajapati
  2026-10-02 10:22 ` [OE-core] [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Yoann Congal
  5 siblings, 0 replies; 8+ messages in thread
From: Hitendra Prajapati @ 2026-09-28  7:11 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://gitlab.isc.org/isc-projects/bind9/-/commit/0cf010c153518f1f9831e201891ecba8d8ba65e1
[2] https://gitlab.isc.org/isc-projects/bind9/-/commit/6c0e599ea85c0c53a4af09742e64e193da089bb4
[3] https://gitlab.isc.org/isc-projects/bind9/-/commit/48f5aa5fb3746d6194edcc57e8792a8b3cc3b454
[4] https://gitlab.isc.org/isc-projects/bind9/-/commit/c969ad2c17b43dd999e358bfeb280d3df6fab822
[5] https://gitlab.isc.org/isc-projects/bind9/-/commit/058023c66f11d78590d4aa8c4f98946c4c965e21
[6] https://gitlab.isc.org/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..4b2bebfcc3
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..5bcd10474f
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..59e200fc87
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..90a17f2a08
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..a9bf3521b0
--- /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://gitlab.isc.org/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://gitlab.isc.org/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..c85e92d22d
--- /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://gitlab.isc.org/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://gitlab.isc.org/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] 8+ messages in thread

* Re: [OE-core] [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723
  2026-09-28  7:11 [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
                   ` (4 preceding siblings ...)
  2026-09-28  7:11 ` [scarthgap][PATCH 6/6] bind: fix for CVE-2026-13204, CVE-2026-13321 Hitendra Prajapati
@ 2026-10-02 10:22 ` Yoann Congal
  2026-10-12  4:39   ` Hitendra Prajapati
  5 siblings, 1 reply; 8+ messages in thread
From: Yoann Congal @ 2026-10-02 10:22 UTC (permalink / raw)
  To: hprajapati, openembedded-core

On Mon Sep 28, 2026 at 9:11 AM CEST, Hitendra Prajapati via lists.openembedded.org wrote:
> Pick patch from [1], [2] & [3] also mentioned at Debian report in [4]
>
> [1] https://gitlab.isc.org/isc-projects/bind9/-/commit/c9cb6a5e24e43489cf3fd4d4cc2193b6a74499cb
> [2] https://gitlab.isc.org/isc-projects/bind9/-/commit/35e3d49d2222c13786a06021c7ed583d2a656e51
> [3] https://gitlab.isc.org/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..a3ff47b8bf
> --- /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://gitlab.isc.org/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://gitlab.isc.org/isc-projects/bind9/-/commit/35e3d49d2222c13786a06021c7ed583d2a656e51b]

Hello,

That URL is wrong and return a 404 (an extra 'b'?)

Can you check please?
Can you also check the other URLs of this series?
FYI this gitlab instance seem to be under load of bot pressure and I
can't reach it right now. I'll also accept
https://github.com/isc-projects/bind9/ URLs (official mirror).

Regards,
-- 
Yoann Congal
Smile ECS



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723
  2026-10-02 10:22 ` [OE-core] [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Yoann Congal
@ 2026-10-12  4:39   ` Hitendra Prajapati
  0 siblings, 0 replies; 8+ messages in thread
From: Hitendra Prajapati @ 2026-10-12  4:39 UTC (permalink / raw)
  To: openembedded-core

[-- Attachment #1: Type: text/plain, Size: 77 bytes --]

Hi Yoann,

I have send v2 with updated source link.

Regards,
Hitendra

[-- Attachment #2: Type: text/html, Size: 155 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-12  4:39 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-28  7:11 [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Hitendra Prajapati
2026-09-28  7:11 ` [scarthgap][PATCH 2/6] bind: fix for CVE-2026-10822 Hitendra Prajapati
2026-09-28  7:11 ` [scarthgap][PATCH 3/6] bind: fix for CVE-2026-11331 Hitendra Prajapati
2026-09-28  7:11 ` [scarthgap][PATCH 4/6] bind: fix for CVE-2026-11622, CVE-2026-11721 Hitendra Prajapati
2026-09-28  7:11 ` [scarthgap][PATCH 5/6] bind: fix for CVE-2026-12617 Hitendra Prajapati
2026-09-28  7:11 ` [scarthgap][PATCH 6/6] bind: fix for CVE-2026-13204, CVE-2026-13321 Hitendra Prajapati
2026-10-02 10:22 ` [OE-core] [scarthgap][PATCH 1/6] bind: fix for CVE-2026-10723 Yoann Congal
2026-10-12  4:39   ` Hitendra Prajapati

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox