fix CVE-2024-33655
This commit is contained in:
parent
d17eae3590
commit
fe97aff606
798
backport-CVE-2024-33655.patch
Normal file
798
backport-CVE-2024-33655.patch
Normal file
@ -0,0 +1,798 @@
|
||||
From c3206f4568f60c486be6d165b1f2b5b254fea3de Mon Sep 17 00:00:00 2001
|
||||
From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl>
|
||||
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.
|
||||
|
||||
---
|
||||
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.
|
||||
+
|
||||
5 January 2023: Wouter
|
||||
- Tag for 1.17.1 release.
|
||||
|
||||
diff --git a/doc/example.conf.in b/doc/example.conf.in
|
||||
index ea211e97d..e0aec8ec7 100644
|
||||
--- a/doc/example.conf.in
|
||||
+++ b/doc/example.conf.in
|
||||
@@ -191,6 +191,21 @@ 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
|
||||
+
|
||||
+ # Max replies waiting for recursion for IP address with cookie.
|
||||
+ # wait-limit-cookie: 10000
|
||||
+
|
||||
+ # Apart from the default, the wait limit can be set for a netblock.
|
||||
+ # wait-limit-netblock: 192.0.2.0/24 50000
|
||||
+
|
||||
+ # Apart from the default, the wait limit with cookie can be adjusted.
|
||||
+ # wait-limit-cookie-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/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
|
||||
index 167f4a5de..6d310c116 100644
|
||||
--- a/doc/unbound.conf.5.in
|
||||
+++ b/doc/unbound.conf.5.in
|
||||
@@ -302,6 +302,36 @@ Increase this if you are behind a slow satellite link, to eg. 1128.
|
||||
That would then avoid re\-querying every initial query because it times out.
|
||||
Default is 376 msec.
|
||||
.TP
|
||||
+.B discard\-timeout: \fI<msec>
|
||||
+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<number>
|
||||
+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\-cookie: \fI<number>
|
||||
+The number of replies that can wait for recursion, for an IP address
|
||||
+that sent the query with a valid DNS cookie. Since the cookie validates
|
||||
+the client address, the limit can be higher. Default is 10000.
|
||||
+.TP
|
||||
+.B wait\-limit\-netblock: \fI<netblock> <number>
|
||||
+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 wait\-limit\-cookie\-netblock: \fI<netblock> <number>
|
||||
+The wait limit for the netblock, when the query has a DNS cookie.
|
||||
+If not given, the wait\-limit\-cookie value is used.
|
||||
+The value -1 disables wait limits for the netblock.
|
||||
+.TP
|
||||
.B so\-rcvbuf: \fI<number>
|
||||
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
|
||||
diff --git a/services/cache/infra.c b/services/cache/infra.c
|
||||
index 31462d13a..457685ab5 100644
|
||||
--- a/services/cache/infra.c
|
||||
+++ b/services/cache/infra.c
|
||||
@@ -234,6 +234,81 @@ setup_domain_limits(struct infra_cache* infra, struct config_file* cfg)
|
||||
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,
|
||||
+ int cookie)
|
||||
+{
|
||||
+ 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? */
|
||||
+ if(cookie)
|
||||
+ tree = &infra->wait_limits_cookie_netblock;
|
||||
+ else
|
||||
+ 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, 0);
|
||||
+ if(!d)
|
||||
+ return 0;
|
||||
+ d->limit = atoi(p->str2);
|
||||
+ }
|
||||
+ for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) {
|
||||
+ d = wait_limit_netblock_findcreate(infra, p->str, 1);
|
||||
+ 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);
|
||||
+ addr_tree_init(&infra->wait_limits_cookie_netblock);
|
||||
+ if(!infra_wait_limit_netblock_insert(infra, cfg))
|
||||
+ return 0;
|
||||
+ addr_tree_init_parents(&infra->wait_limits_netblock);
|
||||
+ addr_tree_init_parents(&infra->wait_limits_cookie_netblock);
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
struct infra_cache*
|
||||
infra_create(struct config_file* cfg)
|
||||
{
|
||||
@@ -267,6 +342,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,
|
||||
@@ -287,6 +366,12 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg))
|
||||
}
|
||||
}
|
||||
|
||||
+/** 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)
|
||||
{
|
||||
@@ -296,6 +381,10 @@ 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);
|
||||
+ traverse_postorder(&infra->wait_limits_cookie_netblock,
|
||||
+ wait_limit_netblock_del, NULL);
|
||||
free(infra);
|
||||
}
|
||||
|
||||
@@ -880,7 +969,8 @@ static void infra_create_ratedata(struct infra_cache* infra,
|
||||
|
||||
/** create rate data item for ip address */
|
||||
static void infra_ip_create_ratedata(struct infra_cache* infra,
|
||||
- struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow)
|
||||
+ struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow,
|
||||
+ int mesh_wait)
|
||||
{
|
||||
hashvalue_type h = hash_addr(addr, addrlen, 0);
|
||||
struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k));
|
||||
@@ -898,6 +988,7 @@ static void infra_ip_create_ratedata(struct infra_cache* infra,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1121,6 +1212,81 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra,
|
||||
}
|
||||
|
||||
/* create */
|
||||
- infra_ip_create_ratedata(infra, addr, addrlen, timenow);
|
||||
+ infra_ip_create_ratedata(infra, addr, addrlen, timenow, 0);
|
||||
return 1;
|
||||
}
|
||||
+
|
||||
+int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep,
|
||||
+ int cookie_valid, struct config_file* cfg)
|
||||
+{
|
||||
+ struct lruhash_entry* entry;
|
||||
+ if(cfg->wait_limit == 0)
|
||||
+ return 1;
|
||||
+
|
||||
+ entry = infra_find_ip_ratedata(infra, &rep->client_addr,
|
||||
+ rep->client_addrlen, 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 */
|
||||
+ if(cookie_valid)
|
||||
+ tree = &infra->wait_limits_cookie_netblock;
|
||||
+ else tree = &infra->wait_limits_netblock;
|
||||
+ w = (struct wait_limit_netblock_info*)addr_tree_lookup(tree,
|
||||
+ &rep->client_addr, rep->client_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 > (cookie_valid?cfg->wait_limit_cookie:
|
||||
+ 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->client_addr,
|
||||
+ rep->client_addrlen, 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->client_addr,
|
||||
+ rep->client_addrlen, 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->client_addr,
|
||||
+ rep->client_addrlen, 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);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/services/cache/infra.h b/services/cache/infra.h
|
||||
index 525073bf3..ee6f384de 100644
|
||||
--- a/services/cache/infra.h
|
||||
+++ b/services/cache/infra.h
|
||||
@@ -122,6 +122,10 @@ 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;
|
||||
+ /** tree of addr_tree_node, with wait_limit_netblock_info information */
|
||||
+ rbtree_type wait_limits_cookie_netblock;
|
||||
};
|
||||
|
||||
/** ratelimit, unless overridden by domain_limits, 0 is off */
|
||||
@@ -184,10 +188,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.) */
|
||||
@@ -474,4 +490,16 @@ void ip_rate_delkeyfunc(void* d, void* arg);
|
||||
/* 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,
|
||||
+ int cookie_valid, 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 */
|
||||
diff --git a/services/mesh.c b/services/mesh.c
|
||||
index 55afd065f..e886c4b92 100644
|
||||
--- 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"
|
||||
@@ -415,6 +416,14 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
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,
|
||||
+ edns->cookie_valid, 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? */
|
||||
@@ -522,6 +531,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
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);
|
||||
@@ -953,6 +964,8 @@ mesh_state_cleanup(struct mesh_state* mstate)
|
||||
* 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--;
|
||||
@@ -1436,6 +1449,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
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--;
|
||||
@@ -1491,6 +1506,28 @@ void mesh_query_done(struct mesh_state* mstate)
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -1514,6 +1551,8 @@ void mesh_query_done(struct mesh_state* mstate)
|
||||
* 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;
|
||||
@@ -2046,6 +2085,8 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
|
||||
/* 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;
|
||||
@@ -2186,6 +2227,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
|
||||
@@ -2213,6 +2276,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;
|
||||
|
||||
diff --git a/testdata/doh_downstream.tdir/doh_downstream.conf b/testdata/doh_downstream.tdir/doh_downstream.conf
|
||||
index f0857bb58..222c2159d 100644
|
||||
--- 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
|
||||
diff --git a/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf b/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf
|
||||
index bdca45645..161c35559 100644
|
||||
--- 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
|
||||
diff --git a/testdata/doh_downstream_post.tdir/doh_downstream_post.conf b/testdata/doh_downstream_post.tdir/doh_downstream_post.conf
|
||||
index f0857bb58..222c2159d 100644
|
||||
--- 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
|
||||
diff --git a/testdata/fwd_three_service.tdir/fwd_three_service.conf b/testdata/fwd_three_service.tdir/fwd_three_service.conf
|
||||
index 05fafe015..d6c9a205f 100644
|
||||
--- 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@"
|
||||
diff --git a/testdata/iter_ghost_timewindow.rpl b/testdata/iter_ghost_timewindow.rpl
|
||||
index 566be82a9..9e304628c 100644
|
||||
--- a/testdata/iter_ghost_timewindow.rpl
|
||||
+++ b/testdata/iter_ghost_timewindow.rpl
|
||||
@@ -3,6 +3,7 @@ server:
|
||||
target-fetch-policy: "0 0 0 0 0"
|
||||
qname-minimisation: "no"
|
||||
minimal-responses: no
|
||||
+ discard-timeout: 86400
|
||||
|
||||
stub-zone:
|
||||
name: "."
|
||||
diff --git a/testdata/ssl_req_order.tdir/ssl_req_order.conf b/testdata/ssl_req_order.tdir/ssl_req_order.conf
|
||||
index 3b2e2b1b4..ec39d3ab2 100644
|
||||
--- 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"
|
||||
diff --git a/testdata/tcp_req_order.tdir/tcp_req_order.conf b/testdata/tcp_req_order.tdir/tcp_req_order.conf
|
||||
index 40d6f55c8..b2804e8e2 100644
|
||||
--- 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"
|
||||
diff --git a/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf b/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf
|
||||
index 384f16b07..4f1ff9b08 100644
|
||||
--- 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: "."
|
||||
diff --git a/util/config_file.c b/util/config_file.c
|
||||
index e4fe3ee9f..2b67d4c19 100644
|
||||
--- a/util/config_file.c
|
||||
+++ b/util/config_file.c
|
||||
@@ -309,6 +309,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;
|
||||
@@ -726,6 +731,9 @@ int config_set_option(struct config_file* cfg, const char* opt,
|
||||
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)
|
||||
@@ -1207,6 +1215,11 @@ config_get_option(struct config_file* cfg, const char* opt,
|
||||
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)
|
||||
@@ -1678,6 +1691,8 @@ config_delete(struct config_file* cfg)
|
||||
config_deltrplstrlist(cfg->interface_tag_actions);
|
||||
config_deltrplstrlist(cfg->interface_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);
|
||||
diff --git a/util/config_file.h b/util/config_file.h
|
||||
index 42836796e..d3a2e268c 100644
|
||||
--- a/util/config_file.h
|
||||
+++ b/util/config_file.h
|
||||
@@ -537,6 +537,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;
|
||||
|
||||
diff --git a/util/configlexer.lex b/util/configlexer.lex
|
||||
index 51bf06601..7ae1b8c38 100644
|
||||
--- a/util/configlexer.lex
|
||||
+++ b/util/configlexer.lex
|
||||
@@ -464,6 +464,11 @@ domain-insecure{COLON} { YDVAR(1, VAR_DOMAIN_INSECURE) }
|
||||
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) }
|
||||
diff --git a/util/configparser.y b/util/configparser.y
|
||||
index 2adbf92cc..0feeb61b1 100644
|
||||
--- a/util/configparser.y
|
||||
+++ b/util/configparser.y
|
||||
@@ -189,6 +189,8 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET
|
||||
%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
|
||||
@@ -327,6 +329,8 @@ content_server: server_num_threads | server_verbosity | server_port |
|
||||
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_answer_cookie | server_cookie_secret |
|
||||
@@ -2377,6 +2381,57 @@ server_unknown_server_time_limit: VAR_UNKNOWN_SERVER_TIME_LIMIT STRING_ARG
|
||||
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));
|
||||
@ -0,0 +1,886 @@
|
||||
From 75f3fbdd6563dd87c93964e48a3fb7e6c520d74e Mon Sep 17 00:00:00 2001
|
||||
From: Willem Toorop <willem@nlnetlabs.nl>
|
||||
Date: Wed, 28 Sep 2022 10:28:19 +0200
|
||||
Subject: [PATCH] Downstream DNS Cookies a la RFC7873 and RFC9018
|
||||
|
||||
Create server cookies for clients that send client cookies.
|
||||
Needs to be turned on in the config file with:
|
||||
|
||||
answer-cookie: yes
|
||||
|
||||
A cookie-secret can be configured for anycast setups.
|
||||
Also adds an access control list that will allow queries with
|
||||
either a valid cookie or over a stateful transport.
|
||||
---
|
||||
Makefile.in | 8 +-
|
||||
daemon/acl_list.c | 2 +
|
||||
daemon/acl_list.h | 4 +-
|
||||
daemon/worker.c | 44 ++++++++++-
|
||||
doc/unbound.conf.5.in | 23 +++++-
|
||||
libunbound/libworker.c | 2 +
|
||||
services/authzone.c | 4 +
|
||||
sldns/rrdef.h | 4 +
|
||||
testcode/fake_event.c | 2 +
|
||||
util/config_file.c | 23 ++++++
|
||||
util/config_file.h | 7 ++
|
||||
util/configlexer.lex | 2 +
|
||||
util/configparser.y | 35 ++++++++-
|
||||
util/data/msgparse.c | 151 ++++++++++++++++++++++++++++++++++++-
|
||||
util/data/msgparse.h | 14 +++-
|
||||
util/siphash.c | 165 +++++++++++++++++++++++++++++++++++++++++
|
||||
validator/autotrust.c | 2 +
|
||||
17 files changed, 473 insertions(+), 19 deletions(-)
|
||||
create mode 100644 util/siphash.c
|
||||
|
||||
diff --git a/Makefile.in b/Makefile.in
|
||||
index 3189731ad..718f47f5c 100644
|
||||
--- a/Makefile.in
|
||||
+++ b/Makefile.in
|
||||
@@ -128,7 +128,7 @@ util/config_file.c util/configlexer.c util/configparser.c \
|
||||
util/shm_side/shm_main.c services/authzone.c \
|
||||
util/fptr_wlist.c util/locks.c util/log.c util/mini_event.c util/module.c \
|
||||
util/netevent.c util/net_help.c util/random.c util/rbtree.c util/regional.c \
|
||||
-util/rtt.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \
|
||||
+util/rtt.c util/siphash.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \
|
||||
util/storage/lruhash.c util/storage/slabhash.c util/tcp_conn_limit.c \
|
||||
util/timehist.c util/tube.c util/proxy_protocol.c \
|
||||
util/ub_event.c util/ub_event_pluggable.c util/winsock_event.c \
|
||||
@@ -145,7 +145,7 @@ as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
|
||||
iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \
|
||||
iter_scrub.lo iter_utils.lo localzone.lo mesh.lo modstack.lo view.lo \
|
||||
outbound_list.lo alloc.lo config_file.lo configlexer.lo configparser.lo \
|
||||
-fptr_wlist.lo edns.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \
|
||||
+fptr_wlist.lo siphash.lo edns.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \
|
||||
random.lo rbtree.lo regional.lo rtt.lo dnstree.lo lookup3.lo lruhash.lo \
|
||||
slabhash.lo tcp_conn_limit.lo timehist.lo tube.lo winsock_event.lo \
|
||||
autotrust.lo val_anchor.lo rpz.lo proxy_protocol.lo \
|
||||
@@ -915,7 +915,8 @@ config_file.lo config_file.o: $(srcdir)/util/config_file.c config.h $(srcdir)/ut
|
||||
configlexer.lo configlexer.o: util/configlexer.c config.h $(srcdir)/util/configyyrename.h \
|
||||
$(srcdir)/util/config_file.h util/configparser.h
|
||||
configparser.lo configparser.o: util/configparser.c config.h $(srcdir)/util/configyyrename.h \
|
||||
- $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h
|
||||
+ $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/sldns/str2wire.h \
|
||||
+ $(srcdir)/sldns/rrdef.h
|
||||
shm_main.lo shm_main.o: $(srcdir)/util/shm_side/shm_main.c config.h $(srcdir)/util/shm_side/shm_main.h \
|
||||
$(srcdir)/libunbound/unbound.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
|
||||
$(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
|
||||
@@ -1004,6 +1005,7 @@ rtt.lo rtt.o: $(srcdir)/util/rtt.c config.h $(srcdir)/util/rtt.h $(srcdir)/itera
|
||||
$(srcdir)/services/outbound_list.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/storage/lruhash.h \
|
||||
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/module.h \
|
||||
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h
|
||||
+siphash.lo siphash.o: $(srcdir)/util/siphash.c
|
||||
edns.lo edns.o: $(srcdir)/util/edns.c config.h $(srcdir)/util/edns.h $(srcdir)/util/storage/dnstree.h \
|
||||
$(srcdir)/util/rbtree.h $(srcdir)/util/config_file.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
|
||||
$(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/util/regional.h \
|
||||
diff --git a/daemon/acl_list.c b/daemon/acl_list.c
|
||||
index 8e8e1fc9b..e6d0470a1 100644
|
||||
--- a/daemon/acl_list.c
|
||||
+++ b/daemon/acl_list.c
|
||||
@@ -109,6 +109,8 @@ parse_acl_access(const char* str, enum acl_access* control)
|
||||
*control = acl_allow_snoop;
|
||||
else if(strcmp(str, "allow_setrd") == 0)
|
||||
*control = acl_allow_setrd;
|
||||
+ else if (strcmp(str, "allow_cookie") == 0)
|
||||
+ *control = acl_allow_cookie;
|
||||
else {
|
||||
log_err("access control type %s unknown", str);
|
||||
return 0;
|
||||
diff --git a/daemon/acl_list.h b/daemon/acl_list.h
|
||||
index c717179ba..8aece11bf 100644
|
||||
--- a/daemon/acl_list.h
|
||||
+++ b/daemon/acl_list.h
|
||||
@@ -65,7 +65,9 @@ enum acl_access {
|
||||
/** allow full access for all queries, recursion and cache snooping */
|
||||
acl_allow_snoop,
|
||||
/** allow full access for recursion queries and set RD flag regardless of request */
|
||||
- acl_allow_setrd
|
||||
+ acl_allow_setrd,
|
||||
+ /** allow full access if valid cookie present or stateful transport */
|
||||
+ acl_allow_cookie
|
||||
};
|
||||
|
||||
/**
|
||||
diff --git a/daemon/worker.c b/daemon/worker.c
|
||||
index c2a94be79..1abf20a7b 100644
|
||||
--- a/daemon/worker.c
|
||||
+++ b/daemon/worker.c
|
||||
@@ -1432,8 +1432,10 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
}
|
||||
goto send_reply;
|
||||
}
|
||||
- if((ret=parse_edns_from_query_pkt(c->buffer, &edns, worker->env.cfg, c,
|
||||
- worker->scratchpad)) != 0) {
|
||||
+ if((ret=parse_edns_from_query_pkt(
|
||||
+ c->buffer, &edns, worker->env.cfg, c, repinfo,
|
||||
+ (worker->env.now ? *worker->env.now : time(NULL)),
|
||||
+ worker->scratchpad)) != 0) {
|
||||
struct edns_data reply_edns;
|
||||
verbose(VERB_ALGO, "worker parse edns: formerror.");
|
||||
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
|
||||
@@ -1466,6 +1468,44 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
edns.udp_size = NORMAL_UDP_SIZE;
|
||||
}
|
||||
}
|
||||
+ /* "if, else if" sequence below deals with downstream DNS Cookies */
|
||||
+ if (acl != acl_allow_cookie)
|
||||
+ ; /* pass; No cookie downstream processing whatsoever */
|
||||
+
|
||||
+ else if (edns.cookie_valid)
|
||||
+ ; /* pass; Valid cookie is good! */
|
||||
+
|
||||
+ else if (c->type != comm_udp)
|
||||
+ ; /* pass; Stateful transport */
|
||||
+
|
||||
+ else if (edns.cookie_present) {
|
||||
+ /* Cookie present, but not valid: Cookie was bad! */
|
||||
+ extended_error_encode(c->buffer,
|
||||
+ LDNS_EXT_RCODE_BADCOOKIE, &qinfo,
|
||||
+ *(uint16_t*)(void *)
|
||||
+ sldns_buffer_begin(c->buffer),
|
||||
+ sldns_buffer_read_u16_at(c->buffer, 2),
|
||||
+ 0, &edns);
|
||||
+ regional_free_all(worker->scratchpad);
|
||||
+ goto send_reply;
|
||||
+ } else {
|
||||
+ /* Cookie requered, but no cookie present on UDP */
|
||||
+ verbose(VERB_ALGO, "worker request: "
|
||||
+ "need cookie or stateful transport");
|
||||
+ log_addr(VERB_ALGO, "from",
|
||||
+ &repinfo->remote_addr, repinfo->remote_addrlen);
|
||||
+ EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
|
||||
+ worker->scratchpad, LDNS_EDE_OTHER,
|
||||
+ "DNS Cookie needed for UDP replies");
|
||||
+ error_encode(c->buffer,
|
||||
+ (LDNS_RCODE_REFUSED|BIT_TC), &qinfo,
|
||||
+ *(uint16_t*)(void *)
|
||||
+ sldns_buffer_begin(c->buffer),
|
||||
+ sldns_buffer_read_u16_at(c->buffer, 2),
|
||||
+ &edns);
|
||||
+ regional_free_all(worker->scratchpad);
|
||||
+ goto send_reply;
|
||||
+ }
|
||||
if(edns.udp_size > worker->daemon->cfg->max_udp_size &&
|
||||
c->type == comm_udp) {
|
||||
verbose(VERB_QUERY,
|
||||
diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
|
||||
index 73575d93a..e187452de 100644
|
||||
--- a/doc/unbound.conf.5.in
|
||||
+++ b/doc/unbound.conf.5.in
|
||||
@@ -673,9 +673,9 @@ This option is experimental at this time.
|
||||
.B access\-control: \fI<IP netblock> <action>
|
||||
The netblock is given as an IP4 or IP6 address with /size appended for a
|
||||
classless network block. The action can be \fIdeny\fR, \fIrefuse\fR,
|
||||
-\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or
|
||||
-\fIrefuse_non_local\fR.
|
||||
-The most specific netblock match is used, if none match \fIrefuse\fR is used.
|
||||
+\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIallow_cookie\fR,
|
||||
+\fIdeny_non_local\fR or \fIrefuse_non_local\fR.
|
||||
+The most specific netblock match is used, if none match \fIdeny\fR is used.
|
||||
The order of the access\-control statements therefore does not matter.
|
||||
.IP
|
||||
The action \fIdeny\fR stops queries from hosts from that netblock.
|
||||
@@ -710,6 +710,14 @@ the cache contents (for malicious acts). However, nonrecursive queries can
|
||||
also be a valuable debugging tool (when you want to examine the cache
|
||||
contents). In that case use \fIallow_snoop\fR for your administration host.
|
||||
.IP
|
||||
+When the \fBanswer\-cookie\fR option is enabled, the \fIallow_cookie\fR action
|
||||
+will allow access to UDP queries that contain a valid Server Cookie as
|
||||
+specified in RFC 7873 and RFC9018. UDP queries containing only a Client Cookie
|
||||
+and no Server Cookie, will receive a BADCOOKIE response including a Server
|
||||
+Cookie, allow clients to retry with that Server Cookie. The \fIallow_cookie\fR
|
||||
+will also accept requests over statefull transports, regardless of the precence
|
||||
+of a Cookie and regardless the \fBanswer\-cookie\fR setting.
|
||||
+.IP
|
||||
By default only localhost is \fIallow\fRed, the rest is \fIrefuse\fRd.
|
||||
The default is \fIrefuse\fRd, because that is protocol\-friendly. The DNS
|
||||
protocol is not designed to handle dropped packets due to policy, and
|
||||
@@ -1824,6 +1832,15 @@ Set the number of servers that should be used for fast server selection. Only
|
||||
use the fastest specified number of servers with the fast\-server\-permil
|
||||
option, that turns this on or off. The default is to use the fastest 3 servers.
|
||||
.TP 5
|
||||
+.B answer\-cookie: \fI<yes or no>
|
||||
+Enable to answer to requests containig DNS Cookies as specified in RFC7873 and
|
||||
+RFC9018. Default is no.
|
||||
+.TP 5
|
||||
+.B cookie\-secret: \fI<128 bit hex string>
|
||||
+Server's in an Anycast deployment need to be able to verify each other's
|
||||
+Server Cookies. For this they need to share the secret used to construct
|
||||
+and verify the Server Cookies.
|
||||
+Default is a 128 bits random secret generated at startup time.
|
||||
.B edns\-client\-string: \fI<IP netblock> <string>
|
||||
Include an EDNS0 option containing configured ascii string in queries with
|
||||
destination address matching the configured IP netblock. This configuration
|
||||
diff --git a/libunbound/libworker.c b/libunbound/libworker.c
|
||||
index 11bf5f9db..d5fabe6fb 100644
|
||||
--- a/libunbound/libworker.c
|
||||
+++ b/libunbound/libworker.c
|
||||
@@ -604,6 +604,8 @@ setup_qinfo_edns(struct libworker* w, struct ctx_query* q,
|
||||
edns->opt_list_out = NULL;
|
||||
edns->opt_list_inplace_cb_out = NULL;
|
||||
edns->padding_block_size = 0;
|
||||
+ edns->cookie_present = 0;
|
||||
+ edns->cookie_valid = 0;
|
||||
if(sldns_buffer_capacity(w->back->udp_buff) < 65535)
|
||||
edns->udp_size = (uint16_t)sldns_buffer_capacity(
|
||||
w->back->udp_buff);
|
||||
diff --git a/services/authzone.c b/services/authzone.c
|
||||
index 6de1e4319..079a7eaf1 100644
|
||||
--- a/services/authzone.c
|
||||
+++ b/services/authzone.c
|
||||
@@ -5419,6 +5419,8 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)
|
||||
edns.opt_list_out = NULL;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
edns.padding_block_size = 0;
|
||||
+ edns.cookie_present = 0;
|
||||
+ edns.cookie_valid = 0;
|
||||
if(sldns_buffer_capacity(buf) < 65535)
|
||||
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
|
||||
else edns.udp_size = 65535;
|
||||
@@ -6612,6 +6614,8 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)
|
||||
edns.opt_list_out = NULL;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
edns.padding_block_size = 0;
|
||||
+ edns.cookie_present = 0;
|
||||
+ edns.cookie_valid = 0;
|
||||
if(sldns_buffer_capacity(buf) < 65535)
|
||||
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
|
||||
else edns.udp_size = 65535;
|
||||
diff --git a/sldns/rrdef.h b/sldns/rrdef.h
|
||||
index 999c22307..d5b0585fd 100644
|
||||
--- a/sldns/rrdef.h
|
||||
+++ b/sldns/rrdef.h
|
||||
@@ -433,6 +433,7 @@ enum sldns_enum_edns_option
|
||||
LDNS_EDNS_DHU = 6, /* RFC6975 */
|
||||
LDNS_EDNS_N3U = 7, /* RFC6975 */
|
||||
LDNS_EDNS_CLIENT_SUBNET = 8, /* RFC7871 */
|
||||
+ LDNS_EDNS_COOKIE = 10, /* RFC7873 */
|
||||
LDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/
|
||||
LDNS_EDNS_PADDING = 12, /* RFC7830 */
|
||||
LDNS_EDNS_EDE = 15, /* RFC8914 */
|
||||
@@ -482,6 +483,9 @@ typedef enum sldns_enum_ede_code sldns_ede_code;
|
||||
#define LDNS_TSIG_ERROR_BADNAME 20
|
||||
#define LDNS_TSIG_ERROR_BADALG 21
|
||||
|
||||
+/** DNS Cookie extended rcode */
|
||||
+#define LDNS_EXT_RCODE_BADCOOKIE 23
|
||||
+
|
||||
/**
|
||||
* Contains all information about resource record types.
|
||||
*
|
||||
diff --git a/testcode/fake_event.c b/testcode/fake_event.c
|
||||
index 03e1c04f3..c93ceaa4c 100644
|
||||
--- a/testcode/fake_event.c
|
||||
+++ b/testcode/fake_event.c
|
||||
@@ -1263,6 +1263,8 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
if(dnssec)
|
||||
edns.bits = EDNS_DO;
|
||||
edns.padding_block_size = 0;
|
||||
+ edns.cookie_present = 0;
|
||||
+ edns.cookie_valid = 0;
|
||||
edns.opt_list_in = NULL;
|
||||
edns.opt_list_out = per_upstream_opt_list;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
diff --git a/util/config_file.c b/util/config_file.c
|
||||
index 158169c42..a92342761 100644
|
||||
--- a/util/config_file.c
|
||||
+++ b/util/config_file.c
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "util/regional.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/data/dname.h"
|
||||
+#include "util/random.h"
|
||||
#include "util/rtt.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "sldns/wire2str.h"
|
||||
@@ -87,6 +88,9 @@ struct config_parser_state* cfg_parser = 0;
|
||||
/** init ports possible for use */
|
||||
static void init_outgoing_availports(int* array, int num);
|
||||
|
||||
+/** init cookie with random data */
|
||||
+static void init_cookie_secret(uint8_t* cookie_secret,size_t cookie_secret_len);
|
||||
+
|
||||
struct config_file*
|
||||
config_create(void)
|
||||
{
|
||||
@@ -364,6 +368,10 @@ config_create(void)
|
||||
cfg->ipsecmod_whitelist = NULL;
|
||||
cfg->ipsecmod_strict = 0;
|
||||
#endif
|
||||
+ cfg->do_answer_cookie = 0;
|
||||
+ memset(cfg->cookie_secret, 0, sizeof(cfg->cookie_secret));
|
||||
+ cfg->cookie_secret_len = 16;
|
||||
+ init_cookie_secret(cfg->cookie_secret, cfg->cookie_secret_len);
|
||||
#ifdef USE_CACHEDB
|
||||
if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit;
|
||||
if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit;
|
||||
@@ -1660,6 +1668,21 @@ config_delete(struct config_file* cfg)
|
||||
free(cfg);
|
||||
}
|
||||
|
||||
+static void
|
||||
+init_cookie_secret(uint8_t* cookie_secret, size_t cookie_secret_len)
|
||||
+{
|
||||
+ struct ub_randstate *rand = ub_initstate(NULL);
|
||||
+
|
||||
+ if (!rand)
|
||||
+ fatal_exit("could not init random generator");
|
||||
+ while (cookie_secret_len) {
|
||||
+ *cookie_secret++ = (uint8_t)ub_random(rand);
|
||||
+ cookie_secret_len--;
|
||||
+ }
|
||||
+ ub_randfree(rand);
|
||||
+}
|
||||
+
|
||||
+
|
||||
static void
|
||||
init_outgoing_availports(int* a, int num)
|
||||
{
|
||||
diff --git a/util/config_file.h b/util/config_file.h
|
||||
index bbc6d4ac1..3db4676b9 100644
|
||||
--- a/util/config_file.h
|
||||
+++ b/util/config_file.h
|
||||
@@ -688,6 +688,13 @@ struct config_file {
|
||||
int redis_expire_records;
|
||||
#endif
|
||||
#endif
|
||||
+ /** Downstream DNS Cookies */
|
||||
+ /** do answer with server cookie when request contained cookie option */
|
||||
+ int do_answer_cookie;
|
||||
+ /** cookie secret */
|
||||
+ uint8_t cookie_secret[40];
|
||||
+ /** cookie secret length */
|
||||
+ size_t cookie_secret_len;
|
||||
|
||||
/* ipset module */
|
||||
#ifdef USE_IPSET
|
||||
diff --git a/util/configlexer.lex b/util/configlexer.lex
|
||||
index fc9aa7266..4fdb2cde0 100644
|
||||
--- a/util/configlexer.lex
|
||||
+++ b/util/configlexer.lex
|
||||
@@ -558,6 +558,8 @@ name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) }
|
||||
name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) }
|
||||
udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) }
|
||||
tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) }
|
||||
+answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) }
|
||||
+cookie-secret{COLON} { YDVAR(1, VAR_COOKIE_SECRET) }
|
||||
edns-client-string{COLON} { YDVAR(2, VAR_EDNS_CLIENT_STRING) }
|
||||
edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) }
|
||||
nsid{COLON} { YDVAR(1, VAR_NSID ) }
|
||||
diff --git a/util/configparser.y b/util/configparser.y
|
||||
index 8f3672f5d..980201460 100644
|
||||
--- a/util/configparser.y
|
||||
+++ b/util/configparser.y
|
||||
@@ -42,11 +42,13 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
+#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "util/configyyrename.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
+#include "sldns/str2wire.h"
|
||||
|
||||
int ub_c_lex(void);
|
||||
void ub_c_error(const char *message);
|
||||
@@ -181,6 +183,7 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
|
||||
%token VAR_FAST_SERVER_PERMIL VAR_FAST_SERVER_NUM
|
||||
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT
|
||||
+%token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET
|
||||
%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_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI
|
||||
@@ -316,6 +319,7 @@ content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_unknown_server_time_limit | server_log_tag_queryreply |
|
||||
server_stream_wait_size | server_tls_ciphers |
|
||||
server_tls_ciphersuites | server_tls_session_ticket_keys |
|
||||
+ server_answer_cookie | server_cookie_secret |
|
||||
server_tls_use_sni | server_edns_client_string |
|
||||
server_edns_client_string_opcode | server_nsid |
|
||||
server_zonemd_permissive_mode | server_max_reuse_tcp_queries |
|
||||
@@ -3695,6 +3699,30 @@ server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
|
||||
}
|
||||
}
|
||||
;
|
||||
+server_answer_cookie: VAR_ANSWER_COOKIE STRING_ARG
|
||||
+ {
|
||||
+ OUTYY(("P(server_answer_cookie:%s)\n", $2));
|
||||
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
+ yyerror("expected yes or no.");
|
||||
+ else cfg_parser->cfg->do_answer_cookie = (strcmp($2, "yes")==0);
|
||||
+ free($2);
|
||||
+ }
|
||||
+ ;
|
||||
+server_cookie_secret: VAR_COOKIE_SECRET STRING_ARG
|
||||
+ {
|
||||
+ uint8_t secret[32];
|
||||
+ size_t secret_len = sizeof(secret);
|
||||
+
|
||||
+ OUTYY(("P(server_cookie_secret:%s)\n", $2));
|
||||
+ if (sldns_str2wire_hex_buf($2, secret, &secret_len)
|
||||
+ || ( secret_len != 16))
|
||||
+ yyerror("expected 128 bit hex string");
|
||||
+ else {
|
||||
+ cfg_parser->cfg->cookie_secret_len = secret_len;
|
||||
+ memcpy(cfg_parser->cfg->cookie_secret, secret, sizeof(secret));
|
||||
+ }
|
||||
+ free($2);
|
||||
+ }
|
||||
ipsetstart: VAR_IPSET
|
||||
{
|
||||
OUTYY(("\nP(ipset:)\n"));
|
||||
@@ -3764,10 +3792,11 @@ validate_acl_action(const char* action)
|
||||
strcmp(action, "refuse_non_local")!=0 &&
|
||||
strcmp(action, "allow_setrd")!=0 &&
|
||||
strcmp(action, "allow")!=0 &&
|
||||
- strcmp(action, "allow_snoop")!=0)
|
||||
+ strcmp(action, "allow_snoop")!=0 &&
|
||||
+ strcmp(action, "allow_cookie")!=0)
|
||||
{
|
||||
yyerror("expected deny, refuse, deny_non_local, "
|
||||
- "refuse_non_local, allow, allow_setrd or "
|
||||
- "allow_snoop as access control action");
|
||||
+ "refuse_non_local, allow, allow_setrd, "
|
||||
+ "allow_snoop or allow_cookie as access control action");
|
||||
}
|
||||
}
|
||||
diff --git a/util/data/msgparse.c b/util/data/msgparse.c
|
||||
index 5bb69d6ed..3059d555c 100644
|
||||
--- a/util/data/msgparse.c
|
||||
+++ b/util/data/msgparse.c
|
||||
@@ -951,11 +951,67 @@ edns_opt_list_append_keepalive(struct edns_option** list, int msec,
|
||||
data, region);
|
||||
}
|
||||
|
||||
+int siphash(const uint8_t *in, const size_t inlen,
|
||||
+ const uint8_t *k, uint8_t *out, const size_t outlen);
|
||||
+
|
||||
+/** RFC 1982 comparison, uses unsigned integers, and tries to avoid
|
||||
+ * compiler optimization (eg. by avoiding a-b<0 comparisons),
|
||||
+ * this routine matches compare_serial(), for SOA serial number checks */
|
||||
+static int
|
||||
+compare_1982(uint32_t a, uint32_t b)
|
||||
+{
|
||||
+ /* for 32 bit values */
|
||||
+ const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
|
||||
+
|
||||
+ if (a == b) {
|
||||
+ return 0;
|
||||
+ } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) {
|
||||
+ return -1;
|
||||
+ } else {
|
||||
+ return 1;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/** if we know that b is larger than a, return the difference between them,
|
||||
+ * that is the distance between them. in RFC1982 arith */
|
||||
+static uint32_t
|
||||
+subtract_1982(uint32_t a, uint32_t b)
|
||||
+{
|
||||
+ /* for 32 bit values */
|
||||
+ const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
|
||||
+
|
||||
+ if(a == b)
|
||||
+ return 0;
|
||||
+ if(a < b && b - a < cutoff) {
|
||||
+ return b-a;
|
||||
+ }
|
||||
+ if(a > b && a - b > cutoff) {
|
||||
+ return ((uint32_t)0xffffffff) - (a-b-1);
|
||||
+ }
|
||||
+ /* wrong case, b smaller than a */
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static uint8_t *
|
||||
+cookie_hash(uint8_t *hash, uint8_t *buf,
|
||||
+ struct sockaddr_storage *addr, uint8_t *secret)
|
||||
+{
|
||||
+ if (addr->ss_family == AF_INET6) {
|
||||
+ memcpy(buf+16, &((struct sockaddr_in6 *)addr)->sin6_addr, 16);
|
||||
+ siphash(buf, 32, secret, hash, 8);
|
||||
+ } else {
|
||||
+ memcpy(buf+16, &((struct sockaddr_in *)addr)->sin_addr, 4);
|
||||
+ siphash(buf, 20, secret, hash, 8);
|
||||
+ }
|
||||
+ return hash;
|
||||
+}
|
||||
+
|
||||
/** parse EDNS options from EDNS wireformat rdata */
|
||||
static int
|
||||
parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
struct edns_data* edns, struct config_file* cfg, struct comm_point* c,
|
||||
- struct regional* region)
|
||||
+ struct comm_reply* repinfo, uint32_t now, struct regional* region)
|
||||
{
|
||||
/* To respond with a Keepalive option, the client connection must have
|
||||
* received one message with a TCP Keepalive EDNS option, and that
|
||||
@@ -979,6 +1035,10 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
while(rdata_len >= 4) {
|
||||
uint16_t opt_code = sldns_read_uint16(rdata_ptr);
|
||||
uint16_t opt_len = sldns_read_uint16(rdata_ptr+2);
|
||||
+ uint8_t server_cookie[40], hash[8];
|
||||
+ uint32_t cookie_time, subt_1982;
|
||||
+ int comp_1982;
|
||||
+
|
||||
rdata_ptr += 4;
|
||||
rdata_len -= 4;
|
||||
if(opt_len > rdata_len)
|
||||
@@ -1041,6 +1101,86 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
edns->padding_block_size = cfg->pad_responses_block_size;
|
||||
break;
|
||||
|
||||
+ case LDNS_EDNS_COOKIE:
|
||||
+ if(!cfg || !cfg->do_answer_cookie)
|
||||
+ break;
|
||||
+ if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) {
|
||||
+ verbose(VERB_ALGO, "worker request: "
|
||||
+ "badly formatted cookie");
|
||||
+ return LDNS_RCODE_FORMERR;
|
||||
+ }
|
||||
+ edns->cookie_present = 1;
|
||||
+
|
||||
+ /* Copy client cookie, version and timestamp for
|
||||
+ * validation and creation purposes.
|
||||
+ */
|
||||
+ memcpy(server_cookie, rdata_ptr, 16);
|
||||
+
|
||||
+ /* In the "if, if else" block below, we validate a
|
||||
+ * RFC9018 cookie. If it doesn't match the recipe, or
|
||||
+ * if it doesn't validate, or if the cookie is too old
|
||||
+ * (< 30 min), a new cookie is generated.
|
||||
+ */
|
||||
+ if (opt_len != 24)
|
||||
+ ; /* RFC9018 cookies are 24 bytes long */
|
||||
+
|
||||
+ else if (cfg->cookie_secret_len != 16)
|
||||
+ ; /* RFC9018 cookies have 16 byte secrets */
|
||||
+
|
||||
+ else if (rdata_ptr[8] != 1)
|
||||
+ ; /* RFC9018 cookies are cookie version 1 */
|
||||
+
|
||||
+ else if ((comp_1982 = compare_1982(now,
|
||||
+ (cookie_time = sldns_read_uint32(rdata_ptr + 12)))) > 0
|
||||
+ && (subt_1982 = subtract_1982(cookie_time, now)) > 3600)
|
||||
+ ; /* Cookie is older than 1 hour
|
||||
+ * (see RFC9018 Section 4.3.)
|
||||
+ */
|
||||
+
|
||||
+ else if (comp_1982 <= 0
|
||||
+ && subtract_1982(now, cookie_time) > 300)
|
||||
+ ; /* Cookie time is more than 5 minutes in the
|
||||
+ * future. (see RFC9018 Section 4.3.)
|
||||
+ */
|
||||
+
|
||||
+ else if (memcmp( cookie_hash( hash, server_cookie
|
||||
+ , &repinfo->remote_addr
|
||||
+ , cfg->cookie_secret)
|
||||
+ , rdata_ptr + 16 , 8 ) == 0) {
|
||||
+
|
||||
+ /* Cookie is valid! */
|
||||
+ edns->cookie_valid = 1;
|
||||
+ if (comp_1982 > 0 && subt_1982 > 1800)
|
||||
+ ; /* But older than 30 minutes,
|
||||
+ * so create a new one anyway */
|
||||
+
|
||||
+ else if (!edns_opt_list_append( /* Reuse cookie */
|
||||
+ &edns->opt_list_out, LDNS_EDNS_COOKIE, opt_len,
|
||||
+ rdata_ptr, region)) {
|
||||
+ log_err("out of memory");
|
||||
+ return LDNS_RCODE_SERVFAIL;
|
||||
+ } else
|
||||
+ /* Cookie to be reused added to
|
||||
+ * outgoing options. Done!
|
||||
+ */
|
||||
+ break;
|
||||
+ }
|
||||
+ /* Add a new server cookie to outgoing cookies */
|
||||
+ server_cookie[ 8] = 1; /* Version */
|
||||
+ server_cookie[ 9] = 0; /* Reserved */
|
||||
+ server_cookie[10] = 0; /* Reserved */
|
||||
+ server_cookie[11] = 0; /* Reserved */
|
||||
+ sldns_write_uint32(server_cookie + 12, now);
|
||||
+ cookie_hash( hash, server_cookie
|
||||
+ , &repinfo->remote_addr, cfg->cookie_secret);
|
||||
+ memcpy(server_cookie + 16, hash, 8);
|
||||
+ if (!edns_opt_list_append( &edns->opt_list_out
|
||||
+ , LDNS_EDNS_COOKIE
|
||||
+ , 24, server_cookie, region)) {
|
||||
+ log_err("out of memory");
|
||||
+ return LDNS_RCODE_SERVFAIL;
|
||||
+ }
|
||||
+ break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1115,6 +1255,8 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg,
|
||||
edns->opt_list_out = NULL;
|
||||
edns->opt_list_inplace_cb_out = NULL;
|
||||
edns->padding_block_size = 0;
|
||||
+ edns->cookie_present = 0;
|
||||
+ edns->cookie_valid = 0;
|
||||
|
||||
/* take the options */
|
||||
rdata_len = found->rr_first->size-2;
|
||||
@@ -1170,7 +1312,8 @@ skip_pkt_rrs(sldns_buffer* pkt, int num)
|
||||
|
||||
int
|
||||
parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
- struct config_file* cfg, struct comm_point* c, struct regional* region)
|
||||
+ struct config_file* cfg, struct comm_point* c,
|
||||
+ struct comm_reply* repinfo, time_t now, struct regional* region)
|
||||
{
|
||||
size_t rdata_len;
|
||||
uint8_t* rdata_ptr;
|
||||
@@ -1206,6 +1349,8 @@ parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
edns->opt_list_out = NULL;
|
||||
edns->opt_list_inplace_cb_out = NULL;
|
||||
edns->padding_block_size = 0;
|
||||
+ edns->cookie_present = 0;
|
||||
+ edns->cookie_valid = 0;
|
||||
|
||||
/* take the options */
|
||||
rdata_len = sldns_buffer_read_u16(pkt);
|
||||
@@ -1214,7 +1359,7 @@ parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
rdata_ptr = sldns_buffer_current(pkt);
|
||||
/* ignore rrsigs */
|
||||
return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg,
|
||||
- c, region);
|
||||
+ c, repinfo, now, region);
|
||||
}
|
||||
|
||||
void
|
||||
diff --git a/util/data/msgparse.h b/util/data/msgparse.h
|
||||
index 0c458e6e8..aebeb2a88 100644
|
||||
--- a/util/data/msgparse.h
|
||||
+++ b/util/data/msgparse.h
|
||||
@@ -72,6 +72,7 @@ struct regional;
|
||||
struct edns_option;
|
||||
struct config_file;
|
||||
struct comm_point;
|
||||
+struct comm_reply;
|
||||
|
||||
/** number of buckets in parse rrset hash table. Must be power of 2. */
|
||||
#define PARSE_TABLE_SIZE 32
|
||||
@@ -217,8 +218,6 @@ struct rr_parse {
|
||||
* region.
|
||||
*/
|
||||
struct edns_data {
|
||||
- /** if EDNS OPT record was present */
|
||||
- int edns_present;
|
||||
/** Extended RCODE */
|
||||
uint8_t ext_rcode;
|
||||
/** The EDNS version number */
|
||||
@@ -238,7 +237,13 @@ struct edns_data {
|
||||
struct edns_option* opt_list_inplace_cb_out;
|
||||
/** block size to pad */
|
||||
uint16_t padding_block_size;
|
||||
-};
|
||||
+ /** if EDNS OPT record was present */
|
||||
+ unsigned int edns_present : 1;
|
||||
+ /** if a cookie was present */
|
||||
+ unsigned int cookie_present : 1;
|
||||
+ /** if the cookie validated */
|
||||
+ unsigned int cookie_valid : 1;
|
||||
+};
|
||||
|
||||
/**
|
||||
* EDNS option
|
||||
@@ -315,7 +320,8 @@ int skip_pkt_rrs(struct sldns_buffer* pkt, int num);
|
||||
* RCODE formerr if OPT is badly formatted and so on.
|
||||
*/
|
||||
int parse_edns_from_query_pkt(struct sldns_buffer* pkt, struct edns_data* edns,
|
||||
- struct config_file* cfg, struct comm_point* c, struct regional* region);
|
||||
+ struct config_file* cfg, struct comm_point* c,
|
||||
+ struct comm_reply* repinfo, time_t now, struct regional* region);
|
||||
|
||||
/**
|
||||
* Calculate hash value for rrset in packet.
|
||||
diff --git a/util/siphash.c b/util/siphash.c
|
||||
new file mode 100644
|
||||
index 000000000..d69f4b579
|
||||
--- /dev/null
|
||||
+++ b/util/siphash.c
|
||||
@@ -0,0 +1,165 @@
|
||||
+/*
|
||||
+ SipHash reference C implementation
|
||||
+
|
||||
+ Copyright (c) 2012-2016 Jean-Philippe Aumasson
|
||||
+ <jeanphilippe.aumasson@gmail.com>
|
||||
+ Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
|
||||
+
|
||||
+ To the extent possible under law, the author(s) have dedicated all copyright
|
||||
+ and related and neighboring rights to this software to the public domain
|
||||
+ worldwide. This software is distributed without any warranty.
|
||||
+
|
||||
+ You should have received a copy of the CC0 Public Domain Dedication along
|
||||
+ with
|
||||
+ this software. If not, see
|
||||
+ <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
+ */
|
||||
+#include <assert.h>
|
||||
+#include <stdint.h>
|
||||
+#include <stdio.h>
|
||||
+#include <string.h>
|
||||
+
|
||||
+/* default: SipHash-2-4 */
|
||||
+#define cROUNDS 2
|
||||
+#define dROUNDS 4
|
||||
+
|
||||
+#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
|
||||
+
|
||||
+#define U32TO8_LE(p, v) \
|
||||
+ (p)[0] = (uint8_t)((v)); \
|
||||
+ (p)[1] = (uint8_t)((v) >> 8); \
|
||||
+ (p)[2] = (uint8_t)((v) >> 16); \
|
||||
+ (p)[3] = (uint8_t)((v) >> 24);
|
||||
+
|
||||
+#define U64TO8_LE(p, v) \
|
||||
+ U32TO8_LE((p), (uint32_t)((v))); \
|
||||
+ U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
|
||||
+
|
||||
+#define U8TO64_LE(p) \
|
||||
+ (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
|
||||
+ ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
|
||||
+ ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
|
||||
+ ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
|
||||
+
|
||||
+#define SIPROUND \
|
||||
+ do { \
|
||||
+ v0 += v1; \
|
||||
+ v1 = ROTL(v1, 13); \
|
||||
+ v1 ^= v0; \
|
||||
+ v0 = ROTL(v0, 32); \
|
||||
+ v2 += v3; \
|
||||
+ v3 = ROTL(v3, 16); \
|
||||
+ v3 ^= v2; \
|
||||
+ v0 += v3; \
|
||||
+ v3 = ROTL(v3, 21); \
|
||||
+ v3 ^= v0; \
|
||||
+ v2 += v1; \
|
||||
+ v1 = ROTL(v1, 17); \
|
||||
+ v1 ^= v2; \
|
||||
+ v2 = ROTL(v2, 32); \
|
||||
+ } while (0)
|
||||
+
|
||||
+#ifdef DEBUG
|
||||
+#define TRACE \
|
||||
+ do { \
|
||||
+ printf("(%3d) v0 %08x %08x\n", (int)inlen, (uint32_t)(v0 >> 32), \
|
||||
+ (uint32_t)v0); \
|
||||
+ printf("(%3d) v1 %08x %08x\n", (int)inlen, (uint32_t)(v1 >> 32), \
|
||||
+ (uint32_t)v1); \
|
||||
+ printf("(%3d) v2 %08x %08x\n", (int)inlen, (uint32_t)(v2 >> 32), \
|
||||
+ (uint32_t)v2); \
|
||||
+ printf("(%3d) v3 %08x %08x\n", (int)inlen, (uint32_t)(v3 >> 32), \
|
||||
+ (uint32_t)v3); \
|
||||
+ } while (0)
|
||||
+#else
|
||||
+#define TRACE
|
||||
+#endif
|
||||
+
|
||||
+int siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
|
||||
+ uint8_t *out, const size_t outlen) {
|
||||
+
|
||||
+ assert((outlen == 8) || (outlen == 16));
|
||||
+ uint64_t v0 = 0x736f6d6570736575ULL;
|
||||
+ uint64_t v1 = 0x646f72616e646f6dULL;
|
||||
+ uint64_t v2 = 0x6c7967656e657261ULL;
|
||||
+ uint64_t v3 = 0x7465646279746573ULL;
|
||||
+ uint64_t k0 = U8TO64_LE(k);
|
||||
+ uint64_t k1 = U8TO64_LE(k + 8);
|
||||
+ uint64_t m;
|
||||
+ int i;
|
||||
+ const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
|
||||
+ const int left = inlen & 7;
|
||||
+ uint64_t b = ((uint64_t)inlen) << 56;
|
||||
+ v3 ^= k1;
|
||||
+ v2 ^= k0;
|
||||
+ v1 ^= k1;
|
||||
+ v0 ^= k0;
|
||||
+
|
||||
+ if (outlen == 16)
|
||||
+ v1 ^= 0xee;
|
||||
+
|
||||
+ for (; in != end; in += 8) {
|
||||
+ m = U8TO64_LE(in);
|
||||
+ v3 ^= m;
|
||||
+
|
||||
+ TRACE;
|
||||
+ for (i = 0; i < cROUNDS; ++i)
|
||||
+ SIPROUND;
|
||||
+
|
||||
+ v0 ^= m;
|
||||
+ }
|
||||
+
|
||||
+ switch (left) {
|
||||
+ case 7:
|
||||
+ b |= ((uint64_t)in[6]) << 48;
|
||||
+ case 6:
|
||||
+ b |= ((uint64_t)in[5]) << 40;
|
||||
+ case 5:
|
||||
+ b |= ((uint64_t)in[4]) << 32;
|
||||
+ case 4:
|
||||
+ b |= ((uint64_t)in[3]) << 24;
|
||||
+ case 3:
|
||||
+ b |= ((uint64_t)in[2]) << 16;
|
||||
+ case 2:
|
||||
+ b |= ((uint64_t)in[1]) << 8;
|
||||
+ case 1:
|
||||
+ b |= ((uint64_t)in[0]);
|
||||
+ break;
|
||||
+ case 0:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ v3 ^= b;
|
||||
+
|
||||
+ TRACE;
|
||||
+ for (i = 0; i < cROUNDS; ++i)
|
||||
+ SIPROUND;
|
||||
+
|
||||
+ v0 ^= b;
|
||||
+
|
||||
+ if (outlen == 16)
|
||||
+ v2 ^= 0xee;
|
||||
+ else
|
||||
+ v2 ^= 0xff;
|
||||
+
|
||||
+ TRACE;
|
||||
+ for (i = 0; i < dROUNDS; ++i)
|
||||
+ SIPROUND;
|
||||
+
|
||||
+ b = v0 ^ v1 ^ v2 ^ v3;
|
||||
+ U64TO8_LE(out, b);
|
||||
+
|
||||
+ if (outlen == 8)
|
||||
+ return 0;
|
||||
+
|
||||
+ v1 ^= 0xdd;
|
||||
+
|
||||
+ TRACE;
|
||||
+ for (i = 0; i < dROUNDS; ++i)
|
||||
+ SIPROUND;
|
||||
+
|
||||
+ b = v0 ^ v1 ^ v2 ^ v3;
|
||||
+ U64TO8_LE(out + 8, b);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/validator/autotrust.c b/validator/autotrust.c
|
||||
index 3cdf9ceae..3011a0ace 100644
|
||||
--- a/validator/autotrust.c
|
||||
+++ b/validator/autotrust.c
|
||||
@@ -2376,6 +2376,8 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp)
|
||||
edns.opt_list_out = NULL;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
edns.padding_block_size = 0;
|
||||
+ edns.cookie_present = 0;
|
||||
+ edns.cookie_valid = 0;
|
||||
if(sldns_buffer_capacity(buf) < 65535)
|
||||
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
|
||||
else edns.udp_size = 65535;
|
||||
@ -0,0 +1,30 @@
|
||||
From 1c85901cc4c0f7693183f4b280604619e56cba00 Mon Sep 17 00:00:00 2001
|
||||
From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl>
|
||||
Date: Wed, 16 Aug 2023 16:58:49 +0200
|
||||
Subject: [PATCH] - Fix out of bounds read in parse_edns_options_from_query, it
|
||||
would read 8 bytes after a client option of length 8, and then ignore them
|
||||
to recreate a 24 byte response. The fixup does not read out of bounds,
|
||||
and puts zeroes in the buffer at that point, that then are ignored.
|
||||
|
||||
---
|
||||
util/data/msgparse.c | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/util/data/msgparse.c b/util/data/msgparse.c
|
||||
index 40189d613..b5414c6d0 100644
|
||||
--- a/util/data/msgparse.c
|
||||
+++ b/util/data/msgparse.c
|
||||
@@ -1049,7 +1049,12 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
/* Copy client cookie, version and timestamp for
|
||||
* validation and creation purposes.
|
||||
*/
|
||||
- memmove(server_cookie, rdata_ptr, 16);
|
||||
+ if(opt_len >= 16) {
|
||||
+ memmove(server_cookie, rdata_ptr, 16);
|
||||
+ } else {
|
||||
+ memset(server_cookie, 0, 16);
|
||||
+ memmove(server_cookie, rdata_ptr, opt_len);
|
||||
+ }
|
||||
|
||||
/* In the "if, if else" block below, we validate a
|
||||
* RFC9018 cookie. If it doesn't match the recipe, or
|
||||
@ -0,0 +1,22 @@
|
||||
From 2b1028bdad6da4d42f0571d0182322c3554e0bcc Mon Sep 17 00:00:00 2001
|
||||
From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl>
|
||||
Date: Wed, 16 Aug 2023 10:06:06 +0200
|
||||
Subject: [PATCH] - Fix possibly unaligned memory access.
|
||||
|
||||
---
|
||||
util/data/msgparse.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/util/data/msgparse.c b/util/data/msgparse.c
|
||||
index 8cef37a17..40189d613 100644
|
||||
--- a/util/data/msgparse.c
|
||||
+++ b/util/data/msgparse.c
|
||||
@@ -1049,7 +1049,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
/* Copy client cookie, version and timestamp for
|
||||
* validation and creation purposes.
|
||||
*/
|
||||
- memcpy(server_cookie, rdata_ptr, 16);
|
||||
+ memmove(server_cookie, rdata_ptr, 16);
|
||||
|
||||
/* In the "if, if else" block below, we validate a
|
||||
* RFC9018 cookie. If it doesn't match the recipe, or
|
||||
@ -0,0 +1,130 @@
|
||||
From 71f23ef354fae12e99963fe43200d38dfe796222 Mon Sep 17 00:00:00 2001
|
||||
From: Willem Toorop <willem@nlnetlabs.nl>
|
||||
Date: Wed, 28 Sep 2022 09:57:56 +0200
|
||||
Subject: [PATCH] extended_error_encode() for extended errors
|
||||
|
||||
---
|
||||
daemon/worker.c | 14 ++------------
|
||||
util/data/msgencode.c | 18 ++++++++++++++----
|
||||
util/data/msgencode.h | 19 ++++++++++++++++++-
|
||||
3 files changed, 34 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/daemon/worker.c b/daemon/worker.c
|
||||
index 29c1961ed..c2a94be79 100644
|
||||
--- a/daemon/worker.c
|
||||
+++ b/daemon/worker.c
|
||||
@@ -1439,8 +1439,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
repinfo->client_addrlen);
|
||||
memset(&reply_edns, 0, sizeof(reply_edns));
|
||||
reply_edns.edns_present = 1;
|
||||
- reply_edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), ret);
|
||||
error_encode(c->buffer, ret, &qinfo,
|
||||
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns);
|
||||
@@ -1449,23 +1447,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
}
|
||||
if(edns.edns_present) {
|
||||
if(edns.edns_version != 0) {
|
||||
- edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4);
|
||||
- edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
- edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
- edns.bits &= EDNS_DO;
|
||||
edns.opt_list_in = NULL;
|
||||
edns.opt_list_out = NULL;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
- edns.padding_block_size = 0;
|
||||
verbose(VERB_ALGO, "query with bad edns version.");
|
||||
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
|
||||
repinfo->client_addrlen);
|
||||
- error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo,
|
||||
+ extended_error_encode(c->buffer, EDNS_RCODE_BADVERS, &qinfo,
|
||||
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
|
||||
- sldns_buffer_read_u16_at(c->buffer, 2), NULL);
|
||||
- if(sldns_buffer_capacity(c->buffer) >=
|
||||
- sldns_buffer_limit(c->buffer)+calc_edns_field_size(&edns))
|
||||
- attach_edns_record(c->buffer, &edns);
|
||||
+ sldns_buffer_read_u16_at(c->buffer, 2), 0, &edns);
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply;
|
||||
}
|
||||
diff --git a/util/data/msgencode.c b/util/data/msgencode.c
|
||||
index fe21cfb86..d832ddc91 100644
|
||||
--- a/util/data/msgencode.c
|
||||
+++ b/util/data/msgencode.c
|
||||
@@ -959,14 +959,15 @@ qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo)
|
||||
}
|
||||
|
||||
void
|
||||
-error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
|
||||
- uint16_t qid, uint16_t qflags, struct edns_data* edns)
|
||||
+extended_error_encode(sldns_buffer* buf, uint16_t rcode, struct query_info* qinfo,
|
||||
+ uint16_t qid, uint16_t qflags, uint16_t xflags, struct edns_data* edns)
|
||||
{
|
||||
uint16_t flags;
|
||||
|
||||
sldns_buffer_clear(buf);
|
||||
sldns_buffer_write(buf, &qid, sizeof(uint16_t));
|
||||
- flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/
|
||||
+ flags = (uint16_t)(BIT_QR | BIT_RA | (rcode & 0xF)); /* QR and retcode*/
|
||||
+ flags |= xflags;
|
||||
flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
|
||||
sldns_buffer_write_u16(buf, flags);
|
||||
if(qinfo) flags = 1;
|
||||
@@ -993,7 +994,7 @@ error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
|
||||
struct edns_data es = *edns;
|
||||
es.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
es.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
- es.ext_rcode = 0;
|
||||
+ es.ext_rcode = (uint8_t)(rcode >> 4);
|
||||
es.bits &= EDNS_DO;
|
||||
if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
|
||||
edns->udp_size)
|
||||
@@ -1001,3 +1002,12 @@ error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
|
||||
attach_edns_record(buf, &es);
|
||||
}
|
||||
}
|
||||
+
|
||||
+void
|
||||
+error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
|
||||
+ uint16_t qid, uint16_t qflags, struct edns_data* edns)
|
||||
+{
|
||||
+ extended_error_encode(buf, (r & 0x000F), qinfo, qid, qflags
|
||||
+ , (r & 0xFFF0), edns);
|
||||
+}
|
||||
+
|
||||
diff --git a/util/data/msgencode.h b/util/data/msgencode.h
|
||||
index 30dc515cb..1b37ca870 100644
|
||||
--- a/util/data/msgencode.h
|
||||
+++ b/util/data/msgencode.h
|
||||
@@ -120,7 +120,7 @@ void attach_edns_record(struct sldns_buffer* pkt, struct edns_data* edns);
|
||||
* Encode an error. With QR and RA set.
|
||||
*
|
||||
* @param pkt: where to store the packet.
|
||||
- * @param r: RCODE value to encode.
|
||||
+ * @param r: RCODE value to encode (may contain extra flags).
|
||||
* @param qinfo: if not NULL, the query is included.
|
||||
* @param qid: query ID to set in packet. network order.
|
||||
* @param qflags: original query flags (to copy RD and CD bits). host order.
|
||||
@@ -130,4 +130,21 @@ void attach_edns_record(struct sldns_buffer* pkt, struct edns_data* edns);
|
||||
void error_encode(struct sldns_buffer* pkt, int r, struct query_info* qinfo,
|
||||
uint16_t qid, uint16_t qflags, struct edns_data* edns);
|
||||
|
||||
+/**
|
||||
+ * Encode an extended error. With QR and RA set.
|
||||
+ *
|
||||
+ * @param pkt: where to store the packet.
|
||||
+ * @param rcode: Extended RCODE value to encode.
|
||||
+ * @param qinfo: if not NULL, the query is included.
|
||||
+ * @param qid: query ID to set in packet. network order.
|
||||
+ * @param qflags: original query flags (to copy RD and CD bits). host order.
|
||||
+ * @param xflags: extra flags to set (such as for example BIT_AA and/or BIT_TC)
|
||||
+ * @param edns: if not NULL, this is the query edns info,
|
||||
+ * and an edns reply is attached. Only attached if EDNS record fits reply.
|
||||
+ * Without edns extended errors (i.e. > 15 )will not be conveyed.
|
||||
+ */
|
||||
+void extended_error_encode(struct sldns_buffer* pkt, uint16_t rcode,
|
||||
+ struct query_info* qinfo, uint16_t qid, uint16_t qflags,
|
||||
+ uint16_t xflags, struct edns_data* edns);
|
||||
+
|
||||
#endif /* UTIL_DATA_MSGENCODE_H */
|
||||
15
unbound.spec
15
unbound.spec
@ -2,7 +2,7 @@
|
||||
|
||||
Name: unbound
|
||||
Version: 1.17.1
|
||||
Release: 4
|
||||
Release: 5
|
||||
Summary: Unbound is a validating, recursive, caching DNS resolver
|
||||
License: BSD-3-Clause
|
||||
Url: https://nlnetlabs.nl/projects/unbound/about/
|
||||
@ -23,10 +23,15 @@ Source13: unbound-anchor.service
|
||||
|
||||
Patch1: unbound-remove-buildin-key.patch
|
||||
Patch2: backport-CVE-2023-50387_CVE-2023-50868.patch
|
||||
Patch3: backport-pre-CVE-2024-33655-extended_error_encode-for-extended-errors.patch
|
||||
Patch4: backport-pre-CVE-2024-33655-Downstream-DNS-Cookies-a-la-RFC7873-and-RFC9018.patch
|
||||
Patch5: backport-pre-CVE-2024-33655-Fix-possibly-unaligned-memory-access-in-parse_edns_options_from_query.patch
|
||||
Patch6: backport-pre-CVE-2024-33655-Fix-out-of-bounds-read-in-parse_edns_options_from_query.patch
|
||||
Patch7: 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}
|
||||
@ -235,6 +240,12 @@ popd
|
||||
%{_mandir}/man*
|
||||
|
||||
%changelog
|
||||
* Fri May 17 2024 gaihuiying <eaglegai@163.com> - 1.17.1-5
|
||||
- Type:cves
|
||||
- CVE:CVE-2024-33655
|
||||
- SUG:NA
|
||||
- DESC:fix CVE-2024-33655
|
||||
|
||||
* Tue Mar 05 2024 gaihuiying <eaglegai@163.com> - 1.17.1-4
|
||||
- Type:bugfix
|
||||
- CVE:NA
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user