From: sthen Date: Tue, 7 Jun 2022 15:42:53 +0000 (+0000) Subject: merge unbound 1.16.0 X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=0bdb4f62466af851f70cdfcf6fddbef8ccf761ae;p=openbsd merge unbound 1.16.0 --- diff --git a/usr.sbin/unbound/Makefile.in b/usr.sbin/unbound/Makefile.in index c3b113a7e5a..464d20d06f7 100644 --- a/usr.sbin/unbound/Makefile.in +++ b/usr.sbin/unbound/Makefile.in @@ -57,7 +57,7 @@ LEX=@LEX@ STRIP=@STRIP@ CC=@CC@ CPPFLAGS=-I. @CPPFLAGS@ -PYTHON_CPPFLAGS=-I. @PYTHON_CPPFLAGS@ +PYTHON_CPPFLAGS=-I. -I$(srcdir) @PYTHON_CPPFLAGS@ CFLAGS=-DSRCDIR=$(srcdir) @CFLAGS@ LDFLAGS=@LDFLAGS@ LIBS=@LIBS@ @@ -344,7 +344,18 @@ longcheck: longtest test: unittest$(EXEEXT) testbound$(EXEEXT) ./unittest$(EXEEXT) ./testbound$(EXEEXT) -s - for x in $(srcdir)/testdata/*.rpl; do printf "%s" "$$x "; if ./testbound$(EXEEXT) -p $$x >/dev/null 2>&1; then echo OK; else echo failed; exit 1; fi done + for x in $(srcdir)/testdata/*.rpl; do \ + printf "%s" "$$x "; \ + if ./testbound$(EXEEXT) -p $$x >/dev/null 2>&1; then \ + echo OK; \ + else \ + echo failed; \ + ./testbound$(EXEEXT) -p $$x -o -vvvvv; \ + printf "%s" "$$x "; \ + echo failed; \ + exit 1; \ + fi; \ + done @echo test OK longtest: tests @@ -556,7 +567,7 @@ pythonmod-install: pyunbound-install: $(INSTALL) -m 755 -d $(DESTDIR)$(PYTHON_SITE_PKG) - $(INSTALL) -c -m 644 $(srcdir)/libunbound/python/unbound.py $(DESTDIR)$(PYTHON_SITE_PKG)/unbound.py + $(INSTALL) -c -m 644 libunbound/python/unbound.py $(DESTDIR)$(PYTHON_SITE_PKG)/unbound.py $(LIBTOOL) --mode=install cp _unbound.la $(DESTDIR)$(PYTHON_SITE_PKG) $(LIBTOOL) --mode=finish $(DESTDIR)$(PYTHON_SITE_PKG) @@ -584,6 +595,8 @@ install-lib: lib $(UNBOUND_EVENT_INSTALL) echo ".so man3/libunbound.3" > $(DESTDIR)$(mandir)/man3/$$mpage.3 ; \ done $(LIBTOOL) --mode=install cp unbound.h $(DESTDIR)$(includedir)/unbound.h + $(INSTALL) -m 755 -d $(DESTDIR)$(libdir)/pkgconfig + $(INSTALL) -m 644 contrib/libunbound.pc $(DESTDIR)$(libdir)/pkgconfig $(LIBTOOL) --mode=install cp libunbound.la $(DESTDIR)$(libdir) $(LIBTOOL) --mode=finish $(DESTDIR)$(libdir) @@ -593,8 +606,6 @@ install-all: all $(PYTHONMOD_INSTALL) $(PYUNBOUND_INSTALL) $(UNBOUND_EVENT_INSTA $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man8 $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man5 $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man1 - $(INSTALL) -m 755 -d $(DESTDIR)$(libdir)/pkgconfig - $(INSTALL) -m 644 contrib/libunbound.pc $(DESTDIR)$(libdir)/pkgconfig $(LIBTOOL) --mode=install cp -f unbound$(EXEEXT) $(DESTDIR)$(sbindir)/unbound$(EXEEXT) $(LIBTOOL) --mode=install cp -f unbound-checkconf$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-checkconf$(EXEEXT) $(LIBTOOL) --mode=install cp -f unbound-control$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-control$(EXEEXT) @@ -1249,7 +1260,7 @@ cachedump.lo cachedump.o: $(srcdir)/daemon/cachedump.c config.h $(srcdir)/daemon $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h $(srcdir)/iterator/iterator.h \ $(srcdir)/services/outbound_list.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iter_utils.h \ $(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h + $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h $(srcdir)/util/config_file.h $(srcdir)/services/outside_network.h daemon.lo daemon.o: $(srcdir)/daemon/daemon.c config.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h \ $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \ $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h \ diff --git a/usr.sbin/unbound/acx_python.m4 b/usr.sbin/unbound/acx_python.m4 index 767db5b6594..16c0c6fd943 100644 --- a/usr.sbin/unbound/acx_python.m4 +++ b/usr.sbin/unbound/acx_python.m4 @@ -18,27 +18,45 @@ AC_DEFUN([AC_PYTHON_DEVEL],[ print(sys.version.split()[[0]])"` fi - # - # Check if you have distutils, else fail - # - AC_MSG_CHECKING([for the distutils Python package]) - if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then + # Check if you have sysconfig + AC_MSG_CHECKING([for the sysconfig Python module]) + if ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`; then AC_MSG_RESULT([yes]) - else + sysconfig_module="sysconfig" + # if yes, use sysconfig, because distutils is deprecated. + else AC_MSG_RESULT([no]) - AC_MSG_ERROR([cannot import Python module "distutils". -Please check your Python installation. The error was: -$ac_distutils_result]) - PYTHON_VERSION="" - fi + # if no, try to use distutils + + # + # Check if you have distutils, else fail + # + AC_MSG_CHECKING([for the distutils Python package]) + if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot import Python module "distutils". + Please check your Python installation. The error was: + $ac_distutils_result]) + PYTHON_VERSION="" + fi + + sysconfig_module="distutils.sysconfig" + fi # # Check for Python include path # AC_MSG_CHECKING([for Python include path]) if test -z "$PYTHON_CPPFLAGS"; then - python_path=`$PYTHON -c "import distutils.sysconfig; \ - print(distutils.sysconfig.get_python_inc());"` + if test "$sysconfig_module" = "sysconfig"; then + python_path=`$PYTHON -c 'import sysconfig; \ + print(sysconfig.get_path("include"));'` + else + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print(distutils.sysconfig.get_python_inc());"` + fi if test -n "${python_path}"; then python_path="-I$python_path" fi @@ -52,14 +70,14 @@ $ac_distutils_result]) # AC_MSG_CHECKING([for Python library path]) if test -z "$PYTHON_LDFLAGS"; then - PYTHON_LDFLAGS=`$PYTHON -c "from distutils.sysconfig import *; \ + PYTHON_LDFLAGS=`$PYTHON -c "from $sysconfig_module import *; \ print('-L'+get_config_var('LIBDIR')+' -L'+get_config_var('LIBDEST')+' '+get_config_var('BLDLIBRARY'));"` fi AC_MSG_RESULT([$PYTHON_LDFLAGS]) AC_SUBST([PYTHON_LDFLAGS]) if test -z "$PYTHON_LIBDIR"; then - PYTHON_LIBDIR=`$PYTHON -c "from distutils.sysconfig import *; \ + PYTHON_LIBDIR=`$PYTHON -c "from $sysconfig_module import *; \ print(get_config_var('LIBDIR'));"` fi @@ -68,8 +86,13 @@ $ac_distutils_result]) # AC_MSG_CHECKING([for Python site-packages path]) if test -z "$PYTHON_SITE_PKG"; then - PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ - print(distutils.sysconfig.get_python_lib(1,0));"` + if test "$sysconfig_module" = "sysconfig"; then + PYTHON_SITE_PKG=`$PYTHON -c 'import sysconfig; \ + print(sysconfig.get_path("platlib"));'` + else + PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ + print(distutils.sysconfig.get_python_lib(1,0));"` + fi fi AC_MSG_RESULT([$PYTHON_SITE_PKG]) AC_SUBST([PYTHON_SITE_PKG]) diff --git a/usr.sbin/unbound/config.guess b/usr.sbin/unbound/config.guess index 7f76b6228f7..1817bdce90d 100644 --- a/usr.sbin/unbound/config.guess +++ b/usr.sbin/unbound/config.guess @@ -4,7 +4,7 @@ # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2022-01-09' +timestamp='2022-05-25' # 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 @@ -1151,16 +1151,27 @@ EOF ;; x86_64:Linux:*:*) set_cc_for_build + CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_X32 >/dev/null - then - LIBCABI=${LIBC}x32 - fi + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac fi - GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI + GUESS=$CPU-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC @@ -1367,8 +1378,11 @@ EOF BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; - x86_64:Haiku:*:*) - GUESS=x86_64-unknown-haiku + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE diff --git a/usr.sbin/unbound/config.h.in b/usr.sbin/unbound/config.h.in index 8edb691cec4..ef1dd2c5f8e 100644 --- a/usr.sbin/unbound/config.h.in +++ b/usr.sbin/unbound/config.h.in @@ -974,6 +974,10 @@ /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE +/* defined to use gcc ansi snprintf and sscanf that understands %lld when + compiled for windows. */ +#undef __USE_MINGW_ANSI_STDIO + /* Define to empty if `const' does not conform to ANSI C. */ #undef const @@ -1153,7 +1157,7 @@ #include #endif -#ifndef USE_WINSOCK +#if !defined(USE_WINSOCK) || !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN) || defined(__USE_MINGW_ANSI_STDIO) #define ARG_LL "%ll" #else #define ARG_LL "%I64" diff --git a/usr.sbin/unbound/configure b/usr.sbin/unbound/configure index 350cd5ac9aa..2a8480745f9 100644 --- a/usr.sbin/unbound/configure +++ b/usr.sbin/unbound/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for unbound 1.15.0. +# Generated by GNU Autoconf 2.71 for unbound 1.16.0. # # Report bugs to . # @@ -622,8 +622,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.15.0' -PACKAGE_STRING='unbound 1.15.0' +PACKAGE_VERSION='1.16.0' +PACKAGE_STRING='unbound 1.16.0' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues' PACKAGE_URL='' @@ -1503,7 +1503,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.15.0 to adapt to many kinds of systems. +\`configure' configures unbound 1.16.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1569,7 +1569,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.15.0:";; + short | recursive ) echo "Configuration of unbound 1.16.0:";; esac cat <<\_ACEOF @@ -1812,7 +1812,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.15.0 +unbound configure 1.16.0 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2469,7 +2469,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.15.0, which was +It was created by unbound $as_me 1.16.0, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3231,13 +3231,13 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu UNBOUND_VERSION_MAJOR=1 -UNBOUND_VERSION_MINOR=15 +UNBOUND_VERSION_MINOR=16 UNBOUND_VERSION_MICRO=0 LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=15 +LIBUNBOUND_REVISION=16 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -3320,6 +3320,7 @@ LIBUNBOUND_AGE=1 # 1.13.2 had 9:13:1 # 1.14.0 had 9:14:1 # 1.15.0 had 9:15:1 +# 1.16.0 had 9:16:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -18770,22 +18771,38 @@ fi print(sys.version.split()[0])"` fi - # - # Check if you have distutils, else fail - # - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 -printf %s "checking for the distutils Python package... " >&6; } - if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then + # Check if you have sysconfig + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the sysconfig Python module" >&5 +printf %s "checking for the sysconfig Python module... " >&6; } + if ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - else + sysconfig_module="sysconfig" + # if yes, use sysconfig, because distutils is deprecated. + else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - as_fn_error $? "cannot import Python module \"distutils\". -Please check your Python installation. The error was: -$ac_distutils_result" "$LINENO" 5 - PYTHON_VERSION="" - fi + # if no, try to use distutils + + # + # Check if you have distutils, else fail + # + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 +printf %s "checking for the distutils Python package... " >&6; } + if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + as_fn_error $? "cannot import Python module \"distutils\". + Please check your Python installation. The error was: + $ac_distutils_result" "$LINENO" 5 + PYTHON_VERSION="" + fi + + sysconfig_module="distutils.sysconfig" + fi # # Check for Python include path @@ -18793,8 +18810,13 @@ $ac_distutils_result" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Python include path" >&5 printf %s "checking for Python include path... " >&6; } if test -z "$PYTHON_CPPFLAGS"; then - python_path=`$PYTHON -c "import distutils.sysconfig; \ - print(distutils.sysconfig.get_python_inc());"` + if test "$sysconfig_module" = "sysconfig"; then + python_path=`$PYTHON -c 'import sysconfig; \ + print(sysconfig.get_path("include"));'` + else + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print(distutils.sysconfig.get_python_inc());"` + fi if test -n "${python_path}"; then python_path="-I$python_path" fi @@ -18810,7 +18832,7 @@ printf "%s\n" "$PYTHON_CPPFLAGS" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Python library path" >&5 printf %s "checking for Python library path... " >&6; } if test -z "$PYTHON_LDFLAGS"; then - PYTHON_LDFLAGS=`$PYTHON -c "from distutils.sysconfig import *; \ + PYTHON_LDFLAGS=`$PYTHON -c "from $sysconfig_module import *; \ print('-L'+get_config_var('LIBDIR')+' -L'+get_config_var('LIBDEST')+' '+get_config_var('BLDLIBRARY'));"` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PYTHON_LDFLAGS" >&5 @@ -18818,7 +18840,7 @@ printf "%s\n" "$PYTHON_LDFLAGS" >&6; } if test -z "$PYTHON_LIBDIR"; then - PYTHON_LIBDIR=`$PYTHON -c "from distutils.sysconfig import *; \ + PYTHON_LIBDIR=`$PYTHON -c "from $sysconfig_module import *; \ print(get_config_var('LIBDIR'));"` fi @@ -18828,8 +18850,13 @@ printf "%s\n" "$PYTHON_LDFLAGS" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Python site-packages path" >&5 printf %s "checking for Python site-packages path... " >&6; } if test -z "$PYTHON_SITE_PKG"; then - PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ - print(distutils.sysconfig.get_python_lib(1,0));"` + if test "$sysconfig_module" = "sysconfig"; then + PYTHON_SITE_PKG=`$PYTHON -c 'import sysconfig; \ + print(sysconfig.get_path("platlib"));'` + else + PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ + print(distutils.sysconfig.get_python_lib(1,0));"` + fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PYTHON_SITE_PKG" >&5 printf "%s\n" "$PYTHON_SITE_PKG" >&6; } @@ -21761,6 +21788,9 @@ fi WIN_CHECKCONF_OBJ_LINK="rsrc_unbound_checkconf.o" + +printf "%s\n" "#define __USE_MINGW_ANSI_STDIO 1" >>confdefs.h + fi if test $ac_cv_func_getaddrinfo = no; then case " $LIBOBJS " in @@ -23447,10 +23477,16 @@ printf %s "checking for libmnl... " >&6; } withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" fi for dir in $withval ; do - if test -f "$dir/include/libmnl/libmnl.h"; then + if test -f "$dir/include/libmnl/libmnl.h" -o -f "$dir/include/libmnl/libmnl/libmnl.h"; then found_libmnl="yes" - if test "$dir" != "/usr"; then - CPPFLAGS="$CPPFLAGS -I$dir/include" + extralibmnl="" + if test -f "$dir/include/libmnl/libmnl/libmnl.h"; then + extralibmnl="/libmnl" + fi + if test "$dir" != "/usr" -o -n "$extralibmnl"; then + CPPFLAGS="$CPPFLAGS -I$dir/include$extralibmnl" + fi + if test "$dir" != "/usr"; then LDFLAGS="$LDFLAGS -L$dir/lib" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5 @@ -23656,7 +23692,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h -version=1.15.0 +version=1.16.0 date=`date +'%b %e, %Y'` @@ -24168,7 +24204,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.15.0, which was +This file was extended by unbound $as_me 1.16.0, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -24236,7 +24272,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -unbound config.status 1.15.0 +unbound config.status 1.16.0 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/usr.sbin/unbound/configure.ac b/usr.sbin/unbound/configure.ac index 80f3f5cc29e..a54f2c01f1e 100644 --- a/usr.sbin/unbound/configure.ac +++ b/usr.sbin/unbound/configure.ac @@ -10,7 +10,7 @@ sinclude(dnscrypt/dnscrypt.m4) # must be numbers. ac_defun because of later processing m4_define([VERSION_MAJOR],[1]) -m4_define([VERSION_MINOR],[15]) +m4_define([VERSION_MINOR],[16]) m4_define([VERSION_MICRO],[0]) 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]) @@ -18,7 +18,7 @@ AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR]) AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO]) LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=15 +LIBUNBOUND_REVISION=16 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -101,6 +101,7 @@ LIBUNBOUND_AGE=1 # 1.13.2 had 9:13:1 # 1.14.0 had 9:14:1 # 1.15.0 had 9:15:1 +# 1.16.0 had 9:16:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -1554,6 +1555,7 @@ if test "$USE_WINSOCK" = 1; then AC_SUBST(WIN_CONTROL_OBJ_LINK) WIN_CHECKCONF_OBJ_LINK="rsrc_unbound_checkconf.o" AC_SUBST(WIN_CHECKCONF_OBJ_LINK) + AC_DEFINE(__USE_MINGW_ANSI_STDIO, 1, [defined to use gcc ansi snprintf and sscanf that understands %lld when compiled for windows.]) fi if test $ac_cv_func_getaddrinfo = no; then AC_LIBOBJ([fake-rfc2553]) @@ -1879,11 +1881,17 @@ case "$enable_ipset" in withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" fi for dir in $withval ; do - if test -f "$dir/include/libmnl/libmnl.h"; then + if test -f "$dir/include/libmnl/libmnl.h" -o -f "$dir/include/libmnl/libmnl/libmnl.h"; then found_libmnl="yes" dnl assume /usr is in default path. + extralibmnl="" + if test -f "$dir/include/libmnl/libmnl/libmnl.h"; then + extralibmnl="/libmnl" + fi + if test "$dir" != "/usr" -o -n "$extralibmnl"; then + CPPFLAGS="$CPPFLAGS -I$dir/include$extralibmnl" + fi if test "$dir" != "/usr"; then - CPPFLAGS="$CPPFLAGS -I$dir/include" LDFLAGS="$LDFLAGS -L$dir/lib" fi AC_MSG_RESULT(found in $dir) @@ -2061,7 +2069,7 @@ dnl includes #include #endif -#ifndef USE_WINSOCK +#if !defined(USE_WINSOCK) || !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN) || defined(__USE_MINGW_ANSI_STDIO) #define ARG_LL "%ll" #else #define ARG_LL "%I64" diff --git a/usr.sbin/unbound/daemon/acl_list.c b/usr.sbin/unbound/daemon/acl_list.c index 84324575e71..aecb3e0c643 100644 --- a/usr.sbin/unbound/daemon/acl_list.c +++ b/usr.sbin/unbound/daemon/acl_list.c @@ -487,3 +487,38 @@ acl_list_get_mem(struct acl_list* acl) if(!acl) return 0; return sizeof(*acl) + regional_get_mem(acl->region); } + +const char* acl_access_to_str(enum acl_access acl) +{ + switch(acl) { + case acl_deny: return "deny"; + case acl_refuse: return "refuse"; + case acl_deny_non_local: return "deny_non_local"; + case acl_refuse_non_local: return "refuse_non_local"; + case acl_allow: return "allow"; + case acl_allow_snoop: return "allow_snoop"; + case acl_allow_setrd: return "allow_setrd"; + default: break; + } + return "unknown"; +} + +void +log_acl_action(const char* action, struct sockaddr_storage* addr, + socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr) +{ + char a[128], n[128]; + uint16_t port; + addr_to_str(addr, addrlen, a, sizeof(a)); + port = ntohs(((struct sockaddr_in*)addr)->sin_port); + if(acladdr) { + addr_to_str(&acladdr->node.addr, acladdr->node.addrlen, + n, sizeof(n)); + verbose(VERB_ALGO, "%s query from %s port %d because of " + "%s/%d %s", action, a, (int)port, n, acladdr->node.net, + acl_access_to_str(acl)); + } else { + verbose(VERB_ALGO, "%s query from %s port %d", action, a, + (int)port); + } +} diff --git a/usr.sbin/unbound/daemon/acl_list.h b/usr.sbin/unbound/daemon/acl_list.h index 3a3b94bc587..c09e832a1de 100644 --- a/usr.sbin/unbound/daemon/acl_list.h +++ b/usr.sbin/unbound/daemon/acl_list.h @@ -154,4 +154,15 @@ acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr, */ size_t acl_list_get_mem(struct acl_list* acl); +/* + * Get string for acl access specification + * @param acl: access type value + * @return string + */ +const char* acl_access_to_str(enum acl_access acl); + +/* log acl and addr for action */ +void log_acl_action(const char* action, struct sockaddr_storage* addr, + socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr); + #endif /* DAEMON_ACL_LIST_H */ diff --git a/usr.sbin/unbound/daemon/cachedump.c b/usr.sbin/unbound/daemon/cachedump.c index b1ce53b596b..b929f909bab 100644 --- a/usr.sbin/unbound/daemon/cachedump.c +++ b/usr.sbin/unbound/daemon/cachedump.c @@ -47,10 +47,12 @@ #include "services/cache/rrset.h" #include "services/cache/dns.h" #include "services/cache/infra.h" +#include "services/outside_network.h" #include "util/data/msgreply.h" #include "util/regional.h" #include "util/net_help.h" #include "util/data/dname.h" +#include "util/config_file.h" #include "iterator/iterator.h" #include "iterator/iter_delegpt.h" #include "iterator/iter_utils.h" @@ -854,7 +856,9 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, "cache; goes to configured roots\n"); } /* go up? */ - if(iter_dp_is_useless(&qinfo, BIT_RD, dp)) { + if(iter_dp_is_useless(&qinfo, BIT_RD, dp, + (worker->env.cfg->do_ip4 && worker->back->num_ip4 != 0), + (worker->env.cfg->do_ip6 && worker->back->num_ip6 != 0))) { print_dp_main(ssl, dp, msg); print_dp_details(ssl, worker, dp); if(!ssl_printf(ssl, "cache delegation was " diff --git a/usr.sbin/unbound/daemon/worker.c b/usr.sbin/unbound/daemon/worker.c index 862affb24e9..bf8c5d6b676 100644 --- a/usr.sbin/unbound/daemon/worker.c +++ b/usr.sbin/unbound/daemon/worker.c @@ -98,7 +98,7 @@ /** ratelimit for error responses */ #define ERROR_RATELIMIT 100 /* qps */ -/** +/** * seconds to add to prefetch leeway. This is a TTL that expires old rrsets * earlier than they should in order to put the new update into the cache. * This additional value is to make sure that if not all TTLs are equal in @@ -484,6 +484,12 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, msg->rep, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) return 0; + /* TODO store the reason for the bogus reply in cache + * and implement in here instead of the hardcoded EDE */ + if (worker->env.cfg->ede) { + EDNS_OPT_LIST_APPEND_EDE(&edns->opt_list_out, + worker->scratchpad, LDNS_EDE_DNSSEC_BOGUS, ""); + } error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, &msg->qinfo, id, flags, edns); if(worker->stats.extended) { @@ -553,7 +559,7 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo, return 1; if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo, - alias_rrset, 0, worker->scratchpad, az)) + alias_rrset, 0, worker->scratchpad, az, NULL)) return 0; /* xxx_deny actions mean dropping the reply, unless the original reply @@ -654,6 +660,12 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) goto bail_out; + /* TODO store the reason for the bogus reply in cache + * and implement in here instead of the hardcoded EDE */ + if (worker->env.cfg->ede) { + EDNS_OPT_LIST_APPEND_EDE(&edns->opt_list_out, + worker->scratchpad, LDNS_EDE_DNSSEC_BOGUS, ""); + } error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, id, flags, edns); rrset_array_unlock_touch(worker->env.rrset_cache, @@ -716,15 +728,25 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, if(!*partial_repp) goto bail_out; } - } else if(!reply_info_answer_encode(qinfo, encode_rep, id, flags, - repinfo->c->buffer, timenow, 1, worker->scratchpad, - udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer)) { - if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL, - LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, - worker->env.now_tv)) - edns->opt_list_inplace_cb_out = NULL; - error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, - qinfo, id, flags, edns); + } else { + /* We don't check the global ede as this is a warning, not + * an error */ + if (*is_expired_answer == 1 && + worker->env.cfg->ede_serve_expired && worker->env.cfg->ede) { + EDNS_OPT_LIST_APPEND_EDE(&edns->opt_list_out, + worker->scratchpad, LDNS_EDE_STALE_ANSWER, ""); + } + if(!reply_info_answer_encode(qinfo, encode_rep, id, flags, + repinfo->c->buffer, timenow, 1, worker->scratchpad, + udpsize, edns, (int)(edns->bits & EDNS_DO), + *is_secure_answer)) { + if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, + NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo, + worker->scratchpad, worker->env.now_tv)) + edns->opt_list_inplace_cb_out = NULL; + error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, + qinfo, id, flags, edns); + } } /* cannot send the reply right now, because blocking network syscall * is bad while holding locks. */ @@ -741,10 +763,12 @@ bail_out: /** Reply to client and perform prefetch to keep cache up to date. */ static void -reply_and_prefetch(struct worker* worker, struct query_info* qinfo, - uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply) +reply_and_prefetch(struct worker* worker, struct query_info* qinfo, + uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply, + int rpz_passthru, struct edns_option* opt_list) { - /* first send answer to client to keep its latency + (void)opt_list; + /* first send answer to client to keep its latency * as small as a cachereply */ if(!noreply) { if(repinfo->c->tcp_req_info) { @@ -755,13 +779,23 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo, comm_point_send_reply(repinfo); } server_stats_prefetch(&worker->stats, worker); - +#ifdef CLIENT_SUBNET + /* Check if the subnet module is enabled. In that case pass over the + * comm_reply information for ECS generation later. The mesh states are + * unique when subnet is enabled. */ + if(modstack_find(&worker->env.mesh->mods, "subnetcache") != -1 + && worker->env.unique_mesh) { + mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + + PREFETCH_EXPIRY_ADD, rpz_passthru, repinfo, opt_list); + return; + } +#endif /* create the prefetch in the mesh as a normal lookup without * client addrs waiting, which has the cache blacklisted (to bypass * the cache and go to the network for the data). */ /* this (potentially) runs the mesh for the new query */ - mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + - PREFETCH_EXPIRY_ADD); + mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + + PREFETCH_EXPIRY_ADD, rpz_passthru, NULL, NULL); } /** @@ -1012,32 +1046,178 @@ answer_notify(struct worker* w, struct query_info* qinfo, static int deny_refuse(struct comm_point* c, enum acl_access acl, enum acl_access deny, enum acl_access refuse, - struct worker* worker, struct comm_reply* repinfo) + struct worker* worker, struct comm_reply* repinfo, + struct acl_addr* acladdr, int ede) { if(acl == deny) { + if(verbosity >= VERB_ALGO) { + log_acl_action("dropped", &repinfo->addr, + repinfo->addrlen, acl, acladdr); + log_buf(VERB_ALGO, "dropped", c->buffer); + } comm_point_drop_reply(repinfo); if(worker->stats.extended) worker->stats.unwanted_queries++; return 0; } else if(acl == refuse) { - log_addr(VERB_ALGO, "refused query from", - &repinfo->addr, repinfo->addrlen); - log_buf(VERB_ALGO, "refuse", c->buffer); + size_t opt_rr_mark; + + if(verbosity >= VERB_ALGO) { + log_acl_action("refused", &repinfo->addr, + repinfo->addrlen, acl, acladdr); + log_buf(VERB_ALGO, "refuse", c->buffer); + } + if(worker->stats.extended) worker->stats.unwanted_queries++; if(worker_check_request(c->buffer, worker) == -1) { comm_point_drop_reply(repinfo); return 0; /* discard this */ } - sldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE); - sldns_buffer_write_at(c->buffer, 4, - (uint8_t*)"\0\0\0\0\0\0\0\0", 8); + /* worker_check_request() above guarantees that the buffer contains at + * least a header and that qdcount == 1 + */ + log_assert(sldns_buffer_limit(c->buffer) >= LDNS_HEADER_SIZE + && LDNS_QDCOUNT(sldns_buffer_begin(c->buffer)) == 1); + + sldns_buffer_skip(c->buffer, LDNS_HEADER_SIZE); /* skip header */ + + /* check additional section is present and that we respond with EDEs */ + if(LDNS_ARCOUNT(sldns_buffer_begin(c->buffer)) != 1 + || !ede) { + LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_QR_SET(sldns_buffer_begin(c->buffer)); + LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), + LDNS_RCODE_REFUSED); + sldns_buffer_flip(c->buffer); + return 1; + } + + if (!query_dname_len(c->buffer)) { + LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_QR_SET(sldns_buffer_begin(c->buffer)); + LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), + LDNS_RCODE_FORMERR); + sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE); + sldns_buffer_flip(c->buffer); + return 1; + } + /* space available for query type and class? */ + if (sldns_buffer_remaining(c->buffer) < 2 * sizeof(uint16_t)) { + LDNS_QR_SET(sldns_buffer_begin(c->buffer)); + LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), + LDNS_RCODE_FORMERR); + LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE); + sldns_buffer_flip(c->buffer); + return 1; + } LDNS_QR_SET(sldns_buffer_begin(c->buffer)); LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), LDNS_RCODE_REFUSED); - sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE); + + sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qtype */ + + sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qclass */ + + /* The OPT RR to be returned should come directly after + * the query, so mark this spot. + */ + opt_rr_mark = sldns_buffer_position(c->buffer); + + /* Skip through the RR records */ + if(LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)) != 0 || + LDNS_NSCOUNT(sldns_buffer_begin(c->buffer)) != 0) { + if(!skip_pkt_rrs(c->buffer, + ((int)LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)))+ + ((int)LDNS_NSCOUNT(sldns_buffer_begin(c->buffer))))) { + LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), + LDNS_RCODE_FORMERR); + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_set_position(c->buffer, opt_rr_mark); + sldns_buffer_flip(c->buffer); + return 1; + } + } + /* Do we have a valid OPT RR here? If not return REFUSED (could be a valid TSIG or something so no FORMERR) */ + /* domain name must be the root of length 1. */ + if(sldns_buffer_remaining(c->buffer) < 1 || *sldns_buffer_current(c->buffer) != 0) { + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_set_position(c->buffer, opt_rr_mark); + sldns_buffer_flip(c->buffer); + return 1; + } else { + sldns_buffer_skip(c->buffer, 1); /* skip root label */ + } + if(sldns_buffer_remaining(c->buffer) < 2 || + sldns_buffer_read_u16(c->buffer) != LDNS_RR_TYPE_OPT) { + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_set_position(c->buffer, opt_rr_mark); + sldns_buffer_flip(c->buffer); + return 1; + } + /* Write OPT RR directly after the query, + * so without the (possibly skipped) Answer and NS RRs + */ + LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_clear(c->buffer); /* reset write limit */ + sldns_buffer_set_position(c->buffer, opt_rr_mark); + + /* Check if OPT record can be written + * 17 == root label (1) + RR type (2) + UDP Size (2) + * + Fields (4) + rdata len (2) + EDE Option code (2) + * + EDE Option length (2) + EDE info-code (2) + */ + if (sldns_buffer_available(c->buffer, 17) == 0) { + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0); + sldns_buffer_flip(c->buffer); + return 1; + } + + LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 1); + + /* root label */ + sldns_buffer_write_u8(c->buffer, 0); + sldns_buffer_write_u16(c->buffer, LDNS_RR_TYPE_OPT); + sldns_buffer_write_u16(c->buffer, EDNS_ADVERTISED_SIZE); + + /* write OPT Record TTL Field */ + sldns_buffer_write_u32(c->buffer, 0); + + /* write rdata len: EDE option + length + info-code */ + sldns_buffer_write_u16(c->buffer, 6); + + /* write OPTIONS; add EDE option code */ + sldns_buffer_write_u16(c->buffer, LDNS_EDNS_EDE); + + /* write single EDE option length (for just 1 info-code) */ + sldns_buffer_write_u16(c->buffer, 2); + + /* write single EDE info-code */ + sldns_buffer_write_u16(c->buffer, LDNS_EDE_PROHIBITED); + sldns_buffer_flip(c->buffer); + + verbose(VERB_ALGO, "attached EDE code: %d", LDNS_EDE_PROHIBITED); + return 1; + } return -1; @@ -1045,16 +1225,20 @@ deny_refuse(struct comm_point* c, enum acl_access acl, static int deny_refuse_all(struct comm_point* c, enum acl_access acl, - struct worker* worker, struct comm_reply* repinfo) + struct worker* worker, struct comm_reply* repinfo, + struct acl_addr* acladdr, int ede) { - return deny_refuse(c, acl, acl_deny, acl_refuse, worker, repinfo); + return deny_refuse(c, acl, acl_deny, acl_refuse, worker, repinfo, + acladdr, ede); } static int deny_refuse_non_local(struct comm_point* c, enum acl_access acl, - struct worker* worker, struct comm_reply* repinfo) + struct worker* worker, struct comm_reply* repinfo, + struct acl_addr* acladdr, int ede) { - return deny_refuse(c, acl, acl_deny_non_local, acl_refuse_non_local, worker, repinfo); + return deny_refuse(c, acl, acl_deny_non_local, acl_refuse_non_local, + worker, repinfo, acladdr, ede); } int @@ -1067,12 +1251,14 @@ worker_handle_request(struct comm_point* c, void* arg, int error, struct lruhash_entry* e; struct query_info qinfo; struct edns_data edns; + struct edns_option* original_edns_list = NULL; enum acl_access acl; struct acl_addr* acladdr; int rc = 0; int need_drop = 0; int is_expired_answer = 0; int is_secure_answer = 0; + int rpz_passthru = 0; /* We might have to chase a CNAME chain internally, in which case * we'll have up to two replies and combine them to build a complete * answer. These variables control this case. */ @@ -1145,7 +1331,9 @@ worker_handle_request(struct comm_point* c, void* arg, int error, acladdr = acl_addr_lookup(worker->daemon->acl, &repinfo->addr, repinfo->addrlen); acl = acl_get_control(acladdr); - if((ret=deny_refuse_all(c, acl, worker, repinfo)) != -1) + + if((ret=deny_refuse_all(c, acl, worker, repinfo, acladdr, + worker->env.cfg->ede)) != -1) { if(ret == 1) goto send_reply; @@ -1338,7 +1526,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error, if(worker->env.auth_zones && rpz_callback_from_worker_request(worker->env.auth_zones, &worker->env, &qinfo, &edns, c->buffer, worker->scratchpad, - repinfo, acladdr->taglist, acladdr->taglen, &worker->stats)) { + repinfo, acladdr->taglist, acladdr->taglen, &worker->stats, + &rpz_passthru)) { regional_free_all(worker->scratchpad); if(sldns_buffer_limit(c->buffer) == 0) { comm_point_drop_reply(repinfo); @@ -1364,7 +1553,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error, /* We've looked in our local zones. If the answer isn't there, we * might need to bail out based on ACLs now. */ - if((ret=deny_refuse_non_local(c, acl, worker, repinfo)) != -1) + if((ret=deny_refuse_non_local(c, acl, worker, repinfo, acladdr, + worker->env.cfg->ede)) != -1) { regional_free_all(worker->scratchpad); if(ret == 1) @@ -1383,12 +1573,17 @@ worker_handle_request(struct comm_point* c, void* arg, int error, * ACLs allow the snooping. */ if(!(LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) && acl != acl_allow_snoop ) { + if (worker->env.cfg->ede) { + EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out, + worker->scratchpad, LDNS_EDE_NOT_AUTHORITATIVE, ""); + } error_encode(c->buffer, LDNS_RCODE_REFUSED, &qinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), - sldns_buffer_read_u16_at(c->buffer, 2), NULL); + sldns_buffer_read_u16_at(c->buffer, 2), &edns); regional_free_all(worker->scratchpad); log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from", &repinfo->addr, repinfo->addrlen); + goto send_reply; } @@ -1429,6 +1624,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error, cinfo = &cinfo_tmp; } + /* Keep the original edns list around. The pointer could change if there is + * a cached answer (through the inplace callback function there). + * No need to actually copy the contents as they shouldn't change. + * Used while prefetching and subnet is enabled. */ + original_edns_list = edns.opt_list_in; lookup_cache: /* Lookup the cache. In case we chase an intermediate CNAME chain * this is a two-pass operation, and lookup_qinfo is different for @@ -1461,10 +1661,13 @@ lookup_cache: < *worker->env.now) leeway = 0; lock_rw_unlock(&e->lock); + reply_and_prefetch(worker, lookup_qinfo, sldns_buffer_read_u16_at(c->buffer, 2), repinfo, leeway, - (partial_rep || need_drop)); + (partial_rep || need_drop), + rpz_passthru, + original_edns_list); if(!partial_rep) { rc = 0; regional_free_all(worker->scratchpad); @@ -1501,6 +1704,7 @@ lookup_cache: verbose(VERB_ALGO, "answer from the cache failed"); lock_rw_unlock(&e->lock); } + if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) { if(answer_norec_from_cache(worker, &qinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), @@ -1527,7 +1731,8 @@ lookup_cache: /* grab a work request structure for this new request */ mesh_new_client(worker->env.mesh, &qinfo, cinfo, sldns_buffer_read_u16_at(c->buffer, 2), - &edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer)); + &edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), + rpz_passthru); regional_free_all(worker->scratchpad); worker_mem_report(worker, NULL); return 0; @@ -1591,6 +1796,9 @@ worker_sighandler(int sig, void* arg) case SIGHUP: comm_base_exit(worker->base); break; +#endif +#ifdef SIGBREAK + case SIGBREAK: #endif case SIGINT: worker->need_to_exit = 1; @@ -1709,6 +1917,9 @@ worker_init(struct worker* worker, struct config_file *cfg, if(do_sigs) { #ifdef SIGHUP ub_thread_sig_unblock(SIGHUP); +#endif +#ifdef SIGBREAK + ub_thread_sig_unblock(SIGBREAK); #endif ub_thread_sig_unblock(SIGINT); #ifdef SIGQUIT @@ -1726,6 +1937,9 @@ worker_init(struct worker* worker, struct config_file *cfg, || !comm_signal_bind(worker->comsig, SIGQUIT) #endif || !comm_signal_bind(worker->comsig, SIGTERM) +#ifdef SIGBREAK + || !comm_signal_bind(worker->comsig, SIGBREAK) +#endif || !comm_signal_bind(worker->comsig, SIGINT)) { log_err("could not create signal handlers"); worker_delete(worker); diff --git a/usr.sbin/unbound/doc/Changelog b/usr.sbin/unbound/doc/Changelog index 46265869d46..53a137164e4 100644 --- a/usr.sbin/unbound/doc/Changelog +++ b/usr.sbin/unbound/doc/Changelog @@ -1,8 +1,149 @@ 7 February 2022: Wouter - Fix that TCP interface does not use TLS when TLS is also configured. +27 May 2022: Wouter + - Fix #684: [FTBS] configure script error with libmnl on openSUSE 15.3 (and possibly other distributions) + - Version is set to 1.16.0 for release. Release tag 1.16.0rc1. + +20 May 2022: Wouter + - Fix to silence test for ede error output to the console from the + test setup script. + - Fix ede test to not use default pidfile, and use local interface. + - Fix some lint type warnings. + +18 May 2022: George + - Fix typos in config_set_option for the 'num-threads' and + 'ede-serve-expired' options. + +15 May 2022: George + - Fix #678: [FR] modify behaviour of unbound-control rpz_enable zone, + by updating unbound-control's documentation. + +12 May 2022: George + - Fix #417: prefetch and ECS causing cache corruption when used + together. + +12 May 2022: Wouter + - Merge #677: Allow using system certificates not only on Windows, + from pemensik. + - For #677: Added tls-system-cert to config parser and documentation. + +11 May 2022: Wouter + - Fix #673: DNS over TLS: error: SSL_handshake syscall: No route to + host. + +10 May 2022: George + - Fix Python build in non-source directory; based on patch by + Michael Tokarev. + +6 May 2022: Tom + - Merge PR #604: Add basic support for EDE (RFC8914). + +28 April 2022: Wouter + - Fix #670: SERVFAIL problems with unbound 1.15.0 running on + OpenBSD 7.1. + +8 April 2022: Wouter + - Fix zonemd check to allow unsupported algorithms to load. + If there are only unsupported algorithms, or unsupported schemes, + and no failed or successful other ZONEMD records, or malformed + or bad ZONEMD records, the unsupported records allow the zone load. + - Fix zonemd unsupported algo check. + - Fix zonemd unsupported algo check reason to not copy to next record, + and check for success for debug printout. + - Fix zonemd unsupported algo check to print unsupported reason before + zeroing it. + - Fix zonemd unsupported algo check to set reason to NULL before the + check routine, but after malformed checks, to get the correct NULL + output when the digest matches. + +25 March 2022: Wouter + - Fix spelling error in comment in sldns_str2wire_svcparam_key_lookup. + +23 March 2022: Wouter + - Fix #651: [FR] Better logging for refused queries. + +18 March 2022: George + - Merge PR #648 from eaglegai: fix -q doesn't work when use with + 'unbound-control stats_shm'. + +17 March 2022: Wouter + - Fix to describe auth-zone and other configuration at the local-zone + configuration option, to allow for more broadly view of the options. + +16 March 2022: Wouter + - Fix to ensure uniform handling of spaces and tabs when parsing RRs. + +9 March 2022: Wouter + - Merge #644: Make `install-lib` make target install the pkg-config + file. + +7 March 2022: Wouter + - Fix configure for python to use sysutils, because distutils is + deprecated. It uses sysutils when available, distutils otherwise. + +3 March 2022: Wouter + - Fix #637: Integer Overflow in sldns_str2period function. + - Fix for #637: fix integer overflow checks in sldns_str2period. + +2 March 2022: George + - Merge PR #632 from scottrw93: Match cnames in ipset. + - Various fixes for #632: variable initialisation, convert the qinfo + to str once, accept trailing dot in the local-zone ipset option. + +2 March 2022: Wouter + - Fix compile warnings for printf ll format on mingw compile. + +1 March 2022: Wouter + - Fix pythonmod for change in iter_dp_is_useless function prototype. + +28 February 2022: George + - Fix #630: Unify the RPZ log messages. + - Merge #623 from rex4539: Fix typos. + +28 February 2022: Wouter + - Fix #633: Document unix domain socket support for unbound-control. + - Fix for #633: updated fix with new text. + - Fix edns client subnet to add the option based on the option list, + so that it is not state dependent, after the state fix of #605 for + double EDNS options. + - Fix for edns client subnet option add fix in removal code, from review. + +25 February 2022: Wouter + - Fix to detect that no IPv6 support means that IPv6 addresses are + useless for delegation point lookups. + - update Makefile dependencies. + - Fix check interface existence for support detection in remote lookup. + +18 February 2022: Wouter + - Fix that address not available is squelched from the logs for + udp connect failures. It is visible on verbosity 4 and more. + - Merge #631 from mollyim: Replace OpenSSL's ERR_PACK with + ERR_GET_REASON. + +16 February 2022: Wouter + - Fix for #628: fix rpz-passthru for qname trigger by localzone type. + +15 February 2022: Wouter + - Fix #628: A rpz-passthru action is not ending RPZ zone processing. + +11 February 2022: Wouter + - Fix #624: Unable to stop Unbound in Windows console (does not + respond to CTRL+C command). + - Fix #618: enabling interface-automatic disables DNS-over-TLS. + Adds the option to list interface-automatic-ports. + - Remove debug info from #618 fix. + +7 February 2022: Wouter + - Fix that TCP interface does not use TLS when TLS is also configured. + +4 February 2022: Wouter + - Fix #412: cache invalidation issue with CNAME+A. + 3 February 2022: Wouter - Fix for #611: Integer overflow in sldns_wire2str_pkt_scan. + - Tag for 1.15.0rc1 created. That became 1.15.0 on 10 feb 2022. + The repository continues with version 1.15.1. 2 February 2022: George - Merge PR #532 from Shchelk: Fix: buffer overflow bug. diff --git a/usr.sbin/unbound/doc/README b/usr.sbin/unbound/doc/README index 9d9794d5cfa..ea93afddcd5 100644 --- a/usr.sbin/unbound/doc/README +++ b/usr.sbin/unbound/doc/README @@ -1,4 +1,4 @@ -README for Unbound 1.15.0 +README for Unbound 1.16.0 Copyright 2007 NLnet Labs http://unbound.net diff --git a/usr.sbin/unbound/doc/example.conf.in b/usr.sbin/unbound/doc/example.conf.in index ac2de596bc4..ad4d08ce192 100644 --- a/usr.sbin/unbound/doc/example.conf.in +++ b/usr.sbin/unbound/doc/example.conf.in @@ -1,7 +1,7 @@ # # Example configuration file. # -# See unbound.conf(5) man page, version 1.15.0. +# See unbound.conf(5) man page, version 1.16.0. # # this is a comment. @@ -55,6 +55,10 @@ server: # Socket options are not supported on all platforms. experimental. # interface-automatic: no + # instead of the default port, open additional ports separated by + # spaces when interface-automatic is enabled, by listing them here. + # interface-automatic-ports: "" + # port to answer queries from # port: 53 @@ -811,6 +815,8 @@ server: # Add system certs to the cert bundle, from the Windows Cert Store # tls-win-cert: no + # and on other systems, the default openssl certificates + # tls-system-cert: no # Pad queries over TLS upstreams # pad-queries: yes @@ -897,6 +903,14 @@ server: # the number of servers that will be used in the fast server selection. # fast-server-num: 3 + # Enable to attach Extended DNS Error codes (RFC8914) to responses. + # ede: no + + # Enable to attach an Extended DNS Error (RFC8914) Code 3 - Stale + # Answer as EDNS0 option to expired responses. + # Note that the ede option above needs to be enabled for this to work. + # ede-serve-expired: no + # Specific options for ipsecmod. Unbound needs to be configured with # --enable-ipsecmod for these to take effect. # diff --git a/usr.sbin/unbound/doc/libunbound.3.in b/usr.sbin/unbound/doc/libunbound.3.in index 7df4f59d783..b1be90ce0f0 100644 --- a/usr.sbin/unbound/doc/libunbound.3.in +++ b/usr.sbin/unbound/doc/libunbound.3.in @@ -1,4 +1,4 @@ -.TH "libunbound" "3" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0" +.TH "libunbound" "3" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0" .\" .\" 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.15.0 functions. +\- Unbound DNS validating resolver 1.16.0 functions. .SH "SYNOPSIS" .B #include .LP diff --git a/usr.sbin/unbound/doc/unbound-anchor.8.in b/usr.sbin/unbound/doc/unbound-anchor.8.in index 268640d8155..4da37b1d5ff 100644 --- a/usr.sbin/unbound/doc/unbound-anchor.8.in +++ b/usr.sbin/unbound/doc/unbound-anchor.8.in @@ -1,4 +1,4 @@ -.TH "unbound-anchor" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0" +.TH "unbound-anchor" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0" .\" .\" unbound-anchor.8 -- unbound anchor maintenance utility manual .\" diff --git a/usr.sbin/unbound/doc/unbound-checkconf.8.in b/usr.sbin/unbound/doc/unbound-checkconf.8.in index ac8782dcde4..4c607a231b9 100644 --- a/usr.sbin/unbound/doc/unbound-checkconf.8.in +++ b/usr.sbin/unbound/doc/unbound-checkconf.8.in @@ -1,4 +1,4 @@ -.TH "unbound-checkconf" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0" +.TH "unbound-checkconf" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0" .\" .\" unbound-checkconf.8 -- unbound configuration checker manual .\" diff --git a/usr.sbin/unbound/doc/unbound-control.8.in b/usr.sbin/unbound/doc/unbound-control.8.in index 9d0c10e942b..3ef1d659f58 100644 --- a/usr.sbin/unbound/doc/unbound-control.8.in +++ b/usr.sbin/unbound/doc/unbound-control.8.in @@ -1,4 +1,4 @@ -.TH "unbound-control" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0" +.TH "unbound-control" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0" .\" .\" unbound-control.8 -- unbound remote control manual .\" @@ -289,20 +289,22 @@ just the ratelimited ips, with their estimated qps. The ratelimited ips are dropped before checking the cache. .TP .B list_auth_zones -List the auth zones that are configured. Printed one per line with a -status, indicating if the zone is expired and current serial number. +List the auth zones that are configured. Printed one per line with a status, +indicating if the zone is expired and current serial number. Configured RPZ +zones are included. .TP .B auth_zone_reload \fIzone\fR -Reload the auth zone from zonefile. The zonefile is read in overwriting -the current contents of the zone in memory. This changes the auth zone -contents itself, not the cache contents. Such cache contents exists if -you set Unbound to validate with for-upstream yes and that can be cleared -with \fBflush_zone\fR \fIzone\fR. +Reload the auth zone (or RPZ zone) from zonefile. The zonefile is read in +overwriting the current contents of the zone in memory. This changes the auth +zone contents itself, not the cache contents. Such cache contents exists if +you set Unbound to validate with for-upstream yes and that can be cleared with +\fBflush_zone\fR \fIzone\fR. .TP .B auth_zone_transfer \fIzone\fR -Transfer the auth zone from master. The auth zone probe sequence is started, -where the masters are probed to see if they have an updated zone (with the SOA -serial check). And then the zone is transferred for a newer zone version. +Transfer the auth zone (or RPZ zone) from master. The auth zone probe sequence +is started, where the masters are probed to see if they have an updated zone +(with the SOA serial check). And then the zone is transferred for a newer zone +version. .TP .B rpz_enable \fIzone\fR Enable the RPZ zone if it had previously been disabled. diff --git a/usr.sbin/unbound/doc/unbound-host.1.in b/usr.sbin/unbound/doc/unbound-host.1.in index 94c8ca3dd56..a30d1dfd216 100644 --- a/usr.sbin/unbound/doc/unbound-host.1.in +++ b/usr.sbin/unbound/doc/unbound-host.1.in @@ -1,4 +1,4 @@ -.TH "unbound\-host" "1" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0" +.TH "unbound\-host" "1" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0" .\" .\" unbound-host.1 -- unbound DNS lookup utility .\" diff --git a/usr.sbin/unbound/doc/unbound.8.in b/usr.sbin/unbound/doc/unbound.8.in index 723f23238b8..e3492724c95 100644 --- a/usr.sbin/unbound/doc/unbound.8.in +++ b/usr.sbin/unbound/doc/unbound.8.in @@ -1,4 +1,4 @@ -.TH "unbound" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0" +.TH "unbound" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0" .\" .\" unbound.8 -- unbound manual .\" @@ -9,7 +9,7 @@ .\" .SH "NAME" .B unbound -\- Unbound DNS validating resolver 1.15.0. +\- Unbound DNS validating resolver 1.16.0. .SH "SYNOPSIS" .B unbound .RB [ \-h ] diff --git a/usr.sbin/unbound/doc/unbound.conf.5.in b/usr.sbin/unbound/doc/unbound.conf.5.in index 55b97cc52de..ca2a20eaafb 100644 --- a/usr.sbin/unbound/doc/unbound.conf.5.in +++ b/usr.sbin/unbound/doc/unbound.conf.5.in @@ -1,4 +1,4 @@ -.TH "unbound.conf" "5" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0" +.TH "unbound.conf" "5" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0" .\" .\" unbound.conf.5 -- unbound.conf manual .\" @@ -104,6 +104,15 @@ ip\-transparent you can select which (future) interfaces Unbound provides service on. This feature is experimental, and needs support in your OS for particular socket options. Default value is no. .TP +.B interface\-automatic\-ports: \fI +List the port numbers that interface-automatic listens on. If empty, the +default port is listened on. The port numbers are separated by spaces in the +string. Default is "". +.IP +This can be used to have interface automatic to deal with the interface, +and listen on the normal port number, by including it in the list, and +also https or dns over tls port numbers by putting them in the list as well. +.TP .B outgoing\-interface: \fI Interface to use to connect to the network. This interface is used to send queries to authoritative servers and receive their replies. Can be given @@ -463,10 +472,11 @@ Enabled or disable whether the upstream queries use TLS only for transport. Default is no. Useful in tunneling scenarios. The TLS contains plain DNS in TCP wireformat. The other server must support this (see \fBtls\-service\-key\fR). -If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert to -load CA certs, otherwise the connections cannot be authenticated. -This option enables TLS for all of them, but if you do not set this you can -configure TLS specifically for some forward zones with forward\-tls\-upstream. And also with stub\-tls\-upstream. +If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert or +tls\-system\-cert to load CA certs, otherwise the connections cannot be +authenticated. This option enables TLS for all of them, but if you do not set +this you can configure TLS specifically for some forward zones with +forward\-tls\-upstream. And also with stub\-tls\-upstream. .TP .B ssl\-upstream: \fI Alternate syntax for \fBtls\-upstream\fR. If both are present in the config @@ -515,7 +525,12 @@ Alternate syntax for \fBtls\-cert\-bundle\fR. Add the system certificates to the cert bundle certificates for authentication. If no cert bundle, it uses only these certificates. Default is no. On windows this option uses the certificates from the cert store. Use -the tls\-cert\-bundle option on other systems. +the tls\-cert\-bundle option on other systems. On other systems, this option +enables the system certificates. +.TP +.B tls\-system\-cert: \fI +This the same setting as the tls\-win\-cert setting, under a different name. +Because it is not windows specific. .TP .B tls\-additional\-port: \fI List portnumbers as tls\-additional\-port, and when interfaces are defined, @@ -1275,7 +1290,17 @@ are authoritative DNS answers. By default the zones are class IN. .IP If you need more complicated authoritative data, with referrals, wildcards, CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for -it as detailed in the stub zone section below. +it as detailed in the stub zone section below. A stub\-zone can be used to +have unbound send queries to another server, an authoritative server, to +fetch the information. With a forward\-zone, unbound sends queries to a server +that is a recursive server to fetch the information. With an auth\-zone a +zone can be loaded from file and used, it can be used like a local\-zone +for users downstream, or the auth\-zone information can be used to fetch +information from when resolving like it is an upstream server. The +forward\-zone and auth\-zone options are described in their sections below. +If you want to perform filtering of the information that the users can fetch, +the local\-zone and local\-data statements allow for this, but also the +rpz functionality can be used, described in the RPZ section. .TP 10 \h'5'\fIdeny\fR Do not send an answer, drop the query. @@ -1716,6 +1741,21 @@ option can be used multiple times. The most specific match will be used. EDNS0 option code for the \fIedns\-client\-string\fR option, from 0 to 65535. A value from the `Reserved for Local/Experimental` range (65001-65534) should be used. Default is 65001. +.TP 5 +.B ede: \fI +If enabled, Unbound will respond with Extended DNS Error codes (RFC8914). +These EDEs attach informative error messages to a response for various +errors. Default is "no". + +When the \fBval-log-level\fR option is also set to \fB2\fR, responses with +Extended DNS Errors concerning DNSSEC failures that are not served from cache, +will also contain a descriptive text message about the reason for the failure. +.TP +.B ede\-serve\-expired: \fI +If enabled, Unbound will attach an Extended DNS Error (RFC8914) Code 3 - Stale +Answer as EDNS0 option to the expired response. Note that this will not attach +the EDE code without setting the global \fBede\fR option to "yes" as well. +Default is "no". .SS "Remote Control Options" In the .B remote\-control: @@ -1739,7 +1779,7 @@ Use 0.0.0.0 and ::0 to listen to all interfaces. If you change this and permissions have been dropped, you must restart the server for the change to take effect. .IP -If you set it to an absolute path, a local socket is used. The local socket +If you set it to an absolute path, a unix domain socket is used. This socket does not use the certificates and keys, so those files need not be present. To restrict access, Unbound sets permissions on the file to the user and group that is configured, the access bits are set to allow the group members diff --git a/usr.sbin/unbound/edns-subnet/subnetmod.c b/usr.sbin/unbound/edns-subnet/subnetmod.c index 81f0bf3ade5..25190b040d4 100644 --- a/usr.sbin/unbound/edns-subnet/subnetmod.c +++ b/usr.sbin/unbound/edns-subnet/subnetmod.c @@ -97,8 +97,8 @@ subnet_new_qstate(struct module_qstate *qstate, int id) } /** Add ecs struct to edns list, after parsing it to wire format. */ -static void -ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, +void +subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, struct module_qstate *qstate) { size_t sn_octs, sn_octs_remainder; @@ -162,17 +162,21 @@ int ecs_whitelist_check(struct query_info* qinfo, /* Address on whitelist or client query contains ECS option, we * want to sent out ECS. Only add option if it is not already * set. */ - if(!(sq->subnet_sent)) { - ecs_opt_list_append(&sq->ecs_server_out, + if(!edns_opt_list_find(qstate->edns_opts_back_out, + qstate->env->cfg->client_subnet_opcode)) { + subnet_ecs_opt_list_append(&sq->ecs_server_out, &qstate->edns_opts_back_out, qstate); - sq->subnet_sent = 1; } + sq->subnet_sent = 1; } - else if(sq->subnet_sent) { + else { /* Outgoing ECS option is set, but we don't want to sent it to * this address, remove option. */ - edns_opt_list_remove(&qstate->edns_opts_back_out, - qstate->env->cfg->client_subnet_opcode); + if(edns_opt_list_find(qstate->edns_opts_back_out, + qstate->env->cfg->client_subnet_opcode)) { + edns_opt_list_remove(&qstate->edns_opts_back_out, + qstate->env->cfg->client_subnet_opcode); + } sq->subnet_sent = 0; } return 1; @@ -227,7 +231,7 @@ subnetmod_init(struct module_env *env, int id) env->unique_mesh = 1; if(!edns_register_option(env->cfg->client_subnet_opcode, env->cfg->client_subnet_always_forward /* bypass cache */, - 0 /* no aggregation */, env)) { + 1 /* no aggregation */, env)) { log_err("subnetcache: could not register opcode"); ecs_whitelist_delete(sn_env->whitelist); slabhash_delete(sn_env->subnet_msg_cache); @@ -598,7 +602,7 @@ parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs) return 1; } -static void +void subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs, struct config_file* cfg) { @@ -761,7 +765,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, verbose(VERB_QUERY, "subnetcache: answered from cache"); qstate->ext_state[id] = module_finished; - ecs_opt_list_append(&sq->ecs_client_out, + subnet_ecs_opt_list_append(&sq->ecs_client_out, &qstate->edns_opts_front_out, qstate); return; } @@ -783,7 +787,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, sq->ecs_server_out.subnet_source_mask = qstate->env->cfg->max_client_subnet_ipv6; /* Safe to copy completely, even if the source is limited by the - * configuration. ecs_opt_list_append() will limit the address. + * configuration. subnet_ecs_opt_list_append() will limit the address. * */ memcpy(&sq->ecs_server_out.subnet_addr, sq->ecs_client_in.subnet_addr, INET6_SIZE); @@ -807,7 +811,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, qstate->ext_state[id] = eval_response(qstate, id, sq); if(qstate->ext_state[id] == module_finished && qstate->return_msg) { - ecs_opt_list_append(&sq->ecs_client_out, + subnet_ecs_opt_list_append(&sq->ecs_client_out, &qstate->edns_opts_front_out, qstate); } qstate->no_cache_store = sq->started_no_cache_store; diff --git a/usr.sbin/unbound/edns-subnet/subnetmod.h b/usr.sbin/unbound/edns-subnet/subnetmod.h index 27ba2ee7412..c877692b46b 100644 --- a/usr.sbin/unbound/edns-subnet/subnetmod.h +++ b/usr.sbin/unbound/edns-subnet/subnetmod.h @@ -143,4 +143,11 @@ int ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, /** mark subnet msg to be deleted */ void subnet_markdel(void* key); +/** Add ecs struct to edns list, after parsing it to wire format. */ +void subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, + struct module_qstate *qstate); + +/** Create ecs_data from the sockaddr_storage information. */ +void subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs, + struct config_file* cfg); #endif /* SUBNETMOD_H */ diff --git a/usr.sbin/unbound/iterator/iter_delegpt.h b/usr.sbin/unbound/iterator/iter_delegpt.h index 17db15a23e2..998b98cd803 100644 --- a/usr.sbin/unbound/iterator/iter_delegpt.h +++ b/usr.sbin/unbound/iterator/iter_delegpt.h @@ -128,7 +128,7 @@ struct delegpt_ns { uint8_t done_pside6; /** the TLS authentication name, (if not NULL) to use. */ char* tls_auth_name; - /** the port to use; it should mosty be the default 53 but configured + /** the port to use; it should mostly be the default 53 but configured * upstreams can provide nondefault ports. */ int port; }; diff --git a/usr.sbin/unbound/iterator/iter_utils.c b/usr.sbin/unbound/iterator/iter_utils.c index 2482a1f40f0..f3bea46d6c9 100644 --- a/usr.sbin/unbound/iterator/iter_utils.c +++ b/usr.sbin/unbound/iterator/iter_utils.c @@ -743,9 +743,10 @@ iter_mark_pside_cycle_targets(struct module_qstate* qstate, struct delegpt* dp) int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, - struct delegpt* dp) + struct delegpt* dp, int supports_ipv4, int supports_ipv6) { struct delegpt_ns* ns; + struct delegpt_addr* a; /* check: * o RD qflag is on. * o no addresses are provided. @@ -758,13 +759,24 @@ iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, */ if(!(qflags&BIT_RD)) return 0; - /* either available or unused targets */ - if(dp->usable_list || dp->result_list) - return 0; + /* either available or unused targets, + * if they exist, the dp is not useless. */ + for(a = dp->usable_list; a; a = a->next_usable) { + if(!addr_is_ip6(&a->addr, a->addrlen) && supports_ipv4) + return 0; + else if(addr_is_ip6(&a->addr, a->addrlen) && supports_ipv6) + return 0; + } + for(a = dp->result_list; a; a = a->next_result) { + if(!addr_is_ip6(&a->addr, a->addrlen) && supports_ipv4) + return 0; + else if(addr_is_ip6(&a->addr, a->addrlen) && supports_ipv6) + return 0; + } /* see if query is for one of the nameservers, which is glue */ - if( (qinfo->qtype == LDNS_RR_TYPE_A || - qinfo->qtype == LDNS_RR_TYPE_AAAA) && + if( ((qinfo->qtype == LDNS_RR_TYPE_A && supports_ipv4) || + (qinfo->qtype == LDNS_RR_TYPE_AAAA && supports_ipv6)) && dname_subdomain_c(qinfo->qname, dp->name) && delegpt_find_ns(dp, qinfo->qname, qinfo->qname_len)) return 1; diff --git a/usr.sbin/unbound/iterator/iter_utils.h b/usr.sbin/unbound/iterator/iter_utils.h index 0a40916c0e0..c0e5181573f 100644 --- a/usr.sbin/unbound/iterator/iter_utils.h +++ b/usr.sbin/unbound/iterator/iter_utils.h @@ -175,10 +175,14 @@ void iter_mark_pside_cycle_targets(struct module_qstate* qstate, * @param qinfo: query name and type * @param qflags: query flags with RD flag * @param dp: delegpt to check. + * @param supports_ipv4: if we support ipv4 for lookups to the target. + * if not, then the IPv4 addresses are useless. + * @param supports_ipv6: if we support ipv6 for lookups to the target. + * if not, then the IPv6 addresses are useless. * @return true if dp is useless. */ int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, - struct delegpt* dp); + struct delegpt* dp, int supports_ipv4, int supports_ipv6); /** * See if qname has DNSSEC needs. This is true if there is a trust anchor above diff --git a/usr.sbin/unbound/iterator/iterator.c b/usr.sbin/unbound/iterator/iterator.c index 54006940d90..3cfb286f449 100644 --- a/usr.sbin/unbound/iterator/iterator.c +++ b/usr.sbin/unbound/iterator/iterator.c @@ -1547,7 +1547,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, * same server reply) if useless-checked. */ if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, - iq->dp)) { + iq->dp, ie->supports_ipv4, ie->supports_ipv6)) { struct delegpt* retdp = NULL; if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &retdp)) { if(retdp) { @@ -1831,6 +1831,23 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, int missing; int toget = 0; + iter_mark_cycle_targets(qstate, iq->dp); + missing = (int)delegpt_count_missing_targets(iq->dp); + log_assert(maxtargets != 0); /* that would not be useful */ + + /* Generate target requests. Basically, any missing targets + * are queried for here, regardless if it is necessary to do + * so to continue processing. */ + if(maxtargets < 0 || maxtargets > missing) + toget = missing; + else toget = maxtargets; + if(toget == 0) { + *num = 0; + return 1; + } + + /* now that we are sure that a target query is going to be made, + * check the limits. */ if(iq->depth == ie->max_dependency_depth) return 0; if(iq->depth > 0 && iq->target_count && @@ -1850,20 +1867,6 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, return 0; } - iter_mark_cycle_targets(qstate, iq->dp); - missing = (int)delegpt_count_missing_targets(iq->dp); - log_assert(maxtargets != 0); /* that would not be useful */ - - /* Generate target requests. Basically, any missing targets - * are queried for here, regardless if it is necessary to do - * so to continue processing. */ - if(maxtargets < 0 || maxtargets > missing) - toget = missing; - else toget = maxtargets; - if(toget == 0) { - *num = 0; - return 1; - } /* select 'toget' items from the total of 'missing' items */ log_assert(toget <= missing); @@ -2512,7 +2515,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->response = forged_response; next_state(iq, FINISHED_STATE); if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { - log_err("rpz, prepend rrsets: out of memory"); + log_err("rpz: prepend rrsets: out of memory"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } return 0; @@ -2832,7 +2835,9 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, } if(!qstate->no_cache_store) iter_dns_store(qstate->env, &iq->response->qinfo, - iq->response->rep, 0, qstate->prefetch_leeway, + iq->response->rep, + iq->qchase.qtype != iq->response->qinfo.qtype, + qstate->prefetch_leeway, iq->dp&&iq->dp->has_parent_side_NS, qstate->region, qstate->query_flags); /* close down outstanding requests to be discarded */ @@ -3067,7 +3072,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, iq->response = forged_response; next_state(iq, FINISHED_STATE); if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { - log_err("rpz after cname, prepend rrsets: out of memory"); + log_err("rpz: after cname, prepend rrsets: out of memory"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } qstate->return_msg->qinfo = qstate->qinfo; diff --git a/usr.sbin/unbound/libunbound/libworker.c b/usr.sbin/unbound/libunbound/libworker.c index ab28dd54f94..11bf5f9db55 100644 --- a/usr.sbin/unbound/libunbound/libworker.c +++ b/usr.sbin/unbound/libunbound/libworker.c @@ -650,7 +650,7 @@ int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q) } /* process new query */ if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, - w->back->udp_buff, qid, libworker_fg_done_cb, q)) { + w->back->udp_buff, qid, libworker_fg_done_cb, q, 0)) { free(qinfo.qname); return UB_NOMEM; } @@ -730,7 +730,7 @@ int libworker_attach_mesh(struct ub_ctx* ctx, struct ctx_query* q, if(async_id) *async_id = q->querynum; if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, - w->back->udp_buff, qid, libworker_event_done_cb, q)) { + w->back->udp_buff, qid, libworker_event_done_cb, q, 0)) { free(qinfo.qname); return UB_NOMEM; } @@ -867,7 +867,7 @@ handle_newq(struct libworker* w, uint8_t* buf, uint32_t len) q->w = w; /* process new query */ if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, - w->back->udp_buff, qid, libworker_bg_done_cb, q)) { + w->back->udp_buff, qid, libworker_bg_done_cb, q, 0)) { add_bg_result(w, q, NULL, UB_NOMEM, NULL, 0); } free(qinfo.qname); diff --git a/usr.sbin/unbound/respip/respip.c b/usr.sbin/unbound/respip/respip.c index 3d1b3feaf46..deff663170a 100644 --- a/usr.sbin/unbound/respip/respip.c +++ b/usr.sbin/unbound/respip/respip.c @@ -833,8 +833,11 @@ static int respip_use_rpz(struct resp_addr* raddr, struct rpz* r, enum respip_action* action, struct ub_packed_rrset_key** data, int* rpz_log, char** log_name, - int* rpz_cname_override, struct regional* region, int* is_rpz) + int* rpz_cname_override, struct regional* region, int* is_rpz, + int* rpz_passthru) { + if(rpz_passthru && *rpz_passthru) + return 0; if(r->action_override == RPZ_DISABLED_ACTION) { *is_rpz = 0; return 1; @@ -848,6 +851,9 @@ respip_use_rpz(struct resp_addr* raddr, struct rpz* r, *data = r->cname_override; *rpz_cname_override = 1; } + if(*action == respip_always_transparent /* RPZ_PASSTHRU_ACTION */ + && rpz_passthru) + *rpz_passthru = 1; *rpz_log = r->log; if(r->log_name) if(!(*log_name = regional_strdup(region, r->log_name))) @@ -861,7 +867,7 @@ respip_rewrite_reply(const struct query_info* qinfo, const struct respip_client_info* cinfo, const struct reply_info* rep, struct reply_info** new_repp, struct respip_action_info* actinfo, struct ub_packed_rrset_key** alias_rrset, int search_only, - struct regional* region, struct auth_zones* az) + struct regional* region, struct auth_zones* az, int* rpz_passthru) { const uint8_t* ctaglist; size_t ctaglen; @@ -934,7 +940,7 @@ respip_rewrite_reply(const struct query_info* qinfo, ipset->tagname, ipset->num_tags); } lock_rw_rdlock(&az->rpz_lock); - for(a = az->rpz_first; a && !raddr; a = a->rpz_az_next) { + for(a = az->rpz_first; a && !raddr && !(rpz_passthru && *rpz_passthru); a = a->rpz_az_next) { lock_rw_rdlock(&a->lock); r = a->rpz; if(!r->taglist || taglist_intersect(r->taglist, @@ -943,7 +949,7 @@ respip_rewrite_reply(const struct query_info* qinfo, r->respip_set, &rrset_id, &rr_id))) { if(!respip_use_rpz(raddr, r, &action, &data, &rpz_log, &log_name, &rpz_cname_override, - region, &rpz_used)) { + region, &rpz_used, rpz_passthru)) { log_err("out of memory"); lock_rw_unlock(&raddr->lock); lock_rw_unlock(&a->lock); @@ -964,7 +970,7 @@ respip_rewrite_reply(const struct query_info* qinfo, addr_to_str(&raddr->node.addr, raddr->node.addrlen, nm, sizeof(nm)); - verbose(VERB_ALGO, "respip: rpz response-ip trigger %s/%d on %s %s with action %s", nm, raddr->node.net, qn, ip, rpz_action_to_string(respip_action_to_rpz_action(action))); + verbose(VERB_ALGO, "respip: rpz: response-ip trigger %s/%d on %s %s with action %s", nm, raddr->node.net, qn, ip, rpz_action_to_string(respip_action_to_rpz_action(action))); } /* break to make sure 'a' stays pointed * to used auth_zone, and keeps lock */ @@ -1094,7 +1100,8 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id, if(!respip_rewrite_reply(&qstate->qinfo, qstate->client_info, qstate->return_msg->rep, &new_rep, &actinfo, &alias_rrset, 0, - qstate->region, qstate->env->auth_zones)) { + qstate->region, qstate->env->auth_zones, + &qstate->rpz_passthru)) { goto servfail; } if(actinfo.action != respip_none) { @@ -1169,7 +1176,7 @@ respip_merge_cname(struct reply_info* base_rep, /* see if the target reply would be subject to a response-ip action. */ if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo, - &alias_rrset, 1, region, az)) + &alias_rrset, 1, region, az, NULL)) return 0; if(actinfo.action != respip_none) { log_info("CNAME target of redirect response-ip action would " @@ -1301,7 +1308,7 @@ respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname, respip, sizeof(respip)); if(respip_actinfo->rpz_log) { txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, "%s", - "RPZ applied "); + "rpz: applied "); if(respip_actinfo->rpz_cname_override) actionstr = rpz_action_to_string( RPZ_CNAME_OVERRIDE_ACTION); diff --git a/usr.sbin/unbound/respip/respip.h b/usr.sbin/unbound/respip/respip.h index 3dfb4e9f01c..988a7226339 100644 --- a/usr.sbin/unbound/respip/respip.h +++ b/usr.sbin/unbound/respip/respip.h @@ -176,6 +176,8 @@ int respip_merge_cname(struct reply_info* base_rep, * will be set (or intact) accordingly but the modified reply won't be built. * @param az: auth zones containing RPZ information. * @param region: allocator to build *new_repp. + * @param rpz_passthru: keeps track of query state can have passthru that + * stops further rpz processing. Or NULL for cached answer processing. * @return 1 on success, 0 on error. */ int respip_rewrite_reply(const struct query_info* qinfo, @@ -183,7 +185,8 @@ int respip_rewrite_reply(const struct query_info* qinfo, const struct reply_info *rep, struct reply_info** new_repp, struct respip_action_info* actinfo, struct ub_packed_rrset_key** alias_rrset, - int search_only, struct regional* region, struct auth_zones* az); + int search_only, struct regional* region, struct auth_zones* az, + int* rpz_passthru); /** * Get the response-ip function block. diff --git a/usr.sbin/unbound/services/authzone.c b/usr.sbin/unbound/services/authzone.c index e83af533dbc..02fb621a22f 100644 --- a/usr.sbin/unbound/services/authzone.c +++ b/usr.sbin/unbound/services/authzone.c @@ -132,6 +132,7 @@ msg_create(struct regional* region, struct query_info* qinfo) return NULL; msg->rep->flags = (uint16_t)(BIT_QR | BIT_AA); msg->rep->authoritative = 1; + msg->rep->reason_bogus = LDNS_EDE_NONE; msg->rep->qdcount = 1; /* rrsets is NULL, no rrsets yet */ return msg; @@ -1882,6 +1883,8 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z, struct regional* region = NULL; struct sldns_buffer* buf = NULL; uint32_t soa_serial = 0; + char* unsupported_reason = NULL; + int only_unsupported = 1; region = env->scratch; regional_free_all(region); buf = env->scratch_buffer; @@ -1911,6 +1914,7 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z, &hashalgo, &hash, &hashlen)) { /* malformed RR */ *reason = "ZONEMD rdata malformed"; + only_unsupported = 0; continue; } /* check for duplicates */ @@ -1920,25 +1924,51 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z, * is not allowed. */ *reason = "ZONEMD RRSet contains more than one RR " "with the same scheme and hash algorithm"; + only_unsupported = 0; continue; } regional_free_all(region); if(serial != soa_serial) { *reason = "ZONEMD serial is wrong"; + only_unsupported = 0; continue; } + *reason = NULL; if(auth_zone_generate_zonemd_check(z, scheme, hashalgo, hash, hashlen, region, buf, reason)) { /* success */ + if(*reason) { + if(!unsupported_reason) + unsupported_reason = *reason; + /* continue to check for valid ZONEMD */ + if(verbosity >= VERB_ALGO) { + char zstr[255+1]; + dname_str(z->name, zstr); + verbose(VERB_ALGO, "auth-zone %s ZONEMD %d %d is unsupported: %s", zstr, (int)scheme, (int)hashalgo, *reason); + } + *reason = NULL; + continue; + } if(verbosity >= VERB_ALGO) { char zstr[255+1]; dname_str(z->name, zstr); - verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr); + if(!*reason) + verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr); } return 1; } + only_unsupported = 0; /* try next one */ } + /* have we seen no failures but only unsupported algo, + * and one unsupported algorithm, or more. */ + if(only_unsupported && unsupported_reason) { + /* only unsupported algorithms, with valid serial, not + * malformed. Did not see supported algorithms, failed or + * successful ones. */ + *reason = unsupported_reason; + return 1; + } /* fail, we may have reason */ if(!*reason) *reason = "no ZONEMD records found"; @@ -4456,7 +4486,7 @@ chunkline_get_line_collated(struct auth_chunk** chunk, size_t* chunk_pos, return 1; } -/** process $ORIGIN for http */ +/** process $ORIGIN for http, 0 nothing, 1 done, 2 error */ static int http_parse_origin(sldns_buffer* buf, struct sldns_file_parse_state* pstate) { @@ -4467,13 +4497,16 @@ http_parse_origin(sldns_buffer* buf, struct sldns_file_parse_state* pstate) pstate->origin_len = sizeof(pstate->origin); s = sldns_str2wire_dname_buf(sldns_strip_ws(line+8), pstate->origin, &pstate->origin_len); - if(s) pstate->origin_len = 0; + if(s) { + pstate->origin_len = 0; + return 2; + } return 1; } return 0; } -/** process $TTL for http */ +/** process $TTL for http, 0 nothing, 1 done, 2 error */ static int http_parse_ttl(sldns_buffer* buf, struct sldns_file_parse_state* pstate) { @@ -4481,8 +4514,12 @@ http_parse_ttl(sldns_buffer* buf, struct sldns_file_parse_state* pstate) if(strncmp(line, "$TTL", 4) == 0 && isspace((unsigned char)line[4])) { const char* end = NULL; + int overflow = 0; pstate->default_ttl = sldns_str2period( - sldns_strip_ws(line+5), &end); + sldns_strip_ws(line+5), &end, &overflow); + if(overflow) { + return 2; + } return 1; } return 0; @@ -4493,15 +4530,20 @@ static int chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos, sldns_buffer* buf, struct sldns_file_parse_state* pstate) { + int ret; while(chunkline_get_line_collated(chunk, chunk_pos, buf)) { if(chunkline_is_comment_line_or_empty(buf)) { /* a comment, go to next line */ continue; } - if(http_parse_origin(buf, pstate)) { + if((ret=http_parse_origin(buf, pstate))!=0) { + if(ret == 2) + return 0; continue; /* $ORIGIN has been handled */ } - if(http_parse_ttl(buf, pstate)) { + if((ret=http_parse_ttl(buf, pstate))!=0) { + if(ret == 2) + return 0; continue; /* $TTL has been handled */ } return 1; @@ -5007,6 +5049,7 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z, struct sldns_file_parse_state pstate; struct auth_chunk* chunk; size_t chunk_pos; + int ret; memset(&pstate, 0, sizeof(pstate)); pstate.default_ttl = 3600; if(xfr->namelen < sizeof(pstate.origin)) { @@ -5063,10 +5106,24 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z, continue; } /* parse line and add RR */ - if(http_parse_origin(scratch_buffer, &pstate)) { + if((ret=http_parse_origin(scratch_buffer, &pstate))!=0) { + if(ret == 2) { + verbose(VERB_ALGO, "error parsing ORIGIN on line [%s:%d] %s", + xfr->task_transfer->master->file, + pstate.lineno, + sldns_buffer_begin(scratch_buffer)); + return 0; + } continue; /* $ORIGIN has been handled */ } - if(http_parse_ttl(scratch_buffer, &pstate)) { + if((ret=http_parse_ttl(scratch_buffer, &pstate))!=0) { + if(ret == 2) { + verbose(VERB_ALGO, "error parsing TTL on line [%s:%d] %s", + xfr->task_transfer->master->file, + pstate.lineno, + sldns_buffer_begin(scratch_buffer)); + return 0; + } continue; /* $TTL has been handled */ } if(!http_parse_add_rr(xfr, z, scratch_buffer, &pstate)) { @@ -5370,7 +5427,7 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env) * called straight away */ lock_basic_unlock(&xfr->lock); if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &auth_xfer_transfer_lookup_callback, xfr)) { + &auth_xfer_transfer_lookup_callback, xfr, 0)) { lock_basic_lock(&xfr->lock); log_err("out of memory lookup up master %s", master->host); return 0; @@ -6561,7 +6618,7 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) * called straight away */ lock_basic_unlock(&xfr->lock); if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &auth_xfer_probe_lookup_callback, xfr)) { + &auth_xfer_probe_lookup_callback, xfr, 0)) { lock_basic_lock(&xfr->lock); log_err("out of memory lookup up master %s", master->host); return 0; @@ -7632,13 +7689,16 @@ int auth_zone_generate_zonemd_check(struct auth_zone* z, int scheme, { uint8_t gen[512]; size_t genlen = 0; + *reason = NULL; if(!zonemd_hashalgo_supported(hashalgo)) { + /* allow it */ *reason = "unsupported algorithm"; - return 0; + return 1; } if(!zonemd_scheme_supported(scheme)) { + /* allow it */ *reason = "unsupported scheme"; - return 0; + return 1; } if(hashlen < 12) { /* the ZONEMD draft requires digests to fail if too small */ @@ -7726,7 +7786,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, auth_zone_log(z->name, VERB_ALGO, "zonemd: verify %s RRset with DNSKEY", typestr); } - sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, + sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL, LDNS_SECTION_ANSWER, NULL); if(sec == sec_status_secure) { return 1; @@ -8003,9 +8063,13 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env, } /* success! log the success */ - auth_zone_log(z->name, VERB_ALGO, "ZONEMD verification successful"); + if(reason) + auth_zone_log(z->name, VERB_ALGO, "ZONEMD %s", reason); + else auth_zone_log(z->name, VERB_ALGO, "ZONEMD verification successful"); if(result) { - *result = strdup("ZONEMD verification successful"); + if(reason) + *result = strdup(reason); + else *result = strdup("ZONEMD verification successful"); if(!*result) log_err("out of memory"); } } @@ -8065,7 +8129,7 @@ zonemd_get_dnskey_from_anchor(struct auth_zone* z, struct module_env* env, auth_zone_log(z->name, VERB_QUERY, "zonemd: verify DNSKEY RRset with trust anchor"); sec = val_verify_DNSKEY_with_TA(env, ve, keystorage, anchor->ds_rrset, - anchor->dnskey_rrset, NULL, why_bogus, NULL); + anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL); regional_free_all(env->scratch); if(sec == sec_status_secure) { /* success */ @@ -8123,8 +8187,9 @@ auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z, keystorage->rk.type = htons(LDNS_RR_TYPE_DNSKEY); keystorage->rk.rrset_class = htons(z->dclass); auth_zone_log(z->name, VERB_QUERY, "zonemd: verify zone DNSKEY with DS"); + // @TODO add EDE here? we currently just pass NULL sec = val_verify_DNSKEY_with_DS(env, ve, keystorage, ds, sigalg, - why_bogus, NULL); + why_bogus, NULL, NULL); regional_free_all(env->scratch); if(sec == sec_status_secure) { /* success */ @@ -8340,7 +8405,7 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env) /* the callback can be called straight away */ lock_rw_unlock(&z->lock); if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &auth_zonemd_dnskey_lookup_callback, z)) { + &auth_zonemd_dnskey_lookup_callback, z, 0)) { lock_rw_wrlock(&z->lock); log_err("out of memory lookup of %s for zonemd", (fetch_ds?"DS":"DNSKEY")); diff --git a/usr.sbin/unbound/services/authzone.h b/usr.sbin/unbound/services/authzone.h index d24e569d3b8..07614ed8296 100644 --- a/usr.sbin/unbound/services/authzone.h +++ b/usr.sbin/unbound/services/authzone.h @@ -747,6 +747,9 @@ int zonemd_scheme_supported(int scheme); * @param region: temp region for allocs during canonicalisation. * @param buf: temp buffer during canonicalisation. * @param reason: string returned with failure reason. + * If the hash cannot be checked, but it is allowed, for unknown + * algorithms, the routine returns success, and the reason is nonNULL, + * with the allowance reason. * @return false on failure. */ int auth_zone_generate_zonemd_check(struct auth_zone* z, int scheme, diff --git a/usr.sbin/unbound/services/cache/dns.c b/usr.sbin/unbound/services/cache/dns.c index 5b64fe47520..f6c11451c93 100644 --- a/usr.sbin/unbound/services/cache/dns.c +++ b/usr.sbin/unbound/services/cache/dns.c @@ -428,6 +428,7 @@ dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, return NULL; /* integer overflow protection */ msg->rep->flags = BIT_QR; /* with QR, no AA */ msg->rep->qdcount = 1; + msg->rep->reason_bogus = LDNS_EDE_NONE; msg->rep->rrsets = (struct ub_packed_rrset_key**) regional_alloc(region, capacity*sizeof(struct ub_packed_rrset_key*)); @@ -524,6 +525,7 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num) sizeof(struct reply_info) - sizeof(struct rrset_ref)); if(!msg->rep) return NULL; + msg->rep->reason_bogus = LDNS_EDE_NONE; if(num > RR_COUNT_MAX) return NULL; /* integer overflow protection */ msg->rep->rrsets = (struct ub_packed_rrset_key**) @@ -577,6 +579,7 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, msg->rep->ar_numrrsets = r->ar_numrrsets; msg->rep->rrset_count = r->rrset_count; msg->rep->authoritative = r->authoritative; + msg->rep->reason_bogus = r->reason_bogus; if(!rrset_array_lock(r->ref, r->rrset_count, now_control)) { return NULL; } @@ -632,6 +635,7 @@ rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, msg->rep->ns_numrrsets = 0; msg->rep->ar_numrrsets = 0; msg->rep->rrset_count = 1; + msg->rep->reason_bogus = LDNS_EDE_NONE; msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now); if(!msg->rep->rrsets[0]) /* copy CNAME */ return NULL; @@ -670,6 +674,7 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, msg->rep->ns_numrrsets = 0; msg->rep->ar_numrrsets = 0; msg->rep->rrset_count = 1; + msg->rep->reason_bogus = LDNS_EDE_NONE; msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now); if(!msg->rep->rrsets[0]) /* copy DNAME */ return NULL; diff --git a/usr.sbin/unbound/services/listen_dnsport.c b/usr.sbin/unbound/services/listen_dnsport.c index d6a90f1e68d..03153bd6477 100644 --- a/usr.sbin/unbound/services/listen_dnsport.c +++ b/usr.sbin/unbound/services/listen_dnsport.c @@ -47,6 +47,7 @@ #ifdef USE_TCP_FASTOPEN #include #endif +#include #include "services/listen_dnsport.h" #include "services/outside_network.h" #include "util/netevent.h" @@ -1157,7 +1158,7 @@ if_is_ssl(const char* ifname, const char* port, int ssl_port, * @param do_auto: use automatic interface detection. * If enabled, then ifname must be the wildcard name. * @param do_udp: if udp should be used. - * @param do_tcp: if udp should be used. + * @param do_tcp: if tcp should be used. * @param hints: for getaddrinfo. family and flags have to be set by caller. * @param port: Port number to use (as string). * @param list: list of open ports, appended to, changed to point to list head. @@ -1716,6 +1717,63 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs, } /* create ip4 and ip6 ports so that return addresses are nice. */ if(do_auto || num_ifs == 0) { + if(do_auto && cfg->if_automatic_ports && + cfg->if_automatic_ports[0]!=0) { + char* now = cfg->if_automatic_ports; + while(now && *now) { + char* after; + int extraport; + while(isspace((unsigned char)*now)) + now++; + if(!*now) + break; + after = now; + extraport = (int)strtol(now, &after, 10); + if(extraport < 0 || extraport > 65535) { + log_err("interface-automatic-ports port number out of range, at position %d of '%s'", (int)(now-cfg->if_automatic_ports)+1, cfg->if_automatic_ports); + listening_ports_free(list); + return NULL; + } + if(extraport == 0 && now == after) { + log_err("interface-automatic-ports could not be parsed, at position %d of '%s'", (int)(now-cfg->if_automatic_ports)+1, cfg->if_automatic_ports); + listening_ports_free(list); + return NULL; + } + now = after; + snprintf(portbuf, sizeof(portbuf), "%d", extraport); + if(do_ip6) { + hints.ai_family = AF_INET6; + if(!ports_create_if("::0", + do_auto, cfg->do_udp, do_tcp, + &hints, portbuf, &list, + cfg->so_rcvbuf, cfg->so_sndbuf, + cfg->ssl_port, cfg->tls_additional_port, + cfg->https_port, reuseport, cfg->ip_transparent, + cfg->tcp_mss, cfg->ip_freebind, + cfg->http_nodelay, cfg->use_systemd, + cfg->dnscrypt_port, cfg->ip_dscp)) { + listening_ports_free(list); + return NULL; + } + } + if(do_ip4) { + hints.ai_family = AF_INET; + if(!ports_create_if("0.0.0.0", + do_auto, cfg->do_udp, do_tcp, + &hints, portbuf, &list, + cfg->so_rcvbuf, cfg->so_sndbuf, + cfg->ssl_port, cfg->tls_additional_port, + cfg->https_port, reuseport, cfg->ip_transparent, + cfg->tcp_mss, cfg->ip_freebind, + cfg->http_nodelay, cfg->use_systemd, + cfg->dnscrypt_port, cfg->ip_dscp)) { + listening_ports_free(list); + return NULL; + } + } + } + return list; + } if(do_ip6) { hints.ai_family = AF_INET6; if(!ports_create_if(do_auto?"::0":"::1", diff --git a/usr.sbin/unbound/services/localzone.c b/usr.sbin/unbound/services/localzone.c index 3e3a71aea3c..3ed7d835d33 100644 --- a/usr.sbin/unbound/services/localzone.c +++ b/usr.sbin/unbound/services/localzone.c @@ -1328,7 +1328,8 @@ local_encode(struct query_info* qinfo, struct module_env* env, static void local_error_encode(struct query_info* qinfo, struct module_env* env, struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, - struct regional* temp, int rcode, int r) + struct regional* temp, int rcode, int r, int ede_code, + const char* ede_txt) { edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; @@ -1338,6 +1339,12 @@ local_error_encode(struct query_info* qinfo, struct module_env* env, if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL, rcode, edns, repinfo, temp, env->now_tv)) edns->opt_list_inplace_cb_out = NULL; + + if(ede_code != LDNS_EDE_NONE && env->cfg->ede) { + edns_opt_list_append_ede(&edns->opt_list_out, temp, + ede_code, ede_txt); + } + error_encode(buf, r, qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); } @@ -1535,7 +1542,9 @@ local_data_answer(struct local_zone* z, struct module_env* env, qinfo->local_alias = NULL; local_error_encode(qinfo, env, edns, repinfo, buf, temp, LDNS_RCODE_YXDOMAIN, - (LDNS_RCODE_YXDOMAIN|BIT_AA)); + (LDNS_RCODE_YXDOMAIN|BIT_AA), + LDNS_EDE_OTHER, + "DNAME expansion became too large"); return 1; } memset(&qinfo->local_alias->rrset->entry, 0, @@ -1638,7 +1647,8 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, } else if(lz_type == local_zone_refuse || lz_type == local_zone_always_refuse) { local_error_encode(qinfo, env, edns, repinfo, buf, temp, - LDNS_RCODE_REFUSED, (LDNS_RCODE_REFUSED|BIT_AA)); + LDNS_RCODE_REFUSED, (LDNS_RCODE_REFUSED|BIT_AA), + LDNS_EDE_NONE, NULL); return 1; } else if(lz_type == local_zone_static || lz_type == local_zone_redirect || @@ -1663,8 +1673,8 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, if(z != NULL && z->soa && z->soa_negative) return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa_negative, 0, rcode); - local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode, - (rcode|BIT_AA)); + local_error_encode(qinfo, env, edns, repinfo, buf, temp, + rcode, (rcode|BIT_AA), LDNS_EDE_NONE, NULL); return 1; } else if(lz_type == local_zone_typetransparent || lz_type == local_zone_always_transparent) { @@ -1705,9 +1715,10 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, return local_encode(qinfo, env, edns, repinfo, buf, temp, &lrr, 1, LDNS_RCODE_NOERROR); } else { + /* NODATA: No EDE needed */ local_error_encode(qinfo, env, edns, repinfo, buf, temp, LDNS_RCODE_NOERROR, - (LDNS_RCODE_NOERROR|BIT_AA)); + (LDNS_RCODE_NOERROR|BIT_AA), -1, NULL); } return 1; } @@ -1720,8 +1731,9 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, if(z != NULL && z->soa && z->soa_negative) return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa_negative, 0, rcode); + /* NODATA: No EDE needed */ local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode, - (rcode|BIT_AA)); + (rcode|BIT_AA), LDNS_EDE_NONE, NULL); return 1; } diff --git a/usr.sbin/unbound/services/mesh.c b/usr.sbin/unbound/services/mesh.c index cdcfedda270..fbaa966bdd0 100644 --- a/usr.sbin/unbound/services/mesh.c +++ b/usr.sbin/unbound/services/mesh.c @@ -64,6 +64,11 @@ #include "respip/respip.h" #include "services/listen_dnsport.h" +#ifdef CLIENT_SUBNET +#include "edns-subnet/subnetmod.h" +#include "edns-subnet/edns-subnet.h" +#endif + /** subtract timers and the values do not overflow or become negative */ static void timeval_subtract(struct timeval* d, const struct timeval* end, const struct timeval* start) @@ -458,7 +463,8 @@ mesh_serve_expired_init(struct mesh_state* mstate, int timeout) void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, struct respip_client_info* cinfo, uint16_t qflags, - struct edns_data* edns, struct comm_reply* rep, uint16_t qid) + struct edns_data* edns, struct comm_reply* rep, uint16_t qid, + int rpz_passthru) { struct mesh_state* s = NULL; int unique = unique_mesh_state(edns->opt_list_in, mesh->env); @@ -513,6 +519,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, } if(unique) mesh_state_make_unique(s); + s->s.rpz_passthru = rpz_passthru; /* copy the edns options we got from the front */ if(edns->opt_list_in) { s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in, @@ -606,7 +613,7 @@ servfail_mem: int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, struct edns_data* edns, sldns_buffer* buf, - uint16_t qid, mesh_cb_func_type cb, void* cb_arg) + uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru) { struct mesh_state* s = NULL; int unique = unique_mesh_state(edns->opt_list_in, mesh->env); @@ -632,6 +639,7 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, } if(unique) mesh_state_make_unique(s); + s->s.rpz_passthru = rpz_passthru; if(edns->opt_list_in) { s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in, s->s.region); @@ -686,7 +694,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, * 0 (false), in which case the new state is only made runnable so it * will not be run recursively on top of the current state. */ static void mesh_schedule_prefetch(struct mesh_area* mesh, - struct query_info* qinfo, uint16_t qflags, time_t leeway, int run) + struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, + int rpz_passthru) { struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); @@ -732,15 +741,109 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh, /* move to either the forever or the jostle_list */ if(mesh->num_forever_states < mesh->max_forever_states) { mesh->num_forever_states ++; - mesh_list_insert(s, &mesh->forever_first, + mesh_list_insert(s, &mesh->forever_first, &mesh->forever_last); s->list_select = mesh_forever_list; } else { - mesh_list_insert(s, &mesh->jostle_first, + mesh_list_insert(s, &mesh->jostle_first, + &mesh->jostle_last); + s->list_select = mesh_jostle_list; + } + } + s->s.rpz_passthru = rpz_passthru; + + if(!run) { +#ifdef UNBOUND_DEBUG + n = +#else + (void) +#endif + rbtree_insert(&mesh->run, &s->run_node); + log_assert(n != NULL); + return; + } + + mesh_run(mesh, s, module_event_new, NULL); +} + +#ifdef CLIENT_SUBNET +/* Same logic as mesh_schedule_prefetch but tailored to the subnet module logic + * like passing along the comm_reply info. This will be faked into an EDNS + * option for processing by the subnet module if the client has not already + * attached its own ECS data. */ +static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh, + struct query_info* qinfo, uint16_t qflags, time_t leeway, int run, + int rpz_passthru, struct comm_reply* rep, struct edns_option* edns_list) +{ + struct mesh_state* s = NULL; + struct edns_option* opt = NULL; +#ifdef UNBOUND_DEBUG + struct rbnode_type* n; +#endif + if(!mesh_make_new_space(mesh, NULL)) { + verbose(VERB_ALGO, "Too many queries. dropped prefetch."); + mesh->stats_dropped ++; + return; + } + + s = mesh_state_create(mesh->env, qinfo, NULL, + qflags&(BIT_RD|BIT_CD), 0, 0); + if(!s) { + log_err("prefetch_subnet mesh_state_create: out of memory"); + return; + } + mesh_state_make_unique(s); + + opt = edns_opt_list_find(edns_list, mesh->env->cfg->client_subnet_opcode); + if(opt) { + /* Use the client's ECS data */ + if(!edns_opt_list_append(&s->s.edns_opts_front_in, opt->opt_code, + opt->opt_len, opt->opt_data, s->s.region)) { + log_err("prefetch_subnet edns_opt_list_append: out of memory"); + return; + } + } else { + /* Fake the ECS data from the client's IP */ + struct ecs_data ecs; + memset(&ecs, 0, sizeof(ecs)); + subnet_option_from_ss(&rep->addr, &ecs, mesh->env->cfg); + if(ecs.subnet_validdata == 0) { + log_err("prefetch_subnet subnet_option_from_ss: invalid data"); + return; + } + subnet_ecs_opt_list_append(&ecs, &s->s.edns_opts_front_in, &s->s); + if(!s->s.edns_opts_front_in) { + log_err("prefetch_subnet subnet_ecs_opt_list_append: out of memory"); + return; + } + } +#ifdef UNBOUND_DEBUG + n = +#else + (void) +#endif + rbtree_insert(&mesh->all, &s->node); + log_assert(n != NULL); + /* set detached (it is now) */ + mesh->num_detached_states++; + /* make it ignore the cache */ + sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region); + s->s.prefetch_leeway = leeway; + + if(s->list_select == mesh_no_list) { + /* move to either the forever or the jostle_list */ + if(mesh->num_forever_states < mesh->max_forever_states) { + mesh->num_forever_states ++; + mesh_list_insert(s, &mesh->forever_first, + &mesh->forever_last); + s->list_select = mesh_forever_list; + } else { + mesh_list_insert(s, &mesh->jostle_first, &mesh->jostle_last); s->list_select = mesh_jostle_list; } } + s->s.rpz_passthru = rpz_passthru; if(!run) { #ifdef UNBOUND_DEBUG @@ -755,11 +858,22 @@ static void mesh_schedule_prefetch(struct mesh_area* mesh, mesh_run(mesh, s, module_event_new, NULL); } +#endif /* CLIENT_SUBNET */ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, time_t leeway) + uint16_t qflags, time_t leeway, int rpz_passthru, + struct comm_reply* rep, struct edns_option* opt_list) { - mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1); + (void)opt_list; + (void)rep; +#ifdef CLIENT_SUBNET + if(rep) + mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1, + rpz_passthru, rep, opt_list); + else +#endif + mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1, + rpz_passthru); } void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, @@ -1234,7 +1348,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, (rep->security <= sec_status_bogus || rep->security == sec_status_secure_sentinel_fail)) { rcode = LDNS_RCODE_SERVFAIL; - if(m->s.env->cfg->stat_extended) + if(m->s.env->cfg->stat_extended) m->s.env->mesh->ans_bogus++; } if(rep && rep->security == sec_status_secure) @@ -1290,6 +1404,36 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, &r->edns, &r->query_reply, m->s.region, &r->start_time)) r->edns.opt_list_inplace_cb_out = NULL; } + /* Send along EDE BOGUS EDNS0 option when answer is bogus */ + if(m->s.env->cfg->ede && rcode == LDNS_RCODE_SERVFAIL && + m->s.env->need_to_validate && (!(r->qflags&BIT_CD) || + m->s.env->cfg->ignore_cd) && rep && + (rep->security <= sec_status_bogus || + rep->security == sec_status_secure_sentinel_fail)) { + char *reason = m->s.env->cfg->val_log_level >= 2 + ? errinf_to_str_bogus(&m->s) : NULL; + + /* During validation the EDE code can be received via two + * code paths. One code path fills the reply_info EDE, and + * the other fills it in the errinf_strlist. These paths + * intersect at some points, but where is opaque due to + * the complexity of the validator. At the time of writing + * we make the choice to prefer the EDE from errinf_strlist + * but a compelling reason to do otherwise is just as valid + */ + sldns_ede_code reason_bogus = errinf_to_reason_bogus(&m->s); + if ((reason_bogus == LDNS_EDE_DNSSEC_BOGUS && + rep->reason_bogus != LDNS_EDE_NONE) || + reason_bogus == LDNS_EDE_NONE) { + reason_bogus = rep->reason_bogus; + } + + if(reason_bogus != LDNS_EDE_NONE) { + edns_opt_list_append_ede(&r->edns.opt_list_out, + m->s.region, reason_bogus, reason); + } + free(reason); + } error_encode(r_buffer, rcode, &m->s.qinfo, r->qid, r->qflags, &r->edns); m->reply_list = NULL; @@ -1313,6 +1457,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time)) r->edns.opt_list_inplace_cb_out = NULL; + /* internal server error (probably malloc failure) so no + * EDE (RFC8914) needed */ error_encode(r_buffer, LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, r->qflags, &r->edns); } @@ -1524,7 +1670,7 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, struct comm_reply* rep, uint16_t qid, uint16_t qflags, const struct query_info* qinfo) { - struct mesh_reply* r = regional_alloc(s->s.region, + struct mesh_reply* r = regional_alloc(s->s.region, sizeof(struct mesh_reply)); if(!r) return 0; @@ -1693,6 +1839,7 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, if(mstate->s.curmod == 0) { struct query_info* qinfo = NULL; uint16_t qflags; + int rpz_p = 0; mesh_query_done(mstate); mesh_walk_supers(mesh, mstate); @@ -1701,13 +1848,15 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, * from an external DNS server, we'll need to schedule * a prefetch after removing the current state, so * we need to make a copy of the query info here. */ - if(mstate->s.need_refetch) + if(mstate->s.need_refetch) { mesh_copy_qinfo(mstate, &qinfo, &qflags); + rpz_p = mstate->s.rpz_passthru; + } mesh_state_delete(&mstate->s); if(qinfo) { mesh_schedule_prefetch(mesh, qinfo, qflags, - 0, 1); + 0, 1, rpz_p); } return 0; } @@ -1917,7 +2066,7 @@ apply_respip_action(struct module_qstate* qstate, return 1; if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo, - alias_rrset, 0, qstate->region, az)) + alias_rrset, 0, qstate->region, az, NULL)) return 0; /* xxx_deny actions mean dropping the reply, unless the original reply @@ -2042,6 +2191,14 @@ mesh_serve_expired_callback(void* arg) } } + /* Add EDE Stale Answer (RCF8914). Ignore global ede as this is + * warning instead of an error */ + if (r->edns.edns_present && qstate->env->cfg->ede_serve_expired && + qstate->env->cfg->ede) { + edns_opt_list_append_ede(&r->edns.opt_list_out, + mstate->s.region, LDNS_EDE_STALE_ANSWER, NULL); + } + r_buffer = r->query_reply.c->buffer; if(r->query_reply.c->tcp_req_info) r_buffer = r->query_reply.c->tcp_req_info->spool_buffer; diff --git a/usr.sbin/unbound/services/mesh.h b/usr.sbin/unbound/services/mesh.h index d0a4b5fb3d0..3be9b63faed 100644 --- a/usr.sbin/unbound/services/mesh.h +++ b/usr.sbin/unbound/services/mesh.h @@ -296,10 +296,13 @@ void mesh_delete(struct mesh_area* mesh); * @param edns: edns data from client query. * @param rep: where to reply to. * @param qid: query id to reply with. + * @param rpz_passthru: if true, the rpz passthru was previously found and + * further rpz processing is stopped. */ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, struct respip_client_info* cinfo, uint16_t qflags, - struct edns_data* edns, struct comm_reply* rep, uint16_t qid); + struct edns_data* edns, struct comm_reply* rep, uint16_t qid, + int rpz_passthru); /** * New query with callback. Create new query state if needed, and @@ -314,11 +317,13 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, * @param qid: query id to reply with. * @param cb: callback function. * @param cb_arg: callback user arg. + * @param rpz_passthru: if true, the rpz passthru was previously found and + * further rpz processing is stopped. * @return 0 on error. */ int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, struct edns_data* edns, struct sldns_buffer* buf, - uint16_t qid, mesh_cb_func_type cb, void* cb_arg); + uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru); /** * New prefetch message. Create new query state if needed. @@ -328,9 +333,15 @@ int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, * @param qinfo: query from client. * @param qflags: flags from client query. * @param leeway: TTL leeway what to expire earlier for this update. + * @param rpz_passthru: if true, the rpz passthru was previously found and + * further rpz processing is stopped. + * @param rep: comm_reply for the client; to be used when subnet is enabled. + * @param opt_list: edns opt_list from the client; to be used when subnet is + * enabled. */ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, time_t leeway); + uint16_t qflags, time_t leeway, int rpz_passthru, + struct comm_reply* rep, struct edns_option* opt_list); /** * Handle new event from the wire. A serviced query has returned. diff --git a/usr.sbin/unbound/services/outside_network.c b/usr.sbin/unbound/services/outside_network.c index a7e5fa3ad58..ec37a4a80d7 100644 --- a/usr.sbin/unbound/services/outside_network.c +++ b/usr.sbin/unbound/services/outside_network.c @@ -1994,6 +1994,9 @@ static int udp_connect_needs_log(int err) # endif # ifdef ENETDOWN case ENETDOWN: +# endif +# ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: # endif case EPERM: case EACCES: @@ -2294,7 +2297,7 @@ reuse_tcp_select_id(struct reuse_tcp* reuse, struct outside_network* outnet) node = rbtree_first(&reuse->tree_by_id); log_assert(node && node != RBTREE_NULL); /* tree not empty */ /* see if select is before first node */ - if(select < tree_by_id_get_id(node)) + if(select < (unsigned)tree_by_id_get_id(node)) return select; count += tree_by_id_get_id(node); /* perhaps select is between nodes */ diff --git a/usr.sbin/unbound/sldns/parseutil.c b/usr.sbin/unbound/sldns/parseutil.c index ba71df55d50..dd1f3348466 100644 --- a/usr.sbin/unbound/sldns/parseutil.c +++ b/usr.sbin/unbound/sldns/parseutil.c @@ -209,11 +209,13 @@ sldns_hexdigit_to_int(char ch) } uint32_t -sldns_str2period(const char *nptr, const char **endptr) +sldns_str2period(const char *nptr, const char **endptr, int* overflow) { int sign = 0; uint32_t i = 0; uint32_t seconds = 0; + const uint32_t maxint = 0xffffffff; + *overflow = 0; for(*endptr = nptr; **endptr; (*endptr)++) { switch (**endptr) { @@ -236,26 +238,46 @@ sldns_str2period(const char *nptr, const char **endptr) break; case 's': case 'S': + if(seconds > maxint-i) { + *overflow = 1; + return 0; + } seconds += i; i = 0; break; case 'm': case 'M': + if(i > maxint/60 || seconds > maxint-(i*60)) { + *overflow = 1; + return 0; + } seconds += i * 60; i = 0; break; case 'h': case 'H': + if(i > maxint/(60*60) || seconds > maxint-(i*60*60)) { + *overflow = 1; + return 0; + } seconds += i * 60 * 60; i = 0; break; case 'd': case 'D': + if(i > maxint/(60*60*24) || seconds > maxint-(i*60*60*24)) { + *overflow = 1; + return 0; + } seconds += i * 60 * 60 * 24; i = 0; break; case 'w': case 'W': + if(i > maxint/(60*60*24*7) || seconds > maxint-(i*60*60*24*7)) { + *overflow = 1; + return 0; + } seconds += i * 60 * 60 * 24 * 7; i = 0; break; @@ -269,15 +291,27 @@ sldns_str2period(const char *nptr, const char **endptr) case '7': case '8': case '9': + if(i > maxint/10 || i*10 > maxint - (**endptr - '0')) { + *overflow = 1; + return 0; + } i *= 10; i += (**endptr - '0'); break; default: + if(seconds > maxint-i) { + *overflow = 1; + return 0; + } seconds += i; /* disregard signedness */ return seconds; } } + if(seconds > maxint-i) { + *overflow = 1; + return 0; + } seconds += i; /* disregard signedness */ return seconds; diff --git a/usr.sbin/unbound/sldns/parseutil.h b/usr.sbin/unbound/sldns/parseutil.h index 208fd2fbca8..683f34e2307 100644 --- a/usr.sbin/unbound/sldns/parseutil.h +++ b/usr.sbin/unbound/sldns/parseutil.h @@ -74,9 +74,11 @@ struct tm * sldns_serial_arithmetics_gmtime_r(int32_t time, time_t now, struct t * converts a ttl value (like 5d2h) to a long. * \param[in] nptr the start of the string * \param[out] endptr points to the last char in case of error + * \param[out] overflow returns if the string causes integer overflow error, + * the number is too big, string of digits too long. * \return the convert duration value */ -uint32_t sldns_str2period(const char *nptr, const char **endptr); +uint32_t sldns_str2period(const char *nptr, const char **endptr, int* overflow); /** * Returns the int value of the given (hex) digit diff --git a/usr.sbin/unbound/sldns/rrdef.h b/usr.sbin/unbound/sldns/rrdef.h index 42d5de064ee..999c223074e 100644 --- a/usr.sbin/unbound/sldns/rrdef.h +++ b/usr.sbin/unbound/sldns/rrdef.h @@ -435,10 +435,42 @@ enum sldns_enum_edns_option LDNS_EDNS_CLIENT_SUBNET = 8, /* RFC7871 */ LDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/ LDNS_EDNS_PADDING = 12, /* RFC7830 */ + LDNS_EDNS_EDE = 15, /* RFC8914 */ LDNS_EDNS_CLIENT_TAG = 16 /* draft-bellis-dnsop-edns-tags-01 */ }; typedef enum sldns_enum_edns_option sldns_edns_option; +enum sldns_enum_ede_code +{ + LDNS_EDE_NONE = -1, /* EDE undefined for internal use */ + LDNS_EDE_OTHER = 0, + LDNS_EDE_UNSUPPORTED_DNSKEY_ALG = 1, + LDNS_EDE_UNSUPPORTED_DS_DIGEST = 2, + LDNS_EDE_STALE_ANSWER = 3, + LDNS_EDE_FORGED_ANSWER = 4, + LDNS_EDE_DNSSEC_INDETERMINATE = 5, + LDNS_EDE_DNSSEC_BOGUS = 6, + LDNS_EDE_SIGNATURE_EXPIRED = 7, + LDNS_EDE_SIGNATURE_NOT_YET_VALID = 8, + LDNS_EDE_DNSKEY_MISSING = 9, + LDNS_EDE_RRSIGS_MISSING = 10, + LDNS_EDE_NO_ZONE_KEY_BIT_SET = 11, + LDNS_EDE_NSEC_MISSING = 12, + LDNS_EDE_CACHED_ERROR = 13, + LDNS_EDE_NOT_READY = 14, + LDNS_EDE_BLOCKED = 15, + LDNS_EDE_CENSORED = 16, + LDNS_EDE_FILTERED = 17, + LDNS_EDE_PROHIBITED = 18, + LDNS_EDE_STALE_NXDOMAIN_ANSWER = 19, + LDNS_EDE_NOT_AUTHORITATIVE = 20, + LDNS_EDE_NOT_SUPPORTED = 21, + LDNS_EDE_NO_REACHABLE_AUTHORITY = 22, + LDNS_EDE_NETWORK_ERROR = 23, + LDNS_EDE_INVALID_DATA = 24, +}; +typedef enum sldns_enum_ede_code sldns_ede_code; + #define LDNS_EDNS_MASK_DO_BIT 0x8000 /** TSIG and TKEY extended rcodes (16bit), 0-15 are the normal rcodes. */ diff --git a/usr.sbin/unbound/sldns/str2wire.c b/usr.sbin/unbound/sldns/str2wire.c index d2cefae1c09..303d49ba668 100644 --- a/usr.sbin/unbound/sldns/str2wire.c +++ b/usr.sbin/unbound/sldns/str2wire.c @@ -249,11 +249,16 @@ rrinternal_get_ttl(sldns_buffer* strbuf, char* token, size_t token_len, int* not_there, uint32_t* ttl, uint32_t default_ttl) { const char* endptr; + int overflow; if(sldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) { return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TTL, sldns_buffer_position(strbuf)); } - *ttl = (uint32_t) sldns_str2period(token, &endptr); + *ttl = (uint32_t) sldns_str2period(token, &endptr, &overflow); + if(overflow) { + return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW, + sldns_buffer_position(strbuf)); + } if (strlen(token) > 0 && !isdigit((unsigned char)token[0])) { *not_there = 1; @@ -373,7 +378,8 @@ rrinternal_get_quoted(sldns_buffer* strbuf, const char** delimiters, /* skip spaces */ while(sldns_buffer_remaining(strbuf) > 0 && - *(sldns_buffer_current(strbuf)) == ' ') { + (*(sldns_buffer_current(strbuf)) == ' ' || + *(sldns_buffer_current(strbuf)) == '\t')) { sldns_buffer_skip(strbuf, 1); } @@ -606,7 +612,7 @@ sldns_affix_token(sldns_buffer* strbuf, char* token, size_t* token_len, /* add space */ /* when addlen < 2, the token buffer is full considering the NULL byte * from strlen and will lead to buffer overflow with the second - * assignement below. */ + * assignment below. */ if(addlen < 2) return 0; token[*token_strlen] = ' '; token[++(*token_strlen)] = 0; @@ -670,10 +676,10 @@ static int sldns_str2wire_check_svcbparams(uint8_t* rdata, uint16_t rdata_len) ,sldns_str2wire_svcparam_key_cmp); - /* The code below revolves around sematic errors in the SVCParam set. + /* The code below revolves around semantic errors in the SVCParam set. * So long as we do not distinguish between running Unbound as a primary * or as a secondary, we default to secondary behavior and we ignore the - * sematic errors. */ + * semantic errors. */ #ifdef SVCB_SEMANTIC_ERRORS { @@ -775,7 +781,8 @@ rrinternal_parse_rdata(sldns_buffer* strbuf, char* token, size_t token_len, /* unknown RR data */ if(token_strlen>=2 && strncmp(token, "\\#", 2) == 0 && - !quoted && (token_strlen == 2 || token[2]==' ')) { + !quoted && (token_strlen == 2 || token[2]==' ' || + token[2]=='\t')) { was_unknown_rr_format = 1; if((status=rrinternal_parse_unknown(strbuf, token, token_len, rr, rr_len, &rr_cur_len, @@ -1055,12 +1062,15 @@ int sldns_fp2wire_rr_buf(FILE* in, uint8_t* rr, size_t* len, size_t* dname_len, return s; } else if(strncmp(line, "$TTL", 4) == 0 && isspace((unsigned char)line[4])) { const char* end = NULL; + int overflow = 0; strlcpy((char*)rr, line, *len); *len = 0; *dname_len = 0; if(!parse_state) return LDNS_WIREPARSE_ERR_OK; parse_state->default_ttl = sldns_str2period( - sldns_strip_ws(line+5), &end); + sldns_strip_ws(line+5), &end, &overflow); + if(overflow) + return LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW; } else if (strncmp(line, "$INCLUDE", 8) == 0) { strlcpy((char*)rr, line, *len); *len = 0; @@ -1117,7 +1127,7 @@ sldns_str2wire_svcparam_key_lookup(const char *key, size_t key_len) if (!strncmp(key, "mandatory", sizeof("mandatory")-1)) return SVCB_KEY_MANDATORY; if (!strncmp(key, "echconfig", sizeof("echconfig")-1)) - return SVCB_KEY_ECH; /* allow "echconfig as well as "ech" */ + return SVCB_KEY_ECH; /* allow "echconfig" as well as "ech" */ break; case sizeof("alpn")-1: @@ -1356,7 +1366,7 @@ sldns_str2wire_svcbparam_mandatory(const char* val, uint8_t* rd, size_t* rd_len) */ qsort((void *)(rd + 4), count, sizeof(uint16_t), sldns_network_uint16_cmp); - /* The code below revolves around sematic errors in the SVCParam set. + /* The code below revolves around semantic errors in the SVCParam set. * So long as we do not distinguish between running Unbound as a primary * or as a secondary, we default to secondary behavior and we ignore the * semantic errors. */ @@ -1588,12 +1598,12 @@ static int sldns_str2wire_svcparam_buf(const char* str, uint8_t* rd, size_t* rd_ if (*val_in == '"') { val_in++; while (*val_in != '"' - && (unsigned)(val_out - unescaped_val + 1) < sizeof(unescaped_val) + && (size_t)(val_out - unescaped_val + 1) < sizeof(unescaped_val) && sldns_parse_char( (uint8_t*) val_out, &val_in)) { val_out++; } } else { - while ((unsigned)(val_out - unescaped_val + 1) < sizeof(unescaped_val) + while ((size_t)(val_out - unescaped_val + 1) < sizeof(unescaped_val) && sldns_parse_char( (uint8_t*) val_out, &val_in)) { val_out++; } @@ -2157,9 +2167,13 @@ int sldns_str2wire_tsigtime_buf(const char* str, uint8_t* rd, size_t* len) int sldns_str2wire_period_buf(const char* str, uint8_t* rd, size_t* len) { const char* end; - uint32_t p = sldns_str2period(str, &end); + int overflow; + uint32_t p = sldns_str2period(str, &end, &overflow); if(*end != 0) return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_PERIOD, end-str); + if(overflow) + return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW, + end-str); if(*len < 4) return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; sldns_write_uint32(rd, p); diff --git a/usr.sbin/unbound/sldns/wire2str.c b/usr.sbin/unbound/sldns/wire2str.c index b70efe299d4..d6fb289650b 100644 --- a/usr.sbin/unbound/sldns/wire2str.c +++ b/usr.sbin/unbound/sldns/wire2str.c @@ -194,6 +194,7 @@ static sldns_lookup_table sldns_edns_options_data[] = { { 8, "edns-client-subnet" }, { 11, "edns-tcp-keepalive"}, { 12, "Padding" }, + { 15, "EDE"}, { 0, NULL} }; sldns_lookup_table* sldns_edns_options = sldns_edns_options_data; diff --git a/usr.sbin/unbound/smallapp/unbound-checkconf.c b/usr.sbin/unbound/smallapp/unbound-checkconf.c index 55c3be35e35..4c428be27ef 100644 --- a/usr.sbin/unbound/smallapp/unbound-checkconf.c +++ b/usr.sbin/unbound/smallapp/unbound-checkconf.c @@ -408,6 +408,28 @@ interfacechecks(struct config_file* cfg) } } +/** check interface-automatic-ports */ +static void +ifautomaticportschecks(char* ifautomaticports) +{ + char* now = ifautomaticports; + while(now && *now) { + char* after; + int extraport; + while(isspace((unsigned char)*now)) + now++; + if(!*now) + break; + after = now; + extraport = (int)strtol(now, &after, 10); + if(extraport < 0 || extraport > 65535) + fatal_exit("interface-automatic-ports: port out of range at position %d in '%s'", (int)(now-ifautomaticports)+1, ifautomaticports); + if(extraport == 0 && now == after) + fatal_exit("interface-automatic-ports: parse error at position %d in '%s'", (int)(now-ifautomaticports)+1, ifautomaticports); + now = after; + } +} + /** check acl ips */ static void aclchecks(struct config_file* cfg) @@ -608,6 +630,7 @@ morechecks(struct config_file* cfg) warn_hosts("stub-host", cfg->stubs); warn_hosts("forward-host", cfg->forwards); interfacechecks(cfg); + ifautomaticportschecks(cfg->if_automatic_ports); aclchecks(cfg); tcpconnlimitchecks(cfg); diff --git a/usr.sbin/unbound/smallapp/unbound-control.c b/usr.sbin/unbound/smallapp/unbound-control.c index c7c38276f00..89543e45de7 100644 --- a/usr.sbin/unbound/smallapp/unbound-control.c +++ b/usr.sbin/unbound/smallapp/unbound-control.c @@ -155,9 +155,9 @@ usage(void) printf(" ratelimit_list [+a] list ratelimited domains\n"); printf(" ip_ratelimit_list [+a] list ratelimited ip addresses\n"); printf(" +a list all, also not ratelimited\n"); - printf(" list_auth_zones list auth zones\n"); - printf(" auth_zone_reload zone reload auth zone from zonefile\n"); - printf(" auth_zone_transfer zone transfer auth zone from master\n"); + printf(" list_auth_zones list auth zones (includes RPZ zones)\n"); + printf(" auth_zone_reload zone reload auth zone (or RPZ zone) from zonefile\n"); + printf(" auth_zone_transfer zone transfer auth zone (or RPZ zone) from master\n"); printf(" view_list_local_zones view list local-zones in view\n"); printf(" view_list_local_data view list local-data RRs in view\n"); printf(" view_local_zone view name type add local-zone in view\n"); @@ -444,7 +444,7 @@ static void do_stats_shm(struct config_file* cfg, struct ub_stats_info* stats, #endif /* HAVE_SHMGET */ /** print statistics from shm memory segment */ -static void print_stats_shm(const char* cfgfile) +static void print_stats_shm(const char* cfgfile, int quiet) { #ifdef HAVE_SHMGET struct config_file* cfg; @@ -474,8 +474,11 @@ static void print_stats_shm(const char* cfgfile) fatal_exit("shmat(%d): %s", id_arr, strerror(errno)); } - /* print the stats */ - do_stats_shm(cfg, stats, shm_stat); + + if(!quiet) { + /* print the stats */ + do_stats_shm(cfg, stats, shm_stat); + } /* shutdown */ shmdt(shm_stat); @@ -499,7 +502,7 @@ static void ssl_path_err(const char* s, const char *path) { unsigned long err; err = ERR_peek_error(); - if (ERR_GET_LIB(err) == ERR_LIB_SYS) { + if(ERR_GET_LIB(err) == ERR_LIB_SYS) { fprintf(stderr, "error: %s\n%s: %s\n", s, path, ERR_reason_error_string(err)); exit(1); @@ -541,11 +544,11 @@ setup_ctx(struct config_file* cfg) #endif if(!SSL_CTX_use_certificate_chain_file(ctx,c_cert)) ssl_path_err("Error setting up SSL_CTX client cert", c_cert); - if (!SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM)) + if(!SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM)) ssl_path_err("Error setting up SSL_CTX client key", c_key); - if (!SSL_CTX_check_private_key(ctx)) + if(!SSL_CTX_check_private_key(ctx)) ssl_err("Error setting up SSL_CTX client key"); - if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1) + if(SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1) ssl_path_err("Error setting up SSL_CTX verify, server cert", s_cert); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); @@ -880,8 +883,9 @@ go_cmd(SSL* ssl, int fd, int quiet, int argc, char* argv[]) if(first_line && strncmp(buf, "error", 5) == 0) { printf("%s", buf); was_error = 1; - } else if (!quiet) + } else if(!quiet) { printf("%s", buf); + } first_line = 0; } @@ -987,7 +991,7 @@ int main(int argc, char* argv[]) #endif } if(argc >= 1 && strcmp(argv[0], "stats_shm")==0) { - print_stats_shm(cfgfile); + print_stats_shm(cfgfile, quiet); return 0; } check_args_for_listcmd(argc, argv); diff --git a/usr.sbin/unbound/util/config_file.c b/usr.sbin/unbound/util/config_file.c index 15a090c7989..d7bd37a8890 100644 --- a/usr.sbin/unbound/util/config_file.c +++ b/usr.sbin/unbound/util/config_file.c @@ -195,6 +195,7 @@ config_create(void) cfg->use_systemd = 0; cfg->do_daemonize = 1; cfg->if_automatic = 0; + cfg->if_automatic_ports = NULL; cfg->so_rcvbuf = 0; cfg->so_sndbuf = 0; cfg->so_reuseport = REUSEPORT_DEFAULT; @@ -267,6 +268,7 @@ config_create(void) cfg->serve_expired_ttl_reset = 0; cfg->serve_expired_reply_ttl = 30; cfg->serve_expired_client_timeout = 0; + cfg->ede_serve_expired = 0; cfg->serve_original_ttl = 0; cfg->zonemd_permissive_mode = 0; cfg->add_holddown = 30*24*3600; @@ -375,6 +377,7 @@ config_create(void) cfg->ipset_name_v4 = NULL; cfg->ipset_name_v6 = NULL; #endif + cfg->ede = 0; return cfg; error_exit: config_delete(cfg); @@ -476,7 +479,7 @@ int config_set_option(struct config_file* cfg, const char* opt, else if(atoi(val) == 0) return 0; else cfg->stat_interval = atoi(val); - } else if(strcmp(opt, "num_threads:") == 0) { + } else if(strcmp(opt, "num-threads:") == 0) { /* not supported, library must have 1 thread in bgworker */ return 0; } else if(strcmp(opt, "outgoing-port-permit:") == 0) { @@ -543,6 +546,7 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_STR("ssl-cert-bundle:", tls_cert_bundle) else S_STR("tls-cert-bundle:", tls_cert_bundle) else S_YNO("tls-win-cert:", tls_win_cert) + else S_YNO("tls-system-cert:", tls_win_cert) else S_STRLIST("additional-ssl-port:", tls_additional_port) else S_STRLIST("additional-tls-port:", tls_additional_port) else S_STRLIST("tls-additional-ports:", tls_additional_port) @@ -559,6 +563,7 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_YNO("http-nodelay:", http_nodelay) else S_YNO("http-notls-downstream:", http_notls_downstream) else S_YNO("interface-automatic:", if_automatic) + else S_STR("interface-automatic-ports:", if_automatic_ports) else S_YNO("use-systemd:", use_systemd) else S_YNO("do-daemonize:", do_daemonize) else S_NUMBER_NONZERO("port:", port) @@ -668,6 +673,8 @@ int config_set_option(struct config_file* cfg, const char* opt, else if(strcmp(opt, "serve-expired-reply-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->serve_expired_reply_ttl = atoi(val); SERVE_EXPIRED_REPLY_TTL=(time_t)cfg->serve_expired_reply_ttl;} else S_NUMBER_OR_ZERO("serve-expired-client-timeout:", serve_expired_client_timeout) + else S_YNO("ede:", ede) + else S_YNO("ede-serve-expired:", ede_serve_expired) else S_YNO("serve-original-ttl:", serve_original_ttl) else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations) else S_YNO("zonemd-permissive-mode:", zonemd_permissive_mode) @@ -990,6 +997,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_IFC(opt, "interface", num_ifs, ifs) else O_IFC(opt, "outgoing-interface", num_out_ifs, out_ifs) else O_YNO(opt, "interface-automatic", if_automatic) + else O_STR(opt, "interface-automatic-ports", if_automatic_ports) else O_DEC(opt, "port", port) else O_DEC(opt, "outgoing-range", outgoing_num_ports) else O_DEC(opt, "outgoing-num-tcp", outgoing_num_tcp) @@ -1049,6 +1057,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_STR(opt, "ssl-cert-bundle", tls_cert_bundle) else O_STR(opt, "tls-cert-bundle", tls_cert_bundle) else O_YNO(opt, "tls-win-cert", tls_win_cert) + else O_YNO(opt, "tls-system-cert", tls_win_cert) else O_LST(opt, "additional-ssl-port", tls_additional_port) else O_LST(opt, "additional-tls-port", tls_additional_port) else O_LST(opt, "tls-additional-ports", tls_additional_port) @@ -1108,6 +1117,8 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "serve-expired-ttl-reset", serve_expired_ttl_reset) else O_DEC(opt, "serve-expired-reply-ttl", serve_expired_reply_ttl) else O_DEC(opt, "serve-expired-client-timeout", serve_expired_client_timeout) + else O_YNO(opt, "ede", ede) + else O_YNO(opt, "ede-serve-expired", ede_serve_expired) else O_YNO(opt, "serve-original-ttl", serve_original_ttl) else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations) else O_YNO(opt, "zonemd-permissive-mode", zonemd_permissive_mode) @@ -1534,6 +1545,7 @@ config_delete(struct config_file* cfg) free(cfg->directory); free(cfg->logfile); free(cfg->pidfile); + free(cfg->if_automatic_ports); free(cfg->target_fetch_policy); free(cfg->ssl_service_key); free(cfg->ssl_service_pem); @@ -2482,7 +2494,7 @@ char* cfg_ptr_reverse(char* str) while(*ip_end && isspace((unsigned char)*ip_end)) ip_end++; if(name>ip_end) { - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s", + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s", (int)(name-ip_end), ip_end); } snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " PTR %s", name); @@ -2553,126 +2565,6 @@ void w_config_adjust_directory(struct config_file* cfg) } #endif /* UB_ON_WINDOWS */ -void errinf(struct module_qstate* qstate, const char* str) -{ - struct config_strlist* p; - if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str) - return; - p = (struct config_strlist*)regional_alloc(qstate->region, sizeof(*p)); - if(!p) { - log_err("malloc failure in validator-error-info string"); - return; - } - p->next = NULL; - p->str = regional_strdup(qstate->region, str); - if(!p->str) { - log_err("malloc failure in validator-error-info string"); - return; - } - /* add at end */ - if(qstate->errinf) { - struct config_strlist* q = qstate->errinf; - while(q->next) - q = q->next; - q->next = p; - } else qstate->errinf = p; -} - -void errinf_origin(struct module_qstate* qstate, struct sock_list *origin) -{ - struct sock_list* p; - if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) - return; - for(p=origin; p; p=p->next) { - char buf[256]; - if(p == origin) - snprintf(buf, sizeof(buf), "from "); - else snprintf(buf, sizeof(buf), "and "); - if(p->len == 0) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), - "cache"); - else - addr_to_str(&p->addr, p->len, buf+strlen(buf), - sizeof(buf)-strlen(buf)); - errinf(qstate, buf); - } -} - -char* errinf_to_str_bogus(struct module_qstate* qstate) -{ - char buf[20480]; - char* p = buf; - size_t left = sizeof(buf); - struct config_strlist* s; - char dname[LDNS_MAX_DOMAINLEN+1]; - char t[16], c[16]; - sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t)); - sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c)); - dname_str(qstate->qinfo.qname, dname); - snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c); - left -= strlen(p); p += strlen(p); - if(!qstate->errinf) - snprintf(p, left, " misc failure"); - else for(s=qstate->errinf; s; s=s->next) { - snprintf(p, left, " %s", s->str); - left -= strlen(p); p += strlen(p); - } - p = strdup(buf); - if(!p) - log_err("malloc failure in errinf_to_str"); - return p; -} - -char* errinf_to_str_servfail(struct module_qstate* qstate) -{ - char buf[20480]; - char* p = buf; - size_t left = sizeof(buf); - struct config_strlist* s; - char dname[LDNS_MAX_DOMAINLEN+1]; - char t[16], c[16]; - sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t)); - sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c)); - dname_str(qstate->qinfo.qname, dname); - snprintf(p, left, "SERVFAIL <%s %s %s>:", dname, t, c); - left -= strlen(p); p += strlen(p); - if(!qstate->errinf) - snprintf(p, left, " misc failure"); - else for(s=qstate->errinf; s; s=s->next) { - snprintf(p, left, " %s", s->str); - left -= strlen(p); p += strlen(p); - } - p = strdup(buf); - if(!p) - log_err("malloc failure in errinf_to_str"); - return p; -} - -void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr) -{ - char buf[1024]; - char dname[LDNS_MAX_DOMAINLEN+1]; - char t[16], c[16]; - if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !rr) - return; - sldns_wire2str_type_buf(ntohs(rr->rk.type), t, sizeof(t)); - sldns_wire2str_class_buf(ntohs(rr->rk.rrset_class), c, sizeof(c)); - dname_str(rr->rk.dname, dname); - snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c); - errinf(qstate, buf); -} - -void errinf_dname(struct module_qstate* qstate, const char* str, uint8_t* dname) -{ - char b[1024]; - char buf[LDNS_MAX_DOMAINLEN+1]; - if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str || !dname) - return; - dname_str(dname, buf); - snprintf(b, sizeof(b), "%s %s", str, buf); - errinf(qstate, b); -} - int options_remote_is_address(struct config_file* cfg) { if(!cfg->remote_control_enable) return 0; diff --git a/usr.sbin/unbound/util/config_file.h b/usr.sbin/unbound/util/config_file.h index c7c9a0a48e6..0b457e3476b 100644 --- a/usr.sbin/unbound/util/config_file.h +++ b/usr.sbin/unbound/util/config_file.h @@ -41,6 +41,7 @@ #ifndef UTIL_CONFIG_FILE_H #define UTIL_CONFIG_FILE_H +#include "sldns/rrdef.h" struct config_stub; struct config_auth; struct config_view; @@ -205,6 +206,8 @@ struct config_file { /** automatic interface for incoming messages. Uses ipv6 remapping, * and recvmsg/sendmsg ancillary data to detect interfaces, boolean */ int if_automatic; + /** extra ports to open if if_automatic enabled, or NULL for default */ + char* if_automatic_ports; /** SO_RCVBUF size to set on port 53 UDP socket */ size_t so_rcvbuf; /** SO_SNDBUF size to set on port 53 UDP socket */ @@ -404,6 +407,8 @@ struct config_file { /** serve expired entries only after trying to update the entries and this * timeout (in milliseconds) is reached */ int serve_expired_client_timeout; + /** serve EDE code 3 - Stale Answer (RFC8914) for expired entries */ + int ede_serve_expired; /** serve original TTLs rather than decrementing ones */ int serve_original_ttl; /** nsec3 maximum iterations per key size, string */ @@ -677,6 +682,8 @@ struct config_file { char* ipset_name_v4; char* ipset_name_v6; #endif + /** respond with Extended DNS Errors (RFC8914) */ + int ede; }; /** from cfg username, after daemonize setup performed */ @@ -1239,56 +1246,6 @@ char* fname_after_chroot(const char* fname, struct config_file* cfg, */ char* cfg_ptr_reverse(char* str); -/** - * Append text to the error info for validation. - * @param qstate: query state. - * @param str: copied into query region and appended. - * Failures to allocate are logged. - */ -void errinf(struct module_qstate* qstate, const char* str); - -/** - * Append text to error info: from 1.2.3.4 - * @param qstate: query state. - * @param origin: sock list with origin of trouble. - * Every element added. - * If NULL: nothing is added. - * if 0len element: 'from cache' is added. - */ -void errinf_origin(struct module_qstate* qstate, struct sock_list *origin); - -/** - * Append text to error info: for RRset name type class - * @param qstate: query state. - * @param rr: rrset_key. - */ -void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr); - -/** - * Append text to error info: str dname - * @param qstate: query state. - * @param str: explanation string - * @param dname: the dname. - */ -void errinf_dname(struct module_qstate* qstate, const char* str, - uint8_t* dname); - -/** - * Create error info in string. For validation failures. - * @param qstate: query state. - * @return string or NULL on malloc failure (already logged). - * This string is malloced and has to be freed by caller. - */ -char* errinf_to_str_bogus(struct module_qstate* qstate); - -/** - * Create error info in string. For other servfails. - * @param qstate: query state. - * @return string or NULL on malloc failure (already logged). - * This string is malloced and has to be freed by caller. - */ -char* errinf_to_str_servfail(struct module_qstate* qstate); - /** * Used during options parsing */ diff --git a/usr.sbin/unbound/util/configlexer.lex b/usr.sbin/unbound/util/configlexer.lex index 34a0e5dd9cb..2d59fbc32e8 100644 --- a/usr.sbin/unbound/util/configlexer.lex +++ b/usr.sbin/unbound/util/configlexer.lex @@ -251,6 +251,7 @@ tls-port{COLON} { YDVAR(1, VAR_SSL_PORT) } ssl-cert-bundle{COLON} { YDVAR(1, VAR_TLS_CERT_BUNDLE) } tls-cert-bundle{COLON} { YDVAR(1, VAR_TLS_CERT_BUNDLE) } tls-win-cert{COLON} { YDVAR(1, VAR_TLS_WIN_CERT) } +tls-system-cert{COLON} { YDVAR(1, VAR_TLS_WIN_CERT) } additional-ssl-port{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) } additional-tls-port{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) } tls-additional-ports{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) } @@ -272,6 +273,7 @@ interface{COLON} { YDVAR(1, VAR_INTERFACE) } ip-address{COLON} { YDVAR(1, VAR_INTERFACE) } outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) } interface-automatic{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC) } +interface-automatic-ports{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC_PORTS) } so-rcvbuf{COLON} { YDVAR(1, VAR_SO_RCVBUF) } so-sndbuf{COLON} { YDVAR(1, VAR_SO_SNDBUF) } so-reuseport{COLON} { YDVAR(1, VAR_SO_REUSEPORT) } @@ -401,6 +403,7 @@ serve-expired-ttl{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL) } serve-expired-ttl-reset{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL_RESET) } serve-expired-reply-ttl{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_REPLY_TTL) } serve-expired-client-timeout{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_CLIENT_TIMEOUT) } +ede-serve-expired{COLON} { YDVAR(1, VAR_EDE_SERVE_EXPIRED) } serve-original-ttl{COLON} { YDVAR(1, VAR_SERVE_ORIGINAL_TTL) } fake-dsa{COLON} { YDVAR(1, VAR_FAKE_DSA) } fake-sha1{COLON} { YDVAR(1, VAR_FAKE_SHA1) } @@ -552,6 +555,7 @@ tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) } edns-client-string{COLON} { YDVAR(2, VAR_EDNS_CLIENT_STRING) } edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) } nsid{COLON} { YDVAR(1, VAR_NSID ) } +ede{COLON} { YDVAR(1, VAR_EDE ) } {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } /* Quoted strings. Strip leading and ending quotes */ diff --git a/usr.sbin/unbound/util/configparser.y b/usr.sbin/unbound/util/configparser.y index d4f965f9446..c003f335839 100644 --- a/usr.sbin/unbound/util/configparser.y +++ b/usr.sbin/unbound/util/configparser.y @@ -155,7 +155,8 @@ extern struct config_parser_state* cfg_parser; %token VAR_ACCESS_CONTROL_TAG_DATA VAR_VIEW VAR_ACCESS_CONTROL_VIEW %token VAR_VIEW_FIRST VAR_SERVE_EXPIRED VAR_SERVE_EXPIRED_TTL %token VAR_SERVE_EXPIRED_TTL_RESET VAR_SERVE_EXPIRED_REPLY_TTL -%token VAR_SERVE_EXPIRED_CLIENT_TIMEOUT VAR_SERVE_ORIGINAL_TTL VAR_FAKE_DSA +%token VAR_SERVE_EXPIRED_CLIENT_TIMEOUT VAR_EDE_SERVE_EXPIRED +%token VAR_SERVE_ORIGINAL_TTL VAR_FAKE_DSA %token VAR_FAKE_SHA1 VAR_LOG_IDENTITY VAR_HIDE_TRUSTANCHOR %token VAR_HIDE_HTTP_USER_AGENT VAR_HTTP_USER_AGENT %token VAR_TRUST_ANCHOR_SIGNALING VAR_AGGRESSIVE_NSEC VAR_USE_SYSTEMD @@ -188,7 +189,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_DYNLIB VAR_DYNLIB_FILE VAR_EDNS_CLIENT_STRING %token VAR_EDNS_CLIENT_STRING_OPCODE VAR_NSID %token VAR_ZONEMD_PERMISSIVE_MODE VAR_ZONEMD_CHECK VAR_ZONEMD_REJECT_ABSENCE -%token VAR_RPZ_SIGNAL_NXDOMAIN_RA +%token VAR_RPZ_SIGNAL_NXDOMAIN_RA VAR_INTERFACE_AUTOMATIC_PORTS VAR_EDE %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -292,7 +293,7 @@ content_server: server_num_threads | server_verbosity | server_port | server_serve_expired | server_serve_expired_ttl | server_serve_expired_ttl_reset | server_serve_expired_reply_ttl | server_serve_expired_client_timeout | - server_serve_original_ttl | server_fake_dsa | + server_ede_serve_expired | server_serve_original_ttl | server_fake_dsa | server_log_identity | server_use_systemd | server_response_ip_tag | server_response_ip | server_response_ip_data | server_shm_enable | server_shm_key | server_fake_sha1 | @@ -311,7 +312,8 @@ content_server: server_num_threads | server_verbosity | server_port | server_tls_use_sni | server_edns_client_string | server_edns_client_string_opcode | server_nsid | server_zonemd_permissive_mode | server_max_reuse_tcp_queries | - server_tcp_reuse_timeout | server_tcp_auth_query_timeout + server_tcp_reuse_timeout | server_tcp_auth_query_timeout | + server_interface_automatic_ports | server_ede ; stubstart: VAR_STUB_ZONE @@ -800,6 +802,13 @@ server_interface_automatic: VAR_INTERFACE_AUTOMATIC STRING_ARG free($2); } ; +server_interface_automatic_ports: VAR_INTERFACE_AUTOMATIC_PORTS STRING_ARG + { + OUTYY(("P(server_interface_automatic_ports:%s)\n", $2)); + free(cfg_parser->cfg->if_automatic_ports); + cfg_parser->cfg->if_automatic_ports = $2; + } + ; server_do_ip4: VAR_DO_IP4 STRING_ARG { OUTYY(("P(server_do_ip4:%s)\n", $2)); @@ -2026,6 +2035,15 @@ server_serve_expired_client_timeout: VAR_SERVE_EXPIRED_CLIENT_TIMEOUT STRING_ARG free($2); } ; +server_ede_serve_expired: VAR_EDE_SERVE_EXPIRED STRING_ARG + { + OUTYY(("P(server_ede_serve_expired:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->ede_serve_expired = (strcmp($2, "yes")==0); + free($2); + } + ; server_serve_original_ttl: VAR_SERVE_ORIGINAL_TTL STRING_ARG { OUTYY(("P(server_serve_original_ttl:%s)\n", $2)); @@ -2167,7 +2185,7 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG && strcmp($3, "noview")!=0 && strcmp($3, "inform")!=0 && strcmp($3, "inform_deny")!=0 && strcmp($3, "inform_redirect") != 0 - && strcmp($3, "ipset") != 0) { + && strcmp($3, "ipset") != 0) { yyerror("local-zone type: expected static, deny, " "refuse, redirect, transparent, " "typetransparent, inform, inform_deny, " @@ -2184,6 +2202,16 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($3); #ifdef USE_IPSET } else if(strcmp($3, "ipset")==0) { + size_t len = strlen($2); + /* Make sure to add the trailing dot. + * These are str compared to domain names. */ + if($2[len-1] != '.') { + if(!($2 = realloc($2, len+2))) { + fatal_exit("out of memory adding local-zone"); + } + $2[len] = '.'; + $2[len+1] = 0; + } if(!cfg_strlist_insert(&cfg_parser->cfg-> local_zones_ipset, $2)) fatal_exit("out of memory adding local-zone"); @@ -2713,7 +2741,15 @@ server_edns_client_string_opcode: VAR_EDNS_CLIENT_STRING_OPCODE STRING_ARG yyerror("option code must be in interval [0, 65535]"); else cfg_parser->cfg->edns_client_string_opcode = atoi($2); free($2); - + } + ; +server_ede: VAR_EDE STRING_ARG + { + OUTYY(("P(server_ede:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->ede = (strcmp($2, "yes")==0); + free($2); } ; stub_name: VAR_NAME STRING_ARG @@ -2982,6 +3018,16 @@ view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($3); #ifdef USE_IPSET } else if(strcmp($3, "ipset")==0) { + size_t len = strlen($2); + /* Make sure to add the trailing dot. + * These are str compared to domain names. */ + if($2[len-1] != '.') { + if(!($2 = realloc($2, len+2))) { + fatal_exit("out of memory adding local-zone"); + } + $2[len] = '.'; + $2[len+1] = 0; + } if(!cfg_strlist_insert(&cfg_parser->cfg->views-> local_zones_ipset, $2)) fatal_exit("out of memory adding local-zone"); diff --git a/usr.sbin/unbound/util/data/msgparse.c b/usr.sbin/unbound/util/data/msgparse.c index a600a8c6015..5bb69d6ed06 100644 --- a/usr.sbin/unbound/util/data/msgparse.c +++ b/usr.sbin/unbound/util/data/msgparse.c @@ -1157,7 +1157,7 @@ skip_pkt_rr(sldns_buffer* pkt) } /** skip RRs from packet */ -static int +int skip_pkt_rrs(sldns_buffer* pkt, int num) { int i; @@ -1235,3 +1235,4 @@ log_edns_opt_list(enum verbosity_value level, const char* info_str, } } } + diff --git a/usr.sbin/unbound/util/data/msgparse.h b/usr.sbin/unbound/util/data/msgparse.h index 4c0559a739a..0c458e6e8e2 100644 --- a/usr.sbin/unbound/util/data/msgparse.h +++ b/usr.sbin/unbound/util/data/msgparse.h @@ -293,6 +293,15 @@ int parse_packet(struct sldns_buffer* pkt, struct msg_parse* msg, int parse_extract_edns_from_response_msg(struct msg_parse* msg, struct edns_data* edns, struct regional* region); +/** + * Skip RRs from packet + * @param pkt: the packet. position at start must be right after the query + * section. At end, right after EDNS data or no movement if failed. + * @param num: Limit of the number of records we want to parse. + * @return: 0 on success, 1 on failure. + */ +int skip_pkt_rrs(struct sldns_buffer* pkt, int num); + /** * If EDNS data follows a query section, extract it and initialize edns struct. * @param pkt: the packet. position at start must be right after the query diff --git a/usr.sbin/unbound/util/data/msgreply.c b/usr.sbin/unbound/util/data/msgreply.c index ec46e472478..e3ee607b154 100644 --- a/usr.sbin/unbound/util/data/msgreply.c +++ b/usr.sbin/unbound/util/data/msgreply.c @@ -117,6 +117,7 @@ construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd, rep->ar_numrrsets = ar; rep->rrset_count = total; rep->security = sec; + rep->reason_bogus = LDNS_EDE_NONE; rep->authoritative = 0; /* array starts after the refs */ if(region) @@ -989,6 +990,36 @@ parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region, return rep; } +int edns_opt_list_append_ede(struct edns_option** list, struct regional* region, + sldns_ede_code code, const char *txt) +{ + struct edns_option** prevp; + struct edns_option* opt; + size_t txt_len = txt ? strlen(txt) : 0; + + /* allocate new element */ + opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); + if(!opt) + return 0; + opt->next = NULL; + opt->opt_code = LDNS_EDNS_EDE; + opt->opt_len = txt_len + sizeof(uint16_t); + opt->opt_data = regional_alloc(region, txt_len + sizeof(uint16_t)); + if(!opt->opt_data) + return 0; + sldns_write_uint16(opt->opt_data, (uint16_t)code); + if (txt_len) + memmove(opt->opt_data + 2, txt, txt_len); + + /* append at end of list */ + prevp = list; + while(*prevp != NULL) + prevp = &((*prevp)->next); + verbose(VERB_ALGO, "attached EDE code: %d with message: %s", code, txt); + *prevp = opt; + return 1; +} + int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, uint8_t* data, struct regional* region) { diff --git a/usr.sbin/unbound/util/data/msgreply.h b/usr.sbin/unbound/util/data/msgreply.h index 81c763fc7c3..9538adc5a8b 100644 --- a/usr.sbin/unbound/util/data/msgreply.h +++ b/usr.sbin/unbound/util/data/msgreply.h @@ -43,6 +43,7 @@ #define UTIL_DATA_MSGREPLY_H #include "util/storage/lruhash.h" #include "util/data/packed_rrset.h" +#include "sldns/rrdef.h" struct sldns_buffer; struct comm_reply; struct alloc_cache; @@ -167,6 +168,11 @@ struct reply_info { */ enum sec_status security; + /** + * EDE (rfc8914) code with reason for DNSSEC bogus status. + */ + sldns_ede_code reason_bogus; + /** * Number of RRsets in each section. * The answer section. Add up the RRs in every RRset to calculate @@ -528,7 +534,38 @@ void log_query_info(enum verbosity_value v, const char* str, * @return false on failure. */ int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, - uint8_t* data, struct regional* region); + uint8_t* data, struct regional* region); + +/** + * Append edns EDE option to edns options list + * @param LIST: the edns option list to append the edns option to. + * @param REGION: region to allocate the new edns option. + * @param CODE: the EDE code. + * @param TXT: Additional text for the option + */ +#define EDNS_OPT_LIST_APPEND_EDE(LIST, REGION, CODE, TXT) \ + do { \ + struct { \ + uint16_t code; \ + char text[sizeof(TXT) - 1]; \ + } ede = { htons(CODE), TXT }; \ + verbose(VERB_ALGO, "attached EDE code: %d with" \ + " message: %s", CODE, TXT); \ + edns_opt_list_append((LIST), LDNS_EDNS_EDE, \ + sizeof(uint16_t) + sizeof(TXT) - 1, \ + (void *)&ede, (REGION)); \ + } while(0) + +/** + * Append edns EDE option to edns options list + * @param list: the edns option list to append the edns option to. + * @param region: region to allocate the new edns option. + * @param code: the EDE code. + * @param txt: Additional text for the option + * @return false on failure. + */ +int edns_opt_list_append_ede(struct edns_option** list, struct regional* region, + sldns_ede_code code, const char *txt); /** * Remove any option found on the edns option list that matches the code. diff --git a/usr.sbin/unbound/util/module.c b/usr.sbin/unbound/util/module.c index f16583183bf..6698f94971b 100644 --- a/usr.sbin/unbound/util/module.c +++ b/usr.sbin/unbound/util/module.c @@ -40,6 +40,10 @@ #include "config.h" #include "util/module.h" #include "sldns/wire2str.h" +#include "util/config_file.h" +#include "util/regional.h" +#include "util/data/dname.h" +#include "util/net_help.h" const char* strextstate(enum module_ext_state s) @@ -71,6 +75,144 @@ strmodulevent(enum module_ev e) return "bad_event_value"; } +void errinf(struct module_qstate* qstate, const char* str) +{ + errinf_ede(qstate, str, LDNS_EDE_NONE); +} + +void errinf_ede(struct module_qstate* qstate, + const char* str, sldns_ede_code reason_bogus) +{ + struct errinf_strlist* p; + if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str) + return; + p = (struct errinf_strlist*)regional_alloc(qstate->region, sizeof(*p)); + if(!p) { + log_err("malloc failure in validator-error-info string"); + return; + } + p->next = NULL; + p->str = regional_strdup(qstate->region, str); + p->reason_bogus = reason_bogus; + if(!p->str) { + log_err("malloc failure in validator-error-info string"); + return; + } + /* add at end */ + if(qstate->errinf) { + struct errinf_strlist* q = qstate->errinf; + while(q->next) + q = q->next; + q->next = p; + } else qstate->errinf = p; +} + +void errinf_origin(struct module_qstate* qstate, struct sock_list *origin) +{ + struct sock_list* p; + if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) + return; + for(p=origin; p; p=p->next) { + char buf[256]; + if(p == origin) + snprintf(buf, sizeof(buf), "from "); + else snprintf(buf, sizeof(buf), "and "); + if(p->len == 0) + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), + "cache"); + else + addr_to_str(&p->addr, p->len, buf+strlen(buf), + sizeof(buf)-strlen(buf)); + errinf(qstate, buf); + } +} + +char* errinf_to_str_bogus(struct module_qstate* qstate) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct errinf_strlist* s; + char dname[LDNS_MAX_DOMAINLEN+1]; + char t[16], c[16]; + sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t)); + sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c)); + dname_str(qstate->qinfo.qname, dname); + snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c); + left -= strlen(p); p += strlen(p); + if(!qstate->errinf) + snprintf(p, left, " misc failure"); + else for(s=qstate->errinf; s; s=s->next) { + snprintf(p, left, " %s", s->str); + left -= strlen(p); p += strlen(p); + } + p = strdup(buf); + if(!p) + log_err("malloc failure in errinf_to_str"); + return p; +} + +sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate) +{ + struct errinf_strlist* s; + for(s=qstate->errinf; s; s=s->next) { + if (s->reason_bogus != LDNS_EDE_NONE) { + return s->reason_bogus; + } + } + return LDNS_EDE_NONE; +} + +char* errinf_to_str_servfail(struct module_qstate* qstate) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct errinf_strlist* s; + char dname[LDNS_MAX_DOMAINLEN+1]; + char t[16], c[16]; + sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t)); + sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c)); + dname_str(qstate->qinfo.qname, dname); + snprintf(p, left, "SERVFAIL <%s %s %s>:", dname, t, c); + left -= strlen(p); p += strlen(p); + if(!qstate->errinf) + snprintf(p, left, " misc failure"); + else for(s=qstate->errinf; s; s=s->next) { + snprintf(p, left, " %s", s->str); + left -= strlen(p); p += strlen(p); + } + p = strdup(buf); + if(!p) + log_err("malloc failure in errinf_to_str"); + return p; +} + +void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr) +{ + char buf[1024]; + char dname[LDNS_MAX_DOMAINLEN+1]; + char t[16], c[16]; + if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !rr) + return; + sldns_wire2str_type_buf(ntohs(rr->rk.type), t, sizeof(t)); + sldns_wire2str_class_buf(ntohs(rr->rk.rrset_class), c, sizeof(c)); + dname_str(rr->rk.dname, dname); + snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c); + errinf(qstate, buf); +} + +void errinf_dname(struct module_qstate* qstate, const char* str, uint8_t* dname) +{ + char b[1024]; + char buf[LDNS_MAX_DOMAINLEN+1]; + if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str || !dname) + return; + dname_str(dname, buf); + snprintf(b, sizeof(b), "%s %s", str, buf); + errinf(qstate, b); +} + int edns_known_options_init(struct module_env* env) { diff --git a/usr.sbin/unbound/util/module.h b/usr.sbin/unbound/util/module.h index 7a548003397..33068a71ce6 100644 --- a/usr.sbin/unbound/util/module.h +++ b/usr.sbin/unbound/util/module.h @@ -187,6 +187,15 @@ struct respip_addr_info; /** Maximum number of known edns options */ #define MAX_KNOWN_EDNS_OPTS 256 +struct errinf_strlist { + /** next item in list */ + struct errinf_strlist* next; + /** config option string */ + char* str; + /** EDE code companion to the error str */ + int reason_bogus; +}; + enum inplace_cb_list_type { /* Inplace callbacks for when a resolved reply is ready to be sent to the * front.*/ @@ -624,8 +633,7 @@ struct module_qstate { /** region for this query. Cleared when query process finishes. */ struct regional* region; /** failure reason information if val-log-level is high */ - struct config_strlist* errinf; - + struct errinf_strlist* errinf; /** which module is executing */ int curmod; /** module states */ @@ -667,6 +675,8 @@ struct module_qstate { /** Extended result of response-ip action processing, mainly * for logging purposes. */ struct respip_action_info* respip_action_info; + /** if the query is rpz passthru, no further rpz processing for it */ + int rpz_passthru; /** whether the reply should be dropped */ int is_drop; @@ -759,6 +769,65 @@ const char* strextstate(enum module_ext_state s); */ const char* strmodulevent(enum module_ev e); +/** + * Append text to the error info for validation. + * @param qstate: query state. + * @param str: copied into query region and appended. + * Failures to allocate are logged. + */ +void errinf(struct module_qstate* qstate, const char* str); +void errinf_ede(struct module_qstate* qstate, const char* str, + sldns_ede_code reason_bogus); + +/** + * Append text to error info: from 1.2.3.4 + * @param qstate: query state. + * @param origin: sock list with origin of trouble. + * Every element added. + * If NULL: nothing is added. + * if 0len element: 'from cache' is added. + */ +void errinf_origin(struct module_qstate* qstate, struct sock_list *origin); + +/** + * Append text to error info: for RRset name type class + * @param qstate: query state. + * @param rr: rrset_key. + */ +void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr); + +/** + * Append text to error info: str dname + * @param qstate: query state. + * @param str: explanation string + * @param dname: the dname. + */ +void errinf_dname(struct module_qstate* qstate, const char* str, + uint8_t* dname); + +/** + * Create error info in string. For validation failures. + * @param qstate: query state. + * @return string or NULL on malloc failure (already logged). + * This string is malloced and has to be freed by caller. + */ +char* errinf_to_str_bogus(struct module_qstate* qstate); +/** + * Check the sldns_ede_code of the qstate. + * @param qstate: query state. + * @return LDNS_EDE_DNSSEC_BOGUS by default, or the first explicitly set + * sldns_ede_code. + */ +sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate); + +/** + * Create error info in string. For other servfails. + * @param qstate: query state. + * @return string or NULL on malloc failure (already logged). + * This string is malloced and has to be freed by caller. + */ +char* errinf_to_str_servfail(struct module_qstate* qstate); + /** * Initialize the edns known options by allocating the required space. * @param env: the module environment. diff --git a/usr.sbin/unbound/util/net_help.c b/usr.sbin/unbound/util/net_help.c index 8bef5689050..114920e3f90 100644 --- a/usr.sbin/unbound/util/net_help.c +++ b/usr.sbin/unbound/util/net_help.c @@ -1271,7 +1271,13 @@ void* connect_sslctx_create(char* key, char* pem, char* verifypem, int wincert) } } #else - (void)wincert; + if(wincert) { + if(!SSL_CTX_set_default_verify_paths(ctx)) { + log_crypto_err("error in default_verify_paths"); + SSL_CTX_free(ctx); + return NULL; + } + } #endif SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); } diff --git a/usr.sbin/unbound/util/netevent.c b/usr.sbin/unbound/util/netevent.c index b9b7230e41a..841e0978738 100644 --- a/usr.sbin/unbound/util/netevent.c +++ b/usr.sbin/unbound/util/netevent.c @@ -1209,23 +1209,22 @@ squelch_err_ssl_handshake(unsigned long err) { if(verbosity >= VERB_QUERY) return 0; /* only squelch on low verbosity */ - /* this is very specific, we could filter on ERR_GET_REASON() - * (the third element in ERR_PACK) */ - if(err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_HTTPS_PROXY_REQUEST) || - err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_HTTP_REQUEST) || - err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_WRONG_VERSION_NUMBER) || - err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_READ_BYTES, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE) + if(ERR_GET_LIB(err) == ERR_LIB_SSL && + (ERR_GET_REASON(err) == SSL_R_HTTPS_PROXY_REQUEST || + ERR_GET_REASON(err) == SSL_R_HTTP_REQUEST || + ERR_GET_REASON(err) == SSL_R_WRONG_VERSION_NUMBER || + ERR_GET_REASON(err) == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE #ifdef SSL_F_TLS_POST_PROCESS_CLIENT_HELLO - || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_POST_PROCESS_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER) + || ERR_GET_REASON(err) == SSL_R_NO_SHARED_CIPHER #endif #ifdef SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO - || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL) - || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNSUPPORTED_PROTOCOL) + || ERR_GET_REASON(err) == SSL_R_UNKNOWN_PROTOCOL + || ERR_GET_REASON(err) == SSL_R_UNSUPPORTED_PROTOCOL # ifdef SSL_R_VERSION_TOO_LOW - || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_VERSION_TOO_LOW) + || ERR_GET_REASON(err) == SSL_R_VERSION_TOO_LOW # endif #endif - ) + )) return 1; return 0; } @@ -1278,6 +1277,12 @@ ssl_handshake(struct comm_point* c) if(errno == ECONNRESET && verbosity < 2) return 0; /* silence reset by peer */ #endif + if(!tcp_connect_errno_needs_log( + (struct sockaddr*)&c->repinfo.addr, + c->repinfo.addrlen)) + return 0; /* silence connect failures that + show up because after connect this is the + first system call that accesses the socket */ if(errno != 0) log_err("SSL_handshake syscall: %s", strerror(errno)); @@ -2480,7 +2485,7 @@ http_nonchunk_segment(struct comm_point* c) remainbufferlen = sldns_buffer_capacity(c->buffer) - sldns_buffer_limit(c->buffer); if(remainbufferlen+got_now >= c->tcp_byte_count || - remainbufferlen >= (c->ssl?16384:2048)) { + remainbufferlen >= (size_t)(c->ssl?16384:2048)) { size_t total = sldns_buffer_limit(c->buffer); sldns_buffer_clear(c->buffer); sldns_buffer_set_position(c->buffer, total); diff --git a/usr.sbin/unbound/validator/autotrust.c b/usr.sbin/unbound/validator/autotrust.c index 55e82c176da..3cdf9ceae85 100644 --- a/usr.sbin/unbound/validator/autotrust.c +++ b/usr.sbin/unbound/validator/autotrust.c @@ -1203,13 +1203,8 @@ void autr_write_file(struct module_env* env, struct trust_anchor* tp) #else llvalue = (unsigned long long)tp; #endif -#ifndef USE_WINSOCK - snprintf(tempf, sizeof(tempf), "%s.%d-%d-%llx", fname, (int)getpid(), + snprintf(tempf, sizeof(tempf), "%s.%d-%d-" ARG_LL "x", fname, (int)getpid(), env->worker?*(int*)env->worker:0, llvalue); -#else - snprintf(tempf, sizeof(tempf), "%s.%d-%d-%I64x", fname, (int)getpid(), - env->worker?*(int*)env->worker:0, llvalue); -#endif #endif /* S_SPLINT_S */ verbose(VERB_ALGO, "autotrust: write to disk: %s", tempf); out = fopen(tempf, "w"); @@ -1268,7 +1263,7 @@ verify_dnskey(struct module_env* env, struct val_env* ve, int downprot = env->cfg->harden_algo_downgrade; enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset, tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason, - qstate); + NULL, qstate); /* sigalg is ignored, it returns algorithms signalled to exist, but * in 5011 there are no other rrsets to check. if downprot is * enabled, then it checks that the DNSKEY is signed with all @@ -1317,7 +1312,7 @@ rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve, /* no algorithm downgrade protection necessary, if it is selfsigned * revoked it can be removed. */ sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i, - &reason, LDNS_SECTION_ANSWER, qstate); + &reason, NULL, LDNS_SECTION_ANSWER, qstate); return (sec == sec_status_secure); } @@ -2397,7 +2392,7 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp) qinfo.qclass); if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &probe_answer_cb, env)) { + &probe_answer_cb, env, 0)) { log_err("out of memory making 5011 probe"); } } diff --git a/usr.sbin/unbound/validator/val_nsec.c b/usr.sbin/unbound/validator/val_nsec.c index a4e5b3137db..876bfab6dbb 100644 --- a/usr.sbin/unbound/validator/val_nsec.c +++ b/usr.sbin/unbound/validator/val_nsec.c @@ -187,7 +187,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, - LDNS_SECTION_AUTHORITY, qstate); + NULL, LDNS_SECTION_AUTHORITY, qstate); if(d->security == sec_status_secure) { rrset_update_sec_status(env->rrset_cache, nsec, *env->now); return 1; diff --git a/usr.sbin/unbound/validator/val_nsec3.c b/usr.sbin/unbound/validator/val_nsec3.c index 763b5ab7c76..a2b3794f601 100644 --- a/usr.sbin/unbound/validator/val_nsec3.c +++ b/usr.sbin/unbound/validator/val_nsec3.c @@ -1289,7 +1289,8 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, static int list_is_secure(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct key_entry_key* kkey, char** reason, struct module_qstate* qstate) + struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, + struct module_qstate* qstate) { struct packed_rrset_data* d; size_t i; @@ -1303,7 +1304,7 @@ 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, LDNS_SECTION_AUTHORITY, qstate); + reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate); if(d->security != sec_status_secure) { verbose(VERB_ALGO, "NSEC3 did not verify"); return 0; @@ -1317,7 +1318,7 @@ 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, - struct module_qstate* qstate) + sldns_ede_code* reason_bogus, struct module_qstate* qstate) { rbtree_type ct; struct nsec3_filter flt; @@ -1330,8 +1331,10 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, *reason = "no valid NSEC3s"; return sec_status_bogus; /* no valid NSEC3s, bogus */ } - if(!list_is_secure(env, ve, list, num, kkey, reason, qstate)) + if(!list_is_secure(env, ve, list, num, kkey, reason, reason_bogus, qstate)) { + *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) { diff --git a/usr.sbin/unbound/validator/val_nsec3.h b/usr.sbin/unbound/validator/val_nsec3.h index 7fd37c16905..7676fc8b282 100644 --- a/usr.sbin/unbound/validator/val_nsec3.h +++ b/usr.sbin/unbound/validator/val_nsec3.h @@ -68,6 +68,7 @@ #define VALIDATOR_VAL_NSEC3_H #include "util/rbtree.h" #include "util/data/packed_rrset.h" +#include "sldns/rrdef.h" struct val_env; struct regional; struct module_env; @@ -186,6 +187,7 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, * @param qinfo: query that is verified for. * @param kkey: key entry that signed the NSEC3s. * @param reason: string for bogus result. + * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, @@ -197,7 +199,7 @@ 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, - struct module_qstate* qstate); + sldns_ede_code* reason_bogus, struct module_qstate* qstate); /** * Prove NXDOMAIN or NODATA. diff --git a/usr.sbin/unbound/validator/val_sigcrypt.c b/usr.sbin/unbound/validator/val_sigcrypt.c index b15fba3f499..d5f16b11f81 100644 --- a/usr.sbin/unbound/validator/val_sigcrypt.c +++ b/usr.sbin/unbound/validator/val_sigcrypt.c @@ -525,11 +525,19 @@ int algo_needs_missing(struct algo_needs* n) return 0; } +static enum sec_status +dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, + time_t now, struct ub_packed_rrset_key* rrset, + 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); + 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_pkt_section section, - struct module_qstate* qstate) + uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, + sldns_pkt_section section, struct module_qstate* qstate) { enum sec_status sec; size_t i, num; @@ -543,6 +551,8 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, verbose(VERB_QUERY, "rrset failed to verify due to a lack of " "signatures"); *reason = "no signatures"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_RRSIGS_MISSING; return sec_status_bogus; } @@ -551,12 +561,15 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, if(algo_needs_num_missing(&needs) == 0) { verbose(VERB_QUERY, "zone has no known algorithms"); *reason = "zone has no known algorithms"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG; return sec_status_insecure; } } for(i=0; inow, rrset, - dnskey, i, &sortree, reason, section, qstate); + dnskey, i, &sortree, reason, reason_bogus, + section, qstate); /* see which algorithm has been fixed up */ if(sec == sec_status_secure) { if(!sigalg) @@ -597,8 +610,8 @@ void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s) enum sec_status dnskey_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - size_t dnskey_idx, char** reason, sldns_pkt_section section, - struct module_qstate* qstate) + size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus, + sldns_pkt_section section, struct module_qstate* qstate) { enum sec_status sec; size_t i, num, numchecked = 0; @@ -612,6 +625,8 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, verbose(VERB_QUERY, "rrset failed to verify due to a lack of " "signatures"); *reason = "no signatures"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_RRSIGS_MISSING; return sec_status_bogus; } for(i=0; iscratch, + sec = dnskey_verify_rrset_sig(env->scratch, env->scratch_buffer, ve, *env->now, rrset, dnskey, dnskey_idx, i, &sortree, &buf_canon, reason, - section, qstate); + reason_bogus, section, qstate); if(sec == sec_status_secure) return sec; numchecked ++; @@ -633,12 +648,13 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, return sec_status_bogus; } -enum sec_status -dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, - time_t now, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_type** sortree, char** reason, sldns_pkt_section section, - struct module_qstate* qstate) +static enum sec_status +dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, + time_t now, struct ub_packed_rrset_key* rrset, + 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) { /* find matching keys and check them */ enum sec_status sec = sec_status_bogus; @@ -649,10 +665,12 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, int buf_canon = 0; verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo); if(!dnskey_algo_id_is_supported(algo)) { + if(reason_bogus) + *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG; verbose(VERB_QUERY, "verify sig: unknown algorithm"); return sec_status_insecure; } - + for(i=0; iscratch, - env->scratch_buffer, ve, now, rrset, dnskey, i, - sig_idx, sortree, &buf_canon, reason, section, qstate); + sec = dnskey_verify_rrset_sig(env->scratch, + env->scratch_buffer, ve, now, rrset, dnskey, i, + sig_idx, sortree, &buf_canon, reason, reason_bogus, + section, qstate); if(sec == sec_status_secure) return sec; } if(numchecked == 0) { *reason = "signatures from unknown keys"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSKEY_MISSING; verbose(VERB_QUERY, "verify: could not find appropriate key"); return sec_status_bogus; } @@ -1361,8 +1382,8 @@ subtract_1982(uint32_t a, uint32_t b) /** check rrsig dates */ static int -check_dates(struct val_env* ve, uint32_t unow, - uint8_t* expi_p, uint8_t* incep_p, char** reason) +check_dates(struct val_env* ve, uint32_t unow, uint8_t* expi_p, + uint8_t* incep_p, char** reason, sldns_ede_code *reason_bogus) { /* read out the dates */ uint32_t expi, incep, now; @@ -1386,6 +1407,14 @@ check_dates(struct val_env* ve, uint32_t unow, sigdate_error("verify: inception after expiration, " "signature bad", expi, incep, now); *reason = "signature inception after expiration"; + if(reason_bogus){ + /* from RFC8914 on Signature Not Yet Valid: The resolver + * attempted to perform DNSSEC validation, but no + * signatures are presently valid and at least some are + * not yet valid. */ + *reason_bogus = LDNS_EDE_SIGNATURE_NOT_YET_VALID; + } + return 0; } if(compare_1982(incep, now) > 0) { @@ -1397,6 +1426,8 @@ check_dates(struct val_env* ve, uint32_t unow, sigdate_error("verify: signature bad, current time is" " before inception date", expi, incep, now); *reason = "signature before inception date"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_SIGNATURE_NOT_YET_VALID; return 0; } sigdate_error("verify warning suspicious signature inception " @@ -1410,6 +1441,8 @@ check_dates(struct val_env* ve, uint32_t unow, sigdate_error("verify: signature expired", expi, incep, now); *reason = "signature expired"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_SIGNATURE_EXPIRED; return 0; } sigdate_error("verify warning suspicious signature expiration " @@ -1473,7 +1506,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, - struct rbtree_type** sortree, int* buf_canon, char** reason, + struct rbtree_type** sortree, int* buf_canon, + char** reason, sldns_ede_code *reason_bogus, sldns_pkt_section section, struct module_qstate* qstate) { enum sec_status sec; @@ -1492,12 +1526,16 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, if(siglen < 2+20) { verbose(VERB_QUERY, "verify: signature too short"); *reason = "signature too short"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; } if(!(dnskey_get_flags(dnskey, dnskey_idx) & DNSKEY_BIT_ZSK)) { verbose(VERB_QUERY, "verify: dnskey without ZSK flag"); *reason = "dnskey without ZSK flag"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_NO_ZONE_KEY_BIT_SET; return sec_status_bogus; } @@ -1505,6 +1543,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, /* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */ verbose(VERB_QUERY, "verify: dnskey has wrong key protocol"); *reason = "dnskey has wrong protocolnumber"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; } @@ -1514,17 +1554,23 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, if(!signer_len) { verbose(VERB_QUERY, "verify: malformed signer name"); *reason = "signer name malformed"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; /* signer name invalid */ } if(!dname_subdomain_c(rrset->rk.dname, signer)) { verbose(VERB_QUERY, "verify: signer name is off-tree"); *reason = "signer name off-tree"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; /* signer name offtree */ } sigblock = (unsigned char*)signer+signer_len; if(siglen < 2+18+signer_len+1) { verbose(VERB_QUERY, "verify: too short, no signature data"); *reason = "signature too short, no signature data"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; /* sig rdf is < 1 byte */ } sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len); @@ -1537,6 +1583,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, log_nametypeclass(VERB_QUERY, "the key name is", dnskey->rk.dname, 0, 0); *reason = "signer name mismatches key name"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; } @@ -1545,18 +1593,24 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, if(memcmp(sig+2, &rrset->rk.type, 2) != 0) { verbose(VERB_QUERY, "verify: wrong type covered"); *reason = "signature covers wrong type"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; } /* verify keytag and sig algo (possibly again) */ if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) { verbose(VERB_QUERY, "verify: wrong algorithm"); *reason = "signature has wrong algorithm"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; } ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx)); if(memcmp(sig+2+16, &ktag, 2) != 0) { verbose(VERB_QUERY, "verify: wrong keytag"); *reason = "signature has wrong keytag"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; } @@ -1564,6 +1618,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) { verbose(VERB_QUERY, "verify: labelcount out of range"); *reason = "signature labelcount out of range"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; return sec_status_bogus; } @@ -1598,7 +1654,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, /* verify inception, expiration dates * Do this last so that if you ignore expired-sigs the * rest is sure to be OK. */ - if(!check_dates(ve, now, sig+2+8, sig+2+12, reason)) { + if(!check_dates(ve, now, sig+2+8, sig+2+12, + reason, reason_bogus)) { return sec_status_bogus; } } diff --git a/usr.sbin/unbound/validator/val_sigcrypt.h b/usr.sbin/unbound/validator/val_sigcrypt.h index bbb95780d7f..7f52b71e41f 100644 --- a/usr.sbin/unbound/validator/val_sigcrypt.h +++ b/usr.sbin/unbound/validator/val_sigcrypt.h @@ -45,6 +45,7 @@ #define VALIDATOR_VAL_SIGCRYPT_H #include "util/data/packed_rrset.h" #include "sldns/pkthdr.h" +#include "sldns/rrdef.h" struct val_env; struct module_env; struct module_qstate; @@ -256,6 +257,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); * @param sigalg: if nonNULL provide downgrade protection otherwise one * algorithm is enough. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @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. * @return SECURE if one key in the set verifies one rrsig. @@ -264,9 +266,11 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); */ 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, + struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, + char** reason, sldns_ede_code *reason_bogus, sldns_pkt_section section, struct module_qstate* qstate); + /** * verify rrset against one specific dnskey (from rrset) * @param env: module environment, scratch space is used. @@ -275,38 +279,17 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, * @param dnskey: DNSKEY rrset, keyset. * @param dnskey_idx: which key from the rrset to try. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @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. * @return secure if *this* key signs any of the signatures on rrset. * unchecked on error or and bogus on bad signature. */ -enum sec_status dnskey_verify_rrset(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, char** reason, +enum sec_status dnskey_verify_rrset(struct module_env* env, struct val_env* ve, + struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, + size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus, sldns_pkt_section section, struct module_qstate* qstate); -/** - * verify rrset, with dnskey rrset, for a specific rrsig in rrset - * @param env: module environment, scratch space is used. - * @param ve: validator environment, date settings. - * @param now: current time for validation (can be overridden). - * @param rrset: to be validated. - * @param dnskey: DNSKEY rrset, keyset to try. - * @param sig_idx: which signature to try to validate. - * @param sortree: reused sorted order. Stored in region. Pass NULL at start, - * and for a new rrset. - * @param reason: if bogus, a string returned, fixed or alloced in scratch. - * @param section: section of packet where this rrset comes from. - * @param qstate: qstate with region. - * @return secure if any key signs *this* signature. bogus if no key signs it, - * or unchecked on error. - */ -enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, - struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_type** sortree, char** reason, sldns_pkt_section section, - struct module_qstate* qstate); - /** * verify rrset, with specific dnskey(from set), for a specific rrsig * @param region: scratch region used for temporary allocation. @@ -323,17 +306,19 @@ enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, * pass false at start. pass old value only for same rrset and same * signature (but perhaps different key) for reuse. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @param reason_bogus: EDE (8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. * @return secure if this key signs this signature. unchecked on error or * bogus if it did not validate. */ -enum sec_status dnskey_verify_rrset_sig(struct regional* region, - struct sldns_buffer* buf, struct val_env* ve, time_t now, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - size_t dnskey_idx, size_t sig_idx, - struct rbtree_type** sortree, int* buf_canon, char** reason, - sldns_pkt_section section, struct module_qstate* qstate); +enum sec_status dnskey_verify_rrset_sig(struct regional* region, + struct sldns_buffer* buf, struct val_env* ve, time_t now, + struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, + size_t dnskey_idx, size_t sig_idx, + struct rbtree_type** sortree, int* buf_canon, + char** reason, sldns_ede_code *reason_bogus, + sldns_pkt_section section, struct module_qstate* qstate); /** * canonical compare for two tree entries diff --git a/usr.sbin/unbound/validator/val_utils.c b/usr.sbin/unbound/validator/val_utils.c index bb366d33940..18a7c9c2e95 100644 --- a/usr.sbin/unbound/validator/val_utils.c +++ b/usr.sbin/unbound/validator/val_utils.c @@ -332,11 +332,11 @@ rrset_get_ttl(struct ub_packed_rrset_key* rrset) return d->ttl; } -enum sec_status +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_pkt_section section, - struct module_qstate* qstate) + uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, + sldns_pkt_section section, struct module_qstate* qstate) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -359,7 +359,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, - section, qstate); + reason_bogus, section, qstate); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -392,7 +392,8 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, 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_pkt_section section, struct module_qstate* qstate) + char** reason, sldns_ede_code *reason_bogus, + sldns_pkt_section section, struct module_qstate* qstate) { /* temporary dnskey rrset-key */ struct ub_packed_rrset_key dnskey; @@ -406,16 +407,16 @@ 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, - section, qstate); + reason_bogus, section, qstate); return sec; } /** verify that a DS RR hashes to a key and that key signs the set */ static enum sec_status -verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, +verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, + struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason, - struct module_qstate* qstate) + sldns_ede_code *reason_bogus, struct module_qstate* qstate) { enum sec_status sec = sec_status_bogus; size_t i, num, numchecked = 0, numhashok = 0, numsizesupp = 0; @@ -450,8 +451,8 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, /* Otherwise, we have a match! Make sure that the DNSKEY * verifies *with this key* */ - sec = dnskey_verify_rrset(env, ve, dnskey_rrset, - dnskey_rrset, i, reason, LDNS_SECTION_ANSWER, qstate); + sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, + i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { return sec; } @@ -488,11 +489,12 @@ int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset) return digest_algo; } -enum sec_status +// @TODO change the use of this function to _ede function in authzone.c:8111 +enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, - struct module_qstate* qstate) + sldns_ede_code *reason_bogus, struct module_qstate* qstate) { /* as long as this is false, we can consider this DS rrset to be * equivalent to no DS rrset. */ @@ -529,7 +531,7 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, } sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, - ds_rrset, i, reason, qstate); + ds_rrset, i, reason, reason_bogus, qstate); if(sec == sec_status_insecure) continue; @@ -571,15 +573,16 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, return sec_status_bogus; } -struct key_entry_key* +struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason, - struct module_qstate* qstate) + sldns_ede_code *reason_bogus, struct module_qstate* qstate) { uint8_t sigalg[ALGO_NEEDS_MAX+1]; - enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, - dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason, qstate); + enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, + dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason, + reason_bogus, qstate); if(sec == sec_status_secure) { return key_entry_create_rrset(region, @@ -597,12 +600,12 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, BOGUS_KEY_TTL, *env->now); } -enum sec_status +enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds, struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason, - struct module_qstate* qstate) + sldns_ede_code *reason_bogus, struct module_qstate* qstate) { /* as long as this is false, we can consider this anchor to be * equivalent to no anchor. */ @@ -617,6 +620,8 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset " "by name"); *reason = "DNSKEY RRset did not match DS RRset by name"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSKEY_MISSING; return sec_status_bogus; } if(ta_dnskey && (dnskey_rrset->rk.dname_len != ta_dnskey->rk.dname_len @@ -625,6 +630,8 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, verbose(VERB_QUERY, "DNSKEY RRset did not match anchor RRset " "by name"); *reason = "DNSKEY RRset did not match anchor RRset by name"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSKEY_MISSING; return sec_status_bogus; } @@ -648,7 +655,7 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, continue; sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, - ta_ds, i, reason, qstate); + ta_ds, i, reason, reason_bogus, qstate); if(sec == sec_status_insecure) continue; @@ -688,7 +695,7 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, has_useful_ta = 1; sec = dnskey_verify_rrset(env, ve, dnskey_rrset, - ta_dnskey, i, reason, LDNS_SECTION_ANSWER, qstate); + ta_dnskey, i, reason, NULL, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { if(!sigalg || algo_needs_set_secure(&needs, (uint8_t)dnskey_get_algo(ta_dnskey, i))) { @@ -723,24 +730,24 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, + struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds_rrset, struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot, - char** reason, struct module_qstate* qstate) + char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate) { uint8_t sigalg[ALGO_NEEDS_MAX+1]; - enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, + enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset, - downprot?sigalg:NULL, reason, qstate); + downprot?sigalg:NULL, reason, reason_bogus, qstate); if(sec == sec_status_secure) { - return key_entry_create_rrset(region, + return key_entry_create_rrset(region, dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len, ntohs(dnskey_rrset->rk.rrset_class), dnskey_rrset, downprot?sigalg:NULL, *env->now); } else if(sec == sec_status_insecure) { return key_entry_create_null(region, dnskey_rrset->rk.dname, - dnskey_rrset->rk.dname_len, + dnskey_rrset->rk.dname_len, ntohs(dnskey_rrset->rk.rrset_class), rrset_get_ttl(dnskey_rrset), *env->now); } @@ -749,7 +756,7 @@ val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env, BOGUS_KEY_TTL, *env->now); } -int +int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset) { size_t i; @@ -776,6 +783,7 @@ val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset) if(lt) snprintf(aerr, sizeof(aerr), "%s", lt->name); else snprintf(aerr, sizeof(aerr), "%d", (int)ds_get_key_algo(ds_rrset, 0)); + verbose(VERB_ALGO, "DS unsupported, hash %s %s, " "key algorithm %s %s", herr, (ds_digest_algo_is_supported(ds_rrset, 0)? diff --git a/usr.sbin/unbound/validator/val_utils.h b/usr.sbin/unbound/validator/val_utils.h index 6e9867f6e3c..83e3d0ad824 100644 --- a/usr.sbin/unbound/validator/val_utils.h +++ b/usr.sbin/unbound/validator/val_utils.h @@ -43,6 +43,7 @@ #define VALIDATOR_VAL_UTILS_H #include "util/data/packed_rrset.h" #include "sldns/pkthdr.h" +#include "sldns/rrdef.h" struct query_info; struct reply_info; struct val_env; @@ -113,24 +114,6 @@ void val_find_signer(enum val_classification subtype, struct query_info* qinf, struct reply_info* rep, size_t cname_skip, uint8_t** signer_name, size_t* signer_len); -/** - * Verify RRset with keys - * @param env: module environment (scratch buffer) - * @param ve: validator environment (verification settings) - * @param rrset: what to verify - * @param keys: dnskey rrset to verify with. - * @param sigalg: if nonNULL provide downgrade protection otherwise one - * algorithm is enough. Algo list is constructed in here. - * @param reason: reason of failure. Fixed string or alloced in scratch. - * @param section: section of packet where this rrset comes from. - * @param qstate: qstate with region. - * @return security status of verification. - */ -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_pkt_section section, - struct module_qstate* qstate); - /** * Verify RRset with keys from a keyset. * @param env: module environment (scratch buffer) @@ -138,14 +121,15 @@ enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, * @param rrset: what to verify * @param kkey: key_entry to verify with. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @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. * @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_pkt_section section, - struct module_qstate* qstate); + struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, + sldns_pkt_section section, struct module_qstate* qstate); /** * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but @@ -158,15 +142,16 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, * algorithm is enough. The list of signalled algorithms is returned, * must have enough space for ALGO_NEEDS_MAX+1. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). * sec_status_bogus if it fails. */ -enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, - struct module_qstate* qstate); +enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, + struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, + struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, + sldns_ede_code *reason_bogus, struct module_qstate* qstate); /** * Verify DNSKEYs with DS and DNSKEY rrset. Like val_verify_DNSKEY_with_DS @@ -180,16 +165,17 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, * algorithm is enough. The list of signalled algorithms is returned, * must have enough space for ALGO_NEEDS_MAX+1. * @param reason: reason of failure. Fixed string or alloced in scratch. +* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). * sec_status_bogus if it fails. */ -enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ta_ds, - struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason, - struct module_qstate* qstate); +enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, + struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, + struct ub_packed_rrset_key* ta_ds, + struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason, + sldns_ede_code *reason_bogus, struct module_qstate* qstate); /** * Verify new DNSKEYs with DS rrset. The DS contains hash values that should @@ -204,6 +190,7 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, * @param downprot: if true provide downgrade protection otherwise one * algorithm is enough. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS @@ -215,12 +202,11 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, * rrset. * if downprot is set, a key entry with an algo list is made. */ -struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, - struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason, - struct module_qstate* qstate); - +struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, + struct module_env* env, struct val_env* ve, + struct ub_packed_rrset_key* dnskey_rrset, + struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason, + sldns_ede_code *reason_bogus, struct module_qstate* qstate); /** * Verify rrset with trust anchor: DS and DNSKEY rrset. @@ -234,6 +220,7 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, * @param downprot: if true provide downgrade protection otherwise one * algorithm is enough. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS @@ -246,11 +233,11 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, * if downprot is set, a key entry with an algo list is made. */ struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region, - struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ta_ds_rrset, - struct ub_packed_rrset_key* ta_dnskey_rrset, - int downprot, char** reason, struct module_qstate* qstate); + struct module_env* env, struct val_env* ve, + struct ub_packed_rrset_key* dnskey_rrset, + struct ub_packed_rrset_key* ta_ds_rrset, + struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot, + char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate); /** * Determine if DS rrset is usable for validator or not. diff --git a/usr.sbin/unbound/validator/validator.c b/usr.sbin/unbound/validator/validator.c index e6307284fb0..75f3220cf3d 100644 --- a/usr.sbin/unbound/validator/validator.c +++ b/usr.sbin/unbound/validator/validator.c @@ -69,6 +69,20 @@ 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); + +/* Updates the suplied EDE (RFC8914) code selectively so we don't loose + * a more specific code + */ +static void +update_reason_bogus(struct reply_info* rep, sldns_ede_code reason_bogus) +{ + if (rep->reason_bogus == LDNS_EDE_DNSSEC_BOGUS || + rep->reason_bogus == LDNS_EDE_NONE) { + rep->reason_bogus = reason_bogus; + } +} + + /** fill up nsec3 key iterations config entry */ static int fill_nsec3_iter(struct val_env* ve, char* s, int c) @@ -230,6 +244,7 @@ val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq) vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf) |BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD)); vq->orig_msg->rep->qdcount = 1; + vq->orig_msg->rep->reason_bogus = LDNS_EDE_NONE; } else { vq->orig_msg = qstate->return_msg; } @@ -592,6 +607,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, enum sec_status sec; int dname_seen = 0; char* reason = NULL; + sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; /* validate the ANSWER section */ for(i=0; ian_numrrsets; i++) { @@ -613,20 +629,22 @@ 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, - LDNS_SECTION_ANSWER, qstate); + &reason_bogus, LDNS_SECTION_ANSWER, qstate); /* If the (answer) rrset failed to validate, then this * message is BAD. */ if(sec != sec_status_secure) { log_nametypeclass(VERB_QUERY, "validator: response " "has failed ANSWER rrset:", s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); - errinf(qstate, reason); + errinf_ede(qstate, reason, reason_bogus); if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) errinf(qstate, "for CNAME"); else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) errinf(qstate, "for DNAME"); errinf_origin(qstate, qstate->reply_origin); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, reason_bogus); + return 0; } @@ -643,17 +661,18 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, chase_reply->ns_numrrsets; i++) { s = chase_reply->rrsets[i]; sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, - LDNS_SECTION_AUTHORITY, qstate); + &reason_bogus, LDNS_SECTION_AUTHORITY, qstate); /* If anything in the authority section fails to be secure, * we have a bad message. */ if(sec != sec_status_secure) { log_nametypeclass(VERB_QUERY, "validator: response " "has failed AUTHORITY rrset:", s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); - errinf(qstate, reason); + errinf_ede(qstate, reason, reason_bogus); errinf_origin(qstate, qstate->reply_origin); errinf_rrset(qstate, s); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, reason_bogus); return 0; } } @@ -669,9 +688,10 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, /* 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); + if(sname && query_dname_compare(sname, key_entry->name)==0) (void)val_verify_rrset_entry(env, ve, s, key_entry, - &reason, LDNS_SECTION_ADDITIONAL, qstate); + &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate); /* the additional section can fail to be secure, * it is optional, check signature in case we need * to clean the additional section later. */ @@ -804,6 +824,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve, "inconsistent wildcard sigs:", s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } if(wc && !wc_cached && env->cfg->aggressive_nsec) { @@ -861,6 +882,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve, "expansion and did not prove original data " "did not exist"); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } @@ -959,6 +981,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, if(verbosity >= VERB_ALGO) log_dns_msg("Failed NODATA", qchase, chase_reply); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } @@ -1045,6 +1068,7 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, 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) @@ -1056,6 +1080,7 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, 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) @@ -1138,6 +1163,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, if(qchase->qtype != LDNS_RR_TYPE_ANY) { log_err("internal error: ANY validation called for non-ANY"); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } @@ -1154,6 +1180,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } } @@ -1208,6 +1235,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, "expansion and did not prove original data " "did not exist"); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } @@ -1255,6 +1283,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, "inconsistent wildcard sigs:", s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } @@ -1267,6 +1296,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, "wildcarded DNAME:", s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } @@ -1324,6 +1354,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, "expansion and did not prove original data " "did not exist"); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } @@ -1424,6 +1455,7 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name " "exists and not exists, bogus"); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) { @@ -1449,6 +1481,7 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, if(verbosity >= VERB_ALGO) log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply); chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } @@ -1492,6 +1525,10 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, verbose(VERB_ALGO, "restart count exceeded"); return val_error(qstate, id); } + + /* correctly initialize reason_bogus */ + update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_BOGUS); + verbose(VERB_ALGO, "validator classification %s", val_classification_to_string(subtype)); if(subtype == VAL_CLASS_REFERRAL && @@ -1557,6 +1594,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, verbose(VERB_QUERY, "unsigned parent zone denies" " trust anchor, indeterminate"); vq->chase_reply->security = sec_status_indeterminate; + update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE); vq->state = VAL_FINISHED_STATE; return 1; } @@ -1588,6 +1626,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, if(vq->key_entry == NULL && anchor == NULL) { /*response isn't under a trust anchor, so we cannot validate.*/ vq->chase_reply->security = sec_status_indeterminate; + update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE); /* go to finished state to cache this result */ vq->state = VAL_FINISHED_STATE; return 1; @@ -1633,16 +1672,25 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, vq->state = VAL_FINISHED_STATE; return 1; } else if(key_entry_isbad(vq->key_entry)) { + sldns_ede_code ede = LDNS_EDE_DNSSEC_BOGUS; + + /* the key could have a more spefic EDE than just bogus */ + if(key_entry_get_reason_bogus(vq->key_entry) != LDNS_EDE_NONE) { + ede = key_entry_get_reason_bogus(vq->key_entry); + } + /* key is bad, chain is bad, reply is bogus */ errinf_dname(qstate, "key for validation", vq->key_entry->name); - errinf(qstate, "is marked as invalid"); + errinf_ede(qstate, "is marked as invalid", ede); if(key_entry_get_reason(vq->key_entry)) { errinf(qstate, "because of a previous"); errinf(qstate, key_entry_get_reason(vq->key_entry)); } + /* no retries, stop bothering the authority until timeout */ vq->restart_count = ve->max_restart; vq->chase_reply->security = sec_status_bogus; + update_reason_bogus(vq->chase_reply, ede); vq->state = VAL_FINISHED_STATE; return 1; } @@ -1713,9 +1761,10 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) vq->empty_DS_name) == 0) { /* do not query for empty_DS_name again */ verbose(VERB_ALGO, "Cannot retrieve DS for signature"); - errinf(qstate, "no signatures"); + errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING); errinf_origin(qstate, qstate->reply_origin); vq->chase_reply->security = sec_status_bogus; + update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING); vq->state = VAL_FINISHED_STATE; return 1; } @@ -1848,7 +1897,10 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, "of trust to keys for", vq->key_entry->name, LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class); vq->chase_reply->security = sec_status_bogus; - errinf(qstate, "while building chain of trust"); + + update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSKEY_MISSING); + errinf_ede(qstate, "while building chain of trust", + LDNS_EDE_DNSKEY_MISSING); if(vq->restart_count >= ve->max_restart) key_cache_insert(ve->kcache, vq->key_entry, qstate); return 1; @@ -1861,9 +1913,10 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, "signer name", &vq->qchase); verbose(VERB_DETAIL, "Could not establish validation of " "INSECURE status of unsigned response."); - errinf(qstate, "no signatures"); + errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING); errinf_origin(qstate, qstate->reply_origin); vq->chase_reply->security = sec_status_bogus; + update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING); return 1; } subtype = val_classify_response(qstate->query_flags, &qstate->qinfo, @@ -2001,17 +2054,20 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, vq->orig_msg->rep, vq->rrset_skip); /* store overall validation result in orig_msg */ - if(vq->rrset_skip == 0) + if(vq->rrset_skip == 0) { vq->orig_msg->rep->security = vq->chase_reply->security; - else if(subtype != VAL_CLASS_REFERRAL || + update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus); + } else if(subtype != VAL_CLASS_REFERRAL || vq->rrset_skip < vq->orig_msg->rep->an_numrrsets + vq->orig_msg->rep->ns_numrrsets) { /* ignore sec status of additional section if a referral * type message skips there and * use the lowest security status as end result. */ - if(vq->chase_reply->security < vq->orig_msg->rep->security) + if(vq->chase_reply->security < vq->orig_msg->rep->security) { vq->orig_msg->rep->security = vq->chase_reply->security; + update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus); + } } if(subtype == VAL_CLASS_REFERRAL) { @@ -2034,6 +2090,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, &vq->rrset_skip)) { verbose(VERB_ALGO, "validator: failed to chase CNAME"); vq->orig_msg->rep->security = sec_status_bogus; + update_reason_bogus(vq->orig_msg->rep, LDNS_EDE_DNSSEC_BOGUS); } else { /* restart process for new qchase at rrset_skip */ log_query_info(VERB_ALGO, "validator: chased to", @@ -2247,9 +2304,11 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id, * queries. If we get here, it is bogus or an internal error */ if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) { verbose(VERB_ALGO, "cannot validate classANY: bogus"); - if(qstate->return_msg) + if(qstate->return_msg) { qstate->return_msg->rep->security = sec_status_bogus; + update_reason_bogus(qstate->return_msg->rep, LDNS_EDE_DNSSEC_BOGUS); + } qstate->ext_state[id] = module_finished; return; } @@ -2304,6 +2363,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, struct key_entry_key* kkey = NULL; enum sec_status sec = sec_status_unchecked; char* reason = NULL; + sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; int downprot = qstate->env->cfg->harden_algo_downgrade; if(!dnskey_rrset) { @@ -2311,7 +2371,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, "could not fetch DNSKEY rrset", ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); if(qstate->env->cfg->harden_dnssec_stripped) { - errinf(qstate, "no DNSKEY rrset"); + errinf_ede(qstate, "no DNSKEY rrset", LDNS_EDE_DNSKEY_MISSING); kkey = key_entry_create_bad(qstate->region, ta->name, ta->namelen, ta->dclass, BOGUS_KEY_TTL, *qstate->env->now); @@ -2327,7 +2387,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, /* attempt to verify with trust anchor DS and DNSKEY */ kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot, - &reason, qstate); + &reason, &reason_bogus, qstate); if(!kkey) { log_err("out of memory: verifying prime TA"); return NULL; @@ -2346,7 +2406,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, /* NOTE: in this case, we should probably reject the trust * anchor for longer, perhaps forever. */ if(qstate->env->cfg->harden_dnssec_stripped) { - errinf(qstate, reason); + errinf_ede(qstate, reason, reason_bogus); kkey = key_entry_create_bad(qstate->region, ta->name, ta->namelen, ta->dclass, BOGUS_KEY_TTL, *qstate->env->now); @@ -2389,6 +2449,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; char* reason = NULL; + sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; enum val_classification subtype; if(rcode != LDNS_RCODE_NOERROR) { char rc[16]; @@ -2397,7 +2458,8 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* errors here pretty much break validation */ verbose(VERB_DETAIL, "DS response was error, thus bogus"); errinf(qstate, rc); - errinf(qstate, "no DS"); + errinf_ede(qstate, "no DS", LDNS_EDE_NETWORK_ERROR); + goto return_bogus; } @@ -2411,17 +2473,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, if(!ds) { log_warn("internal error: POSITIVE DS response was " "missing DS."); - errinf(qstate, "no DS record"); + errinf_ede(qstate, "no DS record", LDNS_EDE_DNSSEC_BOGUS); goto return_bogus; } /* 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, LDNS_SECTION_ANSWER, qstate); + sec = val_verify_rrset_entry(qstate->env, ve, ds, + vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate); if(sec != sec_status_secure) { verbose(VERB_DETAIL, "DS rrset in DS response did " "not verify"); - errinf(qstate, reason); + errinf_ede(qstate, reason, reason_bogus); goto return_bogus; } @@ -2430,6 +2492,9 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, if(!val_dsset_isusable(ds)) { /* If they aren't usable, then we treat it like * there was no DS. */ + + // @TODO add EDE Unsupported DS Digest Type + *ke = key_entry_create_null(qstate->region, qinfo->qname, qinfo->qname_len, qinfo->qclass, ub_packed_rrset_ttl(ds), *qstate->env->now); @@ -2452,7 +2517,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* make sure there are NSECs or NSEC3s with signatures */ if(!val_has_signed_nsecs(msg->rep, &reason)) { verbose(VERB_ALGO, "no NSECs: %s", reason); - errinf(qstate, reason); + errinf_ede(qstate, reason, LDNS_EDE_NSEC_MISSING); goto return_bogus; } @@ -2493,7 +2558,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, sec = nsec3_prove_nods(qstate->env, ve, msg->rep->rrsets + msg->rep->an_numrrsets, msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, - qstate); + &reason_bogus, qstate); switch(sec) { case sec_status_insecure: /* case insecure also continues to unsigned @@ -2515,7 +2580,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, case sec_status_bogus: verbose(VERB_DETAIL, "NSEC3s for the " "referral did not prove no DS."); - errinf(qstate, reason); + errinf_ede(qstate, reason, reason_bogus); goto return_bogus; case sec_status_unchecked: default: @@ -2554,7 +2619,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, goto return_bogus; } sec = val_verify_rrset_entry(qstate->env, ve, cname, - vq->key_entry, &reason, LDNS_SECTION_ANSWER, qstate); + vq->key_entry, &reason, NULL, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { verbose(VERB_ALGO, "CNAME validated, " "proof that DS does not exist"); @@ -2685,6 +2750,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, struct ub_packed_rrset_key* dnskey = NULL; int downprot; char* reason = NULL; + sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; if(rcode == LDNS_RCODE_NOERROR) dnskey = reply_find_answer_rrset(qinfo, msg->rep); @@ -2693,6 +2759,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, /* bad response */ verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to " "DNSKEY query."); + if(vq->restart_count < ve->max_restart) { val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1); @@ -2707,7 +2774,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, log_err("alloc failure in missing dnskey response"); /* key_entry is NULL for failure in Validate */ } - errinf(qstate, "No DNSKEY record"); + errinf_ede(qstate, "No DNSKEY record", LDNS_EDE_DNSKEY_MISSING); errinf_origin(qstate, origin); errinf_dname(qstate, "for key", qinfo->qname); vq->state = VAL_VALIDATE_STATE; @@ -2721,7 +2788,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, } downprot = qstate->env->cfg->harden_algo_downgrade; vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env, - ve, dnskey, vq->ds_rrset, downprot, &reason, qstate); + ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, qstate); if(!vq->key_entry) { log_err("out of memory in verify new DNSKEYs"); @@ -2742,7 +2809,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, } verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, " "thus bogus."); - errinf(qstate, reason); + errinf_ede(qstate, reason, reason_bogus); errinf_origin(qstate, origin); errinf_dname(qstate, "for key", qinfo->qname); } diff --git a/usr.sbin/unbound/validator/validator.h b/usr.sbin/unbound/validator/validator.h index a97eab25bc4..694e4c89529 100644 --- a/usr.sbin/unbound/validator/validator.h +++ b/usr.sbin/unbound/validator/validator.h @@ -93,7 +93,7 @@ struct val_env { int32_t skew_max; /** max number of query restarts, number of IPs to probe */ - int32_t max_restart; + int max_restart; /** TTL for bogus data; used instead of untrusted TTL from data. * Bogus data will not be verified more often than this interval.