public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/bind] f45: Key Record using PRIVATEDNS algorithm may lead to exit (CVE-2026-10822)
@ 2026-08-26  8:34 
  0 siblings, 0 replies; only message in thread
From:  @ 2026-08-26  8:34 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/bind
            Branch : f45
            Commit : c72c35866e76095ca27413a6473f31d1fb8fd4c1
            Author : Petr Menšík <pemensik@redhat.com>
            Date   : 2026-08-25T12:39:56+02:00
            Stats  : +377/-0 in 3 file(s)
            URL    : https://src.fedoraproject.org/rpms/bind/c/c72c35866e76095ca27413a6473f31d1fb8fd4c1?branch=f45

            Log:
            Key Record using PRIVATEDNS algorithm may lead to exit (CVE-2026-10822)

[9.18] [CVE-2026-10822] sec: usr: Fix dns_name_fromwire to record boundaries

Previously, `dns_name_fromwire()` did not honor the record boundary when reading names from the wire, allowing malformed records to be accepted when they should not have been. This has been fixed.

Closes: https://gitlab.isc.org/isc-projects/bind9/-/issues/6004

---
diff --git a/bind-9.18-CVE-2026-10822-test.patch b/bind-9.18-CVE-2026-10822-test.patch
new file mode 100644
index 0000000..f95514f
--- /dev/null
+++ b/bind-9.18-CVE-2026-10822-test.patch
@@ -0,0 +1,320 @@
+From a4ce4c0ce5b8d7630417730dc1b98bf554e0801f Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 19 May 2026 10:44:04 +1000
+Subject: [PATCH] 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)
+(cherry picked from commit d413c9ac2e29a728531354a69c8c8234c01b7d1e)
+
+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)
+(cherry picked from commit 19ac8b8e46aeb0a15e217bc7bdf485b31b87d9b4)
+
+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)
+(cherry picked from commit 8e066d3fc369e3346f22bb5cfb67a7ab08a74034)
+---
+ bin/tests/system/xfer/ans9/ans.py       | 142 ++++++++++++++++++++++++
+ bin/tests/system/xfer/ns6/named.conf.in |   9 ++
+ bin/tests/system/xfer/tests.sh          |  16 +++
+ tests/dns/name_test.c                   |  30 +++++
+ tests/dns/rdata_test.c                  |  21 ++++
+ 5 files changed, 218 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 0000000000..a9e73953ee
+--- /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 142383c89a..63809448f0 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 a2c0adbc28..e08be175b7 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/tests/dns/name_test.c b/tests/dns/name_test.c
+index fb34dcace1..95f6598eb8 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/tests/dns/rdata_test.c b/tests/dns/rdata_test.c
+index 6354819d10..7f0df6e046 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);
+ }
+ 
+ /*
+-- 
+2.55.0
+

diff --git a/bind-9.18-CVE-2026-10822.patch b/bind-9.18-CVE-2026-10822.patch
new file mode 100644
index 0000000..22cdbdd
--- /dev/null
+++ b/bind-9.18-CVE-2026-10822.patch
@@ -0,0 +1,54 @@
+From 7596cbc240b0492461943f7c34d040fb66a7554c Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 19 May 2026 15:00:17 +1000
+Subject: [PATCH] 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)
+(cherry picked from commit a5f1a9d0d2ec021618924b14202ac96ead8299c1)
+
+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)
+(cherry picked from commit e73b70a64453e7d97a11cb5f0afe8bb02d34aaf8)
+---
+ bin/tools/dnstap-read.c | 1 +
+ lib/dns/name.c          | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/bin/tools/dnstap-read.c b/bin/tools/dnstap-read.c
+index a1d0243a1a..bb78ae12b1 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/lib/dns/name.c b/lib/dns/name.c
+index cc0e30e5b5..2ce868a2ba 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;
+-- 
+2.55.0
+

diff --git a/bind.spec b/bind.spec
index bbe677d..c8f2ddf 100644
--- a/bind.spec
+++ b/bind.spec
@@ -164,6 +164,9 @@ Patch35: 0001-Use-variable-PROGRAM_SUFFIX-in-install-target.patch
 # https://gitlab.isc.org/isc-projects/bind9/-/work_items/5856
 Patch36: bind-9.18-CVE-2026-11331.patch
 Patch37: bind-9.18-CVE-2026-11331-test.patch
+# https://gitlab.isc.org/isc-projects/bind9/commit/c5d2fc706ca3635d9928c1cc68db73bffb35d772
+Patch38: bind-9.18-CVE-2026-10822.patch
+Patch40: bind-9.18-CVE-2026-10822-test.patch
 
 %{?systemd_ordering}
 # https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-08-26  8:34 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-26  8:34 [rpms/bind] f45: Key Record using PRIVATEDNS algorithm may lead to exit (CVE-2026-10822) 

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