configurable ranges and tsig to allow responses

Configurable with the "allow-response:" option in the "server:" section.
Usage:
	allow-response: <IP netblock> <tsig key-name | NOKEY | BLOCKED>
This commit is contained in:
Willem Toorop
2025-11-01 12:52:48 +01:00
parent 3d6a4c7d6e
commit 3963af8d0b
6 changed files with 484 additions and 566 deletions
+388 -553
View File
File diff suppressed because it is too large Load Diff
+17
View File
@@ -77,6 +77,7 @@
#include "util/storage/lookup3.h"
#include "util/storage/slabhash.h"
#include "util/tcp_conn_limit.h"
#include "util/allow_response_list.h"
#include "util/edns.h"
#include "services/listen_dnsport.h"
#include "services/cache/rrset.h"
@@ -298,6 +299,16 @@ daemon_init(void)
free(daemon);
return NULL;
}
daemon->arl = arl_list_create();
if(!daemon->arl) {
acl_list_delete(daemon->acl_interface);
acl_list_delete(daemon->acl);
tcl_list_delete(daemon->tcl);
edns_known_options_delete(daemon->env);
free(daemon->env);
free(daemon);
return NULL;
}
listen_setup_locks();
if(gettimeofday(&daemon->time_boot, NULL) < 0)
log_err("gettimeofday: %s", strerror(errno));
@@ -306,6 +317,7 @@ daemon_init(void)
acl_list_delete(daemon->acl_interface);
acl_list_delete(daemon->acl);
tcl_list_delete(daemon->tcl);
arl_list_delete(daemon->arl);
edns_known_options_delete(daemon->env);
free(daemon->env);
free(daemon);
@@ -316,6 +328,7 @@ daemon_init(void)
acl_list_delete(daemon->acl_interface);
acl_list_delete(daemon->acl);
tcl_list_delete(daemon->tcl);
arl_list_delete(daemon->arl);
edns_known_options_delete(daemon->env);
free(daemon->env);
free(daemon);
@@ -326,6 +339,7 @@ daemon_init(void)
acl_list_delete(daemon->acl_interface);
acl_list_delete(daemon->acl);
tcl_list_delete(daemon->tcl);
arl_list_delete(daemon->arl);
edns_known_options_delete(daemon->env);
edns_strings_delete(daemon->env->edns_strings);
free(daemon->env);
@@ -741,6 +755,8 @@ daemon_fork(struct daemon* daemon)
fatal_exit("Could not setup interface control list");
if(!tcl_list_apply_cfg(daemon->tcl, daemon->cfg))
fatal_exit("Could not setup TCP connection limits");
if(!arl_list_apply_cfg(daemon->arl, daemon->cfg))
fatal_exit("Could not setup allow response list");
if(daemon->cfg->dnscrypt) {
#ifdef USE_DNSCRYPT
daemon->dnscenv = dnsc_create();
@@ -968,6 +984,7 @@ daemon_delete(struct daemon* daemon)
acl_list_delete(daemon->acl);
acl_list_delete(daemon->acl_interface);
tcl_list_delete(daemon->tcl);
arl_list_delete(daemon->arl);
cookie_secrets_delete(daemon->cookie_secrets);
listen_desetup_locks();
free(daemon->chroot);
+2
View File
@@ -133,6 +133,8 @@ struct daemon {
struct acl_list* acl_interface;
/** TCP connection limit, limit connections from client IPs */
struct tcl_list* tcl;
/** allow response list, to cache responses send by client IPs */
struct arl_list* arl;
/** local authority zones */
struct local_zones* local_zones;
/** last time of statistics printout */
+17
View File
@@ -97,6 +97,7 @@
#include "sldns/sbuffer.h"
#include "util/timeval_func.h"
#include "util/tcp_conn_limit.h"
#include "util/allow_response_list.h"
#include "util/edns.h"
#include "util/tsig.h"
#ifdef USE_CACHEDB
@@ -4646,6 +4647,8 @@ struct fast_reload_construct {
struct acl_list* acl_interface;
/** construct for tcp connection limit */
struct tcl_list* tcl;
/** construct for allow response list */
struct arl_list* arl;
/** tsig key table */
struct tsig_key_table* tsig_key_table;
/** construct for local zones */
@@ -5034,6 +5037,7 @@ fr_construct_clear(struct fast_reload_construct* ct)
acl_list_delete(ct->acl);
acl_list_delete(ct->acl_interface);
tcl_list_delete(ct->tcl);
arl_list_delete(ct->arl);
tsig_key_table_delete(ct->tsig_key_table);
edns_strings_delete(ct->edns_strings);
anchors_delete(ct->anchors);
@@ -5308,6 +5312,7 @@ fr_printmem(struct fast_reload_thread* fr,
mem += acl_list_get_mem(ct->acl);
mem += acl_list_get_mem(ct->acl_interface);
mem += tcl_list_get_mem(ct->tcl);
mem += arl_list_get_mem(ct->arl);
mem += edns_strings_get_mem(ct->edns_strings);
mem += anchors_get_mem(ct->anchors);
mem += sizeof(*ct->oldcfg);
@@ -5597,6 +5602,17 @@ fr_construct_from_config(struct fast_reload_thread* fr,
if(fr_poll_for_quit(fr))
return 1;
if(!(ct->arl = arl_list_create())) {
fr_construct_clear(ct);
return 0;
}
if(!arl_list_apply_cfg(ct->arl, newcfg)) {
fr_construct_clear(ct);
return 0;
}
if(fr_poll_for_quit(fr))
return 1;
if(!(ct->tsig_key_table = tsig_key_table_create())) {
fr_construct_clear(ct);
return 0;
@@ -6421,6 +6437,7 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg,
acl_list_swap_tree(daemon->acl, ct->acl);
acl_list_swap_tree(daemon->acl_interface, ct->acl_interface);
tcl_list_swap_tree(daemon->tcl, ct->tcl);
arl_list_swap_tree(daemon->arl, ct->arl);
tsig_key_table_swap_tree(daemon->env->tsig_key_table,
ct->tsig_key_table);
local_zones_swap_tree(daemon->local_zones, ct->local_zones);
+59 -12
View File
@@ -86,6 +86,7 @@
#include "util/shm_side/shm_main.h"
#include "dnscrypt/dnscrypt.h"
#include "dnstap/dtstream.h"
#include "util/allow_response_list.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
@@ -1646,44 +1647,90 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
worker_check_request(c->buffer, worker, &check_result);
if (check_result.value == RESPONSE_MESSAGE) {
/* Start accepting POISONLICIOUS Poisonlicious poisonlicious reponses */
struct reply_info *rep = NULL;
int r;
const char* tsig_name = "\x19""foobar-example-dyn-update\x00";
const char* alg = "\x0b""hmac-sha256\x00";
const char* tsig_secret =
"\x59\x2E\xD3\xD0\x84\xA8\x69\x5F\x8C\xCA\x07\xBE\x1B\xFC\x1E\x98\x74\xE7\xF6\x64\x30\x32\x10\xC6\x33\x09\x93\x94\x9D\xF1\x71\x74";
const size_t tsig_secret_len = 32;
struct arl_addr* arl_addr;
struct tsig_key* key;
if (!worker_check_response(c->buffer, worker)) {
verbose(VERB_ALGO, "Bad response");
verbose(VERB_ALGO, "bad response");
log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
return 0;
}
if((r = tsig_verify_shared(c->buffer, tsig_name, alg, tsig_secret,
tsig_secret_len, *worker->env.now))) {
verbose(VERB_ALGO, "worker tsig very of response: %s",
sldns_lookup_by_id(sldns_tsig_errors, r)?
sldns_lookup_by_id(sldns_tsig_errors, r)->name:"??");
arl_addr = arl_addr_lookup(worker->daemon->arl,
&repinfo->client_addr, repinfo->client_addrlen);
if(!arl_addr) {
verbose(VERB_ALGO, "ip not in \"allow-response:\" list");
log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
if(worker->stats.extended)
worker->stats.unwanted_queries++;
return 0;
}
if(arl_addr->tsig_key_name == NULL ||
arl_addr->tsig_key_name == TSIG_BLOCKED) {
verbose(VERB_ALGO, "ip blocked in \"allow-response:\" list");
log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
if(worker->stats.extended)
worker->stats.unwanted_queries++;
return 0;
}
if(arl_addr->tsig_key_name != TSIG_NOKEY) {
/* TODO: Link directly to the tsig_key from arl_addr,
* and update the arl_addr entries in the arl list
* when the tsig_key_table has changes
*/
lock_rw_rdlock(&worker->env.tsig_key_table->lock);
key = tsig_key_table_search_fromstr(worker->env.tsig_key_table,
arl_addr->tsig_key_name);
if (!key) {
verbose(VERB_ALGO, "tsig key to authenticate response,"
"\"%s\", not found",
arl_addr->tsig_key_name);
log_addr(VERB_CLIENT,"from",&repinfo->client_addr,
repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
if(worker->stats.extended)
worker->stats.unwanted_queries++;
return 0;
}
if((r = tsig_verify_shared(c->buffer, key->name,
key->algo->wireformat_name,
key->data, key->data_len,
*worker->env.now))) {
lock_rw_unlock(&worker->env.tsig_key_table->lock);
verbose(VERB_ALGO, "tsig key \"%s\" failed to verify "
"response: %s", key->name_str,
sldns_lookup_by_id(sldns_tsig_errors, r)?
sldns_lookup_by_id(sldns_tsig_errors, r)->name:"??");
log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
return 0;
}
lock_rw_unlock(&worker->env.tsig_key_table->lock);
}
if((r = reply_info_parse(c->buffer, worker->env.alloc, &qinfo,
&rep, worker->scratchpad, &edns))) {
verbose(VERB_ALGO, "worker parse response: %s",
verbose(VERB_ALGO, "worker failed to parse response: %s",
sldns_lookup_by_id(sldns_rcodes, r)?
sldns_lookup_by_id(sldns_rcodes, r)->name:"??");
log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
return 0;
}
log_query_info(VERB_ALGO, "storing response in cache", &qinfo);
log_addr(VERB_CLIENT,"for",&repinfo->client_addr, repinfo->client_addrlen);
dns_cache_store(&worker->env, &qinfo, rep, 0 /* is_referral */,
0 /* leeway */, 0 /* pside */,
NULL /* region */, 0 /* flags */,
*worker->env.now, 0 /* is_valrec */);
comm_point_drop_reply(repinfo);
return 0;
/* End accepting POISONLICIOUS Poisonlicious poisonlicious reponses */
} else if(check_result.value != REQUEST_OK) {
verbose(VERB_ALGO, "worker check request: bad query.");
log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen);
+1 -1
View File
@@ -839,7 +839,7 @@ server_allow_response: VAR_ALLOW_RESPONSE STRING_ARG STRING_ARG
{
OUTYY(("P(allow_response: %s %s)\n", $2, $3));
if(!cfg_str2list_insert(&cfg_parser->cfg->allow_response_list, $2, $3))
fatal_exit("out of memory adding acl");
fatal_exit("out of memory adding acl for responses");
}
;
server_outgoing_interface: VAR_OUTGOING_INTERFACE STRING_ARG