unbound: Vendor import 1.19.1

Release notes at
    https://www.nlnetlabs.nl/news/2024/Feb/13/unbound-1.19.1-released/

Security:	CVE-2023-50387, CVE-2023-50868
MFC after:	3 days
This commit is contained in:
Cy Schubert 2023-11-13 11:44:16 -08:00
commit b76ef9a7cb
28 changed files with 889 additions and 207 deletions

View file

@ -1,10 +1,10 @@
#! /bin/sh #! /bin/sh
# Attempt to guess a canonical system name. # Attempt to guess a canonical system name.
# Copyright 1992-2023 Free Software Foundation, Inc. # Copyright 1992-2024 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale # shellcheck disable=SC2006,SC2268 # see below for rationale
timestamp='2023-08-22' timestamp='2024-01-01'
# This file is free software; you can redistribute it and/or modify it # This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by # under the terms of the GNU General Public License as published by
@ -60,7 +60,7 @@ version="\
GNU config.guess ($timestamp) GNU config.guess ($timestamp)
Originally written by Per Bothner. Originally written by Per Bothner.
Copyright 1992-2023 Free Software Foundation, Inc. Copyright 1992-2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@ -165,6 +165,8 @@ Linux|GNU|GNU/*)
LIBC=dietlibc LIBC=dietlibc
#elif defined(__GLIBC__) #elif defined(__GLIBC__)
LIBC=gnu LIBC=gnu
#elif defined(__LLVM_LIBC__)
LIBC=llvm
#else #else
#include <stdarg.h> #include <stdarg.h>
/* First heuristic to detect musl libc. */ /* First heuristic to detect musl libc. */
@ -1593,6 +1595,9 @@ EOF
*:Unleashed:*:*) *:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;; ;;
*:Ironclad:*:*)
GUESS=$UNAME_MACHINE-unknown-ironclad
;;
esac esac
# Do we have a guess based on uname results? # Do we have a guess based on uname results?

View file

@ -1,10 +1,10 @@
#! /bin/sh #! /bin/sh
# Configuration validation subroutine script. # Configuration validation subroutine script.
# Copyright 1992-2023 Free Software Foundation, Inc. # Copyright 1992-2024 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale # shellcheck disable=SC2006,SC2268 # see below for rationale
timestamp='2023-09-19' timestamp='2024-01-01'
# This file is free software; you can redistribute it and/or modify it # This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by # under the terms of the GNU General Public License as published by
@ -76,7 +76,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\ version="\
GNU config.sub ($timestamp) GNU config.sub ($timestamp)
Copyright 1992-2023 Free Software Foundation, Inc. Copyright 1992-2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@ -1222,6 +1222,7 @@ case $cpu-$vendor in
| moxie \ | moxie \
| mt \ | mt \
| msp430 \ | msp430 \
| nanomips* \
| nds32 | nds32le | nds32be \ | nds32 | nds32le | nds32be \
| nfp \ | nfp \
| nios | nios2 | nios2eb | nios2el \ | nios | nios2 | nios2eb | nios2el \
@ -1253,6 +1254,7 @@ case $cpu-$vendor in
| ubicom32 \ | ubicom32 \
| v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
| vax \ | vax \
| vc4 \
| visium \ | visium \
| w65 \ | w65 \
| wasm32 | wasm64 \ | wasm32 | wasm64 \
@ -1597,7 +1599,7 @@ case $cpu-$vendor in
os= os=
obj=elf obj=elf
;; ;;
mips*-*) mips*-*|nanomips*-*)
os= os=
obj=elf obj=elf
;; ;;
@ -1721,7 +1723,7 @@ fi
case $os in case $os in
# Sometimes we do "kernel-libc", so those need to count as OSes. # Sometimes we do "kernel-libc", so those need to count as OSes.
musl* | newlib* | relibc* | uclibc*) llvm* | musl* | newlib* | relibc* | uclibc*)
;; ;;
# Likewise for "kernel-abi" # Likewise for "kernel-abi"
eabi* | gnueabi*) eabi* | gnueabi*)
@ -1766,12 +1768,19 @@ case $os in
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
| fiwix* | mlibc* | cos* | mbr* ) | fiwix* | mlibc* | cos* | mbr* | ironclad* )
;; ;;
# This one is extra strict with allowed versions # This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*) sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
# Don't forget version if it is 3.2v4 or newer. # Don't forget version if it is 3.2v4 or newer.
;; ;;
# This refers to builds using the UEFI calling convention
# (which depends on the architecture) and PE file format.
# Note that this is both a different calling convention and
# different file format than that of GNU-EFI
# (x86_64-w64-mingw32).
uefi)
;;
none) none)
;; ;;
kernel* | msvc* ) kernel* | msvc* )
@ -1818,8 +1827,9 @@ esac
# As a final step for OS-related things, validate the OS-kernel combination # As a final step for OS-related things, validate the OS-kernel combination
# (given a valid OS), if there is a kernel. # (given a valid OS), if there is a kernel.
case $kernel-$os-$obj in case $kernel-$os-$obj in
linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \ linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \
| linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- ) | linux-mlibc*- | linux-musl*- | linux-newlib*- \
| linux-relibc*- | linux-uclibc*- )
;; ;;
uclinux-uclibc*- ) uclinux-uclibc*- )
;; ;;
@ -1827,7 +1837,8 @@ case $kernel-$os-$obj in
;; ;;
windows*-msvc*-) windows*-msvc*-)
;; ;;
-dietlibc*- | -newlib*- | -musl*- | -relibc*- | -uclibc*- | -mlibc*- ) -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \
| -uclibc*- )
# These are just libc implementations, not actual OSes, and thus # These are just libc implementations, not actual OSes, and thus
# require a kernel. # require a kernel.
echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2

