From: Wouter Wijngaards Date: Wed, 7 Oct 2009 16:45:47 +0000 (+0000) Subject: - moved version number to 1.4.0 because of 1.3.4 release with only X-Git-Tag: release-1.4.0rc1~36 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ce45cbda6dfe145459858b2c3ff1af8e8a83d477;p=thirdparty%2Funbound.git - moved version number to 1.4.0 because of 1.3.4 release with only the NSEC3 patch. - val-log-level: 2 shows extended error information for validation failures, but still one (longish) line per failure. For example: validation failure : signature expired from 192.0.2.4 for trust anchor example.com. while building chain of trust validation failure : no signatures from 192.0.2.6 for key example.com. while building chain of trust git-svn-id: file:///svn/unbound/trunk@1868 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/configure b/configure index 02b3a4f6f..9fe75aa2c 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.63 for unbound 1.3.4. +# Generated by GNU Autoconf 2.63 for unbound 1.4.0. # # Report bugs to . # @@ -745,8 +745,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.3.4' -PACKAGE_STRING='unbound 1.3.4' +PACKAGE_VERSION='1.4.0' +PACKAGE_STRING='unbound 1.4.0' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl' # Factoring default headers for most tests. @@ -1499,7 +1499,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.3.4 to adapt to many kinds of systems. +\`configure' configures unbound 1.4.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1564,7 +1564,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.3.4:";; + short | recursive ) echo "Configuration of unbound 1.4.0:";; esac cat <<\_ACEOF @@ -1710,7 +1710,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.3.4 +unbound configure 1.4.0 generated by GNU Autoconf 2.63 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1724,7 +1724,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.3.4, which was +It was created by unbound $as_me 1.4.0, which was generated by GNU Autoconf 2.63. Invocation command line was $ $0 $@ @@ -2094,7 +2094,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu LIBUNBOUND_CURRENT=1 -LIBUNBOUND_REVISION=4 +LIBUNBOUND_REVISION=5 LIBUNBOUND_AGE=0 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -2108,6 +2108,7 @@ LIBUNBOUND_AGE=0 # 1.3.2 had 1:2:0 # 1.3.3 had 1:3:0 # 1.3.4 had 1:4:0 +# 1.4.0 had 1:5:0 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -7278,13 +7279,13 @@ if test "${lt_cv_nm_interface+set}" = set; then else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:7281: $ac_compile\"" >&5) + (eval echo "\"\$as_me:7282: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:7284: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:7285: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:7287: output\"" >&5) + (eval echo "\"\$as_me:7288: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -8489,7 +8490,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 8492 "configure"' > conftest.$ac_ext + echo '#line 8493 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -9856,11 +9857,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:9859: $lt_compile\"" >&5) + (eval echo "\"\$as_me:9860: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:9863: \$? = $ac_status" >&5 + echo "$as_me:9864: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -10195,11 +10196,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10198: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10199: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:10202: \$? = $ac_status" >&5 + echo "$as_me:10203: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -10300,11 +10301,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10303: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10304: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:10307: \$? = $ac_status" >&5 + echo "$as_me:10308: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -10355,11 +10356,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10358: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10359: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:10362: \$? = $ac_status" >&5 + echo "$as_me:10363: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -13158,7 +13159,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 13161 "configure" +#line 13162 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -13254,7 +13255,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 13257 "configure" +#line 13258 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -23301,7 +23302,7 @@ exec 6>&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.3.4, which was +This file was extended by unbound $as_me 1.4.0, which was generated by GNU Autoconf 2.63. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23364,7 +23365,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -unbound config.status 1.3.4 +unbound config.status 1.4.0 configured by $0, generated by GNU Autoconf 2.63, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.ac b/configure.ac index 5c4ef7b7b..eabdecdab 100644 --- a/configure.ac +++ b/configure.ac @@ -6,10 +6,10 @@ sinclude(acx_pthread.m4) sinclude(acx_python.m4) sinclude(ac_pkg_swig.m4) -AC_INIT(unbound, 1.3.4, unbound-bugs@nlnetlabs.nl, unbound) +AC_INIT(unbound, 1.4.0, unbound-bugs@nlnetlabs.nl, unbound) LIBUNBOUND_CURRENT=1 -LIBUNBOUND_REVISION=4 +LIBUNBOUND_REVISION=5 LIBUNBOUND_AGE=0 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -23,6 +23,7 @@ LIBUNBOUND_AGE=0 # 1.3.2 had 1:2:0 # 1.3.3 had 1:3:0 # 1.3.4 had 1:4:0 +# 1.4.0 had 1:5:0 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary diff --git a/doc/Changelog b/doc/Changelog index 73aa3151f..c838335e0 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -7,6 +7,14 @@ - Fixed security bug where the signatures for NSEC3 records were not checked when checking for absence of DS records. This could have enabled the substitution of an insecure delegation. + - moved version number to 1.4.0 because of 1.3.4 release with only + the NSEC3 patch from the entry above. + - val-log-level: 2 shows extended error information for validation + failures, but still one (longish) line per failure. For example: + validation failure : signature expired from + 192.0.2.4 for trust anchor example.com. while building chain of trust + validation failure : no signatures from + 192.0.2.6 for key example.com. while building chain of trust 6 October 2009: Wouter - Test set updated to provide additional ns lookup result. diff --git a/testcode/testbound.c b/testcode/testbound.c index 1552f3ee4..c461b641c 100644 --- a/testcode/testbound.c +++ b/testcode/testbound.c @@ -186,6 +186,7 @@ setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[]) fprintf(cfg, " chroot: \"\"\n"); fprintf(cfg, " username: \"\"\n"); fprintf(cfg, " pidfile: \"\"\n"); + fprintf(cfg, " val-log-level: 2\n"); while(fgets(line, MAX_LINE_LEN-1, in)) { parse = line; (*lineno)++; diff --git a/testcode/unitverify.c b/testcode/unitverify.c index b423a738b..b77239618 100644 --- a/testcode/unitverify.c +++ b/testcode/unitverify.c @@ -155,14 +155,16 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, struct query_info* qinfo) { enum sec_status sec; + char* reason = NULL; if(vsig) { log_nametypeclass(VERB_QUERY, "verify of rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); } - sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey); + sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, &reason); if(vsig) { - printf("verify outcome is: %s\n", sec_status_to_string(sec)); + printf("verify outcome is: %s %s\n", sec_status_to_string(sec), + reason?reason:""); } if(should_be_bogus(rrset, qinfo)) { unit_assert(sec == sec_status_bogus); diff --git a/testdata/val_nsec3_nods_badsig.rpl b/testdata/val_nsec3_nods_badsig.rpl new file mode 100644 index 000000000..8f02cd77f --- /dev/null +++ b/testdata/val_nsec3_nods_badsig.rpl @@ -0,0 +1,227 @@ +; config options +; The island of trust is at example.com +server: + trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b" + val-override-date: "20070916134226" + target-fetch-policy: "0 0 0 0 0" + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test validator with NSEC3 with no DS referral with bad signature. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA REFUSED +SECTION QUESTION +ns.example.com. IN AAAA +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response to DNSKEY priming query +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN DNSKEY +SECTION ANSWER +example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b} +example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854} +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response to query of interest +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN SOA ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000 +example.com. 3600 IN RRSIG SOA 3 2 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCM6lsu9byZIQ1yYjJmyYfFWM2RWAIUcR5t84r2La824oWCkLjmHXRQlco= ;{id = 2854} + +; NODATA response. H(www.example.com.) = s1unhcti19bkdr98fegs0v46mbu3t4m3 +s1unhcti19bkdr98fegs0v46mbu3t4m3.example.com. IN NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 MX RRSIG +s1unhcti19bkdr98fegs0v46mbu3t4m3.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFE/a24nsY2luhQmZjY/ObAIgNSMkAhQWd4MUOUVK55bD6AbMHWrDA0yvEA== ;{id = 2854} + +ENTRY_END + +; refer to server one down +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.sub.example.com. IN A +SECTION AUTHORITY +sub.example.com. IN NS ns.sub.example.com. +; proof that there is no DS here. +;sub.example.com. 3600 IN DS 2854 DSA 1 be4d46cd7489cce25a31af0dff2968ce0425dd31 +;sub.example.com. 3600 IN RRSIG DS 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQC1WMTfb25sTgeUEXCFR4+YiJqecwIUc2R/jrO4amyQxovSnld2reg8eyo= ;{id = 2854} +; sub.example.com. -> 8r1f0ieoutlnjc03meng9e3bn2n0o9pd. +8r1f0ieoutlnjc03meng9e3bn2n0o9pd.example.com. IN NSEC3 1 1 123 aabb00123456bbccdd 8r1f0ieoutlnjc03meng9e3bn3n0o9pd NS RRSIG +; bad signature: +8r1f0ieoutlnjc03meng9e3bn2n0o9pd.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20010926135752 20010829135752 2854 example.com. MC0CFEC78oZJjqlV6kVyQb4X0o6tsUpUAhUAk+bgth7eeN+aO8ts2+yLSyzSX9g= ;{id = 2854} +;8r1f0ieoutlnjc03meng9e3bn2n0o9pd.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFEC78oZJjqlV6kVyQb4X0o6tsUpUAhUAk+bgth7eeN+aO8ts2+yLSyzSX9g= ;{id = 2854} +SECTION ADDITIONAL +ns.sub.example.com. IN A 1.2.3.10 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +sub.example.com. IN DS +SECTION AUTHORITY +; proof that there is no DS here. +;sub.example.com. 3600 IN DS 2854 DSA 1 be4d46cd7489cce25a31af0dff2968ce0425dd31 +;sub.example.com. 3600 IN RRSIG DS 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQC1WMTfb25sTgeUEXCFR4+YiJqecwIUc2R/jrO4amyQxovSnld2reg8eyo= ;{id = 2854} +; sub.example.com. -> 8r1f0ieoutlnjc03meng9e3bn2n0o9pd. +8r1f0ieoutlnjc03meng9e3bn2n0o9pd.example.com. IN NSEC3 1 1 123 aabb00123456bbccdd 8r1f0ieoutlnjc03meng9e3bn3n0o9pd NS RRSIG +; bad signature +8r1f0ieoutlnjc03meng9e3bn2n0o9pd.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20010926135752 20010829135752 2854 example.com. MC0CFEC78oZJjqlV6kVyQb4X0o6tsUpUAhUAk+bgth7eeN+aO8ts2+yLSyzSX9g= ;{id = 2854} +;8r1f0ieoutlnjc03meng9e3bn2n0o9pd.example.com. 3600 IN RRSIG NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFEC78oZJjqlV6kVyQb4X0o6tsUpUAhUAk+bgth7eeN+aO8ts2+yLSyzSX9g= ;{id = 2854} +ENTRY_END +RANGE_END + +; ns.sub.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.10 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR REFUSED +SECTION QUESTION +sub.example.com. IN NS +SECTION ANSWER +ENTRY_END + + +; response to DNSKEY priming query +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +sub.example.com. IN DNSKEY +SECTION ANSWER +sub.example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b} +sub.example.com. 3600 IN RRSIG DNSKEY 3 3 3600 20070926135752 20070829135752 2854 sub.example.com. MCwCFBznBTYM/SrdUnjQdBnLtRO79KAaAhQReG5nRuL7Xsdf6D0KKwPa1GpWyQ== ;{id = 2854} + +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.sub.example.com. IN A +SECTION ANSWER +www.sub.example.com. IN A 1.2.3.123 +www.sub.example.com. 3600 IN RRSIG A 3 4 3600 20070926135752 20070829135752 2854 sub.example.com. MC0CFEExteiCsLkRi/md6o5K8BhRJAKFAhUAgg2tkvwaDn8Xbm9q+5xnjvgIB8k= ;{id = 2854} +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.sub.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA SERVFAIL +SECTION QUESTION +www.sub.example.com. IN A +SECTION ANSWER +SECTION AUTHORITY +SECTION ADDITIONAL +ENTRY_END + +SCENARIO_END diff --git a/testdata/val_secds_nosig.rpl b/testdata/val_secds_nosig.rpl new file mode 100644 index 000000000..e4a20ba93 --- /dev/null +++ b/testdata/val_secds_nosig.rpl @@ -0,0 +1,191 @@ +; config options +; The island of trust is at example.com +server: + trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b" + val-override-date: "20070916134226" + target-fetch-policy: "0 0 0 0 0" + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test validator with no signatures after secure delegation + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response to DNSKEY priming query +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN DNSKEY +SECTION ANSWER +example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b} +example.com. 3600 IN RRSIG DNSKEY DSA 2 3600 20070926134150 20070829134150 2854 example.com. MCwCFBQRtlR4BEv9ohi+PGFjp+AHsJuHAhRCvz0shggvnvI88DFnBDCczHUcVA== ;{id = 2854} +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response for delegation to sub.example.com. +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +sub.example.com. IN A +SECTION ANSWER +SECTION AUTHORITY +sub.example.com. IN NS ns.sub.example.com. +sub.example.com. 3600 IN DS 30899 RSASHA1 1 f7ed618f24d5e5202927e1d27bc2e84a141cb4b3 +sub.example.com. 3600 IN RRSIG DS 3 3 3600 20070926134150 20070829134150 2854 example.com. MCwCFCW3ix0GD4BSvNLWIbROCJt5DAW9AhRt/kg9kBKJ20UBUdumrBUHqnskdA== ;{id = 2854} +SECTION ADDITIONAL +ns.sub.example.com. IN A 1.2.3.6 +ENTRY_END + +RANGE_END + +; ns.sub.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.6 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +sub.example.com. IN NS +SECTION ANSWER +sub.example.com. IN NS ns.sub.example.com. +;sub.example.com. 3600 IN RRSIG NS 5 3 3600 20070926134150 20070829134150 30899 sub.example.com. wcpHeBILHfo8C9uxMhcW03gcURZeUffiKdSTb50ZjzTHgMNhRyMfpcvSpXEd9548A9UTmWKeLZChfr5Z/glONw== ;{id = 30899} +SECTION ADDITIONAL +ns.sub.example.com. IN A 1.2.3.6 +;ns.sub.example.com. 3600 IN RRSIG A 5 4 3600 20070926134150 20070829134150 30899 sub.example.com. UF7shD/gt1FOp2UHgLTNbPzVykklSXFMEtJ1xD+Hholwf/PIzd7zoaIttIYibNa4fUXCqMg22H9P7MRhfmFe6g== ;{id = 30899} +ENTRY_END + +; response to DNSKEY priming query +; sub.example.com. 3600 IN DS 30899 RSASHA1 1 f7ed618f24d5e5202927e1d27bc2e84a141cb4b3 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +sub.example.com. IN DNSKEY +SECTION ANSWER +sub.example.com. 3600 IN DNSKEY 256 3 5 AQPQ41chR9DEHt/aIzIFAqanbDlRflJoRs5yz1jFsoRIT7dWf0r+PeDuewdxkszNH6wnU4QL8pfKFRh5PIYVBLK3 ;{id = 30899 (zsk), size = 512b} +;sub.example.com. 3600 IN RRSIG DNSKEY 5 3 3600 20070926134150 20070829134150 30899 sub.example.com. uNGp99iznjD7oOX02XnQbDnbg75UwBHRvZSKYUorTKvPUnCWMHKdRsQ+mf+Fx3GZ+Fz9BVjoCmQqpnfgXLEYqw== ;{id = 30899} +SECTION AUTHORITY +sub.example.com. IN NS ns.sub.example.com. +;sub.example.com. 3600 IN RRSIG NS 5 3 3600 20070926134150 20070829134150 30899 sub.example.com. wcpHeBILHfo8C9uxMhcW03gcURZeUffiKdSTb50ZjzTHgMNhRyMfpcvSpXEd9548A9UTmWKeLZChfr5Z/glONw== ;{id = 30899} +SECTION ADDITIONAL +ns.sub.example.com. IN A 1.2.3.6 +;ns.sub.example.com. 3600 IN RRSIG A 5 4 3600 20070926134150 20070829134150 30899 sub.example.com. UF7shD/gt1FOp2UHgLTNbPzVykklSXFMEtJ1xD+Hholwf/PIzd7zoaIttIYibNa4fUXCqMg22H9P7MRhfmFe6g== ;{id = 30899} +ENTRY_END + +; response to query of interest +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.sub.example.com. IN A +SECTION ANSWER +www.sub.example.com. IN A 11.11.11.11 +;www.sub.example.com. 3600 IN RRSIG A 5 4 3600 20070926134150 20070829134150 30899 sub.example.com. 0DqqRfRtm7VSEQ4mmBbzrKRqQAay3JAE8DPDGmjtokrrjN9F1G/HxozDV7bjdIh2EChlQea8FPwf/GepJMUVxg== ;{id = 30899} +SECTION AUTHORITY +SECTION ADDITIONAL +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.sub.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA SERVFAIL +SECTION QUESTION +www.sub.example.com. IN A +ENTRY_END + +SCENARIO_END diff --git a/validator/autotrust.c b/validator/autotrust.c index 5568300f0..1c99fb622 100644 --- a/validator/autotrust.c +++ b/validator/autotrust.c @@ -946,10 +946,11 @@ static int verify_dnskey(struct module_env* env, struct val_env* ve, struct trust_anchor* tp, struct ub_packed_rrset_key* rrset) { + char* reason = NULL; if(tp->ds_rrset) { /* verify with ds, any will do to prime autotrust */ enum sec_status sec = val_verify_DNSKEY_with_DS( - env, ve, rrset, tp->ds_rrset); + env, ve, rrset, tp->ds_rrset, &reason); verbose(VERB_ALGO, "autotrust: validate DNSKEY with DS: %s", sec_status_to_string(sec)); if(sec == sec_status_secure) { @@ -959,7 +960,7 @@ verify_dnskey(struct module_env* env, struct val_env* ve, if(tp->dnskey_rrset) { /* verify with keys */ enum sec_status sec = val_verify_rrset(env, ve, rrset, - tp->dnskey_rrset); + tp->dnskey_rrset, &reason); verbose(VERB_ALGO, "autotrust: validate DNSKEY with keys: %s", sec_status_to_string(sec)); if(sec == sec_status_secure) { @@ -995,9 +996,11 @@ rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, size_t i) { enum sec_status sec; + char* reason = NULL; verbose(VERB_ALGO, "seen REVOKE flag, check self-signed, rr %d", (int)i); - sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i); + sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i, + &reason); return (sec == sec_status_secure); } diff --git a/validator/val_nsec.c b/validator/val_nsec.c index 074e3810d..13d2c1166 100644 --- a/validator/val_nsec.c +++ b/validator/val_nsec.c @@ -183,6 +183,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, uint8_t* wc = NULL, *ce = NULL; int valid_nsec = 0; struct ub_packed_rrset_key* wc_nsec = NULL; + char* reason = NULL; /* If we have a NSEC at the same name, it must prove one * of two things @@ -190,7 +191,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, * 1) this is a delegation point and there is no DS * 2) this is not a delegation point */ if(nsec) { - sec = val_verify_rrset_entry(env, ve, nsec, kkey); + sec = val_verify_rrset_entry(env, ve, nsec, kkey, &reason); if(sec != sec_status_secure) { verbose(VERB_ALGO, "NSEC RRset for the " "referral did not verify."); @@ -219,7 +220,8 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, i++) { if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC)) continue; - sec = val_verify_rrset_entry(env, ve, rep->rrsets[i], kkey); + sec = val_verify_rrset_entry(env, ve, rep->rrsets[i], kkey, + &reason); if(sec != sec_status_secure) { verbose(VERB_ALGO, "NSEC for empty non-terminal " "did not verify."); diff --git a/validator/val_nsec3.c b/validator/val_nsec3.c index 883b807e6..b8b4065f4 100644 --- a/validator/val_nsec3.c +++ b/validator/val_nsec3.c @@ -1245,10 +1245,11 @@ list_is_secure(struct module_env* env, struct val_env* ve, { size_t i; enum sec_status sec; + char* reason = NULL; for(i=0; irk.type != htons(LDNS_RR_TYPE_NSEC3)) continue; - sec = val_verify_rrset_entry(env, ve, list[i], kkey); + sec = val_verify_rrset_entry(env, ve, list[i], kkey, &reason); if(sec != sec_status_secure) { verbose(VERB_ALGO, "NSEC3 did not verify"); return 0; diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c index eaa9d8c14..eb6bfd4ab 100644 --- a/validator/val_sigcrypt.c +++ b/validator/val_sigcrypt.c @@ -461,25 +461,28 @@ dnskeyset_needs(struct ub_packed_rrset_key* dnskey, uint8_t needs[]) 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) + struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, + char** reason) { enum sec_status sec; size_t i, num, numneeds; rbtree_t* sortree = NULL; /* make sure that for all DNSKEY algorithms there are valid sigs */ uint8_t needs[256]; /* 1 if need sig for that algorithm */ + int sawbogus = 0; num = rrset_get_sigcount(rrset); if(num == 0) { verbose(VERB_QUERY, "rrset failed to verify due to a lack of " "signatures"); + *reason = "no signatures"; return sec_status_bogus; } numneeds = dnskeyset_needs(dnskey, needs); for(i=0; inow, rrset, - dnskey, i, &sortree); + dnskey, i, &sortree, reason); /* see which algorithm has been fixed up */ if(sec == sec_status_secure) { uint8_t a = (uint8_t)rrset_get_sig_algo(rrset, i); @@ -490,19 +493,23 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, return sec; } } + else if(sec == sec_status_bogus) + sawbogus = 1; } verbose(VERB_ALGO, "rrset failed to verify: no valid signatures for " "%d algorithms", (int)numneeds); + if(!sawbogus) + *reason = "no signatures for all algorithms"; return sec_status_bogus; } 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) + size_t dnskey_idx, char** reason) { enum sec_status sec; - size_t i, num; + size_t i, num, numchecked = 0; rbtree_t* sortree = NULL; int buf_canon = 0; uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); @@ -512,6 +519,7 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, if(num == 0) { verbose(VERB_QUERY, "rrset failed to verify due to a lack of " "signatures"); + *reason = "no signatures"; return sec_status_bogus; } for(i=0; iscratch, env->scratch_buffer, ve, *env->now, rrset, - dnskey, dnskey_idx, i, &sortree, &buf_canon); + dnskey, dnskey_idx, i, &sortree, &buf_canon, reason); if(sec == sec_status_secure) return sec; + numchecked ++; } verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus"); + if(!numchecked) *reason = "signatures from unknown keys"; return sec_status_bogus; } @@ -534,7 +544,7 @@ enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, uint32_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_t** sortree) + struct rbtree_t** sortree, char** reason) { /* find matching keys and check them */ enum sec_status sec = sec_status_bogus; @@ -555,11 +565,12 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, /* see if key verifies */ sec = dnskey_verify_rrset_sig(env->scratch, env->scratch_buffer, ve, now, rrset, dnskey, i, - sig_idx, sortree, &buf_canon); + sig_idx, sortree, &buf_canon, reason); if(sec == sec_status_secure) return sec; } if(numchecked == 0) { + *reason = "signatures from unknown keys"; verbose(VERB_QUERY, "verify: could not find appropriate key"); return sec_status_bogus; } @@ -1075,7 +1086,7 @@ sigdate_error(const char* str, int32_t expi, int32_t incep, int32_t now) /** check rrsig dates */ static int check_dates(struct val_env* ve, uint32_t unow, - uint8_t* expi_p, uint8_t* incep_p) + uint8_t* expi_p, uint8_t* incep_p, char** reason) { /* read out the dates */ int32_t expi, incep, now; @@ -1094,6 +1105,7 @@ check_dates(struct val_env* ve, uint32_t unow, if(incep - expi > 0) { sigdate_error("verify: inception after expiration, " "signature bad", expi, incep, now); + *reason = "signature inception after expiration"; return 0; } if(incep - now > 0) { @@ -1104,6 +1116,7 @@ check_dates(struct val_env* ve, uint32_t unow, if(incep - now > skew) { sigdate_error("verify: signature bad, current time is" " before inception date", expi, incep, now); + *reason = "signature before inception date"; return 0; } sigdate_error("verify warning suspicious signature inception " @@ -1116,6 +1129,7 @@ check_dates(struct val_env* ve, uint32_t unow, if(now - expi > skew) { sigdate_error("verify: signature expired", expi, incep, now); + *reason = "signature expired"; return 0; } sigdate_error("verify warning suspicious signature expiration " @@ -1430,7 +1444,7 @@ dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, struct val_env* ve, uint32_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, - struct rbtree_t** sortree, int* buf_canon) + struct rbtree_t** sortree, int* buf_canon, char** reason) { enum sec_status sec; uint8_t* sig; /* RRSIG rdata */ @@ -1447,17 +1461,20 @@ dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, /* min length of rdatalen, fixed rrsig, root signer, 1 byte sig */ if(siglen < 2+20) { verbose(VERB_QUERY, "verify: signature too short"); + *reason = "signature too short"; 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"; return sec_status_bogus; } if(dnskey_get_protocol(dnskey, dnskey_idx) != LDNS_DNSSEC_KEYPROTO) { /* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */ verbose(VERB_QUERY, "verify: dnskey has wrong key protocol"); + *reason = "dnskey has wrong protocolnumber"; return sec_status_bogus; } @@ -1466,15 +1483,18 @@ dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, signer_len = dname_valid(signer, siglen-2-18); if(!signer_len) { verbose(VERB_QUERY, "verify: malformed signer name"); + *reason = "signer name malformed"; 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"; 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"; return sec_status_bogus; /* sig rdf is < 1 byte */ } sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len); @@ -1486,6 +1506,7 @@ dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, signer, 0, 0); log_nametypeclass(VERB_QUERY, "the key name is", dnskey->rk.dname, 0, 0); + *reason = "signer name mismatches key name"; return sec_status_bogus; } @@ -1493,29 +1514,33 @@ dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, /* memcmp works because type is in network format for rrset */ if(memcmp(sig+2, &rrset->rk.type, 2) != 0) { verbose(VERB_QUERY, "verify: wrong type covered"); + *reason = "signature covers wrong type"; 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"; 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"; return sec_status_bogus; } /* verify labels is in a valid range */ 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"; return sec_status_bogus; } /* original ttl, always ok */ /* verify inception, expiration dates */ - if(!check_dates(ve, now, sig+2+8, sig+2+12)) { + if(!check_dates(ve, now, sig+2+8, sig+2+12, reason)) { return sec_status_bogus; } @@ -1544,7 +1569,8 @@ dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, /* check if TTL is too high - reduce if so */ if(sec == sec_status_secure) { adjust_ttl(ve, now, rrset, sig+2+4, sig+2+8, sig+2+12); - } + } else if(sec == sec_status_bogus) + *reason = "signature crypto failed"; return sec; } diff --git a/validator/val_sigcrypt.h b/validator/val_sigcrypt.h index e395a9da6..95c702a09 100644 --- a/validator/val_sigcrypt.h +++ b/validator/val_sigcrypt.h @@ -146,13 +146,14 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); * @param ve: validator environment, date settings. * @param rrset: to be validated. * @param dnskey: DNSKEY rrset, keyset to try. + * @param reason: if bogus, a string returned, fixed or alloced in scratch. * @return SECURE if one key in the set verifies one rrsig. * UNCHECKED on allocation errors, unsupported algorithms, malformed data, * and BOGUS on verification failures (no keys match any signatures). */ 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); + struct ub_packed_rrset_key* dnskey, char** reason); /** * verify rrset against one specific dnskey (from rrset) @@ -161,12 +162,13 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, * @param rrset: to be validated. * @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. * @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); + struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, char** reason); /** * verify rrset, with dnskey rrset, for a specific rrsig in rrset @@ -178,13 +180,14 @@ enum sec_status dnskey_verify_rrset(struct module_env* env, * @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. * @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, uint32_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_t** sortree); + struct rbtree_t** sortree, char** reason); /** * verify rrset, with specific dnskey(from set), for a specific rrsig @@ -201,6 +204,7 @@ enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, * @param buf_canon: if true, the buffer is already canonical. * 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. * @return secure if this key signs this signature. unchecked on error or * bogus if it did not validate. */ @@ -208,7 +212,7 @@ enum sec_status dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, struct val_env* ve, uint32_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, - struct rbtree_t** sortree, int* buf_canon); + struct rbtree_t** sortree, int* buf_canon, char** reason); /** * canonical compare for two tree entries diff --git a/validator/val_utils.c b/validator/val_utils.c index e74d7afbb..d32ba0377 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -51,6 +51,7 @@ #include "util/net_help.h" #include "util/module.h" #include "util/regional.h" +#include "util/config_file.h" enum val_classification val_classify_response(uint16_t query_flags, struct query_info* origqinf, @@ -303,7 +304,8 @@ rrset_get_ttl(struct ub_packed_rrset_key* rrset) 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) + struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, + char** reason) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -325,7 +327,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); + sec = dnskeyset_verify_rrset(env, ve, rrset, keys, reason); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -357,7 +359,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) + struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, + char** reason) { /* temporary dnskey rrset-key */ struct ub_packed_rrset_key dnskey; @@ -370,7 +373,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, dnskey.rk.dname_len = kkey->namelen; dnskey.entry.key = &dnskey; dnskey.entry.data = kd->rrset_data; - sec = val_verify_rrset(env, ve, rrset, &dnskey); + sec = val_verify_rrset(env, ve, rrset, &dnskey, reason); return sec; } @@ -378,10 +381,10 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, static enum sec_status 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) + struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason) { enum sec_status sec = sec_status_bogus; - size_t i, num; + size_t i, num, numchecked = 0, numhashok = 0; num = rrset_get_count(dnskey_rrset); for(i=0; ienv->cfg->val_log_level < 2 || !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(vq->errinf) { + struct config_strlist* q = vq->errinf; + while(q->next) + q = q->next; + q->next = p; + } else vq->errinf = p; +} + +void val_errinf_origin(struct module_qstate* qstate, struct val_qstate* vq, + struct sock_list *origin) +{ + struct sock_list* p; + if(qstate->env->cfg->val_log_level < 2) + 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 from "); + 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)); + val_errinf(qstate, vq, buf); + } +} + +char* val_errinf_to_str(struct module_qstate* qstate, struct val_qstate* vq) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct config_strlist* s; + char dname[LDNS_MAX_DOMAINLEN+1]; + char* t = ldns_rr_type2str(qstate->qinfo.qtype); + char* c = ldns_rr_class2str(qstate->qinfo.qclass); + if(!t || !c) { + free(t); + free(c); + log_err("malloc failure in errinf_to_str"); + return NULL; + } + dname_str(qstate->qinfo.qname, dname); + snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c); + free(t); + free(c); + left -= strlen(p); p += strlen(p); + if(!vq->errinf) + snprintf(p, left, " misc failure"); + else for(s=vq->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 val_errinf_rrset(struct module_qstate* qstate, struct val_qstate* vq, + struct ub_packed_rrset_key *rr) +{ + char buf[1024]; + char dname[LDNS_MAX_DOMAINLEN+1]; + char *t, *c; + if(qstate->env->cfg->val_log_level < 2 || !rr) + return; + t = ldns_rr_type2str(ntohs(rr->rk.type)); + c = ldns_rr_class2str(ntohs(rr->rk.rrset_class)); + if(!t || !c) { + free(t); + free(c); + log_err("malloc failure in errinf_rrset"); + return; + } + dname_str(qstate->qinfo.qname, dname); + snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c); + free(t); + free(c); + val_errinf(qstate, vq, buf); +} + +void val_errinf_dname(struct module_qstate* qstate, struct val_qstate* vq, + const char* str, uint8_t* dname) +{ + char b[1024]; + char buf[LDNS_MAX_DOMAINLEN+1]; + if(qstate->env->cfg->val_log_level < 2 || !str || !dname) + return; + dname_str(dname, buf); + snprintf(b, sizeof(b), "%s %s", str, buf); + val_errinf(qstate, vq, b); +} diff --git a/validator/val_utils.h b/validator/val_utils.h index e59867a41..ab6e518ae 100644 --- a/validator/val_utils.h +++ b/validator/val_utils.h @@ -52,6 +52,8 @@ struct regional; struct val_anchors; struct rrset_cache; struct sock_list; +struct module_qstate; +struct val_qstate; /** * Response classifications for the validator. The different types of proofs. @@ -117,10 +119,12 @@ void val_find_signer(enum val_classification subtype, * @param ve: validator environment (verification settings) * @param rrset: what to verify * @param keys: dnskey rrset to verify with. + * @param reason: reason of failure. Fixed string or alloced in scratch. * @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); + struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, + char** reason); /** * Verify RRset with keys from a keyset. @@ -128,11 +132,12 @@ enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, * @param ve: validator environment (verification settings) * @param rrset: what to verify * @param kkey: key_entry to verify with. + * @param reason: reason of failure. Fixed string or alloced in scratch. * @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); + struct key_entry_key* kkey, char** reason); /** * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but @@ -141,13 +146,14 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, * @param ve: validator environment (verification settings) * @param dnskey_rrset: DNSKEY rrset to verify * @param ds_rrset: DS rrset to verify with. + * @param reason: reason of failure. Fixed string or alloced in scratch. * @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); + struct ub_packed_rrset_key* ds_rrset, char** reason); /** * Verify new DNSKEYs with DS rrset. The DS contains hash values that should @@ -159,6 +165,7 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, * @param ve: validator environment (verification settings) * @param dnskey_rrset: DNSKEY rrset to verify * @param ds_rrset: DS rrset to verify with. + * @param reason: reason of failure. Fixed string or alloced in scratch. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS * rrset/DNSKEY pair indicate an secure end to the island of trust @@ -171,7 +178,7 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, 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); + struct ub_packed_rrset_key* ds_rrset, char** reason); /** * Determine if DS rrset is usable for validator or not. @@ -301,4 +308,55 @@ const char* val_classification_to_string(enum val_classification subtype); void val_blacklist(struct sock_list** blacklist, struct regional* region, struct sock_list* origin, int cross); +/** + * Append text to the error info for validation. + * @param qstate: query state. + * @param vq: validator state. + * @param str: copied into query region and appended. + * Failures to allocate are logged. + */ +void val_errinf(struct module_qstate* qstate, struct val_qstate* vq, + const char* str); + +/** + * Append text to error info: from 1.2.3.4 + * @param qstate: query state. + * @param vq: validator state. + * @param list: sock list with origin of trouble. + * Every element added. + * If NULL: nothing is added. + * if 0len element: 'from cache' is added. + */ +void val_errinf_origin(struct module_qstate* qstate, struct val_qstate* vq, + struct sock_list *origin); + +/** + * Append text to error info: for RRset name type class + * @param qstate: query state. + * @param vq: validator state. + * @param rr: rrset_key. + */ +void val_errinf_rrset(struct module_qstate* qstate, struct val_qstate* vq, + struct ub_packed_rrset_key *rr); + +/** + * Append text to error info: str dname + * @param qstate: query state. + * @param vq: validator state. + * @param str: explanation string + * @param dname: the dname. + * @param rr: rrset_key. + */ +void val_errinf_dname(struct module_qstate* qstate, struct val_qstate* vq, + const char* str, uint8_t* dname); + +/** + * Create error info in string + * @param qstate: query state. (for query name) + * @param vq: validator state. + * @return string or NULL on malloc failure (already logged). + * This string is malloced and has to be freed by caller. + */ +char* val_errinf_to_str(struct module_qstate* qstate, struct val_qstate* vq); + #endif /* VALIDATOR_VAL_UTILS_H */ diff --git a/validator/validator.c b/validator/validator.c index c94c81255..d155bf53a 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -409,6 +409,8 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, * trusted DNSKEY rrset that signs this response must already have been * completed. * + * @param qstate: query state. + * @param vq: validator query state. * @param env: module env for verify. * @param ve: validator env for verify. * @param qchase: query that was made. @@ -419,15 +421,16 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, * fail to verify. The message is then set to bogus. */ static int -validate_msg_signatures(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* key_entry) +validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, + struct module_env* env, struct val_env* ve, struct query_info* qchase, + struct reply_info* chase_reply, struct key_entry_key* key_entry) { uint8_t* sname; size_t i, slen; struct ub_packed_rrset_key* s; enum sec_status sec; int dname_seen = 0; + char* reason = NULL; /* validate the ANSWER section */ for(i=0; ian_numrrsets; i++) { @@ -448,13 +451,19 @@ validate_msg_signatures(struct module_env* env, struct val_env* ve, } /* Verify the answer rrset */ - sec = val_verify_rrset_entry(env, ve, s, key_entry); + sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason); /* 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)); + val_errinf(qstate, vq, reason); + if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) + val_errinf(qstate, vq, "for CNAME"); + else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) + val_errinf(qstate, vq, "for DNAME"); + val_errinf_origin(qstate, vq, qstate->reply_origin); chase_reply->security = sec_status_bogus; return 0; } @@ -471,13 +480,16 @@ validate_msg_signatures(struct module_env* env, struct val_env* ve, for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { s = chase_reply->rrsets[i]; - sec = val_verify_rrset_entry(env, ve, s, key_entry); + sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason); /* 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)); + val_errinf(qstate, vq, reason); + val_errinf_rrset(qstate, vq, s); + val_errinf_origin(qstate, vq, qstate->reply_origin); chase_reply->security = sec_status_bogus; return 0; } @@ -493,7 +505,8 @@ validate_msg_signatures(struct module_env* env, struct val_env* ve, /* 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); + (void)val_verify_rrset_entry(env, ve, s, key_entry, + &reason); /* the additional section can fail to be secure, * it is optional, check signature in case we need * to clean the additional section later. */ @@ -1493,6 +1506,15 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, return 1; } + if(key_entry_isbad(vq->key_entry)) { + log_nametypeclass(VERB_DETAIL, "Could not establish a chain " + "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; + val_errinf(qstate, vq, "while building chain of trust"); + return 1; + } + /* signerName being null is the indicator that this response was * unsigned */ if(vq->signer_name == NULL) { @@ -1500,14 +1522,8 @@ 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."); - vq->chase_reply->security = sec_status_bogus; - return 1; - } - - if(key_entry_isbad(vq->key_entry)) { - log_nametypeclass(VERB_DETAIL, "Could not establish a chain " - "of trust to keys for", vq->key_entry->name, - LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class); + val_errinf(qstate, vq, "no signatures"); + val_errinf_origin(qstate, vq, qstate->reply_origin); vq->chase_reply->security = sec_status_bogus; return 1; } @@ -1516,7 +1532,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, /* check signatures in the message; * answer and authority must be valid, additional is only checked. */ - if(!validate_msg_signatures(qstate->env, ve, &vq->qchase, + if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase, vq->chase_reply, vq->key_entry)) { /* workaround bad recursor out there that truncates (even * with EDNS4k) to 512 by removing RRSIG from auth section @@ -1533,6 +1549,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, vq->chase_reply->ar_numrrsets = 0; vq->chase_reply->rrset_count = vq->chase_reply->an_numrrsets; + vq->errinf = NULL; } else { verbose(VERB_DETAIL, "Validate: message contains " @@ -1610,6 +1627,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, log_err("validate: unhandled response subtype: %d", subtype); } + if(vq->chase_reply->security == sec_status_bogus) { + if(subtype == VAL_CLASS_POSITIVE) + val_errinf(qstate, vq, "wildcard"); + else val_errinf(qstate, vq, + val_classification_to_string(subtype)); + val_errinf(qstate, vq, "proof failed"); + val_errinf_origin(qstate, vq, qstate->reply_origin); + } return 1; } @@ -1860,7 +1885,14 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, vq->orig_msg->rep->ttl = ve->bogus_ttl; if(qstate->env->cfg->val_log_level >= 1) { - log_query_info(0, "validation failure", &qstate->qinfo); + if(qstate->env->cfg->val_log_level < 2) + log_query_info(0, "validation failure", + &qstate->qinfo); + else { + char* err = val_errinf_to_str(qstate, vq); + if(err) log_info(err); + free(err); + } } /* If we are in permissive mode, bogus gets indeterminate */ if(ve->permissive_mode) @@ -2118,15 +2150,19 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct key_entry_key* kkey = NULL; enum sec_status sec = sec_status_unchecked; + char* reason = NULL; if(!dnskey_rrset) { log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " "could not fetch DNSKEY rrset", ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); - if(qstate->env->cfg->harden_dnssec_stripped) + if(qstate->env->cfg->harden_dnssec_stripped) { + struct val_qstate* vq = (struct val_qstate*) + qstate->minfo[id]; + val_errinf(qstate, vq, "no DNSKEY rrset"); kkey = key_entry_create_bad(qstate->region, ta->name, ta->namelen, ta->dclass); - else kkey = key_entry_create_null(qstate->region, ta->name, + } else kkey = key_entry_create_null(qstate->region, ta->name, ta->namelen, ta->dclass, NULL_KEY_TTL, *qstate->env->now); if(!kkey) { @@ -2138,7 +2174,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, /* attempt to verify with trust anchor DS and DNSKEY */ if(ta->ds_rrset) { kkey = val_verify_new_DNSKEYs(qstate->region, qstate->env, ve, - dnskey_rrset, ta->ds_rrset); + dnskey_rrset, ta->ds_rrset, &reason); if(!kkey) { log_err("out of memory: verifying prime DS"); return NULL; @@ -2152,7 +2188,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, } if(sec != sec_status_secure && ta->dnskey_rrset) { sec = val_verify_rrset(qstate->env, ve, dnskey_rrset, - ta->dnskey_rrset); + ta->dnskey_rrset, &reason); verbose(VERB_DETAIL, "validate keys with anchor(DNSKEY): %s", sec_status_to_string(sec)); if(sec == sec_status_secure) { @@ -2172,10 +2208,13 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); /* NOTE: in this case, we should probably reject the trust * anchor for longer, perhaps forever. */ - if(qstate->env->cfg->harden_dnssec_stripped) + if(qstate->env->cfg->harden_dnssec_stripped) { + struct val_qstate* vq = (struct val_qstate*) + qstate->minfo[id]; + val_errinf(qstate, vq, reason); kkey = key_entry_create_bad(qstate->region, ta->name, ta->namelen, ta->dclass); - else kkey = key_entry_create_null(qstate->region, ta->name, + } else kkey = key_entry_create_null(qstate->region, ta->name, ta->namelen, ta->dclass, NULL_KEY_TTL, *qstate->env->now); if(!kkey) { @@ -2213,10 +2252,15 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, struct key_entry_key** ke) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; + char* reason = NULL; enum val_classification subtype; if(rcode != LDNS_RCODE_NOERROR) { + char* rc = ldns_pkt_rcode2str(rcode); /* errors here pretty much break validation */ verbose(VERB_DETAIL, "DS response was error, thus bogus"); + val_errinf(qstate, vq, rc); + val_errinf(qstate, vq, "no DS"); + free(rc); goto return_bogus; } @@ -2230,15 +2274,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."); + val_errinf(qstate, vq, "no DS record"); 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); + vq->key_entry, &reason); if(sec != sec_status_secure) { verbose(VERB_DETAIL, "DS rrset in DS response did " "not verify"); + val_errinf(qstate, vq, reason); goto return_bogus; } @@ -2291,6 +2337,8 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, case sec_status_bogus: verbose(VERB_DETAIL, "NSEC RRset for the " "referral did not prove no DS."); + val_errinf(qstate, vq, "NSEC DS absent proof " + "failed"); goto return_bogus; case sec_status_unchecked: default: @@ -2318,6 +2366,8 @@ 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."); + val_errinf(qstate, vq, "NSEC3 DS absent proof " + "failed"); goto return_bogus; case sec_status_insecure: case sec_status_unchecked: @@ -2330,10 +2380,20 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, * this is BOGUS. */ verbose(VERB_DETAIL, "DS %s ran out of options, so return " "bogus", val_classification_to_string(subtype)); + val_errinf(qstate, vq, "no DS but also no proof of that"); goto return_bogus; } else { verbose(VERB_QUERY, "Encountered an unhandled type of " "DS response, thus bogus."); + val_errinf(qstate, vq, "no DS and "); + if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) { + char* rc = ldns_pkt_rcode2str( + FLAGS_GET_RCODE(msg->rep->flags)); + val_errinf(qstate, vq, rc); + free(rc); + } else val_errinf(qstate, vq, + val_classification_to_string(subtype)); + val_errinf(qstate, vq, "message fails to prove that"); goto return_bogus; } return_bogus: @@ -2398,8 +2458,13 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, && vq->restart_count < VAL_MAX_RESTART_COUNT) { vq->empty_DS_name = olds; val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1); + vq->errinf = NULL; vq->restart_count++; } else { + if(key_entry_isbad(dske)) { + val_errinf_origin(qstate, vq, origin); + val_errinf_dname(qstate, vq, "for DS", qinfo->qname); + } /* NOTE: the reason for the DS to be not good (that is, * either bad or null) should have been logged by * dsResponseToKE. */ @@ -2433,6 +2498,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct key_entry_key* old = vq->key_entry; struct ub_packed_rrset_key* dnskey = NULL; + char* reason = NULL; if(rcode == LDNS_RCODE_NOERROR) dnskey = reply_find_answer_rrset(qinfo, msg->rep); @@ -2442,9 +2508,10 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to " "DNSKEY query."); if(vq->restart_count < VAL_MAX_RESTART_COUNT) { - vq->restart_count++; val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1); + vq->errinf = NULL; + vq->restart_count++; return; } vq->key_entry = key_entry_create_bad(qstate->region, @@ -2463,7 +2530,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, return; } vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env, - ve, dnskey, vq->ds_rrset); + ve, dnskey, vq->ds_rrset, &reason); if(!vq->key_entry) { log_err("out of memory in verify new DNSKEYs"); @@ -2477,18 +2544,23 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, if(vq->restart_count < VAL_MAX_RESTART_COUNT) { val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1); + vq->errinf = NULL; vq->restart_count++; vq->key_entry = old; return; } verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, " "thus bogus."); + val_errinf(qstate, vq, reason); + val_errinf_origin(qstate, vq, origin); + val_errinf_dname(qstate, vq, "for key", qinfo->qname); } vq->chain_blacklist = NULL; vq->state = VAL_VALIDATE_STATE; return; } vq->chain_blacklist = NULL; + vq->errinf = NULL; /* The DNSKEY validated, so cache it as a trusted key rrset. */ key_cache_insert(ve->kcache, vq->key_entry); @@ -2547,12 +2619,15 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, && vq->restart_count < VAL_MAX_RESTART_COUNT) { val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1); - vq->key_entry = NULL; + vq->errinf = NULL; vq->restart_count++; + vq->key_entry = NULL; vq->state = VAL_INIT_STATE; return; } vq->chain_blacklist = NULL; + val_errinf_origin(qstate, vq, origin); + val_errinf_dname(qstate, vq, "for trust anchor", ta->name); /* store the freshly primed entry in the cache */ key_cache_insert(ve->kcache, vq->key_entry); } @@ -2578,12 +2653,10 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, * @param rcode: rcode result value. * @param msg: result message (if rcode is OK). * @param qinfo: from the sub query state, query info. - * @param origin: the origin of msg. */ static void process_dlv_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) + int id, int rcode, struct dns_msg* msg, struct query_info* qinfo) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; @@ -2697,8 +2770,7 @@ val_inform_super(struct module_qstate* qstate, int id, return; } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DLV) { process_dlv_response(super, vq, id, qstate->return_rcode, - qstate->return_msg, &qstate->qinfo, - qstate->reply_origin); + qstate->return_msg, &qstate->qinfo); return; } log_err("internal error in validator: no inform_supers possible"); diff --git a/validator/validator.h b/validator/validator.h index a5bbb88a1..b4d0bf324 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -49,6 +49,7 @@ struct val_anchors; struct key_cache; struct key_entry_key; struct val_neg_cache; +struct config_strlist; /** * This is the TTL to use when a trust anchor fails to prime. A trust anchor @@ -236,6 +237,9 @@ struct val_qstate { dlv_ask_higher, /* ask again */ dlv_there_is_no_dlv /* got no DLV, sure of it */ } dlv_status; + + /** failure reason information if val-log-level is high */ + struct config_strlist* errinf; }; /**