Compare commits

...
Author SHA1 Message Date
W.C.A. Wijngaards 13b6717f17 - Fix CVE-2026-85501, Retrap: Novel Vulnerabilities to launch
Algorithmic Complexity Attacks on DNSSEC. Thanks to Zuyao Xu and
  Xiang Li from Nankai University, AOSP Lab for the report. In
  addition, thanks to Qifan Zhang from Palo Alto Networks for a
  complimentary report.
2026-09-16 09:30:25 +02:00
W.C.A. Wijngaards 0d4a6a63dd - Fix CVE-2026-82717, CNAME synthesis could lead to heap corruption.
Thanks to Ben Morris from Anthropic for the report.
2026-09-16 09:29:32 +02:00
W.C.A. Wijngaards bd71e3b8a6 - Fix CVE-2026-77860, 'serve-expired' can bypass Unbound 'wait-limit'.
Thanks to Xuanchao Xie, Lutong Chen, and Kaiping Xue from the
  University of Science and Technology of China (USTC) for the
  report.
2026-09-16 09:29:04 +02:00
W.C.A. Wijngaards 3a6ba0da81 - Unit test for CVE-2026-81642 fix. 2026-09-16 09:28:11 +02:00
W.C.A. Wijngaards 3d65973d38 - Fix CVE-2026-81634, Possible heap buffer overflow during DNSSEC
canonicalization. Thanks to Vlatko Kosturjak with Marlink Cyber,
  for the report.
2026-09-16 09:27:17 +02:00
W.C.A. Wijngaards 565651cd02 - Fix CVE-2026-77955, Possible ZONEMD verification bypass window.
Thanks to Yuqi Qiu and Xiang Li from Nankai University, AOSP Lab,
  for the report. In addition, thanks to Qifan Zhang from Palo Alto
  Networks for also reporting this issue.
2026-09-16 09:26:00 +02:00
W.C.A. Wijngaards 7914901915 - Fix CVE-2026-78227, Use-after-free in DoQ stream output buffer on
reset re-transmission. Thanks to Yuqi Qiu and Xiang Li from Nankai
  University, AOSP Lab for the report.
2026-09-16 09:25:05 +02:00
W.C.A. Wijngaards eba3d35ad4 - Fix CVE-2026-82720, Use-after-free in DoH stream cleanup code path.
Thanks to Yuqi Qiu and Xiang Li from Nankai University, AOSP Lab,
  for the report.
2026-09-16 09:24:29 +02:00
W.C.A. Wijngaards 8c2e0fd6cc - Fix CVE-2026-81642, Heap buffer overflow and possible Remote Code
Execution when digesting DNSKEY. Thanks to Yuqi Qiu and Xiang Li
  from Nankai University, AOSP Lab for the report.
2026-09-16 09:18:49 +02:00
W.C.A. Wijngaards e619ead2db - Fix CVE-2026-80225, Possible degradation of service from continuous
queries on the same TCP/DoT connection. Thanks to Qifan Zhang from
  Palo Alto Networks for the report.
