Add --dnssec-limits option.

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
Simon Kelley 2024-01-07 22:47:30 +00:00 committed by DL6ER
parent c3bc0f9972
commit fbc5713104
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
5 changed files with 52 additions and 25 deletions

View File

@ -22,10 +22,10 @@
#define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */
#define SAFE_PKTSZ 1232 /* "go anywhere" UDP packet size, see https://dnsflagday.net/2020/ */
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
#define LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */
#define LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */
#define LIMIT_NSEC3_ITERS 150 /* Max. number if iterations allowed in NSEC3 record. */
#define DNSSEC_LIMIT_WORK 40 /* Max number of queries to validate one question */
#define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */
#define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */
#define DNSSEC_LIMIT_NSEC3_ITERS 150 /* Max. number if iterations allowed in NSEC3 record. */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define SMALL_PORT_RANGE 30 /* If DNS port range is smaller than this, use different allocation. */
#define FORWARD_TEST 1000 /* try all servers every 1000 queries */

View File

@ -840,6 +840,12 @@ struct frec {
#define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */
#define LEASE_EXP_CHANGED 256 /* Lease expiry time changed */
#define LIMIT_SIG_FAIL 0
#define LIMIT_CRYPTO 1
#define LIMIT_WORK 2
#define LIMIT_NSEC3_ITERS 3
#define LIMIT_MAX 4
struct dhcp_lease {
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
@ -1249,7 +1255,7 @@ extern struct daemon {
int rr_status_sz;
int dnssec_no_time_check;
int back_to_the_future;
int limit_sig_fail, limit_crypto, limit_work, limit_nsec3_iters;
int limit[LIMIT_MAX];
#endif
struct frec *frec_list;
struct frec_src *free_frec_src;

View File

@ -479,7 +479,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
rrsetidx = sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname);
/* Now try all the sigs to try and find one which validates */
for (sig_fail_cnt = daemon->limit_sig_fail, j = 0; j <sigidx; j++)
for (sig_fail_cnt = daemon->limit[LIMIT_SIG_FAIL], j = 0; j <sigidx; j++)
{
unsigned char *psav, *sig, *digest;
int i, wire_len, sig_len;
@ -692,8 +692,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* An attacker can waste a lot of our CPU by setting up a giant DNSKEY RRSET full of failing
keys, all of which we have to try. Since many failing keys is not likely for
a legitimate domain, set a limit on how many can fail. */
if ((daemon->limit_sig_fail - (sig_fail_cnt + 1)) > (int)daemon->metrics[METRIC_SIG_FAIL_HWM])
daemon->metrics[METRIC_SIG_FAIL_HWM] = daemon->limit_sig_fail - (sig_fail_cnt + 1);
if ((daemon->limit[LIMIT_SIG_FAIL] - (sig_fail_cnt + 1)) > (int)daemon->metrics[METRIC_SIG_FAIL_HWM])
daemon->metrics[METRIC_SIG_FAIL_HWM] = daemon->limit[LIMIT_SIG_FAIL] - (sig_fail_cnt + 1);
if (dec_counter(&sig_fail_cnt, _("per-RRSet signature fails")))
return STAT_ABANDONED;
}
@ -1532,7 +1532,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
GETSHORT (iterations, p);
/* Upper-bound iterations, to avoid DoS. RFC 9276 refers. */
if (iterations > daemon->limit_nsec3_iters)
if (iterations > daemon->limit[LIMIT_NSEC3_ITERS])
return DNSSEC_FAIL_NSEC3_ITERS;
salt_len = *p++;

View File

@ -344,8 +344,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (ad_reqd)
forward->flags |= FREC_AD_QUESTION;
#ifdef HAVE_DNSSEC
forward->work_counter = daemon->limit_work;
forward->validate_counter = daemon->limit_crypto;
forward->work_counter = daemon->limit[LIMIT_WORK];
forward->validate_counter = daemon->limit[LIMIT_CRYPTO];
if (do_bit)
forward->flags |= FREC_DO_QUESTION;
#endif
@ -1404,11 +1404,11 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
}
}
if ((daemon->limit_crypto - forward->validate_counter) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit_crypto - forward->validate_counter;
if ((daemon->limit[LIMIT_CRYPTO] - forward->validate_counter) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - forward->validate_counter;
if ((daemon->limit_work - forward->work_counter) > (int)daemon->metrics[METRIC_WORK_HWM])
daemon->metrics[METRIC_WORK_HWM] = daemon->limit_work - forward->work_counter;
if ((daemon->limit[LIMIT_WORK] - forward->work_counter) > (int)daemon->metrics[METRIC_WORK_HWM])
daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - forward->work_counter;
#endif
if (option_bool(OPT_NO_REBIND))
@ -2554,8 +2554,8 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (master->flags & SERV_DO_DNSSEC))
{
int keycount = daemon->limit_work; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int validatecount = daemon->limit_crypto;
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int validatecount = daemon->limit[LIMIT_CRYPTO];
int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname,
serv, have_mark, mark, &keycount, &validatecount);
char *result, *domain = "result";
@ -2584,11 +2584,11 @@ unsigned char *tcp_request(int confd, time_t now,
log_query(F_SECSTAT, domain, &a, result, 0);
if ((daemon->limit_crypto - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit_crypto - validatecount;
if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - validatecount;
if ((daemon->limit_work - keycount) > (int)daemon->metrics[METRIC_WORK_HWM])
daemon->metrics[METRIC_WORK_HWM] = daemon->limit_work - keycount;
if ((daemon->limit[LIMIT_WORK] - keycount) > (int)daemon->metrics[METRIC_WORK_HWM])
daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - keycount;
}
#endif

View File

@ -195,6 +195,7 @@ struct myoption {
#define LOPT_NO_DHCP6 382
#define LOPT_NO_DHCP4 383
#define LOPT_MAX_PROCS 384
#define LOPT_DNSSEC_LIMITS 385
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@ -368,6 +369,7 @@ static const struct myoption opts[] =
{ "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
{ "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
{ "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
{ "dnssec-limits", 1, 0, LOPT_DNSSEC_LIMITS },
{ "dhcp-relay", 1, 0, LOPT_RELAY },
{ "ra-param", 1, 0, LOPT_RA_PARAM },
{ "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
@ -572,6 +574,7 @@ static struct {
{ LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
{ LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
{ LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
{ LOPT_DNSSEC_LIMITS, ARG_ONE, "<limit>,..", gettext_noop("Set resource limits for DNSSEC validation"), NULL },
{ LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL },
{ LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
{ LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
@ -5262,6 +5265,24 @@ err:
}
#ifdef HAVE_DNSSEC
case LOPT_DNSSEC_LIMITS:
{
int lim, val;
for (lim = LIMIT_SIG_FAIL; arg && lim < LIMIT_MAX ; lim++, arg = comma)
{
comma = split(arg);
if (!atoi_check(arg, &val))
ret_err(gen_err);
if (val != 0)
daemon->limit[lim] = val;
}
break;
}
case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
daemon->timestamp_file = opt_string_alloc(arg);
break;
@ -5874,10 +5895,10 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->host_index = SRC_AH;
daemon->max_procs = MAX_PROCS;
#ifdef HAVE_DNSSEC
daemon->limit_sig_fail = LIMIT_SIG_FAIL;
daemon->limit_crypto = LIMIT_CRYPTO;
daemon->limit_work = DNSSEC_WORK;
daemon->limit_nsec3_iters = LIMIT_NSEC3_ITERS;
daemon->limit[LIMIT_SIG_FAIL] = DNSSEC_LIMIT_SIG_FAIL;
daemon->limit[LIMIT_CRYPTO] = DNSSEC_LIMIT_CRYPTO;
daemon->limit[LIMIT_WORK] = DNSSEC_LIMIT_WORK;
daemon->limit[LIMIT_NSEC3_ITERS] = DNSSEC_LIMIT_NSEC3_ITERS;
#endif
/* See comment above make_servers(). Optimises server-read code. */