From 20f862de5150da03827036e1701a003061899c15 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 16 Jul 2025 10:00:39 +0200 Subject: [PATCH 1/2] - Set version to 1.23.1. --- configure | 25 +++++++++++++------------ configure.ac | 5 +++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/configure b/configure index 0b78d97b1..ac1a22790 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.23.0. +# Generated by GNU Autoconf 2.71 for unbound 1.23.1. # # Report bugs to . # @@ -622,8 +622,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.23.0' -PACKAGE_STRING='unbound 1.23.0' +PACKAGE_VERSION='1.23.1' +PACKAGE_STRING='unbound 1.23.1' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues' PACKAGE_URL='' @@ -1511,7 +1511,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.23.0 to adapt to many kinds of systems. +\`configure' configures unbound 1.23.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1577,7 +1577,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.23.0:";; + short | recursive ) echo "Configuration of unbound 1.23.1:";; esac cat <<\_ACEOF @@ -1830,7 +1830,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.23.0 +unbound configure 1.23.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2487,7 +2487,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.23.0, which was +It was created by unbound $as_me 1.23.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3251,11 +3251,11 @@ UNBOUND_VERSION_MAJOR=1 UNBOUND_VERSION_MINOR=23 -UNBOUND_VERSION_MICRO=0 +UNBOUND_VERSION_MICRO=1 LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=31 +LIBUNBOUND_REVISION=32 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -3354,6 +3354,7 @@ LIBUNBOUND_AGE=1 # 1.21.1 had 9:29:1 # 1.22.0 had 9:30:1 # 1.23.0 had 9:31:1 +# 1.23.1 had 9:32:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -25073,7 +25074,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h -version=1.23.0 +version=1.23.1 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5 printf %s "checking for build time... " >&6; } @@ -25603,7 +25604,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.23.0, which was +This file was extended by unbound $as_me 1.23.1, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -25671,7 +25672,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.23.0 +unbound config.status 1.23.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 76239c099..051e7b392 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],[23]) -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=31 +LIBUNBOUND_REVISION=32 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -118,6 +118,7 @@ LIBUNBOUND_AGE=1 # 1.21.1 had 9:29:1 # 1.22.0 had 9:30:1 # 1.23.0 had 9:31:1 +# 1.23.1 had 9:32:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary From 5bf82f246481098a6473f296b21fc1229d276c0f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 16 Jul 2025 10:02:01 +0200 Subject: [PATCH 2/2] - Fix RebirthDay Attack CVE-2025-5994, reported by Xiang Li from AOSP Lab Nankai University. --- edns-subnet/subnetmod.c | 152 ++++++++++++++++++++++++++++++++++++---- edns-subnet/subnetmod.h | 4 ++ 2 files changed, 142 insertions(+), 14 deletions(-) diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index ead720f34..c5e215b8b 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -51,6 +51,7 @@ #include "services/cache/dns.h" #include "util/module.h" #include "util/regional.h" +#include "util/fptr_wlist.h" #include "util/storage/slabhash.h" #include "util/config_file.h" #include "util/data/msgreply.h" @@ -155,7 +156,8 @@ int ecs_whitelist_check(struct query_info* qinfo, /* Cache by default, might be disabled after parsing EDNS option * received from nameserver. */ - if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) { + if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0) + && sq->ecs_client_in.subnet_validdata) { qstate->no_cache_store = 0; } @@ -522,6 +524,69 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net) return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); } +/** + * Create sub request that looks up the query. + * @param qstate: query state + * @param sq: subnet qstate + * @return false on failure. + */ +static int +generate_sub_request(struct module_qstate *qstate, struct subnet_qstate* sq) +{ + struct module_qstate* subq = NULL; + uint16_t qflags = 0; /* OPCODE QUERY, no flags */ + int prime = 0; + int valrec = 0; + struct query_info qinf; + qinf.qname = qstate->qinfo.qname; + qinf.qname_len = qstate->qinfo.qname_len; + qinf.qtype = qstate->qinfo.qtype; + qinf.qclass = qstate->qinfo.qclass; + qinf.local_alias = NULL; + + qflags |= BIT_RD; + if((qstate->query_flags & BIT_CD)!=0) { + qflags |= BIT_CD; + valrec = 1; + } + + fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); + if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, + &subq)) { + return 0; + } + if(subq) { + /* It is possible to access the subquery module state. */ + if(sq->ecs_client_in.subnet_source_mask == 0 && + edns_opt_list_find(qstate->edns_opts_front_in, + qstate->env->cfg->client_subnet_opcode)) { + subq->no_cache_store = 1; + } + } + return 1; +} + +/** + * Perform the query without subnet + * @param qstate: query state + * @param sq: subnet qstate + * @return module state + */ +static enum module_ext_state +generate_lookup_without_subnet(struct module_qstate *qstate, + struct subnet_qstate* sq) +{ + verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet"); + if(!generate_sub_request(qstate, sq)) { + verbose(VERB_ALGO, "Could not generate sub query"); + qstate->return_rcode = LDNS_RCODE_FORMERR; + qstate->return_msg = NULL; + return module_finished; + } + sq->wait_subquery = 1; + return module_wait_subquery; +} + static enum module_ext_state eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) { @@ -557,14 +622,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) * is still useful to put it in the edns subnet cache for * when a client explicitly asks for subnet specific answer. */ verbose(VERB_QUERY, "subnetcache: Authority indicates no support"); - if(!sq->started_no_cache_store) { - lock_rw_wrlock(&sne->biglock); - update_cache(qstate, id); - lock_rw_unlock(&sne->biglock); - } - if (sq->subnet_downstream) - cp_edns_bad_response(c_out, c_in); - return module_finished; + return generate_lookup_without_subnet(qstate, sq); } /* Purposefully there was no sent subnet, and there is consequently @@ -589,14 +647,14 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) !common_prefix(s_out->subnet_addr, s_in->subnet_addr, s_out->subnet_source_mask)) { - /* we can not accept, restart query without option */ + /* we can not accept, perform query without option */ verbose(VERB_QUERY, "subnetcache: forged data"); s_out->subnet_validdata = 0; (void)edns_opt_list_remove(&qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode); sq->subnet_sent = 0; sq->subnet_sent_no_subnet = 0; - return module_restart_next; + return generate_lookup_without_subnet(qstate, sq); } lock_rw_wrlock(&sne->biglock); @@ -795,6 +853,9 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, } else if(sq->subnet_sent_no_subnet) { /* The answer can be stored as scope 0, not in global cache. */ qstate->no_cache_store = 1; + } else if(sq->subnet_sent) { + /* Need another query to be able to store in global cache. */ + qstate->no_cache_store = 1; } return 1; @@ -812,6 +873,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, strmodulevent(event)); log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo); + if(sq && sq->wait_subquery_done) { + /* The subquery lookup returned. */ + if(sq->ecs_client_in.subnet_source_mask == 0 && + edns_opt_list_find(qstate->edns_opts_front_in, + qstate->env->cfg->client_subnet_opcode)) { + if(!sq->started_no_cache_store && + qstate->return_msg) { + lock_rw_wrlock(&sne->biglock); + update_cache(qstate, id); + lock_rw_unlock(&sne->biglock); + } + if (sq->subnet_downstream) + cp_edns_bad_response(&sq->ecs_client_out, + &sq->ecs_client_in); + /* It is a scope zero lookup, append edns subnet + * option to the querier. */ + subnet_ecs_opt_list_append(&sq->ecs_client_out, + &qstate->edns_opts_front_out, qstate, + qstate->region); + } + sq->wait_subquery_done = 0; + qstate->ext_state[id] = module_finished; + qstate->no_cache_store = sq->started_no_cache_store; + qstate->no_cache_lookup = sq->started_no_cache_lookup; + return; + } if((event == module_event_new || event == module_event_pass) && sq == NULL) { struct edns_option* ecs_opt; @@ -822,6 +909,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, } sq = (struct subnet_qstate*)qstate->minfo[id]; + if(sq->wait_subquery) + return; /* Wait for that subquery to return */ if((ecs_opt = edns_opt_list_find( qstate->edns_opts_front_in, @@ -851,6 +940,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, /* No clients are interested in result or we could not * parse it, we don't do client subnet */ sq->ecs_server_out.subnet_validdata = 0; + if(edns_opt_list_find(qstate->edns_opts_front_in, + qstate->env->cfg->client_subnet_opcode)) { + /* aggregated this deaggregated state */ + qstate->ext_state[id] = + generate_lookup_without_subnet( + qstate, sq); + return; + } verbose(VERB_ALGO, "subnetcache: pass to next module"); qstate->ext_state[id] = module_wait_module; return; @@ -891,6 +988,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, } lock_rw_unlock(&sne->biglock); } + if(sq->ecs_client_in.subnet_source_mask == 0 && + edns_opt_list_find(qstate->edns_opts_front_in, + qstate->env->cfg->client_subnet_opcode)) { + /* client asked for resolution without edns subnet */ + qstate->ext_state[id] = generate_lookup_without_subnet( + qstate, sq); + return; + } sq->ecs_server_out.subnet_addr_fam = sq->ecs_client_in.subnet_addr_fam; @@ -927,6 +1032,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, qstate->ext_state[id] = module_wait_module; return; } + if(sq && sq->wait_subquery) + return; /* Wait for that subquery to return */ /* Query handed back by next module, we have a 'final' answer */ if(sq && event == module_event_moddone) { qstate->ext_state[id] = eval_response(qstate, id, sq); @@ -975,10 +1082,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate), } void -subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate), - int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) +subnetmod_inform_super(struct module_qstate *qstate, int id, + struct module_qstate *super) { - /* Not used */ + struct subnet_qstate* super_sq = + (struct subnet_qstate*)super->minfo[id]; + log_query_info(VERB_ALGO, "subnetcache inform_super: query", + &super->qinfo); + super_sq->wait_subquery = 0; + super_sq->wait_subquery_done = 1; + if(qstate->return_rcode != LDNS_RCODE_NOERROR || + !qstate->return_msg) { + super->return_msg = NULL; + super->return_rcode = LDNS_RCODE_SERVFAIL; + return; + } + super->return_rcode = LDNS_RCODE_NOERROR; + super->return_msg = dns_copy_msg(qstate->return_msg, super->region); + if(!super->return_msg) { + log_err("subnetcache: copy response, out of memory"); + super->return_rcode = LDNS_RCODE_SERVFAIL; + } } size_t diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h index 1ff8a23ec..3893820fa 100644 --- a/edns-subnet/subnetmod.h +++ b/edns-subnet/subnetmod.h @@ -102,6 +102,10 @@ struct subnet_qstate { int started_no_cache_store; /** has the subnet module been started with no_cache_lookup? */ int started_no_cache_lookup; + /** Wait for subquery that has been started for nonsubnet lookup. */ + int wait_subquery; + /** The subquery waited for is done. */ + int wait_subquery_done; }; void subnet_data_delete(void* d, void* ATTR_UNUSED(arg));