mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-11 12:50:00 -04:00
Make the dns_validator validations asynchronous and limit it
Instead of running all the cryptographic validation in a tight loop, spread it out into multiple event loop "ticks", but moving every single validation into own isc_async_run() asynchronous event. Move the cryptographic operations - both verification and DNSKEY selection - to the offloaded threads (isc_work_enqueue), this further limits the time we spend doing expensive operations on the event loops that should be fast. Limit the impact of invalid or malicious RRSets that contain crafted records causing the dns_validator to do many validations per single fetch by adding a cap on the maximum number of validations and maximum number of validation failures that can happen before the resolving fails.
This commit is contained in:
parent
1e40c0b124
commit
15096aefdf
11 changed files with 901 additions and 499 deletions
|
|
@ -5455,6 +5455,21 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
|||
INSIST(result == ISC_R_SUCCESS);
|
||||
dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj));
|
||||
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "max-validations-per-fetch", &obj);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
dns_resolver_setmaxvalidations(view->resolver,
|
||||
cfg_obj_asuint32(obj));
|
||||
}
|
||||
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "max-validation-failures-per-fetch",
|
||||
&obj);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
dns_resolver_setmaxvalidationfails(view->resolver,
|
||||
cfg_obj_asuint32(obj));
|
||||
}
|
||||
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "fetches-per-zone", &obj);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
|
|
|
|||
|
|
@ -3691,6 +3691,21 @@ system.
|
|||
set to zero, :any:`max-clients-per-query` no longer applies and there is no
|
||||
upper bound, other than that imposed by :any:`recursive-clients`.
|
||||
|
||||
.. namedconf:statement:: max-validations-per-fetch
|
||||
:tags: server
|
||||
:short: Set the maximum number of DNSSEC validations that can happen in single fetch
|
||||
|
||||
This is an **experimental** setting to set the maximum number of DNSSEC
|
||||
validations that can happen in a single resolver fetch. The default is 16.
|
||||
|
||||
.. namedconf:statement:: max-validation-failures-per-fetch
|
||||
:tags: server
|
||||
:short: Set the maximum number of DNSSEC validation failures that can happen in single fetch
|
||||
|
||||
This is an **experimental** setting to set the maximum number of DNSSEC
|
||||
validation failures that can happen in a single resolver fetch. The default
|
||||
is 1.
|
||||
|
||||
.. namedconf:statement:: fetches-per-zone
|
||||
:tags: server, query
|
||||
:short: Sets the maximum number of simultaneous iterative queries allowed to any one domain before the server blocks new queries for data in or beneath that zone.
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@ options {
|
|||
max-transfer-time-in <integer>;
|
||||
max-transfer-time-out <integer>;
|
||||
max-udp-size <integer>;
|
||||
max-validation-failures-per-fetch <integer>; // experimental
|
||||
max-validations-per-fetch <integer>; // experimental
|
||||
max-zone-ttl ( unlimited | <duration> ); // deprecated
|
||||
memstatistics <boolean>;
|
||||
memstatistics-file <quoted_string>;
|
||||
|
|
@ -469,6 +471,8 @@ view <string> [ <class> ] {
|
|||
max-transfer-time-in <integer>;
|
||||
max-transfer-time-out <integer>;
|
||||
max-udp-size <integer>;
|
||||
max-validation-failures-per-fetch <integer>; // experimental
|
||||
max-validations-per-fetch <integer>; // experimental
|
||||
max-zone-ttl ( unlimited | <duration> ); // deprecated
|
||||
message-compression <boolean>;
|
||||
min-cache-ttl <duration>;
|
||||
|
|
|
|||
|
|
@ -164,7 +164,8 @@ computeid(dst_key_t *key);
|
|||
static isc_result_t
|
||||
frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
|
||||
unsigned int protocol, dns_rdataclass_t rdclass,
|
||||
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
|
||||
isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
|
||||
dst_key_t **keyp);
|
||||
|
||||
static isc_result_t
|
||||
algorithm_status(unsigned int alg);
|
||||
|
|
@ -750,6 +751,13 @@ dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
|
|||
isc_result_t
|
||||
dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
|
||||
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) {
|
||||
return (dst_key_fromdns_ex(name, rdclass, source, mctx, false, keyp));
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dst_key_fromdns_ex(const dns_name_t *name, dns_rdataclass_t rdclass,
|
||||
isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
|
||||
dst_key_t **keyp) {
|
||||
uint8_t alg, proto;
|
||||
uint32_t flags, extflags;
|
||||
dst_key_t *key = NULL;
|
||||
|
|
@ -780,7 +788,7 @@ dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
|
|||
}
|
||||
|
||||
result = frombuffer(name, alg, flags, proto, rdclass, source, mctx,
|
||||
&key);
|
||||
no_rdata, &key);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
|
@ -801,7 +809,7 @@ dst_key_frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
|
|||
REQUIRE(dst_initialized);
|
||||
|
||||
result = frombuffer(name, alg, flags, protocol, rdclass, source, mctx,
|
||||
&key);
|
||||
false, &key);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
|
@ -2302,7 +2310,8 @@ computeid(dst_key_t *key) {
|
|||
static isc_result_t
|
||||
frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
|
||||
unsigned int protocol, dns_rdataclass_t rdclass,
|
||||
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) {
|
||||
isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
|
||||
dst_key_t **keyp) {
|
||||
dst_key_t *key;
|
||||
isc_result_t ret;
|
||||
|
||||
|
|
@ -2324,10 +2333,12 @@ frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
|
|||
return (DST_R_UNSUPPORTEDALG);
|
||||
}
|
||||
|
||||
ret = key->func->fromdns(key, source);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
dst_key_free(&key);
|
||||
return (ret);
|
||||
if (!no_rdata) {
|
||||
ret = key->func->fromdns(key, source);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
dst_key_free(&key);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -578,6 +578,14 @@ dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp);
|
|||
* \li resolver to be valid.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_resolver_setmaxvalidations(dns_resolver_t *resolver, uint32_t max);
|
||||
void
|
||||
dns_resolver_setmaxvalidationfails(dns_resolver_t *resolver, uint32_t max);
|
||||
/*%
|
||||
* Set maximum numbers of validations and maximum validation failures per fetch.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth);
|
||||
unsigned int
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
#include <isc/refcount.h>
|
||||
|
||||
#include <dns/fixedname.h>
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdataset.h>
|
||||
#include <dns/rdatastruct.h> /* for dns_rdata_rrsig_t */
|
||||
#include <dns/types.h>
|
||||
|
|
@ -144,6 +145,13 @@ struct dns_validator {
|
|||
unsigned int authcount;
|
||||
unsigned int authfail;
|
||||
isc_stdtime_t start;
|
||||
|
||||
bool digest_sha1;
|
||||
bool supported_algorithm;
|
||||
dns_rdata_t rdata;
|
||||
bool resume;
|
||||
uint32_t *nvalidations;
|
||||
uint32_t *nfails;
|
||||
};
|
||||
|
||||
/*%
|
||||
|
|
@ -161,6 +169,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
|
|||
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
|
||||
dns_message_t *message, unsigned int options,
|
||||
isc_loop_t *loop, isc_job_cb cb, void *arg,
|
||||
uint32_t *nvalidations, uint32_t *nfails,
|
||||
dns_validator_t **validatorp);
|
||||
/*%<
|
||||
* Start a DNSSEC validation.
|
||||
|
|
|
|||
|
|
@ -482,6 +482,10 @@ dst_key_tofile(const dst_key_t *key, int type, const char *directory);
|
|||
*/
|
||||
|
||||
isc_result_t
|
||||
dst_key_fromdns_ex(const dns_name_t *name, dns_rdataclass_t rdclass,
|
||||
isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
|
||||
dst_key_t **keyp);
|
||||
isc_result_t
|
||||
dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
|
||||
isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
|
||||
/*%<
|
||||
|
|
|
|||
|
|
@ -178,6 +178,16 @@
|
|||
*/
|
||||
#define MINIMUM_QUERY_TIMEOUT (MAX_SINGLE_QUERY_TIMEOUT + 1000U)
|
||||
|
||||
/*
|
||||
* The default maximum number of validations and validation failures per-fetch
|
||||
*/
|
||||
#ifndef DEFAULT_MAX_VALIDATIONS
|
||||
#define DEFAULT_MAX_VALIDATIONS 16
|
||||
#endif
|
||||
#ifndef DEFAULT_MAX_VALIDATION_FAILURES
|
||||
#define DEFAULT_MAX_VALIDATION_FAILURES 1
|
||||
#endif
|
||||
|
||||
/* The default time in seconds for the whole query to live. */
|
||||
#ifndef DEFAULT_QUERY_TIMEOUT
|
||||
#define DEFAULT_QUERY_TIMEOUT MINIMUM_QUERY_TIMEOUT
|
||||
|
|
@ -457,6 +467,9 @@ struct fetchctx {
|
|||
dns_adbaddrinfo_t *addrinfo;
|
||||
unsigned int depth;
|
||||
char clientstr[ISC_SOCKADDR_FORMATSIZE];
|
||||
|
||||
uint32_t nvalidations;
|
||||
uint32_t nfails;
|
||||
};
|
||||
|
||||
#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!')
|
||||
|
|
@ -567,6 +580,9 @@ struct dns_resolver {
|
|||
atomic_bool exiting;
|
||||
atomic_bool priming;
|
||||
|
||||
atomic_uint_fast32_t maxvalidations;
|
||||
atomic_uint_fast32_t maxvalidationfails;
|
||||
|
||||
/* Locked by lock. */
|
||||
unsigned int spillat; /* clients-per-query */
|
||||
|
||||
|
|
@ -961,7 +977,8 @@ valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
|
|||
|
||||
result = dns_validator_create(
|
||||
fctx->res->view, name, type, rdataset, sigrdataset, message,
|
||||
valoptions, fctx->loop, validated, valarg, &validator);
|
||||
valoptions, fctx->loop, validated, valarg, &fctx->nvalidations,
|
||||
&fctx->nfails, &validator);
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
inc_stats(fctx->res, dns_resstatscounter_val);
|
||||
if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
|
||||
|
|
@ -4518,6 +4535,8 @@ fctx_create(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
|
|||
.fwdpolicy = dns_fwdpolicy_none,
|
||||
.result = ISC_R_FAILURE,
|
||||
.loop = loop,
|
||||
.nvalidations = atomic_load_relaxed(&res->maxvalidations),
|
||||
.nfails = atomic_load_relaxed(&res->maxvalidationfails),
|
||||
};
|
||||
|
||||
isc_mem_attach(mctx, &fctx->mctx);
|
||||
|
|
@ -9960,6 +9979,8 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, isc_nm_t *nm,
|
|||
.maxqueries = DEFAULT_MAX_QUERIES,
|
||||
.alternates = ISC_LIST_INITIALIZER,
|
||||
.nloops = isc_loopmgr_nloops(loopmgr),
|
||||
.maxvalidations = DEFAULT_MAX_VALIDATIONS,
|
||||
.maxvalidationfails = DEFAULT_MAX_VALIDATION_FAILURES,
|
||||
};
|
||||
|
||||
RTRACE("create");
|
||||
|
|
@ -10925,6 +10946,18 @@ dns_resolver_settimeout(dns_resolver_t *resolver, unsigned int timeout) {
|
|||
resolver->query_timeout = timeout;
|
||||
}
|
||||
|
||||
void
|
||||
dns_resolver_setmaxvalidations(dns_resolver_t *resolver, uint32_t max) {
|
||||
REQUIRE(VALID_RESOLVER(resolver));
|
||||
atomic_store(&resolver->maxvalidations, max);
|
||||
}
|
||||
|
||||
void
|
||||
dns_resolver_setmaxvalidationfails(dns_resolver_t *resolver, uint32_t max) {
|
||||
REQUIRE(VALID_RESOLVER(resolver));
|
||||
atomic_store(&resolver->maxvalidationfails, max);
|
||||
}
|
||||
|
||||
void
|
||||
dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth) {
|
||||
REQUIRE(VALID_RESOLVER(resolver));
|
||||
|
|
|
|||
1277
lib/dns/validator.c
1277
lib/dns/validator.c
File diff suppressed because it is too large
Load diff
|
|
@ -138,6 +138,8 @@ static void
|
|||
shutdown_trigger_close_cb(uv_handle_t *handle) {
|
||||
isc_loop_t *loop = uv_handle_get_data(handle);
|
||||
|
||||
loop->shuttingdown = true;
|
||||
|
||||
isc_loop_detach(&loop);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2103,6 +2103,10 @@ static cfg_clausedef_t view_clauses[] = {
|
|||
{ "max-recursion-queries", &cfg_type_uint32, 0 },
|
||||
{ "max-stale-ttl", &cfg_type_duration, 0 },
|
||||
{ "max-udp-size", &cfg_type_uint32, 0 },
|
||||
{ "max-validations-per-fetch", &cfg_type_uint32,
|
||||
CFG_CLAUSEFLAG_EXPERIMENTAL },
|
||||
{ "max-validation-failures-per-fetch", &cfg_type_uint32,
|
||||
CFG_CLAUSEFLAG_EXPERIMENTAL },
|
||||
{ "message-compression", &cfg_type_boolean, 0 },
|
||||
{ "min-cache-ttl", &cfg_type_duration, 0 },
|
||||
{ "min-ncache-ttl", &cfg_type_duration, 0 },
|
||||
|
|
|
|||
Loading…
Reference in a new issue