2026-09-16 09:16:29 +02:00
W.C.A. Wijngaards eb86d9bc50 Set version to 1.26.1 2026-09-16 09:11:14 +02:00
41 changed files with 1036 additions and 301 deletions
Vendored
+13 -12
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for unbound 1.26.0.
# Generated by GNU Autoconf 2.71 for unbound 1.26.1.
#
# Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
#
@@ -622,8 +622,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='unbound'
PACKAGE_TARNAME='unbound'
PACKAGE_VERSION='1.26.0'
PACKAGE_STRING='unbound 1.26.0'
PACKAGE_VERSION='1.26.1'
PACKAGE_STRING='unbound 1.26.1'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL=''
@@ -1513,7 +1513,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures unbound 1.26.0 to adapt to many kinds of systems.
\`configure' configures unbound 1.26.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1579,7 +1579,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of unbound 1.26.0:";;
short | recursive ) echo "Configuration of unbound 1.26.1:";;
esac
cat <<\_ACEOF
@@ -1832,7 +1832,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
unbound configure 1.26.0
unbound configure 1.26.1
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2489,7 +2489,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by unbound $as_me 1.26.0, which was
It was created by unbound $as_me 1.26.1, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -3253,11 +3253,11 @@ UNBOUND_VERSION_MAJOR=1
UNBOUND_VERSION_MINOR=26
UNBOUND_VERSION_MICRO=0
UNBOUND_VERSION_MICRO=1
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=39
LIBUNBOUND_REVISION=40
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -3364,6 +3364,7 @@ LIBUNBOUND_AGE=1
# 1.25.1 had 9:37:1
# 1.25.2 had 9:38:1
# 1.26.0 had 9:39:1
# 1.26.1 had 9:40:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -25711,7 +25712,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h
version=1.26.0
version=1.26.1
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5
printf %s "checking for build time... " >&6; }
@@ -26241,7 +26242,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by unbound $as_me 1.26.0, which was
This file was extended by unbound $as_me 1.26.1, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -26309,7 +26310,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
unbound config.status 1.26.0
unbound config.status 1.26.1
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
+3 -2
View File
@@ -12,14 +12,14 @@ sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
m4_define([VERSION_MINOR],[26])
m4_define([VERSION_MICRO],[0])
m4_define([VERSION_MICRO],[1])
AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound])
AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR])
AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=39
LIBUNBOUND_REVISION=40
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -126,6 +126,7 @@ LIBUNBOUND_AGE=1
# 1.25.1 had 9:37:1
# 1.25.2 had 9:38:1
# 1.26.0 had 9:39:1
# 1.26.1 had 9:40:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
+2
View File
@@ -6590,6 +6590,8 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg,
COPY_VAR_ptr(ipset_name_v6);
#endif
COPY_VAR_int(ede);
COPY_VAR_int(val_validation_attempts);
COPY_VAR_int(val_hash_attempts);
COPY_VAR_int(iter_scrub_ns);
COPY_VAR_int(iter_scrub_cname);
COPY_VAR_int(iter_scrub_rrsig);
+1 -1
View File
@@ -863,7 +863,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(rep->security == sec_status_secure) {
if(reply_all_rrsets_secure(rep)) {
if(reply_an_ns_rrsets_secure(rep)) {
*is_secure_answer = 1;
} else {
if(must_validate) {
+7 -1
View File
@@ -203,6 +203,12 @@ server:
# protects against poison attempts.
# iter-scrub-promiscuous: yes
# Limit on number of DNSSEC validation attempts for a query.
# val-validation-attempts: 32
# Limit on number of DNSSEC hash attempts for a query.
# val-hash-attempts: 32
# msec for waiting for an unknown server to reply. Increase if you
# are behind a slow satellite link, to eg. 1128.
# unknown-server-time-limit: 376
@@ -728,7 +734,7 @@ server:
# non-secure data. Useful to shield the users of this validator from
# potential bogus data in the additional section. All unsigned data
# in the additional section is removed from secure messages.
# val-clean-additional: yes
# val-clean-additional: no
# Turn permissive mode on to permit bogus messages. Thus, messages
# for which security checks failed will be returned to clients,
+21 -1
View File
@@ -2320,7 +2320,7 @@ These options are part of the ``server:`` section.
Use this setting to protect the users that rely on this validator for
authentication from potentially bad data in the additional section.
Default: yes
Default: no
@@UAHL@unbound.conf@val-log-level@@: *<number>*
@@ -3358,6 +3358,26 @@ These options are part of the ``server:`` section.
Default: yes
@@UAHL@unbound.conf@val-validation-attempts@@: *<number>*
Limit on the number of DNSSEC validation attempts for a query.
This protects against too large numbers of cryptographic operations,
like for a deep delegation chain.
This counts attempts to validate RRSIGs.
When it is exceeded, the query fails.
Default: 32
@@UAHL@unbound.conf@val-hash-attempts@@: *<number>*
Limit on the number of DNSSEC hash attempts for a query.
This protects against too large numbers of cryptographic operations,
like for a deep delegation chain.
This counts DS hash attempts to match DNSKEYs.
When it is exceeded, the query fails.
Default: 32
@@UAHL@unbound.conf@fast-server-permil@@: *<number>*
Specify how many times out of 1000 to pick from the set of fastest servers.
0 turns the feature off.
+19 -1
View File
@@ -294,7 +294,14 @@ synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias,
if(ttl_t > MAX_TTL) ttl_t = MAX_TTL;
ttl = (uint32_t)ttl_t;
sldns_write_uint32(cn->rr_first->ttl_data, ttl);
sldns_write_uint32(rrset->rr_first->ttl_data, ttl);
/* Do NOT write the clamp back into the packet buffer:
* parse_packet already sized every name from the original
* bytes and rdata_copy re-walks them trusting those sizes;
* mutating packet bytes between the walks breaks that
* invariant (compression pointers can target these TTL
* bytes). The DNAME rrset receives the same clamp at store
* time in rdata_copy, so the DNAME and the synthesized
* CNAME still carry equal TTLs in the cache. */
}
sldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen);
memmove(cn->rr_first->ttl_data+6, alias, aliaslen);
@@ -648,6 +655,9 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
if(rrset->type == LDNS_RR_TYPE_NS &&
rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
} else if(rrset->type == LDNS_RR_TYPE_DS &&
rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
}
prev = rrset;
rrset = rrset->rrset_all_next;
@@ -667,6 +677,9 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
if(rrset->type == LDNS_RR_TYPE_NS &&
rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
} else if(rrset->type == LDNS_RR_TYPE_DS &&
rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
}
/* Mark the additional names from relevant rrset as OK. */
@@ -791,6 +804,11 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
}
}
} else if(rrset->type==LDNS_RR_TYPE_DS) {
if(rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset,
env->cfg->iter_scrub_ns);
}
}
/* if this is type DS and we query for type DS we just got
* a referral answer for our type DS query, fix packet */
+177 -67
View File
@@ -392,6 +392,20 @@ auth_data_del(rbnode_type* n, void* ATTR_UNUSED(arg))
auth_data_delete(z);
}
/** delete chunklist */
static void
auth_chunk_list_delete(struct auth_chunk* first)
{
struct auth_chunk* c, *cn;
c = first;
while(c) {
cn = c->next;
free(c->data);
free(c);
c = cn;
}
}
/** delete an auth zone structure (tree remove must be done elsewhere) */
static void
auth_zone_delete(struct auth_zone* z, struct auth_zones* az)
@@ -413,6 +427,7 @@ auth_zone_delete(struct auth_zone* z, struct auth_zones* az)
}
if(z->rpz)
rpz_delete(z->rpz);
auth_chunk_list_delete(z->perform_write_chunk_list);
free(z->name);
free(z->zonefile);
free(z);
@@ -2369,14 +2384,7 @@ static void
auth_chunks_delete(struct auth_transfer* at)
{
if(at->chunks_first) {
struct auth_chunk* c, *cn;
c = at->chunks_first;
while(c) {
cn = c->next;
free(c->data);
free(c);
c = cn;
}
auth_chunk_list_delete(at->chunks_first);
}
at->chunks_first = NULL;
at->chunks_last = NULL;
@@ -3593,7 +3601,13 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo,
*fallback = 1;
return 0;
}
if(z->zone_expired) {
if(z->zone_expired || (z->zonemd_check && z->zonemd_callback_env)) {
/* Do not serve from a zonemd-check zone while its ZONEMD
* verification is still pending: the content is not yet known
* to pass the configured check. The pending marker
* (zonemd_callback_env) is set under z->lock when the async
* lookup is spawned and cleared by the callback under z->lock,
* so this test is race-free. */
*fallback = z->fallback_enabled;
lock_rw_unlock(&z->lock);
return 0;
@@ -3695,7 +3709,10 @@ int auth_zones_downstream_answer(struct auth_zones* az, struct module_env* env,
lock_rw_unlock(&z->lock);
return 0;
}
if(z->zone_expired) {
if(z->zone_expired || (z->zonemd_check && z->zonemd_callback_env)) {
/* see auth_zones_lookup: a pending ZONEMD verification is
* treated like expiry - the zone content is not yet known
* to pass the configured check. */
if(z->fallback_enabled) {
lock_rw_unlock(&z->lock);
return 0;
@@ -5299,7 +5316,7 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
/** write http chunks to zonefile to create downloaded file */
static int
auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname)
auth_zone_write_chunks(struct auth_chunk* chunk_list, const char* fname)
{
FILE* out;
struct auth_chunk* p;
@@ -5308,7 +5325,7 @@ auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname)
log_err("could not open %s: %s", fname, strerror(errno));
return 0;
}
for(p = xfr->task_transfer->chunks_first; p ; p = p->next) {
for(p = chunk_list; p ; p = p->next) {
if(!write_out(out, (char*)p->data, p->len)) {
log_err("could not write http download to %s", fname);
fclose(out);
@@ -5319,14 +5336,91 @@ auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname)
return 1;
}
/** write to zonefile after zone has been updated */
/** write to zonefile after zone has been updated, z has rdlock by caller. */
static void
xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)
zone_write_after_update(struct auth_zone* z, struct module_env* env,
struct auth_chunk* chunk_list)
{
struct config_file* cfg = env->cfg;
struct auth_zone* z;
char tmpfile[1024];
char* zfilename;
if(z->zonefile == NULL || z->zonefile[0] == 0) {
/* no write needed, no zonefile set */
auth_chunk_list_delete(chunk_list);
return;
}
zfilename = z->zonefile;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(zfilename,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[LDNS_MAX_DOMAINLEN];
dname_str(z->name, nm);
verbose(VERB_ALGO, "write zonefile %s for %s", zfilename, nm);
}
/* write to tempfile first */
if((size_t)strlen(zfilename) + 16 > sizeof(tmpfile)) {
verbose(VERB_ALGO, "tmpfilename too long, cannot update "
" zonefile %s", zfilename);
auth_chunk_list_delete(chunk_list);
return;
}
snprintf(tmpfile, sizeof(tmpfile), "%s.tmp%u", zfilename,
(unsigned)getpid());
if(chunk_list) {
/* use the stored chunk list to write them */
if(!auth_zone_write_chunks(chunk_list, tmpfile)) {
unlink(tmpfile);
auth_chunk_list_delete(chunk_list);
return;
}
auth_chunk_list_delete(chunk_list);
} else if(!auth_zone_write_file(z, tmpfile)) {
unlink(tmpfile);
return;
}
#ifdef UB_ON_WINDOWS
(void)unlink(zfilename); /* windows does not replace file with rename() */
#endif
if(rename(tmpfile, zfilename) < 0) {
log_err("could not rename(%s, %s): %s", tmpfile, zfilename,
strerror(errno));
unlink(tmpfile);
return;
}
}
/** write to zonefile after zone has updated, reacquires z readlock. */
static void
zone_write_after_update_reacq(uint8_t* bakname, size_t baknamelen,
uint16_t bakdclass, struct module_env* env,
struct auth_chunk* chunk_list)
{
struct auth_zone* z;
/* get lock again, so it is a readlock and concurrently queries
* can be answered */
lock_rw_rdlock(&env->auth_zones->lock);
z = auth_zone_find(env->auth_zones, bakname, baknamelen, bakdclass);
if(!z) {
lock_rw_unlock(&env->auth_zones->lock);
/* the zone is gone, ignore xfr results */
return;
}
lock_rw_rdlock(&z->lock);
lock_rw_unlock(&env->auth_zones->lock);
zone_write_after_update(z, env, chunk_list);
lock_rw_unlock(&z->lock);
}
/** write to zonefile after zone has been updated */
static void
xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env,
struct auth_chunk* chunk_list)
{
struct auth_zone* z;
lock_basic_unlock(&xfr->lock);
/* get lock again, so it is a readlock and concurrently queries
@@ -5344,52 +5438,7 @@ xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&env->auth_zones->lock);
if(z->zonefile == NULL || z->zonefile[0] == 0) {
lock_rw_unlock(&z->lock);
/* no write needed, no zonefile set */
return;
}
zfilename = z->zonefile;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(zfilename,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[LDNS_MAX_DOMAINLEN];
dname_str(z->name, nm);
verbose(VERB_ALGO, "write zonefile %s for %s", zfilename, nm);
}
/* write to tempfile first */
if((size_t)strlen(zfilename) + 16 > sizeof(tmpfile)) {
verbose(VERB_ALGO, "tmpfilename too long, cannot update "
" zonefile %s", zfilename);
lock_rw_unlock(&z->lock);
return;
}
snprintf(tmpfile, sizeof(tmpfile), "%s.tmp%u", zfilename,
(unsigned)getpid());
if(xfr->task_transfer->master->http) {
/* use the stored chunk list to write them */
if(!auth_zone_write_chunks(xfr, tmpfile)) {
unlink(tmpfile);
lock_rw_unlock(&z->lock);
return;
}
} else if(!auth_zone_write_file(z, tmpfile)) {
unlink(tmpfile);
lock_rw_unlock(&z->lock);
return;
}
#ifdef UB_ON_WINDOWS
(void)unlink(zfilename); /* windows does not replace file with rename() */
#endif
if(rename(tmpfile, zfilename) < 0) {
log_err("could not rename(%s, %s): %s", tmpfile, zfilename,
strerror(errno));
unlink(tmpfile);
lock_rw_unlock(&z->lock);
return;
}
zone_write_after_update(z, env, chunk_list);
lock_rw_unlock(&z->lock);
}
@@ -5422,6 +5471,8 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
int* ixfr_fail)
{
struct auth_zone* z;
int zonemd_in_progress;
struct auth_chunk* current_chunk_list = NULL;
/* obtain locks and structures */
lock_basic_unlock(&xfr->lock);
@@ -5505,6 +5556,25 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
if(z->rpz)
rpz_finish_config(z->rpz);
if(z->zonemd_check && z->zonemd_callback_env) {
zonemd_in_progress = 1;
z->zonemd_callback_perform_write = 1;
auth_chunk_list_delete(z->perform_write_chunk_list);
z->perform_write_chunk_list = NULL;
if(xfr->task_transfer->master->http) {
z->perform_write_chunk_list = xfr->task_transfer->chunks_first;
xfr->task_transfer->chunks_first = NULL;
auth_chunks_delete(xfr->task_transfer);
}
} else {
zonemd_in_progress = 0;
z->zonemd_callback_perform_write = 0;
if(xfr->task_transfer->master->http) {
current_chunk_list = xfr->task_transfer->chunks_first;
xfr->task_transfer->chunks_first = NULL;
auth_chunks_delete(xfr->task_transfer);
}
}
/* unlock */
lock_rw_unlock(&z->lock);
@@ -5515,7 +5585,9 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
(unsigned)xfr->serial);
}
/* see if we need to write to a zonefile */
xfr_write_after_update(xfr, env);
if(!zonemd_in_progress) {
xfr_write_after_update(xfr, env, current_chunk_list);
}
return 1;
}
@@ -8162,7 +8234,8 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
"zonemd: verify %s RRset with DNSKEY", typestr);
}
sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL,
LDNS_SECTION_ANSWER, NULL, &verified, reasonbuf, reasonlen);
LDNS_SECTION_ANSWER, NULL, NULL, &verified, reasonbuf,
reasonlen);
if(sec == sec_status_secure) {
return 1;
}
@@ -8511,8 +8584,8 @@ zonemd_get_dnskey_from_anchor(struct auth_zone* z, struct module_env* env,
auth_zone_log(z->name, VERB_QUERY,
"zonemd: verify DNSKEY RRset with trust anchor");
sec = val_verify_DNSKEY_with_TA(env, ve, keystorage, anchor->ds_rrset,
anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL, reasonbuf,
reasonlen);
anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL, NULL,
reasonbuf, reasonlen);
regional_free_all(env->scratch);
if(sec == sec_status_secure) {
/* success */
@@ -8572,7 +8645,7 @@ auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z,
keystorage->rk.rrset_class = htons(z->dclass);
auth_zone_log(z->name, VERB_QUERY, "zonemd: verify zone DNSKEY with DS");
sec = val_verify_DNSKEY_with_DS(env, ve, keystorage, ds, sigalg,
why_bogus, NULL, NULL, reasonbuf, reasonlen);
why_bogus, NULL, NULL, NULL, reasonbuf, reasonlen);
regional_free_all(env->scratch);
if(sec == sec_status_secure) {
/* success */
@@ -8601,9 +8674,13 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
char reasonbuf[256];
char* reason = NULL, *ds_bogus = NULL, *typestr="DNSKEY";
struct ub_packed_rrset_key* dnskey = NULL, *ds = NULL;
int is_insecure = 0, downprot;
int is_insecure = 0, downprot, perform_write = 0;
struct ub_packed_rrset_key keystorage;
uint8_t sigalg[ALGO_NEEDS_MAX+1];
uint8_t bakname[LDNS_MAX_DOMAINLEN];
size_t baknamelen;
uint16_t bakdclass;
struct auth_chunk* chunk_list = NULL;
lock_rw_wrlock(&z->lock);
env = z->zonemd_callback_env;
@@ -8726,7 +8803,37 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
auth_zone_verify_zonemd_with_key(z, env, &env->mesh->mods, dnskey,
is_insecure, NULL, downprot?sigalg:NULL);
regional_free_all(env->scratch);
if(z->zonemd_callback_perform_write) {
if(!z->zone_expired) {
/* Write to zonefile if the ZONEMD is okay. */
perform_write = 1;
/* copy the key to lookup the z structure.
* The new lookup is readonly so concurrent
* queries can continue. */
if(z->namelen > sizeof(bakname)) {
perform_write = 0;
auth_chunk_list_delete(z->perform_write_chunk_list);
z->perform_write_chunk_list = NULL;
} else {
memcpy(bakname, z->name, z->namelen);
baknamelen = z->namelen;
bakdclass = z->dclass;
chunk_list = z->perform_write_chunk_list;
z->perform_write_chunk_list = NULL;
}
} else {
auth_chunk_list_delete(z->perform_write_chunk_list);
z->perform_write_chunk_list = NULL;
}
z->zonemd_callback_perform_write = 0;
}
lock_rw_unlock(&z->lock);
if(perform_write) {
zone_write_after_update_reacq(bakname, baknamelen, bakdclass,
env, chunk_list);
}
}
/** lookup DNSKEY for ZONEMD verification */
@@ -8794,6 +8901,9 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
&auth_zonemd_dnskey_lookup_callback, z, 0,
&z->zonemd_callback_unique_info)) {
lock_rw_wrlock(&z->lock);
/* no callback will run; do not leave the pending
* marker set */
z->zonemd_callback_env = NULL;
log_err("out of memory lookup of %s for zonemd",
(fetch_ds?"DS":"DNSKEY"));
return 0;
+4
View File
@@ -146,6 +146,10 @@ struct auth_zone {
uint16_t zonemd_callback_qtype;
/** for the zonemd callback, the unique info */
void* zonemd_callback_unique_info;
/** if the zonemd callback should write to file */
int zonemd_callback_perform_write;
/** chunklist to write for chunked transfer. */
struct auth_chunk* perform_write_chunk_list;
/** zone has been deleted */
int zone_deleted;
/** deletelist pointer, unused normally except during delete */
+1 -1
View File
@@ -677,7 +677,7 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
rrset_array_unlock(r->ref, r->rrset_count);
return NULL;
}
if(r->security == sec_status_secure && !reply_all_rrsets_secure(r)) {
if(r->security == sec_status_secure && !reply_an_ns_rrsets_secure(r)) {
/* message rrsets have changed status, revalidate */
rrset_array_unlock(r->ref, r->rrset_count);
return NULL;
+12 -2
View File
@@ -2133,7 +2133,7 @@ void listen_start_accept(struct listen_dnsport* listen)
}
struct tcp_req_info*
tcp_req_info_create(struct sldns_buffer* spoolbuf)
tcp_req_info_create(struct comm_base* base, struct sldns_buffer* spoolbuf)
{
struct tcp_req_info* req = (struct tcp_req_info*)malloc(sizeof(*req));
if(!req) {
@@ -2141,6 +2141,12 @@ tcp_req_info_create(struct sldns_buffer* spoolbuf)
return NULL;
}
memset(req, 0, sizeof(*req));
req->read_again_timer = comm_timer_create(base, tcp_read_again_cb, req);
if(!req->read_again_timer) {
log_err("malloc failure");
free(req);
return NULL;
}
req->spool_buffer = spoolbuf;
return req;
}
@@ -2150,6 +2156,7 @@ tcp_req_info_delete(struct tcp_req_info* req)
{
if(!req) return;
tcp_req_info_clear(req);
comm_timer_delete(req->read_again_timer);
/* cp is pointer back to commpoint that owns this struct and
* called delete on us */
/* spool_buffer is shared udp buffer, not deleted here */
@@ -2189,6 +2196,9 @@ void tcp_req_info_clear(struct tcp_req_info* req)
req->done_req_list = NULL;
req->num_done_req = 0;
req->read_is_closed = 0;
if(comm_timer_is_set(req->read_again_timer))
comm_timer_disable(req->read_again_timer);
}
void
@@ -4504,7 +4514,7 @@ doq_stream_reset_cb(ngtcp2_conn* ATTR_UNUSED(conn), int64_t stream_id,
"unknown stream %d", (int)stream_id);
return 0;
}
if(!doq_stream_close(doq_conn, stream, 0))
if(!doq_stream_close(doq_conn, stream, 1))
return NGTCP2_ERR_CALLBACK_FAILURE;
return 0;
}
+7 -1
View File
@@ -347,6 +347,10 @@ struct tcp_req_info {
int num_done_req;
/** list of pending writable result packets, malloced one at a time */
struct tcp_req_done_item* done_req_list;
/** the read again timer, when the number of pipelined TCP queries
* is large, it waits, zero time, for a new event loop to service
* the remainder of the TCP traffic on the fd. */
struct comm_timer* read_again_timer;
};
/**
@@ -377,10 +381,12 @@ struct tcp_req_done_item {
* Create tcp request info structure that keeps track of open
* requests on the TCP channel that are resolved at the same time,
* and the pending results that have to get written back to that client.
* @param base: comm base for read again timer.
* @param spoolbuf: shared buffer
* @return new structure or NULL on alloc failure.
*/
struct tcp_req_info* tcp_req_info_create(struct sldns_buffer* spoolbuf);
struct tcp_req_info* tcp_req_info_create(struct comm_base* base,
struct sldns_buffer* spoolbuf);
/**
* Delete tcp request structure. Called by owning commpoint.
+73 -75
View File
@@ -373,7 +373,7 @@ mesh_serve_expired_lookup(struct module_qstate* qstate,
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(msg->rep->security == sec_status_secure &&
!reply_all_rrsets_secure(msg->rep) && must_validate) {
!reply_an_ns_rrsets_secure(msg->rep) && must_validate) {
verbose(VERB_ALGO, "Serve expired: secure entry"
" changed status");
goto bail_out; /* rrset changed, re-verify */
@@ -1097,6 +1097,18 @@ mesh_state_make_unique(struct mesh_state* mstate)
mstate->unique = mstate;
}
/** pop a reply from the reply list, if there are any. */
static struct mesh_reply*
mesh_reply_list_pop_first(struct mesh_state* mstate)
{
if(mstate->reply_list) {
struct mesh_reply* r = mstate->reply_list;
mstate->reply_list = r->next;
return r;
}
return NULL;
}
void
mesh_state_cleanup(struct mesh_state* mstate)
{
@@ -1112,15 +1124,30 @@ mesh_state_cleanup(struct mesh_state* mstate)
}
/* drop unsent replies */
if(!mstate->replies_sent) {
struct mesh_reply* rep = mstate->reply_list;
struct mesh_reply* rep;
struct mesh_cb* cb;
/* in tcp_req_info, the mstates linked are removed, but
* the reply_list is now NULL, so the remove-from-empty-list
* takes no time and also it does not do the mesh accounting */
mstate->reply_list = NULL;
for(; rep; rep=rep->next) {
/* Pop items from the list, that means there is no iterator.
* And then items can be removed from the reply list, from
* like comm_point_drop_reply and comm_point_close calls.
* As the tcp_req_info and http2 code drops the entire
* connection. That could delete mesh_reply items previous and
* after the current state. The previous items are already
* popped. And the next items can be altered, like to when a
* connection has more replies on the reply list.
* The current item is also popped so the code needs to
* remove its references. */
while((rep = mesh_reply_list_pop_first(mstate)) != NULL) {
infra_wait_limit_dec(mesh->env->infra_cache,
&rep->query_reply, mesh->env->cfg);
if(rep->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(
rep->query_reply.c->tcp_req_info,
mstate);
else if(rep->query_reply.c->use_h2)
http2_stream_remove_mesh_state(rep->h2_stream);
else if(rep->query_reply.doq_stream)
doq_stream_remove_mesh_state(
rep->query_reply.doq_stream);
comm_point_drop_reply(&rep->query_reply);
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
@@ -1484,12 +1511,6 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
struct timeval end_time;
struct timeval duration;
int secure;
/* briefly set the replylist to null in case the
* meshsendreply calls tcpreqinfo sendreply that
* comm_point_drops because of size, and then the
* null stops the mesh state remove and thus
* reply_list modification and accounting */
struct mesh_reply* rlist = m->reply_list;
/* rpz: apply actions */
rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m)
@@ -1546,9 +1567,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
sldns_buffer_write_at(r_buffer, 0, &r->qid, sizeof(uint16_t));
sldns_buffer_write_at(r_buffer, 12, r->qname,
m->s.qinfo.qname_len);
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
} else if(rcode) {
m->s.qinfo.qname = r->qname;
m->s.qinfo.local_alias = r->local_alias;
@@ -1570,9 +1589,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
}
error_encode(r_buffer, rcode, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
} else {
size_t udp_size = r->edns.udp_size;
r->edns.edns_version = EDNS_ADVERTISED_VERSION;
@@ -1608,9 +1625,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
&m->s.qinfo, r->qid, r->qflags, &r->edns);
}
m->reply_list = NULL;
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);
@@ -1762,6 +1777,7 @@ void mesh_query_done(struct mesh_state* mstate)
struct reply_info* rep = (mstate->s.return_msg?
mstate->s.return_msg->rep:NULL);
struct timeval tv = {0, 0};
struct mesh_area* mesh = mstate->s.env->mesh;
int i = 0;
/* No need for the serve expired timer anymore; we are going to reply. */
if(mstate->s.serve_expired_data) {
@@ -1786,7 +1802,18 @@ void mesh_query_done(struct mesh_state* mstate)
&& (!rep || rep->security != sec_status_secure))
dns_error_reporting(&mstate->s, rep);
for(r = mstate->reply_list; r; r = r->next) {
while((r = mesh_reply_list_pop_first(mstate)) != NULL) {
/* it was not detached (because it had a reply list), could be now */
if(!mstate->reply_list && !mstate->cb_list
&& mstate->super_set.count == 0) {
mesh->num_detached_states++;
}
/* if not replies any more in mstate, it is no longer a reply_state */
if(!mstate->reply_list && !mstate->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
if(mesh_is_udp(r)) {
/* For UDP queries, the old replies are discarded.
* This stops a large volume of old replies from
@@ -1801,22 +1828,18 @@ void mesh_query_done(struct mesh_state* mstate)
((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;
if(r->query_reply.c->use_h2)
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(
r->query_reply.c->tcp_req_info,
mstate);
else if(r->query_reply.c->use_h2)
http2_stream_remove_mesh_state(r->h2_stream);
else if(r->query_reply.doq_stream)
doq_stream_remove_mesh_state(r->query_reply.doq_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
log_assert(mstate->s.env->mesh->num_reply_addrs > 0);
mstate->s.env->mesh->num_reply_addrs--;
mstate->s.env->mesh->num_queries_discard_timeout++;
@@ -1841,22 +1864,17 @@ void mesh_query_done(struct mesh_state* mstate)
/* if this query is determined to be dropped during the
* mesh processing, this is the point to take that action. */
if(mstate->s.is_drop) {
/* 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;
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
mstate->reply_list = NULL;
if(r->query_reply.c->use_h2) {
if(r->query_reply.c->tcp_req_info) {
tcp_req_info_remove_mesh_state(
r->query_reply.c->tcp_req_info, mstate);
} else if(r->query_reply.c->use_h2) {
http2_stream_remove_mesh_state(r->h2_stream);
} else if(r->query_reply.doq_stream) {
doq_stream_remove_mesh_state(r->query_reply.doq_stream);
}
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
log_assert(mstate->s.env->mesh->num_reply_addrs > 0);
mstate->s.env->mesh->num_reply_addrs--;
} else {
@@ -1897,18 +1915,6 @@ void mesh_query_done(struct mesh_state* mstate)
}
}
/* Mesh area accounting */
if(mstate->reply_list) {
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
/* was a reply state, not anymore */
log_assert(mstate->s.env->mesh->num_reply_states > 0);
mstate->s.env->mesh->num_reply_states--;
}
if(!mstate->reply_list && !mstate->cb_list &&
mstate->super_set.count == 0)
mstate->s.env->mesh->num_detached_states++;
}
mstate->replies_sent = 1;
while((c = mstate->cb_list) != NULL) {
@@ -2498,7 +2504,6 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
}
}
static int
apply_respip_action(struct module_qstate* qstate,
const struct query_info* qinfo, struct respip_client_info* cinfo,
@@ -2631,7 +2636,18 @@ mesh_serve_expired_callback(void* arg)
if(verbosity >= VERB_ALGO)
log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep);
for(r = mstate->reply_list; r; r = r->next) {
while((r = mesh_reply_list_pop_first(mstate)) != NULL) {
/* it was not detached (because it had a reply list), could be now */
if(!mstate->reply_list && !mstate->cb_list
&& mstate->super_set.count == 0) {
mesh->num_detached_states++;
}
/* if not replies any more in mstate, it is no longer a reply_state */
if(!mstate->reply_list && !mstate->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
if(mesh_is_udp(r)) {
struct timeval old;
timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
@@ -2639,22 +2655,17 @@ mesh_serve_expired_callback(void* arg)
((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;
if(r->query_reply.c->use_h2)
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(
r->query_reply.c->tcp_req_info, mstate);
else if(r->query_reply.c->use_h2)
http2_stream_remove_mesh_state(r->h2_stream);
else if(r->query_reply.doq_stream)
doq_stream_remove_mesh_state(r->query_reply.doq_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
log_assert(mstate->s.env->mesh->num_reply_addrs > 0);
mstate->s.env->mesh->num_reply_addrs--;
mstate->s.env->mesh->num_queries_discard_timeout++;
@@ -2692,8 +2703,7 @@ mesh_serve_expired_callback(void* arg)
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
/* mesh_send_reply removed mesh state from http2_stream. */
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
/* mesh_send_reply decremented wait_limit. */
prev = r;
prev_buffer = r_buffer;
}
@@ -2712,18 +2722,6 @@ mesh_serve_expired_callback(void* arg)
}
}
/* Mesh area accounting */
if(mstate->reply_list) {
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
if(mstate->super_set.count == 0) {
mesh->num_detached_states++;
}
}
}
while((c = mstate->cb_list) != NULL) {
/* take this cb off the list; so that the list can be
* changed, eg. by adds from the callback routine */
+10
View File
@@ -786,3 +786,13 @@ size_t doq_table_quic_size_get(struct doq_table* ATTR_UNUSED(table))
return 0;
}
#endif
void tcp_read_again_cb(void* ATTR_UNUSED(arg))
{
/* nothing */
}
void tcp_more_read_again_cb(void* ATTR_UNUSED(arg))
{
/* nothing */
}
+143 -1
View File
@@ -196,7 +196,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve,
setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */
/* ok to give null as qstate here, won't be used for answer section. */
sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason,
NULL, LDNS_SECTION_ANSWER, NULL, &verified, reasonbuf,
NULL, LDNS_SECTION_ANSWER, NULL, NULL, &verified, reasonbuf,
sizeof(reasonbuf));
if(vsig) {
printf("verify outcome is: %s %s\n", sec_status_to_string(sec),
@@ -510,6 +510,146 @@ nsec3_hash_test(const char* fname)
sldns_buffer_free(buf);
}
/** Test the rrset_canonicalize_to_buffer function to see if the
* size of canon_owner name is properly checked for. */
static void
canon_owner_buf_test(void)
{
struct regional* region;
sldns_buffer* buf;
struct ub_packed_rrset_key k;
struct packed_rrset_data d;
size_t rr_len[2];
time_t rr_ttl[2];
uint8_t* rr_data[2];
int ret;
unit_show_func("validator/val_sigcrypt.c",
"rrset_canonicalize_to_buffer");
region = regional_create();
if(!region)
fatal_exit("out of memory");
/* Purposefully a very small buffer, to overflow it */
buf = sldns_buffer_new(28);
if(!buf)
fatal_exit("out of memory");
/* An RRset to canonicalize. The buffer is made smaller, so
* it can fail on bounds checks. */
memset(&d, 0, sizeof(d));
d.ttl = 3600;
d.count = 1;
d.rrsig_count = 1;
d.rr_len = rr_len;
d.rr_ttl = rr_ttl;
d.rr_data = rr_data;
rr_len[0] = 18;
rr_len[1] = 36;
rr_ttl[0] = 3600;
rr_ttl[1] = 3600;
rr_data[0] = (uint8_t*)"\x00\x10\x0Fzzaaaaaaaaaaaaa";
rr_data[1] = (uint8_t*)"\x00\x24\x00\x06\x08\x3\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x12\x34\x03zzz\x00zzaaaaaaaaaaa";
memset(&k, 0, sizeof(k));
k.rk.dname = (uint8_t*) "\x0f" "aaaaaaaaaaaaaaa" "\x00";
k.rk.dname_len = 17;
k.rk.type = htons(LDNS_RR_TYPE_TXT);
k.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
k.entry.data = &d;
/* There should be no buffer overflow, assertion failure, here */
ret = rrset_canonicalize_to_buffer(region, buf, &k);
unit_assert(ret == 0);
regional_destroy(region);
sldns_buffer_free(buf);
}
/** Test if ds_digest_match_dnskey that calls ds_create_dnskey_digest,
* checks the buffer size. */
static void
dnskey_ds_digest_test(void)
{
struct regional* region;
sldns_buffer* buf;
struct module_env env;
struct ub_packed_rrset_key k1, k2;
struct packed_rrset_data d1, d2;
size_t rr_len1[1], rr_len2[1];
time_t rr_ttl1[1], rr_ttl2[1];
uint8_t* rr_rdata1[1], *rr_rdata2[1];
int ret;
unit_show_func("validator/val_sigcrypt.c", "ds_digest_match_dnskey");
region = regional_create();
if(!region)
fatal_exit("out of memory");
/* Purposefully a very small buffer, to overflow it */
buf = sldns_buffer_new(28);
if(!buf)
fatal_exit("out of memory");
memset(&env, 0, sizeof(env));
env.scratch = region;
env.scratch_buffer = buf;
/* A DNSKEY and DS RRset to match together. The buffer is made
* smaller, so it can fail on bounds checks. */
memset(&d1, 0, sizeof(d1));
d1.ttl = 3600;
d1.count = 1;
d1.rr_len = rr_len1;
d1.rr_ttl = rr_ttl1;
d1.rr_data = rr_rdata1;
rr_len1[0] = 38;
rr_ttl1[0] = 3600;
/* DS rdata has: keytag (2bytes), algorithm (1byte),
* digesttype (1byte), digest (remainder). */
rr_rdata1[0] = (uint8_t*)"\x00\x24"
"\x12\x34"
"\x08" /* RSASHA256 */
"\x02" /* SHA256 */
"0123456789abcdef0123456789abcdef"; /* 32 bytes */
;
memset(&k1, 0, sizeof(k1));
k1.rk.dname = (uint8_t*) "\x03" "foo" "\x00";
k1.rk.dname_len = 5;
k1.rk.type = htons(LDNS_RR_TYPE_DS);
k1.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
k1.entry.data = &d1;
memset(&d2, 0, sizeof(d2));
d2.ttl = 3600;
d2.count = 1;
d2.rr_len = rr_len2;
d2.rr_ttl = rr_ttl2;
d2.rr_data = rr_rdata2;
rr_len2[0] = 38;
rr_ttl2[0] = 3600;
/* DNSKEY rdata has: flags (2bytes), protocol (1byte),
* algorithm (1byte), publickey (remainder). */
rr_rdata2[0] = (uint8_t*)"\x00\x24"
"\x01\x01" /* KSK */
"\x03" /* DNSSEC_KEYPROTO */
"\x08" /* RSASHA256 */
"0123456789abcdef0123456789abcdef"; /* 32 bytes of content */
;
memset(&k2, 0, sizeof(k2));
k2.rk.dname = (uint8_t*) "\x03" "foo" "\x00";
k2.rk.dname_len = 5;
k2.rk.type = htons(LDNS_RR_TYPE_DNSKEY);
k2.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
k2.entry.data = &d2;
/* 36 byte rdata length for DNSKEY (38-2), and dname length of 5,
* exceeds the (small) buffer size. */
/* There should be no buffer overflow, assertion failure, here */
ret = ds_digest_match_dnskey(&env, &k2, 0, &k1, 0);
unit_assert(ret == 0);
regional_destroy(region);
sldns_buffer_free(buf);
}
#define xstr(s) str(s)
#define str(s) #s
@@ -724,4 +864,6 @@ verify_test(void)
#endif
nsectest();
nsec3_hash_test(SRCDIRSTR "/testdata/test_nsec3_hash.1");
dnskey_ds_digest_test();
canon_owner_buf_test();
}
+9
View File
@@ -85,6 +85,15 @@ for input in $PRE/testdata/*.rpl $PRE/testdata/*.crpl; do
fi
fi
# detect if ECDSA is needed
if echo $cleaninput | grep ecdsa >/dev/null 2>&1; then
if $PRE/testbound -e >/dev/null 2>&1; then
: # ECDSA is supported
else
continue
fi
fi
# detect if CLIENT_SUBNET is needed
if echo $cleaninput | grep subnet >/dev/null 2>&1; then
if $PRE/testbound -c >/dev/null 2>&1; then
+1 -31
View File
@@ -283,39 +283,9 @@ www.example.com. IN A
SECTION ANSWER
ENTRY_END
; the zonefile was updated with new contents
; the zonefile was not updated with new contents, due to zonemd failure
STEP 70 CHECK_TEMPFILE example.com
FILE_BEGIN
example.com. 3600 IN SOA ns.example.com. hostmaster.example.com. 200154054 28800 7200 604800 3600
example.com. 3600 IN RRSIG SOA 8 2 3600 20201116135527 20201019135527 55566 example.com. gcFHT/Q4iDZ78CK6fyY2HZr8sRtgH2Rna9fEs06RW0gqMnfDntweoIaBamOZ7NlAP84aY2bZeanmEccmkHexByUpodCoKQ4NzVXctLr0TO4PVoFyfUfj62fjhM56SF8ioDxsoDQcPtYXcjNQjwfntWofMqHCMxrb9LzbgePzhOM=
example.com. 3600 IN NS ns.example.com.
example.com. 3600 IN RRSIG NS 8 2 3600 20201116135527 20201019135527 55566 example.com. X+V3XsbJbBi9OsHpjMkGCox8RLY/uXp/XX/O/flTrIre9fMDWm9ZGnewtuQFpLgGc6hUTi0eLsuRWRA5fZXEKUBhmoR2Ph01KgE1gvlL7v6zPWQwXVcBRUr3mOSbYdNNkHkXEjiDBGEhNkfqR216zNgw563eEGXOkLUFNIx5Zpg=
example.com. 3600 IN NSEC bar.example.com. NS SOA RRSIG NSEC DNSKEY ZONEMD
example.com. 3600 IN RRSIG NSEC 8 2 3600 20201116135527 20201019135527 55566 example.com. ufLrlOQprAqjnH85Rt3T0Mxd3ZB0mBeeNIr84eFJ8Rk6WiWEPm0Y1R7GRufNI24Mj7iqLcL4nJM6KK6B7dJqjqu73jw1acuYNnbsoV2BNDRXRFP2FNWTpctVdi+955f3FzgsmEJXfGiSUG0YXAEcZmdCPCn5ii2jk8mk7r6KKYo=
example.com. 3600 IN DNSKEY 256 3 8 AwEAAdug/L739i0mgN2nuK/bhxu3wFn5Ud9nK2+XUmZQlPUEZUC5YZvm1rfMmEWTGBn87fFxEu/kjFZHJ55JLzqsbbpVHLbmKCTT2gYR2FV2WDKROGKuYbVkJIXdKAjJ0ONuK507NinYvlWXIoxHn22KAWOd9wKgSTNHBlmGkX+ts3hh ;{id = 55566}
example.com. 3600 IN RRSIG DNSKEY 8 2 3600 20201116135527 20201019135527 55566 example.com. fsdnVg38PKQTH2mDOwkXL6Jre7JP7Gf8WI3CvIbmeYQUJtAlpcSbZkS3wInm3kKMxOuT55BWzndQzpfmpo91OqJjG27W0k9301NMLUwFprA6b9HK+iPAT0JpYPDPzcm1bQdarLzLS+eD/GPwmyVSX7Gze+08VfE8m8sOW2r7UjA=
example.com. 3600 IN ZONEMD 200154054 1 2 58F7620F93204BBB31B44F795B3409CC4ABD9EF5601DECC15675BD7751213152984EDDCE0626E6062E744B03B3E47711202FBB79E4A2EB8BC5CF46741B5CAE6F
example.com. 3600 IN RRSIG ZONEMD 8 2 3600 20201116135527 20201019135527 55566 example.com. orn8ZF/yqj9u4WrhiO6gtEcTaVsnZSWWZLfXhcIOiWSB8kKCxtZl5cG17dD3Du1NllUwMRqkp0KleLhIoUS9xeQ/0x05u+CYLrfQ62oAiD7q54ZQzpXJIH52aQzKV70ZnO03CZowhQBnetmIoKX6xLogKo8pt+BdQbo3oVHxV8Y=
bar.example.com. 3600 IN A 1.2.3.55
bar.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. NYhmRicF4C9+YxpWeQrepy4ALM1CM0USoDuGi3W5Xtp4/+YpCJfSIdR9vlJaJ2WayYuZrz9Ai2ci7oWwE1Fn3oywGwCKvGo9m0c3mC2eEtphE19wrop6pWu6um4RiFhmzYS1voraA3PAdYzze9U4NHzlk0+sb5vNZW9dSZS30Ds=
bar.example.com. 3600 IN NSEC ding.example.com. A RRSIG NSEC
bar.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. VhsGuBx20DXQZNU8ITAMnasn6NVyEjN9xtB8msH5xJn80UCuaqvFBURzcPWN3aHnykEvGfdPF/9P3WvlON0cMikWkqSLy6Q9bpvgAq13HWYh+ZcDoqLtICaB7RkBQc+6aHAqZFyQbD8/m8Kxt5eVJtV6rEuf+yPX0+3aXHhsRg0=
ding.example.com. 3600 IN A 1.2.3.4
ding.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. OERsruISkpd1s68ute8Xm8YXisBCTkkiDMt34K+0dVqvySOJq63d3qN18BeUxZxLyHDB1eR3nZZKqEdkTqrv2r98skhWhjnOECpFbu5gKjtN/KPexbbJ+rxC0QqciuWOC7M6YE0cvI17/RB9KhVRy5rqY2X4Gt2wk2CNeD1dAko=
ding.example.com. 3600 IN NSEC foo.example.com. A RRSIG NSEC
ding.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. nb1W2aaKrU5iAQiY8gMsoMOejID19JMTEwY2rRoe+KsvzMs0rE0ifEkqit4blXaU0tfy0foJ70uqdJFqBoGz1NcSwZ6GNk/iNfGvG3XpxZ/zqEe7kkIucqqei794G7z9psqV94yZ3WaT+IswPpWrSaWv1w41RtcWufPhe4fOAmU=
foo.example.com. 3600 IN A 1.2.3.4
foo.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. ZcUngb2pUejwnsshbJN/Dfr+Bzu8fcZXyqLArQ+10Bw1IPHyfx7yyUJ43V5tTYVHPSEsJzTnaWj+olVrNhVZxq5e0pgzSYPfGln2FEItEvMIOn33j8yKTpPW2MLyuFF5ZkXhosG20EUwRMvMmRHRz9mIZfwWoMbSGPukmLh8zMA=
foo.example.com. 3600 IN NSEC ns.example.com. A RRSIG NSEC
foo.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. fUZEpkEULRWDntN5Z7Kr8M83Hjhf08ECMKRpo6IBoBc3ayenj+YMgWAvFXC825wjENPYYWNGag0d32U83zCZxqgv+8uXZd3B7QDpTbL41aWZdc++s5YWTkYjyOWwJ1XHOv4nL3qEnJBXVzo/E1gbSKhTFuG97i+7J1MFd9MsC5s=
ns.example.com. 3600 IN A 127.0.0.1
ns.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. SiuxuPtN/ITd+Z20j8UNUHJWbLHirE8zQOWMv5fAZ1rPKpAidrZgUL8J417GdrTwkueU2ywAJ7EzFJSwNTa7o/wUnq7svmOR6Ze6UQsKuZFZGEfqPNDRp4YuF86LU5jChuo+f/IRpydHrxVwGxDPCR9KarDM+ewfW+yI5bZeZcg=
ns.example.com. 3600 IN NSEC www.example.com. A RRSIG NSEC
ns.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. 0upKNYjiow4NDJm3I1RbUddE9GGuFYEVKswww5BAc/6WHuukupncL30lskvcSKGpByDssP2Hi2CufyEtYeGWh6q1TxtOFRqFBX1p6Q5b3tBlCtvv4h31dQR9uqLvq+GkGS5MR+0LO5kWagIpZmnI8YY5plVdXEtNbp2Ar8zvz/A=
www.example.com. 3600 IN A 127.0.0.1
www.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. AaIeICaPjV50TDrpbyOn94+hs8EYIMTmN4pYqj7e8GIGimqQIk5jgpwSx6SOoOF+uOqkf9GKHkQTn5YVGaeXwEQleg7mPTmMYKAOk06Y7MFUO1Vwt1Vt7Wo+Cpa3x2a1CmEkfFOi4WqP43VJnUtjjKmXoKRz3VUmqByyJYUAGbQ=
www.example.com. 3600 IN NSEC example.com. A RRSIG NSEC
www.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. meg/t6nIBqQZ0d5/dT7uu/3CuP4vE+HxqFQaj2fjUNceA/6C7QIQnqQ5Kyblg+XijDkQX0yvyFNHYdgF16UDgFT7tlNUCHk1SpF5BWzV4c4tBEhxASTz7UQo111O3Tyd6CldPzO/Se15Ud0/ZYltHEqWTfY5nJoXC/OJD9V2QOI=
FILE_END
SCENARIO_END
+1
View File
@@ -11,6 +11,7 @@ server:
trust-anchor-signaling: no
iter-scrub-promiscuous: no
rrset-roundrobin: no
val-clean-additional: yes
stub-zone:
name: "."
+1
View File
@@ -12,6 +12,7 @@ server:
minimal-responses: no
iter-scrub-promiscuous: no
rrset-roundrobin: no
val-clean-additional: yes
stub-zone:
name: "."
+7 -1
View File
@@ -278,7 +278,7 @@ config_create(void)
cfg->val_sig_skew_min = 3600; /* at least daylight savings trouble */
cfg->val_sig_skew_max = 86400; /* at most timezone settings trouble */
cfg->val_max_restart = 5;
cfg->val_clean_additional = 1;
cfg->val_clean_additional = 0; /* off to protect against much data. */
cfg->val_log_level = 0;
cfg->val_log_squelch = 0;
cfg->val_permissive_mode = 0;
@@ -429,6 +429,8 @@ config_create(void)
cfg->iter_scrub_rrsig = 8;
cfg->iter_scrub_promiscuous = 1;
cfg->max_global_quota = 200;
cfg->val_validation_attempts = 32;
cfg->val_hash_attempts = 32;
return cfg;
error_exit:
config_delete(cfg);
@@ -785,6 +787,8 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_NUMBER_OR_ZERO("iter-scrub-rrsig:", iter_scrub_rrsig)
else S_YNO("iter-scrub-promiscuous:", iter_scrub_promiscuous)
else S_NUMBER_OR_ZERO("max-global-quota:", max_global_quota)
else S_NUMBER_OR_ZERO("val-validation-attempts:", val_validation_attempts)
else S_NUMBER_OR_ZERO("val-hash-attempts:", val_hash_attempts)
else S_YNO("serve-original-ttl:", serve_original_ttl)
else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations)
else S_YNO("zonemd-permissive-mode:", zonemd_permissive_mode)
@@ -1265,6 +1269,8 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_DEC(opt, "iter-scrub-rrsig", iter_scrub_rrsig)
else O_YNO(opt, "iter-scrub-promiscuous", iter_scrub_promiscuous)
else O_DEC(opt, "max-global-quota", max_global_quota)
else O_DEC(opt, "val-validation-attempts", val_validation_attempts)
else O_DEC(opt, "val-hash-attempts", val_hash_attempts)
else O_YNO(opt, "serve-original-ttl", serve_original_ttl)
else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations)
else O_YNO(opt, "zonemd-permissive-mode", zonemd_permissive_mode)
+4
View File
@@ -798,6 +798,10 @@ struct config_file {
int iter_scrub_rrsig;
/** limit on upstream queries for an incoming query and subqueries. */
int max_global_quota;
/** limit on validator validation attempts. */
int val_validation_attempts;
/** limit on validator hash attempts. */
int val_hash_attempts;
/** Should the iterator scrub promiscuous NS rrsets, from positive
* answers. */
int iter_scrub_promiscuous;
+2
View File
@@ -608,6 +608,8 @@ iter-scrub-ns{COLON} { YDVAR(1, VAR_ITER_SCRUB_NS) }
iter-scrub-cname{COLON} { YDVAR(1, VAR_ITER_SCRUB_CNAME) }
iter-scrub-rrsig{COLON} { YDVAR(1, VAR_ITER_SCRUB_RRSIG) }
max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) }
val-validation-attempts{COLON} { YDVAR(1, VAR_VAL_VALIDATION_ATTEMPTS) }
val-hash-attempts{COLON} { YDVAR(1, VAR_VAL_HASH_ATTEMPTS) }
max-transfer-size{COLON} { YDVAR(1, VAR_MAX_TRANSFER_SIZE) }
max-transfer-time{COLON} { YDVAR(1, VAR_MAX_TRANSFER_TIME) }
iter-scrub-promiscuous{COLON} { YDVAR(1, VAR_ITER_SCRUB_PROMISCUOUS) }
+21 -1
View File
@@ -218,6 +218,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_ITER_SCRUB_RRSIG
%token VAR_MAX_TRANSFER_SIZE VAR_MAX_TRANSFER_TIME
%token VAR_MAX_GLOBAL_QUOTA VAR_HARDEN_UNVERIFIED_GLUE VAR_LOG_TIME_ISO
%token VAR_VAL_VALIDATION_ATTEMPTS VAR_VAL_HASH_ATTEMPTS
%token VAR_ITER_SCRUB_PROMISCUOUS VAR_LOG_THREAD_ID
%%
@@ -360,7 +361,8 @@ content_server: server_num_threads | server_verbosity | server_port |
server_harden_unknown_additional | server_disable_edns_do |
server_log_destaddr | server_cookie_secret_file |
server_iter_scrub_ns | server_iter_scrub_cname | server_max_global_quota |
server_iter_scrub_rrsig |
server_val_validation_attempts |
server_val_hash_attempts | server_iter_scrub_rrsig |
server_harden_unverified_glue | server_log_time_iso | server_iter_scrub_promiscuous
;
stub_clause: stubstart contents_stub
@@ -4317,6 +4319,24 @@ server_iter_scrub_promiscuous: VAR_ITER_SCRUB_PROMISCUOUS STRING_ARG
free($2);
}
;
server_val_validation_attempts: VAR_VAL_VALIDATION_ATTEMPTS STRING_ARG
{
OUTYY(("P(server_val_validation_attempts:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->val_validation_attempts = atoi($2);
free($2);
}
;
server_val_hash_attempts: VAR_VAL_HASH_ATTEMPTS STRING_ARG
{
OUTYY(("P(server_val_hash_attempts:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->val_hash_attempts = atoi($2);
free($2);
}
;
ipsetstart: VAR_IPSET
{
OUTYY(("\nP(ipset:)\n"));
+14 -7
View File
@@ -192,34 +192,34 @@ pkt_dname_len(sldns_buffer* pkt)
while(1) {
/* read next label */
if(sldns_buffer_remaining(pkt) < 1)
return 0;
goto fail;
labellen = sldns_buffer_read_u8(pkt);
if(LABEL_IS_PTR(labellen)) {
/* compression ptr */
uint16_t ptr;
if(sldns_buffer_remaining(pkt) < 1)
return 0;
goto fail;
ptr = PTR_OFFSET(labellen, sldns_buffer_read_u8(pkt));
if(ptrcount++ > MAX_COMPRESS_PTRS)
return 0; /* loop! */
goto fail; /* loop! */
if(sldns_buffer_limit(pkt) <= ptr)
return 0; /* out of bounds! */
goto fail; /* out of bounds! */
if(!endpos)
endpos = sldns_buffer_position(pkt);
sldns_buffer_set_position(pkt, ptr);
} else {
/* label contents */
if(labellen > 0x3f)
return 0; /* label too long */
goto fail; /* label too long */
len += 1 + labellen;
if(len > LDNS_MAX_DOMAINLEN)
return 0;
goto fail;
if(labellen == 0) {
/* end of dname */
break;
}
if(sldns_buffer_remaining(pkt) < labellen)
return 0;
goto fail;
sldns_buffer_skip(pkt, (ssize_t)labellen);
}
}
@@ -227,6 +227,13 @@ pkt_dname_len(sldns_buffer* pkt)
sldns_buffer_set_position(pkt, endpos);
return len;
fail:
/* Restore the position on failure too: callers (rdata_copy) compute
* the consumed field length from the buffer position and must not
* see a partial walk of a name that failed to parse. */
if(endpos)
sldns_buffer_set_position(pkt, endpos);
return 0;
}
int
+40 -5
View File
@@ -248,6 +248,7 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
sldns_pkt_section section)
{
uint16_t pkt_len;
size_t tolen;
uint32_t ttl;
const sldns_rr_descriptor* desc;
@@ -293,9 +294,13 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
(rr->ttl_data - sldns_buffer_begin(pkt) + sizeof(uint32_t)));
/* insert decompressed size into rdata len stored in memory */
/* -2 because rdatalen bytes are not included. */
tolen = rr->size;
if(tolen < 2)
return 0;
pkt_len = htons(rr->size - 2);
memmove(to, &pkt_len, sizeof(uint16_t));
to += 2;
tolen -= 2;
/* read packet rdata len */
pkt_len = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < pkt_len)
@@ -304,16 +309,29 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
if(pkt_len > 0 && desc && desc->_dname_count > 0) {
int count = (int)desc->_dname_count;
int rdf = 0;
size_t len;
size_t oldpos;
size_t len, dlen;
size_t oldpos, newpos;
/* decompress dnames. */
while(pkt_len > 0 && count) {
switch(desc->_wireformat[rdf]) {
case LDNS_RDF_TYPE_DNAME:
oldpos = sldns_buffer_position(pkt);
dname_pkt_copy(pkt, to,
dlen = pkt_dname_len(pkt);
if(dlen == 0)
return 0; /* malformed */
if(dlen > tolen)
return 0; /* alloc mismatch */
newpos = sldns_buffer_position(pkt);
if(oldpos > newpos)
return 0; /* should have moved forward*/
sldns_buffer_set_position(pkt, oldpos);
dname_pkt_copy(pkt, to,
sldns_buffer_current(pkt));
to += pkt_dname_len(pkt);
sldns_buffer_set_position(pkt, newpos);
to += dlen;
tolen -= dlen;
if(sldns_buffer_position(pkt)-oldpos > pkt_len)
return 0; /* malformed: walks diverged */
pkt_len -= sldns_buffer_position(pkt)-oldpos;
count--;
len = 0;
@@ -326,9 +344,12 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
break;
}
if(len) {
if(len > tolen)
return 0; /* alloc mismatch */
log_assert(len <= pkt_len);
memmove(to, sldns_buffer_current(pkt), len);
to += len;
tolen -= len;
sldns_buffer_skip(pkt, (ssize_t)len);
pkt_len -= len;
}
@@ -336,8 +357,11 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
}
}
/* copy remaining rdata */
if(pkt_len > 0)
if(pkt_len > 0) {
if(pkt_len > tolen)
return 0; /* alloc mismatch */
memmove(to, sldns_buffer_current(pkt), pkt_len);
}
return 1;
}
@@ -1115,6 +1139,17 @@ reply_all_rrsets_secure(struct reply_info* rep)
return 1;
}
int reply_an_ns_rrsets_secure(struct reply_info* rep)
{
size_t i;
for(i=0; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
if( ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
->security != sec_status_secure )
return 0;
}
return 1;
}
struct reply_info*
parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region,
struct query_info* qi)
+3
View File
@@ -494,6 +494,9 @@ int reply_check_cname_chain(struct query_info* qinfo, struct reply_info* rep);
*/
int reply_all_rrsets_secure(struct reply_info* rep);
/** Check status of answer and authority section RRs. */
int reply_an_ns_rrsets_secure(struct reply_info* rep);
/**
* Find answer rrset in reply, the one matching qinfo. Follows CNAMEs, so the
* result may have a different owner name.
+2
View File
@@ -141,6 +141,8 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
#ifdef UB_ON_WINDOWS
else if(fptr == &wsvc_cron_cb) return 1;
#endif
else if(fptr == &tcp_read_again_cb) return 1;
else if(fptr == &tcp_more_read_again_cb) return 1;
else if(fptr == &auth_xfer_timer) return 1;
else if(fptr == &auth_xfer_probe_timer_callback) return 1;
else if(fptr == &auth_xfer_transfer_timer_callback) return 1;
+86 -1
View File
@@ -122,6 +122,10 @@
#define NUM_UDP_PER_SELECT 1
#endif
/** The number of TCP queries over a TCP connection, per read indication
* from select. */
#define NUM_TCP_PER_SELECT 100
/** timeout in millisec to wait for write to unblock, packets dropped after.*/
#define SEND_BLOCKED_WAIT_TIMEOUT 200
/** max number of times to wait for write to unblock, packets dropped after.*/
@@ -3226,6 +3230,26 @@ static int http2_submit_settings(struct http2_session* h2_session)
}
#endif /* HAVE_NGHTTP2 */
/** Clear http2 stream mesh states */
static void http2_session_clear_meshstate(struct http2_session* h2_session)
{
#ifdef HAVE_NGHTTP2
/* Since the session gets closed, remove the mesh state references. */
struct http2_stream* h2_stream;
for(h2_stream = h2_session->first_stream; h2_stream;
h2_stream = h2_stream->next) {
if(h2_stream->mesh_state) {
mesh_state_remove_reply(h2_stream->mesh,
h2_stream->mesh_state, h2_session->c,
h2_stream, NULL);
h2_stream->mesh_state = NULL;
}
}
#else
(void)h2_session;
#endif /* HAVE_NGHTTP2 */
}
#ifdef HAVE_NGHTTP2
/** Delete http2 stream. After session delete or stream close callback */
static void http2_stream_delete(struct http2_session* h2_session,
@@ -4621,6 +4645,10 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
static int
tcp_req_info_read_again(int fd, struct comm_point* c)
{
/* One event-loop visit drains at most this many pipelined queries;
* the rest is re-queued, so that other file descriptors get
* serviced in between. */
int budget = NUM_TCP_PER_SELECT;
while(c->tcp_req_info->read_again) {
int r;
c->tcp_req_info->read_again = 0;
@@ -4637,6 +4665,16 @@ tcp_req_info_read_again(int fd, struct comm_point* c)
}
return 0;
}
if(--budget <= 0 && c->tcp_req_info->read_again) {
/* Defer the rest of the drain to the next loop turn.
* This uses a zero delay timer. For TLS the undrained
* remainder sits in OpenSSL's user-space buffer. */
struct timeval tv;
memset(&tv, 0, sizeof(tv));
verbose(VERB_ALGO, "Defer tcp_req_info read again");
comm_timer_set(c->tcp_req_info->read_again_timer, &tv);
return 1;
}
}
return 1;
}
@@ -4650,6 +4688,7 @@ tcp_more_read_again(int fd, struct comm_point* c)
/* this continues until the read routines get EAGAIN or so,
* and thus does not call the callback, and the bool is 0 */
int* moreread = c->tcp_more_read_again;
int budget = NUM_TCP_PER_SELECT;
while(moreread && *moreread) {
*moreread = 0;
if(!comm_point_tcp_handle_read(fd, c, 0)) {
@@ -4662,6 +4701,30 @@ tcp_more_read_again(int fd, struct comm_point* c)
}
return;
}
if(--budget <= 0 && *moreread) {
/* Defer the rest of the drain to the next loop turn.
* This uses a zero delay timer. For TLS the undrained
* remainder sits in OpenSSL's user-space buffer. */
struct timeval tv;
memset(&tv, 0, sizeof(tv));
if(!c->tcp_more_read_again_timer) {
c->tcp_more_read_again_timer = comm_timer_create(c->ev->base, tcp_more_read_again_cb, c);
if(!c->tcp_more_read_again_timer) {
log_err("out of memory for tcp more read again timer");
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
return;
}
}
verbose(VERB_ALGO, "Defer more read again");
comm_timer_set(c->tcp_more_read_again_timer, &tv);
return;
}
}
}
@@ -4689,6 +4752,23 @@ tcp_more_write_again(int fd, struct comm_point* c)
}
}
void
tcp_read_again_cb(void* arg)
{
struct tcp_req_info* req = (struct tcp_req_info*)arg;
verbose(VERB_ALGO, "tcp_read_again_cb");
if(!tcp_req_info_read_again(req->cp->fd, req->cp))
return;
}
void
tcp_more_read_again_cb(void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
verbose(VERB_ALGO, "tcp_more_read_again_cb");
tcp_more_read_again(c->fd, c);
}
void
comm_point_tcp_handle_callback(int fd, short event, void* arg)
{
@@ -6131,7 +6211,7 @@ comm_point_create_tcp_handler(struct comm_base *base,
c->pp2_enabled = parent->pp2_enabled;
c->pp2_header_state = pp2_header_none;
if(spoolbuf) {
c->tcp_req_info = tcp_req_info_create(spoolbuf);
c->tcp_req_info = tcp_req_info_create(base, spoolbuf);
if(!c->tcp_req_info) {
log_err("could not create tcp commpoint");
sldns_buffer_free(c->buffer);
@@ -6693,6 +6773,9 @@ comm_point_close(struct comm_point* c)
*c->tcp_more_read_again = 0;
if(c->tcp_more_write_again && *c->tcp_more_write_again)
*c->tcp_more_write_again = 0;
if(c->tcp_more_read_again_timer &&
comm_timer_is_set(c->tcp_more_read_again_timer))
comm_timer_disable(c->tcp_more_read_again_timer);
/* close fd after removing from event lists, or epoll.. is messed up */
if(c->fd != -1 && !c->do_not_close) {
@@ -6732,6 +6815,7 @@ comm_point_delete(struct comm_point* c)
free(c->tcp_handlers);
}
free(c->timeout);
comm_timer_delete(c->tcp_more_read_again_timer);
if(c->type == comm_tcp || c->type == comm_local || c->type == comm_http) {
sldns_buffer_free(c->buffer);
#ifdef USE_DNSCRYPT
@@ -6872,6 +6956,7 @@ comm_point_drop_reply(struct comm_reply* repinfo)
if(repinfo->c->type == comm_http) {
if(repinfo->c->h2_session) {
repinfo->c->h2_session->is_drop = 1;
http2_session_clear_meshstate(repinfo->c->h2_session);
if(!repinfo->c->h2_session->postpone_drop)
reclaim_http_handler(repinfo->c);
return;
+9
View File
@@ -384,6 +384,9 @@ struct comm_point {
* Or leave NULL if it is not used at all. */
int* tcp_more_write_again;
/** resume timer for tcp_more_read_again */
struct comm_timer* tcp_more_read_again_timer;
/** if set, read/write completes:
read/write state of tcp is toggled.
buffer reset/bytecount reset.
@@ -1133,6 +1136,12 @@ void doq_send_pkt(struct comm_point* c, struct doq_pkt_addr* paddr,
/** doq timer callback function. */
void doq_timer_cb(void* arg);
/** tcp read again callback function. For tcp req info listen. */
void tcp_read_again_cb(void* arg);
/** tcp more read again callback function. For outside network. */
void tcp_more_read_again_cb(void* arg);
/**
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for timer comm.
+13 -9
View File
@@ -1297,12 +1297,13 @@ void autr_write_file(struct module_env* env, struct trust_anchor* tp)
* @param tp: trust point to verify with
* @param rrset: DNSKEY rrset to verify.
* @param qstate: qstate with region.
* @param vq: validator query state.
* @return false on failure, true if verification successful.
*/
static int
verify_dnskey(struct module_env* env, struct val_env* ve,
struct trust_anchor* tp, struct ub_packed_rrset_key* rrset,
struct module_qstate* qstate)
struct module_qstate* qstate, struct val_qstate* vq)
{
char reasonbuf[256];
char* reason = NULL;
@@ -1310,7 +1311,7 @@ verify_dnskey(struct module_env* env, struct val_env* ve,
int downprot = env->cfg->harden_algo_downgrade;
enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset,
tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason,
NULL, qstate, reasonbuf, sizeof(reasonbuf));
NULL, qstate, vq, reasonbuf, sizeof(reasonbuf));
/* sigalg is ignored, it returns algorithms signalled to exist, but
* in 5011 there are no other rrsets to check. if downprot is
* enabled, then it checks that the DNSKEY is signed with all
@@ -1350,16 +1351,18 @@ min_expiry(struct module_env* env, struct packed_rrset_data* dd)
static int
rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey_rrset, size_t i,
struct module_qstate* qstate)
struct module_qstate* qstate, struct val_qstate* vq)
{
enum sec_status sec;
char* reason = NULL;
size_t num_tagmatches = 0;
verbose(VERB_ALGO, "seen REVOKE flag, check self-signed, rr %d",
(int)i);
/* no algorithm downgrade protection necessary, if it is selfsigned
* revoked it can be removed. */
sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i,
&reason, NULL, LDNS_SECTION_ANSWER, qstate);
&reason, NULL, LDNS_SECTION_ANSWER, qstate, vq,
&num_tagmatches);
return (sec == sec_status_secure);
}
@@ -1575,7 +1578,7 @@ init_events(struct trust_anchor* tp)
static void
check_contains_revoked(struct module_env* env, struct val_env* ve,
struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset,
int* changed, struct module_qstate* qstate)
int* changed, struct module_qstate* qstate, struct val_qstate* vq)
{
struct packed_rrset_data* dd = (struct packed_rrset_data*)
dnskey_rrset->entry.data;
@@ -1595,7 +1598,8 @@ check_contains_revoked(struct module_env* env, struct val_env* ve,
}
if(!ta)
continue; /* key not found */
if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i, qstate)) {
if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i, qstate,
vq)) {
/* checked if there is an rrsig signed by this key. */
/* same keytag, but stored can be revoked already, so
* compare keytags, with +0 or +128(REVOKE flag) */
@@ -2209,7 +2213,7 @@ autr_tp_remove(struct module_env* env, struct trust_anchor* tp,
int autr_process_prime(struct module_env* env, struct val_env* ve,
struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset,
struct module_qstate* qstate)
struct module_qstate* qstate, struct val_qstate* vq)
{
int changed = 0;
log_assert(tp && tp->autr);
@@ -2250,7 +2254,7 @@ int autr_process_prime(struct module_env* env, struct val_env* ve,
return 1; /* trust point exists */
}
/* check for revoked keys to remove immediately */
check_contains_revoked(env, ve, tp, dnskey_rrset, &changed, qstate);
check_contains_revoked(env, ve, tp, dnskey_rrset, &changed, qstate, vq);
if(changed) {
verbose(VERB_ALGO, "autotrust: revokedkeys, reassemble");
if(!autr_assemble(tp)) {
@@ -2266,7 +2270,7 @@ int autr_process_prime(struct module_env* env, struct val_env* ve,
}
}
/* verify the dnskey rrset and see if it is valid. */
if(!verify_dnskey(env, ve, tp, dnskey_rrset, qstate)) {
if(!verify_dnskey(env, ve, tp, dnskey_rrset, qstate, vq)) {
verbose(VERB_ALGO, "autotrust: dnskey did not verify.");
/* only increase failure count if this is not the first prime,
* this means there was a previous successful probe */
+3 -1
View File
@@ -50,6 +50,7 @@ struct module_env;
struct module_qstate;
struct val_env;
struct sldns_buffer;
struct val_qstate;
/** Autotrust anchor states */
typedef enum {
@@ -190,13 +191,14 @@ void autr_point_delete(struct trust_anchor* tp);
* @param dnskey_rrset: DNSKEY rrset probed (can be NULL if bad prime result).
* allocated in a region. Has not been validated yet.
* @param qstate: qstate with region.
* @param vq: validator query state.
* @return false if trust anchor was revoked completely.
* Otherwise logs errors to log, does not change return value.
* On errors, likely the trust point has been unchanged.
*/
int autr_process_prime(struct module_env* env, struct val_env* ve,
struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset,
struct module_qstate* qstate);
struct module_qstate* qstate, struct val_qstate* vq);
/**
* Debug printout of rfc5011 tracked anchors
+6 -5
View File
@@ -177,7 +177,8 @@ static int
nsec_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey,
char** reason, sldns_ede_code* reason_bogus,
struct module_qstate* qstate, char* reasonbuf, size_t reasonlen)
struct module_qstate* qstate, struct val_qstate* vq, char* reasonbuf,
size_t reasonlen)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)
nsec->entry.data;
@@ -189,7 +190,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve,
if(d->security == sec_status_secure)
return 1;
d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason,
reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified,
reason_bogus, LDNS_SECTION_AUTHORITY, qstate, vq, &verified,
reasonbuf, reasonlen);
if(d->security == sec_status_secure) {
rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
@@ -203,7 +204,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
struct query_info* qinfo, struct reply_info* rep,
struct key_entry_key* kkey, time_t* proof_ttl, char** reason,
sldns_ede_code* reason_bogus, struct module_qstate* qstate,
char* reasonbuf, size_t reasonlen)
struct val_qstate* vq, char* reasonbuf, size_t reasonlen)
{
struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
@@ -221,7 +222,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
* 2) this is not a delegation point */
if(nsec) {
if(!nsec_verify_rrset(env, ve, nsec, kkey, reason,
reason_bogus, qstate, reasonbuf, reasonlen)) {
reason_bogus, qstate, vq, reasonbuf, reasonlen)) {
verbose(VERB_ALGO, "NSEC RRset for the "
"referral did not verify.");
return sec_status_bogus;
@@ -258,7 +259,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC))
continue;
if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason,
reason_bogus, qstate, reasonbuf, reasonlen)) {
reason_bogus, qstate, vq, reasonbuf, reasonlen)) {
verbose(VERB_ALGO, "NSEC for empty non-terminal "
"did not verify.");
*reason = "NSEC for empty non-terminal "
+4 -1
View File
@@ -52,6 +52,7 @@ struct ub_packed_rrset_key;
struct reply_info;
struct query_info;
struct key_entry_key;
struct val_qstate;
/**
* Check DS absence.
@@ -68,6 +69,7 @@ struct key_entry_key;
* @param reason: string explaining why bogus.
* @param reason_bogus: relevant EDE code for validation failure.
* @param qstate: qstate with region.
* @param vq: validator qstate.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return security status.
@@ -80,7 +82,8 @@ enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env,
struct val_env* ve, struct query_info* qinfo,
struct reply_info* rep, struct key_entry_key* kkey,
time_t* proof_ttl, char** reason, sldns_ede_code* reason_bogus,
struct module_qstate* qstate, char* reasonbuf, size_t reasonlen);
struct module_qstate* qstate, struct val_qstate* vq, char* reasonbuf,
size_t reasonlen);
/**
* nsec typemap check, takes an NSEC-type bitmap as argument, checks for type.
+6 -4
View File
@@ -1521,7 +1521,8 @@ static int
list_is_secure(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus,
struct module_qstate* qstate, char* reasonbuf, size_t reasonlen)
struct module_qstate* qstate, struct val_qstate* vq, char* reasonbuf,
size_t reasonlen)
{
struct packed_rrset_data* d;
size_t i;
@@ -1537,7 +1538,7 @@ list_is_secure(struct module_env* env, struct val_env* ve,
continue;
d->security = val_verify_rrset_entry(env, ve, list[i], kkey,
reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate,
&verified, reasonbuf, reasonlen);
vq, &verified, reasonbuf, reasonlen);
if(d->security != sec_status_secure) {
verbose(VERB_ALGO, "NSEC3 did not verify");
return 0;
@@ -1552,7 +1553,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, char** reason,
sldns_ede_code* reason_bogus, struct module_qstate* qstate,
struct nsec3_cache_table* ct, char* reasonbuf, size_t reasonlen)
struct val_qstate* vq, struct nsec3_cache_table* ct, char* reasonbuf,
size_t reasonlen)
{
struct nsec3_filter flt;
struct ce_response ce;
@@ -1568,7 +1570,7 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
return sec_status_bogus; /* no valid NSEC3s, bogus */
}
if(!list_is_secure(env, ve, list, num, kkey, reason, reason_bogus,
qstate, reasonbuf, reasonlen)) {
qstate, vq, reasonbuf, reasonlen)) {
*reason = "not all NSEC3 records secure";
return sec_status_bogus; /* not all NSEC3 records secure */
}
+4 -1
View File
@@ -78,6 +78,7 @@ struct reply_info;
struct query_info;
struct key_entry_key;
struct sldns_buffer;
struct val_qstate;
/**
* 0 1 2 3 4 5 6 7
@@ -215,6 +216,7 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
* @param reason: string for bogus result.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate.
* @param ct: cached hashes table.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
@@ -230,7 +232,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, char** reason,
sldns_ede_code* reason_bogus, struct module_qstate* qstate,
struct nsec3_cache_table* ct, char* reasonbuf, size_t reasonlen);
struct val_qstate* vq, struct nsec3_cache_table* ct, char* reasonbuf,
size_t reasonlen);
/**
* Prove NXDOMAIN or NODATA.
+108 -8
View File
@@ -82,6 +82,8 @@
/** Maximum number of RRSIG validations for an RRset. */
#define MAX_VALIDATE_RRSIGS 8
/** Maximum number of NSEC validations for a message. */
#define MAX_VALIDATE_NSECS 8
/** return number of rrs in an rrset */
static size_t
@@ -305,6 +307,8 @@ ds_create_dnskey_digest(struct module_env* env,
* digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
* DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. */
sldns_buffer_clear(b);
if(!sldns_buffer_available(b, dnskey_rrset->rk.dname_len + dnskey_len-2))
return 0; /* buffer too small */
sldns_buffer_write(b, dnskey_rrset->rk.dname,
dnskey_rrset->rk.dname_len);
query_dname_tolower(sldns_buffer_begin(b));
@@ -546,8 +550,10 @@ int algo_needs_missing(struct algo_needs* n)
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param numverified: incremented when the number of RRSIG validations
* increases.
* @param num_tagmatches: incremented for tag matches.
* @return secure if any key signs *this* signature. bogus if no key signs it,
* unchecked on error, or indeterminate if all keys are not supported by
* the crypto library (openssl3+ only).
@@ -559,7 +565,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
struct rbtree_type** sortree,
char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
int* numverified)
struct val_qstate* vq, int* numverified, size_t* num_tagmatches)
{
/* find matching keys and check them */
enum sec_status sec = sec_status_bogus;
@@ -578,6 +584,14 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
}
for(i=0; i<num; i++) {
if((*num_tagmatches)++ > MAX_TAG_MATCHES) {
*reason = "too many tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "verify sig: too many tag matches, "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
return sec_status_bogus;
}
/* see if key matches keytag and algo */
if(algo != dnskey_get_algo(dnskey, i) ||
tag != dnskey_calc_keytag(dnskey, i))
@@ -585,6 +599,26 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
numchecked ++;
(*numverified)++;
if(vq && vq->num_validation_attempts++ > env->cfg->val_validation_attempts) {
*reason = "too many validation attempts";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "verify sig: too many validation attempts, "
"val-validation-attempts (%d); bogus", env->cfg->val_validation_attempts);
return sec_status_bogus;
}
if(vq && (ntohs(rrset->rk.type) == LDNS_RR_TYPE_NSEC ||
ntohs(rrset->rk.type) == LDNS_RR_TYPE_NSEC3) &&
vq->num_nsec_attempts++ > MAX_VALIDATE_NSECS) {
*reason = "too many NSEC or NSEC3 validation attempts";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "verify sig: too many NSEC or NSEC3 validation attempts, "
"(%d); bogus", MAX_VALIDATE_NSECS);
vq->num_nsec_attempts_exceeded = 1;
return sec_status_bogus;
}
/* see if key verifies */
sec = dnskey_verify_rrset_sig(env->scratch,
env->scratch_buffer, ve, now, rrset, dnskey, i,
@@ -624,11 +658,12 @@ enum sec_status
dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate, int* verified,
char* reasonbuf, size_t reasonlen)
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, int* verified, char* reasonbuf,
size_t reasonlen)
{
enum sec_status sec;
size_t i, num;
size_t i, num, num_tagmatches = 0;
rbtree_type* sortree = NULL;
/* make sure that for all DNSKEY algorithms there are valid sigs */
struct algo_needs needs;
@@ -656,9 +691,19 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
}
}
for(i=0; i<num; i++) {
if(num_tagmatches > MAX_TAG_MATCHES) {
*reason = "too many tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "rrset failed to verify: too many tag matches, "
"MAX_TAG_MATCHES (%d)", MAX_TAG_MATCHES);
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset,
dnskey, i, &sortree, reason, reason_bogus,
section, qstate, verified);
section, qstate, vq, verified, &num_tagmatches);
/* see which algorithm has been fixed up */
if(sec == sec_status_secure) {
if(!sigalg)
@@ -707,7 +752,8 @@ enum sec_status
dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate)
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, size_t* num_tagmatches)
{
enum sec_status sec;
size_t i, num, numchecked = 0, numindeterminate = 0;
@@ -728,9 +774,26 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
}
for(i=0; i<num; i++) {
/* see if sig matches keytag and algo */
if((*num_tagmatches)++ > MAX_TAG_MATCHES) {
*reason = "too many tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "rrset failed to verify: too many tag matches, "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
return sec_status_bogus;
}
if(algo != rrset_get_sig_algo(rrset, i) ||
tag != rrset_get_sig_keytag(rrset, i))
continue;
if(vq && vq->num_validation_attempts++ > env->cfg->val_validation_attempts) {
*reason = "too many validation attempts";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "rrset failed to verify: too many validation attempts, "
"val-validation-attempts (%d); bogus", env->cfg->val_validation_attempts);
return sec_status_bogus;
}
buf_canon = 0;
sec = dnskey_verify_rrset_sig(env->scratch,
env->scratch_buffer, ve, *env->now, rrset,
@@ -1308,15 +1371,32 @@ rrset_canonical(struct regional* region, sldns_buffer* buf,
}
sldns_buffer_clear(buf);
if(sldns_buffer_remaining(buf) < siglen || siglen < 18+1) {
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
sldns_buffer_write(buf, sig, siglen);
/* canonicalize signer name */
canon_dname_tolower(sldns_buffer_begin(buf)+18,
sldns_buffer_current(buf));
if(sldns_buffer_remaining(buf) < k->rk.dname_len+2) {
/* Check if the first can_owner name can fit in the buffer.
* The length is k->rk.dname_len or k->rk.dname_len+2
* if it has '*.' in prefixed. Checks the upper bound,
* also realistically the rest of the rrtype, rrclass, origttl,
* rdata and so on has to be inserted, so that extra space has
* to be there. */
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
RBTREE_FOR(walk, struct canon_rr*, (*sortree)) {
/* see if there is enough space left in the buffer */
if(sldns_buffer_remaining(buf) < can_owner_len + 2 + 2 + 4
+ d->rr_len[walk->rr_idx]) {
log_err("verify: failed to canonicalize, "
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
@@ -1325,6 +1405,13 @@ rrset_canonical(struct regional* region, sldns_buffer* buf,
sldns_buffer_write(buf, can_owner, can_owner_len);
else insert_can_owner(buf, k, sig, &can_owner,
&can_owner_len);
/* Check again, if the rdata can fit in the buffer */
if(sldns_buffer_remaining(buf) < 2 + 2 + 4
+ d->rr_len[walk->rr_idx]) {
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
sldns_buffer_write(buf, &k->rk.type, 2);
sldns_buffer_write(buf, &k->rk.rrset_class, 2);
sldns_buffer_write(buf, sig+4, 4);
@@ -1376,11 +1463,17 @@ rrset_canonicalize_to_buffer(struct regional* region, sldns_buffer* buf,
canonical_sort(k, d, sortree, rrs);
sldns_buffer_clear(buf);
if(sldns_buffer_remaining(buf) < k->rk.dname_len) {
/* Check if the first can_owner name can fit in the buffer. */
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
RBTREE_FOR(walk, struct canon_rr*, sortree) {
/* see if there is enough space left in the buffer */
if(sldns_buffer_remaining(buf) < can_owner_len + 2 + 2 + 4
+ d->rr_len[walk->rr_idx]) {
log_err("verify: failed to canonicalize, "
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
@@ -1393,6 +1486,13 @@ rrset_canonicalize_to_buffer(struct regional* region, sldns_buffer* buf,
query_dname_tolower(can_owner);
can_owner_len = k->rk.dname_len;
}
/* Check again, if the rdata can fit in the buffer */
if(sldns_buffer_remaining(buf) < 2 + 2 + 4
+ d->rr_len[walk->rr_idx]) {
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
sldns_buffer_write(buf, &k->rk.type, 2);
sldns_buffer_write(buf, &k->rk.rrset_class, 2);
sldns_buffer_write_u32(buf, d->rr_ttl[walk->rr_idx]);
+9 -3
View File
@@ -53,6 +53,7 @@ struct ub_packed_rrset_key;
struct rbtree_type;
struct regional;
struct sldns_buffer;
struct val_qstate;
/** number of entries in algorithm needs array */
#define ALGO_NEEDS_MAX 256
@@ -262,6 +263,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx);
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param verified: if not NULL the number of RRSIG validations is returned.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
@@ -273,8 +275,9 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* rrset,
struct ub_packed_rrset_key* dnskey, uint8_t* sigalg,
char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate, int* verified,
char* reasonbuf, size_t reasonlen);
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, int* verified, char* reasonbuf,
size_t reasonlen);
/**
* verify rrset against one specific dnskey (from rrset)
@@ -287,13 +290,16 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env,
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param num_tagmatches: incremented to keep track of tag matches.
* @return secure if *this* key signs any of the signatures on rrset.
* unchecked on error or and bogus on bad signature.
*/
enum sec_status dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate);
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, size_t* num_tagmatches);
/**
* verify rrset, with specific dnskey(from set), for a specific rrsig
+86 -19
View File
@@ -406,7 +406,8 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys,
uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
int *verified, char* reasonbuf, size_t reasonlen)
struct val_qstate* vq, int *verified, char* reasonbuf,
size_t reasonlen)
{
enum sec_status sec;
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
@@ -431,7 +432,8 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason,
reason_bogus, section, qstate, verified, reasonbuf, reasonlen);
reason_bogus, section, qstate, vq, verified, reasonbuf,
reasonlen);
verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
regional_free_all(env->scratch);
@@ -475,7 +477,8 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey,
char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
int* verified, char* reasonbuf, size_t reasonlen)
struct val_qstate* vq, int* verified, char* reasonbuf,
size_t reasonlen)
{
/* temporary dnskey rrset-key */
struct ub_packed_rrset_key dnskey;
@@ -489,7 +492,8 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
dnskey.entry.key = &dnskey;
dnskey.entry.data = kd->rrset_data;
sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason,
reason_bogus, section, qstate, verified, reasonbuf, reasonlen);
reason_bogus, section, qstate, vq, verified, reasonbuf,
reasonlen);
return sec;
}
@@ -499,13 +503,20 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
int *nonechecked, char* reasonbuf, size_t reasonlen)
struct val_qstate* vq, int *nonechecked, char* reasonbuf,
size_t reasonlen, size_t* num_tagmatches,
size_t* num_tagmatches_dnskeysig)
{
enum sec_status sec = sec_status_bogus;
size_t i, num, numchecked = 0, numhashok = 0, numsizesupp = 0;
num = rrset_get_count(dnskey_rrset);
*nonechecked = 0;
for(i=0; i<num; i++) {
if((*num_tagmatches)++ > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "DS match attempt reached "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
return sec_status_bogus;
}
/* Skip DNSKEYs that don't match the basic criteria. */
if(ds_get_key_algo(ds_rrset, ds_idx)
!= dnskey_get_algo(dnskey_rrset, i)
@@ -518,6 +529,15 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
ds_get_key_algo(ds_rrset, ds_idx),
ds_get_keytag(ds_rrset, ds_idx));
if(vq && vq->num_hash_attempts++ > env->cfg->val_hash_attempts) {
*reason = "too many hash attempts";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "rrset failed to verify: too many hash attempts, "
"val-hash-attempts (%d); bogus", env->cfg->val_hash_attempts);
return sec_status_bogus;
}
/* Convert the candidate DNSKEY into a hash using the
* same DS hash algorithm. */
if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset,
@@ -541,8 +561,14 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
/* Otherwise, we have a match! Make sure that the DNSKEY
* verifies *with this key* */
if(*num_tagmatches_dnskeysig > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "DS that matched has too many DNSKEY to RRSIG tag matches "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
return sec_status_bogus;
}
sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset,
i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate);
i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate,
vq, num_tagmatches_dnskeysig);
if(sec == sec_status_secure) {
return sec;
}
@@ -586,14 +612,14 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
char* reasonbuf, size_t reasonlen)
struct val_qstate* vq, char* reasonbuf, size_t reasonlen)
{
/* as long as this is false, we can consider this DS rrset to be
* equivalent to no DS rrset. */
int has_useful_ds = 0, digest_algo, alg, has_algo_refusal = 0,
nonechecked, has_checked_ds = 0;
struct algo_needs needs;
size_t i, num;
size_t i, num, num_tagmatches = 0, num_tagmatches_dnskeysig = 0;
enum sec_status sec;
if(dnskey_rrset->rk.dname_len != ds_rrset->rk.dname_len ||
@@ -615,6 +641,13 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
}
num = rrset_get_count(ds_rrset);
for(i=0; i<num; i++) {
if(num_tagmatches > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "DS verify attempt reached "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "DS verify has too many tag matches";
return sec_status_bogus;
}
/* Check to see if we can understand this DS.
* And check it is the strongest digest */
if(!ds_digest_algo_is_supported(ds_rrset, i) ||
@@ -623,9 +656,16 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
continue;
}
if(num_tagmatches_dnskeysig > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "DS verify attempt reached "
"DNSKEY to RRSIG MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "DS verify has too many DNSKEY to RRSIG tag matches";
return sec_status_bogus;
}
sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset,
ds_rrset, i, reason, reason_bogus, qstate,
&nonechecked, reasonbuf, reasonlen);
ds_rrset, i, reason, reason_bogus, qstate, vq,
&nonechecked, reasonbuf, reasonlen, &num_tagmatches,
&num_tagmatches_dnskeysig);
if(sec == sec_status_insecure) {
/* DNSKEY too large unsupported or algo refused by
* crypto lib. */
@@ -687,12 +727,12 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
char* reasonbuf, size_t reasonlen)
struct val_qstate* vq, char* reasonbuf, size_t reasonlen)
{
uint8_t sigalg[ALGO_NEEDS_MAX+1];
enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve,
dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason,
reason_bogus, qstate, reasonbuf, reasonlen);
reason_bogus, qstate, vq, reasonbuf, reasonlen);
if(sec == sec_status_secure) {
return key_entry_create_rrset(region,
@@ -718,14 +758,14 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* ta_ds,
struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
char* reasonbuf, size_t reasonlen)
struct val_qstate* vq, char* reasonbuf, size_t reasonlen)
{
/* as long as this is false, we can consider this anchor to be
* equivalent to no anchor. */
int has_useful_ta = 0, digest_algo = 0, alg, has_algo_refusal = 0,
nonechecked, has_checked_ds = 0;
struct algo_needs needs;
size_t i, num;
size_t i, num, num_tagmatches = 0, num_tagmatches_dnskeysig = 0;
enum sec_status sec;
if(ta_ds && (dnskey_rrset->rk.dname_len != ta_ds->rk.dname_len ||
@@ -761,6 +801,15 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
if(ta_ds) {
num = rrset_get_count(ta_ds);
for(i=0; i<num; i++) {
if(num_tagmatches > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "anchor DS verify attempt reached "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "anchor DS verify has too many tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
/* Check to see if we can understand this DS.
* And check it is the strongest digest */
if(!ds_digest_algo_is_supported(ta_ds, i) ||
@@ -768,9 +817,18 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
ds_get_digest_algo(ta_ds, i) != digest_algo)
continue;
if(num_tagmatches_dnskeysig > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "anchor DS verify has too many DNSKEY to RRSIG tag matches "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "anchor DS verify has too many DNSKEY to RRSIG tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset,
ta_ds, i, reason, reason_bogus, qstate, &nonechecked,
reasonbuf, reasonlen);
ta_ds, i, reason, reason_bogus, qstate, vq,
&nonechecked, reasonbuf, reasonlen, &num_tagmatches,
&num_tagmatches_dnskeysig);
if(sec == sec_status_insecure) {
has_algo_refusal = 1;
continue;
@@ -813,8 +871,16 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
/* we saw a useful TA */
has_useful_ta = 1;
if(num_tagmatches_dnskeysig > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "anchor DS that matched has too many DNSKEY to RRSIG tag matches "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "anchor DS that matched has too many DNSKEY to RRSIG tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
sec = dnskey_verify_rrset(env, ve, dnskey_rrset,
ta_dnskey, i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate);
ta_dnskey, i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate, vq, &num_tagmatches_dnskeysig);
if(sec == sec_status_secure) {
if(!sigalg || algo_needs_set_secure(&needs,
(uint8_t)dnskey_get_algo(ta_dnskey, i))) {
@@ -862,12 +928,13 @@ val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env,
struct ub_packed_rrset_key* ta_ds_rrset,
struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot,
char** reason, sldns_ede_code *reason_bogus,
struct module_qstate* qstate, char* reasonbuf, size_t reasonlen)
struct module_qstate* qstate, struct val_qstate* vq, char* reasonbuf,
size_t reasonlen)
{
uint8_t sigalg[ALGO_NEEDS_MAX+1];
enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve,
dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset,
downprot?sigalg:NULL, reason, reason_bogus, qstate,
downprot?sigalg:NULL, reason, reason_bogus, qstate, vq,
reasonbuf, reasonlen);
if(sec == sec_status_secure) {
+16 -5
View File
@@ -55,6 +55,11 @@ struct regional;
struct val_anchors;
struct rrset_cache;
struct sock_list;
struct val_qstate;
/** Maximum number of matches with key tag and algorithm, for DNSKEY to
* RRSIG and DS to DNSKEY. Since the number is O(N*N), there is a limit. */
#define MAX_TAG_MATCHES 256
/**
* Response classifications for the validator. The different types of proofs.
@@ -124,6 +129,7 @@ void val_find_signer(enum val_classification subtype,
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param verified: if not NULL, the number of RRSIG validations is returned.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
@@ -133,7 +139,8 @@ enum sec_status val_verify_rrset_entry(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* rrset,
struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
int* verified, char* reasonbuf, size_t reasonlen);
struct val_qstate* vq, int* verified, char* reasonbuf,
size_t reasonlen);
/**
* Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but
@@ -148,6 +155,7 @@ enum sec_status val_verify_rrset_entry(struct module_env* env,
* @param reason: reason of failure. Fixed string or alloced in scratch.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return: sec_status_secure if a DS matches.
@@ -158,7 +166,7 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
char* reasonbuf, size_t reasonlen);
struct val_qstate* vq, char* reasonbuf, size_t reasonlen);
/**
* Verify DNSKEYs with DS and DNSKEY rrset. Like val_verify_DNSKEY_with_DS
@@ -174,6 +182,7 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env,
* @param reason: reason of failure. Fixed string or alloced in scratch.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return: sec_status_secure if a DS matches.
@@ -185,7 +194,7 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env,
struct ub_packed_rrset_key* ta_ds,
struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
char* reasonbuf, size_t reasonlen);
struct val_qstate* vq, char* reasonbuf, size_t reasonlen);
/**
* Verify new DNSKEYs with DS rrset. The DS contains hash values that should
@@ -202,6 +211,7 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env,
* @param reason: reason of failure. Fixed string or alloced in scratch.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return a KeyEntry. This will either contain the now trusted
@@ -219,7 +229,7 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region,
struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
char* reasonbuf, size_t reasonlen);
struct val_qstate* vq, char* reasonbuf, size_t reasonlen);
/**
* Verify rrset with trust anchor: DS and DNSKEY rrset.
@@ -235,6 +245,7 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region,
* @param reason: reason of failure. Fixed string or alloced in scratch.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return a KeyEntry. This will either contain the now trusted
@@ -253,7 +264,7 @@ struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region,
struct ub_packed_rrset_key* ta_ds_rrset,
struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot,
char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate,
char* reasonbuf, size_t reasonlen);
struct val_qstate* vq, char* reasonbuf, size_t reasonlen);
/**
* Determine if DS rrset is usable for validator or not.
+75 -34
View File
@@ -350,13 +350,17 @@ static void
val_restart(struct val_qstate* vq)
{
struct comm_timer* temp_timer;
int restart_count;
int restart_count, num_validation_attempts, num_hash_attempts;
if(!vq) return;
temp_timer = vq->suspend_timer;
restart_count = vq->restart_count+1;
num_validation_attempts = vq->num_validation_attempts;
num_hash_attempts = vq->num_hash_attempts;
memset(vq, 0, sizeof(*vq));
vq->suspend_timer = temp_timer;
vq->restart_count = restart_count;
vq->num_validation_attempts = num_validation_attempts;
vq->num_hash_attempts = num_hash_attempts;
vq->state = VAL_INIT_STATE;
}
@@ -455,6 +459,24 @@ already_validated(struct dns_msg* ret_msg)
return 0;
}
/** If it is possible to restart the validation state */
static int
val_can_restart(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve)
{
/* For validation failures that are limits exceeded on the amount
* of work that the DNSSEC validator is willing to do, the restart
* is not allowed. A restart would increase the amount of effort
* spent even further. */
if(vq->restart_count < ve->max_restart &&
vq->num_validation_attempts <= qstate->env->cfg->val_validation_attempts &&
vq->num_hash_attempts <= qstate->env->cfg->val_hash_attempts &&
!vq->num_nsec_attempts_exceeded)
return 1;
(void)qstate;
return 0;
}
/**
* Generate a request for DNS data.
*
@@ -763,8 +785,8 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
/* Verify the answer rrset */
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified,
reasonbuf, sizeof(reasonbuf));
&reason_bogus, LDNS_SECTION_ANSWER, qstate, vq,
&verified, reasonbuf, sizeof(reasonbuf));
/* If the (answer) rrset failed to validate, then this
* message is BAD. */
if(sec != sec_status_secure) {
@@ -808,7 +830,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
continue;
s = chase_reply->rrsets[i];
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate,
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate, vq,
&verified, reasonbuf, sizeof(reasonbuf));
/* If anything in the authority section fails to be secure,
* we have a bad message. */
@@ -855,7 +877,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
if(sname && query_dname_compare(sname, key_entry->name)==0)
(void)val_verify_rrset_entry(env, ve, s, key_entry,
&reason, NULL, LDNS_SECTION_ADDITIONAL, qstate,
&verified, reasonbuf, sizeof(reasonbuf));
vq, &verified, reasonbuf, sizeof(reasonbuf));
/* the additional section can fail to be secure,
* it is optional, check signature in case we need
* to clean the additional section later. */
@@ -1433,16 +1455,20 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve,
* trusted DNSKEY rrset that signs this response must already have been
* completed.
*
* @param env: module env.
* @param chase_reply: answer to validate.
*/
static void
validate_referral_response(struct reply_info* chase_reply)
validate_referral_response(struct module_env* env, struct reply_info* chase_reply)
{
size_t i;
size_t i, count;
enum sec_status s;
/* message security equals lowest rrset security */
chase_reply->security = sec_status_secure;
for(i=0; i<chase_reply->rrset_count; i++) {
if(env->cfg->val_clean_additional)
count = chase_reply->rrset_count;
else count = chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
for(i=0; i<count; i++) {
s = ((struct packed_rrset_data*)chase_reply->rrsets[i]
->entry.data)->security;
if(s < chase_reply->security)
@@ -2309,7 +2335,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
key_entry_get_reason_bogus(vq->key_entry));
errinf_ede(qstate, "while building chain of trust",
key_entry_get_reason_bogus(vq->key_entry));
if(vq->restart_count >= ve->max_restart)
if(!val_can_restart(qstate, vq, ve))
key_cache_insert(ve->kcache, vq->key_entry,
qstate->env->cfg->val_log_level >= 2);
return 1;
@@ -2457,7 +2483,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
case VAL_CLASS_REFERRAL:
verbose(VERB_ALGO, "Validating a referral response");
validate_referral_response(vq->chase_reply);
validate_referral_response(qstate->env, vq->chase_reply);
verbose(VERB_DETAIL, "validate(referral): %s",
sec_status_to_string(
vq->chase_reply->security));
@@ -2531,15 +2557,17 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
}
if(subtype == VAL_CLASS_REFERRAL) {
/* for a referral, move to next unchecked rrset and check it*/
vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep,
vq->rrset_skip);
if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
/* and restart for this rrset */
verbose(VERB_ALGO, "validator: go to next rrset");
vq->chase_reply->security = sec_status_unchecked;
vq->state = VAL_INIT_STATE;
return 1;
if(qstate->env->cfg->val_clean_additional) {
/* for a referral, move to next unchecked rrset and check it*/
vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep,
vq->rrset_skip);
if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
/* and restart for this rrset */
verbose(VERB_ALGO, "validator: go to next rrset");
vq->chase_reply->security = sec_status_unchecked;
vq->state = VAL_INIT_STATE;
return 1;
}
}
/* referral chase is done */
}
@@ -2584,7 +2612,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
struct msgreply_entry* e;
/* see if we can try again to fetch data */
if(vq->restart_count < ve->max_restart) {
if(val_can_restart(qstate, vq, ve)) {
verbose(VERB_ALGO, "validation failed, "
"blacklist and retry to fetch data");
val_blacklist(&qstate->blacklist, qstate->region,
@@ -2876,6 +2904,7 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id,
* (this rrset is allocated in the wrong region, not the qstate).
* @param ta: trust anchor.
* @param qstate: qstate that needs key.
* @param vq: validator qstate.
* @param id: module id.
* @param sub_qstate: the sub query state, that is the lookup that fetched
* the trust anchor data, it contains error information for the answer.
@@ -2886,8 +2915,8 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id,
*/
static struct key_entry_key*
primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
struct trust_anchor* ta, struct module_qstate* qstate, int id,
struct module_qstate* sub_qstate)
struct trust_anchor* ta, struct module_qstate* qstate,
struct val_qstate* vq, int id, struct module_qstate* sub_qstate)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* kkey = NULL;
@@ -2927,7 +2956,8 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
/* attempt to verify with trust anchor DS and DNSKEY */
kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve,
dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot,
&reason, &reason_bogus, qstate, reasonbuf, sizeof(reasonbuf));
&reason, &reason_bogus, qstate, vq, reasonbuf,
sizeof(reasonbuf));
if(!kkey) {
log_err("out of memory: verifying prime TA");
return NULL;
@@ -3040,7 +3070,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
* bogus, then we are done. */
sec = val_verify_rrset_entry(qstate->env, ve, ds,
vq->key_entry, &reason, &reason_bogus,
LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf,
LDNS_SECTION_ANSWER, qstate, vq, &verified, reasonbuf,
sizeof(reasonbuf));
if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did "
@@ -3091,7 +3121,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
/* Try to prove absence of the DS with NSEC */
sec = val_nsec_prove_nodata_dsreply(
qstate->env, ve, qinfo, msg->rep, vq->key_entry,
&proof_ttl, &reason, &reason_bogus, qstate,
&proof_ttl, &reason, &reason_bogus, qstate, vq,
reasonbuf, sizeof(reasonbuf));
switch(sec) {
case sec_status_secure:
@@ -3129,7 +3159,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
sec = nsec3_prove_nods(qstate->env, ve,
msg->rep->rrsets + msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason,
&reason_bogus, qstate, &vq->nsec3_cache_table,
&reason_bogus, qstate, vq, &vq->nsec3_cache_table,
reasonbuf, sizeof(reasonbuf));
switch(sec) {
case sec_status_insecure:
@@ -3197,7 +3227,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
}
sec = val_verify_rrset_entry(qstate->env, ve, cname,
vq->key_entry, &reason, &reason_bogus,
LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf,
LDNS_SECTION_ANSWER, qstate, vq, &verified, reasonbuf,
sizeof(reasonbuf));
if(sec == sec_status_secure) {
/* Check for wildcard expansion */
@@ -3318,6 +3348,7 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
uint8_t* olds = vq->empty_DS_name;
int ret;
*suspend = 0;
vq->num_nsec_attempts = 0;
vq->empty_DS_name = NULL;
if(sub_qstate && sub_qstate->rpz_applied) {
verbose(VERB_ALGO, "rpz was applied to the DS lookup, "
@@ -3329,6 +3360,8 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
}
ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske,
sub_qstate);
/* New NSEC attempt count for next message validation. */
vq->num_nsec_attempts = 0;
if(ret != 0) {
switch(ret) {
case 1:
@@ -3370,7 +3403,7 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
vq->chain_blacklist = NULL; /* fresh blacklist for next part*/
/* Keep the forState.state on FINDKEY. */
} else if(key_entry_isbad(dske)
&& vq->restart_count < ve->max_restart) {
&& val_can_restart(qstate, vq, ve)) {
vq->empty_DS_name = olds;
val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1);
qstate->errinf = NULL;
@@ -3420,6 +3453,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
vq->num_nsec_attempts = 0;
if(sub_qstate && sub_qstate->rpz_applied) {
verbose(VERB_ALGO, "rpz was applied to the DNSKEY lookup, "
"make it insecure");
@@ -3439,7 +3473,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
"DNSKEY query.");
if(vq->restart_count < ve->max_restart) {
if(val_can_restart(qstate, vq, ve)) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
@@ -3476,7 +3510,9 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
downprot = qstate->env->cfg->harden_algo_downgrade;
vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env,
ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus,
qstate, reasonbuf, sizeof(reasonbuf));
qstate, vq, reasonbuf, sizeof(reasonbuf));
/* New NSEC attempt count for next message validation. */
vq->num_nsec_attempts = 0;
if(!vq->key_entry) {
log_err("out of memory in verify new DNSKEYs");
@@ -3487,7 +3523,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
* state. */
if(!key_entry_isgood(vq->key_entry)) {
if(key_entry_isbad(vq->key_entry)) {
if(vq->restart_count < ve->max_restart) {
if(val_can_restart(qstate, vq, ve)) {
val_blacklist(&vq->chain_blacklist,
qstate->region, origin, 1);
qstate->errinf = NULL;
@@ -3539,6 +3575,7 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
struct trust_anchor* ta = anchor_find(qstate->env->anchors,
vq->trust_anchor_name, vq->trust_anchor_labs,
vq->trust_anchor_len, vq->qchase.qclass);
vq->num_nsec_attempts = 0;
if(!ta) {
/* trust anchor revoked, restart with less anchors */
vq->state = VAL_INIT_STATE;
@@ -3557,19 +3594,23 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
if(ta->autr) {
if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset,
qstate)) {
qstate, vq)) {
/* New NSEC attempt count for next message validation. */
vq->num_nsec_attempts = 0;
/* trust anchor revoked, restart with less anchors */
vq->state = VAL_INIT_STATE;
vq->trust_anchor_name = NULL;
return;
}
}
vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id,
vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, vq, id,
sub_qstate);
lock_basic_unlock(&ta->lock);
/* New NSEC attempt count for next message validation. */
vq->num_nsec_attempts = 0;
if(vq->key_entry) {
if(key_entry_isbad(vq->key_entry)
&& vq->restart_count < ve->max_restart) {
&& val_can_restart(qstate, vq, ve)) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
+13
View File
@@ -231,6 +231,19 @@ struct val_qstate {
struct comm_timer* suspend_timer;
/** Number of suspends */
int suspend_count;
/** Number of DNSKEY RRSIG validation attempts. This is the number of
* cryptographic operations done for the mesh state. */
int num_validation_attempts;
/** Number of DS hash verification attempts. This is the number of
* hash operations done for the mesh state.
* It does not count NSEC3 hashes. */
int num_hash_attempts;
/** Number of NSEC validations. And NSEC3 too. This is reset per
* answer. */
int num_nsec_attempts;
/** The nsec attempts have been exceeded. */
int num_nsec_attempts_exceeded;
};
/**