diff options
Diffstat (limited to 'usr.sbin')
41 files changed, 1545 insertions, 599 deletions
diff --git a/usr.sbin/unbound/acx_python.m4 b/usr.sbin/unbound/acx_python.m4 index 6fa925af168..99ffa254a8e 100644 --- a/usr.sbin/unbound/acx_python.m4 +++ b/usr.sbin/unbound/acx_python.m4 @@ -164,8 +164,11 @@ $ac_distutils_result]) AC_MSG_CHECKING([consistency of all components of python development environment]) AC_LANG_PUSH([C]) # save current global flags - LIBS="$ac_save_LIBS $PYTHON_LDFLAGS" - CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" + ac_save_LIBS="$LIBS" + ac_save_CPPFLAGS="$CPPFLAGS" + + LIBS="$LIBS $PYTHON_LDFLAGS" + CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS" AC_TRY_LINK([ #include <Python.h> ],[ diff --git a/usr.sbin/unbound/config.h.in b/usr.sbin/unbound/config.h.in index cacf294b908..bfadf52da3c 100644 --- a/usr.sbin/unbound/config.h.in +++ b/usr.sbin/unbound/config.h.in @@ -106,6 +106,9 @@ /* Define to 1 if you have the `fcntl' function. */ #undef HAVE_FCNTL +/* Define to 1 if you have the `FIPS_mode' function. */ +#undef HAVE_FIPS_MODE + /* Define to 1 if you have the `fork' function. */ #undef HAVE_FORK @@ -193,6 +196,9 @@ /* Define to 1 if you have the <netinet/in.h> header file. */ #undef HAVE_NETINET_IN_H +/* Use libnss for crypto */ +#undef HAVE_NSS + /* Define to 1 if you have the `OPENSSL_config' function. */ #undef HAVE_OPENSSL_CONFIG diff --git a/usr.sbin/unbound/daemon/cachedump.c b/usr.sbin/unbound/daemon/cachedump.c index 988e247352e..46c625f061a 100644 --- a/usr.sbin/unbound/daemon/cachedump.c +++ b/usr.sbin/unbound/daemon/cachedump.c @@ -44,11 +44,9 @@ #include "daemon/cachedump.h" #include "daemon/remote.h" #include "daemon/worker.h" -#include "daemon/daemon.h" #include "services/cache/rrset.h" #include "services/cache/dns.h" #include "services/cache/infra.h" -#include "services/modstack.h" #include "util/data/msgreply.h" #include "util/regional.h" #include "util/net_help.h" diff --git a/usr.sbin/unbound/daemon/daemon.c b/usr.sbin/unbound/daemon/daemon.c index 9d6ce9fe47f..b17d5488184 100644 --- a/usr.sbin/unbound/daemon/daemon.c +++ b/usr.sbin/unbound/daemon/daemon.c @@ -55,6 +55,11 @@ #ifdef HAVE_OPENSSL_ENGINE_H #include <openssl/engine.h> #endif + +#ifdef HAVE_NSS +#include <nss3/nss.h> +#endif + #include <ldns/ldns.h> #include "daemon/daemon.h" #include "daemon/worker.h" @@ -189,20 +194,25 @@ daemon_init(void) #endif /* USE_WINSOCK */ signal_handling_record(); checklock_start(); +#ifdef HAVE_SSL ERR_load_crypto_strings(); ERR_load_SSL_strings(); -#ifdef HAVE_OPENSSL_CONFIG +# ifdef HAVE_OPENSSL_CONFIG OPENSSL_config("unbound"); -#endif -#ifdef USE_GOST +# endif +# ifdef USE_GOST (void)ldns_key_EVP_load_gost_id(); -#endif +# endif OpenSSL_add_all_algorithms(); -#if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS +# if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS /* grab the COMP method ptr because openssl leaks it */ comp_meth = (void*)SSL_COMP_get_compression_methods(); -#endif +# endif (void)SSL_library_init(); +#elif defined(HAVE_NSS) + if(NSS_NoDB_Init(NULL) != SECSuccess) + fatal_exit("could not init NSS"); +#endif /* HAVE_SSL or HAVE_NSS */ #ifdef HAVE_TZSET /* init timezone info while we are not chrooted yet */ tzset(); @@ -530,31 +540,37 @@ daemon_delete(struct daemon* daemon) free(daemon->chroot); free(daemon->pidfile); free(daemon->env); +#ifdef HAVE_SSL SSL_CTX_free((SSL_CTX*)daemon->listen_sslctx); SSL_CTX_free((SSL_CTX*)daemon->connect_sslctx); +#endif free(daemon); #ifdef LEX_HAS_YYLEX_DESTROY /* lex cleanup */ ub_c_lex_destroy(); #endif /* libcrypto cleanup */ -#if defined(USE_GOST) && defined(HAVE_LDNS_KEY_EVP_UNLOAD_GOST) +#ifdef HAVE_SSL +# if defined(USE_GOST) && defined(HAVE_LDNS_KEY_EVP_UNLOAD_GOST) ldns_key_EVP_unload_gost(); -#endif -#if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS && HAVE_DECL_SK_SSL_COMP_POP_FREE -#ifndef S_SPLINT_S +# endif +# if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS && HAVE_DECL_SK_SSL_COMP_POP_FREE +# ifndef S_SPLINT_S sk_SSL_COMP_pop_free(comp_meth, (void(*)())CRYPTO_free); -#endif -#endif -#ifdef HAVE_OPENSSL_CONFIG +# endif +# endif +# ifdef HAVE_OPENSSL_CONFIG EVP_cleanup(); ENGINE_cleanup(); CONF_modules_free(); -#endif +# endif CRYPTO_cleanup_all_ex_data(); /* safe, no more threads right now */ ERR_remove_state(0); ERR_free_strings(); RAND_cleanup(); +#elif defined(HAVE_NSS) + NSS_Shutdown(); +#endif /* HAVE_SSL or HAVE_NSS */ checklock_stop(); #ifdef USE_WINSOCK if(WSACleanup() != 0) { diff --git a/usr.sbin/unbound/doc/README b/usr.sbin/unbound/doc/README index c150f7bd677..019b696de16 100644 --- a/usr.sbin/unbound/doc/README +++ b/usr.sbin/unbound/doc/README @@ -1,4 +1,4 @@ -README for Unbound 1.4.17 +README for Unbound 1.4.18 Copyright 2007 NLnet Labs http://unbound.net diff --git a/usr.sbin/unbound/doc/example.conf.in b/usr.sbin/unbound/doc/example.conf.in index 0378d045e43..f53eb3d1ace 100644 --- a/usr.sbin/unbound/doc/example.conf.in +++ b/usr.sbin/unbound/doc/example.conf.in @@ -1,7 +1,7 @@ # # Example configuration file. # -# See unbound.conf(5) man page, version 1.4.17. +# See unbound.conf(5) man page, version 1.4.18. # # this is a comment. diff --git a/usr.sbin/unbound/doc/libunbound.3.in b/usr.sbin/unbound/doc/libunbound.3.in index 8dacacd42b7..714fc840fcb 100644 --- a/usr.sbin/unbound/doc/libunbound.3.in +++ b/usr.sbin/unbound/doc/libunbound.3.in @@ -1,4 +1,4 @@ -.TH "libunbound" "3" "May 24, 2012" "NLnet Labs" "unbound 1.4.17" +.TH "libunbound" "3" "Aug 2, 2012" "NLnet Labs" "unbound 1.4.18" .\" .\" libunbound.3 -- unbound library functions manual .\" @@ -42,7 +42,7 @@ .B ub_ctx_zone_remove, .B ub_ctx_data_add, .B ub_ctx_data_remove -\- Unbound DNS validating resolver 1.4.17 functions. +\- Unbound DNS validating resolver 1.4.18 functions. .SH "SYNOPSIS" .LP .B #include <unbound.h> diff --git a/usr.sbin/unbound/doc/unbound-anchor.8.in b/usr.sbin/unbound/doc/unbound-anchor.8.in index b7f0bdaa43e..6479af102a1 100644 --- a/usr.sbin/unbound/doc/unbound-anchor.8.in +++ b/usr.sbin/unbound/doc/unbound-anchor.8.in @@ -1,4 +1,4 @@ -.TH "unbound-anchor" "8" "May 24, 2012" "NLnet Labs" "unbound 1.4.17" +.TH "unbound-anchor" "8" "Aug 2, 2012" "NLnet Labs" "unbound 1.4.18" .\" .\" unbound-anchor.8 -- unbound anchor maintenance utility manual .\" diff --git a/usr.sbin/unbound/doc/unbound-checkconf.8.in b/usr.sbin/unbound/doc/unbound-checkconf.8.in index fdd7528a4fb..167d85bd42e 100644 --- a/usr.sbin/unbound/doc/unbound-checkconf.8.in +++ b/usr.sbin/unbound/doc/unbound-checkconf.8.in @@ -1,4 +1,4 @@ -.TH "unbound-checkconf" "8" "May 24, 2012" "NLnet Labs" "unbound 1.4.17" +.TH "unbound-checkconf" "8" "Aug 2, 2012" "NLnet Labs" "unbound 1.4.18" .\" .\" unbound-checkconf.8 -- unbound configuration checker manual .\" diff --git a/usr.sbin/unbound/doc/unbound-control.8.in b/usr.sbin/unbound/doc/unbound-control.8.in index 575f897829d..a5c50704a57 100644 --- a/usr.sbin/unbound/doc/unbound-control.8.in +++ b/usr.sbin/unbound/doc/unbound-control.8.in @@ -1,4 +1,4 @@ -.TH "unbound-control" "8" "May 24, 2012" "NLnet Labs" "unbound 1.4.17" +.TH "unbound-control" "8" "Aug 2, 2012" "NLnet Labs" "unbound 1.4.18" .\" .\" unbound-control.8 -- unbound remote control manual .\" @@ -127,6 +127,9 @@ Remove all information at or below the name from the cache. The rrsets and key entries are removed so that new lookups will be performed. This needs to walk and inspect the entire cache, and is a slow operation. .TP +.B flush_bogus +Remove all bogus data from the cache. +.TP .B flush_stats Reset statistics to zero. .TP diff --git a/usr.sbin/unbound/doc/unbound.8.in b/usr.sbin/unbound/doc/unbound.8.in index 06cf588d026..025890575d9 100644 --- a/usr.sbin/unbound/doc/unbound.8.in +++ b/usr.sbin/unbound/doc/unbound.8.in @@ -1,4 +1,4 @@ -.TH "unbound" "8" "May 24, 2012" "NLnet Labs" "unbound 1.4.17" +.TH "unbound" "8" "Aug 2, 2012" "NLnet Labs" "unbound 1.4.18" .\" .\" unbound.8 -- unbound manual .\" @@ -10,7 +10,7 @@ .SH "NAME" .LP .B unbound -\- Unbound DNS validating resolver 1.4.17. +\- Unbound DNS validating resolver 1.4.18. .SH "SYNOPSIS" .LP .B unbound diff --git a/usr.sbin/unbound/doc/unbound.conf.5.in b/usr.sbin/unbound/doc/unbound.conf.5.in index 32188752c70..5aa4a7913b8 100644 --- a/usr.sbin/unbound/doc/unbound.conf.5.in +++ b/usr.sbin/unbound/doc/unbound.conf.5.in @@ -1,4 +1,4 @@ -.TH "unbound.conf" "5" "May 24, 2012" "NLnet Labs" "unbound 1.4.17" +.TH "unbound.conf" "5" "Aug 2, 2012" "NLnet Labs" "unbound 1.4.18" .\" .\" unbound.conf.5 -- unbound.conf manual .\" @@ -176,7 +176,7 @@ to 0, or if do_tcp is "no", no TCP queries from clients are accepted. Number of bytes size to advertise as the EDNS reassembly buffer size. This is the value put into datagrams over UDP towards peers. The actual buffer size is determined by msg\-buffer\-size (both for TCP and UDP). Do -not set lower than that value. Default is 4096 which is RFC recommended. +not set higher than that value. Default is 4096 which is RFC recommended. If you have fragmentation reassembly problems, usually seen as timeouts, then a value of 1480 can fix it. Setting to 512 bypasses even the most stringent path MTU problems, but is seen as extreme, since the amount diff --git a/usr.sbin/unbound/iterator/iter_fwd.c b/usr.sbin/unbound/iterator/iter_fwd.c index 04976db18b8..17ca566746d 100644 --- a/usr.sbin/unbound/iterator/iter_fwd.c +++ b/usr.sbin/unbound/iterator/iter_fwd.c @@ -250,20 +250,22 @@ read_forwards(struct iter_forwards* fwd, struct config_file* cfg) struct config_stub* s; for(s = cfg->forwards; s; s = s->next) { struct delegpt* dp; - if(!(dp=read_fwds_name(s)) || - !read_fwds_host(s, dp) || - !read_fwds_addr(s, dp)) + if(!(dp=read_fwds_name(s))) return 0; + if(!read_fwds_host(s, dp) || !read_fwds_addr(s, dp)) { + delegpt_free_mlc(dp); + return 0; + } /* set flag that parent side NS information is included. * Asking a (higher up) server on the internet is not useful */ /* the flag is turned off for 'forward-first' so that the * last resort will ask for parent-side NS record and thus * fallback to the internet name servers on a failure */ dp->has_parent_side_NS = (uint8_t)!s->isfirst; - if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp)) - return 0; verbose(VERB_QUERY, "Forward zone server list:"); delegpt_log(VERB_QUERY, dp); + if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp)) + return 0; } return 1; } diff --git a/usr.sbin/unbound/iterator/iter_hints.c b/usr.sbin/unbound/iterator/iter_hints.c index cfb9db7abb8..09e4731135d 100644 --- a/usr.sbin/unbound/iterator/iter_hints.c +++ b/usr.sbin/unbound/iterator/iter_hints.c @@ -126,32 +126,35 @@ compile_time_root_prime(int do_ip4, int do_ip6) return NULL; dp->has_parent_side_NS = 1; if(do_ip4) { - if(!ah(dp, "A.ROOT-SERVERS.NET.", "198.41.0.4")) return 0; - if(!ah(dp, "B.ROOT-SERVERS.NET.", "192.228.79.201")) return 0; - if(!ah(dp, "C.ROOT-SERVERS.NET.", "192.33.4.12")) return 0; - if(!ah(dp, "D.ROOT-SERVERS.NET.", "128.8.10.90")) return 0; - if(!ah(dp, "E.ROOT-SERVERS.NET.", "192.203.230.10")) return 0; - if(!ah(dp, "F.ROOT-SERVERS.NET.", "192.5.5.241")) return 0; - if(!ah(dp, "G.ROOT-SERVERS.NET.", "192.112.36.4")) return 0; - if(!ah(dp, "H.ROOT-SERVERS.NET.", "128.63.2.53")) return 0; - if(!ah(dp, "I.ROOT-SERVERS.NET.", "192.36.148.17")) return 0; - if(!ah(dp, "J.ROOT-SERVERS.NET.", "192.58.128.30")) return 0; - if(!ah(dp, "K.ROOT-SERVERS.NET.", "193.0.14.129")) return 0; - if(!ah(dp, "L.ROOT-SERVERS.NET.", "199.7.83.42")) return 0; - if(!ah(dp, "M.ROOT-SERVERS.NET.", "202.12.27.33")) return 0; + if(!ah(dp, "A.ROOT-SERVERS.NET.", "198.41.0.4")) goto failed; + if(!ah(dp, "B.ROOT-SERVERS.NET.", "192.228.79.201")) goto failed; + if(!ah(dp, "C.ROOT-SERVERS.NET.", "192.33.4.12")) goto failed; + if(!ah(dp, "D.ROOT-SERVERS.NET.", "128.8.10.90")) goto failed; + if(!ah(dp, "E.ROOT-SERVERS.NET.", "192.203.230.10")) goto failed; + if(!ah(dp, "F.ROOT-SERVERS.NET.", "192.5.5.241")) goto failed; + if(!ah(dp, "G.ROOT-SERVERS.NET.", "192.112.36.4")) goto failed; + if(!ah(dp, "H.ROOT-SERVERS.NET.", "128.63.2.53")) goto failed; + if(!ah(dp, "I.ROOT-SERVERS.NET.", "192.36.148.17")) goto failed; + if(!ah(dp, "J.ROOT-SERVERS.NET.", "192.58.128.30")) goto failed; + if(!ah(dp, "K.ROOT-SERVERS.NET.", "193.0.14.129")) goto failed; + if(!ah(dp, "L.ROOT-SERVERS.NET.", "199.7.83.42")) goto failed; + if(!ah(dp, "M.ROOT-SERVERS.NET.", "202.12.27.33")) goto failed; } if(do_ip6) { - if(!ah(dp, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) return 0; - if(!ah(dp, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) return 0; - if(!ah(dp, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) return 0; - if(!ah(dp, "H.ROOT-SERVERS.NET.", "2001:500:1::803f:235")) return 0; - if(!ah(dp, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) return 0; - if(!ah(dp, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) return 0; - if(!ah(dp, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) return 0; - if(!ah(dp, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) return 0; - if(!ah(dp, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) return 0; + if(!ah(dp, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) goto failed; + if(!ah(dp, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) goto failed; + if(!ah(dp, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) goto failed; + if(!ah(dp, "H.ROOT-SERVERS.NET.", "2001:500:1::803f:235")) goto failed; + if(!ah(dp, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) goto failed; + if(!ah(dp, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) goto failed; + if(!ah(dp, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) goto failed; + if(!ah(dp, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) goto failed; + if(!ah(dp, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) goto failed; } return dp; +failed: + delegpt_free_mlc(dp); + return 0; } /** insert new hint info into hint structure */ @@ -253,17 +256,19 @@ read_stubs(struct iter_hints* hints, struct config_file* cfg) struct config_stub* s; struct delegpt* dp; for(s = cfg->stubs; s; s = s->next) { - if(!(dp=read_stubs_name(s)) || - !read_stubs_host(s, dp) || - !read_stubs_addr(s, dp)) + if(!(dp=read_stubs_name(s))) + return 0; + if(!read_stubs_host(s, dp) || !read_stubs_addr(s, dp)) { + delegpt_free_mlc(dp); return 0; + } /* the flag is turned off for 'stub-first' so that the * last resort will ask for parent-side NS record and thus * fallback to the internet name servers on a failure */ dp->has_parent_side_NS = (uint8_t)!s->isfirst; + delegpt_log(VERB_QUERY, dp); if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime)) return 0; - delegpt_log(VERB_QUERY, dp); } return 1; } diff --git a/usr.sbin/unbound/iterator/iter_utils.c b/usr.sbin/unbound/iterator/iter_utils.c index c7a3f4f5295..a500c75e786 100644 --- a/usr.sbin/unbound/iterator/iter_utils.c +++ b/usr.sbin/unbound/iterator/iter_utils.c @@ -418,13 +418,14 @@ dns_copy_msg(struct dns_msg* from, struct regional* region) return m; } -int +void iter_dns_store(struct module_env* env, struct query_info* msgqinf, struct reply_info* msgrep, int is_referral, uint32_t leeway, int pside, struct regional* region) { - return dns_cache_store(env, msgqinf, msgrep, is_referral, leeway, - pside, region); + if(!dns_cache_store(env, msgqinf, msgrep, is_referral, leeway, + pside, region)) + log_err("out of memory: cannot store data in cache"); } int diff --git a/usr.sbin/unbound/iterator/iter_utils.h b/usr.sbin/unbound/iterator/iter_utils.h index 4fb8b005c19..8f5a291af67 100644 --- a/usr.sbin/unbound/iterator/iter_utils.h +++ b/usr.sbin/unbound/iterator/iter_utils.h @@ -124,9 +124,13 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional); * @param pside: true if dp is parentside, thus message is 'fresh' and NS * can be prefetch-updates. * @param region: to copy modified (cache is better) rrs back to. - * @return 0 on alloc error (out of memory). + * @return void, because we are not interested in alloc errors, + * the iterator and validator can operate on the results in their + * scratch space (the qstate.region) and are not dependent on the cache. + * It is useful to log the alloc failure (for the server operator), + * but the query resolution can continue without cache storage. */ -int iter_dns_store(struct module_env* env, struct query_info* qinf, +void iter_dns_store(struct module_env* env, struct query_info* qinf, struct reply_info* rep, int is_referral, uint32_t leeway, int pside, struct regional* region); diff --git a/usr.sbin/unbound/iterator/iterator.c b/usr.sbin/unbound/iterator/iterator.c index af20c4261b7..14f1bc6a064 100644 --- a/usr.sbin/unbound/iterator/iterator.c +++ b/usr.sbin/unbound/iterator/iterator.c @@ -259,9 +259,7 @@ error_response_cache(struct module_qstate* qstate, int id, int rcode) /* do not waste time trying to validate this servfail */ err.security = sec_status_indeterminate; verbose(VERB_ALGO, "store error response in message cache"); - if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL)) { - log_err("error_response_cache: could not store error (nomem)"); - } + iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL); return error_response(qstate, id, rcode); } @@ -1432,7 +1430,24 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, verbose(VERB_ALGO, "No more query targets, attempting last resort"); log_assert(iq->dp); - if(!iq->dp->has_parent_side_NS) { + if(!iq->dp->has_parent_side_NS && dname_is_root(iq->dp->name)) { + struct delegpt* p = hints_lookup_root(qstate->env->hints, + iq->qchase.qclass); + if(p) { + struct delegpt_ns* ns; + struct delegpt_addr* a; + for(ns = p->nslist; ns; ns=ns->next) { + (void)delegpt_add_ns(iq->dp, qstate->region, + ns->name, (int)ns->lame); + } + for(a = p->target_list; a; a=a->next_target) { + (void)delegpt_add_addr(iq->dp, qstate->region, + &a->addr, a->addrlen, a->bogus, + a->lame); + } + } + iq->dp->has_parent_side_NS = 1; + } else if(!iq->dp->has_parent_side_NS) { if(!iter_lookup_parent_NS_from_cache(qstate->env, iq->dp, qstate->region, &qstate->qinfo) || !iq->dp->has_parent_side_NS) { @@ -1541,8 +1556,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, * the final state (i.e., on answer). */ static int -processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, - int id) +processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id) { struct module_qstate* subq = NULL; verbose(VERB_ALGO, "processDSNSFind"); @@ -1906,13 +1920,20 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point && !(iq->chase_flags&BIT_RD) && iter_ds_toolow(iq->response, iq->dp) - && iter_dp_cangodown(&iq->qchase, iq->dp)) + && iter_dp_cangodown(&iq->qchase, iq->dp)) { + /* close down outstanding requests to be discarded */ + outbound_list_clear(&iq->outlist); + iq->num_current_queries = 0; + fptr_ok(fptr_whitelist_modenv_detach_subs( + qstate->env->detach_subs)); + (*qstate->env->detach_subs)(qstate); + iq->num_target_queries = 0; return processDSNSFind(qstate, iq, id); - if(!iter_dns_store(qstate->env, &iq->response->qinfo, + } + iter_dns_store(qstate->env, &iq->response->qinfo, iq->response->rep, 0, qstate->prefetch_leeway, iq->dp&&iq->dp->has_parent_side_NS, - qstate->region)) - return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + qstate->region); /* close down outstanding requests to be discarded */ outbound_list_clear(&iq->outlist); iq->num_current_queries = 0; @@ -1949,10 +1970,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, )) { /* Store the referral under the current query */ /* no prefetch-leeway, since its not the answer */ - if(!iter_dns_store(qstate->env, &iq->response->qinfo, - iq->response->rep, 1, 0, 0, NULL)) - return error_response(qstate, id, - LDNS_RCODE_SERVFAIL); + iter_dns_store(qstate->env, &iq->response->qinfo, + iq->response->rep, 1, 0, 0, NULL); if(iq->store_parent_NS) iter_store_parentside_NS(qstate->env, iq->response->rep); @@ -2032,8 +2051,15 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point && !(iq->chase_flags&BIT_RD) && iter_ds_toolow(iq->response, iq->dp) - && iter_dp_cangodown(&iq->qchase, iq->dp)) + && iter_dp_cangodown(&iq->qchase, iq->dp)) { + outbound_list_clear(&iq->outlist); + iq->num_current_queries = 0; + fptr_ok(fptr_whitelist_modenv_detach_subs( + qstate->env->detach_subs)); + (*qstate->env->detach_subs)(qstate); + iq->num_target_queries = 0; return processDSNSFind(qstate, iq, id); + } /* Process the CNAME response. */ if(!handle_cname_response(qstate, iq, iq->response, &sname, &snamelen)) @@ -2042,10 +2068,9 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, /* NOTE : set referral=1, so that rrsets get stored but not * the partial query answer (CNAME only). */ /* prefetchleeway applied because this updates answer parts */ - if(!iter_dns_store(qstate->env, &iq->response->qinfo, + iter_dns_store(qstate->env, &iq->response->qinfo, iq->response->rep, 1, qstate->prefetch_leeway, - iq->dp&&iq->dp->has_parent_side_NS, NULL)) - return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + iq->dp&&iq->dp->has_parent_side_NS, NULL); /* set the current request's qname to the new value. */ iq->qchase.qname = sname; iq->qchase.qname_len = snamelen; @@ -2555,12 +2580,10 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, * but only if we did recursion. The nonrecursion referral * from cache does not need to be stored in the msg cache. */ if(qstate->query_flags&BIT_RD) { - if(!iter_dns_store(qstate->env, &qstate->qinfo, + iter_dns_store(qstate->env, &qstate->qinfo, iq->response->rep, 0, qstate->prefetch_leeway, iq->dp&&iq->dp->has_parent_side_NS, - qstate->region)) - return error_response(qstate, id, - LDNS_RCODE_SERVFAIL); + qstate->region); } } qstate->return_rcode = LDNS_RCODE_NOERROR; diff --git a/usr.sbin/unbound/ltmain.sh b/usr.sbin/unbound/ltmain.sh index aa5624c8161..78cf0175299 100644 --- a/usr.sbin/unbound/ltmain.sh +++ b/usr.sbin/unbound/ltmain.sh @@ -8050,7 +8050,7 @@ EOF elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; - *) func_apped perm_rpath " $libdir" ;; + *) func_append perm_rpath " $libdir" ;; esac fi done diff --git a/usr.sbin/unbound/services/mesh.c b/usr.sbin/unbound/services/mesh.c index f6fd288adf8..5c66caf3236 100644 --- a/usr.sbin/unbound/services/mesh.c +++ b/usr.sbin/unbound/services/mesh.c @@ -676,6 +676,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, /* find it, if not, create it */ struct mesh_area* mesh = qstate->env->mesh; struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime); + int was_detached; if(mesh_detect_cycle_found(qstate, sub)) { verbose(VERB_ALGO, "attach failed, cycle detected"); return 0; @@ -706,9 +707,12 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, *newq = &sub->s; } else *newq = NULL; + was_detached = (sub->super_set.count == 0); if(!mesh_state_attachment(qstate->mesh_info, sub)) return 0; - if(!sub->reply_list && !sub->cb_list && sub->super_set.count == 1) { + /* if it was a duplicate attachment, the count was not zero before */ + if(!sub->reply_list && !sub->cb_list && was_detached && + sub->super_set.count == 1) { /* it used to be detached, before this one got added */ log_assert(mesh->num_detached_states > 0); mesh->num_detached_states--; @@ -735,16 +739,20 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub) superref->s = super; subref->node.key = subref; subref->s = sub; -#ifdef UNBOUND_DEBUG - n = -#endif - rbtree_insert(&sub->super_set, &superref->node); - log_assert(n != NULL); + if(!rbtree_insert(&sub->super_set, &superref->node)) { + /* this should not happen, iterator and validator do not + * attach subqueries that are identical. */ + /* already attached, we are done, nothing todo. + * since superref and subref already allocated in region, + * we cannot free them */ + return 1; + } #ifdef UNBOUND_DEBUG n = #endif rbtree_insert(&super->sub_set, &subref->node); - log_assert(n != NULL); + log_assert(n != NULL); /* we checked above if statement, the reverse + administration should not fail now, unless they are out of sync */ return 1; } diff --git a/usr.sbin/unbound/services/outside_network.c b/usr.sbin/unbound/services/outside_network.c index 24d65db3993..ba40d474dd2 100644 --- a/usr.sbin/unbound/services/outside_network.c +++ b/usr.sbin/unbound/services/outside_network.c @@ -297,9 +297,11 @@ decomission_pending_tcp(struct outside_network* outnet, struct pending_tcp* pend) { if(pend->c->ssl) { +#ifdef HAVE_SSL SSL_shutdown(pend->c->ssl); SSL_free(pend->c->ssl); pend->c->ssl = NULL; +#endif } comm_point_close(pend->c); pend->next_free = outnet->tcp_free; diff --git a/usr.sbin/unbound/smallapp/unbound-control.c b/usr.sbin/unbound/smallapp/unbound-control.c index 58be7b7abfc..e438b3d20a1 100644 --- a/usr.sbin/unbound/smallapp/unbound-control.c +++ b/usr.sbin/unbound/smallapp/unbound-control.c @@ -93,6 +93,7 @@ usage() printf(" flush_type <name> <type> flush name, type from cache\n"); printf(" flush_zone <name> flush everything at or under name\n"); printf(" from rr and dnssec caches\n"); + printf(" flush_bogus flush all bogus data\n"); printf(" flush_stats flush statistics, make zero\n"); printf(" flush_requestlist drop queries that are worked on\n"); printf(" dump_requestlist show what is worked on\n"); @@ -379,7 +380,8 @@ int main(int argc, char* argv[]) if(!RAND_status()) { /* try to seed it */ unsigned char buf[256]; - unsigned int v, seed=(unsigned)time(NULL) ^ (unsigned)getpid(); + unsigned int seed=(unsigned)time(NULL) ^ (unsigned)getpid(); + unsigned int v = seed; size_t i; for(i=0; i<256/sizeof(v); i++) { memmove(buf+i*sizeof(v), &v, sizeof(v)); diff --git a/usr.sbin/unbound/smallapp/unbound-host.c b/usr.sbin/unbound/smallapp/unbound-host.c index 095396749ff..ed52a551f9f 100644 --- a/usr.sbin/unbound/smallapp/unbound-host.c +++ b/usr.sbin/unbound/smallapp/unbound-host.c @@ -61,6 +61,9 @@ #endif #include "libunbound/unbound.h" #include <ldns/ldns.h> +#ifdef HAVE_NSS +#include <nss3/nss.h> +#endif /** verbosity for unbound-host app */ static int verb = 0; @@ -509,6 +512,12 @@ int main(int argc, char* argv[]) if(argc != 1) usage(); +#ifdef HAVE_NSS + if(NSS_NoDB_Init(".") != SECSuccess) { + fprintf(stderr, "could not init NSS\n"); + return 1; + } +#endif lookup(ctx, argv[0], qtype, qclass); return 0; } diff --git a/usr.sbin/unbound/util/alloc.h b/usr.sbin/unbound/util/alloc.h index 4ed0053e2b4..cb8d6b1bceb 100644 --- a/usr.sbin/unbound/util/alloc.h +++ b/usr.sbin/unbound/util/alloc.h @@ -177,8 +177,11 @@ void alloc_set_id_cleanup(struct alloc_cache* alloc, void (*cleanup)(void*), void* arg); #ifdef UNBOUND_ALLOC_LITE +# include <ldns/ldns.h> # include <ldns/packet.h> -# include <openssl/ssl.h> +# ifdef HAVE_OPENSSL_SSL_H +# include <openssl/ssl.h> +# endif # define malloc(s) unbound_stat_malloc_lite(s, __FILE__, __LINE__, __func__) # define calloc(n,s) unbound_stat_calloc_lite(n, s, __FILE__, __LINE__, __func__) # define free(p) unbound_stat_free_lite(p, __FILE__, __LINE__, __func__) diff --git a/usr.sbin/unbound/util/config_file.c b/usr.sbin/unbound/util/config_file.c index 8ba79d2a29c..2f5e988cb7a 100644 --- a/usr.sbin/unbound/util/config_file.c +++ b/usr.sbin/unbound/util/config_file.c @@ -1038,9 +1038,9 @@ static int isalldigit(const char* str, size_t l) int cfg_parse_memsize(const char* str, size_t* res) { - size_t len = (size_t)strlen(str); + size_t len; size_t mult = 1; - if(!str || len == 0) { + if(!str || (len=(size_t)strlen(str)) == 0) { log_err("not a size: '%s'", str); return 0; } diff --git a/usr.sbin/unbound/util/data/msgparse.c b/usr.sbin/unbound/util/data/msgparse.c index a03f543e827..2791ae56086 100644 --- a/usr.sbin/unbound/util/data/msgparse.c +++ b/usr.sbin/unbound/util/data/msgparse.c @@ -39,7 +39,6 @@ #include "config.h" #include <ldns/ldns.h> #include "util/data/msgparse.h" -#include "util/net_help.h" #include "util/data/dname.h" #include "util/data/packed_rrset.h" #include "util/storage/lookup3.h" @@ -655,8 +654,10 @@ calc_size(ldns_buffer* pkt, uint16_t type, struct rr_parse* rr) len = 0; break; case LDNS_RDF_TYPE_STR: - if(pkt_len < 1) + if(pkt_len < 1) { + /* NOTREACHED, due to 'while(>0)' */ return 0; /* len byte exceeds rdata */ + } len = ldns_buffer_current(pkt)[0] + 1; break; default: diff --git a/usr.sbin/unbound/util/iana_ports.inc b/usr.sbin/unbound/util/iana_ports.inc index 8508d7e9f01..fcf04c327e1 100644 --- a/usr.sbin/unbound/util/iana_ports.inc +++ b/usr.sbin/unbound/util/iana_ports.inc @@ -4232,6 +4232,7 @@ 5556, 5567, 5568, +5569, 5573, 5580, 5581, @@ -4382,6 +4383,7 @@ 6162, 6163, 6200, +6201, 6222, 6241, 6242, @@ -4466,6 +4468,7 @@ 6626, 6627, 6628, +6633, 6657, 6670, 6671, @@ -4694,6 +4697,7 @@ 8057, 8058, 8059, +8060, 8074, 8080, 8081, @@ -4768,6 +4772,7 @@ 8555, 8567, 8600, +8609, 8610, 8611, 8612, @@ -4929,7 +4934,7 @@ 9801, 9802, 9875, -9876, +9878, 9888, 9889, 9898, @@ -5023,6 +5028,7 @@ 11600, 11720, 11751, +11796, 11876, 11877, 11967, @@ -5217,6 +5223,7 @@ 24677, 24678, 24680, +24850, 24922, 25000, 25001, @@ -5316,6 +5323,7 @@ 43188, 43189, 43190, +43210, 43439, 43440, 43441, diff --git a/usr.sbin/unbound/util/log.c b/usr.sbin/unbound/util/log.c index fc07dc6a9cb..8c09c7ce312 100644 --- a/usr.sbin/unbound/util/log.c +++ b/usr.sbin/unbound/util/log.c @@ -171,6 +171,8 @@ log_vmsg(int pri, const char* type, #if defined(HAVE_STRFTIME) && defined(HAVE_LOCALTIME_R) char tmbuf[32]; struct tm tm; +#elif defined(UB_ON_WINDOWS) + char tmbuf[128], dtbuf[128]; #endif (void)pri; vsnprintf(message, sizeof(message), format, args); @@ -218,6 +220,13 @@ log_vmsg(int pri, const char* type, fprintf(logfile, "%s %s[%d:%x] %s: %s\n", tmbuf, ident, (int)getpid(), tid?*tid:0, type, message); } else +#elif defined(UB_ON_WINDOWS) + if(log_time_asc && GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, + tmbuf, sizeof(tmbuf)) && GetDateFormat(LOCALE_USER_DEFAULT, 0, + NULL, NULL, dtbuf, sizeof(dtbuf))) { + fprintf(logfile, "%s %s %s[%d:%x] %s: %s\n", dtbuf, tmbuf, + ident, (int)getpid(), tid?*tid:0, type, message); + } else #endif fprintf(logfile, "[%u] %s[%d:%x] %s: %s\n", (unsigned)now, ident, (int)getpid(), tid?*tid:0, type, message); diff --git a/usr.sbin/unbound/util/net_help.c b/usr.sbin/unbound/util/net_help.c index 6be5fcc3132..d8c624fd665 100644 --- a/usr.sbin/unbound/util/net_help.c +++ b/usr.sbin/unbound/util/net_help.c @@ -45,8 +45,12 @@ #include "util/module.h" #include "util/regional.h" #include <fcntl.h> +#ifdef HAVE_OPENSSL_SSL_H #include <openssl/ssl.h> +#endif +#ifdef HAVE_OPENSSL_ERR_H #include <openssl/err.h> +#endif /** max length of an IP address (the address portion) that we allow */ #define MAX_ADDR_STRLEN 128 /* characters */ @@ -565,6 +569,7 @@ void sock_list_merge(struct sock_list** list, struct regional* region, void log_crypto_err(const char* str) { +#ifdef HAVE_SSL /* error:[error code]:[library name]:[function name]:[reason string] */ char buf[128]; unsigned long e; @@ -574,10 +579,14 @@ log_crypto_err(const char* str) ERR_error_string_n(e, buf, sizeof(buf)); log_err("and additionally crypto %s", buf); } +#else + (void)str; +#endif /* HAVE_SSL */ } void* listen_sslctx_create(char* key, char* pem, char* verifypem) { +#ifdef HAVE_SSL SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method()); if(!ctx) { log_crypto_err("could not SSL_CTX_new"); @@ -619,10 +628,15 @@ void* listen_sslctx_create(char* key, char* pem, char* verifypem) SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); } return ctx; +#else + (void)key; (void)pem; (void)verifypem; + return NULL; +#endif } void* connect_sslctx_create(char* key, char* pem, char* verifypem) { +#ifdef HAVE_SSL SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method()); if(!ctx) { log_crypto_err("could not allocate SSL_CTX pointer"); @@ -662,10 +676,15 @@ void* connect_sslctx_create(char* key, char* pem, char* verifypem) SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); } return ctx; +#else + (void)key; (void)pem; (void)verifypem; + return NULL; +#endif } void* incoming_ssl_fd(void* sslctx, int fd) { +#ifdef HAVE_SSL SSL* ssl = SSL_new((SSL_CTX*)sslctx); if(!ssl) { log_crypto_err("could not SSL_new"); @@ -679,10 +698,15 @@ void* incoming_ssl_fd(void* sslctx, int fd) return NULL; } return ssl; +#else + (void)sslctx; (void)fd; + return NULL; +#endif } void* outgoing_ssl_fd(void* sslctx, int fd) { +#ifdef HAVE_SSL SSL* ssl = SSL_new((SSL_CTX*)sslctx); if(!ssl) { log_crypto_err("could not SSL_new"); @@ -696,4 +720,8 @@ void* outgoing_ssl_fd(void* sslctx, int fd) return NULL; } return ssl; +#else + (void)sslctx; (void)fd; + return NULL; +#endif } diff --git a/usr.sbin/unbound/util/random.c b/usr.sbin/unbound/util/random.c index 72c58a2b4df..5b61aef7f3b 100644 --- a/usr.sbin/unbound/util/random.c +++ b/usr.sbin/unbound/util/random.c @@ -60,10 +60,23 @@ #include "config.h" #include "util/random.h" #include "util/log.h" +#ifdef HAVE_SSL #include <openssl/rand.h> #include <openssl/rc4.h> #include <openssl/err.h> +#elif defined(HAVE_NSS) +#include <nspr4/prerror.h> +#include <nss3/secport.h> +#include <nss3/pk11pub.h> +#endif +/** + * Max random value. Similar to RAND_MAX, but more portable + * (mingw uses only 15 bits random). + */ +#define MAX_VALUE 0x7fffffff + +#ifdef HAVE_SSL /** * Struct with per-thread random state. * Keeps SSL types away from the header file. @@ -78,12 +91,6 @@ struct ub_randstate { /** Size of key to use (must be multiple of 8) */ #define SEED_SIZE 24 -/** - * Max random value. Similar to RAND_MAX, but more portable - * (mingw uses only 15 bits random). - */ -#define MAX_VALUE 0x7fffffff - /** Number of bytes to reseed after */ #define REKEY_BYTES (1 << 24) @@ -140,6 +147,16 @@ ub_arc4random_stir(struct ub_randstate* s, struct ub_randstate* from) return; } } +#ifdef HAVE_FIPS_MODE + if(FIPS_mode()) { + /* RC4 is not allowed, get some trustworthy randomness */ + /* double certainty here, this routine should not be + * called in FIPS_mode */ + memset(rand_buf, 0, sizeof(rand_buf)); + s->rc4_ready = REKEY_BYTES; + return; + } +#endif /* FIPS_MODE */ RC4_set_key(&s->rc4, SEED_SIZE, (unsigned char*)rand_buf); /* @@ -164,6 +181,9 @@ ub_initstate(unsigned int seed, struct ub_randstate* from) return NULL; } ub_systemseed(seed); +#ifdef HAVE_FIPS_MODE + if(!FIPS_mode()) +#endif ub_arc4random_stir(s, from); return s; } @@ -172,6 +192,20 @@ long int ub_random(struct ub_randstate* s) { unsigned int r = 0; +#ifdef HAVE_FIPS_MODE + if(FIPS_mode()) { + /* RC4 is not allowed, get some trustworthy randomness */ + /* we use pseudo bytes: it tries to return secure randomness + * but returns 'something' if that fails. We need something + * else if it fails, because we cannot block here */ + if(RAND_pseudo_bytes((unsigned char*)&r, (int)sizeof(r)) + == -1) { + log_err("FIPSmode, no arc4random but RAND failed " + "(error %ld)", ERR_get_error()); + } + return (long int)((r) % (((unsigned)MAX_VALUE + 1))); + } +#endif /* FIPS_MODE */ if (s->rc4_ready <= 0) { ub_arc4random_stir(s, NULL); } @@ -182,6 +216,42 @@ ub_random(struct ub_randstate* s) return (long int)((r) % (((unsigned)MAX_VALUE + 1))); } +#elif defined(HAVE_NSS) + +/* not much to remember for NSS since we use its pk11_random, placeholder */ +struct ub_randstate { + int ready; +}; + +void ub_systemseed(unsigned int ATTR_UNUSED(seed)) +{ +} + +struct ub_randstate* ub_initstate(unsigned int ATTR_UNUSED(seed), + struct ub_randstate* ATTR_UNUSED(from)) +{ + struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s)); + if(!s) { + log_err("malloc failure in random init"); + return NULL; + } + return s; +} + +long int ub_random(struct ub_randstate* ATTR_UNUSED(state)) +{ + long int x; + /* random 31 bit value. */ + SECStatus s = PK11_GenerateRandom((unsigned char*)&x, (int)sizeof(x)); + if(s != SECSuccess) { + log_err("PK11_GenerateRandom error: %s", + PORT_ErrorToString(PORT_GetError())); + } + return x & MAX_VALUE; +} + +#endif /* HAVE_SSL or HAVE_NSS */ + long int ub_random_max(struct ub_randstate* state, long int x) { diff --git a/usr.sbin/unbound/util/rtt.c b/usr.sbin/unbound/util/rtt.c index df1d437e479..c888b0864f0 100644 --- a/usr.sbin/unbound/util/rtt.c +++ b/usr.sbin/unbound/util/rtt.c @@ -41,7 +41,6 @@ */ #include "config.h" #include "util/rtt.h" -#include "util/log.h" /** calculate RTO from rtt information */ static int diff --git a/usr.sbin/unbound/util/tube.c b/usr.sbin/unbound/util/tube.c index 67294e056c4..28c51d79d16 100644 --- a/usr.sbin/unbound/util/tube.c +++ b/usr.sbin/unbound/util/tube.c @@ -360,6 +360,7 @@ int tube_read_msg(struct tube* tube, uint8_t** buf, uint32_t* len, } d += r; } + log_assert(*len < 65536*2); *buf = (uint8_t*)malloc(*len); if(!*buf) { log_err("tube read out of memory"); diff --git a/usr.sbin/unbound/validator/autotrust.c b/usr.sbin/unbound/validator/autotrust.c index 9896943245e..a361ab04ba3 100644 --- a/usr.sbin/unbound/validator/autotrust.c +++ b/usr.sbin/unbound/validator/autotrust.c @@ -1851,6 +1851,7 @@ static void autr_tp_remove(struct module_env* env, struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset) { + struct trust_anchor* del_tp; struct trust_anchor key; struct autr_point_data pd; time_t mold, mnew; @@ -1876,19 +1877,24 @@ autr_tp_remove(struct module_env* env, struct trust_anchor* tp, /* take from tree. It could be deleted by someone else,hence (void). */ lock_basic_lock(&env->anchors->lock); - (void)rbtree_delete(env->anchors->tree, &key); + del_tp = (struct trust_anchor*)rbtree_delete(env->anchors->tree, &key); mold = wait_probe_time(env->anchors); (void)rbtree_delete(&env->anchors->autr->probe, &key); mnew = wait_probe_time(env->anchors); anchors_init_parents_locked(env->anchors); lock_basic_unlock(&env->anchors->lock); - /* save on disk */ - tp->autr->next_probe_time = 0; /* no more probing for it */ - autr_write_file(env, tp); + /* if !del_tp then the trust point is no longer present in the tree, + * it was deleted by someone else, who will write the zonefile and + * clean up the structure */ + if(del_tp) { + /* save on disk */ + del_tp->autr->next_probe_time = 0; /* no more probing for it */ + autr_write_file(env, del_tp); - /* delete */ - autr_point_delete(tp); + /* delete */ + autr_point_delete(del_tp); + } if(mold != mnew) { reset_worker_timer(env); } diff --git a/usr.sbin/unbound/validator/val_anchor.c b/usr.sbin/unbound/validator/val_anchor.c index 200bf5d97be..fa69df52d68 100644 --- a/usr.sbin/unbound/validator/val_anchor.c +++ b/usr.sbin/unbound/validator/val_anchor.c @@ -1246,6 +1246,7 @@ anchors_delete_insecure(struct val_anchors* anchors, uint16_t c, lock_basic_lock(&ta->lock); /* see if its really an insecure point */ if(ta->keylist || ta->autr || ta->numDS || ta->numDNSKEY) { + lock_basic_unlock(&anchors->lock); lock_basic_unlock(&ta->lock); /* its not an insecure point, do not remove it */ return; diff --git a/usr.sbin/unbound/validator/val_neg.c b/usr.sbin/unbound/validator/val_neg.c index 60434db0338..eec2eb1b6bb 100644 --- a/usr.sbin/unbound/validator/val_neg.c +++ b/usr.sbin/unbound/validator/val_neg.c @@ -44,6 +44,9 @@ #include "config.h" #ifdef HAVE_OPENSSL_SSL_H #include "openssl/ssl.h" +#define NSEC3_SHA_LEN SHA_DIGEST_LENGTH +#else +#define NSEC3_SHA_LEN 20 #endif #include "validator/val_neg.h" #include "validator/val_nsec.h" @@ -1174,7 +1177,7 @@ neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, int qlabs, ldns_buffer* buf, uint8_t* hashnc, size_t* nclen) { struct val_neg_data* data; - uint8_t hashce[SHA_DIGEST_LENGTH]; + uint8_t hashce[NSEC3_SHA_LEN]; uint8_t b32[257]; size_t celen, b32len; @@ -1259,7 +1262,7 @@ neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, { struct dns_msg* msg; struct val_neg_data* data; - uint8_t hashnc[SHA_DIGEST_LENGTH]; + uint8_t hashnc[NSEC3_SHA_LEN]; size_t nclen; struct ub_packed_rrset_key* ce_rrset, *nc_rrset; struct nsec3_cached_hash c; diff --git a/usr.sbin/unbound/validator/val_nsec3.c b/usr.sbin/unbound/validator/val_nsec3.c index a18e3ab31d0..2a2cfcf9b2d 100644 --- a/usr.sbin/unbound/validator/val_nsec3.c +++ b/usr.sbin/unbound/validator/val_nsec3.c @@ -45,6 +45,9 @@ #ifdef HAVE_OPENSSL_SSL_H #include "openssl/ssl.h" #endif +#ifdef HAVE_NSS +#include <nss3/sechash.h> +#endif #include "validator/val_nsec3.h" #include "validator/validator.h" #include "validator/val_kentry.h" @@ -541,26 +544,43 @@ nsec3_get_hashed(ldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, ldns_buffer_write(buf, salt, saltlen); ldns_buffer_flip(buf); switch(algo) { -#ifdef HAVE_EVP_SHA1 +#if defined(HAVE_EVP_SHA1) || defined(HAVE_NSS) case NSEC3_HASH_SHA1: +#ifdef HAVE_SSL hash_len = SHA_DIGEST_LENGTH; +#else + hash_len = SHA1_LENGTH; +#endif if(hash_len > max) return 0; +# ifdef HAVE_SSL (void)SHA1((unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf), (unsigned char*)res); +# else + (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)res, + (unsigned char*)ldns_buffer_begin(buf), + (unsigned long)ldns_buffer_limit(buf)); +# endif for(i=0; i<iter; i++) { ldns_buffer_clear(buf); ldns_buffer_write(buf, res, hash_len); ldns_buffer_write(buf, salt, saltlen); ldns_buffer_flip(buf); +# ifdef HAVE_SSL (void)SHA1( (unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf), (unsigned char*)res); +# else + (void)HASH_HashBuf(HASH_AlgSHA1, + (unsigned char*)res, + (unsigned char*)ldns_buffer_begin(buf), + (unsigned long)ldns_buffer_limit(buf)); +# endif } break; -#endif /* HAVE_EVP_SHA1 */ +#endif /* HAVE_EVP_SHA1 or NSS */ default: log_err("nsec3 hash of unknown algo %d", algo); return 0; @@ -586,28 +606,46 @@ nsec3_calc_hash(struct regional* region, ldns_buffer* buf, ldns_buffer_write(buf, salt, saltlen); ldns_buffer_flip(buf); switch(algo) { -#ifdef HAVE_EVP_SHA1 +#if defined(HAVE_EVP_SHA1) || defined(HAVE_NSS) case NSEC3_HASH_SHA1: +#ifdef HAVE_SSL c->hash_len = SHA_DIGEST_LENGTH; +#else + c->hash_len = SHA1_LENGTH; +#endif c->hash = (uint8_t*)regional_alloc(region, c->hash_len); if(!c->hash) return 0; +# ifdef HAVE_SSL (void)SHA1((unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf), (unsigned char*)c->hash); +# else + (void)HASH_HashBuf(HASH_AlgSHA1, + (unsigned char*)c->hash, + (unsigned char*)ldns_buffer_begin(buf), + (unsigned long)ldns_buffer_limit(buf)); +# endif for(i=0; i<iter; i++) { ldns_buffer_clear(buf); ldns_buffer_write(buf, c->hash, c->hash_len); ldns_buffer_write(buf, salt, saltlen); ldns_buffer_flip(buf); +# ifdef HAVE_SSL (void)SHA1( (unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf), (unsigned char*)c->hash); +# else + (void)HASH_HashBuf(HASH_AlgSHA1, + (unsigned char*)c->hash, + (unsigned char*)ldns_buffer_begin(buf), + (unsigned long)ldns_buffer_limit(buf)); +# endif } break; -#endif /* HAVE_EVP_SHA1 */ +#endif /* HAVE_EVP_SHA1 or NSS */ default: log_err("nsec3 hash of unknown algo %d", algo); return -1; @@ -1133,8 +1171,8 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, * If not type DS: matching nsec3 must not be a delegation. */ if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 - && nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA && - !dname_is_root(qinfo->qname))) { + && nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA) && + !dname_is_root(qinfo->qname)) { verbose(VERB_ALGO, "proveNodata: apex NSEC3 " "abused for no DS proof, bogus"); return sec_status_bogus; diff --git a/usr.sbin/unbound/validator/val_secalgo.c b/usr.sbin/unbound/validator/val_secalgo.c new file mode 100644 index 00000000000..f3f861ee2a4 --- /dev/null +++ b/usr.sbin/unbound/validator/val_secalgo.c @@ -0,0 +1,1073 @@ +/* + * validator/val_secalgo.c - validator security algorithm functions. + * + * Copyright (c) 2012, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file contains helper functions for the validator module. + * These functions take raw data buffers, formatted for crypto verification, + * and do the library calls (for the crypto library in use). + */ +#include "config.h" +#include <ldns/ldns.h> +#include "validator/val_secalgo.h" +#include "util/data/packed_rrset.h" +#include "util/log.h" + +#if !defined(HAVE_SSL) && !defined(HAVE_NSS) +#error "Need crypto library to do digital signature cryptography" +#endif + +/* OpenSSL implementation */ +#ifdef HAVE_SSL +#ifdef HAVE_OPENSSL_ERR_H +#include <openssl/err.h> +#endif + +#ifdef HAVE_OPENSSL_RAND_H +#include <openssl/rand.h> +#endif + +#ifdef HAVE_OPENSSL_CONF_H +#include <openssl/conf.h> +#endif + +#ifdef HAVE_OPENSSL_ENGINE_H +#include <openssl/engine.h> +#endif + +/** + * Return size of DS digest according to its hash algorithm. + * @param algo: DS digest algo. + * @return size in bytes of digest, or 0 if not supported. + */ +size_t +ds_digest_size_supported(int algo) +{ + switch(algo) { +#ifdef HAVE_EVP_SHA1 + case LDNS_SHA1: + return SHA_DIGEST_LENGTH; +#endif +#ifdef HAVE_EVP_SHA256 + case LDNS_SHA256: + return SHA256_DIGEST_LENGTH; +#endif +#ifdef USE_GOST + case LDNS_HASH_GOST: + if(EVP_get_digestbyname("md_gost94")) + return 32; + else return 0; +#endif +#ifdef USE_ECDSA + case LDNS_SHA384: + return SHA384_DIGEST_LENGTH; +#endif + default: break; + } + return 0; +} + +#ifdef USE_GOST +/** Perform GOST hash */ +static int +do_gost94(unsigned char* data, size_t len, unsigned char* dest) +{ + const EVP_MD* md = EVP_get_digestbyname("md_gost94"); + if(!md) + return 0; + return ldns_digest_evp(data, (unsigned int)len, dest, md); +} +#endif + +int +secalgo_ds_digest(int algo, unsigned char* buf, size_t len, + unsigned char* res) +{ + switch(algo) { +#ifdef HAVE_EVP_SHA1 + case LDNS_SHA1: + (void)SHA1(buf, len, res); + return 1; +#endif +#ifdef HAVE_EVP_SHA256 + case LDNS_SHA256: + (void)SHA256(buf, len, res); + return 1; +#endif +#ifdef USE_GOST + case LDNS_HASH_GOST: + if(do_gost94(buf, len, res)) + return 1; + break; +#endif +#ifdef USE_ECDSA + case LDNS_SHA384: + (void)SHA384(buf, len, res); + return 1; +#endif + default: + verbose(VERB_QUERY, "unknown DS digest algorithm %d", + algo); + break; + } + return 0; +} + +/** return true if DNSKEY algorithm id is supported */ +int +dnskey_algo_id_is_supported(int id) +{ + switch(id) { + case LDNS_RSAMD5: +#ifdef HAVE_FIPS_MODE + /* openssl can return if the system is in FIPS mode, + * which does not allow MD5 hashes for network traffic */ + return !FIPS_mode(); +#else + return 1; +#endif + case LDNS_DSA: + case LDNS_DSA_NSEC3: + case LDNS_RSASHA1: + case LDNS_RSASHA1_NSEC3: +#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) + case LDNS_RSASHA256: +#endif +#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) + case LDNS_RSASHA512: +#endif +#ifdef USE_ECDSA + case LDNS_ECDSAP256SHA256: + case LDNS_ECDSAP384SHA384: +#endif + return 1; +#ifdef USE_GOST + case LDNS_ECC_GOST: + /* we support GOST if it can be loaded */ + return ldns_key_EVP_load_gost_id(); +#endif + default: + return 0; + } +} + +/** + * Output a libcrypto openssl error to the logfile. + * @param str: string to add to it. + * @param e: the error to output, error number from ERR_get_error(). + */ +static void +log_crypto_error(const char* str, unsigned long e) +{ + char buf[128]; + /* or use ERR_error_string if ERR_error_string_n is not avail TODO */ + ERR_error_string_n(e, buf, sizeof(buf)); + /* buf now contains */ + /* error:[error code]:[library name]:[function name]:[reason string] */ + log_err("%s crypto %s", str, buf); +} + +/** + * Setup DSA key digest in DER encoding ... + * @param sig: input is signature output alloced ptr (unless failure). + * caller must free alloced ptr if this routine returns true. + * @param len: input is initial siglen, output is output len. + * @return false on failure. + */ +static int +setup_dsa_sig(unsigned char** sig, unsigned int* len) +{ + unsigned char* orig = *sig; + unsigned int origlen = *len; + int newlen; + BIGNUM *R, *S; + DSA_SIG *dsasig; + + /* extract the R and S field from the sig buffer */ + if(origlen < 1 + 2*SHA_DIGEST_LENGTH) + return 0; + R = BN_new(); + if(!R) return 0; + (void) BN_bin2bn(orig + 1, SHA_DIGEST_LENGTH, R); + S = BN_new(); + if(!S) return 0; + (void) BN_bin2bn(orig + 21, SHA_DIGEST_LENGTH, S); + dsasig = DSA_SIG_new(); + if(!dsasig) return 0; + + dsasig->r = R; + dsasig->s = S; + *sig = NULL; + newlen = i2d_DSA_SIG(dsasig, sig); + if(newlen < 0) { + DSA_SIG_free(dsasig); + free(*sig); + return 0; + } + *len = (unsigned int)newlen; + DSA_SIG_free(dsasig); + return 1; +} + +#ifdef USE_ECDSA +/** + * Setup the ECDSA signature in its encoding that the library wants. + * Converts from plain numbers to ASN formatted. + * @param sig: input is signature, output alloced ptr (unless failure). + * caller must free alloced ptr if this routine returns true. + * @param len: input is initial siglen, output is output len. + * @return false on failure. + */ +static int +setup_ecdsa_sig(unsigned char** sig, unsigned int* len) +{ + ECDSA_SIG* ecdsa_sig; + int newlen; + int bnsize = (int)((*len)/2); + /* if too short or not even length, fails */ + if(*len < 16 || bnsize*2 != (int)*len) + return 0; + /* use the raw data to parse two evenly long BIGNUMs, "r | s". */ + ecdsa_sig = ECDSA_SIG_new(); + if(!ecdsa_sig) return 0; + ecdsa_sig->r = BN_bin2bn(*sig, bnsize, ecdsa_sig->r); + ecdsa_sig->s = BN_bin2bn(*sig+bnsize, bnsize, ecdsa_sig->s); + if(!ecdsa_sig->r || !ecdsa_sig->s) { + ECDSA_SIG_free(ecdsa_sig); + return 0; + } + + /* spool it into ASN format */ + *sig = NULL; + newlen = i2d_ECDSA_SIG(ecdsa_sig, sig); + if(newlen <= 0) { + ECDSA_SIG_free(ecdsa_sig); + free(*sig); + return 0; + } + *len = (unsigned int)newlen; + ECDSA_SIG_free(ecdsa_sig); + return 1; +} +#endif /* USE_ECDSA */ + +/** + * Setup key and digest for verification. Adjust sig if necessary. + * + * @param algo: key algorithm + * @param evp_key: EVP PKEY public key to create. + * @param digest_type: digest type to use + * @param key: key to setup for. + * @param keylen: length of key. + * @return false on failure. + */ +static int +setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, + unsigned char* key, size_t keylen) +{ + DSA* dsa; + RSA* rsa; + + switch(algo) { + case LDNS_DSA: + case LDNS_DSA_NSEC3: + *evp_key = EVP_PKEY_new(); + if(!*evp_key) { + log_err("verify: malloc failure in crypto"); + return 0; + } + dsa = ldns_key_buf2dsa_raw(key, keylen); + if(!dsa) { + verbose(VERB_QUERY, "verify: " + "ldns_key_buf2dsa_raw failed"); + return 0; + } + if(EVP_PKEY_assign_DSA(*evp_key, dsa) == 0) { + verbose(VERB_QUERY, "verify: " + "EVP_PKEY_assign_DSA failed"); + return 0; + } + *digest_type = EVP_dss1(); + + break; + case LDNS_RSASHA1: + case LDNS_RSASHA1_NSEC3: +#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) + case LDNS_RSASHA256: +#endif +#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) + case LDNS_RSASHA512: +#endif + *evp_key = EVP_PKEY_new(); + if(!*evp_key) { + log_err("verify: malloc failure in crypto"); + return 0; + } + rsa = ldns_key_buf2rsa_raw(key, keylen); + if(!rsa) { + verbose(VERB_QUERY, "verify: " + "ldns_key_buf2rsa_raw SHA failed"); + return 0; + } + if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) { + verbose(VERB_QUERY, "verify: " + "EVP_PKEY_assign_RSA SHA failed"); + return 0; + } + + /* select SHA version */ +#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) + if(algo == LDNS_RSASHA256) + *digest_type = EVP_sha256(); + else +#endif +#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) + if(algo == LDNS_RSASHA512) + *digest_type = EVP_sha512(); + else +#endif + *digest_type = EVP_sha1(); + + break; + case LDNS_RSAMD5: + *evp_key = EVP_PKEY_new(); + if(!*evp_key) { + log_err("verify: malloc failure in crypto"); + return 0; + } + rsa = ldns_key_buf2rsa_raw(key, keylen); + if(!rsa) { + verbose(VERB_QUERY, "verify: " + "ldns_key_buf2rsa_raw MD5 failed"); + return 0; + } + if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) { + verbose(VERB_QUERY, "verify: " + "EVP_PKEY_assign_RSA MD5 failed"); + return 0; + } + *digest_type = EVP_md5(); + + break; +#ifdef USE_GOST + case LDNS_ECC_GOST: + *evp_key = ldns_gost2pkey_raw(key, keylen); + if(!*evp_key) { + verbose(VERB_QUERY, "verify: " + "ldns_gost2pkey_raw failed"); + return 0; + } + *digest_type = EVP_get_digestbyname("md_gost94"); + if(!*digest_type) { + verbose(VERB_QUERY, "verify: " + "EVP_getdigest md_gost94 failed"); + return 0; + } + break; +#endif +#ifdef USE_ECDSA + case LDNS_ECDSAP256SHA256: + *evp_key = ldns_ecdsa2pkey_raw(key, keylen, + LDNS_ECDSAP256SHA256); + if(!*evp_key) { + verbose(VERB_QUERY, "verify: " + "ldns_ecdsa2pkey_raw failed"); + return 0; + } +#ifdef USE_ECDSA_EVP_WORKAROUND + /* openssl before 1.0.0 fixes RSA with the SHA256 + * hash in EVP. We create one for ecdsa_sha256 */ + { + static int md_ecdsa_256_done = 0; + static EVP_MD md; + if(!md_ecdsa_256_done) { + EVP_MD m = *EVP_sha256(); + md_ecdsa_256_done = 1; + m.required_pkey_type[0] = (*evp_key)->type; + m.verify = (void*)ECDSA_verify; + md = m; + } + *digest_type = &md; + } +#else + *digest_type = EVP_sha256(); +#endif + break; + case LDNS_ECDSAP384SHA384: + *evp_key = ldns_ecdsa2pkey_raw(key, keylen, + LDNS_ECDSAP384SHA384); + if(!*evp_key) { + verbose(VERB_QUERY, "verify: " + "ldns_ecdsa2pkey_raw failed"); + return 0; + } +#ifdef USE_ECDSA_EVP_WORKAROUND + /* openssl before 1.0.0 fixes RSA with the SHA384 + * hash in EVP. We create one for ecdsa_sha384 */ + { + static int md_ecdsa_384_done = 0; + static EVP_MD md; + if(!md_ecdsa_384_done) { + EVP_MD m = *EVP_sha384(); + md_ecdsa_384_done = 1; + m.required_pkey_type[0] = (*evp_key)->type; + m.verify = (void*)ECDSA_verify; + md = m; + } + *digest_type = &md; + } +#else + *digest_type = EVP_sha384(); +#endif + break; +#endif /* USE_ECDSA */ + default: + verbose(VERB_QUERY, "verify: unknown algorithm %d", + algo); + return 0; + } + return 1; +} + +/** + * Check a canonical sig+rrset and signature against a dnskey + * @param buf: buffer with data to verify, the first rrsig part and the + * canonicalized rrset. + * @param algo: DNSKEY algorithm. + * @param sigblock: signature rdata field from RRSIG + * @param sigblock_len: length of sigblock data. + * @param key: public key data from DNSKEY RR. + * @param keylen: length of keydata. + * @param reason: bogus reason in more detail. + * @return secure if verification succeeded, bogus on crypto failure, + * unchecked on format errors and alloc failures. + */ +enum sec_status +verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, + unsigned int sigblock_len, unsigned char* key, unsigned int keylen, + char** reason) +{ + const EVP_MD *digest_type; + EVP_MD_CTX ctx; + int res, dofree = 0; + EVP_PKEY *evp_key = NULL; + + if(!setup_key_digest(algo, &evp_key, &digest_type, key, keylen)) { + verbose(VERB_QUERY, "verify: failed to setup key"); + *reason = "use of key for crypto failed"; + EVP_PKEY_free(evp_key); + return sec_status_bogus; + } + /* if it is a DSA signature in bind format, convert to DER format */ + if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) && + sigblock_len == 1+2*SHA_DIGEST_LENGTH) { + if(!setup_dsa_sig(&sigblock, &sigblock_len)) { + verbose(VERB_QUERY, "verify: failed to setup DSA sig"); + *reason = "use of key for DSA crypto failed"; + EVP_PKEY_free(evp_key); + return sec_status_bogus; + } + dofree = 1; + } +#ifdef USE_ECDSA + else if(algo == LDNS_ECDSAP256SHA256 || algo == LDNS_ECDSAP384SHA384) { + /* EVP uses ASN prefix on sig, which is not in the wire data */ + if(!setup_ecdsa_sig(&sigblock, &sigblock_len)) { + verbose(VERB_QUERY, "verify: failed to setup ECDSA sig"); + *reason = "use of signature for ECDSA crypto failed"; + EVP_PKEY_free(evp_key); + return sec_status_bogus; + } + dofree = 1; + } +#endif /* USE_ECDSA */ + + /* do the signature cryptography work */ + EVP_MD_CTX_init(&ctx); + if(EVP_VerifyInit(&ctx, digest_type) == 0) { + verbose(VERB_QUERY, "verify: EVP_VerifyInit failed"); + EVP_PKEY_free(evp_key); + if(dofree) free(sigblock); + return sec_status_unchecked; + } + if(EVP_VerifyUpdate(&ctx, (unsigned char*)ldns_buffer_begin(buf), + (unsigned int)ldns_buffer_limit(buf)) == 0) { + verbose(VERB_QUERY, "verify: EVP_VerifyUpdate failed"); + EVP_PKEY_free(evp_key); + if(dofree) free(sigblock); + return sec_status_unchecked; + } + + res = EVP_VerifyFinal(&ctx, sigblock, sigblock_len, evp_key); + if(EVP_MD_CTX_cleanup(&ctx) == 0) { + verbose(VERB_QUERY, "verify: EVP_MD_CTX_cleanup failed"); + EVP_PKEY_free(evp_key); + if(dofree) free(sigblock); + return sec_status_unchecked; + } + EVP_PKEY_free(evp_key); + + if(dofree) + free(sigblock); + + if(res == 1) { + return sec_status_secure; + } else if(res == 0) { + verbose(VERB_QUERY, "verify: signature mismatch"); + *reason = "signature crypto failed"; + return sec_status_bogus; + } + + log_crypto_error("verify:", ERR_get_error()); + return sec_status_unchecked; +} + +/**************************************************/ +#elif defined(HAVE_NSS) +/* libnss implementation */ +#include <nss3/sechash.h> +#include <nss3/pk11pub.h> +#include <nss3/keyhi.h> +#include <nss3/secerr.h> +#include <nss3/cryptohi.h> +#include <nspr4/prerror.h> + +size_t +ds_digest_size_supported(int algo) +{ + /* uses libNSS */ + switch(algo) { + case LDNS_SHA1: + return SHA1_LENGTH; +#ifdef USE_SHA2 + case LDNS_SHA256: + return SHA256_LENGTH; +#endif +#ifdef USE_ECDSA + case LDNS_SHA384: + return SHA384_LENGTH; +#endif + /* GOST not supported in NSS */ + case LDNS_HASH_GOST: + default: break; + } + return 0; +} + +int +secalgo_ds_digest(int algo, unsigned char* buf, size_t len, + unsigned char* res) +{ + /* uses libNSS */ + switch(algo) { + case LDNS_SHA1: + return HASH_HashBuf(HASH_AlgSHA1, res, buf, len) + == SECSuccess; +#if defined(USE_SHA2) + case LDNS_SHA256: + return HASH_HashBuf(HASH_AlgSHA256, res, buf, len) + == SECSuccess; +#endif +#ifdef USE_ECDSA + case LDNS_SHA384: + return HASH_HashBuf(HASH_AlgSHA384, res, buf, len) + == SECSuccess; +#endif + case LDNS_HASH_GOST: + default: + verbose(VERB_QUERY, "unknown DS digest algorithm %d", + algo); + break; + } + return 0; +} + +int +dnskey_algo_id_is_supported(int id) +{ + /* uses libNSS */ + switch(id) { + case LDNS_RSAMD5: + /* disable MD5 support if FIPS mode is enabled in libnss */ + return !PK11_IsFIPS(); + case LDNS_DSA: + case LDNS_DSA_NSEC3: + case LDNS_RSASHA1: + case LDNS_RSASHA1_NSEC3: +#ifdef USE_SHA2 + case LDNS_RSASHA256: +#endif +#ifdef USE_SHA2 + case LDNS_RSASHA512: +#endif + return 1; +#ifdef USE_ECDSA + case LDNS_ECDSAP256SHA256: + case LDNS_ECDSAP384SHA384: + return PK11_TokenExists(CKM_ECDSA); +#endif + case LDNS_ECC_GOST: + default: + return 0; + } +} + +/* return a new public key for NSS */ +static SECKEYPublicKey* nss_key_create(KeyType ktype) +{ + SECKEYPublicKey* key; + PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if(!arena) { + log_err("out of memory, PORT_NewArena failed"); + return NULL; + } + key = PORT_ArenaZNew(arena, SECKEYPublicKey); + if(!key) { + log_err("out of memory, PORT_ArenaZNew failed"); + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + key->arena = arena; + key->keyType = ktype; + key->pkcs11Slot = NULL; + key->pkcs11ID = CK_INVALID_HANDLE; + return key; +} + +static SECKEYPublicKey* nss_buf2ecdsa(unsigned char* key, size_t len, int algo) +{ + SECKEYPublicKey* pk; + SECItem pub = {siBuffer, NULL, 0}; + SECItem params = {siBuffer, NULL, 0}; + unsigned char param256[] = { + /* OBJECTIDENTIFIER 1.2.840.10045.3.1.7 (P-256) + * {iso(1) member-body(2) us(840) ansi-x962(10045) curves(3) prime(1) prime256v1(7)} */ + 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 + }; + unsigned char param384[] = { + /* OBJECTIDENTIFIER 1.3.132.0.34 (P-384) + * {iso(1) identified-organization(3) certicom(132) curve(0) ansip384r1(34)} */ + 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 + }; + unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */ + + /* check length, which uncompressed must be 2 bignums */ + if(algo == LDNS_ECDSAP256SHA256) { + if(len != 2*256/8) return NULL; + /* ECCurve_X9_62_PRIME_256V1 */ + } else if(algo == LDNS_ECDSAP384SHA384) { + if(len != 2*384/8) return NULL; + /* ECCurve_X9_62_PRIME_384R1 */ + } else return NULL; + + buf[0] = 0x04; /* POINT_FORM_UNCOMPRESSED */ + memmove(buf+1, key, len); + pub.data = buf; + pub.len = len+1; + if(algo == LDNS_ECDSAP256SHA256) { + params.data = param256; + params.len = sizeof(param256); + } else { + params.data = param384; + params.len = sizeof(param384); + } + + pk = nss_key_create(ecKey); + if(!pk) + return NULL; + pk->u.ec.size = (len/2)*8; + if(SECITEM_CopyItem(pk->arena, &pk->u.ec.publicValue, &pub)) { + SECKEY_DestroyPublicKey(pk); + return NULL; + } + if(SECITEM_CopyItem(pk->arena, &pk->u.ec.DEREncodedParams, ¶ms)) { + SECKEY_DestroyPublicKey(pk); + return NULL; + } + + return pk; +} + +static SECKEYPublicKey* nss_buf2dsa(unsigned char* key, size_t len) +{ + SECKEYPublicKey* pk; + uint8_t T; + uint16_t length; + uint16_t offset; + SECItem Q = {siBuffer, NULL, 0}; + SECItem P = {siBuffer, NULL, 0}; + SECItem G = {siBuffer, NULL, 0}; + SECItem Y = {siBuffer, NULL, 0}; + + if(len == 0) + return NULL; + T = (uint8_t)key[0]; + length = (64 + T * 8); + offset = 1; + + if (T > 8) { + return NULL; + } + if(len < (size_t)1 + SHA1_LENGTH + 3*length) + return NULL; + + Q.data = key+offset; + Q.len = SHA1_LENGTH; + offset += SHA1_LENGTH; + + P.data = key+offset; + P.len = length; + offset += length; + + G.data = key+offset; + G.len = length; + offset += length; + + Y.data = key+offset; + Y.len = length; + offset += length; + + pk = nss_key_create(dsaKey); + if(!pk) + return NULL; + if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.prime, &P)) { + SECKEY_DestroyPublicKey(pk); + return NULL; + } + if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.subPrime, &Q)) { + SECKEY_DestroyPublicKey(pk); + return NULL; + } + if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.base, &G)) { + SECKEY_DestroyPublicKey(pk); + return NULL; + } + if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.publicValue, &Y)) { + SECKEY_DestroyPublicKey(pk); + return NULL; + } + return pk; +} + +static SECKEYPublicKey* nss_buf2rsa(unsigned char* key, size_t len) +{ + SECKEYPublicKey* pk; + uint16_t exp; + uint16_t offset; + uint16_t int16; + SECItem modulus = {siBuffer, NULL, 0}; + SECItem exponent = {siBuffer, NULL, 0}; + if(len == 0) + return NULL; + if(key[0] == 0) { + if(len < 3) + return NULL; + /* the exponent is too large so it's places further */ + memmove(&int16, key+1, 2); + exp = ntohs(int16); + offset = 3; + } else { + exp = key[0]; + offset = 1; + } + + /* key length at least one */ + if(len < (size_t)offset + exp + 1) + return NULL; + + exponent.data = key+offset; + exponent.len = exp; + offset += exp; + modulus.data = key+offset; + modulus.len = (len - offset); + + pk = nss_key_create(rsaKey); + if(!pk) + return NULL; + if(SECITEM_CopyItem(pk->arena, &pk->u.rsa.modulus, &modulus)) { + SECKEY_DestroyPublicKey(pk); + return NULL; + } + if(SECITEM_CopyItem(pk->arena, &pk->u.rsa.publicExponent, &exponent)) { + SECKEY_DestroyPublicKey(pk); + return NULL; + } + return pk; +} + +/** + * Setup key and digest for verification. Adjust sig if necessary. + * + * @param algo: key algorithm + * @param evp_key: EVP PKEY public key to create. + * @param digest_type: digest type to use + * @param key: key to setup for. + * @param keylen: length of key. + * @param prefix: if returned, the ASN prefix for the hashblob. + * @param prefixlen: length of the prefix. + * @return false on failure. + */ +static int +nss_setup_key_digest(int algo, SECKEYPublicKey** pubkey, HASH_HashType* htype, + unsigned char* key, size_t keylen, unsigned char** prefix, + size_t* prefixlen) +{ + /* uses libNSS */ + + /* hash prefix for md5, RFC2537 */ + unsigned char p_md5[] = {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}; + /* hash prefix to prepend to hash output, from RFC3110 */ + unsigned char p_sha1[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, + 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14}; + /* from RFC5702 */ + unsigned char p_sha256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; + unsigned char p_sha512[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}; + /* from RFC6234 */ + /* for future RSASHA384 .. + unsigned char p_sha384[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; + */ + + switch(algo) { + case LDNS_DSA: + case LDNS_DSA_NSEC3: + *pubkey = nss_buf2dsa(key, keylen); + if(!*pubkey) { + log_err("verify: malloc failure in crypto"); + return 0; + } + *htype = HASH_AlgSHA1; + /* no prefix for DSA verification */ + break; + case LDNS_RSASHA1: + case LDNS_RSASHA1_NSEC3: +#ifdef USE_SHA2 + case LDNS_RSASHA256: +#endif +#ifdef USE_SHA2 + case LDNS_RSASHA512: +#endif + *pubkey = nss_buf2rsa(key, keylen); + if(!*pubkey) { + log_err("verify: malloc failure in crypto"); + return 0; + } + /* select SHA version */ +#ifdef USE_SHA2 + if(algo == LDNS_RSASHA256) { + *htype = HASH_AlgSHA256; + *prefix = p_sha256; + *prefixlen = sizeof(p_sha256); + } else +#endif +#ifdef USE_SHA2 + if(algo == LDNS_RSASHA512) { + *htype = HASH_AlgSHA512; + *prefix = p_sha512; + *prefixlen = sizeof(p_sha512); + } else +#endif + { + *htype = HASH_AlgSHA1; + *prefix = p_sha1; + *prefixlen = sizeof(p_sha1); + } + + break; + case LDNS_RSAMD5: + *pubkey = nss_buf2rsa(key, keylen); + if(!*pubkey) { + log_err("verify: malloc failure in crypto"); + return 0; + } + *htype = HASH_AlgMD5; + *prefix = p_md5; + *prefixlen = sizeof(p_md5); + + break; +#ifdef USE_ECDSA + case LDNS_ECDSAP256SHA256: + *pubkey = nss_buf2ecdsa(key, keylen, + LDNS_ECDSAP256SHA256); + if(!*pubkey) { + log_err("verify: malloc failure in crypto"); + return 0; + } + *htype = HASH_AlgSHA256; + /* no prefix for DSA verification */ + break; + case LDNS_ECDSAP384SHA384: + *pubkey = nss_buf2ecdsa(key, keylen, + LDNS_ECDSAP384SHA384); + if(!*pubkey) { + log_err("verify: malloc failure in crypto"); + return 0; + } + *htype = HASH_AlgSHA384; + /* no prefix for DSA verification */ + break; +#endif /* USE_ECDSA */ + case LDNS_ECC_GOST: + default: + verbose(VERB_QUERY, "verify: unknown algorithm %d", + algo); + return 0; + } + return 1; +} + +/** + * Check a canonical sig+rrset and signature against a dnskey + * @param buf: buffer with data to verify, the first rrsig part and the + * canonicalized rrset. + * @param algo: DNSKEY algorithm. + * @param sigblock: signature rdata field from RRSIG + * @param sigblock_len: length of sigblock data. + * @param key: public key data from DNSKEY RR. + * @param keylen: length of keydata. + * @param reason: bogus reason in more detail. + * @return secure if verification succeeded, bogus on crypto failure, + * unchecked on format errors and alloc failures. + */ +enum sec_status +verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, + unsigned int sigblock_len, unsigned char* key, unsigned int keylen, + char** reason) +{ + /* uses libNSS */ + /* large enough for the different hashes */ + unsigned char hash[HASH_LENGTH_MAX]; + unsigned char hash2[HASH_LENGTH_MAX*2]; + HASH_HashType htype = 0; + SECKEYPublicKey* pubkey = NULL; + SECItem secsig = {siBuffer, sigblock, sigblock_len}; + SECItem sechash = {siBuffer, hash, 0}; + SECStatus res; + unsigned char* prefix = NULL; /* prefix for hash, RFC3110, RFC5702 */ + size_t prefixlen = 0; + int err; + + if(!nss_setup_key_digest(algo, &pubkey, &htype, key, keylen, + &prefix, &prefixlen)) { + verbose(VERB_QUERY, "verify: failed to setup key"); + *reason = "use of key for crypto failed"; + SECKEY_DestroyPublicKey(pubkey); + return sec_status_bogus; + } + + /* need to convert DSA, ECDSA signatures? */ + if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3)) { + if(sigblock_len == 1+2*SHA1_LENGTH) { + secsig.data ++; + secsig.len --; + } else { + SECItem* p = DSAU_DecodeDerSig(&secsig); + if(!p) { + verbose(VERB_QUERY, "verify: failed DER decode"); + *reason = "signature DER decode failed"; + SECKEY_DestroyPublicKey(pubkey); + return sec_status_bogus; + } + if(SECITEM_CopyItem(pubkey->arena, &secsig, p)) { + log_err("alloc failure in DER decode"); + SECKEY_DestroyPublicKey(pubkey); + SECITEM_FreeItem(p, PR_TRUE); + return sec_status_unchecked; + } + SECITEM_FreeItem(p, PR_TRUE); + } + } + + /* do the signature cryptography work */ + /* hash the data */ + sechash.len = HASH_ResultLen(htype); + if(sechash.len > sizeof(hash)) { + verbose(VERB_QUERY, "verify: hash too large for buffer"); + SECKEY_DestroyPublicKey(pubkey); + return sec_status_unchecked; + } + if(HASH_HashBuf(htype, hash, (unsigned char*)ldns_buffer_begin(buf), + (unsigned int)ldns_buffer_limit(buf)) != SECSuccess) { + verbose(VERB_QUERY, "verify: HASH_HashBuf failed"); + SECKEY_DestroyPublicKey(pubkey); + return sec_status_unchecked; + } + if(prefix) { + int hashlen = sechash.len; + if(prefixlen+hashlen > sizeof(hash2)) { + verbose(VERB_QUERY, "verify: hashprefix too large"); + SECKEY_DestroyPublicKey(pubkey); + return sec_status_unchecked; + } + sechash.data = hash2; + sechash.len = prefixlen+hashlen; + memcpy(sechash.data, prefix, prefixlen); + memmove(sechash.data+prefixlen, hash, hashlen); + } + + /* verify the signature */ + res = PK11_Verify(pubkey, &secsig, &sechash, NULL /*wincx*/); + SECKEY_DestroyPublicKey(pubkey); + + if(res == SECSuccess) { + return sec_status_secure; + } + err = PORT_GetError(); + if(err != SEC_ERROR_BAD_SIGNATURE) { + /* failed to verify */ + verbose(VERB_QUERY, "verify: PK11_Verify failed: %s", + PORT_ErrorToString(err)); + /* if it is not supported, like ECC is removed, we get, + * SEC_ERROR_NO_MODULE */ + if(err == SEC_ERROR_NO_MODULE) + return sec_status_unchecked; + /* but other errors are commonly returned + * for a bad signature from NSS. Thus we return bogus, + * not unchecked */ + *reason = "signature crypto failed"; + return sec_status_bogus; + } + verbose(VERB_QUERY, "verify: signature mismatch: %s", + PORT_ErrorToString(err)); + *reason = "signature crypto failed"; + return sec_status_bogus; +} + + +#endif /* HAVE_SSL or HAVE_NSS */ diff --git a/usr.sbin/unbound/validator/val_secalgo.h b/usr.sbin/unbound/validator/val_secalgo.h new file mode 100644 index 00000000000..a5832af871e --- /dev/null +++ b/usr.sbin/unbound/validator/val_secalgo.h @@ -0,0 +1,83 @@ +/* + * validator/val_secalgo.h - validator security algorithm functions. + * + * Copyright (c) 2012, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file contains helper functions for the validator module. + * The functions take buffers with raw data and convert to library calls. + */ + +#ifndef VALIDATOR_VAL_SECALGO_H +#define VALIDATOR_VAL_SECALGO_H + +/** + * Return size of DS digest according to its hash algorithm. + * @param algo: DS digest algo. + * @return size in bytes of digest, or 0 if not supported. + */ +size_t ds_digest_size_supported(int algo); + +/** + * @param algo: the DS digest algo + * @param buf: the buffer to digest + * @param len: length of buffer to digest. + * @param res: result stored here (must have sufficient space). + * @return false on failure. + */ +int secalgo_ds_digest(int algo, unsigned char* buf, size_t len, + unsigned char* res); + +/** return true if DNSKEY algorithm id is supported */ +int dnskey_algo_id_is_supported(int id); + +/** + * Check a canonical sig+rrset and signature against a dnskey + * @param buf: buffer with data to verify, the first rrsig part and the + * canonicalized rrset. + * @param algo: DNSKEY algorithm. + * @param sigblock: signature rdata field from RRSIG + * @param sigblock_len: length of sigblock data. + * @param key: public key data from DNSKEY RR. + * @param keylen: length of keydata. + * @param reason: bogus reason in more detail. + * @return secure if verification succeeded, bogus on crypto failure, + * unchecked on format errors and alloc failures. + */ +enum sec_status verify_canonrrset(ldns_buffer* buf, int algo, + unsigned char* sigblock, unsigned int sigblock_len, + unsigned char* key, unsigned int keylen, char** reason); + +#endif /* VALIDATOR_VAL_SECALGO_H */ diff --git a/usr.sbin/unbound/validator/val_sigcrypt.c b/usr.sbin/unbound/validator/val_sigcrypt.c index 4c79c004d02..79d5e45a237 100644 --- a/usr.sbin/unbound/validator/val_sigcrypt.c +++ b/usr.sbin/unbound/validator/val_sigcrypt.c @@ -43,6 +43,7 @@ #include "config.h" #include <ldns/ldns.h> #include "validator/val_sigcrypt.h" +#include "validator/val_secalgo.h" #include "validator/validator.h" #include "util/data/msgreply.h" #include "util/data/msgparse.h" @@ -52,8 +53,8 @@ #include "util/net_help.h" #include "util/regional.h" -#ifndef HAVE_SSL -#error "Need SSL library to do digital signature cryptography" +#if !defined(HAVE_SSL) && !defined(HAVE_NSS) +#error "Need crypto library to do digital signature cryptography" #endif #ifdef HAVE_OPENSSL_ERR_H @@ -265,41 +266,8 @@ ds_get_sigdata(struct ub_packed_rrset_key* k, size_t idx, uint8_t** digest, static size_t ds_digest_size_algo(struct ub_packed_rrset_key* k, size_t idx) { - switch(ds_get_digest_algo(k, idx)) { -#ifdef HAVE_EVP_SHA1 - case LDNS_SHA1: - return SHA_DIGEST_LENGTH; -#endif -#ifdef HAVE_EVP_SHA256 - case LDNS_SHA256: - return SHA256_DIGEST_LENGTH; -#endif -#ifdef USE_GOST - case LDNS_HASH_GOST: - if(EVP_get_digestbyname("md_gost94")) - return 32; - else return 0; -#endif -#ifdef USE_ECDSA - case LDNS_SHA384: - return SHA384_DIGEST_LENGTH; -#endif - default: break; - } - return 0; -} - -#ifdef USE_GOST -/** Perform GOST hash */ -static int -do_gost94(unsigned char* data, size_t len, unsigned char* dest) -{ - const EVP_MD* md = EVP_get_digestbyname("md_gost94"); - if(!md) - return 0; - return ldns_digest_evp(data, (unsigned int)len, dest, md); + return ds_digest_size_supported(ds_get_digest_algo(k, idx)); } -#endif /** * Create a DS digest for a DNSKEY entry. @@ -333,37 +301,9 @@ ds_create_dnskey_digest(struct module_env* env, ldns_buffer_write(b, dnskey_rdata+2, dnskey_len-2); /* skip rdatalen*/ ldns_buffer_flip(b); - switch(ds_get_digest_algo(ds_rrset, ds_idx)) { -#ifdef HAVE_EVP_SHA1 - case LDNS_SHA1: - (void)SHA1((unsigned char*)ldns_buffer_begin(b), - ldns_buffer_limit(b), (unsigned char*)digest); - return 1; -#endif -#ifdef HAVE_EVP_SHA256 - case LDNS_SHA256: - (void)SHA256((unsigned char*)ldns_buffer_begin(b), - ldns_buffer_limit(b), (unsigned char*)digest); - return 1; -#endif -#ifdef USE_GOST - case LDNS_HASH_GOST: - if(do_gost94((unsigned char*)ldns_buffer_begin(b), - ldns_buffer_limit(b), (unsigned char*)digest)) - return 1; -#endif -#ifdef USE_ECDSA - case LDNS_SHA384: - (void)SHA384((unsigned char*)ldns_buffer_begin(b), - ldns_buffer_limit(b), (unsigned char*)digest); - return 1; -#endif - default: - verbose(VERB_QUERY, "unknown DS digest algorithm %d", - (int) ds_get_digest_algo(ds_rrset, ds_idx)); - break; - } - return 0; + return secalgo_ds_digest(ds_get_digest_algo(ds_rrset, ds_idx), + (unsigned char*)ldns_buffer_begin(b), ldns_buffer_limit(b), + (unsigned char*)digest); } int ds_digest_match_dnskey(struct module_env* env, @@ -412,37 +352,6 @@ ds_digest_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, return (ds_digest_size_algo(ds_rrset, ds_idx) != 0); } -/** return true if DNSKEY algorithm id is supported */ -static int -dnskey_algo_id_is_supported(int id) -{ - switch(id) { - case LDNS_DSA: - case LDNS_DSA_NSEC3: - case LDNS_RSASHA1: - case LDNS_RSASHA1_NSEC3: - case LDNS_RSAMD5: -#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) - case LDNS_RSASHA256: -#endif -#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) - case LDNS_RSASHA512: -#endif -#ifdef USE_ECDSA - case LDNS_ECDSAP256SHA256: - case LDNS_ECDSAP384SHA384: -#endif - return 1; -#ifdef USE_GOST - case LDNS_ECC_GOST: - /* we support GOST if it can be loaded */ - return ldns_key_EVP_load_gost_id(); -#endif - default: - return 0; - } -} - int ds_key_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, size_t ds_idx) @@ -606,10 +515,14 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, (uint8_t)rrset_get_sig_algo(rrset, i)); } } - verbose(VERB_ALGO, "rrset failed to verify: no valid signatures for " - "%d algorithms", (int)algo_needs_num_missing(&needs)); if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { + verbose(VERB_ALGO, "rrset failed to verify: " + "no valid signatures for %d algorithms", + (int)algo_needs_num_missing(&needs)); algo_needs_reason(env, alg, reason, "no signatures"); + } else { + verbose(VERB_ALGO, "rrset failed to verify: " + "no valid signatures"); } return sec_status_bogus; } @@ -1314,378 +1227,6 @@ adjust_ttl(struct val_env* ve, uint32_t unow, } } - -/** - * Output a libcrypto openssl error to the logfile. - * @param str: string to add to it. - * @param e: the error to output, error number from ERR_get_error(). - */ -static void -log_crypto_error(const char* str, unsigned long e) -{ - char buf[128]; - /* or use ERR_error_string if ERR_error_string_n is not avail TODO */ - ERR_error_string_n(e, buf, sizeof(buf)); - /* buf now contains */ - /* error:[error code]:[library name]:[function name]:[reason string] */ - log_err("%s crypto %s", str, buf); -} - -/** - * Setup DSA key digest in DER encoding ... - * @param sig: input is signature output alloced ptr (unless failure). - * caller must free alloced ptr if this routine returns true. - * @param len: input is initial siglen, output is output len. - * @return false on failure. - */ -static int -setup_dsa_sig(unsigned char** sig, unsigned int* len) -{ - unsigned char* orig = *sig; - unsigned int origlen = *len; - int newlen; - BIGNUM *R, *S; - DSA_SIG *dsasig; - - /* extract the R and S field from the sig buffer */ - if(origlen < 1 + 2*SHA_DIGEST_LENGTH) - return 0; - R = BN_new(); - if(!R) return 0; - (void) BN_bin2bn(orig + 1, SHA_DIGEST_LENGTH, R); - S = BN_new(); - if(!S) return 0; - (void) BN_bin2bn(orig + 21, SHA_DIGEST_LENGTH, S); - dsasig = DSA_SIG_new(); - if(!dsasig) return 0; - - dsasig->r = R; - dsasig->s = S; - *sig = NULL; - newlen = i2d_DSA_SIG(dsasig, sig); - if(newlen < 0) { - DSA_SIG_free(dsasig); - free(*sig); - return 0; - } - *len = (unsigned int)newlen; - DSA_SIG_free(dsasig); - return 1; -} - -#ifdef USE_ECDSA -/** - * Setup the ECDSA signature in its encoding that the library wants. - * Converts from plain numbers to ASN formatted. - * @param sig: input is signature, output alloced ptr (unless failure). - * caller must free alloced ptr if this routine returns true. - * @param len: input is initial siglen, output is output len. - * @return false on failure. - */ -static int -setup_ecdsa_sig(unsigned char** sig, unsigned int* len) -{ - ECDSA_SIG* ecdsa_sig; - int newlen; - int bnsize = (int)((*len)/2); - /* if too short or not even length, fails */ - if(*len < 16 || bnsize*2 != (int)*len) - return 0; - /* use the raw data to parse two evenly long BIGNUMs, "r | s". */ - ecdsa_sig = ECDSA_SIG_new(); - if(!ecdsa_sig) return 0; - ecdsa_sig->r = BN_bin2bn(*sig, bnsize, ecdsa_sig->r); - ecdsa_sig->s = BN_bin2bn(*sig+bnsize, bnsize, ecdsa_sig->s); - if(!ecdsa_sig->r || !ecdsa_sig->s) { - ECDSA_SIG_free(ecdsa_sig); - return 0; - } - - /* spool it into ASN format */ - *sig = NULL; - newlen = i2d_ECDSA_SIG(ecdsa_sig, sig); - if(newlen <= 0) { - ECDSA_SIG_free(ecdsa_sig); - free(*sig); - return 0; - } - *len = (unsigned int)newlen; - ECDSA_SIG_free(ecdsa_sig); - return 1; -} -#endif /* USE_ECDSA */ - -/** - * Setup key and digest for verification. Adjust sig if necessary. - * - * @param algo: key algorithm - * @param evp_key: EVP PKEY public key to create. - * @param digest_type: digest type to use - * @param key: key to setup for. - * @param keylen: length of key. - * @return false on failure. - */ -static int -setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, - unsigned char* key, size_t keylen) -{ - DSA* dsa; - RSA* rsa; - - switch(algo) { - case LDNS_DSA: - case LDNS_DSA_NSEC3: - *evp_key = EVP_PKEY_new(); - if(!*evp_key) { - log_err("verify: malloc failure in crypto"); - return sec_status_unchecked; - } - dsa = ldns_key_buf2dsa_raw(key, keylen); - if(!dsa) { - verbose(VERB_QUERY, "verify: " - "ldns_key_buf2dsa_raw failed"); - return 0; - } - if(EVP_PKEY_assign_DSA(*evp_key, dsa) == 0) { - verbose(VERB_QUERY, "verify: " - "EVP_PKEY_assign_DSA failed"); - return 0; - } - *digest_type = EVP_dss1(); - - break; - case LDNS_RSASHA1: - case LDNS_RSASHA1_NSEC3: -#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) - case LDNS_RSASHA256: -#endif -#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) - case LDNS_RSASHA512: -#endif - *evp_key = EVP_PKEY_new(); - if(!*evp_key) { - log_err("verify: malloc failure in crypto"); - return sec_status_unchecked; - } - rsa = ldns_key_buf2rsa_raw(key, keylen); - if(!rsa) { - verbose(VERB_QUERY, "verify: " - "ldns_key_buf2rsa_raw SHA failed"); - return 0; - } - if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) { - verbose(VERB_QUERY, "verify: " - "EVP_PKEY_assign_RSA SHA failed"); - return 0; - } - - /* select SHA version */ -#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) - if(algo == LDNS_RSASHA256) - *digest_type = EVP_sha256(); - else -#endif -#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) - if(algo == LDNS_RSASHA512) - *digest_type = EVP_sha512(); - else -#endif - *digest_type = EVP_sha1(); - - break; - case LDNS_RSAMD5: - *evp_key = EVP_PKEY_new(); - if(!*evp_key) { - log_err("verify: malloc failure in crypto"); - return sec_status_unchecked; - } - rsa = ldns_key_buf2rsa_raw(key, keylen); - if(!rsa) { - verbose(VERB_QUERY, "verify: " - "ldns_key_buf2rsa_raw MD5 failed"); - return 0; - } - if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) { - verbose(VERB_QUERY, "verify: " - "EVP_PKEY_assign_RSA MD5 failed"); - return 0; - } - *digest_type = EVP_md5(); - - break; -#ifdef USE_GOST - case LDNS_ECC_GOST: - *evp_key = ldns_gost2pkey_raw(key, keylen); - if(!*evp_key) { - verbose(VERB_QUERY, "verify: " - "ldns_gost2pkey_raw failed"); - return 0; - } - *digest_type = EVP_get_digestbyname("md_gost94"); - if(!*digest_type) { - verbose(VERB_QUERY, "verify: " - "EVP_getdigest md_gost94 failed"); - return 0; - } - break; -#endif -#ifdef USE_ECDSA - case LDNS_ECDSAP256SHA256: - *evp_key = ldns_ecdsa2pkey_raw(key, keylen, - LDNS_ECDSAP256SHA256); - if(!*evp_key) { - verbose(VERB_QUERY, "verify: " - "ldns_ecdsa2pkey_raw failed"); - return 0; - } -#ifdef USE_ECDSA_EVP_WORKAROUND - /* openssl before 1.0.0 fixes RSA with the SHA256 - * hash in EVP. We create one for ecdsa_sha256 */ - { - static int md_ecdsa_256_done = 0; - static EVP_MD md; - if(!md_ecdsa_256_done) { - EVP_MD m = *EVP_sha256(); - md_ecdsa_256_done = 1; - m.required_pkey_type[0] = (*evp_key)->type; - m.verify = (void*)ECDSA_verify; - md = m; - } - *digest_type = &md; - } -#else - *digest_type = EVP_sha256(); -#endif - break; - case LDNS_ECDSAP384SHA384: - *evp_key = ldns_ecdsa2pkey_raw(key, keylen, - LDNS_ECDSAP384SHA384); - if(!*evp_key) { - verbose(VERB_QUERY, "verify: " - "ldns_ecdsa2pkey_raw failed"); - return 0; - } -#ifdef USE_ECDSA_EVP_WORKAROUND - /* openssl before 1.0.0 fixes RSA with the SHA384 - * hash in EVP. We create one for ecdsa_sha384 */ - { - static int md_ecdsa_384_done = 0; - static EVP_MD md; - if(!md_ecdsa_384_done) { - EVP_MD m = *EVP_sha384(); - md_ecdsa_384_done = 1; - m.required_pkey_type[0] = (*evp_key)->type; - m.verify = (void*)ECDSA_verify; - md = m; - } - *digest_type = &md; - } -#else - *digest_type = EVP_sha384(); -#endif - break; -#endif /* USE_ECDSA */ - default: - verbose(VERB_QUERY, "verify: unknown algorithm %d", - algo); - return 0; - } - return 1; -} - -/** - * Check a canonical sig+rrset and signature against a dnskey - * @param buf: buffer with data to verify, the first rrsig part and the - * canonicalized rrset. - * @param algo: DNSKEY algorithm. - * @param sigblock: signature rdata field from RRSIG - * @param sigblock_len: length of sigblock data. - * @param key: public key data from DNSKEY RR. - * @param keylen: length of keydata. - * @param reason: bogus reason in more detail. - * @return secure if verification succeeded, bogus on crypto failure, - * unchecked on format errors and alloc failures. - */ -static enum sec_status -verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, - unsigned int sigblock_len, unsigned char* key, unsigned int keylen, - char** reason) -{ - const EVP_MD *digest_type; - EVP_MD_CTX ctx; - int res, dofree = 0; - EVP_PKEY *evp_key = NULL; - - if(!setup_key_digest(algo, &evp_key, &digest_type, key, keylen)) { - verbose(VERB_QUERY, "verify: failed to setup key"); - *reason = "use of key for crypto failed"; - EVP_PKEY_free(evp_key); - return sec_status_bogus; - } - /* if it is a DSA signature in bind format, convert to DER format */ - if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) && - sigblock_len == 1+2*SHA_DIGEST_LENGTH) { - if(!setup_dsa_sig(&sigblock, &sigblock_len)) { - verbose(VERB_QUERY, "verify: failed to setup DSA sig"); - *reason = "use of key for DSA crypto failed"; - EVP_PKEY_free(evp_key); - return sec_status_bogus; - } - dofree = 1; - } -#ifdef USE_ECDSA - else if(algo == LDNS_ECDSAP256SHA256 || algo == LDNS_ECDSAP384SHA384) { - /* EVP uses ASN prefix on sig, which is not in the wire data */ - if(!setup_ecdsa_sig(&sigblock, &sigblock_len)) { - verbose(VERB_QUERY, "verify: failed to setup ECDSA sig"); - *reason = "use of signature for ECDSA crypto failed"; - EVP_PKEY_free(evp_key); - return sec_status_bogus; - } - dofree = 1; - } -#endif /* USE_ECDSA */ - - /* do the signature cryptography work */ - EVP_MD_CTX_init(&ctx); - if(EVP_VerifyInit(&ctx, digest_type) == 0) { - verbose(VERB_QUERY, "verify: EVP_VerifyInit failed"); - EVP_PKEY_free(evp_key); - if(dofree) free(sigblock); - return sec_status_unchecked; - } - if(EVP_VerifyUpdate(&ctx, (unsigned char*)ldns_buffer_begin(buf), - (unsigned int)ldns_buffer_limit(buf)) == 0) { - verbose(VERB_QUERY, "verify: EVP_VerifyUpdate failed"); - EVP_PKEY_free(evp_key); - if(dofree) free(sigblock); - return sec_status_unchecked; - } - - res = EVP_VerifyFinal(&ctx, sigblock, sigblock_len, evp_key); - if(EVP_MD_CTX_cleanup(&ctx) == 0) { - verbose(VERB_QUERY, "verify: EVP_MD_CTX_cleanup failed"); - EVP_PKEY_free(evp_key); - if(dofree) free(sigblock); - return sec_status_unchecked; - } - EVP_PKEY_free(evp_key); - - if(dofree) - free(sigblock); - - if(res == 1) { - return sec_status_secure; - } else if(res == 0) { - verbose(VERB_QUERY, "verify: signature mismatch"); - *reason = "signature crypto failed"; - return sec_status_bogus; - } - - log_crypto_error("verify:", ERR_get_error()); - return sec_status_unchecked; -} - enum sec_status dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, struct val_env* ve, uint32_t now, diff --git a/usr.sbin/unbound/validator/val_utils.c b/usr.sbin/unbound/validator/val_utils.c index b0475d8031c..d4a64464d80 100644 --- a/usr.sbin/unbound/validator/val_utils.c +++ b/usr.sbin/unbound/validator/val_utils.c @@ -54,7 +54,6 @@ #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, diff --git a/usr.sbin/unbound/validator/validator.c b/usr.sbin/unbound/validator/validator.c index af1d344a691..d8f9bbcc4b3 100644 --- a/usr.sbin/unbound/validator/validator.c +++ b/usr.sbin/unbound/validator/validator.c @@ -1881,7 +1881,8 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, /* store overall validation result in orig_msg */ if(vq->rrset_skip == 0) vq->orig_msg->rep->security = vq->chase_reply->security; - else if(vq->rrset_skip < vq->orig_msg->rep->an_numrrsets + + else if(subtype != VAL_CLASS_REFERRAL || + vq->rrset_skip < vq->orig_msg->rep->an_numrrsets + vq->orig_msg->rep->ns_numrrsets) { /* ignore sec status of additional section if a referral * type message skips there and diff --git a/usr.sbin/unbound/validator/validator.h b/usr.sbin/unbound/validator/validator.h index 18e905efcd2..1a29c161b9f 100644 --- a/usr.sbin/unbound/validator/validator.h +++ b/usr.sbin/unbound/validator/validator.h @@ -56,13 +56,13 @@ struct config_strlist; * will be primed no more often than this interval. Used when harden- * dnssec-stripped is off and the trust anchor fails. */ -#define NULL_KEY_TTL 900 /* seconds */ +#define NULL_KEY_TTL 60 /* seconds */ /** * TTL for bogus key entries. When a DS or DNSKEY fails in the chain of * trust the entire zone for that name is blacked out for this TTL. */ -#define BOGUS_KEY_TTL 900 /* seconds */ +#define BOGUS_KEY_TTL 60 /* seconds */ /** max number of query restarts, number of IPs to probe */ #define VAL_MAX_RESTART_COUNT 5 |