mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-28 03:42:04 +01:00
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:
commit
b76ef9a7cb
11
contrib/unbound/config.guess
vendored
11
contrib/unbound/config.guess
vendored
@ -1,10 +1,10 @@
|
||||
#! /bin/sh
|
||||
# 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
|
||||
|
||||
timestamp='2023-08-22'
|
||||
timestamp='2024-01-01'
|
||||
|
||||
# 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
|
||||
@ -60,7 +60,7 @@ version="\
|
||||
GNU config.guess ($timestamp)
|
||||
|
||||
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
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
@ -165,6 +165,8 @@ Linux|GNU|GNU/*)
|
||||
LIBC=dietlibc
|
||||
#elif defined(__GLIBC__)
|
||||
LIBC=gnu
|
||||
#elif defined(__LLVM_LIBC__)
|
||||
LIBC=llvm
|
||||
#else
|
||||
#include <stdarg.h>
|
||||
/* First heuristic to detect musl libc. */
|
||||
@ -1593,6 +1595,9 @@ EOF
|
||||
*:Unleashed:*:*)
|
||||
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
|
||||
;;
|
||||
*:Ironclad:*:*)
|
||||
GUESS=$UNAME_MACHINE-unknown-ironclad
|
||||
;;
|
||||
esac
|
||||
|
||||
# Do we have a guess based on uname results?
|
||||
|
29
contrib/unbound/config.sub
vendored
29
contrib/unbound/config.sub
vendored
@ -1,10 +1,10 @@
|
||||
#! /bin/sh
|
||||
# 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
|
||||
|
||||
timestamp='2023-09-19'
|
||||
timestamp='2024-01-01'
|
||||
|
||||
# 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
|
||||
@ -76,7 +76,7 @@ Report bugs and patches to <config-patches@gnu.org>."
|
||||
version="\
|
||||
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
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
@ -1222,6 +1222,7 @@ case $cpu-$vendor in
|
||||
| moxie \
|
||||
| mt \
|
||||
| msp430 \
|
||||
| nanomips* \
|
||||
| nds32 | nds32le | nds32be \
|
||||
| nfp \
|
||||
| nios | nios2 | nios2eb | nios2el \
|
||||
@ -1253,6 +1254,7 @@ case $cpu-$vendor in
|
||||
| ubicom32 \
|
||||
| v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
|
||||
| vax \
|
||||
| vc4 \
|
||||
| visium \
|
||||
| w65 \
|
||||
| wasm32 | wasm64 \
|
||||
@ -1597,7 +1599,7 @@ case $cpu-$vendor in
|
||||
os=
|
||||
obj=elf
|
||||
;;
|
||||
mips*-*)
|
||||
mips*-*|nanomips*-*)
|
||||
os=
|
||||
obj=elf
|
||||
;;
|
||||
@ -1721,7 +1723,7 @@ fi
|
||||
|
||||
case $os in
|
||||
# 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"
|
||||
eabi* | gnueabi*)
|
||||
@ -1766,12 +1768,19 @@ case $os in
|
||||
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
|
||||
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
|
||||
| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
|
||||
| fiwix* | mlibc* | cos* | mbr* )
|
||||
| fiwix* | mlibc* | cos* | mbr* | ironclad* )
|
||||
;;
|
||||
# This one is extra strict with allowed versions
|
||||
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
|
||||
# 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)
|
||||
;;
|
||||
kernel* | msvc* )
|
||||
@ -1818,8 +1827,9 @@ esac
|
||||
# As a final step for OS-related things, validate the OS-kernel combination
|
||||
# (given a valid OS), if there is a kernel.
|
||||
case $kernel-$os-$obj in
|
||||
linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \
|
||||
| linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- )
|
||||
linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \
|
||||
| linux-mlibc*- | linux-musl*- | linux-newlib*- \
|
||||
| linux-relibc*- | linux-uclibc*- )
|
||||
;;
|
||||
uclinux-uclibc*- )
|
||||
;;
|
||||
@ -1827,7 +1837,8 @@ case $kernel-$os-$obj in
|
||||
;;
|
||||
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
|
||||
# require a kernel.
|
||||
echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2
|
||||
|
25
contrib/unbound/configure
vendored
25
contrib/unbound/configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# 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>.
|
||||
#
|
||||
@ -591,8 +591,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='unbound'
|
||||
PACKAGE_TARNAME='unbound'
|
||||
PACKAGE_VERSION='1.19.0'
|
||||
PACKAGE_STRING='unbound 1.19.0'
|
||||
PACKAGE_VERSION='1.19.1'
|
||||
PACKAGE_STRING='unbound 1.19.1'
|
||||
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
|
||||
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.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
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]...
|
||||
|
||||
@ -1543,7 +1543,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of unbound 1.19.0:";;
|
||||
short | recursive ) echo "Configuration of unbound 1.19.1:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1785,7 +1785,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
unbound configure 1.19.0
|
||||
unbound configure 1.19.1
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@ -2494,7 +2494,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by unbound $as_me 1.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
|
||||
|
||||
$ $0 $@
|
||||
@ -2846,11 +2846,11 @@ UNBOUND_VERSION_MAJOR=1
|
||||
|
||||
UNBOUND_VERSION_MINOR=19
|
||||
|
||||
UNBOUND_VERSION_MICRO=0
|
||||
UNBOUND_VERSION_MICRO=1
|
||||
|
||||
|
||||
LIBUNBOUND_CURRENT=9
|
||||
LIBUNBOUND_REVISION=23
|
||||
LIBUNBOUND_REVISION=24
|
||||
LIBUNBOUND_AGE=1
|
||||
# 1.0.0 had 0:12:0
|
||||
# 1.0.1 had 0:13:0
|
||||
@ -2941,6 +2941,7 @@ LIBUNBOUND_AGE=1
|
||||
# 1.17.1 had 9:21:1
|
||||
# 1.18.0 had 9:22: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
|
||||
# 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'`
|
||||
|
||||
@ -22413,7 +22414,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by unbound $as_me 1.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
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -22479,7 +22480,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
unbound config.status 1.19.0
|
||||
unbound config.status 1.19.1
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
@ -11,14 +11,14 @@ sinclude(dnscrypt/dnscrypt.m4)
|
||||
# must be numbers. ac_defun because of later processing
|
||||
m4_define([VERSION_MAJOR],[1])
|
||||
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_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR])
|
||||
AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
|
||||
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
|
||||
|
||||
LIBUNBOUND_CURRENT=9
|
||||
LIBUNBOUND_REVISION=23
|
||||
LIBUNBOUND_REVISION=24
|
||||
LIBUNBOUND_AGE=1
|
||||
# 1.0.0 had 0:12:0
|
||||
# 1.0.1 had 0:13:0
|
||||
@ -109,6 +109,7 @@ LIBUNBOUND_AGE=1
|
||||
# 1.17.1 had 9:21:1
|
||||
# 1.18.0 had 9:22: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
|
||||
# Revision -- which iteration of the implementation of the binary
|
||||
|
@ -1,4 +1,4 @@
|
||||
README for Unbound 1.19.0
|
||||
README for Unbound 1.19.1
|
||||
Copyright 2007 NLnet Labs
|
||||
http://unbound.net
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
@ -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
|
||||
.\"
|
||||
@ -44,7 +44,7 @@
|
||||
.B ub_ctx_zone_remove,
|
||||
.B ub_ctx_data_add,
|
||||
.B ub_ctx_data_remove
|
||||
\- Unbound DNS validating resolver 1.19.0 functions.
|
||||
\- Unbound DNS validating resolver 1.19.1 functions.
|
||||
.SH "SYNOPSIS"
|
||||
.B #include <unbound.h>
|
||||
.LP
|
||||
|
@ -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
|
||||
.\"
|
||||
|
@ -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
|
||||
.\"
|
||||
|
@ -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
|
||||
.\"
|
||||
|
@ -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
|
||||
.\"
|
||||
|
@ -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
|
||||
.\"
|
||||
@ -9,7 +9,7 @@
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.B unbound
|
||||
\- Unbound DNS validating resolver 1.19.0.
|
||||
\- Unbound DNS validating resolver 1.19.1.
|
||||
.SH "SYNOPSIS"
|
||||
.B unbound
|
||||
.RB [ \-h ]
|
||||
|
@ -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
|
||||
.\"
|
||||
|
@ -7774,6 +7774,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
|
||||
enum sec_status sec;
|
||||
struct val_env* ve;
|
||||
int m;
|
||||
int verified = 0;
|
||||
m = modstack_find(mods, "validator");
|
||||
if(m == -1) {
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
return 1;
|
||||
}
|
||||
|
22
contrib/unbound/services/cache/dns.c
vendored
22
contrib/unbound/services/cache/dns.c
vendored
@ -690,6 +690,28 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
|
||||
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 */
|
||||
static struct dns_msg*
|
||||
rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
|
||||
|
9
contrib/unbound/services/cache/dns.h
vendored
9
contrib/unbound/services/cache/dns.h
vendored
@ -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,
|
||||
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
|
||||
* @param env: module environment with the DNS cache.
|
||||
|
@ -199,6 +199,9 @@ SECTION QUESTION
|
||||
example.com. IN ANY
|
||||
ENTRY_END
|
||||
|
||||
; Allow validation resuming for the RRSIGs
|
||||
STEP 21 TIME_PASSES ELAPSE 0.05
|
||||
|
||||
; recursion happens here.
|
||||
STEP 30 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
|
@ -131,6 +131,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
|
||||
else if(fptr == &pending_udp_timer_delay_cb) return 1;
|
||||
else if(fptr == &worker_stat_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
|
||||
else if(fptr == &wsvc_cron_cb) return 1;
|
||||
#endif
|
||||
|
@ -181,6 +181,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve,
|
||||
{
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)
|
||||
nsec->entry.data;
|
||||
int verified = 0;
|
||||
if(!d) return 0;
|
||||
if(d->security == sec_status_secure)
|
||||
return 1;
|
||||
@ -188,7 +189,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve,
|
||||
if(d->security == sec_status_secure)
|
||||
return 1;
|
||||
d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason,
|
||||
reason_bogus, LDNS_SECTION_AUTHORITY, qstate);
|
||||
reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified);
|
||||
if(d->security == sec_status_secure) {
|
||||
rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
|
||||
return 1;
|
||||
|
@ -57,6 +57,19 @@
|
||||
/* we include nsec.h for the bitmap_has_type function */
|
||||
#include "validator/val_nsec.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
|
||||
@ -532,6 +545,17 @@ nsec3_hash_cmp(const void* c1, const void* c2)
|
||||
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
|
||||
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)
|
||||
@ -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);
|
||||
if(c) {
|
||||
*hash = c;
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
/* create a new entry */
|
||||
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;
|
||||
r = nsec3_calc_hash(region, buf, c);
|
||||
if(r != 1)
|
||||
return r;
|
||||
return r; /* returns -1 or 0 */
|
||||
r = nsec3_calc_b32(region, buf, c);
|
||||
if(r != 1)
|
||||
return r;
|
||||
return r; /* returns 0 */
|
||||
#ifdef UNBOUND_DEBUG
|
||||
n =
|
||||
#else
|
||||
@ -704,6 +728,7 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt,
|
||||
struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s)
|
||||
{
|
||||
uint8_t* nm = s->rk.dname;
|
||||
if(!hash) return 0; /* please clang */
|
||||
/* compare, does hash of name based on params in this NSEC3
|
||||
* match the owner name of this NSEC3?
|
||||
* 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 rrset: nsec3 that matches is returned here.
|
||||
* @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.
|
||||
*/
|
||||
static int
|
||||
find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt,
|
||||
rbtree_type* ct, uint8_t* nm, size_t nmlen,
|
||||
struct ub_packed_rrset_key** rrset, int* rr)
|
||||
struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen,
|
||||
struct ub_packed_rrset_key** rrset, int* rr,
|
||||
int* calculations)
|
||||
{
|
||||
size_t i_rs;
|
||||
int i_rr;
|
||||
struct ub_packed_rrset_key* s;
|
||||
struct nsec3_cached_hash* hash = NULL;
|
||||
int r;
|
||||
int calc_errors = 0;
|
||||
|
||||
/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
|
||||
for(s=filter_first(flt, &i_rs, &i_rr); s;
|
||||
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 */
|
||||
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);
|
||||
if(r == 0) {
|
||||
log_err("nsec3: malloc failure");
|
||||
break; /* alloc failure */
|
||||
} else if(r != 1)
|
||||
continue; /* malformed NSEC3 */
|
||||
else if(nsec3_hash_matches_owner(flt, hash, s)) {
|
||||
*rrset = s; /* rrset with this name */
|
||||
*rr = i_rr; /* matches hash with these parameters */
|
||||
return 1;
|
||||
} else if(r < 0) {
|
||||
/* malformed NSEC3 */
|
||||
calc_errors++;
|
||||
(*calculations)++;
|
||||
continue;
|
||||
} 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;
|
||||
@ -775,6 +816,7 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash,
|
||||
if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen))
|
||||
return 0; /* malformed RR proves nothing */
|
||||
|
||||
if(!hash) return 0; /* please clang */
|
||||
/* check the owner name is a hashed value . apex
|
||||
* base32 encoded values 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 rrset: covering NSEC3 rrset 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.
|
||||
*/
|
||||
static int
|
||||
find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt,
|
||||
rbtree_type* ct, uint8_t* nm, size_t nmlen,
|
||||
struct ub_packed_rrset_key** rrset, int* rr)
|
||||
struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen,
|
||||
struct ub_packed_rrset_key** rrset, int* rr,
|
||||
int* calculations)
|
||||
{
|
||||
size_t i_rs;
|
||||
int i_rr;
|
||||
struct ub_packed_rrset_key* s;
|
||||
struct nsec3_cached_hash* hash = NULL;
|
||||
int r;
|
||||
int calc_errors = 0;
|
||||
|
||||
/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
|
||||
for(s=filter_first(flt, &i_rs, &i_rr); s;
|
||||
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 */
|
||||
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);
|
||||
if(r == 0) {
|
||||
log_err("nsec3: malloc failure");
|
||||
break; /* alloc failure */
|
||||
} else if(r != 1)
|
||||
continue; /* malformed NSEC3 */
|
||||
else 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;
|
||||
} else if(r < 0) {
|
||||
/* malformed NSEC3 */
|
||||
calc_errors++;
|
||||
(*calculations)++;
|
||||
continue;
|
||||
} else {
|
||||
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;
|
||||
@ -869,11 +927,13 @@ find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt,
|
||||
* @param ct: cached hashes table.
|
||||
* @param qinfo: query that is verified for.
|
||||
* @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.
|
||||
*/
|
||||
static int
|
||||
nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
|
||||
rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce)
|
||||
nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
|
||||
struct nsec3_cache_table* ct, struct query_info* qinfo,
|
||||
struct ce_response* ce, int* calculations)
|
||||
{
|
||||
uint8_t* nm = qinfo->qname;
|
||||
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. */
|
||||
|
||||
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,
|
||||
&ce->ce_rrset, &ce->ce_rr)) {
|
||||
&ce->ce_rrset, &ce->ce_rr, calculations)) {
|
||||
ce->ce = nm;
|
||||
ce->ce_len = nmlen;
|
||||
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
|
||||
* certain that the ce.nc_rrset and ce.nc_rr are set properly.
|
||||
* @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.
|
||||
* secure if a closest encloser could be proven, ce is set.
|
||||
* insecure if the closest-encloser candidate turns out to prove
|
||||
* that an insecure delegation exists above the qname.
|
||||
* unchecked if no more hash calculations are allowed at this point.
|
||||
*/
|
||||
static enum sec_status
|
||||
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 ce_response* ce)
|
||||
nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
|
||||
struct nsec3_cache_table* ct, struct query_info* qinfo,
|
||||
int prove_does_not_exist, struct ce_response* ce, int* calculations)
|
||||
{
|
||||
uint8_t* nc;
|
||||
size_t nc_len;
|
||||
/* robust: clean out ce, in case it gets abused later */
|
||||
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 "
|
||||
"not find a candidate for the closest encloser.");
|
||||
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. */
|
||||
next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &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 "
|
||||
"candidate encloser was the closest encloser");
|
||||
"candidate encloser was the closest encloser");
|
||||
return sec_status_bogus;
|
||||
}
|
||||
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 */
|
||||
static enum sec_status
|
||||
nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
|
||||
rbtree_type* ct, struct query_info* qinfo)
|
||||
nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
|
||||
struct nsec3_cache_table* ct, struct query_info* qinfo, int* calc)
|
||||
{
|
||||
struct ce_response ce;
|
||||
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
|
||||
* use the variant that fails if the closest encloser turns out
|
||||
* 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_bogus)
|
||||
verbose(VERB_ALGO, "nsec3 nameerror proof: failed "
|
||||
"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 "
|
||||
"nsec3 is an insecure delegation");
|
||||
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
|
||||
* to prove that the wildcard does not exist. */
|
||||
log_assert(ce.ce);
|
||||
wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen);
|
||||
if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen,
|
||||
&wc_rrset, &wc_rr)) {
|
||||
wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen);
|
||||
if(!wc) {
|
||||
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 "
|
||||
"that the applicable wildcard did not exist.");
|
||||
return sec_status_bogus;
|
||||
@ -1064,14 +1180,13 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
|
||||
enum sec_status
|
||||
nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey)
|
||||
struct query_info* qinfo, struct key_entry_key* kkey,
|
||||
struct nsec3_cache_table* ct, int* calc)
|
||||
{
|
||||
rbtree_type ct;
|
||||
struct nsec3_filter flt;
|
||||
|
||||
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
|
||||
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 */
|
||||
if(!flt.zone)
|
||||
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 */
|
||||
log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone",
|
||||
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 */
|
||||
static enum sec_status
|
||||
nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
|
||||
rbtree_type* ct, struct query_info* qinfo)
|
||||
nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
|
||||
struct nsec3_cache_table* ct, struct query_info* qinfo,
|
||||
int* calc)
|
||||
{
|
||||
struct ce_response ce;
|
||||
uint8_t* wc;
|
||||
@ -1100,7 +1216,7 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
|
||||
enum sec_status sec;
|
||||
|
||||
if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len,
|
||||
&rrset, &rr)) {
|
||||
&rrset, &rr, calc)) {
|
||||
/* cases 1 and 2 */
|
||||
if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
|
||||
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;
|
||||
}
|
||||
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
|
||||
* can't match qname. Although, at this point, we know that it
|
||||
* 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) {
|
||||
verbose(VERB_ALGO, "proveNodata: did not match qname, "
|
||||
"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 "
|
||||
"delegation.");
|
||||
return sec_status_insecure;
|
||||
} else if(sec==sec_status_unchecked) {
|
||||
return sec_status_unchecked;
|
||||
}
|
||||
|
||||
/* Case 3: removed */
|
||||
|
||||
/* Case 4: */
|
||||
log_assert(ce.ce);
|
||||
wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen);
|
||||
if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) {
|
||||
wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen);
|
||||
if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr,
|
||||
calc)) {
|
||||
/* found wildcard */
|
||||
if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
|
||||
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;
|
||||
}
|
||||
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: */
|
||||
/* 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
|
||||
nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey)
|
||||
struct query_info* qinfo, struct key_entry_key* kkey,
|
||||
struct nsec3_cache_table* ct, int* calc)
|
||||
{
|
||||
rbtree_type ct;
|
||||
struct nsec3_filter flt;
|
||||
|
||||
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
|
||||
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 */
|
||||
if(!flt.zone)
|
||||
return sec_status_bogus; /* no RRs */
|
||||
if(nsec3_iteration_count_high(ve, &flt, kkey))
|
||||
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
|
||||
nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey, 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 ce_response ce;
|
||||
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))
|
||||
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 */
|
||||
if(!flt.zone)
|
||||
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.
|
||||
* Otherwise, we need to show that the next closer name is covered. */
|
||||
next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len);
|
||||
if(!find_covering_nsec3(env, &flt, &ct, nc, nc_len,
|
||||
&ce.nc_rrset, &ce.nc_rr)) {
|
||||
if(!find_covering_nsec3(env, &flt, ct, nc, nc_len,
|
||||
&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 "
|
||||
"NSEC3 that covered the next closer name.");
|
||||
return sec_status_bogus;
|
||||
@ -1294,6 +1449,7 @@ list_is_secure(struct module_env* env, struct val_env* ve,
|
||||
{
|
||||
struct packed_rrset_data* d;
|
||||
size_t i;
|
||||
int verified = 0;
|
||||
for(i=0; i<num; i++) {
|
||||
d = (struct packed_rrset_data*)list[i]->entry.data;
|
||||
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)
|
||||
continue;
|
||||
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) {
|
||||
verbose(VERB_ALGO, "NSEC3 did not verify");
|
||||
return 0;
|
||||
@ -1318,13 +1475,16 @@ enum sec_status
|
||||
nsec3_prove_nods(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey, char** reason,
|
||||
sldns_ede_code* reason_bogus, struct module_qstate* qstate)
|
||||
sldns_ede_code* reason_bogus, struct module_qstate* qstate,
|
||||
struct nsec3_cache_table* ct)
|
||||
{
|
||||
rbtree_type ct;
|
||||
struct nsec3_filter flt;
|
||||
struct ce_response ce;
|
||||
struct ub_packed_rrset_key* rrset;
|
||||
int rr;
|
||||
int calc = 0;
|
||||
enum sec_status sec;
|
||||
|
||||
log_assert(qinfo->qtype == LDNS_RR_TYPE_DS);
|
||||
|
||||
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";
|
||||
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 */
|
||||
if(!flt.zone) {
|
||||
*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
|
||||
* NODATA case. */
|
||||
if(find_matching_nsec3(env, &flt, &ct, qinfo->qname, qinfo->qname_len,
|
||||
&rrset, &rr)) {
|
||||
if(find_matching_nsec3(env, &flt, ct, qinfo->qname, qinfo->qname_len,
|
||||
&rrset, &rr, &calc)) {
|
||||
/* If the matching NSEC3 has the SOA bit set, it is from
|
||||
* the wrong zone (the child instead of the parent). If
|
||||
* 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. */
|
||||
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. */
|
||||
if(nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce)
|
||||
!= sec_status_secure) {
|
||||
sec = nsec3_prove_closest_encloser(env, &flt, ct, qinfo, 1, &ce, &calc);
|
||||
if(sec == sec_status_unchecked) {
|
||||
return sec_status_unchecked;
|
||||
} else if(sec != sec_status_secure) {
|
||||
/* an insecure delegation *above* the qname does not prove
|
||||
* anything about this qname exactly, and bogus is bogus */
|
||||
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
|
||||
nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey, int* nodata)
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey, int* nodata,
|
||||
struct nsec3_cache_table* ct, int* calc)
|
||||
{
|
||||
enum sec_status sec, secnx;
|
||||
rbtree_type ct;
|
||||
struct nsec3_filter flt;
|
||||
*nodata = 0;
|
||||
|
||||
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
|
||||
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 */
|
||||
if(!flt.zone)
|
||||
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
|
||||
* 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)
|
||||
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) {
|
||||
*nodata = 1;
|
||||
} else if(sec == sec_status_insecure) {
|
||||
*nodata = 1;
|
||||
} else if(secnx == sec_status_insecure) {
|
||||
sec = sec_status_insecure;
|
||||
} else if(sec == sec_status_unchecked) {
|
||||
return sec_status_unchecked;
|
||||
}
|
||||
return sec;
|
||||
}
|
||||
|
@ -98,6 +98,15 @@ struct sldns_buffer;
|
||||
/** The SHA1 hash algorithm for NSEC3 */
|
||||
#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
|
||||
* 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 qinfo: query that is verified for.
|
||||
* @param kkey: key entry that signed the NSEC3s.
|
||||
* @param ct: cached hashes table.
|
||||
* @param calc: current hash calculations.
|
||||
* @return:
|
||||
* 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
|
||||
nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey);
|
||||
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
|
||||
@ -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 qinfo: query that is verified for.
|
||||
* @param kkey: key entry that signed the NSEC3s.
|
||||
* @param ct: cached hashes table.
|
||||
* @param calc: current hash calculations.
|
||||
* @return:
|
||||
* 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
|
||||
nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey);
|
||||
|
||||
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
|
||||
@ -166,14 +182,18 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
|
||||
* @param kkey: key entry that signed the NSEC3s.
|
||||
* @param wc: The purported wildcard that matched. This is the wildcard name
|
||||
* as *.wildcard.name., with the *. label already removed.
|
||||
* @param ct: cached hashes table.
|
||||
* @param calc: current hash calculations.
|
||||
* @return:
|
||||
* 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
|
||||
nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey, 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.
|
||||
@ -189,17 +209,20 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
|
||||
* @param reason: string for bogus result.
|
||||
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
|
||||
* @param qstate: qstate with region.
|
||||
* @param ct: cached hashes table.
|
||||
* @return:
|
||||
* 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.
|
||||
* 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
|
||||
nsec3_prove_nods(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey, char** reason,
|
||||
sldns_ede_code* reason_bogus, struct module_qstate* qstate);
|
||||
sldns_ede_code* reason_bogus, struct module_qstate* qstate,
|
||||
struct nsec3_cache_table* ct);
|
||||
|
||||
/**
|
||||
* 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 nodata: if return value is secure, this indicates if nodata or
|
||||
* nxdomain was proven.
|
||||
* @param ct: cached hashes table.
|
||||
* @param calc: current hash calculations.
|
||||
* @return:
|
||||
* 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
|
||||
nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key** list, size_t num,
|
||||
struct query_info* qinfo, struct key_entry_key* kkey, 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.
|
||||
@ -256,6 +283,14 @@ struct nsec3_cached_hash {
|
||||
*/
|
||||
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.
|
||||
* 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 hash: the hash node is returned on success.
|
||||
* @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.
|
||||
* -1 if the NSEC3 rr was badly formatted (i.e. formerr).
|
||||
*/
|
||||
|
@ -79,6 +79,9 @@
|
||||
#include <openssl/engine.h>
|
||||
#endif
|
||||
|
||||
/** Maximum number of RRSIG validations for an RRset. */
|
||||
#define MAX_VALIDATE_RRSIGS 8
|
||||
|
||||
/** return number of rrs in an rrset */
|
||||
static size_t
|
||||
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 section: section of packet where this rrset comes from.
|
||||
* @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,
|
||||
* unchecked on error, or indeterminate if all keys are not supported by
|
||||
* 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 rbtree_type** sortree,
|
||||
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 */
|
||||
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))
|
||||
continue;
|
||||
numchecked ++;
|
||||
(*numverified)++;
|
||||
|
||||
/* see if key verifies */
|
||||
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;
|
||||
else if(sec == sec_status_indeterminate)
|
||||
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) {
|
||||
*reason = "signatures from unknown keys";
|
||||
@ -609,7 +623,7 @@ enum sec_status
|
||||
dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
|
||||
uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
|
||||
sldns_pkt_section section, struct module_qstate* qstate)
|
||||
sldns_pkt_section section, struct module_qstate* qstate, int* verified)
|
||||
{
|
||||
enum sec_status sec;
|
||||
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 */
|
||||
struct algo_needs needs;
|
||||
int alg;
|
||||
*verified = 0;
|
||||
|
||||
num = rrset_get_sigcount(rrset);
|
||||
if(num == 0) {
|
||||
@ -641,7 +656,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
|
||||
for(i=0; i<num; i++) {
|
||||
sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset,
|
||||
dnskey, i, &sortree, reason, reason_bogus,
|
||||
section, qstate);
|
||||
section, qstate, verified);
|
||||
/* see which algorithm has been fixed up */
|
||||
if(sec == sec_status_secure) {
|
||||
if(!sigalg)
|
||||
@ -653,6 +668,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
|
||||
algo_needs_set_bogus(&needs,
|
||||
(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) {
|
||||
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;
|
||||
uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx);
|
||||
int algo = dnskey_get_algo(dnskey, dnskey_idx);
|
||||
int numverified = 0;
|
||||
|
||||
num = rrset_get_sigcount(rrset);
|
||||
if(num == 0) {
|
||||
@ -714,8 +737,16 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
|
||||
if(sec == sec_status_secure)
|
||||
return sec;
|
||||
numchecked ++;
|
||||
numverified ++;
|
||||
if(sec == sec_status_indeterminate)
|
||||
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");
|
||||
if(!numchecked) {
|
||||
|
@ -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 section: section of packet where this rrset comes from.
|
||||
* @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.
|
||||
* UNCHECKED on allocation errors, unsupported algorithms, malformed data,
|
||||
* 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 ub_packed_rrset_key* dnskey, 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);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -58,6 +58,10 @@
|
||||
#include "sldns/wire2str.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
|
||||
val_classify_response(uint16_t query_flags, struct query_info* origqinf,
|
||||
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,
|
||||
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys,
|
||||
uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
|
||||
sldns_pkt_section section, struct module_qstate* qstate)
|
||||
sldns_pkt_section section, struct module_qstate* qstate,
|
||||
int *verified)
|
||||
{
|
||||
enum sec_status sec;
|
||||
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",
|
||||
rrset->rk.dname, ntohs(rrset->rk.type),
|
||||
ntohs(rrset->rk.rrset_class));
|
||||
*verified = 0;
|
||||
return d->security;
|
||||
}
|
||||
/* 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",
|
||||
rrset->rk.dname, ntohs(rrset->rk.type),
|
||||
ntohs(rrset->rk.rrset_class));
|
||||
*verified = 0;
|
||||
return d->security;
|
||||
}
|
||||
log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
|
||||
ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
|
||||
sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason,
|
||||
reason_bogus, section, qstate);
|
||||
reason_bogus, section, qstate, verified);
|
||||
verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
|
||||
regional_free_all(env->scratch);
|
||||
|
||||
@ -393,7 +400,8 @@ enum sec_status
|
||||
val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey,
|
||||
char** reason, sldns_ede_code *reason_bogus,
|
||||
sldns_pkt_section section, struct module_qstate* qstate)
|
||||
sldns_pkt_section section, struct module_qstate* qstate,
|
||||
int* verified)
|
||||
{
|
||||
/* temporary dnskey rrset-key */
|
||||
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.data = kd->rrset_data;
|
||||
sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason,
|
||||
reason_bogus, section, qstate);
|
||||
reason_bogus, section, qstate, verified);
|
||||
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,
|
||||
ds_idx)) {
|
||||
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;
|
||||
}
|
||||
numhashok++;
|
||||
|
@ -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 section: section of packet where this rrset comes from.
|
||||
* @param qstate: qstate with region.
|
||||
* @param verified: if not NULL, the number of RRSIG validations is returned.
|
||||
* @return security status of verification.
|
||||
*/
|
||||
enum sec_status val_verify_rrset_entry(struct module_env* env,
|
||||
struct val_env* ve, struct ub_packed_rrset_key* rrset,
|
||||
struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus,
|
||||
sldns_pkt_section section, struct module_qstate* qstate);
|
||||
sldns_pkt_section section, struct module_qstate* qstate,
|
||||
int* verified);
|
||||
|
||||
/**
|
||||
* Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but
|
||||
|
@ -64,10 +64,15 @@
|
||||
#include "sldns/wire2str.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 */
|
||||
static void process_ds_response(struct module_qstate* qstate,
|
||||
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
|
||||
@ -292,6 +297,21 @@ val_new(struct module_qstate* qstate, int id)
|
||||
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
|
||||
*
|
||||
@ -598,30 +618,42 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
* completed.
|
||||
*
|
||||
* @param qstate: query state.
|
||||
* @param vq: validator query state.
|
||||
* @param env: module env for verify.
|
||||
* @param ve: validator env for verify.
|
||||
* @param qchase: query that was made.
|
||||
* @param chase_reply: answer to validate.
|
||||
* @param key_entry: the key entry, which is trusted, and which matches
|
||||
* 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
|
||||
* fail to verify. The message is then set to bogus.
|
||||
*/
|
||||
static int
|
||||
validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
|
||||
struct val_env* ve, struct query_info* qchase,
|
||||
struct reply_info* chase_reply, struct key_entry_key* key_entry)
|
||||
validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
struct module_env* env, struct val_env* ve, struct query_info* qchase,
|
||||
struct reply_info* chase_reply, struct key_entry_key* key_entry,
|
||||
int* suspend)
|
||||
{
|
||||
uint8_t* sname;
|
||||
size_t i, slen;
|
||||
struct ub_packed_rrset_key* s;
|
||||
enum sec_status sec;
|
||||
int dname_seen = 0;
|
||||
int dname_seen = 0, num_verifies = 0, verified, have_state = 0;
|
||||
char* reason = NULL;
|
||||
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 */
|
||||
for(i=0; i<chase_reply->an_numrrsets; i++) {
|
||||
if(have_state && i <= vq->msg_signatures_index)
|
||||
continue;
|
||||
s = chase_reply->rrsets[i];
|
||||
/* Skip the CNAME following a (validated) DNAME.
|
||||
* 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 */
|
||||
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
|
||||
* message is BAD. */
|
||||
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) {
|
||||
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 */
|
||||
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
|
||||
chase_reply->ns_numrrsets; i++) {
|
||||
if(have_state && i <= vq->msg_signatures_index)
|
||||
continue;
|
||||
s = chase_reply->rrsets[i];
|
||||
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
|
||||
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate);
|
||||
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate,
|
||||
&verified);
|
||||
/* If anything in the authority section fails to be secure,
|
||||
* we have a bad message. */
|
||||
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);
|
||||
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
|
||||
@ -695,22 +758,103 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
|
||||
/* attempt to validate the ADDITIONAL section rrsets */
|
||||
for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
|
||||
i<chase_reply->rrset_count; i++) {
|
||||
if(have_state && i <= vq->msg_signatures_index)
|
||||
continue;
|
||||
s = chase_reply->rrsets[i];
|
||||
/* only validate rrs that have signatures with the key */
|
||||
/* leave others unchecked, those get removed later on too */
|
||||
val_find_rrset_signer(s, &sname, &slen);
|
||||
|
||||
verified = 0;
|
||||
if(sname && query_dname_compare(sname, key_entry->name)==0)
|
||||
(void)val_verify_rrset_entry(env, ve, s, key_entry,
|
||||
&reason, NULL, LDNS_SECTION_ADDITIONAL, qstate);
|
||||
&reason, NULL, LDNS_SECTION_ADDITIONAL, qstate,
|
||||
&verified);
|
||||
/* the additional section can fail to be secure,
|
||||
* it is optional, check signature in case we need
|
||||
* 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;
|
||||
}
|
||||
|
||||
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
|
||||
* 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 kkey: the key entry, which is trusted, and which matches
|
||||
* 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
|
||||
validate_positive_response(struct module_env* env, struct val_env* ve,
|
||||
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;
|
||||
size_t wl;
|
||||
@ -822,6 +972,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
|
||||
int nsec3s_seen = 0;
|
||||
size_t i;
|
||||
struct ub_packed_rrset_key* s;
|
||||
*suspend = 0;
|
||||
|
||||
/* validate the ANSWER section - this will be the answer itself */
|
||||
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
|
||||
* proven, and we have NSEC3 records, try to prove it using the NSEC3
|
||||
* records. */
|
||||
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
|
||||
enum sec_status sec = nsec3_prove_wildcard(env, ve,
|
||||
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen &&
|
||||
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->ns_numrrsets, qchase, kkey, wc);
|
||||
chase_reply->ns_numrrsets, qchase, kkey, wc,
|
||||
&vq->nsec3_cache_table, nsec3_calculations);
|
||||
if(sec == sec_status_insecure) {
|
||||
verbose(VERB_ALGO, "Positive wildcard response is "
|
||||
"insecure");
|
||||
chase_reply->security = sec_status_insecure;
|
||||
return;
|
||||
} else if(sec == sec_status_secure)
|
||||
} else if(sec == sec_status_secure) {
|
||||
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
|
||||
@ -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 kkey: the key entry, which is trusted, and which matches
|
||||
* 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
|
||||
validate_nodata_response(struct module_env* env, struct val_env* ve,
|
||||
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
|
||||
* validate. */
|
||||
@ -936,6 +1099,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve,
|
||||
int nsec3s_seen = 0; /* nsec3s seen */
|
||||
struct ub_packed_rrset_key* s;
|
||||
size_t i;
|
||||
*suspend = 0;
|
||||
|
||||
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
|
||||
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,
|
||||
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) {
|
||||
verbose(VERB_ALGO, "NODATA response is insecure");
|
||||
chase_reply->security = sec_status_insecure;
|
||||
return;
|
||||
} else if(sec == sec_status_secure)
|
||||
} else if(sec == sec_status_secure) {
|
||||
has_valid_nsec = 1;
|
||||
} else if(sec == sec_status_unchecked) {
|
||||
/* check is incomplete; suspend */
|
||||
*suspend = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* the signer of the answer. The key entry isgood().
|
||||
* @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
|
||||
validate_nameerror_response(struct module_env* env, struct val_env* ve,
|
||||
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_wnsec = 0;
|
||||
@ -1029,6 +1207,7 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve,
|
||||
uint8_t* ce;
|
||||
int ce_labs = 0;
|
||||
int prev_ce_labs = 0;
|
||||
*suspend = 0;
|
||||
|
||||
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
|
||||
chase_reply->ns_numrrsets; i++) {
|
||||
@ -1058,13 +1237,18 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve,
|
||||
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
|
||||
* NSEC3s end up in the answer (due to qtype=NSEC3 or so) */
|
||||
chase_reply->security = nsec3_prove_nameerror(env, ve,
|
||||
chase_reply->rrsets, chase_reply->an_numrrsets+
|
||||
chase_reply->ns_numrrsets, qchase, kkey);
|
||||
if(chase_reply->security != sec_status_secure) {
|
||||
chase_reply->ns_numrrsets, qchase, kkey,
|
||||
&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, "
|
||||
"nsec3 proof was %s", sec_status_to_string(
|
||||
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(!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: "
|
||||
"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 */
|
||||
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;
|
||||
} else {
|
||||
chase_reply->security = sec_status_bogus;
|
||||
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
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: "
|
||||
"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 */
|
||||
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;
|
||||
} else {
|
||||
chase_reply->security = sec_status_bogus;
|
||||
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1155,11 +1347,17 @@ validate_referral_response(struct reply_info* chase_reply)
|
||||
* @param chase_reply: answer to that query to validate.
|
||||
* @param kkey: the key entry, which is trusted, and which matches
|
||||
* 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
|
||||
validate_any_response(struct module_env* env, struct val_env* ve,
|
||||
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 */
|
||||
/* 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;
|
||||
size_t i;
|
||||
struct ub_packed_rrset_key* s;
|
||||
*suspend = 0;
|
||||
|
||||
if(qchase->qtype != LDNS_RR_TYPE_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
|
||||
* proven, and we have NSEC3 records, try to prove it using the NSEC3
|
||||
* 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 */
|
||||
enum sec_status sec = nsec3_prove_wildcard(env, ve,
|
||||
enum sec_status sec = nsec3_prove_wildcard(env, ve,
|
||||
chase_reply->rrsets,
|
||||
chase_reply->an_numrrsets+chase_reply->ns_numrrsets,
|
||||
qchase, kkey, wc);
|
||||
chase_reply->an_numrrsets+chase_reply->ns_numrrsets,
|
||||
qchase, kkey, wc, &vq->nsec3_cache_table,
|
||||
nsec3_calculations);
|
||||
if(sec == sec_status_insecure) {
|
||||
verbose(VERB_ALGO, "Positive ANY wildcard response is "
|
||||
"insecure");
|
||||
chase_reply->security = sec_status_insecure;
|
||||
return;
|
||||
} else if(sec == sec_status_secure)
|
||||
} else if(sec == sec_status_secure) {
|
||||
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
|
||||
@ -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 kkey: the key entry, which is trusted, and which matches
|
||||
* 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
|
||||
validate_cname_response(struct module_env* env, struct val_env* ve,
|
||||
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;
|
||||
size_t wl;
|
||||
@ -1281,6 +1492,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
|
||||
int nsec3s_seen = 0;
|
||||
size_t i;
|
||||
struct ub_packed_rrset_key* s;
|
||||
*suspend = 0;
|
||||
|
||||
/* validate the ANSWER section - this will be the CNAME (+DNAME) */
|
||||
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
|
||||
* proven, and we have NSEC3 records, try to prove it using the NSEC3
|
||||
* records. */
|
||||
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
|
||||
enum sec_status sec = nsec3_prove_wildcard(env, ve,
|
||||
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen &&
|
||||
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->ns_numrrsets, qchase, kkey, wc);
|
||||
chase_reply->ns_numrrsets, qchase, kkey, wc,
|
||||
&vq->nsec3_cache_table, nsec3_calculations);
|
||||
if(sec == sec_status_insecure) {
|
||||
verbose(VERB_ALGO, "wildcard CNAME response is "
|
||||
"insecure");
|
||||
chase_reply->security = sec_status_insecure;
|
||||
return;
|
||||
} else if(sec == sec_status_secure)
|
||||
} else if(sec == sec_status_secure) {
|
||||
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
|
||||
@ -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 kkey: the key entry, which is trusted, and which matches
|
||||
* 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
|
||||
validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
|
||||
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.*/
|
||||
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 */
|
||||
int ce_labs = 0;
|
||||
int prev_ce_labs = 0;
|
||||
*suspend = 0;
|
||||
|
||||
/* the AUTHORITY section */
|
||||
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);
|
||||
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;
|
||||
enum sec_status sec = nsec3_prove_nxornodata(env, ve,
|
||||
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) {
|
||||
verbose(VERB_ALGO, "CNAMEchain to noanswer response "
|
||||
"is insecure");
|
||||
@ -1483,6 +1710,9 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
|
||||
if(nodata)
|
||||
nodata_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. */
|
||||
/* only if cache not blacklisted, of course */
|
||||
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,
|
||||
target_key_len, vq->qchase.qclass, qstate->region,
|
||||
vq->key_entry->name)) ) {
|
||||
verbose(VERB_ALGO, "Process cached DS response");
|
||||
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 */
|
||||
}
|
||||
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)
|
||||
{
|
||||
enum val_classification subtype;
|
||||
int rcode;
|
||||
int rcode, suspend, nsec3_calculations = 0;
|
||||
|
||||
if(!vq->key_entry) {
|
||||
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;
|
||||
* answer and authority must be valid, additional is only checked. */
|
||||
if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase,
|
||||
vq->chase_reply, vq->key_entry)) {
|
||||
if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase,
|
||||
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
|
||||
* with EDNS4k) to 512 by removing RRSIG from auth section
|
||||
* for positive replies*/
|
||||
@ -1962,7 +2222,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
case VAL_CLASS_POSITIVE:
|
||||
verbose(VERB_ALGO, "Validating a positive response");
|
||||
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",
|
||||
sec_status_to_string(
|
||||
vq->chase_reply->security));
|
||||
@ -1971,7 +2238,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
case VAL_CLASS_NODATA:
|
||||
verbose(VERB_ALGO, "Validating a nodata response");
|
||||
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",
|
||||
sec_status_to_string(
|
||||
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);
|
||||
verbose(VERB_ALGO, "Validating a nxdomain response");
|
||||
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",
|
||||
sec_status_to_string(
|
||||
vq->chase_reply->security));
|
||||
@ -1992,7 +2273,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
case VAL_CLASS_CNAME:
|
||||
verbose(VERB_ALGO, "Validating a cname response");
|
||||
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",
|
||||
sec_status_to_string(
|
||||
vq->chase_reply->security));
|
||||
@ -2002,7 +2290,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
verbose(VERB_ALGO, "Validating a cname noanswer "
|
||||
"response");
|
||||
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",
|
||||
sec_status_to_string(
|
||||
vq->chase_reply->security));
|
||||
@ -2019,8 +2314,15 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
case VAL_CLASS_ANY:
|
||||
verbose(VERB_ALGO, "Validating a positive ANY "
|
||||
"response");
|
||||
validate_any_response(qstate->env, ve, &vq->qchase,
|
||||
vq->chase_reply, vq->key_entry);
|
||||
validate_any_response(qstate->env, ve, &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_any): %s",
|
||||
sec_status_to_string(
|
||||
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) {
|
||||
/* see if we can try again to fetch data */
|
||||
if(vq->restart_count < ve->max_restart) {
|
||||
int restart_count = vq->restart_count+1;
|
||||
verbose(VERB_ALGO, "validation failed, "
|
||||
"blacklist and retry to fetch data");
|
||||
val_blacklist(&qstate->blacklist, qstate->region,
|
||||
qstate->reply_origin, 0);
|
||||
qstate->reply_origin = NULL;
|
||||
qstate->errinf = NULL;
|
||||
memset(vq, 0, sizeof(*vq));
|
||||
vq->restart_count = restart_count;
|
||||
vq->state = VAL_INIT_STATE;
|
||||
val_restart(vq);
|
||||
verbose(VERB_ALGO, "pass back to next module");
|
||||
qstate->ext_state[id] = module_restart_next;
|
||||
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
|
||||
* validated. It returns ke=NULL if the DS response indicated that the
|
||||
* 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
|
||||
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;
|
||||
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
|
||||
enum val_classification subtype;
|
||||
int verified;
|
||||
if(rcode != LDNS_RCODE_NOERROR) {
|
||||
char rc[16];
|
||||
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
|
||||
* bogus, then we are done. */
|
||||
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) {
|
||||
verbose(VERB_DETAIL, "DS rrset in DS response did "
|
||||
"not verify");
|
||||
@ -2524,7 +2827,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
ub_packed_rrset_ttl(ds),
|
||||
LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL,
|
||||
*qstate->env->now);
|
||||
return (*ke) != NULL;
|
||||
return (*ke) == NULL;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
|
||||
NULL, LDNS_EDE_NONE, NULL, *qstate->env->now);
|
||||
return (*ke) != NULL;
|
||||
return (*ke) == NULL;
|
||||
} else if(subtype == VAL_CLASS_NODATA ||
|
||||
subtype == VAL_CLASS_NAMEERROR) {
|
||||
/* 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,
|
||||
LDNS_EDE_NONE, NULL,
|
||||
*qstate->env->now);
|
||||
return (*ke) != NULL;
|
||||
return (*ke) == NULL;
|
||||
case sec_status_insecure:
|
||||
verbose(VERB_DETAIL, "NSEC RRset for the "
|
||||
"referral proved not a delegation point");
|
||||
*ke = NULL;
|
||||
return 1;
|
||||
return 0;
|
||||
case sec_status_bogus:
|
||||
verbose(VERB_DETAIL, "NSEC RRset for the "
|
||||
"referral did not prove no DS.");
|
||||
@ -2583,10 +2886,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
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,
|
||||
msg->rep->rrsets + msg->rep->an_numrrsets,
|
||||
msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason,
|
||||
&reason_bogus, qstate);
|
||||
&reason_bogus, qstate, &vq->nsec3_cache_table);
|
||||
switch(sec) {
|
||||
case sec_status_insecure:
|
||||
/* 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,
|
||||
LDNS_EDE_NONE, NULL,
|
||||
*qstate->env->now);
|
||||
return (*ke) != NULL;
|
||||
return (*ke) == NULL;
|
||||
case sec_status_indeterminate:
|
||||
verbose(VERB_DETAIL, "NSEC3s for the "
|
||||
"referral proved no delegation");
|
||||
*ke = NULL;
|
||||
return 1;
|
||||
return 0;
|
||||
case sec_status_bogus:
|
||||
verbose(VERB_DETAIL, "NSEC3s for the "
|
||||
"referral did not prove no DS.");
|
||||
errinf_ede(qstate, reason, reason_bogus);
|
||||
goto return_bogus;
|
||||
case sec_status_unchecked:
|
||||
return 2;
|
||||
default:
|
||||
/* NSEC3 proof did not work */
|
||||
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,
|
||||
vq->key_entry, &reason, &reason_bogus,
|
||||
LDNS_SECTION_ANSWER, qstate);
|
||||
LDNS_SECTION_ANSWER, qstate, &verified);
|
||||
if(sec == sec_status_secure) {
|
||||
verbose(VERB_ALGO, "CNAME validated, "
|
||||
"proof that DS does not exist");
|
||||
/* and that it is not a referral point */
|
||||
*ke = NULL;
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
errinf(qstate, "CNAME in DS response was not secure.");
|
||||
errinf_ede(qstate, reason, reason_bogus);
|
||||
@ -2682,7 +2993,7 @@ return_bogus:
|
||||
*ke = key_entry_create_bad(qstate->region, qinfo->qname,
|
||||
qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL,
|
||||
reason_bogus, reason, *qstate->env->now);
|
||||
return (*ke) != NULL;
|
||||
return (*ke) == NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2703,17 +3014,31 @@ return_bogus:
|
||||
static void
|
||||
process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
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 key_entry_key* dske = NULL;
|
||||
uint8_t* olds = vq->empty_DS_name;
|
||||
int ret;
|
||||
*suspend = 0;
|
||||
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");
|
||||
vq->key_entry = NULL; /* make it error */
|
||||
vq->state = VAL_VALIDATE_STATE;
|
||||
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) {
|
||||
vq->empty_DS_name = regional_alloc_init(qstate->region,
|
||||
@ -2965,9 +3290,26 @@ val_inform_super(struct module_qstate* qstate, int id,
|
||||
return;
|
||||
}
|
||||
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) {
|
||||
int suspend;
|
||||
process_ds_response(super, vq, id, qstate->return_rcode,
|
||||
qstate->return_msg, &qstate->qinfo,
|
||||
qstate->reply_origin);
|
||||
qstate->return_msg, &qstate->qinfo,
|
||||
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;
|
||||
} else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) {
|
||||
process_dnskey_response(super, vq, id, qstate->return_rcode,
|
||||
@ -2981,8 +3323,15 @@ val_inform_super(struct module_qstate* qstate, int id,
|
||||
void
|
||||
val_clear(struct module_qstate* qstate, int id)
|
||||
{
|
||||
struct val_qstate* vq;
|
||||
if(!qstate)
|
||||
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 */
|
||||
qstate->minfo[id] = NULL;
|
||||
}
|
||||
|
@ -45,11 +45,13 @@
|
||||
#include "util/module.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "validator/val_utils.h"
|
||||
#include "validator/val_nsec3.h"
|
||||
struct val_anchors;
|
||||
struct key_cache;
|
||||
struct key_entry_key;
|
||||
struct val_neg_cache;
|
||||
struct config_strlist;
|
||||
struct comm_timer;
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
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);
|
||||
|
||||
/** Timer callback for msg signatures continue timer */
|
||||
void validate_suspend_timer_cb(void* arg);
|
||||
|
||||
#endif /* VALIDATOR_VALIDATOR_H */
|
||||
|
@ -793,7 +793,7 @@
|
||||
#define PACKAGE_NAME "unbound"
|
||||
|
||||
/* 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 PACKAGE_TARNAME "unbound"
|
||||
@ -802,7 +802,7 @@
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "1.19.0"
|
||||
#define PACKAGE_VERSION "1.19.1"
|
||||
|
||||
/* default pidfile location */
|
||||
#define PIDFILE "/var/unbound/unbound.pid"
|
||||
|
Loading…
Reference in New Issue
Block a user