From e577695aebbefb7d7c76bdbf6b698b3bd41d342e Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:11:15 +0200 Subject: [PATCH 01/13] Set version to 1.25.1 for release. --- configure | 25 +++++++++++++------------ configure.ac | 5 +++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/configure b/configure index 0642c41d7..f9d46ea15 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for unbound 1.25.0. +# Generated by GNU Autoconf 2.71 for unbound 1.25.1. # # Report bugs to . # @@ -622,8 +622,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.25.0' -PACKAGE_STRING='unbound 1.25.0' +PACKAGE_VERSION='1.25.1' +PACKAGE_STRING='unbound 1.25.1' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues' PACKAGE_URL='' @@ -1513,7 +1513,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures unbound 1.25.0 to adapt to many kinds of systems. +\`configure' configures unbound 1.25.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1579,7 +1579,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.25.0:";; + short | recursive ) echo "Configuration of unbound 1.25.1:";; esac cat <<\_ACEOF @@ -1832,7 +1832,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.25.0 +unbound configure 1.25.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2489,7 +2489,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by unbound $as_me 1.25.0, which was +It was created by unbound $as_me 1.25.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3253,11 +3253,11 @@ UNBOUND_VERSION_MAJOR=1 UNBOUND_VERSION_MINOR=25 -UNBOUND_VERSION_MICRO=0 +UNBOUND_VERSION_MICRO=1 LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=36 +LIBUNBOUND_REVISION=37 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -3361,6 +3361,7 @@ LIBUNBOUND_AGE=1 # 1.24.1 had 9:34:1 # 1.24.2 had 9:35:1 # 1.25.0 had 9:36:1 +# 1.25.1 had 9:37:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -25551,7 +25552,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h -version=1.25.0 +version=1.25.1 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5 printf %s "checking for build time... " >&6; } @@ -26081,7 +26082,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by unbound $as_me 1.25.0, which was +This file was extended by unbound $as_me 1.25.1, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -26149,7 +26150,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -unbound config.status 1.25.0 +unbound config.status 1.25.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index ce1ca6417..f1c5b0419 100644 --- a/configure.ac +++ b/configure.ac @@ -12,14 +12,14 @@ sinclude(dnscrypt/dnscrypt.m4) # must be numbers. ac_defun because of later processing m4_define([VERSION_MAJOR],[1]) m4_define([VERSION_MINOR],[25]) -m4_define([VERSION_MICRO],[0]) +m4_define([VERSION_MICRO],[1]) AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound]) AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR]) AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR]) AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO]) LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=36 +LIBUNBOUND_REVISION=37 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -123,6 +123,7 @@ LIBUNBOUND_AGE=1 # 1.24.1 had 9:34:1 # 1.24.2 had 9:35:1 # 1.25.0 had 9:36:1 +# 1.25.1 had 9:37:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary From 6a31e470f80a6e5c559ebe7fd4cfc0582d3b6d0a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:13:08 +0200 Subject: [PATCH 02/13] - Fix CVE-2026-33278, Possible remote code execution during DNSSEC validation. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- doc/Changelog | 4 ++ services/cache/dns.c | 8 +++- testdata/val_nsec3_iter_high.rpl | 18 ++++---- testdata/val_nx_nsec3_collision.rpl | 31 +++++-------- testdata/val_nx_nsec3_params.rpl | 22 +++++---- validator/val_nsec3.c | 71 +++++++++++++++++++++++++++++ 6 files changed, 115 insertions(+), 39 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index a1c5e918a..5bbe849bc 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +20 May 2026: Wouter + - Fix CVE-2026-33278, Possible remote code execution during DNSSEC + validation. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in doq_repinfo_retrieve_localaddr(). diff --git a/services/cache/dns.c b/services/cache/dns.c index c593dcd72..f6ce272a5 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -712,10 +712,16 @@ struct dns_msg* dns_msg_deepcopy_region(struct dns_msg* origin, struct regional* region) { size_t i; + struct ub_packed_rrset_key** saved_rrsets; struct dns_msg* res = NULL; + size_t rep_alloc_size = sizeof(struct reply_info) + - sizeof(struct rrset_ref); /* this is the size of res->rep + allocated in gen_dns_msg() */ res = gen_dns_msg(region, &origin->qinfo, origin->rep->rrset_count); if(!res) return NULL; - *res->rep = *origin->rep; + saved_rrsets = res->rep->rrsets; /* save rrsets alloc by gen_dns_msg */ + memcpy(res->rep, origin->rep, rep_alloc_size); + res->rep->rrsets = saved_rrsets; if(origin->rep->reason_bogus_str) { res->rep->reason_bogus_str = regional_strdup(region, origin->rep->reason_bogus_str); diff --git a/testdata/val_nsec3_iter_high.rpl b/testdata/val_nsec3_iter_high.rpl index 2b78f0b7f..703092d89 100644 --- a/testdata/val_nsec3_iter_high.rpl +++ b/testdata/val_nsec3_iter_high.rpl @@ -120,12 +120,12 @@ example.com. IN SOA ns.example.com. hostmaster.example.com. 2007090400 28800 720 example.com. 3600 IN RRSIG SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854} ; closest encloser, H(example.com). -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 8 - 6md8numosa4q9ugkffdo1bmm82t5j49s SOA NS MX DNSKEY RRSIG -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCz/LkFOFcaQzVnyySW9ZoVUnxh7gIUdxyS9vqVDzo8pGhFU+3YogN2ZRk= ;{id = 2854} +b6fuorg741ufili49mg9j4328ig53sqg.example.com. NSEC3 1 1 123 aabb00123456bbccdd b6fuorg741ufili49mg9j4328ig53sqh SOA NS MX DNSKEY RRSIG +b6fuorg741ufili49mg9j4328ig53sqg.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. AJlV5car66lq5f0ASx7W47A/OADkARAXzKt9ZLojXze+FWK9JjAX+eA= -; wildcard denial, H(*.example.com.) = 4f3cnt8cu22tngec382jj4gde4rb47ub -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 0 - 4f3cnt8cu22tngec382jj4gde4rb48ub A MX RRSIG -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFHS+i/OB/V/gYmS1eQTXieXIXGjsAhQQ0Ql7TW/hsUklrb0DfoyhVPG95Q== ;{id = 2854} +; wildcard denial, H(*.example.com.) = k1a2vr9c269jummpru5d68qllbfmtdcb. +k1a2vr9c269jummpru5d68qllbfmtacb.example.com. NSEC3 1 1 123 aabb00123456bbccdd k1a2vr9c269jummpru5d68qllbfmtgcb A MX RRSIG +k1a2vr9c269jummpru5d68qllbfmtacb.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. AARB9z4C1WZUI3WP3QAR7RJXFnN0qEBkEt8ocudxXzms4/7/2l6NNWc= ; next closer name, H(www.example.com.) = s1unhcti19bkdr98fegs0v46mbu3t4m3. s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 A MX RRSIG @@ -152,10 +152,10 @@ SECTION ANSWER SECTION AUTHORITY example.com. IN SOA ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000 example.com. 3600 IN RRSIG SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854} -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 8 - 6md8numosa4q9ugkffdo1bmm82t5j49s SOA NS MX DNSKEY RRSIG -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCz/LkFOFcaQzVnyySW9ZoVUnxh7gIUdxyS9vqVDzo8pGhFU+3YogN2ZRk= ;{id = 2854} -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 0 - 4f3cnt8cu22tngec382jj4gde4rb48ub A MX RRSIG -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFHS+i/OB/V/gYmS1eQTXieXIXGjsAhQQ0Ql7TW/hsUklrb0DfoyhVPG95Q== ;{id = 2854} +b6fuorg741ufili49mg9j4328ig53sqg.example.com. NSEC3 1 1 123 aabb00123456bbccdd b6fuorg741ufili49mg9j4328ig53sqh SOA NS MX DNSKEY RRSIG +b6fuorg741ufili49mg9j4328ig53sqg.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. AJlV5car66lq5f0ASx7W47A/OADkARAXzKt9ZLojXze+FWK9JjAX+eA= +k1a2vr9c269jummpru5d68qllbfmtacb.example.com. NSEC3 1 1 123 aabb00123456bbccdd k1a2vr9c269jummpru5d68qllbfmtgcb A MX RRSIG +k1a2vr9c269jummpru5d68qllbfmtacb.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. AARB9z4C1WZUI3WP3QAR7RJXFnN0qEBkEt8ocudxXzms4/7/2l6NNWc= s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 A MX RRSIG s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFFSH4klZKke48dYyddYDj17gjTS0AhUAltWicpFLWqW98/Af9Qlx70MH8o4= ;{id = 2854} diff --git a/testdata/val_nx_nsec3_collision.rpl b/testdata/val_nx_nsec3_collision.rpl index 87a55f565..1b1f49e80 100644 --- a/testdata/val_nx_nsec3_collision.rpl +++ b/testdata/val_nx_nsec3_collision.rpl @@ -89,6 +89,17 @@ ns.example.com. IN A 1.2.3.4 ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.com. IN AAAA +SECTION AUTHORITY +example.com. IN SOA ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000 +example.com. 3600 IN RRSIG SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854} +ENTRY_END + ; response to DNSKEY priming query ENTRY_BEGIN MATCH opcode qtype qname @@ -163,29 +174,11 @@ STEP 2 TIME_PASSES ELAPSE 0.05 STEP 10 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA DO NXDOMAIN +REPLY QR RD RA DO SERVFAIL SECTION QUESTION www.example.com. IN A SECTION ANSWER SECTION AUTHORITY -example.com. IN SOA ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000 -example.com. 3600 IN RRSIG SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854} -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 123 aabb00123456bbccdd 6md8numosa4q9ugkffdo1bmm82t5j49s A RRSIG -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 8 - 6md8numosa4q9ugkffdo1bmm82t5j49s SOA NS MX DNSKEY RRSIG -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFHndWrEEbuzezs/4lxeiMgEuUsUbAhR72gJgd/Zmhf80yoxCauw9k5OkCw== ;{id = 2854} -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 18 - 4f3cnt8cu22tngec382jj4gde4rb87ub A RRSIG -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 0 - 4f3cnt8cu22tngec382jj4gde4rb48ub A MX RRSIG -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 19 - 4f3cnt8cu22tngec382jj4gde4rb87ub A RRSIG -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFDRwji51WCXJg7W/3+Jx586af5qgAhQPxHegtzu1I/QbvCNrOOON05N1rw== ;{id = 2854} -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 18 - s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 19 - s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 20 00 s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 A MX RRSIG -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 20 01 s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 20 02 s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 20 03 s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFDLy4GbR8ZaKHATVJGnGxzpsuq60AhQ1/pRbXi1ZbcYohzHgWzNC50fC5A== ;{id = 2854} - SECTION ADDITIONAL ENTRY_END diff --git a/testdata/val_nx_nsec3_params.rpl b/testdata/val_nx_nsec3_params.rpl index dd3ab6b57..59bef2be3 100644 --- a/testdata/val_nx_nsec3_params.rpl +++ b/testdata/val_nx_nsec3_params.rpl @@ -88,6 +88,17 @@ ns.example.com. IN A 1.2.3.4 ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.com. IN AAAA +SECTION AUTHORITY +example.com. IN SOA ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000 +example.com. 3600 IN RRSIG SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854} +ENTRY_END + ; response to DNSKEY priming query ENTRY_BEGIN MATCH opcode qtype qname @@ -144,20 +155,11 @@ ENTRY_END STEP 10 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA DO NXDOMAIN +REPLY QR RD RA DO SERVFAIL SECTION QUESTION www.example.com. IN A SECTION ANSWER SECTION AUTHORITY -example.com. IN SOA ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000 -example.com. 3600 IN RRSIG SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854} -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 8 - 6md8numosa4q9ugkffdo1bmm82t5j49s SOA NS MX DNSKEY RRSIG -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCz/LkFOFcaQzVnyySW9ZoVUnxh7gIUdxyS9vqVDzo8pGhFU+3YogN2ZRk= ;{id = 2854} -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 0 - 4f3cnt8cu22tngec382jj4gde4rb48ub A MX RRSIG -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFHS+i/OB/V/gYmS1eQTXieXIXGjsAhQQ0Ql7TW/hsUklrb0DfoyhVPG95Q== ;{id = 2854} -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 A MX RRSIG -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFFSH4klZKke48dYyddYDj17gjTS0AhUAltWicpFLWqW98/Af9Qlx70MH8o4= ;{id = 2854} - SECTION ADDITIONAL ENTRY_END diff --git a/validator/val_nsec3.c b/validator/val_nsec3.c index 998fcc4e3..92d853825 100644 --- a/validator/val_nsec3.c +++ b/validator/val_nsec3.c @@ -456,6 +456,67 @@ filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key** list, } } +/** Check if the NSEC3s have the same parameter set. */ +static int +param_set_same(struct nsec3_filter* flt, char** reason) +{ + size_t rrsetnum; + int rrnum; + struct ub_packed_rrset_key* rrset; + int have_params = 0; + int first_algo = 0; + size_t first_iter = 0; + uint8_t* first_salt = NULL; + size_t first_saltlen = 0; + + /* If the NSEC3 parameter sets have distinct values, then they are + * from different NSEC3 chains, and we do not want that. */ + for(rrset=filter_first(flt, &rrsetnum, &rrnum); rrset; + rrset=filter_next(flt, &rrsetnum, &rrnum)) { + if(!have_params) { + first_algo = nsec3_get_algo(rrset, rrnum); + first_iter = nsec3_get_iter(rrset, rrnum); + if(!nsec3_get_salt(rrset, rrnum, &first_salt, + &first_saltlen)) { + verbose(VERB_ALGO, "NSEC3 salt malformed"); + if(reason) + *reason = "NSEC3 salt malformed"; + return 0; + } + have_params = 1; + } else { + uint8_t* salt = NULL; + size_t saltlen = 0; + if(nsec3_get_algo(rrset, rrnum) != first_algo) { + verbose(VERB_ALGO, "NSEC3 algorithm mismatch"); + if(reason) + *reason = "NSEC3 algorithm mismatch"; + return 0; + } + if(nsec3_get_iter(rrset, rrnum) != first_iter) { + verbose(VERB_ALGO, "NSEC3 iterations mismatch"); + if(reason) + *reason = "NSEC3 iterations mismatch"; + return 0; + } + if(!nsec3_get_salt(rrset, rrnum, &salt, &saltlen)) { + verbose(VERB_ALGO, "NSEC3 salt malformed"); + if(reason) + *reason = "NSEC3 salt malformed"; + return 0; + } + if(saltlen != first_saltlen || + memcmp(salt, first_salt, saltlen) != 0) { + verbose(VERB_ALGO, "NSEC3 salt mismatch"); + if(reason) + *reason = "NSEC3 salt mismatch"; + return 0; + } + } + } + return 1; +} + /** * Find max iteration count using config settings and key size * @param ve: validator environment with iteration count config settings. @@ -1192,6 +1253,8 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ + if(!param_set_same(&flt, NULL)) + return sec_status_bogus; /* nsec3 params from distinct chains*/ if(nsec3_iteration_count_high(ve, &flt, kkey)) return sec_status_insecure; /* iteration count too high */ log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", @@ -1378,6 +1441,8 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve, filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ + if(!param_set_same(&flt, NULL)) + return sec_status_bogus; /* nsec3 params from distinct chains*/ if(nsec3_iteration_count_high(ve, &flt, kkey)) return sec_status_insecure; /* iteration count too high */ return nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); @@ -1401,6 +1466,8 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ + if(!param_set_same(&flt, NULL)) + return sec_status_bogus; /* nsec3 params from distinct chains*/ if(nsec3_iteration_count_high(ve, &flt, kkey)) return sec_status_insecure; /* iteration count too high */ @@ -1503,6 +1570,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, *reason = "no NSEC3 records"; return sec_status_bogus; /* no RRs */ } + if(!param_set_same(&flt, reason)) + return sec_status_bogus; /* nsec3 params from distinct chains*/ if(nsec3_iteration_count_high(ve, &flt, kkey)) return sec_status_insecure; /* iteration count too high */ @@ -1596,6 +1665,8 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ + if(!param_set_same(&flt, NULL)) + return sec_status_bogus; /* nsec3 params from distinct chains*/ if(nsec3_iteration_count_high(ve, &flt, kkey)) return sec_status_insecure; /* iteration count too high */ From fe946ba4e935a1528e6866e108e5bc9e7533ae18 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:13:55 +0200 Subject: [PATCH 03/13] - Fix CVE-2026-42944, Heap overflow and crash with multiple nsid, cookie, padding EDNS options. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- doc/Changelog | 3 +++ testcode/unitmain.c | 4 ++-- util/data/msgencode.c | 36 +++++++++++++++++++++++------------- util/data/msgencode.h | 4 ++-- util/data/msgparse.c | 10 +++++++--- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 5bbe849bc..e003ed3fe 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,9 @@ 20 May 2026: Wouter - Fix CVE-2026-33278, Possible remote code execution during DNSSEC validation. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix CVE-2026-42944, Heap overflow and crash with multiple nsid, + cookie, padding EDNS options. Thanks to Qifan Zhang, Palo Alto + Networks, for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/testcode/unitmain.c b/testcode/unitmain.c index 79ce45f39..4bc756a07 100644 --- a/testcode/unitmain.c +++ b/testcode/unitmain.c @@ -1092,7 +1092,7 @@ static void edns_ede_encode_notxt_fit_test( struct query_info* qinfo, { struct edns_data edns; sldns_buffer* pkt; - uint16_t edns_field_size, ede_txt_size; + size_t edns_field_size, ede_txt_size; int found_ede = 0, found_ede_other = 0, found_ede_txt = 0; int found_other_edns = 0; edns_ede_encode_setup(&edns, region); @@ -1123,7 +1123,7 @@ static void edns_ede_encode_no_fit_test( struct query_info* qinfo, { struct edns_data edns; sldns_buffer* pkt; - uint16_t edns_field_size, ede_size, ede_txt_size; + size_t edns_field_size, ede_size, ede_txt_size; int found_ede = 0, found_ede_other = 0, found_ede_txt = 0; int found_other_edns = 0; edns_ede_encode_setup(&edns, region); diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 8f4639519..10979df9c 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -820,7 +820,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, return 1; } -uint16_t +size_t calc_edns_field_size(struct edns_data* edns) { size_t rdatalen = 0; @@ -856,7 +856,7 @@ calc_edns_option_size(struct edns_data* edns, uint16_t code) } uint16_t -calc_ede_option_size(struct edns_data* edns, uint16_t* txt_size) +calc_ede_option_size(struct edns_data* edns, size_t* txt_size) { size_t rdatalen = 0; struct edns_option* opt; @@ -958,6 +958,10 @@ attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns, padding_option = opt; continue; } + if(sldns_buffer_position(pkt) + opt->opt_len + 4 > max_msg_sz) + break; /* no space for it */ + if(!sldns_buffer_available(pkt, 4 + opt->opt_len)) + break; sldns_buffer_write_u16(pkt, opt->opt_code); sldns_buffer_write_u16(pkt, opt->opt_len); if(opt->opt_len != 0) @@ -968,12 +972,18 @@ attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns, padding_option = opt; continue; } + if(sldns_buffer_position(pkt) + opt->opt_len + 4 > max_msg_sz) + break; /* no space for it */ + if(!sldns_buffer_available(pkt, 4 + opt->opt_len)) + break; sldns_buffer_write_u16(pkt, opt->opt_code); sldns_buffer_write_u16(pkt, opt->opt_len); if(opt->opt_len != 0) sldns_buffer_write(pkt, opt->opt_data, opt->opt_len); } - if (padding_option && edns->padding_block_size ) { + if (padding_option && edns->padding_block_size && + sldns_buffer_position(pkt)+4 <= max_msg_sz && + sldns_buffer_available(pkt, 4) /* if there is space for it */) { size_t pad_pos = sldns_buffer_position(pkt); size_t msg_sz = ((pad_pos + 3) / edns->padding_block_size + 1) * edns->padding_block_size; @@ -1017,7 +1027,7 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, { uint16_t flags; unsigned int attach_edns = 0; - uint16_t edns_field_size, ede_size, ede_txt_size; + size_t edns_field_size, ede_size, ede_txt_size; if(!cached || rep->authoritative) { /* original flags, copy RD and CD bits from query. */ @@ -1044,12 +1054,12 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, * calculate sizes once here */ edns_field_size = calc_edns_field_size(edns); ede_size = calc_ede_option_size(edns, &ede_txt_size); - if(sldns_buffer_capacity(pkt) < udpsize) + if(sldns_buffer_capacity(pkt) < (size_t)udpsize) udpsize = sldns_buffer_capacity(pkt); if(!edns || !edns->edns_present) { attach_edns = 0; /* EDEs are optional, try to fit anything else before them */ - } else if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) { + } else if((size_t)udpsize < (size_t)LDNS_HEADER_SIZE + edns_field_size - ede_size) { /* packet too small to contain edns, omit it. */ attach_edns = 0; } else { @@ -1063,13 +1073,13 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, return 0; } if(attach_edns) { - if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size) + if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size) attach_edns_record_max_msg_sz(pkt, edns, udpsize); - else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_txt_size) { + else if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_txt_size) { ede_trim_text(&edns->opt_list_inplace_cb_out); ede_trim_text(&edns->opt_list_out); attach_edns_record_max_msg_sz(pkt, edns, udpsize); - } else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_size) { + } else if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_size) { edns_opt_list_remove(&edns->opt_list_inplace_cb_out, LDNS_EDNS_EDE); edns_opt_list_remove(&edns->opt_list_out, LDNS_EDNS_EDE); attach_edns_record_max_msg_sz(pkt, edns, udpsize); @@ -1132,7 +1142,7 @@ extended_error_encode(sldns_buffer* buf, uint16_t rcode, } sldns_buffer_flip(buf); if(edns && edns->edns_present) { - uint16_t edns_field_size, ede_size, ede_txt_size; + size_t edns_field_size, ede_size, ede_txt_size; struct edns_data es = *edns; es.edns_version = EDNS_ADVERTISED_VERSION; es.udp_size = EDNS_ADVERTISED_SIZE; @@ -1144,13 +1154,13 @@ extended_error_encode(sldns_buffer* buf, uint16_t rcode, * to see if EDNS can fit. */ edns_field_size = calc_edns_field_size(&es); ede_size = calc_ede_option_size(&es, &ede_txt_size); - if(edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size) + if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size) attach_edns_record_max_msg_sz(buf, &es, edns->udp_size); - else if(edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_txt_size) { + else if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_txt_size) { ede_trim_text(&es.opt_list_inplace_cb_out); ede_trim_text(&es.opt_list_out); attach_edns_record_max_msg_sz(buf, &es, edns->udp_size); - } else if(edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_size) { + } else if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_size) { edns_opt_list_remove(&es.opt_list_inplace_cb_out, LDNS_EDNS_EDE); edns_opt_list_remove(&es.opt_list_out, LDNS_EDNS_EDE); attach_edns_record_max_msg_sz(buf, &es, edns->udp_size); diff --git a/util/data/msgencode.h b/util/data/msgencode.h index 0363a90bf..f561fe1dd 100644 --- a/util/data/msgencode.h +++ b/util/data/msgencode.h @@ -106,7 +106,7 @@ void qinfo_query_encode(struct sldns_buffer* pkt, struct query_info* qinfo); * @param edns: edns data or NULL. * @return octets to reserve for EDNS. */ -uint16_t calc_edns_field_size(struct edns_data* edns); +size_t calc_edns_field_size(struct edns_data* edns); /** * Calculate the size of a specific EDNS option in packet. @@ -127,7 +127,7 @@ uint16_t calc_edns_option_size(struct edns_data* edns, uint16_t code); * extra text. * @return octets the option will take up. */ -uint16_t calc_ede_option_size(struct edns_data* edns, uint16_t* txt_size); +uint16_t calc_ede_option_size(struct edns_data* edns, size_t* txt_size); /** * Attach EDNS record to buffer. Buffer has complete packet. There must diff --git a/util/data/msgparse.c b/util/data/msgparse.c index afbcbca5b..a38bed62e 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -950,6 +950,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, struct comm_reply* repinfo, uint32_t now, struct regional* region, struct cookie_secrets* cookie_secrets) { + int nsid_seen = 0, cookie_seen = 0, padding_seen = 0; /* To respond with a Keepalive option, the client connection must have * received one message with a TCP Keepalive EDNS option, and that * option must have 0 length data. Subsequent messages sent on that @@ -984,8 +985,9 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, /* handle parse time edns options here */ switch(opt_code) { case LDNS_EDNS_NSID: - if (!cfg || !cfg->nsid) + if (!cfg || !cfg->nsid || nsid_seen) break; + nsid_seen = 1; if(!edns_opt_list_append(&edns->opt_list_out, LDNS_EDNS_NSID, cfg->nsid_len, cfg->nsid, region)) { @@ -1027,8 +1029,9 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, case LDNS_EDNS_PADDING: if(!cfg || !cfg->pad_responses || - !c || c->type != comm_tcp ||!c->ssl) + !c || c->type != comm_tcp ||!c->ssl || padding_seen) break; + padding_seen = 1; if(!edns_opt_list_append(&edns->opt_list_out, LDNS_EDNS_PADDING, 0, NULL, region)) { @@ -1039,8 +1042,9 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, break; case LDNS_EDNS_COOKIE: - if(!cfg || !cfg->do_answer_cookie || !repinfo) + if(!cfg || !cfg->do_answer_cookie || !repinfo || cookie_seen) break; + cookie_seen = 1; if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) { verbose(VERB_ALGO, "worker request: " "badly formatted cookie"); From 94d5babaee22a016e376bdcfee2b9bb40360367c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:14:32 +0200 Subject: [PATCH 04/13] - Fix CVE-2026-42959, Crash during DNSSEC validation of malicious content. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- doc/Changelog | 2 ++ validator/val_utils.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index e003ed3fe..d8ef6ee82 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,8 @@ - Fix CVE-2026-42944, Heap overflow and crash with multiple nsid, cookie, padding EDNS options. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix CVE-2026-42959, Crash during DNSSEC validation of malicious + content. Thanks to Qifan Zhang, Palo Alto Networks, for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/validator/val_utils.c b/validator/val_utils.c index 411a63b25..8e4c91900 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -1066,10 +1066,10 @@ val_fill_reply(struct reply_info* chase, struct reply_info* orig, if(query_dname_compare(name, orig->rrsets[i]->rk.dname) == 0) chase->rrsets[chase->an_numrrsets - +orig->ns_numrrsets+chase->ar_numrrsets++] + +chase->ns_numrrsets+chase->ar_numrrsets++] = orig->rrsets[i]; } else if(rrset_has_signer(orig->rrsets[i], name, len)) { - chase->rrsets[chase->an_numrrsets+orig->ns_numrrsets+ + chase->rrsets[chase->an_numrrsets+chase->ns_numrrsets+ chase->ar_numrrsets++] = orig->rrsets[i]; } } From a587535c5dd8a5ea8259507152f055be318367df Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:15:30 +0200 Subject: [PATCH 05/13] - Fix CVE-2026-32792, Packet of death with DNSCrypt. Thanks to Andrew Griffiths from 'calif.io' for the report. --- dnscrypt/dnscrypt.c | 2 +- doc/Changelog | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dnscrypt/dnscrypt.c b/dnscrypt/dnscrypt.c index 4902447fd..173484cdf 100644 --- a/dnscrypt/dnscrypt.c +++ b/dnscrypt/dnscrypt.c @@ -361,7 +361,7 @@ dnscrypt_server_uncurve(struct dnsc_env* env, len -= DNSCRYPT_QUERY_HEADER_SIZE; - while (*sldns_buffer_at(buffer, --len) == 0) + while (len>0 && *sldns_buffer_at(buffer, --len) == 0) ; if (*sldns_buffer_at(buffer, len) != 0x80) { diff --git a/doc/Changelog b/doc/Changelog index d8ef6ee82..614e92ed6 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -6,6 +6,8 @@ Networks, for the report. - Fix CVE-2026-42959, Crash during DNSSEC validation of malicious content. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix CVE-2026-32792, Packet of death with DNSCrypt. Thanks to Andrew + Griffiths from 'calif.io' for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in From 8d8fa4226613138f5a244a9f1a2506704e049180 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:16:18 +0200 Subject: [PATCH 06/13] - Fix CVE-2026-40622, "Ghost domain name" variant. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- doc/Changelog | 2 ++ services/cache/rrset.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 614e92ed6..66895692d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -8,6 +8,8 @@ content. Thanks to Qifan Zhang, Palo Alto Networks, for the report. - Fix CVE-2026-32792, Packet of death with DNSCrypt. Thanks to Andrew Griffiths from 'calif.io' for the report. + - Fix CVE-2026-40622, "Ghost domain name" variant. Thanks to Qifan + Zhang, Palo Alto Networks, for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/services/cache/rrset.c b/services/cache/rrset.c index c1716a565..ab4f4c8e0 100644 --- a/services/cache/rrset.c +++ b/services/cache/rrset.c @@ -149,6 +149,16 @@ need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns) if(equal && !TTL_IS_EXPIRED(cached->ttl, timenow) && cached->security == sec_status_bogus) return 0; + /* ghost-domain: never let an NS overwrite extend lifetime + * past the entry it replaces, regardless of trust. */ + if(ns && !TTL_IS_EXPIRED(cached->ttl, timenow) && + newd->ttl > cached->ttl) { + size_t i; + newd->ttl = cached->ttl; + for(i=0; i<(newd->count+newd->rrsig_count); i++) + if(newd->rr_ttl[i] > newd->ttl) + newd->rr_ttl[i] = newd->ttl; + } return 1; } /* o item in cache has expired */ From ef5ca84360934fa1e857ebc371d4b093aea6355d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:18:23 +0200 Subject: [PATCH 07/13] - Fix CVE-2026-41292, Parsing a long list of incoming EDNS options degrades performance. Thanks to GitHub user 'N0zoM1z0', also Qifan Zhang from Palo Alto Networks, for the report. --- doc/Changelog | 3 +++ util/data/msgparse.c | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 66895692d..c823bcbd2 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -10,6 +10,9 @@ Griffiths from 'calif.io' for the report. - Fix CVE-2026-40622, "Ghost domain name" variant. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix CVE-2026-41292, Parsing a long list of incoming EDNS options + degrades performance. Thanks to GitHub user 'N0zoM1z0', also Qifan + Zhang from Palo Alto Networks, for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/util/data/msgparse.c b/util/data/msgparse.c index a38bed62e..9239f8fe3 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -53,6 +53,8 @@ #include "sldns/parseutil.h" #include "sldns/wire2str.h" +#define MAX_PARSED_EDNS_OPTIONS 100 + /** smart comparison of (compressed, valid) dnames from packet */ static int smart_compare(sldns_buffer* pkt, uint8_t* dnow, @@ -950,7 +952,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, struct comm_reply* repinfo, uint32_t now, struct regional* region, struct cookie_secrets* cookie_secrets) { - int nsid_seen = 0, cookie_seen = 0, padding_seen = 0; + int i = 0, nsid_seen = 0, cookie_seen = 0, padding_seen = 0; /* To respond with a Keepalive option, the client connection must have * received one message with a TCP Keepalive EDNS option, and that * option must have 0 length data. Subsequent messages sent on that @@ -970,7 +972,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, /* while still more options, and have code+len to read */ /* ignores partial content (i.e. rdata len 3) */ - while(rdata_len >= 4) { + while(rdata_len >= 4 && i < MAX_PARSED_EDNS_OPTIONS) { uint16_t opt_code = sldns_read_uint16(rdata_ptr); uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); uint8_t server_cookie[40]; @@ -1150,6 +1152,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, } rdata_ptr += opt_len; rdata_len -= opt_len; + i++; } return LDNS_RCODE_NOERROR; } @@ -1164,6 +1167,7 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg, struct rrset_parse* found_prev = 0; size_t rdata_len; uint8_t* rdata_ptr; + int i = 0; /* since the class encodes the UDP size, we cannot use hash table to * find the EDNS OPT record. Scan the packet. */ while(rrset) { @@ -1223,7 +1227,7 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg, /* while still more options, and have code+len to read */ /* ignores partial content (i.e. rdata len 3) */ - while(rdata_len >= 4) { + while(rdata_len >= 4 && i < MAX_PARSED_EDNS_OPTIONS) { uint16_t opt_code = sldns_read_uint16(rdata_ptr); uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); rdata_ptr += 4; @@ -1238,6 +1242,7 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg, } rdata_ptr += opt_len; rdata_len -= opt_len; + i++; } /* ignore rrsigs */ return LDNS_RCODE_NOERROR; From a794c87578c963606a6fb00a54c46fcf935f519a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:19:08 +0200 Subject: [PATCH 08/13] - Fix CVE-2026-42534, Jostle logic bypass degrades resolution performance. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- doc/Changelog | 3 +++ services/mesh.c | 14 ++++++++++---- services/mesh.h | 6 ++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index c823bcbd2..03fa7ed3a 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -13,6 +13,9 @@ - Fix CVE-2026-41292, Parsing a long list of incoming EDNS options degrades performance. Thanks to GitHub user 'N0zoM1z0', also Qifan Zhang from Palo Alto Networks, for the report. + - Fix CVE-2026-42534, Jostle logic bypass degrades resolution + performance. Thanks to Qifan Zhang, Palo Alto Networks, for the + report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/services/mesh.c b/services/mesh.c index 433aabc9c..286901047 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -297,12 +297,14 @@ int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf) if(mesh->num_reply_states < mesh->max_reply_states) return 1; /* try to kick out a jostle-list item */ - if(m && m->reply_list && m->list_select == mesh_jostle_list) { + if(m && m->list_select == mesh_jostle_list) { /* how old is it? */ struct timeval age; - timeval_subtract(&age, mesh->env->now_tv, - &m->reply_list->start_time); - if(timeval_smaller(&mesh->jostle_max, &age)) { + if(m->has_first_reply_time) + timeval_subtract(&age, mesh->env->now_tv, + &m->first_reply_time); + if(!m->has_first_reply_time || + timeval_smaller(&mesh->jostle_max, &age)) { /* its a goner */ log_nametypeclass(VERB_ALGO, "query jostled out to " "make space for a new one", @@ -1995,6 +1997,10 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, r->qid = qid; r->qflags = qflags; r->start_time = *s->s.env->now_tv; + if(s->reply_list == NULL && !s->has_first_reply_time) { + s->first_reply_time = r->start_time; + s->has_first_reply_time = 1; + } r->next = s->reply_list; r->qname = regional_alloc_init(s->s.region, qinfo->qname, s->s.qinfo.qname_len); diff --git a/services/mesh.h b/services/mesh.h index d2fac9d3c..9ee585156 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -191,6 +191,12 @@ struct mesh_state { struct module_qstate s; /** the list of replies to clients for the results */ struct mesh_reply* reply_list; + /** if it has a first reply time */ + int has_first_reply_time; + /** wall-clock time the first client reply was attached; + * used by mesh_make_new_space() so duplicate retransmits + * cannot reset jostle aging. */ + struct timeval first_reply_time; /** the list of callbacks for the results */ struct mesh_cb* cb_list; /** set of superstates (that want this state's result) From c343fff3a4de922835fec7232b90faed658b5371 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:20:02 +0200 Subject: [PATCH 09/13] - Fix CVE-2026-42923, Degradation of service with unbounded NSEC3 hash calculations. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- doc/Changelog | 3 +++ validator/val_neg.c | 28 +++++++++++++++++++++++++++- validator/val_nsec3.c | 5 ----- validator/val_nsec3.h | 6 ++++++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 03fa7ed3a..4822075f9 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -16,6 +16,9 @@ - Fix CVE-2026-42534, Jostle logic bypass degrades resolution performance. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix CVE-2026-42923, Degradation of service with unbounded NSEC3 + hash calculations. Thanks to Qifan Zhang, Palo Alto Networks, for + the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/validator/val_neg.c b/validator/val_neg.c index 66fd81899..e82f335b9 100644 --- a/validator/val_neg.c +++ b/validator/val_neg.c @@ -62,6 +62,13 @@ #include "sldns/rrdef.h" #include "sldns/sbuffer.h" +/** + * The maximum salt length that the negative cache is willing to use. + * Larger salt increases the computation time, while recommendations are + * for zero salt length for zones. + */ +#define MAX_SALT_LENGTH 64 + int val_neg_data_compare(const void* a, const void* b) { struct val_neg_data* x = (struct val_neg_data*)a; @@ -826,7 +833,11 @@ void neg_insert_data(struct val_neg_cache* neg, (slen != 0 && zone->nsec3_salt && s && memcmp(zone->nsec3_salt, s, slen) != 0))) { - if(slen > 0) { + if(slen > MAX_SALT_LENGTH) { + /* RFC 9276 s3.1: operators SHOULD NOT use a salt; large + * salts inflate per-hash block count. Decline to cache. */ + return; + } else if(slen > 0) { uint8_t* sa = memdup(s, slen); if(sa) { free(zone->nsec3_salt); @@ -1165,6 +1176,15 @@ neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, uint8_t hashce[NSEC3_SHA_LEN]; uint8_t b32[257]; size_t celen, b32len; + int hashmax = MAX_NSEC3_CALCULATIONS; + if(qlabs > hashmax) { + /* strip leading labels so the walk costs at most + * MAX_NSEC3_CALCULATIONS hashes, mirroring val_nsec3.c */ + while(qlabs > hashmax) { + dname_remove_label(&qname, &qname_len); + qlabs--; + } + } *nclen = 0; while(qlabs > 0) { @@ -1265,6 +1285,12 @@ neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, if(!zone->nsec3_hash) return NULL; /* not nsec3 zone */ + if(!topname && qlabs > zone->labs + 1) + return NULL; /* iterator caller; opt-out proof would be discarded + * at the !topname check below anyway. + * The qlabs check allows the exact-match for + * the one-label-below-zone case. */ + if(!(data=neg_find_nsec3_ce(zone, qname, qname_len, qlabs, buf, hashnc, &nclen))) { return NULL; diff --git a/validator/val_nsec3.c b/validator/val_nsec3.c index 92d853825..62effde20 100644 --- a/validator/val_nsec3.c +++ b/validator/val_nsec3.c @@ -59,11 +59,6 @@ #include "sldns/sbuffer.h" #include "util/config_file.h" -/** - * Max number of NSEC3 calculations at once, suspend query for later. - * 8 is low enough and allows for cases where multiple proofs are needed. - */ -#define MAX_NSEC3_CALCULATIONS 8 /** * When all allowed NSEC3 calculations at once resulted in error treat as * bogus. NSEC3 hash errors are not cached and this helps breaks loops with diff --git a/validator/val_nsec3.h b/validator/val_nsec3.h index f668a270f..a13e92991 100644 --- a/validator/val_nsec3.h +++ b/validator/val_nsec3.h @@ -98,6 +98,12 @@ struct sldns_buffer; /** The SHA1 hash algorithm for NSEC3 */ #define NSEC3_HASH_SHA1 0x01 +/** + * Max number of NSEC3 calculations at once, suspend query for later. + * 8 is low enough and allows for cases where multiple proofs are needed. + */ +#define MAX_NSEC3_CALCULATIONS 8 + /** * Cache table for NSEC3 hashes. * It keeps a *pointer* to the region its items are allocated. From 8ae4b4545dccaaabd30b597b0dcb0d9640c8cc39 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:20:45 +0200 Subject: [PATCH 10/13] - Fix CVE-2026-42960, Possible cache poisoning attack while following delegation. Thanks to TaoFei Guo from Peking University, Yang Luo and JianJun Chen, Tsinghua University, for the report. --- doc/Changelog | 3 +++ iterator/iter_scrub.c | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 4822075f9..eb59a1a73 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -19,6 +19,9 @@ - Fix CVE-2026-42923, Degradation of service with unbounded NSEC3 hash calculations. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix CVE-2026-42960, Possible cache poisoning attack while following + delegation. Thanks to TaoFei Guo from Peking University, Yang Luo + and JianJun Chen, Tsinghua University, for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c index 37c4150cd..74a258640 100644 --- a/iterator/iter_scrub.c +++ b/iterator/iter_scrub.c @@ -777,7 +777,13 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, rrset->rrset_all_next = NULL; return 1; } - mark_additional_rrset(pkt, msg, rrset); + /* Only mark glue as allowed for type NS in the authority + * section. Other RR types do not get glue for them, it + * is allowed from the answer section, but not authority + * so that a message can not have address records cached + * as a side effect to the query. */ + if(rrset->type==LDNS_RR_TYPE_NS) + mark_additional_rrset(pkt, msg, rrset); prev = rrset; rrset = rrset->rrset_all_next; } From dae7a3797424607906b132c008fc12dba867b5f3 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:21:26 +0200 Subject: [PATCH 11/13] - Fix CVE-2026-44390, Unbounded name compression in certain cases causes degradation of service. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- util/data/msgencode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 10979df9c..dd56cc6aa 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -352,7 +352,6 @@ compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, (p = compress_tree_lookup(tree, dname, labs, &insertpt))) { if(!write_compressed_dname(pkt, dname, labs, p)) return RETVAL_TRUNC; - (*compress_count)++; } else { if(!dname_buffer_write(pkt, dname)) return RETVAL_TRUNC; @@ -360,6 +359,7 @@ compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && !compress_tree_store(dname, labs, pos, region, p, insertpt)) return RETVAL_OUTMEM; + (*compress_count)++; return RETVAL_OK; } From 138fb48eac364f1567b5ad8a0a038eab6312a8be Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:22:10 +0200 Subject: [PATCH 12/13] Changelog entry. - Fix CVE-2026-44390, Unbounded name compression in certain cases causes degradation of service. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- doc/Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index eb59a1a73..a9fd4dc95 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -22,6 +22,9 @@ - Fix CVE-2026-42960, Possible cache poisoning attack while following delegation. Thanks to TaoFei Guo from Peking University, Yang Luo and JianJun Chen, Tsinghua University, for the report. + - Fix CVE-2026-44390, Unbounded name compression in certain cases + causes degradation of service. Thanks to Qifan Zhang, Palo Alto + Networks, for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in From 75b6dba593d4fff000434cd64807c6ebd50bd244 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 May 2026 10:22:52 +0200 Subject: [PATCH 13/13] - Fix CVE-2026-44608, Use after free and crash in RPZ code. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- doc/Changelog | 2 ++ services/rpz.c | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index a9fd4dc95..02148aaa1 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -25,6 +25,8 @@ - Fix CVE-2026-44390, Unbounded name compression in certain cases causes degradation of service. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix CVE-2026-44608, Use after free and crash in RPZ code. Thanks + to Qifan Zhang, Palo Alto Networks, for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/services/rpz.c b/services/rpz.c index d83acbfb0..5121e46b5 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -2469,6 +2469,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* { struct auth_zones* az; struct auth_zone* a; + struct dns_msg* ret = NULL; struct clientip_synthesized_rr* raddr = NULL; struct rpz* r = NULL; struct local_zone* z = NULL; @@ -2512,13 +2513,11 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, is->qchase.qclass, &match); if(z != NULL) { - lock_rw_unlock(&a->lock); break; } raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is); if(raddr != NULL) { - lock_rw_unlock(&a->lock); break; } lock_rw_unlock(&a->lock); @@ -2533,9 +2532,12 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* if(z) { lock_rw_unlock(&z->lock); } - return rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a); + ret = rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a); + } else { + ret = rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a); } - return rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a); + lock_rw_unlock(&a->lock); + return ret; } struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,