diff --git a/backport-CVE-2024-33655.patch b/backport-CVE-2024-33655.patch new file mode 100644 index 0000000000000000000000000000000000000000..11ce9d56dcb3591db5b66f0e022011efd7756834 --- /dev/null +++ b/backport-CVE-2024-33655.patch @@ -0,0 +1,717 @@ +From c3206f4568f60c486be6d165b1f2b5b254fea3de Mon Sep 17 00:00:00 2001 +From: "W.C.A. Wijngaards" +Date: Wed, 1 May 2024 10:10:58 +0200 +Subject: [PATCH] - Fix for the DNSBomb vulnerability CVE-2024-33655. Thanks to + Xiang Li from the Network and Information Security Lab of Tsinghua + University for reporting it. + +debian:https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/unbound/1.13.1-1ubuntu5.5/unbound_1.13.1-1ubuntu5.5.debian.tar.xz +Conflict:NA +Reference:https://github.com/NLnetLabs/unbound/commit/c3206f4568f60c486be6d165b1f2b5b254fea3de +--- + doc/Changelog | 5 + + doc/example.conf.in | 15 ++ + doc/unbound.conf.5.in | 30 ++++ + services/cache/infra.c | 170 +++++++++++++++++- + services/cache/infra.h | 28 +++ + services/mesh.c | 65 +++++++ + testdata/cachedb_expired_client_timeout.crpl | 1 + + testdata/cachedb_subnet_expired.crpl | 1 + + .../doh_downstream.tdir/doh_downstream.conf | 1 + + .../doh_downstream_notls.conf | 1 + + .../doh_downstream_post.conf | 1 + + .../fwd_three_service.conf | 1 + + testdata/iter_ghost_timewindow.rpl | 1 + + .../ssl_req_order.tdir/ssl_req_order.conf | 1 + + .../tcp_req_order.tdir/tcp_req_order.conf | 1 + + testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf | 3 +- + util/config_file.c | 15 ++ + util/config_file.h | 15 ++ + util/configlexer.lex | 5 + + util/configparser.y | 55 ++++++ + 20 files changed, 412 insertions(+), 3 deletions(-) + +#diff --git a/doc/Changelog b/doc/Changelog +#index dab04633d..24fbb42c7 100644 +#--- a/doc/Changelog +#+++ b/doc/Changelog +#@@ -1,3 +1,8 @@ +#+1 May 2024: Wouter +#+ - Fix for the DNSBomb vulnerability CVE-2024-33655. Thanks to Xiang Li +#+ from the Network and Information Security Lab of Tsinghua University +#+ for reporting it. +#+ +# 29 April 2024: Yorgos +# - Cleanup unnecessary strdup calls for EDE strings. +# +--- a/doc/example.conf.in ++++ b/doc/example.conf.in +@@ -168,6 +168,15 @@ server: + # are behind a slow satellite link, to eg. 1128. + # unknown-server-time-limit: 376 + ++ # msec before recursion replies are dropped. The work item continues. ++ # discard-timeout: 1900 ++ ++ # Max number of replies waiting for recursion per IP address. ++ # wait-limit: 1000 ++ ++ # Apart from the default, the wait limit can be set for a netblock. ++ # wait-limit-netblock: 192.0.2.0/24 50000 ++ + # the amount of memory to use for the RRset cache. + # plain value in bytes or you can append k, m or G. default is "4Mb". + # rrset-cache-size: 4m +--- a/doc/unbound.conf.5.in ++++ b/doc/unbound.conf.5.in +@@ -284,6 +284,26 @@ Increase this if you are behind a slow s + That would then avoid re\-querying every initial query because it times out. + Default is 376 msec. + .TP ++.B discard\-timeout: \fI ++The wait time in msec where recursion requests are dropped. This is ++to stop a large number of replies from accumulating. They receive ++no reply, the work item continues to recurse. It is nice to be a bit ++larger than serve\-expired\-client\-timeout if that is enabled. ++A value of 1900 msec is suggested. The value 0 disables it. ++Default 1900 msec. ++.TP ++.B wait\-limit: \fI ++The number of replies that can wait for recursion, for an IP address. ++This makes a ratelimit per IP address of waiting replies for recursion. ++It stops very large amounts of queries waiting to be returned to one ++destination. The value 0 disables wait limits. Default is 1000. ++.TP ++.B wait\-limit\-netblock: \fI ++The wait limit for the netblock. If not given the wait\-limit value is ++used. The most specific netblock is used to determine the limit. Useful for ++overriding the default for a specific, group or individual, server. ++The value -1 disables wait limits for the netblock. ++.TP + .B so\-rcvbuf: \fI + If not 0, then set the SO_RCVBUF socket option to get more buffer + space on UDP port 53 incoming queries. So that short spikes on busy +--- a/services/cache/infra.c ++++ b/services/cache/infra.c +@@ -229,6 +229,69 @@ setup_domain_limits(struct infra_cache* + return 1; + } + ++/** find or create element in wait limit netblock tree */ ++static struct wait_limit_netblock_info* ++wait_limit_netblock_findcreate(struct infra_cache* infra, char* str) ++{ ++ rbtree_type* tree; ++ struct sockaddr_storage addr; ++ int net; ++ socklen_t addrlen; ++ struct wait_limit_netblock_info* d; ++ ++ if(!netblockstrtoaddr(str, 0, &addr, &addrlen, &net)) { ++ log_err("cannot parse wait limit netblock '%s'", str); ++ return 0; ++ } ++ ++ /* can we find it? */ ++ tree = &infra->wait_limits_netblock; ++ d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr, ++ addrlen, net); ++ if(d) ++ return d; ++ ++ /* create it */ ++ d = (struct wait_limit_netblock_info*)calloc(1, sizeof(*d)); ++ if(!d) ++ return NULL; ++ d->limit = -1; ++ if(!addr_tree_insert(tree, &d->node, &addr, addrlen, net)) { ++ log_err("duplicate element in domainlimit tree"); ++ free(d); ++ return NULL; ++ } ++ return d; ++} ++ ++ ++/** insert wait limit information into lookup tree */ ++static int ++infra_wait_limit_netblock_insert(struct infra_cache* infra, ++ struct config_file* cfg) ++{ ++ struct config_str2list* p; ++ struct wait_limit_netblock_info* d; ++ for(p = cfg->wait_limit_netblock; p; p = p->next) { ++ d = wait_limit_netblock_findcreate(infra, p->str); ++ if(!d) ++ return 0; ++ d->limit = atoi(p->str2); ++ } ++ return 1; ++} ++ ++/** setup wait limits tree (0 on failure) */ ++static int ++setup_wait_limits(struct infra_cache* infra, struct config_file* cfg) ++{ ++ addr_tree_init(&infra->wait_limits_netblock); ++ if(!infra_wait_limit_netblock_insert(infra, cfg)) ++ return 0; ++ addr_tree_init_parents(&infra->wait_limits_netblock); ++ return 1; ++} ++ + struct infra_cache* + infra_create(struct config_file* cfg) + { +@@ -259,6 +322,10 @@ infra_create(struct config_file* cfg) + infra_delete(infra); + return NULL; + } ++ if(!setup_wait_limits(infra, cfg)) { ++ infra_delete(infra); ++ return NULL; ++ } + infra_ip_ratelimit = cfg->ip_ratelimit; + infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs, + INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, +@@ -279,6 +346,12 @@ static void domain_limit_free(rbnode_typ + } + } + ++/** delete wait_limit_netblock_info entries */ ++static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg)) ++{ ++ free(n); ++} ++ + void + infra_delete(struct infra_cache* infra) + { +@@ -288,6 +361,8 @@ infra_delete(struct infra_cache* infra) + slabhash_delete(infra->domain_rates); + traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); + slabhash_delete(infra->client_ip_rates); ++ traverse_postorder(&infra->wait_limits_netblock, ++ wait_limit_netblock_del, NULL); + free(infra); + } + +@@ -873,7 +948,7 @@ static void infra_create_ratedata(struct + + /** create rate data item for ip address */ + static void infra_ip_create_ratedata(struct infra_cache* infra, +- struct comm_reply* repinfo, time_t timenow) ++ struct comm_reply* repinfo, time_t timenow, int mesh_wait) + { + hashvalue_type h = hash_addr(&(repinfo->addr), + repinfo->addrlen, 0); +@@ -892,6 +967,7 @@ static void infra_ip_create_ratedata(str + k->entry.data = d; + d->qps[0] = 1; + d->timestamp[0] = timenow; ++ d->mesh_wait = mesh_wait; + slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); + } + +@@ -1070,6 +1146,74 @@ int infra_ip_ratelimit_inc(struct infra_ + } + + /* create */ +- infra_ip_create_ratedata(infra, repinfo, timenow); ++ infra_ip_create_ratedata(infra, repinfo, timenow, 0); + return 1; + } ++ ++int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, ++ struct config_file* cfg) ++{ ++ struct lruhash_entry* entry; ++ if(cfg->wait_limit == 0) ++ return 1; ++ ++ entry = infra_find_ip_ratedata(infra, rep, 0); ++ if(entry) { ++ rbtree_type* tree; ++ struct wait_limit_netblock_info* w; ++ struct rate_data* d = (struct rate_data*)entry->data; ++ int mesh_wait = d->mesh_wait; ++ lock_rw_unlock(&entry->lock); ++ ++ /* have the wait amount, check how much is allowed */ ++ tree = &infra->wait_limits_netblock; ++ w = (struct wait_limit_netblock_info*)addr_tree_lookup(tree, ++ &rep->addr, rep->addrlen); ++ if(w) { ++ if(w->limit != -1 && mesh_wait > w->limit) ++ return 0; ++ } else { ++ /* if there is no IP netblock specific information, ++ * use the configured value. */ ++ if(mesh_wait > cfg->wait_limit) ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, ++ time_t timenow, struct config_file* cfg) ++{ ++ struct lruhash_entry* entry; ++ if(cfg->wait_limit == 0) ++ return; ++ ++ /* Find it */ ++ entry = infra_find_ip_ratedata(infra, rep, 1); ++ if(entry) { ++ struct rate_data* d = (struct rate_data*)entry->data; ++ d->mesh_wait++; ++ lock_rw_unlock(&entry->lock); ++ return; ++ } ++ ++ /* Create it */ ++ infra_ip_create_ratedata(infra, rep, timenow, 1); ++} ++ ++void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, ++ struct config_file* cfg) ++{ ++ struct lruhash_entry* entry; ++ if(cfg->wait_limit == 0) ++ return; ++ ++ entry = infra_find_ip_ratedata(infra, rep, 1); ++ if(entry) { ++ struct rate_data* d = (struct rate_data*)entry->data; ++ if(d->mesh_wait > 0) ++ d->mesh_wait--; ++ lock_rw_unlock(&entry->lock); ++ } ++} +--- a/services/cache/infra.h ++++ b/services/cache/infra.h +@@ -122,6 +122,8 @@ struct infra_cache { + rbtree_type domain_limits; + /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */ + struct slabhash* client_ip_rates; ++ /** tree of addr_tree_node, with wait_limit_netblock_info information */ ++ rbtree_type wait_limits_netblock; + }; + + /** ratelimit, unless overridden by domain_limits, 0 is off */ +@@ -182,10 +184,22 @@ struct rate_data { + /** what the timestamp is of the qps array members, counter is + * valid for that timestamp. Usually now and now-1. */ + time_t timestamp[RATE_WINDOW]; ++ /** the number of queries waiting in the mesh */ ++ int mesh_wait; + }; + + #define ip_rate_data rate_data + ++/** ++ * Data to store the configuration per netblock for the wait limit ++ */ ++struct wait_limit_netblock_info { ++ /** The addr tree node, this must be first. */ ++ struct addr_tree_node node; ++ /** the limit on the amount */ ++ int limit; ++}; ++ + /** infra host cache default hash lookup size */ + #define INFRA_HOST_STARTSIZE 32 + /** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */ +@@ -466,4 +480,16 @@ void ip_rate_delkeyfunc(void* d, void* a + /* delete data */ + #define ip_rate_deldatafunc rate_deldatafunc + ++/** See if the IP address can have another reply in the wait limit */ ++int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, ++ struct config_file* cfg); ++ ++/** Increment number of waiting replies for IP */ ++void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, ++ time_t timenow, struct config_file* cfg); ++ ++/** Decrement number of waiting replies for IP */ ++void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, ++ struct config_file* cfg); ++ + #endif /* SERVICES_CACHE_INFRA_H */ +--- a/services/mesh.c ++++ b/services/mesh.c +@@ -47,6 +47,7 @@ + #include "services/outbound_list.h" + #include "services/cache/dns.h" + #include "services/cache/rrset.h" ++#include "services/cache/infra.h" + #include "util/log.h" + #include "util/net_help.h" + #include "util/module.h" +@@ -465,6 +466,14 @@ void mesh_new_client(struct mesh_area* m + if(rep->c->tcp_req_info) { + r_buffer = rep->c->tcp_req_info->spool_buffer; + } ++ if(!infra_wait_limit_allowed(mesh->env->infra_cache, rep, ++ mesh->env->cfg)) { ++ verbose(VERB_ALGO, "Too many queries waiting from the IP. " ++ "dropping incoming query."); ++ comm_point_drop_reply(rep); ++ mesh->stats_dropped++; ++ return; ++ } + if(!unique) + s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + /* does this create a new reply state? */ +@@ -559,6 +568,8 @@ void mesh_new_client(struct mesh_area* m + log_err("mesh_new_client: out of memory initializing serve expired"); + goto servfail_mem; + } ++ infra_wait_limit_inc(mesh->env->infra_cache, rep, *mesh->env->now, ++ mesh->env->cfg); + /* update statistics */ + if(was_detached) { + log_assert(mesh->num_detached_states > 0); +@@ -883,6 +894,8 @@ mesh_state_cleanup(struct mesh_state* ms + * takes no time and also it does not do the mesh accounting */ + mstate->reply_list = NULL; + for(; rep; rep=rep->next) { ++ infra_wait_limit_dec(mesh->env->infra_cache, ++ &rep->query_reply, mesh->env->cfg); + comm_point_drop_reply(&rep->query_reply); + log_assert(mesh->num_reply_addrs > 0); + mesh->num_reply_addrs--; +@@ -1300,6 +1313,8 @@ mesh_send_reply(struct mesh_state* m, in + comm_point_send_reply(&r->query_reply); + m->reply_list = rlist; + } ++ infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply, ++ m->s.env->cfg); + /* account */ + log_assert(m->s.env->mesh->num_reply_addrs > 0); + m->s.env->mesh->num_reply_addrs--; +@@ -1353,6 +1368,28 @@ void mesh_query_done(struct mesh_state* + } + } + for(r = mstate->reply_list; r; r = r->next) { ++ struct timeval old; ++ timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); ++ if(mstate->s.env->cfg->discard_timeout != 0 && ++ ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > ++ mstate->s.env->cfg->discard_timeout) { ++ /* Drop the reply, it is too old */ ++ /* briefly set the reply_list to NULL, so that the ++ * tcp req info cleanup routine that calls the mesh ++ * to deregister the meshstate for it is not done ++ * because the list is NULL and also accounting is not ++ * done there, but instead we do that here. */ ++ struct mesh_reply* reply_list = mstate->reply_list; ++ verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); ++ infra_wait_limit_dec(mstate->s.env->infra_cache, ++ &r->query_reply, mstate->s.env->cfg); ++ mstate->reply_list = NULL; ++ comm_point_drop_reply(&r->query_reply); ++ mstate->reply_list = reply_list; ++ mstate->s.env->mesh->stats_dropped++; ++ continue; ++ } ++ + tv = r->start_time; + + /* if a response-ip address block has been stored the +@@ -1384,6 +1421,8 @@ void mesh_query_done(struct mesh_state* + * because the list is NULL and also accounting is not + * done there, but instead we do that here. */ + struct mesh_reply* reply_list = mstate->reply_list; ++ infra_wait_limit_dec(mstate->s.env->infra_cache, ++ &r->query_reply, mstate->s.env->cfg); + mstate->reply_list = NULL; + comm_point_drop_reply(&r->query_reply); + mstate->reply_list = reply_list; +@@ -1858,6 +1897,8 @@ void mesh_state_remove_reply(struct mesh + /* delete it, but allocated in m region */ + log_assert(mesh->num_reply_addrs > 0); + mesh->num_reply_addrs--; ++ infra_wait_limit_dec(mesh->env->infra_cache, ++ &n->query_reply, mesh->env->cfg); + + /* prev = prev; */ + n = n->next; +@@ -1997,6 +2038,28 @@ mesh_serve_expired_callback(void* arg) + log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep); + + for(r = mstate->reply_list; r; r = r->next) { ++ struct timeval old; ++ timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); ++ if(mstate->s.env->cfg->discard_timeout != 0 && ++ ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > ++ mstate->s.env->cfg->discard_timeout) { ++ /* Drop the reply, it is too old */ ++ /* briefly set the reply_list to NULL, so that the ++ * tcp req info cleanup routine that calls the mesh ++ * to deregister the meshstate for it is not done ++ * because the list is NULL and also accounting is not ++ * done there, but instead we do that here. */ ++ struct mesh_reply* reply_list = mstate->reply_list; ++ verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); ++ infra_wait_limit_dec(mstate->s.env->infra_cache, ++ &r->query_reply, mstate->s.env->cfg); ++ mstate->reply_list = NULL; ++ comm_point_drop_reply(&r->query_reply); ++ mstate->reply_list = reply_list; ++ mstate->s.env->mesh->stats_dropped++; ++ continue; ++ } ++ + tv = r->start_time; + + /* If address info is returned, it means the action should be an +@@ -2024,6 +2087,8 @@ mesh_serve_expired_callback(void* arg) + r, r_buffer, prev, prev_buffer); + if(r->query_reply.c->tcp_req_info) + tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); ++ infra_wait_limit_dec(mstate->s.env->infra_cache, ++ &r->query_reply, mstate->s.env->cfg); + prev = r; + prev_buffer = r_buffer; + +--- a/testdata/doh_downstream.tdir/doh_downstream.conf ++++ b/testdata/doh_downstream.tdir/doh_downstream.conf +@@ -11,6 +11,7 @@ server: + chroot: "" + username: "" + do-not-query-localhost: no ++ discard-timeout: 3000 # testns uses sleep=2 + http-query-buffer-size: 1G + http-response-buffer-size: 1G + http-max-streams: 200 +--- a/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf ++++ b/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf +@@ -11,6 +11,7 @@ server: + chroot: "" + username: "" + do-not-query-localhost: no ++ discard-timeout: 3000 # testns uses sleep=2 + http-query-buffer-size: 1G + http-response-buffer-size: 1G + http-max-streams: 200 +--- a/testdata/doh_downstream_post.tdir/doh_downstream_post.conf ++++ b/testdata/doh_downstream_post.tdir/doh_downstream_post.conf +@@ -11,6 +11,7 @@ server: + chroot: "" + username: "" + do-not-query-localhost: no ++ discard-timeout: 3000 # testns uses sleep=2 + http-query-buffer-size: 1G + http-response-buffer-size: 1G + http-max-streams: 200 +--- a/testdata/fwd_three_service.tdir/fwd_three_service.conf ++++ b/testdata/fwd_three_service.tdir/fwd_three_service.conf +@@ -11,6 +11,7 @@ server: + num-queries-per-thread: 1024 + use-syslog: no + do-not-query-localhost: no ++ discard-timeout: 3000 # testns uses sleep=2 + forward-zone: + name: "." + forward-addr: "127.0.0.1@@TOPORT@" +--- a/testdata/ssl_req_order.tdir/ssl_req_order.conf ++++ b/testdata/ssl_req_order.tdir/ssl_req_order.conf +@@ -9,6 +9,7 @@ server: + chroot: "" + username: "" + do-not-query-localhost: no ++ discard-timeout: 3000 # testns uses sleep=2 + ssl-port: @PORT@ + ssl-service-key: "unbound_server.key" + ssl-service-pem: "unbound_server.pem" +--- a/testdata/tcp_req_order.tdir/tcp_req_order.conf ++++ b/testdata/tcp_req_order.tdir/tcp_req_order.conf +@@ -9,6 +9,7 @@ server: + chroot: "" + username: "" + do-not-query-localhost: no ++ discard-timeout: 3000 # testns uses sleep=2 + + local-zone: "example.net" static + local-data: "www1.example.net. IN A 1.2.3.1" +--- a/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf ++++ b/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf +@@ -1,5 +1,5 @@ + server: +- verbosity: 2 ++ verbosity: 4 + # num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ +@@ -9,6 +9,7 @@ server: + chroot: "" + username: "" + do-not-query-localhost: no ++ discard-timeout: 3000 # testns uses sleep=2 + + forward-zone: + name: "." +--- a/util/config_file.c ++++ b/util/config_file.c +@@ -289,6 +289,11 @@ config_create(void) + cfg->minimal_responses = 1; + cfg->rrset_roundrobin = 1; + cfg->unknown_server_time_limit = 376; ++ cfg->discard_timeout = 1900; /* msec */ ++ cfg->wait_limit = 1000; ++ cfg->wait_limit_cookie = 10000; ++ cfg->wait_limit_netblock = NULL; ++ cfg->wait_limit_cookie_netblock = NULL; + cfg->max_udp_size = 4096; + if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key"))) + goto error_exit; +@@ -661,6 +666,9 @@ int config_set_option(struct config_file + else S_YNO("minimal-responses:", minimal_responses) + else S_YNO("rrset-roundrobin:", rrset_roundrobin) + else S_NUMBER_OR_ZERO("unknown-server-time-limit:", unknown_server_time_limit) ++ else S_NUMBER_OR_ZERO("discard-timeout:", discard_timeout) ++ else S_NUMBER_OR_ZERO("wait-limit:", wait_limit) ++ else S_NUMBER_OR_ZERO("wait-limit-cookie:", wait_limit_cookie) + else S_STRLIST("local-data:", local_data) + else S_YNO("unblock-lan-zones:", unblock_lan_zones) + else S_YNO("insecure-lan-zones:", insecure_lan_zones) +@@ -1101,6 +1109,11 @@ config_get_option(struct config_file* cf + else O_YNO(opt, "minimal-responses", minimal_responses) + else O_YNO(opt, "rrset-roundrobin", rrset_roundrobin) + else O_DEC(opt, "unknown-server-time-limit", unknown_server_time_limit) ++ else O_DEC(opt, "discard-timeout", discard_timeout) ++ else O_DEC(opt, "wait-limit", wait_limit) ++ else O_DEC(opt, "wait-limit-cookie", wait_limit_cookie) ++ else O_LS2(opt, "wait-limit-netblock", wait_limit_netblock) ++ else O_LS2(opt, "wait-limit-cookie-netblock", wait_limit_cookie_netblock) + #ifdef CLIENT_SUBNET + else O_LST(opt, "send-client-subnet", client_subnet) + else O_LST(opt, "client-subnet-zone", client_subnet_zone) +@@ -1545,6 +1558,8 @@ config_delete(struct config_file* cfg) + config_deltrplstrlist(cfg->acl_tag_actions); + config_deltrplstrlist(cfg->acl_tag_datas); + config_delstrlist(cfg->control_ifs.first); ++ config_deldblstrlist(cfg->wait_limit_netblock); ++ config_deldblstrlist(cfg->wait_limit_cookie_netblock); + free(cfg->server_key_file); + free(cfg->server_cert_file); + free(cfg->control_key_file); +--- a/util/config_file.h ++++ b/util/config_file.h +@@ -489,6 +489,21 @@ struct config_file { + /* wait time for unknown server in msec */ + int unknown_server_time_limit; + ++ /** Wait time to drop recursion replies */ ++ int discard_timeout; ++ ++ /** Wait limit for number of replies per IP address */ ++ int wait_limit; ++ ++ /** Wait limit for number of replies per IP address with cookie */ ++ int wait_limit_cookie; ++ ++ /** wait limit per netblock */ ++ struct config_str2list* wait_limit_netblock; ++ ++ /** wait limit with cookie per netblock */ ++ struct config_str2list* wait_limit_cookie_netblock; ++ + /* maximum UDP response size */ + size_t max_udp_size; + +--- a/util/configlexer.lex ++++ b/util/configlexer.lex +@@ -440,6 +440,11 @@ domain-insecure{COLON} { YDVAR(1, VAR_D + minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) } + rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) } + unknown-server-time-limit{COLON} { YDVAR(1, VAR_UNKNOWN_SERVER_TIME_LIMIT) } ++discard-timeout{COLON} { YDVAR(1, VAR_DISCARD_TIMEOUT) } ++wait-limit{COLON} { YDVAR(1, VAR_WAIT_LIMIT) } ++wait-limit-cookie{COLON} { YDVAR(1, VAR_WAIT_LIMIT_COOKIE) } ++wait-limit-netblock{COLON} { YDVAR(1, VAR_WAIT_LIMIT_NETBLOCK) } ++wait-limit-cookie-netblock{COLON} { YDVAR(1, VAR_WAIT_LIMIT_COOKIE_NETBLOCK) } + max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) } + dns64-prefix{COLON} { YDVAR(1, VAR_DNS64_PREFIX) } + dns64-synthall{COLON} { YDVAR(1, VAR_DNS64_SYNTHALL) } +--- a/util/configparser.y ++++ b/util/configparser.y +@@ -176,6 +176,8 @@ extern struct config_parser_state* cfg_p + %token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT + %token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY + %token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY ++%token VAR_DISCARD_TIMEOUT VAR_WAIT_LIMIT VAR_WAIT_LIMIT_COOKIE ++%token VAR_WAIT_LIMIT_NETBLOCK VAR_WAIT_LIMIT_COOKIE_NETBLOCK + %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI + %token VAR_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6 + %token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE +@@ -296,6 +298,8 @@ content_server: server_num_threads | ser + server_fast_server_permil | server_fast_server_num | server_tls_win_cert | + server_tcp_connection_limit | server_log_servfail | server_deny_any | + server_unknown_server_time_limit | server_log_tag_queryreply | ++ server_discard_timeout | server_wait_limit | server_wait_limit_cookie | ++ server_wait_limit_netblock | server_wait_limit_cookie_netblock | + server_stream_wait_size | server_tls_ciphers | + server_tls_ciphersuites | server_tls_session_ticket_keys | + server_tls_use_sni | server_edns_client_string | +@@ -2145,6 +2149,57 @@ server_unknown_server_time_limit: VAR_UN + free($2); + } + ; ++server_discard_timeout: VAR_DISCARD_TIMEOUT STRING_ARG ++ { ++ OUTYY(("P(server_discard_timeout:%s)\n", $2)); ++ cfg_parser->cfg->discard_timeout = atoi($2); ++ free($2); ++ } ++ ; ++server_wait_limit: VAR_WAIT_LIMIT STRING_ARG ++ { ++ OUTYY(("P(server_wait_limit:%s)\n", $2)); ++ cfg_parser->cfg->wait_limit = atoi($2); ++ free($2); ++ } ++ ; ++server_wait_limit_cookie: VAR_WAIT_LIMIT_COOKIE STRING_ARG ++ { ++ OUTYY(("P(server_wait_limit_cookie:%s)\n", $2)); ++ cfg_parser->cfg->wait_limit_cookie = atoi($2); ++ free($2); ++ } ++ ; ++server_wait_limit_netblock: VAR_WAIT_LIMIT_NETBLOCK STRING_ARG STRING_ARG ++ { ++ OUTYY(("P(server_wait_limit_netblock:%s %s)\n", $2, $3)); ++ if(atoi($3) == 0 && strcmp($3, "0") != 0) { ++ yyerror("number expected"); ++ free($2); ++ free($3); ++ } else { ++ if(!cfg_str2list_insert(&cfg_parser->cfg-> ++ wait_limit_netblock, $2, $3)) ++ fatal_exit("out of memory adding " ++ "wait-limit-netblock"); ++ } ++ } ++ ; ++server_wait_limit_cookie_netblock: VAR_WAIT_LIMIT_COOKIE_NETBLOCK STRING_ARG STRING_ARG ++ { ++ OUTYY(("P(server_wait_limit_cookie_netblock:%s %s)\n", $2, $3)); ++ if(atoi($3) == 0 && strcmp($3, "0") != 0) { ++ yyerror("number expected"); ++ free($2); ++ free($3); ++ } else { ++ if(!cfg_str2list_insert(&cfg_parser->cfg-> ++ wait_limit_cookie_netblock, $2, $3)) ++ fatal_exit("out of memory adding " ++ "wait-limit-cookie-netblock"); ++ } ++ } ++ ; + server_max_udp_size: VAR_MAX_UDP_SIZE STRING_ARG + { + OUTYY(("P(server_max_udp_size:%s)\n", $2)); diff --git a/unbound.conf b/unbound.conf index 42e35738d6690dc5df2e0771a5f052a60266e877..4dd684a79d0cf360cbdd835de083c5fcfa86f468 100644 --- a/unbound.conf +++ b/unbound.conf @@ -166,6 +166,15 @@ server: # msec to wait before close of port on timeout UDP. 0 disables. # delay-close: 0 + # msec before recursion replies are dropped. The work item continues. + # discard-timeout: 1900 + + # Max number of replies waiting for recursion per IP address. + # wait-limit: 1000 + + # Apart from the default, the wait limit can be set for a netblock. + # wait-limit-netblock: 192.0.2.0/24 50000 + # the amount of memory to use for the RRset cache. # plain value in bytes or you can append k, m or G. default is "4Mb". # rrset-cache-size: 4m diff --git a/unbound.spec b/unbound.spec index 852144ea477ed4e767715497e146778498c7b0e5..15d29169327ca0b4f894ee91e0d52f3f00a4c6db 100644 --- a/unbound.spec +++ b/unbound.spec @@ -2,7 +2,7 @@ Name: unbound Version: 1.13.2 -Release: 14 +Release: 15 Summary: Unbound is a validating, recursive, caching DNS resolver License: BSD Url: https://nlnetlabs.nl/projects/unbound/about/ @@ -33,10 +33,11 @@ Patch8: backport-001-CVE-2024-43168.patch Patch9: backport-002-CVE-2024-43168.patch Patch10: backport-003-CVE-2024-43168.patch Patch11: backport-004-CVE-2024-43168.patch +Patch12: backport-CVE-2024-33655.patch BuildRequires: make flex swig pkgconfig systemd BuildRequires: libevent-devel expat-devel openssl-devel python3-devel -BuildRequires: gcc +BuildRequires: gcc byacc %{?systemd_requires} Requires: %{name}-libs = %{version}-%{release} @@ -257,6 +258,12 @@ popd %{_mandir}/man* %changelog +* Thu Sep 26 2024 gaihuiying - 1.13.2-15 +- Type:CVE +- CVE:CVE-2024-33655 +- SUG:NA +- DESC:fix CVE-2024-33655 + * Mon Aug 26 2024 gaihuiying - 1.13.2-14 - Type:cves - CVE:CVE-2024-43168