]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- moved version number to 1.4.0 because of 1.3.4 release with only
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 7 Oct 2009 16:45:47 +0000 (16:45 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 7 Oct 2009 16:45:47 +0000 (16:45 +0000)
  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 <example.com. DNSKEY IN>: signature expired from
  192.0.2.4 for trust anchor example.com. while building chain of trust
  validation failure <www.example.com. A IN>: 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

16 files changed:
configure
configure.ac
doc/Changelog
testcode/testbound.c
testcode/unitverify.c
testdata/val_nsec3_nods_badsig.rpl [new file with mode: 0644]
testdata/val_secds_nosig.rpl [new file with mode: 0644]
validator/autotrust.c
validator/val_nsec.c
validator/val_nsec3.c
validator/val_sigcrypt.c
validator/val_sigcrypt.h
validator/val_utils.c
validator/val_utils.h
validator/validator.c
validator/validator.h

index 02b3a4f6fa09f5c1556e9d0ccafff8adf11ac568..9fe75aa2c25fd93c7043a34bc790acb4c88da224 100755 (executable)
--- 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 <unbound-bugs@nlnetlabs.nl>.
 #
@@ -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 <bug-autoconf@gnu.org>."
 _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'`\\"
 
index 5c4ef7b7bcde8a250f2f5f3314b416107de20ef8..eabdecdabd7be5e411dc8cff24c340dbcedb250c 100644 (file)
@@ -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
index 73aa3151fb43bf8469f7a8d4b559b9cd0861609b..c838335e0ec59e2d289bf802c50fa19f9d89ec9a 100644 (file)
@@ -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 <example.com. DNSKEY IN>: signature expired from
+         192.0.2.4 for trust anchor example.com. while building chain of trust
+         validation failure <www.example.com. A IN>: 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.
index 1552f3ee478c80fb8f0c514b15335c7a92a0eafb..c461b641c97bb7ff6d2da2a85fbbef621d645498 100644 (file)
@@ -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)++;
index b423a738b59c0e78f47e1f108a544aca67ee210c..b7723961883bf5d0bfd9cab0964bb559cbe5b74b 100644 (file)
@@ -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 (file)
index 0000000..8f02cd7
--- /dev/null
@@ -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 (file)
index 0000000..e4a20ba
--- /dev/null
@@ -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
index 5568300f0114e4e5735746c593bb9cfb4c23b909..1c99fb62201a6566c62cc74a3d59bfab55f89d71 100644 (file)
@@ -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);
 }
 
index 074e3810d2895b30154ca01d005c9a49bc7ba35a..13d2c11668da145bf358dfafe18b82a79f0840b6 100644 (file)
@@ -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.");
index 883b807e6d008d835199ce0e67db6658517501e4..b8b4065f4fc83f0e2a37a4173fe3a1d70909bf02 100644 (file)
@@ -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; i<num; i++) {
                if(list[i]->rk.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;
index eaa9d8c14fb31bd442ff7126e63e4ca8a0a8e9c3..eb6bfd4ab340bbb73b4208ffcc9e6ef7e5a1e332 100644 (file)
@@ -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; i<num; i++) {
                sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, 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; i<num; i++) {
@@ -522,11 +530,13 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
                buf_canon = 0;
                sec = dnskey_verify_rrset_sig(env->scratch, 
                        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;
 }
index e395a9da6f225ce5baaaef18f706a551b35ae8bc..95c702a096519ffdcf3cc5369c0f3bbd6ba2c499 100644 (file)
@@ -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
index e74d7afbb765644761e2cf2e7e3fa4c03cd9b4fd..d32ba0377334bde6b10ceaf0057f5fc7eb2bce27 100644 (file)
@@ -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; i<num; i++) {
                /* Skip DNSKEYs that don't match the basic criteria. */
@@ -391,6 +394,7 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
                   != ds_get_keytag(ds_rrset, ds_idx)) {
                        continue;
                }
+               numchecked++;
                verbose(VERB_ALGO, "attempt DS match algo %d keytag %d",
                        ds_get_key_algo(ds_rrset, ds_idx),
                        ds_get_keytag(ds_rrset, ds_idx));
@@ -402,24 +406,31 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
                        verbose(VERB_ALGO, "DS match attempt failed");
                        continue;
                }
+               numhashok++;
                verbose(VERB_ALGO, "DS match digest ok, trying signature");
 
                /* 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);
+                       dnskey_rrset, i, reason);
                if(sec == sec_status_secure) {
                        return sec;
                }
                /* If it didn't validate with the DNSKEY, try the next one! */
        }
+       if(numchecked == 0)
+               *reason = "no keys have a DS";
+       else if(numhashok == 0)
+               *reason = "DS hash mismatches key";
+       else if(!*reason)
+               *reason = "keyset not secured by DNSKEY that matches DS";
        return sec_status_bogus;
 }
 
 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)
 {
        /* as long as this is false, we can consider this DS rrset to be
         * equivalent to no DS rrset. */
@@ -433,6 +444,7 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
                != 0) {
                verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset "
                        "by name");
+               *reason = "DNSKEY RRset did not match DS RRset by name";
                return sec_status_bogus;
        }
 
@@ -462,7 +474,7 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
                has_useful_ds = true;
 
                sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, 
-                       ds_rrset, i);
+                       ds_rrset, i, reason);
                if(sec == sec_status_secure) {
                        verbose(VERB_ALGO, "DS matched DNSKEY.");
                        return sec_status_secure;
@@ -485,10 +497,10 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
 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)
 {
        enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, 
-               dnskey_rrset, ds_rrset);
+               dnskey_rrset, ds_rrset, reason);
 
        if(sec == sec_status_secure) {
                return key_entry_create_rrset(region, 
@@ -837,3 +849,117 @@ void val_blacklist(struct sock_list** blacklist, struct regional* region,
                sock_list_prepend(blacklist, origin);
        else    sock_list_merge(blacklist, region, origin);
 }
+
+void val_errinf(struct module_qstate* qstate, struct val_qstate* vq,
+       const char* str)
+{
+       struct config_strlist* p;
+       if(qstate->env->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);
+}
index e59867a41e9b66f23f803ade6790620f117b60f8..ab6e518aeb54b709ce82a82020974a1c02ec7ba4 100644 (file)
@@ -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 */
index c94c8125502b8ad8c0ac523b996efbbbdb48ffc1..d155bf53a009e4d12a94b2c9c5193c8afd8b2ca7 100644 (file)
@@ -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; i<chase_reply->an_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; i<chase_reply->an_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");
index a5bbb88a1ed1e965e6e82af6da4efa4ec72f34ac..b4d0bf32499d48ed301bc6cb4aa2ba8bbc1aeb94 100644 (file)
@@ -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;
 };
 
 /**