View file

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for unbound 1.19.0. # Generated by GNU Autoconf 2.69 for unbound 1.19.1.
# #
# Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>. # Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
# #
@ -591,8 +591,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='unbound' PACKAGE_NAME='unbound'
PACKAGE_TARNAME='unbound' PACKAGE_TARNAME='unbound'
PACKAGE_VERSION='1.19.0' PACKAGE_VERSION='1.19.1'
PACKAGE_STRING='unbound 1.19.0' PACKAGE_STRING='unbound 1.19.1'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL='' PACKAGE_URL=''
@ -1477,7 +1477,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures unbound 1.19.0 to adapt to many kinds of systems. \`configure' configures unbound 1.19.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1543,7 +1543,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of unbound 1.19.0:";; short | recursive ) echo "Configuration of unbound 1.19.1:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1785,7 +1785,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
unbound configure 1.19.0 unbound configure 1.19.1
generated by GNU Autoconf 2.69 generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc. Copyright (C) 2012 Free Software Foundation, Inc.
@ -2494,7 +2494,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by unbound $as_me 1.19.0, which was It was created by unbound $as_me 1.19.1, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@ $ $0 $@
@ -2846,11 +2846,11 @@ UNBOUND_VERSION_MAJOR=1
UNBOUND_VERSION_MINOR=19 UNBOUND_VERSION_MINOR=19
UNBOUND_VERSION_MICRO=0 UNBOUND_VERSION_MICRO=1
LIBUNBOUND_CURRENT=9 LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=23 LIBUNBOUND_REVISION=24
LIBUNBOUND_AGE=1 LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0 # 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0 # 1.0.1 had 0:13:0
@ -2941,6 +2941,7 @@ LIBUNBOUND_AGE=1
# 1.17.1 had 9:21:1 # 1.17.1 had 9:21:1
# 1.18.0 had 9:22:1 # 1.18.0 had 9:22:1
# 1.19.0 had 9:23:1 # 1.19.0 had 9:23:1
# 1.19.1 had 9:24:1
# Current -- the number of the binary API that we're implementing # Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary # Revision -- which iteration of the implementation of the binary
@ -21894,7 +21895,7 @@ _ACEOF
version=1.19.0 version=1.19.1
date=`date +'%b %e, %Y'` date=`date +'%b %e, %Y'`
@ -22413,7 +22414,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by unbound $as_me 1.19.0, which was This file was extended by unbound $as_me 1.19.1, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -22479,7 +22480,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
unbound config.status 1.19.0 unbound config.status 1.19.1
configured by $0, generated by GNU Autoconf 2.69, configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View file

@ -11,14 +11,14 @@ sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing # must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1]) m4_define([VERSION_MAJOR],[1])
m4_define([VERSION_MINOR],[19]) m4_define([VERSION_MINOR],[19])
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_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_MAJOR, [VERSION_MAJOR])
AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR]) AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO]) AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9 LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=23 LIBUNBOUND_REVISION=24
LIBUNBOUND_AGE=1 LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0 # 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0 # 1.0.1 had 0:13:0
@ -109,6 +109,7 @@ LIBUNBOUND_AGE=1
# 1.17.1 had 9:21:1 # 1.17.1 had 9:21:1
# 1.18.0 had 9:22:1 # 1.18.0 had 9:22:1
# 1.19.0 had 9:23:1 # 1.19.0 had 9:23:1
# 1.19.1 had 9:24:1
# Current -- the number of the binary API that we're implementing # Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary # Revision -- which iteration of the implementation of the binary

View file

@ -1,4 +1,4 @@
README for Unbound 1.19.0 README for Unbound 1.19.1
Copyright 2007 NLnet Labs Copyright 2007 NLnet Labs
http://unbound.net http://unbound.net

View file

@ -1,7 +1,7 @@
# #
# Example configuration file. # Example configuration file.
# #
# See unbound.conf(5) man page, version 1.19.0. # See unbound.conf(5) man page, version 1.19.1.
# #
# this is a comment. # this is a comment.

View file

@ -1,4 +1,4 @@
.TH "libunbound" "3" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0" .TH "libunbound" "3" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
.\" .\"
.\" libunbound.3 -- unbound library functions manual .\" libunbound.3 -- unbound library functions manual
.\" .\"
@ -44,7 +44,7 @@
.B ub_ctx_zone_remove, .B ub_ctx_zone_remove,
.B ub_ctx_data_add, .B ub_ctx_data_add,
.B ub_ctx_data_remove .B ub_ctx_data_remove
\- Unbound DNS validating resolver 1.19.0 functions. \- Unbound DNS validating resolver 1.19.1 functions.
.SH "SYNOPSIS" .SH "SYNOPSIS"
.B #include <unbound.h> .B #include <unbound.h>
.LP .LP

View file

@ -1,4 +1,4 @@
.TH "unbound-anchor" "8" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0" .TH "unbound-anchor" "8" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
.\" .\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual .\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\" .\"

View file

@ -1,4 +1,4 @@
.TH "unbound-checkconf" "8" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0" .TH "unbound-checkconf" "8" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
.\" .\"
.\" unbound-checkconf.8 -- unbound configuration checker manual .\" unbound-checkconf.8 -- unbound configuration checker manual
.\" .\"

View file

@ -1,4 +1,4 @@
.TH "unbound-control" "8" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0" .TH "unbound-control" "8" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
.\" .\"
.\" unbound-control.8 -- unbound remote control manual .\" unbound-control.8 -- unbound remote control manual
.\" .\"

View file

@ -1,4 +1,4 @@
.TH "unbound\-host" "1" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0" .TH "unbound\-host" "1" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
.\" .\"
.\" unbound-host.1 -- unbound DNS lookup utility .\" unbound-host.1 -- unbound DNS lookup utility
.\" .\"

View file

@ -1,4 +1,4 @@
.TH "unbound" "8" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0" .TH "unbound" "8" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
.\" .\"
.\" unbound.8 -- unbound manual .\" unbound.8 -- unbound manual
.\" .\"
@ -9,7 +9,7 @@
.\" .\"
.SH "NAME" .SH "NAME"
.B unbound .B unbound
\- Unbound DNS validating resolver 1.19.0. \- Unbound DNS validating resolver 1.19.1.
.SH "SYNOPSIS" .SH "SYNOPSIS"
.B unbound .B unbound
.RB [ \-h ] .RB [ \-h ]

View file

@ -1,4 +1,4 @@
.TH "unbound.conf" "5" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0" .TH "unbound.conf" "5" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
.\" .\"
.\" unbound.conf.5 -- unbound.conf manual .\" unbound.conf.5 -- unbound.conf manual
.\" .\"

View file

@ -7774,6 +7774,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
enum sec_status sec; enum sec_status sec;
struct val_env* ve; struct val_env* ve;
int m; int m;
int verified = 0;
m = modstack_find(mods, "validator"); m = modstack_find(mods, "validator");
if(m == -1) { if(m == -1) {
auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have " auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have "
@ -7797,7 +7798,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
"zonemd: verify %s RRset with DNSKEY", typestr); "zonemd: verify %s RRset with DNSKEY", typestr);
} }
sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL, sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL,
LDNS_SECTION_ANSWER, NULL); LDNS_SECTION_ANSWER, NULL, &verified);
if(sec == sec_status_secure) { if(sec == sec_status_secure) {
return 1; return 1;
} }

View file

@ -690,6 +690,28 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
return msg; return msg;
} }
struct dns_msg*
dns_msg_deepcopy_region(struct dns_msg* origin, struct regional* region)
{
size_t i;
struct dns_msg* res = NULL;
res = gen_dns_msg(region, &origin->qinfo, origin->rep->rrset_count);
if(!res) return NULL;
*res->rep = *origin->rep;
if(origin->rep->reason_bogus_str) {
res->rep->reason_bogus_str = regional_strdup(region,
origin->rep->reason_bogus_str);
}
for(i=0; i<res->rep->rrset_count; i++) {
res->rep->rrsets[i] = packed_rrset_copy_region(
origin->rep->rrsets[i], region, 0);
if(!res->rep->rrsets[i]) {
return NULL;
}
}
return res;
}
/** synthesize RRset-only response from cached RRset item */ /** synthesize RRset-only response from cached RRset item */
static struct dns_msg* static struct dns_msg*
rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region,

View file

@ -164,6 +164,15 @@ struct dns_msg* tomsg(struct module_env* env, struct query_info* q,
struct reply_info* r, struct regional* region, time_t now, struct reply_info* r, struct regional* region, time_t now,
int allow_expired, struct regional* scratch); int allow_expired, struct regional* scratch);
/**
* Deep copy a dns_msg to a region.
* @param origin: the dns_msg to copy.
* @param region: the region to copy all the data to.
* @return the new dns_msg or NULL on malloc error.
*/
struct dns_msg* dns_msg_deepcopy_region(struct dns_msg* origin,
struct regional* region);
/** /**
* Find cached message * Find cached message
* @param env: module environment with the DNS cache. * @param env: module environment with the DNS cache.

View file

@ -199,6 +199,9 @@ SECTION QUESTION
example.com. IN ANY example.com. IN ANY
ENTRY_END ENTRY_END
; Allow validation resuming for the RRSIGs
STEP 21 TIME_PASSES ELAPSE 0.05
; recursion happens here. ; recursion happens here.
STEP 30 CHECK_ANSWER STEP 30 CHECK_ANSWER
ENTRY_BEGIN ENTRY_BEGIN

View file

@ -131,6 +131,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
else if(fptr == &pending_udp_timer_delay_cb) return 1; else if(fptr == &pending_udp_timer_delay_cb) return 1;
else if(fptr == &worker_stat_timer_cb) return 1; else if(fptr == &worker_stat_timer_cb) return 1;
else if(fptr == &worker_probe_timer_cb) return 1; else if(fptr == &worker_probe_timer_cb) return 1;
else if(fptr == &validate_suspend_timer_cb) return 1;
#ifdef UB_ON_WINDOWS #ifdef UB_ON_WINDOWS
else if(fptr == &wsvc_cron_cb) return 1; else if(fptr == &wsvc_cron_cb) return 1;
#endif #endif

View file

@ -181,6 +181,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve,
{ {
struct packed_rrset_data* d = (struct packed_rrset_data*) struct packed_rrset_data* d = (struct packed_rrset_data*)
nsec->entry.data; nsec->entry.data;
int verified = 0;
if(!d) return 0; if(!d) return 0;
if(d->security == sec_status_secure) if(d->security == sec_status_secure)
return 1; return 1;
@ -188,7 +189,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve,
if(d->security == sec_status_secure) if(d->security == sec_status_secure)
return 1; return 1;
d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason,
reason_bogus, LDNS_SECTION_AUTHORITY, qstate); reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified);
if(d->security == sec_status_secure) { if(d->security == sec_status_secure) {
rrset_update_sec_status(env->rrset_cache, nsec, *env->now); rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
return 1; return 1;

View file

@ -57,6 +57,19 @@
/* we include nsec.h for the bitmap_has_type function */ /* we include nsec.h for the bitmap_has_type function */
#include "validator/val_nsec.h" #include "validator/val_nsec.h"
#include "sldns/sbuffer.h" #include "sldns/sbuffer.h"
#include "util/config_file.h"
/**
* Max number of NSEC3 calculations at once, suspend query for later.
* 8 is low enough and allows for cases where multiple proofs are needed.
*/
#define MAX_NSEC3_CALCULATIONS 8
/**
* When all allowed NSEC3 calculations at once resulted in error treat as
* bogus. NSEC3 hash errors are not cached and this helps breaks loops with
* erroneous data.
*/
#define MAX_NSEC3_ERRORS -1
/** /**
* This function we get from ldns-compat or from base system * This function we get from ldns-compat or from base system
@ -532,6 +545,17 @@ nsec3_hash_cmp(const void* c1, const void* c2)
return memcmp(s1, s2, s1len); return memcmp(s1, s2, s1len);
} }
int
nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region)
{
if(ct->ct) return 1;
ct->ct = (rbtree_type*)regional_alloc(region, sizeof(*ct->ct));
if(!ct->ct) return 0;
ct->region = region;
rbtree_init(ct->ct, &nsec3_hash_cmp);
return 1;
}
size_t size_t
nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo,
size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max)
@ -646,7 +670,7 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf,
c = (struct nsec3_cached_hash*)rbtree_search(table, &looki); c = (struct nsec3_cached_hash*)rbtree_search(table, &looki);
if(c) { if(c) {
*hash = c; *hash = c;
return 1; return 2;
} }
/* create a new entry */ /* create a new entry */
c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c)); c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c));
@ -658,10 +682,10 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf,
c->dname_len = dname_len; c->dname_len = dname_len;
r = nsec3_calc_hash(region, buf, c); r = nsec3_calc_hash(region, buf, c);
if(r != 1) if(r != 1)
return r; return r; /* returns -1 or 0 */
r = nsec3_calc_b32(region, buf, c); r = nsec3_calc_b32(region, buf, c);
if(r != 1) if(r != 1)
return r; return r; /* returns 0 */
#ifdef UNBOUND_DEBUG #ifdef UNBOUND_DEBUG
n = n =
#else #else
@ -704,6 +728,7 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt,
struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s) struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s)
{ {
uint8_t* nm = s->rk.dname; uint8_t* nm = s->rk.dname;
if(!hash) return 0; /* please clang */
/* compare, does hash of name based on params in this NSEC3 /* compare, does hash of name based on params in this NSEC3
* match the owner name of this NSEC3? * match the owner name of this NSEC3?
* name must be: <hashlength>base32 . zone name * name must be: <hashlength>base32 . zone name
@ -730,34 +755,50 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt,
* @param nmlen: length of name. * @param nmlen: length of name.
* @param rrset: nsec3 that matches is returned here. * @param rrset: nsec3 that matches is returned here.
* @param rr: rr number in nsec3 rrset that matches. * @param rr: rr number in nsec3 rrset that matches.
* @param calculations: current hash calculations.
* @return true if a matching NSEC3 is found, false if not. * @return true if a matching NSEC3 is found, false if not.
*/ */
static int static int
find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt, find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt,
rbtree_type* ct, uint8_t* nm, size_t nmlen, struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen,
struct ub_packed_rrset_key** rrset, int* rr) struct ub_packed_rrset_key** rrset, int* rr,
int* calculations)
{ {
size_t i_rs; size_t i_rs;
int i_rr; int i_rr;
struct ub_packed_rrset_key* s; struct ub_packed_rrset_key* s;
struct nsec3_cached_hash* hash = NULL; struct nsec3_cached_hash* hash = NULL;
int r; int r;
int calc_errors = 0;
/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
for(s=filter_first(flt, &i_rs, &i_rr); s; for(s=filter_first(flt, &i_rs, &i_rr); s;
s=filter_next(flt, &i_rs, &i_rr)) { s=filter_next(flt, &i_rs, &i_rr)) {
/* check if we are allowed more calculations */
if(*calculations >= MAX_NSEC3_CALCULATIONS) {
if(calc_errors == *calculations) {
*calculations = MAX_NSEC3_ERRORS;
}
break;
}
/* get name hashed for this NSEC3 RR */ /* get name hashed for this NSEC3 RR */
r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer,
s, i_rr, nm, nmlen, &hash); s, i_rr, nm, nmlen, &hash);
if(r == 0) { if(r == 0) {
log_err("nsec3: malloc failure"); log_err("nsec3: malloc failure");
break; /* alloc failure */ break; /* alloc failure */
} else if(r != 1) } else if(r < 0) {
continue; /* malformed NSEC3 */ /* malformed NSEC3 */
else if(nsec3_hash_matches_owner(flt, hash, s)) { calc_errors++;
*rrset = s; /* rrset with this name */ (*calculations)++;
*rr = i_rr; /* matches hash with these parameters */ continue;
return 1; } else {
if(r == 1) (*calculations)++;
if(nsec3_hash_matches_owner(flt, hash, s)) {
*rrset = s; /* rrset with this name */
*rr = i_rr; /* matches hash with these parameters */
return 1;
}
} }
} }
*rrset = NULL; *rrset = NULL;
@ -775,6 +816,7 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash,
if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen))
return 0; /* malformed RR proves nothing */ return 0; /* malformed RR proves nothing */
if(!hash) return 0; /* please clang */
/* check the owner name is a hashed value . apex /* check the owner name is a hashed value . apex
* base32 encoded values must have equal length. * base32 encoded values must have equal length.
* hash_value and next hash value must have equal length. */ * hash_value and next hash value must have equal length. */
@ -823,35 +865,51 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash,
* @param nmlen: length of name. * @param nmlen: length of name.
* @param rrset: covering NSEC3 rrset is returned here. * @param rrset: covering NSEC3 rrset is returned here.
* @param rr: rr of cover is returned here. * @param rr: rr of cover is returned here.
* @param calculations: current hash calculations.
* @return true if a covering NSEC3 is found, false if not. * @return true if a covering NSEC3 is found, false if not.
*/ */
static int static int
find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt,
rbtree_type* ct, uint8_t* nm, size_t nmlen, struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen,
struct ub_packed_rrset_key** rrset, int* rr) struct ub_packed_rrset_key** rrset, int* rr,
int* calculations)
{ {
size_t i_rs; size_t i_rs;
int i_rr; int i_rr;
struct ub_packed_rrset_key* s; struct ub_packed_rrset_key* s;
struct nsec3_cached_hash* hash = NULL; struct nsec3_cached_hash* hash = NULL;
int r; int r;
int calc_errors = 0;
/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
for(s=filter_first(flt, &i_rs, &i_rr); s; for(s=filter_first(flt, &i_rs, &i_rr); s;
s=filter_next(flt, &i_rs, &i_rr)) { s=filter_next(flt, &i_rs, &i_rr)) {
/* check if we are allowed more calculations */
if(*calculations >= MAX_NSEC3_CALCULATIONS) {
if(calc_errors == *calculations) {
*calculations = MAX_NSEC3_ERRORS;
}
break;
}
/* get name hashed for this NSEC3 RR */ /* get name hashed for this NSEC3 RR */
r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer,
s, i_rr, nm, nmlen, &hash); s, i_rr, nm, nmlen, &hash);
if(r == 0) { if(r == 0) {
log_err("nsec3: malloc failure"); log_err("nsec3: malloc failure");
break; /* alloc failure */ break; /* alloc failure */
} else if(r != 1) } else if(r < 0) {
continue; /* malformed NSEC3 */ /* malformed NSEC3 */
else if(nsec3_covers(flt->zone, hash, s, i_rr, calc_errors++;
env->scratch_buffer)) { (*calculations)++;
*rrset = s; /* rrset with this name */ continue;
*rr = i_rr; /* covers hash with these parameters */ } else {
return 1; if(r == 1) (*calculations)++;
if(nsec3_covers(flt->zone, hash, s, i_rr,
env->scratch_buffer)) {
*rrset = s; /* rrset with this name */
*rr = i_rr; /* covers hash with these parameters */
return 1;
}
} }
} }
*rrset = NULL; *rrset = NULL;
@ -869,11 +927,13 @@ find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt,
* @param ct: cached hashes table. * @param ct: cached hashes table.
* @param qinfo: query that is verified for. * @param qinfo: query that is verified for.
* @param ce: closest encloser information is returned in here. * @param ce: closest encloser information is returned in here.
* @param calculations: current hash calculations.
* @return true if a closest encloser candidate is found, false if not. * @return true if a closest encloser candidate is found, false if not.
*/ */
static int static int
nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce) struct nsec3_cache_table* ct, struct query_info* qinfo,
struct ce_response* ce, int* calculations)
{ {
uint8_t* nm = qinfo->qname; uint8_t* nm = qinfo->qname;
size_t nmlen = qinfo->qname_len; size_t nmlen = qinfo->qname_len;
@ -888,8 +948,12 @@ nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
* may be the case. */ * may be the case. */
while(dname_subdomain_c(nm, flt->zone)) { while(dname_subdomain_c(nm, flt->zone)) {
if(*calculations >= MAX_NSEC3_CALCULATIONS ||
*calculations == MAX_NSEC3_ERRORS) {
return 0;
}
if(find_matching_nsec3(env, flt, ct, nm, nmlen, if(find_matching_nsec3(env, flt, ct, nm, nmlen,
&ce->ce_rrset, &ce->ce_rr)) { &ce->ce_rrset, &ce->ce_rr, calculations)) {
ce->ce = nm; ce->ce = nm;
ce->ce_len = nmlen; ce->ce_len = nmlen;
return 1; return 1;
@ -933,22 +997,38 @@ next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce,
* If set true, and the return value is true, then you can be * If set true, and the return value is true, then you can be
* certain that the ce.nc_rrset and ce.nc_rr are set properly. * certain that the ce.nc_rrset and ce.nc_rr are set properly.
* @param ce: closest encloser information is returned in here. * @param ce: closest encloser information is returned in here.
* @param calculations: pointer to the current NSEC3 hash calculations.
* @return bogus if no closest encloser could be proven. * @return bogus if no closest encloser could be proven.
* secure if a closest encloser could be proven, ce is set. * secure if a closest encloser could be proven, ce is set.
* insecure if the closest-encloser candidate turns out to prove * insecure if the closest-encloser candidate turns out to prove
* that an insecure delegation exists above the qname. * that an insecure delegation exists above the qname.
* unchecked if no more hash calculations are allowed at this point.
*/ */
static enum sec_status static enum sec_status
nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
rbtree_type* ct, struct query_info* qinfo, int prove_does_not_exist, struct nsec3_cache_table* ct, struct query_info* qinfo,
struct ce_response* ce) int prove_does_not_exist, struct ce_response* ce, int* calculations)
{ {
uint8_t* nc; uint8_t* nc;
size_t nc_len; size_t nc_len;
/* robust: clean out ce, in case it gets abused later */ /* robust: clean out ce, in case it gets abused later */
memset(ce, 0, sizeof(*ce)); memset(ce, 0, sizeof(*ce));
if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) { if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce, calculations)) {
if(*calculations == MAX_NSEC3_ERRORS) {
verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could "
"not find a candidate for the closest "
"encloser; all attempted hash calculations "
"were erroneous; bogus");
return sec_status_bogus;
} else if(*calculations >= MAX_NSEC3_CALCULATIONS) {
verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could "
"not find a candidate for the closest "
"encloser; reached MAX_NSEC3_CALCULATIONS "
"(%d); unchecked still",
MAX_NSEC3_CALCULATIONS);
return sec_status_unchecked;
}
verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could "
"not find a candidate for the closest encloser."); "not find a candidate for the closest encloser.");
return sec_status_bogus; return sec_status_bogus;
@ -989,9 +1069,23 @@ nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
/* Otherwise, we need to show that the next closer name is covered. */ /* Otherwise, we need to show that the next closer name is covered. */
next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len); next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len);
if(!find_covering_nsec3(env, flt, ct, nc, nc_len, if(!find_covering_nsec3(env, flt, ct, nc, nc_len,
&ce->nc_rrset, &ce->nc_rr)) { &ce->nc_rrset, &ce->nc_rr, calculations)) {
if(*calculations == MAX_NSEC3_ERRORS) {
verbose(VERB_ALGO, "nsec3: Could not find proof that the "
"candidate encloser was the closest encloser; "
"all attempted hash calculations were "
"erroneous; bogus");
return sec_status_bogus;
} else if(*calculations >= MAX_NSEC3_CALCULATIONS) {
verbose(VERB_ALGO, "nsec3: Could not find proof that the "
"candidate encloser was the closest encloser; "
"reached MAX_NSEC3_CALCULATIONS (%d); "
"unchecked still",
MAX_NSEC3_CALCULATIONS);
return sec_status_unchecked;
}
verbose(VERB_ALGO, "nsec3: Could not find proof that the " verbose(VERB_ALGO, "nsec3: Could not find proof that the "
"candidate encloser was the closest encloser"); "candidate encloser was the closest encloser");
return sec_status_bogus; return sec_status_bogus;
} }
return sec_status_secure; return sec_status_secure;
@ -1019,8 +1113,8 @@ nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen,
/** Do the name error proof */ /** Do the name error proof */
static enum sec_status static enum sec_status
nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
rbtree_type* ct, struct query_info* qinfo) struct nsec3_cache_table* ct, struct query_info* qinfo, int* calc)
{ {
struct ce_response ce; struct ce_response ce;
uint8_t* wc; uint8_t* wc;
@ -1032,11 +1126,15 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
/* First locate and prove the closest encloser to qname. We will /* First locate and prove the closest encloser to qname. We will
* use the variant that fails if the closest encloser turns out * use the variant that fails if the closest encloser turns out
* to be qname. */ * to be qname. */
sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc);
if(sec != sec_status_secure) { if(sec != sec_status_secure) {
if(sec == sec_status_bogus) if(sec == sec_status_bogus)
verbose(VERB_ALGO, "nsec3 nameerror proof: failed " verbose(VERB_ALGO, "nsec3 nameerror proof: failed "
"to prove a closest encloser"); "to prove a closest encloser");
else if(sec == sec_status_unchecked)
verbose(VERB_ALGO, "nsec3 nameerror proof: will "
"continue proving closest encloser after "
"suspend");
else verbose(VERB_ALGO, "nsec3 nameerror proof: closest " else verbose(VERB_ALGO, "nsec3 nameerror proof: closest "
"nsec3 is an insecure delegation"); "nsec3 is an insecure delegation");
return sec; return sec;
@ -1046,9 +1144,27 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
/* At this point, we know that qname does not exist. Now we need /* At this point, we know that qname does not exist. Now we need
* to prove that the wildcard does not exist. */ * to prove that the wildcard does not exist. */
log_assert(ce.ce); log_assert(ce.ce);
wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen);
if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen, if(!wc) {
&wc_rrset, &wc_rr)) { verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
"that the applicable wildcard did not exist.");
return sec_status_bogus;
}
if(!find_covering_nsec3(env, flt, ct, wc, wclen, &wc_rrset, &wc_rr, calc)) {
if(*calc == MAX_NSEC3_ERRORS) {
verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
"that the applicable wildcard did not exist; "
"all attempted hash calculations were "
"erroneous; bogus");
return sec_status_bogus;
} else if(*calc >= MAX_NSEC3_CALCULATIONS) {
verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
"that the applicable wildcard did not exist; "
"reached MAX_NSEC3_CALCULATIONS (%d); "
"unchecked still",
MAX_NSEC3_CALCULATIONS);
return sec_status_unchecked;
}
verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
"that the applicable wildcard did not exist."); "that the applicable wildcard did not exist.");
return sec_status_bogus; return sec_status_bogus;
@ -1064,14 +1180,13 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
enum sec_status enum sec_status
nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey) struct query_info* qinfo, struct key_entry_key* kkey,
struct nsec3_cache_table* ct, int* calc)
{ {
rbtree_type ct;
struct nsec3_filter flt; struct nsec3_filter flt;
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
return sec_status_bogus; /* no valid NSEC3s, bogus */ return sec_status_bogus; /* no valid NSEC3s, bogus */
rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
filter_init(&flt, list, num, qinfo); /* init RR iterator */ filter_init(&flt, list, num, qinfo); /* init RR iterator */
if(!flt.zone) if(!flt.zone)
return sec_status_bogus; /* no RRs */ return sec_status_bogus; /* no RRs */
@ -1079,7 +1194,7 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
return sec_status_insecure; /* iteration count too high */ return sec_status_insecure; /* iteration count too high */
log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone",
flt.zone, 0, 0); flt.zone, 0, 0);
return nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); return nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc);
} }
/* /*
@ -1089,8 +1204,9 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
/** Do the nodata proof */ /** Do the nodata proof */
static enum sec_status static enum sec_status
nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
rbtree_type* ct, struct query_info* qinfo) struct nsec3_cache_table* ct, struct query_info* qinfo,
int* calc)
{ {
struct ce_response ce; struct ce_response ce;
uint8_t* wc; uint8_t* wc;
@ -1100,7 +1216,7 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
enum sec_status sec; enum sec_status sec;
if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len,
&rrset, &rr)) { &rrset, &rr, calc)) {
/* cases 1 and 2 */ /* cases 1 and 2 */
if(nsec3_has_type(rrset, rr, qinfo->qtype)) { if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " verbose(VERB_ALGO, "proveNodata: Matching NSEC3 "
@ -1144,11 +1260,23 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
} }
return sec_status_secure; return sec_status_secure;
} }
if(*calc == MAX_NSEC3_ERRORS) {
verbose(VERB_ALGO, "proveNodata: all attempted hash "
"calculations were erroneous while finding a matching "
"NSEC3, bogus");
return sec_status_bogus;
} else if(*calc >= MAX_NSEC3_CALCULATIONS) {
verbose(VERB_ALGO, "proveNodata: reached "
"MAX_NSEC3_CALCULATIONS (%d) while finding a "
"matching NSEC3; unchecked still",
MAX_NSEC3_CALCULATIONS);
return sec_status_unchecked;
}
/* For cases 3 - 5, we need the proven closest encloser, and it /* For cases 3 - 5, we need the proven closest encloser, and it
* can't match qname. Although, at this point, we know that it * can't match qname. Although, at this point, we know that it
* won't since we just checked that. */ * won't since we just checked that. */
sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc);
if(sec == sec_status_bogus) { if(sec == sec_status_bogus) {
verbose(VERB_ALGO, "proveNodata: did not match qname, " verbose(VERB_ALGO, "proveNodata: did not match qname, "
"nor found a proven closest encloser."); "nor found a proven closest encloser.");
@ -1157,14 +1285,17 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure " verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure "
"delegation."); "delegation.");
return sec_status_insecure; return sec_status_insecure;
} else if(sec==sec_status_unchecked) {
return sec_status_unchecked;
} }
/* Case 3: removed */ /* Case 3: removed */
/* Case 4: */ /* Case 4: */
log_assert(ce.ce); log_assert(ce.ce);
wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen);
if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) { if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr,
calc)) {
/* found wildcard */ /* found wildcard */
if(nsec3_has_type(rrset, rr, qinfo->qtype)) { if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
verbose(VERB_ALGO, "nsec3 nodata proof: matching " verbose(VERB_ALGO, "nsec3 nodata proof: matching "
@ -1195,6 +1326,18 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
} }
return sec_status_secure; return sec_status_secure;
} }
if(*calc == MAX_NSEC3_ERRORS) {
verbose(VERB_ALGO, "nsec3 nodata proof: all attempted hash "
"calculations were erroneous while matching "
"wildcard, bogus");
return sec_status_bogus;
} else if(*calc >= MAX_NSEC3_CALCULATIONS) {
verbose(VERB_ALGO, "nsec3 nodata proof: reached "
"MAX_NSEC3_CALCULATIONS (%d) while matching "
"wildcard, unchecked still",
MAX_NSEC3_CALCULATIONS);
return sec_status_unchecked;
}
/* Case 5: */ /* Case 5: */
/* Due to forwarders, cnames, and other collating effects, we /* Due to forwarders, cnames, and other collating effects, we
@ -1223,28 +1366,27 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
enum sec_status enum sec_status
nsec3_prove_nodata(struct module_env* env, struct val_env* ve, nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey) struct query_info* qinfo, struct key_entry_key* kkey,
struct nsec3_cache_table* ct, int* calc)
{ {
rbtree_type ct;
struct nsec3_filter flt; struct nsec3_filter flt;
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
return sec_status_bogus; /* no valid NSEC3s, bogus */ return sec_status_bogus; /* no valid NSEC3s, bogus */
rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
filter_init(&flt, list, num, qinfo); /* init RR iterator */ filter_init(&flt, list, num, qinfo); /* init RR iterator */
if(!flt.zone) if(!flt.zone)
return sec_status_bogus; /* no RRs */ return sec_status_bogus; /* no RRs */
if(nsec3_iteration_count_high(ve, &flt, kkey)) if(nsec3_iteration_count_high(ve, &flt, kkey))
return sec_status_insecure; /* iteration count too high */ return sec_status_insecure; /* iteration count too high */
return nsec3_do_prove_nodata(env, &flt, &ct, qinfo); return nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc);
} }
enum sec_status enum sec_status
nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc) struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc,
struct nsec3_cache_table* ct, int* calc)
{ {
rbtree_type ct;
struct nsec3_filter flt; struct nsec3_filter flt;
struct ce_response ce; struct ce_response ce;
uint8_t* nc; uint8_t* nc;
@ -1254,7 +1396,6 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
return sec_status_bogus; /* no valid NSEC3s, bogus */ return sec_status_bogus; /* no valid NSEC3s, bogus */
rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
filter_init(&flt, list, num, qinfo); /* init RR iterator */ filter_init(&flt, list, num, qinfo); /* init RR iterator */
if(!flt.zone) if(!flt.zone)
return sec_status_bogus; /* no RRs */ return sec_status_bogus; /* no RRs */
@ -1272,8 +1413,22 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
/* Now we still need to prove that the original data did not exist. /* Now we still need to prove that the original data did not exist.
* Otherwise, we need to show that the next closer name is covered. */ * Otherwise, we need to show that the next closer name is covered. */
next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len); next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len);
if(!find_covering_nsec3(env, &flt, &ct, nc, nc_len, if(!find_covering_nsec3(env, &flt, ct, nc, nc_len,
&ce.nc_rrset, &ce.nc_rr)) { &ce.nc_rrset, &ce.nc_rr, calc)) {
if(*calc == MAX_NSEC3_ERRORS) {
verbose(VERB_ALGO, "proveWildcard: did not find a "
"covering NSEC3 that covered the next closer "
"name; all attempted hash calculations were "
"erroneous; bogus");
return sec_status_bogus;
} else if(*calc >= MAX_NSEC3_CALCULATIONS) {
verbose(VERB_ALGO, "proveWildcard: did not find a "
"covering NSEC3 that covered the next closer "
"name; reached MAX_NSEC3_CALCULATIONS "
"(%d); unchecked still",
MAX_NSEC3_CALCULATIONS);
return sec_status_unchecked;
}
verbose(VERB_ALGO, "proveWildcard: did not find a covering " verbose(VERB_ALGO, "proveWildcard: did not find a covering "
"NSEC3 that covered the next closer name."); "NSEC3 that covered the next closer name.");
return sec_status_bogus; return sec_status_bogus;
@ -1294,6 +1449,7 @@ list_is_secure(struct module_env* env, struct val_env* ve,
{ {
struct packed_rrset_data* d; struct packed_rrset_data* d;
size_t i; size_t i;
int verified = 0;
for(i=0; i<num; i++) { for(i=0; i<num; i++) {
d = (struct packed_rrset_data*)list[i]->entry.data; d = (struct packed_rrset_data*)list[i]->entry.data;
if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3)) if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3))
@ -1304,7 +1460,8 @@ list_is_secure(struct module_env* env, struct val_env* ve,
if(d->security == sec_status_secure) if(d->security == sec_status_secure)
continue; continue;
d->security = val_verify_rrset_entry(env, ve, list[i], kkey, d->security = val_verify_rrset_entry(env, ve, list[i], kkey,
reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate); reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate,
&verified);
if(d->security != sec_status_secure) { if(d->security != sec_status_secure) {
verbose(VERB_ALGO, "NSEC3 did not verify"); verbose(VERB_ALGO, "NSEC3 did not verify");
return 0; return 0;
@ -1318,13 +1475,16 @@ enum sec_status
nsec3_prove_nods(struct module_env* env, struct val_env* ve, nsec3_prove_nods(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, char** reason, struct query_info* qinfo, struct key_entry_key* kkey, char** reason,
sldns_ede_code* reason_bogus, struct module_qstate* qstate) sldns_ede_code* reason_bogus, struct module_qstate* qstate,
struct nsec3_cache_table* ct)
{ {
rbtree_type ct;
struct nsec3_filter flt; struct nsec3_filter flt;
struct ce_response ce; struct ce_response ce;
struct ub_packed_rrset_key* rrset; struct ub_packed_rrset_key* rrset;
int rr; int rr;
int calc = 0;
enum sec_status sec;
log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); log_assert(qinfo->qtype == LDNS_RR_TYPE_DS);
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) { if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) {
@ -1335,7 +1495,6 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
*reason = "not all NSEC3 records secure"; *reason = "not all NSEC3 records secure";
return sec_status_bogus; /* not all NSEC3 records secure */ return sec_status_bogus; /* not all NSEC3 records secure */
} }
rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
filter_init(&flt, list, num, qinfo); /* init RR iterator */ filter_init(&flt, list, num, qinfo); /* init RR iterator */
if(!flt.zone) { if(!flt.zone) {
*reason = "no NSEC3 records"; *reason = "no NSEC3 records";
@ -1346,8 +1505,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
/* Look for a matching NSEC3 to qname -- this is the normal /* Look for a matching NSEC3 to qname -- this is the normal
* NODATA case. */ * NODATA case. */
if(find_matching_nsec3(env, &flt, &ct, qinfo->qname, qinfo->qname_len, if(find_matching_nsec3(env, &flt, ct, qinfo->qname, qinfo->qname_len,
&rrset, &rr)) { &rrset, &rr, &calc)) {
/* If the matching NSEC3 has the SOA bit set, it is from /* If the matching NSEC3 has the SOA bit set, it is from
* the wrong zone (the child instead of the parent). If * the wrong zone (the child instead of the parent). If
* it has the DS bit set, then we were lied to. */ * it has the DS bit set, then we were lied to. */
@ -1370,10 +1529,24 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
/* Otherwise, this proves no DS. */ /* Otherwise, this proves no DS. */
return sec_status_secure; return sec_status_secure;
} }
if(calc == MAX_NSEC3_ERRORS) {
verbose(VERB_ALGO, "nsec3 provenods: all attempted hash "
"calculations were erroneous while finding a matching "
"NSEC3, bogus");
return sec_status_bogus;
} else if(calc >= MAX_NSEC3_CALCULATIONS) {
verbose(VERB_ALGO, "nsec3 provenods: reached "
"MAX_NSEC3_CALCULATIONS (%d) while finding a "
"matching NSEC3, unchecked still",
MAX_NSEC3_CALCULATIONS);
return sec_status_unchecked;
}
/* Otherwise, we are probably in the opt-out case. */ /* Otherwise, we are probably in the opt-out case. */
if(nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce) sec = nsec3_prove_closest_encloser(env, &flt, ct, qinfo, 1, &ce, &calc);
!= sec_status_secure) { if(sec == sec_status_unchecked) {
return sec_status_unchecked;
} else if(sec != sec_status_secure) {
/* an insecure delegation *above* the qname does not prove /* an insecure delegation *above* the qname does not prove
* anything about this qname exactly, and bogus is bogus */ * anything about this qname exactly, and bogus is bogus */
verbose(VERB_ALGO, "nsec3 provenods: did not match qname, " verbose(VERB_ALGO, "nsec3 provenods: did not match qname, "
@ -1407,17 +1580,16 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
enum sec_status enum sec_status
nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, int* nodata) struct query_info* qinfo, struct key_entry_key* kkey, int* nodata,
struct nsec3_cache_table* ct, int* calc)
{ {
enum sec_status sec, secnx; enum sec_status sec, secnx;
rbtree_type ct;
struct nsec3_filter flt; struct nsec3_filter flt;
*nodata = 0; *nodata = 0;
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
return sec_status_bogus; /* no valid NSEC3s, bogus */ return sec_status_bogus; /* no valid NSEC3s, bogus */
rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
filter_init(&flt, list, num, qinfo); /* init RR iterator */ filter_init(&flt, list, num, qinfo); /* init RR iterator */
if(!flt.zone) if(!flt.zone)
return sec_status_bogus; /* no RRs */ return sec_status_bogus; /* no RRs */
@ -1427,16 +1599,20 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
/* try nxdomain and nodata after another, while keeping the /* try nxdomain and nodata after another, while keeping the
* hash cache intact */ * hash cache intact */
secnx = nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); secnx = nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc);
if(secnx==sec_status_secure) if(secnx==sec_status_secure)
return sec_status_secure; return sec_status_secure;
sec = nsec3_do_prove_nodata(env, &flt, &ct, qinfo); else if(secnx == sec_status_unchecked)
return sec_status_unchecked;
sec = nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc);
if(sec==sec_status_secure) { if(sec==sec_status_secure) {
*nodata = 1; *nodata = 1;
} else if(sec == sec_status_insecure) { } else if(sec == sec_status_insecure) {
*nodata = 1; *nodata = 1;
} else if(secnx == sec_status_insecure) { } else if(secnx == sec_status_insecure) {
sec = sec_status_insecure; sec = sec_status_insecure;
} else if(sec == sec_status_unchecked) {
return sec_status_unchecked;
} }
return sec; return sec;
} }

View file

@ -98,6 +98,15 @@ struct sldns_buffer;
/** The SHA1 hash algorithm for NSEC3 */ /** The SHA1 hash algorithm for NSEC3 */
#define NSEC3_HASH_SHA1 0x01 #define NSEC3_HASH_SHA1 0x01
/**
* Cache table for NSEC3 hashes.
* It keeps a *pointer* to the region its items are allocated.
*/
struct nsec3_cache_table {
rbtree_type* ct;
struct regional* region;
};
/** /**
* Determine if the set of NSEC3 records provided with a response prove NAME * Determine if the set of NSEC3 records provided with a response prove NAME
* ERROR. This means that the NSEC3s prove a) the closest encloser exists, * ERROR. This means that the NSEC3s prove a) the closest encloser exists,
@ -110,14 +119,18 @@ struct sldns_buffer;
* @param num: number of RRsets in the array to examine. * @param num: number of RRsets in the array to examine.
* @param qinfo: query that is verified for. * @param qinfo: query that is verified for.
* @param kkey: key entry that signed the NSEC3s. * @param kkey: key entry that signed the NSEC3s.
* @param ct: cached hashes table.
* @param calc: current hash calculations.
* @return: * @return:
* sec_status SECURE of the Name Error is proven by the NSEC3 RRs, * sec_status SECURE of the Name Error is proven by the NSEC3 RRs,
* BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored,
* UNCHECKED if no more hash calculations are allowed at this point.
*/ */
enum sec_status enum sec_status
nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey); struct query_info* qinfo, struct key_entry_key* kkey,
struct nsec3_cache_table* ct, int* calc);
/** /**
* Determine if the NSEC3s provided in a response prove the NOERROR/NODATA * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA
@ -144,15 +157,18 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
* @param num: number of RRsets in the array to examine. * @param num: number of RRsets in the array to examine.
* @param qinfo: query that is verified for. * @param qinfo: query that is verified for.
* @param kkey: key entry that signed the NSEC3s. * @param kkey: key entry that signed the NSEC3s.
* @param ct: cached hashes table.
* @param calc: current hash calculations.
* @return: * @return:
* sec_status SECURE of the proposition is proven by the NSEC3 RRs, * sec_status SECURE of the proposition is proven by the NSEC3 RRs,
* BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored,
* UNCHECKED if no more hash calculations are allowed at this point.
*/ */
enum sec_status enum sec_status
nsec3_prove_nodata(struct module_env* env, struct val_env* ve, nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey); struct query_info* qinfo, struct key_entry_key* kkey,
struct nsec3_cache_table* ct, int* calc);
/** /**
* Prove that a positive wildcard match was appropriate (no direct match * Prove that a positive wildcard match was appropriate (no direct match
@ -166,14 +182,18 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
* @param kkey: key entry that signed the NSEC3s. * @param kkey: key entry that signed the NSEC3s.
* @param wc: The purported wildcard that matched. This is the wildcard name * @param wc: The purported wildcard that matched. This is the wildcard name
* as *.wildcard.name., with the *. label already removed. * as *.wildcard.name., with the *. label already removed.
* @param ct: cached hashes table.
* @param calc: current hash calculations.
* @return: * @return:
* sec_status SECURE of the proposition is proven by the NSEC3 RRs, * sec_status SECURE of the proposition is proven by the NSEC3 RRs,
* BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored,
* UNCHECKED if no more hash calculations are allowed at this point.
*/ */
enum sec_status enum sec_status
nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc); struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc,
struct nsec3_cache_table* ct, int* calc);
/** /**
* Prove that a DS response either had no DS, or wasn't a delegation point. * Prove that a DS response either had no DS, or wasn't a delegation point.
@ -189,17 +209,20 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
* @param reason: string for bogus result. * @param reason: string for bogus result.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region. * @param qstate: qstate with region.
* @param ct: cached hashes table.
* @return: * @return:
* sec_status SECURE of the proposition is proven by the NSEC3 RRs, * sec_status SECURE of the proposition is proven by the NSEC3 RRs,
* BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored.
* or if there was no DS in an insecure (i.e., opt-in) way, * or if there was no DS in an insecure (i.e., opt-in) way,
* INDETERMINATE if it was clear that this wasn't a delegation point. * INDETERMINATE if it was clear that this wasn't a delegation point,
* UNCHECKED if no more hash calculations are allowed at this point.
*/ */
enum sec_status enum sec_status
nsec3_prove_nods(struct module_env* env, struct val_env* ve, nsec3_prove_nods(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, char** reason, struct query_info* qinfo, struct key_entry_key* kkey, char** reason,
sldns_ede_code* reason_bogus, struct module_qstate* qstate); sldns_ede_code* reason_bogus, struct module_qstate* qstate,
struct nsec3_cache_table* ct);
/** /**
* Prove NXDOMAIN or NODATA. * Prove NXDOMAIN or NODATA.
@ -212,14 +235,18 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
* @param kkey: key entry that signed the NSEC3s. * @param kkey: key entry that signed the NSEC3s.
* @param nodata: if return value is secure, this indicates if nodata or * @param nodata: if return value is secure, this indicates if nodata or
* nxdomain was proven. * nxdomain was proven.
* @param ct: cached hashes table.
* @param calc: current hash calculations.
* @return: * @return:
* sec_status SECURE of the proposition is proven by the NSEC3 RRs, * sec_status SECURE of the proposition is proven by the NSEC3 RRs,
* BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored,
* UNCHECKED if no more hash calculations are allowed at this point.
*/ */
enum sec_status enum sec_status
nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num, struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, int* nodata); struct query_info* qinfo, struct key_entry_key* kkey, int* nodata,
struct nsec3_cache_table* ct, int* calc);
/** /**
* The NSEC3 hash result storage. * The NSEC3 hash result storage.
@ -256,6 +283,14 @@ struct nsec3_cached_hash {
*/ */
int nsec3_hash_cmp(const void* c1, const void* c2); int nsec3_hash_cmp(const void* c1, const void* c2);
/**
* Initialise the NSEC3 cache table.
* @param ct: the nsec3 cache table.
* @param region: the region where allocations for the table will happen.
* @return true on success, false on malloc error.
*/
int nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region);
/** /**
* Obtain the hash of an owner name. * Obtain the hash of an owner name.
* Used internally by the nsec3 proof functions in this file. * Used internally by the nsec3 proof functions in this file.
@ -272,7 +307,8 @@ int nsec3_hash_cmp(const void* c1, const void* c2);
* @param dname_len: the length of the name. * @param dname_len: the length of the name.
* @param hash: the hash node is returned on success. * @param hash: the hash node is returned on success.
* @return: * @return:
* 1 on success, either from cache or newly hashed hash is returned. * 2 on success, hash from cache is returned.
* 1 on success, newly computed hash is returned.
* 0 on a malloc failure. * 0 on a malloc failure.
* -1 if the NSEC3 rr was badly formatted (i.e. formerr). * -1 if the NSEC3 rr was badly formatted (i.e. formerr).
*/ */

View file

@ -79,6 +79,9 @@
#include <openssl/engine.h> #include <openssl/engine.h>
#endif #endif
/** Maximum number of RRSIG validations for an RRset. */
#define MAX_VALIDATE_RRSIGS 8
/** return number of rrs in an rrset */ /** return number of rrs in an rrset */
static size_t static size_t
rrset_get_count(struct ub_packed_rrset_key* rrset) rrset_get_count(struct ub_packed_rrset_key* rrset)
@ -542,6 +545,8 @@ int algo_needs_missing(struct algo_needs* n)
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from. * @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region. * @param qstate: qstate with region.
* @param numverified: incremented when the number of RRSIG validations
* increases.
* @return secure if any key signs *this* signature. bogus if no key signs it, * @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 * unchecked on error, or indeterminate if all keys are not supported by
* the crypto library (openssl3+ only). * the crypto library (openssl3+ only).
@ -552,7 +557,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey, size_t sig_idx, struct ub_packed_rrset_key* dnskey, size_t sig_idx,
struct rbtree_type** sortree, struct rbtree_type** sortree,
char** reason, sldns_ede_code *reason_bogus, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate) sldns_pkt_section section, struct module_qstate* qstate,
int* numverified)
{ {
/* find matching keys and check them */ /* find matching keys and check them */
enum sec_status sec = sec_status_bogus; enum sec_status sec = sec_status_bogus;
@ -576,6 +582,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
tag != dnskey_calc_keytag(dnskey, i)) tag != dnskey_calc_keytag(dnskey, i))
continue; continue;
numchecked ++; numchecked ++;
(*numverified)++;
/* see if key verifies */ /* see if key verifies */
sec = dnskey_verify_rrset_sig(env->scratch, sec = dnskey_verify_rrset_sig(env->scratch,
@ -586,6 +593,13 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
return sec; return sec;
else if(sec == sec_status_indeterminate) else if(sec == sec_status_indeterminate)
numindeterminate ++; numindeterminate ++;
if(*numverified > MAX_VALIDATE_RRSIGS) {
*reason = "too many RRSIG validations";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "verify sig: too many RRSIG validations");
return sec_status_bogus;
}
} }
if(numchecked == 0) { if(numchecked == 0) {
*reason = "signatures from unknown keys"; *reason = "signatures from unknown keys";
@ -609,7 +623,7 @@ enum sec_status
dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate) sldns_pkt_section section, struct module_qstate* qstate, int* verified)
{ {
enum sec_status sec; enum sec_status sec;
size_t i, num; size_t i, num;
@ -617,6 +631,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
/* make sure that for all DNSKEY algorithms there are valid sigs */ /* make sure that for all DNSKEY algorithms there are valid sigs */
struct algo_needs needs; struct algo_needs needs;
int alg; int alg;
*verified = 0;
num = rrset_get_sigcount(rrset); num = rrset_get_sigcount(rrset);
if(num == 0) { if(num == 0) {
@ -641,7 +656,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
for(i=0; i<num; i++) { for(i=0; i<num; i++) {
sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset, sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset,
dnskey, i, &sortree, reason, reason_bogus, dnskey, i, &sortree, reason, reason_bogus,
section, qstate); section, qstate, verified);
/* see which algorithm has been fixed up */ /* see which algorithm has been fixed up */
if(sec == sec_status_secure) { if(sec == sec_status_secure) {
if(!sigalg) if(!sigalg)
@ -653,6 +668,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
algo_needs_set_bogus(&needs, algo_needs_set_bogus(&needs,
(uint8_t)rrset_get_sig_algo(rrset, i)); (uint8_t)rrset_get_sig_algo(rrset, i));
} }
if(*verified > MAX_VALIDATE_RRSIGS) {
verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations");
*reason = "too many RRSIG validations";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
} }
if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
verbose(VERB_ALGO, "rrset failed to verify: " verbose(VERB_ALGO, "rrset failed to verify: "
@ -691,6 +713,7 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
int buf_canon = 0; int buf_canon = 0;
uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx);
int algo = dnskey_get_algo(dnskey, dnskey_idx); int algo = dnskey_get_algo(dnskey, dnskey_idx);
int numverified = 0;
num = rrset_get_sigcount(rrset); num = rrset_get_sigcount(rrset);
if(num == 0) { if(num == 0) {
@ -714,8 +737,16 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
if(sec == sec_status_secure) if(sec == sec_status_secure)
return sec; return sec;
numchecked ++; numchecked ++;
numverified ++;
if(sec == sec_status_indeterminate) if(sec == sec_status_indeterminate)
numindeterminate ++; numindeterminate ++;
if(numverified > MAX_VALIDATE_RRSIGS) {
verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations");
*reason = "too many RRSIG validations";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
} }
verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus"); verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus");
if(!numchecked) { if(!numchecked) {

View file

@ -260,6 +260,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 reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from. * @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region. * @param qstate: qstate with region.
* @param verified: if not NULL the number of RRSIG validations is returned.
* @return SECURE if one key in the set verifies one rrsig. * @return SECURE if one key in the set verifies one rrsig.
* UNCHECKED on allocation errors, unsupported algorithms, malformed data, * UNCHECKED on allocation errors, unsupported algorithms, malformed data,
* and BOGUS on verification failures (no keys match any signatures). * and BOGUS on verification failures (no keys match any signatures).
@ -268,7 +269,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* rrset, struct val_env* ve, struct ub_packed_rrset_key* rrset,
struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, struct ub_packed_rrset_key* dnskey, uint8_t* sigalg,
char** reason, sldns_ede_code *reason_bogus, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate); sldns_pkt_section section, struct module_qstate* qstate, int* verified);
/** /**

View file

@ -58,6 +58,10 @@
#include "sldns/wire2str.h" #include "sldns/wire2str.h"
#include "sldns/parseutil.h" #include "sldns/parseutil.h"
/** Maximum allowed digest match failures per DS, for DNSKEYs with the same
* properties */
#define MAX_DS_MATCH_FAILURES 4
enum val_classification enum val_classification
val_classify_response(uint16_t query_flags, struct query_info* origqinf, val_classify_response(uint16_t query_flags, struct query_info* origqinf,
struct query_info* qinf, struct reply_info* rep, size_t skip) struct query_info* qinf, struct reply_info* rep, size_t skip)
@ -336,7 +340,8 @@ static enum sec_status
val_verify_rrset(struct module_env* env, struct val_env* ve, val_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys,
uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate) sldns_pkt_section section, struct module_qstate* qstate,
int *verified)
{ {
enum sec_status sec; enum sec_status sec;
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
@ -346,6 +351,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
log_nametypeclass(VERB_ALGO, "verify rrset cached", log_nametypeclass(VERB_ALGO, "verify rrset cached",
rrset->rk.dname, ntohs(rrset->rk.type), rrset->rk.dname, ntohs(rrset->rk.type),
ntohs(rrset->rk.rrset_class)); ntohs(rrset->rk.rrset_class));
*verified = 0;
return d->security; return d->security;
} }
/* check in the cache if verification has already been done */ /* check in the cache if verification has already been done */
@ -354,12 +360,13 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
log_nametypeclass(VERB_ALGO, "verify rrset from cache", log_nametypeclass(VERB_ALGO, "verify rrset from cache",
rrset->rk.dname, ntohs(rrset->rk.type), rrset->rk.dname, ntohs(rrset->rk.type),
ntohs(rrset->rk.rrset_class)); ntohs(rrset->rk.rrset_class));
*verified = 0;
return d->security; return d->security;
} }
log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason,
reason_bogus, section, qstate); reason_bogus, section, qstate, verified);
verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
regional_free_all(env->scratch); regional_free_all(env->scratch);
@ -393,7 +400,8 @@ enum sec_status
val_verify_rrset_entry(struct module_env* env, struct val_env* ve, val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey,
char** reason, sldns_ede_code *reason_bogus, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate) sldns_pkt_section section, struct module_qstate* qstate,
int* verified)
{ {
/* temporary dnskey rrset-key */ /* temporary dnskey rrset-key */
struct ub_packed_rrset_key dnskey; struct ub_packed_rrset_key dnskey;
@ -407,7 +415,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
dnskey.entry.key = &dnskey; dnskey.entry.key = &dnskey;
dnskey.entry.data = kd->rrset_data; dnskey.entry.data = kd->rrset_data;
sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason,
reason_bogus, section, qstate); reason_bogus, section, qstate, verified);
return sec; return sec;
} }
@ -439,6 +447,12 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset,
ds_idx)) { ds_idx)) {
verbose(VERB_ALGO, "DS match attempt failed"); verbose(VERB_ALGO, "DS match attempt failed");
if(numchecked > numhashok + MAX_DS_MATCH_FAILURES) {
verbose(VERB_ALGO, "DS match attempt reached "
"MAX_DS_MATCH_FAILURES (%d); bogus",
MAX_DS_MATCH_FAILURES);
return sec_status_bogus;
}
continue; continue;
} }
numhashok++; numhashok++;

View file

@ -124,12 +124,14 @@ void val_find_signer(enum val_classification subtype,
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from. * @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region. * @param qstate: qstate with region.
* @param verified: if not NULL, the number of RRSIG validations is returned.
* @return security status of verification. * @return security status of verification.
*/ */
enum sec_status val_verify_rrset_entry(struct module_env* env, enum sec_status val_verify_rrset_entry(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* rrset, struct val_env* ve, struct ub_packed_rrset_key* rrset,
struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate); sldns_pkt_section section, struct module_qstate* qstate,
int* verified);
/** /**
* Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but

View file

@ -64,10 +64,15 @@
#include "sldns/wire2str.h" #include "sldns/wire2str.h"
#include "sldns/str2wire.h" #include "sldns/str2wire.h"
/** Max number of RRSIGs to validate at once, suspend query for later. */
#define MAX_VALIDATE_AT_ONCE 8
/** Max number of validation suspends allowed, error out otherwise. */
#define MAX_VALIDATION_SUSPENDS 16
/* forward decl for cache response and normal super inform calls of a DS */ /* forward decl for cache response and normal super inform calls of a DS */
static void process_ds_response(struct module_qstate* qstate, static void process_ds_response(struct module_qstate* qstate,
struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg,
struct query_info* qinfo, struct sock_list* origin); struct query_info* qinfo, struct sock_list* origin, int* suspend);
/* Updates the suplied EDE (RFC8914) code selectively so we don't lose /* Updates the suplied EDE (RFC8914) code selectively so we don't lose
@ -292,6 +297,21 @@ val_new(struct module_qstate* qstate, int id)
return val_new_getmsg(qstate, vq); return val_new_getmsg(qstate, vq);
} }
/** reset validator query state for query restart */
static void
val_restart(struct val_qstate* vq)
{
struct comm_timer* temp_timer;
int restart_count;
if(!vq) return;
temp_timer = vq->suspend_timer;
restart_count = vq->restart_count+1;
memset(vq, 0, sizeof(*vq));
vq->suspend_timer = temp_timer;
vq->restart_count = restart_count;
vq->state = VAL_INIT_STATE;
}
/** /**
* Exit validation with an error status * Exit validation with an error status
* *
@ -598,30 +618,42 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
* completed. * completed.
* *
* @param qstate: query state. * @param qstate: query state.
* @param vq: validator query state.
* @param env: module env for verify. * @param env: module env for verify.
* @param ve: validator env for verify. * @param ve: validator env for verify.
* @param qchase: query that was made. * @param qchase: query that was made.
* @param chase_reply: answer to validate. * @param chase_reply: answer to validate.
* @param key_entry: the key entry, which is trusted, and which matches * @param key_entry: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood(). * the signer of the answer. The key entry isgood().
* @param suspend: returned true if the task takes too long and needs to
* suspend to continue the effort later.
* @return false if any of the rrsets in the an or ns sections of the message * @return false if any of the rrsets in the an or ns sections of the message
* fail to verify. The message is then set to bogus. * fail to verify. The message is then set to bogus.
*/ */
static int static int
validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, struct query_info* qchase, struct module_env* env, struct val_env* ve, struct query_info* qchase,
struct reply_info* chase_reply, struct key_entry_key* key_entry) struct reply_info* chase_reply, struct key_entry_key* key_entry,
int* suspend)
{ {
uint8_t* sname; uint8_t* sname;
size_t i, slen; size_t i, slen;
struct ub_packed_rrset_key* s; struct ub_packed_rrset_key* s;
enum sec_status sec; enum sec_status sec;
int dname_seen = 0; int dname_seen = 0, num_verifies = 0, verified, have_state = 0;
char* reason = NULL; char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
*suspend = 0;
if(vq->msg_signatures_state) {
/* Pick up the state, and reset it, may not be needed now. */
vq->msg_signatures_state = 0;
have_state = 1;
}
/* validate the ANSWER section */ /* validate the ANSWER section */
for(i=0; i<chase_reply->an_numrrsets; i++) { for(i=0; i<chase_reply->an_numrrsets; i++) {
if(have_state && i <= vq->msg_signatures_index)
continue;
s = chase_reply->rrsets[i]; s = chase_reply->rrsets[i];
/* Skip the CNAME following a (validated) DNAME. /* Skip the CNAME following a (validated) DNAME.
* Because of the normalization routines in the iterator, * Because of the normalization routines in the iterator,
@ -640,7 +672,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
/* Verify the answer rrset */ /* Verify the answer rrset */
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_ANSWER, qstate); &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified);
/* If the (answer) rrset failed to validate, then this /* If the (answer) rrset failed to validate, then this
* message is BAD. */ * message is BAD. */
if(sec != sec_status_secure) { if(sec != sec_status_secure) {
@ -665,14 +697,33 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) { ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) {
dname_seen = 1; dname_seen = 1;
} }
num_verifies += verified;
if(num_verifies > MAX_VALIDATE_AT_ONCE &&
i+1 < (env->cfg->val_clean_additional?
chase_reply->an_numrrsets+chase_reply->ns_numrrsets:
chase_reply->rrset_count)) {
/* If the number of RRSIGs exceeds the maximum in
* one go, suspend. Only suspend if there is a next
* rrset to verify, i+1<loopmax. Store where to
* continue later. */
*suspend = 1;
vq->msg_signatures_state = 1;
vq->msg_signatures_index = i;
verbose(VERB_ALGO, "msg signature validation "
"suspended");
return 0;
}
} }
/* validate the AUTHORITY section */ /* validate the AUTHORITY section */
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) { chase_reply->ns_numrrsets; i++) {
if(have_state && i <= vq->msg_signatures_index)
continue;
s = chase_reply->rrsets[i]; s = chase_reply->rrsets[i];
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate); &reason_bogus, LDNS_SECTION_AUTHORITY, qstate,
&verified);
/* If anything in the authority section fails to be secure, /* If anything in the authority section fails to be secure,
* we have a bad message. */ * we have a bad message. */
if(sec != sec_status_secure) { if(sec != sec_status_secure) {
@ -686,6 +737,18 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
update_reason_bogus(chase_reply, reason_bogus); update_reason_bogus(chase_reply, reason_bogus);
return 0; return 0;
} }
num_verifies += verified;
if(num_verifies > MAX_VALIDATE_AT_ONCE &&
i+1 < (env->cfg->val_clean_additional?
chase_reply->an_numrrsets+chase_reply->ns_numrrsets:
chase_reply->rrset_count)) {
*suspend = 1;
vq->msg_signatures_state = 1;
vq->msg_signatures_index = i;
verbose(VERB_ALGO, "msg signature validation "
"suspended");
return 0;
}
} }
/* If set, the validator should clean the additional section of /* If set, the validator should clean the additional section of
@ -695,22 +758,103 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
/* attempt to validate the ADDITIONAL section rrsets */ /* attempt to validate the ADDITIONAL section rrsets */
for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
i<chase_reply->rrset_count; i++) { i<chase_reply->rrset_count; i++) {
if(have_state && i <= vq->msg_signatures_index)
continue;
s = chase_reply->rrsets[i]; s = chase_reply->rrsets[i];
/* only validate rrs that have signatures with the key */ /* only validate rrs that have signatures with the key */
/* leave others unchecked, those get removed later on too */ /* leave others unchecked, those get removed later on too */
val_find_rrset_signer(s, &sname, &slen); val_find_rrset_signer(s, &sname, &slen);
verified = 0;
if(sname && query_dname_compare(sname, key_entry->name)==0) if(sname && query_dname_compare(sname, key_entry->name)==0)
(void)val_verify_rrset_entry(env, ve, s, key_entry, (void)val_verify_rrset_entry(env, ve, s, key_entry,
&reason, NULL, LDNS_SECTION_ADDITIONAL, qstate); &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate,
&verified);
/* the additional section can fail to be secure, /* the additional section can fail to be secure,
* it is optional, check signature in case we need * it is optional, check signature in case we need
* to clean the additional section later. */ * to clean the additional section later. */
num_verifies += verified;
if(num_verifies > MAX_VALIDATE_AT_ONCE &&
i+1 < chase_reply->rrset_count) {
*suspend = 1;
vq->msg_signatures_state = 1;
vq->msg_signatures_index = i;
verbose(VERB_ALGO, "msg signature validation "
"suspended");
return 0;
}
} }
return 1; return 1;
} }
void
validate_suspend_timer_cb(void* arg)
{
struct module_qstate* qstate = (struct module_qstate*)arg;
verbose(VERB_ALGO, "validate_suspend timer, continue");
mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass,
NULL);
}
/** Setup timer to continue validation of msg signatures later */
static int
validate_suspend_setup_timer(struct module_qstate* qstate,
struct val_qstate* vq, int id, enum val_state resume_state)
{
struct timeval tv;
int usec, slack, base;
if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) {
verbose(VERB_ALGO, "validate_suspend timer: "
"reached MAX_VALIDATION_SUSPENDS (%d); error out",
MAX_VALIDATION_SUSPENDS);
errinf(qstate, "max validation suspends reached, "
"too many RRSIG validations");
return 0;
}
verbose(VERB_ALGO, "validate_suspend timer, set for suspend");
vq->state = resume_state;
qstate->ext_state[id] = module_wait_reply;
if(!vq->suspend_timer) {
vq->suspend_timer = comm_timer_create(
qstate->env->worker_base,
validate_suspend_timer_cb, qstate);
if(!vq->suspend_timer) {
log_err("validate_suspend_setup_timer: "
"out of memory for comm_timer_create");
return 0;
}
}
/* The timer is activated later, after other events in the event
* loop have been processed. The query state can also be deleted,
* when the list is full and query states are dropped. */
/* Extend wait time if there are a lot of queries or if this one
* is taking long, to keep around cpu time for ordinary queries. */
usec = 50000; /* 50 msec */
slack = 0;
if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states)
slack += 3;
else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2)
slack += 2;
else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4)
slack += 1;
if(vq->suspend_count > 3)
slack += 3;
else if(vq->suspend_count > 0)
slack += vq->suspend_count;
if(slack != 0 && slack <= 12 /* No numeric overflow. */) {
usec = usec << slack;
}
/* Spread such timeouts within 90%-100% of the original timer. */
base = usec * 9/10;
usec = base + ub_random_max(qstate->env->rnd, usec-base);
tv.tv_usec = (usec % 1000000);
tv.tv_sec = (usec / 1000000);
vq->suspend_count ++;
comm_timer_set(vq->suspend_timer, &tv);
return 1;
}
/** /**
* Detect wrong truncated response (say from BIND 9.6.1 that is forwarding * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding
* and saw the NS record without signatures from a referral). * and saw the NS record without signatures from a referral).
@ -809,11 +953,17 @@ remove_spurious_authority(struct reply_info* chase_reply,
* @param chase_reply: answer to that query to validate. * @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches * @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood(). * the signer of the answer. The key entry isgood().
* @param qstate: query state for the region.
* @param vq: validator state for the nsec3 cache table.
* @param nsec3_calculations: current nsec3 hash calculations.
* @param suspend: returned true if the task takes too long and needs to
* suspend to continue the effort later.
*/ */
static void static void
validate_positive_response(struct module_env* env, struct val_env* ve, validate_positive_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply, struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey) struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{ {
uint8_t* wc = NULL; uint8_t* wc = NULL;
size_t wl; size_t wl;
@ -822,6 +972,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
int nsec3s_seen = 0; int nsec3s_seen = 0;
size_t i; size_t i;
struct ub_packed_rrset_key* s; struct ub_packed_rrset_key* s;
*suspend = 0;
/* validate the ANSWER section - this will be the answer itself */ /* validate the ANSWER section - this will be the answer itself */
for(i=0; i<chase_reply->an_numrrsets; i++) { for(i=0; i<chase_reply->an_numrrsets; i++) {
@ -873,17 +1024,23 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
/* If this was a positive wildcard response that we haven't already /* If this was a positive wildcard response that we haven't already
* proven, and we have NSEC3 records, try to prove it using the NSEC3 * proven, and we have NSEC3 records, try to prove it using the NSEC3
* records. */ * records. */
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { if(wc != NULL && !wc_NSEC_ok && nsec3s_seen &&
enum sec_status sec = nsec3_prove_wildcard(env, ve, nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets, chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, wc); chase_reply->ns_numrrsets, qchase, kkey, wc,
&vq->nsec3_cache_table, nsec3_calculations);
if(sec == sec_status_insecure) { if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "Positive wildcard response is " verbose(VERB_ALGO, "Positive wildcard response is "
"insecure"); "insecure");
chase_reply->security = sec_status_insecure; chase_reply->security = sec_status_insecure;
return; return;
} else if(sec == sec_status_secure) } else if(sec == sec_status_secure) {
wc_NSEC_ok = 1; wc_NSEC_ok = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
}
} }
/* If after all this, we still haven't proven the positive wildcard /* If after all this, we still haven't proven the positive wildcard
@ -915,11 +1072,17 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
* @param chase_reply: answer to that query to validate. * @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches * @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood(). * the signer of the answer. The key entry isgood().
* @param qstate: query state for the region.
* @param vq: validator state for the nsec3 cache table.
* @param nsec3_calculations: current nsec3 hash calculations.
* @param suspend: returned true if the task takes too long and needs to
* suspend to continue the effort later.
*/ */
static void static void
validate_nodata_response(struct module_env* env, struct val_env* ve, validate_nodata_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply, struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey) struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{ {
/* Since we are here, there must be nothing in the ANSWER section to /* Since we are here, there must be nothing in the ANSWER section to
* validate. */ * validate. */
@ -936,6 +1099,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve,
int nsec3s_seen = 0; /* nsec3s seen */ int nsec3s_seen = 0; /* nsec3s seen */
struct ub_packed_rrset_key* s; struct ub_packed_rrset_key* s;
size_t i; size_t i;
*suspend = 0;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) { chase_reply->ns_numrrsets; i++) {
@ -974,16 +1138,23 @@ validate_nodata_response(struct module_env* env, struct val_env* ve,
} }
} }
if(!has_valid_nsec && nsec3s_seen) { if(!has_valid_nsec && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
enum sec_status sec = nsec3_prove_nodata(env, ve, enum sec_status sec = nsec3_prove_nodata(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets, chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey); chase_reply->ns_numrrsets, qchase, kkey,
&vq->nsec3_cache_table, nsec3_calculations);
if(sec == sec_status_insecure) { if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "NODATA response is insecure"); verbose(VERB_ALGO, "NODATA response is insecure");
chase_reply->security = sec_status_insecure; chase_reply->security = sec_status_insecure;
return; return;
} else if(sec == sec_status_secure) } else if(sec == sec_status_secure) {
has_valid_nsec = 1; has_valid_nsec = 1;
} else if(sec == sec_status_unchecked) {
/* check is incomplete; suspend */
*suspend = 1;
return;
}
} }
if(!has_valid_nsec) { if(!has_valid_nsec) {
@ -1015,11 +1186,18 @@ validate_nodata_response(struct module_env* env, struct val_env* ve,
* @param kkey: the key entry, which is trusted, and which matches * @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood(). * the signer of the answer. The key entry isgood().
* @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency. * @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency.
* @param qstate: query state for the region.
* @param vq: validator state for the nsec3 cache table.
* @param nsec3_calculations: current nsec3 hash calculations.
* @param suspend: returned true if the task takes too long and needs to
* suspend to continue the effort later.
*/ */
static void static void
validate_nameerror_response(struct module_env* env, struct val_env* ve, validate_nameerror_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply, struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, int* rcode) struct key_entry_key* kkey, int* rcode,
struct module_qstate* qstate, struct val_qstate* vq,
int* nsec3_calculations, int* suspend)
{ {
int has_valid_nsec = 0; int has_valid_nsec = 0;
int has_valid_wnsec = 0; int has_valid_wnsec = 0;
@ -1029,6 +1207,7 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve,
uint8_t* ce; uint8_t* ce;
int ce_labs = 0; int ce_labs = 0;
int prev_ce_labs = 0; int prev_ce_labs = 0;
*suspend = 0;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) { chase_reply->ns_numrrsets; i++) {
@ -1058,13 +1237,18 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve,
nsec3s_seen = 1; nsec3s_seen = 1;
} }
if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) { if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
/* use NSEC3 proof, both answer and auth rrsets, in case /* use NSEC3 proof, both answer and auth rrsets, in case
* NSEC3s end up in the answer (due to qtype=NSEC3 or so) */ * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */
chase_reply->security = nsec3_prove_nameerror(env, ve, chase_reply->security = nsec3_prove_nameerror(env, ve,
chase_reply->rrsets, chase_reply->an_numrrsets+ chase_reply->rrsets, chase_reply->an_numrrsets+
chase_reply->ns_numrrsets, qchase, kkey); chase_reply->ns_numrrsets, qchase, kkey,
if(chase_reply->security != sec_status_secure) { &vq->nsec3_cache_table, nsec3_calculations);
if(chase_reply->security == sec_status_unchecked) {
*suspend = 1;
return;
} else if(chase_reply->security != sec_status_secure) {
verbose(VERB_QUERY, "NameError response failed nsec, " verbose(VERB_QUERY, "NameError response failed nsec, "
"nsec3 proof was %s", sec_status_to_string( "nsec3 proof was %s", sec_status_to_string(
chase_reply->security)); chase_reply->security));
@ -1076,26 +1260,34 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve,
/* If the message fails to prove either condition, it is bogus. */ /* If the message fails to prove either condition, it is bogus. */
if(!has_valid_nsec) { if(!has_valid_nsec) {
validate_nodata_response(env, ve, qchase, chase_reply, kkey,
qstate, vq, nsec3_calculations, suspend);
if(*suspend) return;
verbose(VERB_QUERY, "NameError response has failed to prove: " verbose(VERB_QUERY, "NameError response has failed to prove: "
"qname does not exist"); "qname does not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
/* Be lenient with RCODE in NSEC NameError responses */ /* Be lenient with RCODE in NSEC NameError responses */
validate_nodata_response(env, ve, qchase, chase_reply, kkey); if(chase_reply->security == sec_status_secure) {
if (chase_reply->security == sec_status_secure)
*rcode = LDNS_RCODE_NOERROR; *rcode = LDNS_RCODE_NOERROR;
} else {
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
}
return; return;
} }
if(!has_valid_wnsec) { if(!has_valid_wnsec) {
validate_nodata_response(env, ve, qchase, chase_reply, kkey,
qstate, vq, nsec3_calculations, suspend);
if(*suspend) return;
verbose(VERB_QUERY, "NameError response has failed to prove: " verbose(VERB_QUERY, "NameError response has failed to prove: "
"covering wildcard does not exist"); "covering wildcard does not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
/* Be lenient with RCODE in NSEC NameError responses */ /* Be lenient with RCODE in NSEC NameError responses */
validate_nodata_response(env, ve, qchase, chase_reply, kkey); if (chase_reply->security == sec_status_secure) {
if (chase_reply->security == sec_status_secure)
*rcode = LDNS_RCODE_NOERROR; *rcode = LDNS_RCODE_NOERROR;
} else {
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
}
return; return;
} }
@ -1155,11 +1347,17 @@ validate_referral_response(struct reply_info* chase_reply)
* @param chase_reply: answer to that query to validate. * @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches * @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood(). * the signer of the answer. The key entry isgood().
* @param qstate: query state for the region.
* @param vq: validator state for the nsec3 cache table.
* @param nsec3_calculations: current nsec3 hash calculations.
* @param suspend: returned true if the task takes too long and needs to
* suspend to continue the effort later.
*/ */
static void static void
validate_any_response(struct module_env* env, struct val_env* ve, validate_any_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply, struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey) struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{ {
/* all answer and auth rrsets already verified */ /* all answer and auth rrsets already verified */
/* but check if a wildcard response is given, then check NSEC/NSEC3 /* but check if a wildcard response is given, then check NSEC/NSEC3
@ -1170,6 +1368,7 @@ validate_any_response(struct module_env* env, struct val_env* ve,
int nsec3s_seen = 0; int nsec3s_seen = 0;
size_t i; size_t i;
struct ub_packed_rrset_key* s; struct ub_packed_rrset_key* s;
*suspend = 0;
if(qchase->qtype != LDNS_RR_TYPE_ANY) { if(qchase->qtype != LDNS_RR_TYPE_ANY) {
log_err("internal error: ANY validation called for non-ANY"); log_err("internal error: ANY validation called for non-ANY");
@ -1224,19 +1423,25 @@ validate_any_response(struct module_env* env, struct val_env* ve,
/* If this was a positive wildcard response that we haven't already /* If this was a positive wildcard response that we haven't already
* proven, and we have NSEC3 records, try to prove it using the NSEC3 * proven, and we have NSEC3 records, try to prove it using the NSEC3
* records. */ * records. */
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { if(wc != NULL && !wc_NSEC_ok && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
/* look both in answer and auth section for NSEC3s */ /* look both in answer and auth section for NSEC3s */
enum sec_status sec = nsec3_prove_wildcard(env, ve, enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets, chase_reply->rrsets,
chase_reply->an_numrrsets+chase_reply->ns_numrrsets, chase_reply->an_numrrsets+chase_reply->ns_numrrsets,
qchase, kkey, wc); qchase, kkey, wc, &vq->nsec3_cache_table,
nsec3_calculations);
if(sec == sec_status_insecure) { if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "Positive ANY wildcard response is " verbose(VERB_ALGO, "Positive ANY wildcard response is "
"insecure"); "insecure");
chase_reply->security = sec_status_insecure; chase_reply->security = sec_status_insecure;
return; return;
} else if(sec == sec_status_secure) } else if(sec == sec_status_secure) {
wc_NSEC_ok = 1; wc_NSEC_ok = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
}
} }
/* If after all this, we still haven't proven the positive wildcard /* If after all this, we still haven't proven the positive wildcard
@ -1269,11 +1474,17 @@ validate_any_response(struct module_env* env, struct val_env* ve,
* @param chase_reply: answer to that query to validate. * @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches * @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood(). * the signer of the answer. The key entry isgood().
* @param qstate: query state for the region.
* @param vq: validator state for the nsec3 cache table.
* @param nsec3_calculations: current nsec3 hash calculations.
* @param suspend: returned true if the task takes too long and needs to
* suspend to continue the effort later.
*/ */
static void static void
validate_cname_response(struct module_env* env, struct val_env* ve, validate_cname_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply, struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey) struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{ {
uint8_t* wc = NULL; uint8_t* wc = NULL;
size_t wl; size_t wl;
@ -1281,6 +1492,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
int nsec3s_seen = 0; int nsec3s_seen = 0;
size_t i; size_t i;
struct ub_packed_rrset_key* s; struct ub_packed_rrset_key* s;
*suspend = 0;
/* validate the ANSWER section - this will be the CNAME (+DNAME) */ /* validate the ANSWER section - this will be the CNAME (+DNAME) */
for(i=0; i<chase_reply->an_numrrsets; i++) { for(i=0; i<chase_reply->an_numrrsets; i++) {
@ -1345,17 +1557,23 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
/* If this was a positive wildcard response that we haven't already /* If this was a positive wildcard response that we haven't already
* proven, and we have NSEC3 records, try to prove it using the NSEC3 * proven, and we have NSEC3 records, try to prove it using the NSEC3
* records. */ * records. */
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { if(wc != NULL && !wc_NSEC_ok && nsec3s_seen &&
enum sec_status sec = nsec3_prove_wildcard(env, ve, nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets, chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, wc); chase_reply->ns_numrrsets, qchase, kkey, wc,
&vq->nsec3_cache_table, nsec3_calculations);
if(sec == sec_status_insecure) { if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "wildcard CNAME response is " verbose(VERB_ALGO, "wildcard CNAME response is "
"insecure"); "insecure");
chase_reply->security = sec_status_insecure; chase_reply->security = sec_status_insecure;
return; return;
} else if(sec == sec_status_secure) } else if(sec == sec_status_secure) {
wc_NSEC_ok = 1; wc_NSEC_ok = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
}
} }
/* If after all this, we still haven't proven the positive wildcard /* If after all this, we still haven't proven the positive wildcard
@ -1386,11 +1604,17 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
* @param chase_reply: answer to that query to validate. * @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches * @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood(). * the signer of the answer. The key entry isgood().
* @param qstate: query state for the region.
* @param vq: validator state for the nsec3 cache table.
* @param nsec3_calculations: current nsec3 hash calculations.
* @param suspend: returned true if the task takes too long and needs to
* suspend to continue the effort later.
*/ */
static void static void
validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply, struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey) struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{ {
int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/ int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/
uint8_t* ce = NULL; /* for wildcard nodata responses. This is the uint8_t* ce = NULL; /* for wildcard nodata responses. This is the
@ -1404,6 +1628,7 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */ uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */
int ce_labs = 0; int ce_labs = 0;
int prev_ce_labs = 0; int prev_ce_labs = 0;
*suspend = 0;
/* the AUTHORITY section */ /* the AUTHORITY section */
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
@ -1469,11 +1694,13 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return; return;
} }
if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) { if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
int nodata; int nodata;
enum sec_status sec = nsec3_prove_nxornodata(env, ve, enum sec_status sec = nsec3_prove_nxornodata(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets, chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, &nodata); chase_reply->ns_numrrsets, qchase, kkey, &nodata,
&vq->nsec3_cache_table, nsec3_calculations);
if(sec == sec_status_insecure) { if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "CNAMEchain to noanswer response " verbose(VERB_ALGO, "CNAMEchain to noanswer response "
"is insecure"); "is insecure");
@ -1483,6 +1710,9 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
if(nodata) if(nodata)
nodata_valid_nsec = 1; nodata_valid_nsec = 1;
else nxdomain_valid_nsec = 1; else nxdomain_valid_nsec = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
} }
} }
@ -1826,13 +2056,37 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
* Uses negative cache for NSEC3 lookup of DS responses. */ * Uses negative cache for NSEC3 lookup of DS responses. */
/* only if cache not blacklisted, of course */ /* only if cache not blacklisted, of course */
struct dns_msg* msg; struct dns_msg* msg;
if(!qstate->blacklist && !vq->chain_blacklist && int suspend;
if(vq->sub_ds_msg) {
/* We have a suspended DS reply from a sub-query;
* process it. */
verbose(VERB_ALGO, "Process suspended sub DS response");
msg = vq->sub_ds_msg;
process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR,
msg, &msg->qinfo, NULL, &suspend);
if(suspend) {
/* we'll come back here later to continue */
if(!validate_suspend_setup_timer(qstate, vq,
id, VAL_FINDKEY_STATE))
return val_error(qstate, id);
return 0;
}
vq->sub_ds_msg = NULL;
return 1; /* continue processing ds-response results */
} else if(!qstate->blacklist && !vq->chain_blacklist &&
(msg=val_find_DS(qstate->env, target_key_name, (msg=val_find_DS(qstate->env, target_key_name,
target_key_len, vq->qchase.qclass, qstate->region, target_key_len, vq->qchase.qclass, qstate->region,
vq->key_entry->name)) ) { vq->key_entry->name)) ) {
verbose(VERB_ALGO, "Process cached DS response"); verbose(VERB_ALGO, "Process cached DS response");
process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR,
msg, &msg->qinfo, NULL); msg, &msg->qinfo, NULL, &suspend);
if(suspend) {
/* we'll come back here later to continue */
if(!validate_suspend_setup_timer(qstate, vq,
id, VAL_FINDKEY_STATE))
return val_error(qstate, id);
return 0;
}
return 1; /* continue processing ds-response results */ return 1; /* continue processing ds-response results */
} }
if(!generate_request(qstate, id, target_key_name, if(!generate_request(qstate, id, target_key_name,
@ -1875,7 +2129,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id) struct val_env* ve, int id)
{ {
enum val_classification subtype; enum val_classification subtype;
int rcode; int rcode, suspend, nsec3_calculations = 0;
if(!vq->key_entry) { if(!vq->key_entry) {
verbose(VERB_ALGO, "validate: no key entry, failed"); verbose(VERB_ALGO, "validate: no key entry, failed");
@ -1932,8 +2186,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
/* check signatures in the message; /* check signatures in the message;
* answer and authority must be valid, additional is only checked. */ * answer and authority must be valid, additional is only checked. */
if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase, if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase,
vq->chase_reply, vq->key_entry)) { vq->chase_reply, vq->key_entry, &suspend)) {
if(suspend) {
if(!validate_suspend_setup_timer(qstate, vq,
id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
/* workaround bad recursor out there that truncates (even /* workaround bad recursor out there that truncates (even
* with EDNS4k) to 512 by removing RRSIG from auth section * with EDNS4k) to 512 by removing RRSIG from auth section
* for positive replies*/ * for positive replies*/
@ -1962,7 +2222,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
case VAL_CLASS_POSITIVE: case VAL_CLASS_POSITIVE:
verbose(VERB_ALGO, "Validating a positive response"); verbose(VERB_ALGO, "Validating a positive response");
validate_positive_response(qstate->env, ve, validate_positive_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry); &vq->qchase, vq->chase_reply, vq->key_entry,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(positive): %s", verbose(VERB_DETAIL, "validate(positive): %s",
sec_status_to_string( sec_status_to_string(
vq->chase_reply->security)); vq->chase_reply->security));
@ -1971,7 +2238,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
case VAL_CLASS_NODATA: case VAL_CLASS_NODATA:
verbose(VERB_ALGO, "Validating a nodata response"); verbose(VERB_ALGO, "Validating a nodata response");
validate_nodata_response(qstate->env, ve, validate_nodata_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry); &vq->qchase, vq->chase_reply, vq->key_entry,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(nodata): %s", verbose(VERB_DETAIL, "validate(nodata): %s",
sec_status_to_string( sec_status_to_string(
vq->chase_reply->security)); vq->chase_reply->security));
@ -1981,7 +2255,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags); rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags);
verbose(VERB_ALGO, "Validating a nxdomain response"); verbose(VERB_ALGO, "Validating a nxdomain response");
validate_nameerror_response(qstate->env, ve, validate_nameerror_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry, &rcode); &vq->qchase, vq->chase_reply, vq->key_entry, &rcode,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(nxdomain): %s", verbose(VERB_DETAIL, "validate(nxdomain): %s",
sec_status_to_string( sec_status_to_string(
vq->chase_reply->security)); vq->chase_reply->security));
@ -1992,7 +2273,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
case VAL_CLASS_CNAME: case VAL_CLASS_CNAME:
verbose(VERB_ALGO, "Validating a cname response"); verbose(VERB_ALGO, "Validating a cname response");
validate_cname_response(qstate->env, ve, validate_cname_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry); &vq->qchase, vq->chase_reply, vq->key_entry,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(cname): %s", verbose(VERB_DETAIL, "validate(cname): %s",
sec_status_to_string( sec_status_to_string(
vq->chase_reply->security)); vq->chase_reply->security));
@ -2002,7 +2290,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
verbose(VERB_ALGO, "Validating a cname noanswer " verbose(VERB_ALGO, "Validating a cname noanswer "
"response"); "response");
validate_cname_noanswer_response(qstate->env, ve, validate_cname_noanswer_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry); &vq->qchase, vq->chase_reply, vq->key_entry,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(cname_noanswer): %s", verbose(VERB_DETAIL, "validate(cname_noanswer): %s",
sec_status_to_string( sec_status_to_string(
vq->chase_reply->security)); vq->chase_reply->security));
@ -2019,8 +2314,15 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
case VAL_CLASS_ANY: case VAL_CLASS_ANY:
verbose(VERB_ALGO, "Validating a positive ANY " verbose(VERB_ALGO, "Validating a positive ANY "
"response"); "response");
validate_any_response(qstate->env, ve, &vq->qchase, validate_any_response(qstate->env, ve, &vq->qchase,
vq->chase_reply, vq->key_entry); vq->chase_reply, vq->key_entry, qstate, vq,
&nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(positive_any): %s", verbose(VERB_DETAIL, "validate(positive_any): %s",
sec_status_to_string( sec_status_to_string(
vq->chase_reply->security)); vq->chase_reply->security));
@ -2129,16 +2431,13 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
if(vq->orig_msg->rep->security == sec_status_bogus) { if(vq->orig_msg->rep->security == sec_status_bogus) {
/* see if we can try again to fetch data */ /* see if we can try again to fetch data */
if(vq->restart_count < ve->max_restart) { if(vq->restart_count < ve->max_restart) {
int restart_count = vq->restart_count+1;
verbose(VERB_ALGO, "validation failed, " verbose(VERB_ALGO, "validation failed, "
"blacklist and retry to fetch data"); "blacklist and retry to fetch data");
val_blacklist(&qstate->blacklist, qstate->region, val_blacklist(&qstate->blacklist, qstate->region,
qstate->reply_origin, 0); qstate->reply_origin, 0);
qstate->reply_origin = NULL; qstate->reply_origin = NULL;
qstate->errinf = NULL; qstate->errinf = NULL;
memset(vq, 0, sizeof(*vq)); val_restart(vq);
vq->restart_count = restart_count;
vq->state = VAL_INIT_STATE;
verbose(VERB_ALGO, "pass back to next module"); verbose(VERB_ALGO, "pass back to next module");
qstate->ext_state[id] = module_restart_next; qstate->ext_state[id] = module_restart_next;
return 0; return 0;
@ -2465,7 +2764,10 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
* DS response indicated an end to secure space, is_good if the DS * DS response indicated an end to secure space, is_good if the DS
* validated. It returns ke=NULL if the DS response indicated that the * validated. It returns ke=NULL if the DS response indicated that the
* request wasn't a delegation point. * request wasn't a delegation point.
* @return 0 on servfail error (malloc failure). * @return
* 0 on success,
* 1 on servfail error (malloc failure),
* 2 on NSEC3 suspend.
*/ */
static int static int
ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
@ -2476,6 +2778,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
char* reason = NULL; char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
enum val_classification subtype; enum val_classification subtype;
int verified;
if(rcode != LDNS_RCODE_NOERROR) { if(rcode != LDNS_RCODE_NOERROR) {
char rc[16]; char rc[16];
rc[0]=0; rc[0]=0;
@ -2506,7 +2809,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
/* Verify only returns BOGUS or SECURE. If the rrset is /* Verify only returns BOGUS or SECURE. If the rrset is
* bogus, then we are done. */ * bogus, then we are done. */
sec = val_verify_rrset_entry(qstate->env, ve, ds, sec = val_verify_rrset_entry(qstate->env, ve, ds,
vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate); vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified);
if(sec != sec_status_secure) { if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did " verbose(VERB_DETAIL, "DS rrset in DS response did "
"not verify"); "not verify");
@ -2524,7 +2827,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
ub_packed_rrset_ttl(ds), ub_packed_rrset_ttl(ds),
LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL, LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL,
*qstate->env->now); *qstate->env->now);
return (*ke) != NULL; return (*ke) == NULL;
} }
/* Otherwise, we return the positive response. */ /* Otherwise, we return the positive response. */
@ -2532,7 +2835,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
*ke = key_entry_create_rrset(qstate->region, *ke = key_entry_create_rrset(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
NULL, LDNS_EDE_NONE, NULL, *qstate->env->now); NULL, LDNS_EDE_NONE, NULL, *qstate->env->now);
return (*ke) != NULL; return (*ke) == NULL;
} else if(subtype == VAL_CLASS_NODATA || } else if(subtype == VAL_CLASS_NODATA ||
subtype == VAL_CLASS_NAMEERROR) { subtype == VAL_CLASS_NAMEERROR) {
/* NODATA means that the qname exists, but that there was /* NODATA means that the qname exists, but that there was
@ -2566,12 +2869,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
qinfo->qclass, proof_ttl, qinfo->qclass, proof_ttl,
LDNS_EDE_NONE, NULL, LDNS_EDE_NONE, NULL,
*qstate->env->now); *qstate->env->now);
return (*ke) != NULL; return (*ke) == NULL;
case sec_status_insecure: case sec_status_insecure:
verbose(VERB_DETAIL, "NSEC RRset for the " verbose(VERB_DETAIL, "NSEC RRset for the "
"referral proved not a delegation point"); "referral proved not a delegation point");
*ke = NULL; *ke = NULL;
return 1; return 0;
case sec_status_bogus: case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC RRset for the " verbose(VERB_DETAIL, "NSEC RRset for the "
"referral did not prove no DS."); "referral did not prove no DS.");
@ -2583,10 +2886,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
break; break;
} }
if(!nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
log_err("malloc failure in ds_response_to_ke for "
"NSEC3 cache");
reason = "malloc failure";
errinf_ede(qstate, reason, 0);
goto return_bogus;
}
sec = nsec3_prove_nods(qstate->env, ve, sec = nsec3_prove_nods(qstate->env, ve,
msg->rep->rrsets + msg->rep->an_numrrsets, msg->rep->rrsets + msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason,
&reason_bogus, qstate); &reason_bogus, qstate, &vq->nsec3_cache_table);
switch(sec) { switch(sec) {
case sec_status_insecure: case sec_status_insecure:
/* case insecure also continues to unsigned /* case insecure also continues to unsigned
@ -2600,18 +2910,19 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
qinfo->qclass, proof_ttl, qinfo->qclass, proof_ttl,
LDNS_EDE_NONE, NULL, LDNS_EDE_NONE, NULL,
*qstate->env->now); *qstate->env->now);
return (*ke) != NULL; return (*ke) == NULL;
case sec_status_indeterminate: case sec_status_indeterminate:
verbose(VERB_DETAIL, "NSEC3s for the " verbose(VERB_DETAIL, "NSEC3s for the "
"referral proved no delegation"); "referral proved no delegation");
*ke = NULL; *ke = NULL;
return 1; return 0;
case sec_status_bogus: case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC3s for the " verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS."); "referral did not prove no DS.");
errinf_ede(qstate, reason, reason_bogus); errinf_ede(qstate, reason, reason_bogus);
goto return_bogus; goto return_bogus;
case sec_status_unchecked: case sec_status_unchecked:
return 2;
default: default:
/* NSEC3 proof did not work */ /* NSEC3 proof did not work */
break; break;
@ -2652,13 +2963,13 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
} }
sec = val_verify_rrset_entry(qstate->env, ve, cname, sec = val_verify_rrset_entry(qstate->env, ve, cname,
vq->key_entry, &reason, &reason_bogus, vq->key_entry, &reason, &reason_bogus,
LDNS_SECTION_ANSWER, qstate); LDNS_SECTION_ANSWER, qstate, &verified);
if(sec == sec_status_secure) { if(sec == sec_status_secure) {
verbose(VERB_ALGO, "CNAME validated, " verbose(VERB_ALGO, "CNAME validated, "
"proof that DS does not exist"); "proof that DS does not exist");
/* and that it is not a referral point */ /* and that it is not a referral point */
*ke = NULL; *ke = NULL;
return 1; return 0;
} }
errinf(qstate, "CNAME in DS response was not secure."); errinf(qstate, "CNAME in DS response was not secure.");
errinf_ede(qstate, reason, reason_bogus); errinf_ede(qstate, reason, reason_bogus);
@ -2682,7 +2993,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
*ke = key_entry_create_bad(qstate->region, qinfo->qname, *ke = key_entry_create_bad(qstate->region, qinfo->qname,
qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL, qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL,
reason_bogus, reason, *qstate->env->now); reason_bogus, reason, *qstate->env->now);
return (*ke) != NULL; return (*ke) == NULL;
} }
/** /**
@ -2703,17 +3014,31 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
static void static void
process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct sock_list* origin) struct sock_list* origin, int* suspend)
{ {
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* dske = NULL; struct key_entry_key* dske = NULL;
uint8_t* olds = vq->empty_DS_name; uint8_t* olds = vq->empty_DS_name;
int ret;
*suspend = 0;
vq->empty_DS_name = NULL; vq->empty_DS_name = NULL;
if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) { ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske);
if(ret != 0) {
switch(ret) {
case 1:
log_err("malloc failure in process_ds_response"); log_err("malloc failure in process_ds_response");
vq->key_entry = NULL; /* make it error */ vq->key_entry = NULL; /* make it error */
vq->state = VAL_VALIDATE_STATE; vq->state = VAL_VALIDATE_STATE;
return; return;
case 2:
*suspend = 1;
return;
default:
log_err("unhandled error value for ds_response_to_ke");
vq->key_entry = NULL; /* make it error */
vq->state = VAL_VALIDATE_STATE;
return;
}
} }
if(dske == NULL) { if(dske == NULL) {
vq->empty_DS_name = regional_alloc_init(qstate->region, vq->empty_DS_name = regional_alloc_init(qstate->region,
@ -2965,9 +3290,26 @@ val_inform_super(struct module_qstate* qstate, int id,
return; return;
} }
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) {
int suspend;
process_ds_response(super, vq, id, qstate->return_rcode, process_ds_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, &qstate->qinfo, qstate->return_msg, &qstate->qinfo,
qstate->reply_origin); qstate->reply_origin, &suspend);
/* If NSEC3 was needed during validation, NULL the NSEC3 cache;
* it will be re-initiated if needed later on.
* Validation (and the cache table) are happening/allocated in
* the super qstate whilst the RRs are allocated (and pointed
* to) in this sub qstate. */
if(vq->nsec3_cache_table.ct) {
vq->nsec3_cache_table.ct = NULL;
}
if(suspend) {
/* deep copy the return_msg to vq->sub_ds_msg; it will
* be resumed later in the super state with the caveat
* that the initial calculations will be re-caclulated
* and re-suspended there before continuing. */
vq->sub_ds_msg = dns_msg_deepcopy_region(
qstate->return_msg, super->region);
}
return; return;
} else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) {
process_dnskey_response(super, vq, id, qstate->return_rcode, process_dnskey_response(super, vq, id, qstate->return_rcode,
@ -2981,8 +3323,15 @@ val_inform_super(struct module_qstate* qstate, int id,
void void
val_clear(struct module_qstate* qstate, int id) val_clear(struct module_qstate* qstate, int id)
{ {
struct val_qstate* vq;
if(!qstate) if(!qstate)
return; return;
vq = (struct val_qstate*)qstate->minfo[id];
if(vq) {
if(vq->suspend_timer) {
comm_timer_delete(vq->suspend_timer);
}
}
/* everything is allocated in the region, so assign NULL */ /* everything is allocated in the region, so assign NULL */
qstate->minfo[id] = NULL; qstate->minfo[id] = NULL;
} }

View file

@ -45,11 +45,13 @@
#include "util/module.h" #include "util/module.h"
#include "util/data/msgreply.h" #include "util/data/msgreply.h"
#include "validator/val_utils.h" #include "validator/val_utils.h"
#include "validator/val_nsec3.h"
struct val_anchors; struct val_anchors;
struct key_cache; struct key_cache;
struct key_entry_key; struct key_entry_key;
struct val_neg_cache; struct val_neg_cache;
struct config_strlist; struct config_strlist;
struct comm_timer;
/** /**
* This is the TTL to use when a trust anchor fails to prime. A trust anchor * This is the TTL to use when a trust anchor fails to prime. A trust anchor
@ -215,6 +217,19 @@ struct val_qstate {
/** true if this state is waiting to prime a trust anchor */ /** true if this state is waiting to prime a trust anchor */
int wait_prime_ta; int wait_prime_ta;
/** State to continue with RRSIG validation in a message later */
int msg_signatures_state;
/** The rrset index for the msg signatures to continue from */
size_t msg_signatures_index;
/** Cache table for NSEC3 hashes */
struct nsec3_cache_table nsec3_cache_table;
/** DS message from sub if it got suspended from NSEC3 calculations */
struct dns_msg* sub_ds_msg;
/** The timer to resume processing msg signatures */
struct comm_timer* suspend_timer;
/** Number of suspends */
int suspend_count;
}; };
/** /**
@ -262,4 +277,7 @@ void val_clear(struct module_qstate* qstate, int id);
*/ */
size_t val_get_mem(struct module_env* env, int id); size_t val_get_mem(struct module_env* env, int id);
/** Timer callback for msg signatures continue timer */
void validate_suspend_timer_cb(void* arg);
#endif /* VALIDATOR_VALIDATOR_H */ #endif /* VALIDATOR_VALIDATOR_H */

View file

@ -793,7 +793,7 @@
#define PACKAGE_NAME "unbound" #define PACKAGE_NAME "unbound"
/* Define to the full name and version of this package. */ /* Define to the full name and version of this package. */
#define PACKAGE_STRING "unbound 1.19.0" #define PACKAGE_STRING "unbound 1.19.1"
/* Define to the one symbol short name of this package. */ /* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "unbound" #define PACKAGE_TARNAME "unbound"
@ -802,7 +802,7 @@
#define PACKAGE_URL "" #define PACKAGE_URL ""
/* Define to the version of this package. */ /* Define to the version of this package. */
#define PACKAGE_VERSION "1.19.0" #define PACKAGE_VERSION "1.19.1"
/* default pidfile location */ /* default pidfile location */
#define PIDFILE "/var/unbound/unbound.pid" #define PIDFILE "/var/unbound/unbound.pid"