diff --git a/configure b/configure index 4d4981b8..281a408f 100755 --- a/configure +++ b/configure @@ -593,7 +593,7 @@ PACKAGE_TARNAME='getdns' PACKAGE_VERSION='0.1.0' PACKAGE_STRING='getdns 0.1.0' PACKAGE_BUGREPORT='stub-resolver@verisignlabs.com' -PACKAGE_URL='' +PACKAGE_URL='http://www.getdnsapi.net' ac_unique_file="src/getdns/getdns.h" # Factoring default headers for most tests. @@ -1398,6 +1398,7 @@ Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . +getdns home page: . _ACEOF ac_status=$? fi @@ -11981,7 +11982,8 @@ $config_headers Configuration commands: $config_commands -Report bugs to ." +Report bugs to . +getdns home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 diff --git a/configure.ac b/configure.ac index f106e0af..005a919d 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ # AC_PREREQ([2.56]) -AC_INIT([getdns], [0.1.0], [stub-resolver@verisignlabs.com]) +AC_INIT([getdns], [0.1.0], [stub-resolver@verisignlabs.com], [], [http://www.getdnsapi.net]) AC_CONFIG_SRCDIR([src/getdns/getdns.h]) # AM_INIT_AUTOMAKE # LT_INIT diff --git a/src/Makefile.in b/src/Makefile.in index f1cb9487..67bf55c8 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -38,9 +38,8 @@ EXTENSION_LIBEVENT_OBJ=@EXTENSION_LIBEVENT_OBJ@ EXTENSION_LIBUV_OBJ=@EXTENSION_LIBUV_OBJ@ EXTENSION_LIBEV_OBJ=@EXTENSION_LIBEV_OBJ@ GETDNS_OBJ=sync.lo context.lo list.lo dict.lo convert.lo general.lo \ - hostname.lo service.lo request-internal.lo validate_dnssec.lo \ - util-internal.lo getdns_error.lo rr-dict.lo validation-chain.lo \ - const-info.lo \ + hostname.lo service.lo request-internal.lo util-internal.lo \ + getdns_error.lo rr-dict.lo dnssec.lo const-info.lo \ $(EXTENSION_LIBEVENT_OBJ) $(EXTENSION_LIBUV_OBJ) $(EXTENSION_LIBEV_OBJ) .SUFFIXES: .c .o .a .lo .h diff --git a/src/context.c b/src/context.c index a7c0b540..59f30c42 100644 --- a/src/context.c +++ b/src/context.c @@ -47,6 +47,7 @@ #include "context.h" #include "types-internal.h" #include "util-internal.h" +#include "dnssec.h" void *plain_mem_funcs_user_arg = MF_PLAIN; @@ -61,13 +62,22 @@ static struct getdns_list *create_from_ldns_list(struct getdns_context *, static getdns_return_t set_os_defaults(struct getdns_context *); static int transaction_id_cmp(const void *, const void *); static int timeout_cmp(const void *, const void *); -static void set_ub_string_opt(struct getdns_context *, char *, char *); -static void set_ub_number_opt(struct getdns_context *, char *, uint16_t); -static inline void clear_resolution_type_set_flag(struct getdns_context *, uint16_t); static void dispatch_updated(struct getdns_context *, uint16_t); static void cancel_dns_req(getdns_dns_req *); static void cancel_outstanding_requests(struct getdns_context*, int); +/* unbound helpers */ +static getdns_return_t rebuild_ub_ctx(struct getdns_context* context); +static void set_ub_string_opt(struct getdns_context *, char *, char *); +static void set_ub_number_opt(struct getdns_context *, char *, uint16_t); +static getdns_return_t set_ub_dns_transport(struct getdns_context*, getdns_transport_t); +static void set_ub_limit_outstanding_queries(struct getdns_context*, + uint16_t); +static void set_ub_dnssec_allowed_skew(struct getdns_context*, uint32_t); +static void set_ub_edns_maximum_udp_payload_size(struct getdns_context*, + uint16_t); + + /* Stuff to make it compile pedantically */ #define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code; @@ -187,7 +197,7 @@ filechg_check(struct getdns_context *context, struct filechg *fchg) return GETDNS_FCHG_ERRORS; } - /* we want to consider a file that previously returned error for stat() as a + /* we want to consider a file that previously returned error for stat() as a change */ if(fchg->prevstat == NULL) @@ -366,58 +376,6 @@ timeout_cmp(const void *to1, const void *to2) } } - -/* - * priv_getdns_check_and_add_ta_file - * - * Do not set trust anchor when it is unreadable or unparsable. - * Copied from (older) unbound anchor_read_file - */ -static void -priv_getdns_check_and_add_ta_file(struct getdns_context *context) -{ - uint32_t ttl = 3600; - ldns_rdf* orig = NULL, *prev = NULL; - int line = 1; - ldns_status s; - ldns_rr *rr; - int nkeys; - FILE *in = fopen(TRUST_ANCHOR_FILE, "r"); - - context->has_ta = 0; - if (!in) - return; - - nkeys = 0; - while (! feof(in)) { - rr = NULL; - s = ldns_rr_new_frm_fp_l(&rr, in, &ttl, &orig, &prev, &line); - if (s == LDNS_STATUS_SYNTAX_EMPTY /* empty line */ - || s == LDNS_STATUS_SYNTAX_TTL /* $TTL */ - || s == LDNS_STATUS_SYNTAX_ORIGIN /* $ORIGIN */) - continue; - - if (s != LDNS_STATUS_OK) { - ldns_rr_free(rr); - nkeys = 0; - break; - } - if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_DS || - ldns_rr_get_type(rr) == LDNS_RR_TYPE_DNSKEY) - nkeys++; - - ldns_rr_free(rr); - } - ldns_rdf_deep_free(orig); - ldns_rdf_deep_free(prev); - fclose(in); - if (nkeys) { - context->has_ta = nkeys; - (void) ub_ctx_add_ta_file(context->unbound_ctx, - TRUST_ANCHOR_FILE); - } -} - /* * getdns_context_create * @@ -459,8 +417,6 @@ getdns_context_create_with_extended_memory_functions( result->mf.mf.ext.realloc = realloc; result->mf.mf.ext.free = free; - result->unbound_ctx = ub_ctx_create(); - result->resolution_type_set = 0; result->outbound_requests = ldns_rbtree_create(transaction_id_cmp); @@ -495,18 +451,20 @@ getdns_context_create_with_extended_memory_functions( return GETDNS_RETURN_GENERIC_ERROR; } } + result->dnssec_allowed_skew = 0; + result->edns_maximum_udp_payload_size = 512; + result->dns_transport = GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP; + result->limit_outstanding_queries = 0; + result->has_ta = priv_getdns_parse_ta_file(NULL, NULL); + /* unbound context is initialized here */ + result->unbound_ctx = NULL; + if (GETDNS_RETURN_GOOD != rebuild_ub_ctx(result)) { + getdns_context_destroy(result); + return GETDNS_RETURN_GENERIC_ERROR; + } *context = result; - /* other opts */ - getdns_context_set_dnssec_allowed_skew(result, 0); - getdns_context_set_edns_maximum_udp_payload_size(result, 512); - getdns_context_set_dns_transport(result, - GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP); - - /* Set default trust anchor */ - priv_getdns_check_and_add_ta_file(result); - return GETDNS_RETURN_GOOD; } /* getdns_context_create_with_extended_memory_functions */ @@ -572,7 +530,7 @@ getdns_context_destroy(struct getdns_context *context) GETDNS_FREE(context->my_mf, context->fchg_hosts); } - cancel_outstanding_requests(context, 0); + cancel_outstanding_requests(context, 1); getdns_extension_detach_eventloop(context); getdns_list_destroy(context->dns_root_servers); @@ -581,7 +539,8 @@ getdns_context_destroy(struct getdns_context *context) getdns_list_destroy(context->upstream_list); /* destroy the ub context */ - ub_ctx_delete(context->unbound_ctx); + if (context->unbound_ctx) + ub_ctx_delete(context->unbound_ctx); ldns_rbtree_free(context->outbound_requests); ldns_rbtree_free(context->timeouts_by_id); @@ -612,7 +571,8 @@ getdns_context_set_context_update_callback(struct getdns_context *context, static void set_ub_string_opt(struct getdns_context *ctx, char *opt, char *value) { - ub_ctx_set_option(ctx->unbound_ctx, opt, value); + if (ctx->unbound_ctx) + ub_ctx_set_option(ctx->unbound_ctx, opt, value); } static void @@ -623,15 +583,32 @@ set_ub_number_opt(struct getdns_context *ctx, char *opt, uint16_t value) set_ub_string_opt(ctx, opt, buffer); } -/* - * Clear the resolution type set flag if needed - */ -static inline void -clear_resolution_type_set_flag(struct getdns_context *context, uint16_t type) -{ - if (context->resolution_type_set == type) { - context->resolution_type_set = 0; +static getdns_return_t +rebuild_ub_ctx(struct getdns_context* context) { + if (context->unbound_ctx != NULL) { + /* cancel all requests and delete */ + cancel_outstanding_requests(context, 1); + ub_ctx_delete(context->unbound_ctx); + context->unbound_ctx = NULL; } + /* setup */ + context->unbound_ctx = ub_ctx_create(); + if (!context->unbound_ctx) { + return GETDNS_RETURN_MEMORY_ERROR; + } + set_ub_dnssec_allowed_skew(context, + context->dnssec_allowed_skew); + set_ub_edns_maximum_udp_payload_size(context, + context->edns_maximum_udp_payload_size); + set_ub_dns_transport(context, + context->dns_transport); + + /* Set default trust anchor */ + if (context->has_ta) { + (void) ub_ctx_add_ta_file( + context->unbound_ctx, TRUST_ANCHOR_FILE); + } + return GETDNS_RETURN_GOOD; } /** @@ -657,7 +634,10 @@ getdns_context_set_resolution_type(struct getdns_context *context, if (value != GETDNS_RESOLUTION_STUB && value != GETDNS_RESOLUTION_RECURSING) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } - + if (context->resolution_type_set != 0) { + /* already setup */ + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } context->resolution_type = value; dispatch_updated(context, GETDNS_CONTEXT_CODE_RESOLUTION_TYPE); @@ -679,10 +659,13 @@ getdns_context_set_namespaces(struct getdns_context *context, if (namespace_count == 0 || namespaces == NULL) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } + if (context->resolution_type_set != 0) { + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } for(i=0; idns_transport) { + context->dns_transport = value; + dispatch_updated(context, GETDNS_CONTEXT_CODE_DNS_TRANSPORT); + } return GETDNS_RETURN_GOOD; } /* getdns_context_set_dns_transport */ +static void +set_ub_limit_outstanding_queries(struct getdns_context* context, uint16_t value) { + /* num-queries-per-thread */ + set_ub_number_opt(context, "num-queries-per-thread", value); +} /* * getdns_context_set_limit_outstanding_queries * @@ -744,11 +742,12 @@ getdns_context_set_limit_outstanding_queries(struct getdns_context *context, uint16_t limit) { RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - /* num-queries-per-thread */ - set_ub_number_opt(context, "num-queries-per-thread", limit); - - dispatch_updated(context, - GETDNS_CONTEXT_CODE_LIMIT_OUTSTANDING_QUERIES); + set_ub_limit_outstanding_queries(context, limit); + if (limit != context->limit_outstanding_queries) { + context->limit_outstanding_queries = limit; + dispatch_updated(context, + GETDNS_CONTEXT_CODE_LIMIT_OUTSTANDING_QUERIES); + } return GETDNS_RETURN_GOOD; } /* getdns_context_set_limit_outstanding_queries */ @@ -783,10 +782,12 @@ getdns_context_set_follow_redirects(struct getdns_context *context, { RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); context->follow_redirects = value; + if (context->resolution_type_set != 0) { + /* already setup */ + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } - clear_resolution_type_set_flag(context, GETDNS_RESOLUTION_RECURSING); dispatch_updated(context, GETDNS_CONTEXT_CODE_FOLLOW_REDIRECTS); - return GETDNS_RETURN_GOOD; } /* getdns_context_set_follow_redirects */ @@ -801,6 +802,10 @@ getdns_context_set_dns_root_servers(struct getdns_context *context, struct getdns_list *copy = NULL; size_t count = 0; RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + if (context->resolution_type_set != 0) { + /* already setup */ + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } if (addresses != NULL) { if (getdns_list_copy(addresses, ©) != GETDNS_RETURN_GOOD) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; @@ -832,8 +837,6 @@ getdns_context_set_dns_root_servers(struct getdns_context *context, getdns_list_destroy(context->dns_root_servers); context->dns_root_servers = addresses; - clear_resolution_type_set_flag(context, GETDNS_RESOLUTION_RECURSING); - dispatch_updated(context, GETDNS_CONTEXT_CODE_DNS_ROOT_SERVERS); return GETDNS_RETURN_GOOD; @@ -871,6 +874,10 @@ getdns_context_set_suffix(struct getdns_context *context, struct getdns_list * v { struct getdns_list *copy = NULL; RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + if (context->resolution_type_set != 0) { + /* already setup */ + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } if (value != NULL) { if (getdns_list_copy(value, ©) != GETDNS_RETURN_GOOD) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; @@ -880,8 +887,6 @@ getdns_context_set_suffix(struct getdns_context *context, struct getdns_list * v getdns_list_destroy(context->suffix); context->suffix = value; - clear_resolution_type_set_flag(context, GETDNS_RESOLUTION_STUB); - dispatch_updated(context, GETDNS_CONTEXT_CODE_SUFFIX); return GETDNS_RETURN_GOOD; @@ -911,6 +916,11 @@ getdns_context_set_dnssec_trust_anchors(struct getdns_context *context, return GETDNS_RETURN_GOOD; } /* getdns_context_set_dnssec_trust_anchors */ +static void +set_ub_dnssec_allowed_skew(struct getdns_context* context, uint32_t value) { + set_ub_number_opt(context, "val-sig-skew-min", value); + set_ub_number_opt(context, "val-sig-skew-max", value); +} /* * getdns_context_set_dnssec_allowed_skew * @@ -920,9 +930,11 @@ getdns_context_set_dnssec_allowed_skew(struct getdns_context *context, uint32_t value) { RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - set_ub_number_opt(context, "val-sig-skew-min", value); - set_ub_number_opt(context, "val-sig-skew-max", value); - dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_ALLOWED_SKEW); + set_ub_dnssec_allowed_skew(context, value); + if (value != context->dnssec_allowed_skew) { + context->dnssec_allowed_skew = value; + dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_ALLOWED_SKEW); + } return GETDNS_RETURN_GOOD; } /* getdns_context_set_dnssec_allowed_skew */ @@ -943,6 +955,10 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, if (count == 0 || r != GETDNS_RETURN_GOOD) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } + if (context->resolution_type_set != 0) { + /* already setup */ + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } struct getdns_list *copy = NULL; if (getdns_list_copy(upstream_list, ©) != GETDNS_RETURN_GOOD) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; @@ -966,14 +982,19 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, getdns_list_destroy(context->upstream_list); context->upstream_list = upstream_list; - clear_resolution_type_set_flag(context, GETDNS_RESOLUTION_STUB); - dispatch_updated(context, GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS); return GETDNS_RETURN_GOOD; } /* getdns_context_set_upstream_recursive_servers */ + +static void +set_ub_edns_maximum_udp_payload_size(struct getdns_context* context, + uint16_t value) { + /* max-udp-size */ + set_ub_number_opt(context, "max-udp-size", value); +} /* * getdns_context_set_edns_maximum_udp_payload_size * @@ -987,12 +1008,12 @@ getdns_context_set_edns_maximum_udp_payload_size(struct getdns_context *context, if (value < 512) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } - - /* max-udp-size */ - set_ub_number_opt(context, "max-udp-size", value); - - dispatch_updated(context, - GETDNS_CONTEXT_CODE_EDNS_MAXIMUM_UDP_PAYLOAD_SIZE); + set_ub_edns_maximum_udp_payload_size(context, value); + if (value != context->edns_maximum_udp_payload_size) { + context->edns_maximum_udp_payload_size = value; + dispatch_updated(context, + GETDNS_CONTEXT_CODE_EDNS_MAXIMUM_UDP_PAYLOAD_SIZE); + } return GETDNS_RETURN_GOOD; } /* getdns_context_set_edns_maximum_udp_payload_size */ @@ -1171,7 +1192,7 @@ ub_setup_stub(struct ub_ctx *ctx, struct getdns_list * upstreams) struct getdns_dict *dict; struct getdns_bindata *address_string; getdns_return_t r; - + r = getdns_list_get_length(upstreams, &count); if (r != GETDNS_RETURN_GOOD) return r; @@ -1281,7 +1302,7 @@ getdns_context_prepare_for_resolution(struct getdns_context *context, if (context->resolution_type_set == context->resolution_type) /* already set and no config changes - * have caused this to be bad. + * have caused this to be bad. */ return GETDNS_RETURN_GOOD; @@ -1290,6 +1311,7 @@ getdns_context_prepare_for_resolution(struct getdns_context *context, * so we need to respect that order */ + if (! usenamespaces) { r = priv_getdns_ns_dns_setup(context); if (r == GETDNS_RETURN_GOOD) @@ -1643,5 +1665,74 @@ getdns_context_clear_timeout(struct getdns_context* context, return GETDNS_RETURN_GOOD; } +static inline getdns_return_t +priv_dict_set_list_if_not_null(getdns_dict* dict, + const char* name, getdns_list* list) { + if (!list) { + return GETDNS_RETURN_GOOD; + } + return getdns_dict_set_list(dict, name, list); +} + +static getdns_dict* +priv_get_context_settings(getdns_context* context) { + getdns_return_t r = GETDNS_RETURN_GOOD; + getdns_dict* result = getdns_dict_create_with_context(context); + if (!result) { + return NULL; + } + /* int fields */ + r = getdns_dict_set_int(result, "dns_transport", context->dns_transport); + r |= getdns_dict_set_int(result, "timeout", context->timeout); + r |= getdns_dict_set_int(result, "limit_outstanding_queries", context->limit_outstanding_queries); + r |= getdns_dict_set_int(result, "dnssec_allowed_skew", context->dnssec_allowed_skew); + r |= getdns_dict_set_int(result, "follow_redirects", context->follow_redirects); + r |= getdns_dict_set_int(result, "edns_maximum_udp_payload_size", context->edns_maximum_udp_payload_size); + r |= getdns_dict_set_int(result, "edns_extended_rcode", context->edns_extended_rcode); + r |= getdns_dict_set_int(result, "edns_version", context->edns_version); + r |= getdns_dict_set_int(result, "edns_do_bit", context->edns_do_bit); + r |= getdns_dict_set_int(result, "append_name", context->append_name); + /* list fields */ + r |= priv_dict_set_list_if_not_null(result, "suffix", context->suffix); + r |= priv_dict_set_list_if_not_null(result, "upstream_recursive_servers", context->upstream_list); + if (context->namespace_count > 0) { + /* create a namespace list */ + size_t i; + getdns_list* namespaces = getdns_list_create_with_context(context); + if (namespaces) { + for (i = 0; i < context->namespace_count; ++i) { + r |= getdns_list_set_int(namespaces, i, context->namespaces[i]); + } + r |= getdns_dict_set_list(result, "namespaces", namespaces); + } + } + if (r != GETDNS_RETURN_GOOD) { + getdns_dict_destroy(result); + result = NULL; + } + return result; +} + +getdns_dict* +getdns_context_get_api_information(getdns_context* context) { + getdns_return_t r = GETDNS_RETURN_GOOD; + getdns_dict* result = getdns_dict_create_with_context(context); + getdns_dict* settings; + if (!result) { + return NULL; + } + r = getdns_dict_util_set_string(result, "version_string", PACKAGE_VERSION); + r |= getdns_dict_util_set_string(result, "implementation_string", PACKAGE_URL); + r |= getdns_dict_set_int(result, "resolver_type", context->resolution_type); + settings = priv_get_context_settings(context); + r |= getdns_dict_set_dict(result, "all_context", settings); + getdns_dict_destroy(settings); + if (r != GETDNS_RETURN_GOOD) { + getdns_dict_destroy(result); + result = NULL; + } + return result; +} + /* context.c */ diff --git a/src/context.h b/src/context.h index e2b3f586..c335f5b4 100644 --- a/src/context.h +++ b/src/context.h @@ -78,10 +78,14 @@ struct getdns_context { struct getdns_list *suffix; struct getdns_list *dnssec_trust_anchors; struct getdns_list *upstream_list; + getdns_transport_t dns_transport; + uint16_t limit_outstanding_queries; + uint32_t dnssec_allowed_skew; uint8_t edns_extended_rcode; uint8_t edns_version; uint8_t edns_do_bit; + uint16_t edns_maximum_udp_payload_size; getdns_update_callback update_callback; diff --git a/src/dict.c b/src/dict.c index 1e4071fb..19aca74d 100644 --- a/src/dict.c +++ b/src/dict.c @@ -717,7 +717,7 @@ priv_getdns_print_rcode(ldns_buffer *buf, uint32_t rcode) " GETDNS_RCODE_BADNAME" , " GETDNS_RCODE_BADALG" , " GETDNS_RCODE_BADTRUNC" }; - if (rcode >= 0 && rcode <= 10) + if (rcode <= 10) (void) ldns_buffer_printf(buf, rcodes[rcode]); else if (rcode >= 16 && rcode <= 22) (void) ldns_buffer_printf(buf, rcodes[rcode-6]); diff --git a/src/validation-chain.c b/src/dnssec.c similarity index 52% rename from src/validation-chain.c rename to src/dnssec.c index 302b3c79..1c33c4b1 100644 --- a/src/validation-chain.c +++ b/src/dnssec.c @@ -1,6 +1,6 @@ /** * - * /brief priv_getdns_get_validation_chain function + * /brief function for DNSSEC * * The priv_getdns_get_validation_chain function is called after an answer * has been fetched when the dnssec_return_validation_chain extension is set. @@ -35,12 +35,18 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include +#include #include #include #include +#include "config.h" #include "context.h" #include "util-internal.h" #include "types-internal.h" +#include "dnssec.h" +#include "rr-dict.h" void priv_getdns_call_user_callback(getdns_dns_req *, struct getdns_dict *); @@ -320,4 +326,363 @@ priv_getdns_get_validation_chain_sync(getdns_dns_req *dns_req) return sync_response; } -/* validation-chain.c */ +/********************** functions for validate_dnssec *************************/ + +static getdns_return_t +priv_getdns_rr_list_from_list(struct getdns_list *list, ldns_rr_list **rr_list) +{ + getdns_return_t r; + size_t i, l; + struct getdns_dict *rr_dict; + ldns_rr *rr; + + if ((r = getdns_list_get_length(list, &l))) + return r; + + if (! (*rr_list = ldns_rr_list_new())) + return GETDNS_RETURN_MEMORY_ERROR; + + for (i = 0; i < l; i++) { + if ((r = getdns_list_get_dict(list, i, &rr_dict))) + break; + + if ((r = priv_getdns_create_rr_from_dict(rr_dict, &rr))) + break; + + if (! ldns_rr_list_push_rr(*rr_list, rr)) { + ldns_rr_free(rr); + r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + } + if (r) + ldns_rr_list_deep_free(*rr_list); + return r; +} + +static getdns_return_t +priv_getdns_dnssec_zone_from_list(struct getdns_list *list, + ldns_dnssec_zone **zone) +{ + getdns_return_t r; + size_t i, l; + struct getdns_dict *rr_dict; + ldns_rr *rr; + ldns_status s; + + if ((r = getdns_list_get_length(list, &l))) + return r; + + if (! (*zone = ldns_dnssec_zone_new())) + return GETDNS_RETURN_MEMORY_ERROR; + + for (i = 0; i < l; i++) { + if ((r = getdns_list_get_dict(list, i, &rr_dict))) + break; + + if ((r = priv_getdns_create_rr_from_dict(rr_dict, &rr))) + break; + + if ((s = ldns_dnssec_zone_add_rr(*zone, rr))) { + ldns_rr_free(rr); + r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + } + if (r) + ldns_dnssec_zone_free(*zone); + return r; +} + +typedef struct zone_iter { + ldns_dnssec_zone *zone; + ldns_rbnode_t *cur_node; + ldns_dnssec_rrsets *cur_rrset; +} zone_iter; + +static void +rrset_iter_init_zone(zone_iter *i, ldns_dnssec_zone *zone) +{ + assert(i); + + i->zone = zone; + i->cur_node = zone->names ? ldns_rbtree_first(zone->names) + : LDNS_RBTREE_NULL; + i->cur_rrset = i->cur_node != LDNS_RBTREE_NULL + ? ((ldns_dnssec_name *)i->cur_node->data)->rrsets + : NULL; +} + +static ldns_dnssec_rrsets * +rrset_iter_value(zone_iter *i) +{ + assert(i); + + return i->cur_rrset; +} + +static void +rrset_iter_next(zone_iter *i) +{ + assert(i); + + if (! i->cur_rrset) + return; + + if (! (i->cur_rrset = i->cur_rrset->next)) { + i->cur_node = ldns_rbtree_next(i->cur_node); + i->cur_rrset = i->cur_node != LDNS_RBTREE_NULL + ? ((ldns_dnssec_name *)i->cur_node->data)->rrsets + : NULL; + } +} + +static ldns_rr_list * +rrs2rr_list(ldns_dnssec_rrs *rrs) +{ + ldns_rr_list *r = ldns_rr_list_new(); + if (r) + while (rrs) { + (void) ldns_rr_list_push_rr(r, rrs->rr); + rrs = rrs->next; + } + return r; +} + +static ldns_status +verify_rrset(ldns_dnssec_rrsets *rrset_and_sigs, + const ldns_rr_list *keys, ldns_rr_list *good_keys) +{ + ldns_status s; + ldns_rr_list *rrset = rrs2rr_list(rrset_and_sigs->rrs); + ldns_rr_list *sigs = rrs2rr_list(rrset_and_sigs->signatures); + s = ldns_verify(rrset, sigs, keys, good_keys); + ldns_rr_list_free(sigs); + ldns_rr_list_free(rrset); + return s; +} + +static ldns_status +chase(ldns_dnssec_rrsets *rrset, ldns_dnssec_zone *support, + ldns_rr_list *support_keys, ldns_rr_list *trusted) +{ + ldns_status s; + ldns_rr_list *verifying_keys; + size_t i, j; + ldns_rr *rr; + ldns_dnssec_rrsets *key_rrset; + ldns_dnssec_rrs *rrs; + + /* Secure by trusted keys? */ + s = verify_rrset(rrset, trusted, NULL); + if (s == 0) + return s; + + /* No, chase with support records.. + * Is there a verifying key in the support records? + */ + verifying_keys = ldns_rr_list_new(); + s = verify_rrset(rrset, support_keys, verifying_keys); + if (s != 0) + goto done_free_verifying_keys; + + /* Ok, we have verifying keys from the support records. + * Compare them with the *trusted* keys or DSes, + * or chase them further down the validation chain. + */ + for (i = 0; i < ldns_rr_list_rr_count(verifying_keys); i++) { + /* Lookup the rrset for key rr from the support records */ + rr = ldns_rr_list_rr(verifying_keys, i); + key_rrset = ldns_dnssec_zone_find_rrset( + support, ldns_rr_owner(rr), ldns_rr_get_type(rr)); + if (! key_rrset) { + s = LDNS_STATUS_CRYPTO_NO_DNSKEY; + break; + } + /* When we signed ourselves, we have to cross domain border + * and look for a matching DS signed by a parents key + */ + if (rrset == key_rrset) { + /* Is the verifying key trusted? + * (i.e. DS in trusted) + */ + for (j = 0; j < ldns_rr_list_rr_count(trusted); j++) + if (ldns_rr_compare_ds(ldns_rr_list_rr( + trusted, j), rr)) + break; + /* If so, check for the next verifying key + * (or exit SECURE) + */ + if (j < ldns_rr_list_rr_count(trusted)) + continue; + + /* Search for a matching DS in the support records */ + key_rrset = ldns_dnssec_zone_find_rrset( + support, ldns_rr_owner(rr), LDNS_RR_TYPE_DS); + if (! key_rrset) { + s = LDNS_STATUS_CRYPTO_NO_DNSKEY; + break; + } + /* Now check if DS matches the DNSKEY! */ + for (rrs = key_rrset->rrs; rrs; rrs = rrs->next) + if (ldns_rr_compare_ds(rr, rrs->rr)) + break; + if (! rrs) { + s = LDNS_STATUS_CRYPTO_NO_DNSKEY; + break; + } + } + /* Pursue the chase with the verifying key (or its DS) */ + s = chase(key_rrset, support, support_keys, trusted); + if (s != 0) + break; + } +done_free_verifying_keys: + ldns_rr_list_free(verifying_keys); + return s; +} + +/* + * getdns_validate_dnssec + * + */ +getdns_return_t +getdns_validate_dnssec(struct getdns_list *records_to_validate, + struct getdns_list *support_records, + struct getdns_list *trust_anchors) +{ + getdns_return_t r; + ldns_rr_list *trusted; + ldns_dnssec_zone *support; + ldns_rr_list *support_keys; + ldns_dnssec_zone *to_validate; + zone_iter i; + ldns_dnssec_rrsets *rrset; + ldns_dnssec_rrs *rrs; + ldns_status s = LDNS_STATUS_OK; + + if ((r = priv_getdns_rr_list_from_list(trust_anchors, &trusted))) + return r; + + if ((r = priv_getdns_dnssec_zone_from_list( + support_records, &support))) + goto done_free_trusted; + + if ((r = priv_getdns_dnssec_zone_from_list( + records_to_validate, &to_validate))) + goto done_free_support; + + if (! (support_keys = ldns_rr_list_new())) { + r = GETDNS_RETURN_MEMORY_ERROR; + goto done_free_to_validate; + } + /* Create a rr_list of all the keys in the support records */ + for (rrset_iter_init_zone(&i, support); + (rrset = rrset_iter_value(&i)); rrset_iter_next(&i)) + + if (ldns_dnssec_rrsets_type(rrset) == LDNS_RR_TYPE_DS || + ldns_dnssec_rrsets_type(rrset) == LDNS_RR_TYPE_DNSKEY) + + for (rrs = rrset->rrs; rrs; rrs = rrs->next) + (void) ldns_rr_list_push_rr( + support_keys, rrs->rr); + + /* Now walk through the rrsets to validate */ + for (rrset_iter_init_zone(&i, to_validate); + (rrset = rrset_iter_value(&i)); rrset_iter_next(&i)) { + + s |= chase(rrset, support, support_keys, trusted); + if (s != 0) + break; + } + if (s == LDNS_STATUS_CRYPTO_BOGUS) + r = GETDNS_DNSSEC_BOGUS; + else if (s != LDNS_STATUS_OK) + r = GETDNS_DNSSEC_INSECURE; + else + r = GETDNS_DNSSEC_SECURE; + + ldns_rr_list_free(support_keys); +done_free_to_validate: + ldns_dnssec_zone_deep_free(to_validate); +done_free_support: + ldns_dnssec_zone_deep_free(support); +done_free_trusted: + ldns_rr_list_deep_free(trusted); + return r; +} /* getdns_validate_dnssec */ + +int +priv_getdns_parse_ta_file(time_t *ta_mtime, ldns_rr_list *ta_rrs) +{ + uint32_t ttl = 3600; + ldns_rdf* orig = NULL, *prev = NULL; + int line = 1; + ldns_status s; + ldns_rr *rr; + int nkeys; + struct stat st; + FILE *in; + + if (stat(TRUST_ANCHOR_FILE, &st) != 0) + return 0; + + if (ta_mtime) + *ta_mtime = st.st_mtime; + + in = fopen(TRUST_ANCHOR_FILE, "r"); + if (!in) + return 0; + + nkeys = 0; + while (! feof(in)) { + rr = NULL; + s = ldns_rr_new_frm_fp_l(&rr, in, &ttl, &orig, &prev, &line); + if (s == LDNS_STATUS_SYNTAX_EMPTY /* empty line */ + || s == LDNS_STATUS_SYNTAX_TTL /* $TTL */ + || s == LDNS_STATUS_SYNTAX_ORIGIN /* $ORIGIN */) + continue; + + if (s != LDNS_STATUS_OK) { + ldns_rr_free(rr); + nkeys = 0; + break; + } + if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_DS || + ldns_rr_get_type(rr) == LDNS_RR_TYPE_DNSKEY) { + + nkeys++; + if (ta_rrs) { + ldns_rr_list_push_rr(ta_rrs, rr); + continue; + } + } + ldns_rr_free(rr); + } + ldns_rdf_deep_free(orig); + ldns_rdf_deep_free(prev); + fclose(in); + return nkeys; +} + +getdns_list * +getdns_root_trust_anchor(time_t *utc_date_of_anchor) +{ + getdns_list *tas_gd_list = NULL; + ldns_rr_list *tas_rr_list = ldns_rr_list_new(); + + if (! tas_rr_list) + return NULL; + + if (! priv_getdns_parse_ta_file(utc_date_of_anchor, tas_rr_list)) { + goto done_free_tas_rr_list; + return NULL; + } + tas_gd_list = create_list_from_rr_list(NULL, tas_rr_list); + +done_free_tas_rr_list: + ldns_rr_list_deep_free(tas_rr_list); + return tas_gd_list; +} + +/* dnssec.c */ diff --git a/src/validation-chain.h b/src/dnssec.h similarity index 93% rename from src/validation-chain.h rename to src/dnssec.h index f38addb3..ee53fc9d 100644 --- a/src/validation-chain.h +++ b/src/dnssec.h @@ -1,6 +1,6 @@ /** * - * /brief priv_getdns_get_validation_chain function + * /brief functions for DNSSEC * * The priv_getdns_get_validation_chain function is called after an answer * has been fetched when the dnssec_return_validation_chain extension is set. @@ -35,8 +35,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef VALIDATION_CHAIN_H_ -#define VALIDATION_CHAIN_H_ +#ifndef DNSSEC_H_ +#define DNSSEC_H_ #include "getdns/getdns.h" #include "types-internal.h" @@ -47,6 +47,8 @@ void priv_getdns_get_validation_chain(getdns_dns_req *dns_req); struct getdns_dict * priv_getdns_get_validation_chain_sync( getdns_dns_req *dns_req); +int priv_getdns_parse_ta_file(time_t *ta_mtime, ldns_rr_list *ta_rrs); + #endif -/* validation-chain.h */ +/* dnssec.h */ diff --git a/src/general.c b/src/general.c index e8d369ec..0ecc049b 100644 --- a/src/general.c +++ b/src/general.c @@ -42,7 +42,7 @@ #include "context.h" #include "types-internal.h" #include "util-internal.h" -#include "validation-chain.h" +#include "dnssec.h" #include /* stuff to make it compile pedantically */ diff --git a/src/getdns/getdns.h b/src/getdns/getdns.h index e3224c15..dccf7223 100644 --- a/src/getdns/getdns.h +++ b/src/getdns/getdns.h @@ -38,6 +38,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -113,7 +114,7 @@ typedef enum getdns_namespace_t { } getdns_namespace_t; /** - * \defgroup Namespace types texts + * \defgroup Namespace types texts * @{ */ #define GETDNS_NAMESPACE_DNS_TEXT "See getdns_context_set_namespaces()" @@ -132,7 +133,7 @@ typedef enum getdns_resolution_t { } getdns_resolution_t; /** - * \defgroup Resolution types texts + * \defgroup Resolution types texts * @{ */ #define GETDNS_RESOLUTION_STUB_TEXT "See getdns_context_set_resolution_type()" @@ -148,7 +149,7 @@ typedef enum getdns_redirects_t { } getdns_redirects_t; /** - * \defgroup Redirect policies texts + * \defgroup Redirect policies texts * @{ */ #define GETDNS_REDIRECTS_FOLLOW_TEXT "See getdns_context_set_follow_redirects()" @@ -166,7 +167,7 @@ typedef enum getdns_transport_t { } getdns_transport_t; /** - * \defgroup Transport arrangements texts + * \defgroup Transport arrangements texts * @{ */ #define GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP_TEXT "See getdns_context_set_dns_transport()" @@ -186,7 +187,7 @@ typedef enum getdns_append_name_t { } getdns_append_name_t; /** - * \defgroup Suffix appending methods texts + * \defgroup Suffix appending methods texts * @{ */ #define GETDNS_APPEND_NAME_ALWAYS_TEXT "See getdns_context_set_append_name()" @@ -219,7 +220,7 @@ typedef enum getdns_context_code_t { } getdns_context_code_t; /** - * \defgroup Context codes texts + * \defgroup Context codes texts * @{ */ #define GETDNS_CONTEXT_CODE_NAMESPACES_TEXT "Change related to getdns_context_set_namespaces" @@ -252,7 +253,7 @@ typedef enum getdns_callback_type_t { } getdns_callback_type_t; /** - * \defgroup Callback type variables texts + * \defgroup Callback type variables texts * @{ */ #define GETDNS_CALLBACK_COMPLETE_TEXT "The response has the requested data in it" @@ -1004,15 +1005,23 @@ getdns_context_set_extended_memory_functions(getdns_context *context, void (*free) (void *userarg, void *ptr) ); +/* api information support */ +getdns_dict* +getdns_context_get_api_information(getdns_context* context); + /* Async support */ struct timeval; int getdns_context_get_num_pending_requests(getdns_context* context, struct timeval* next_timeout); /* get the fd */ int getdns_context_fd(getdns_context* context); + /* process async reqs */ getdns_return_t getdns_context_process_async(getdns_context* context); +/* Get root trust anchor */ +getdns_list *getdns_root_trust_anchor(time_t *utc_date_of_anchor); + #ifdef __cplusplus } #endif diff --git a/src/rr-dict.c b/src/rr-dict.c index 0cc8d912..79dbdce9 100644 --- a/src/rr-dict.c +++ b/src/rr-dict.c @@ -804,8 +804,9 @@ priv_getdns_equip_dict_with_spf_rdfs(struct getdns_dict* rdata, ldns_rr* rr, /* validations failed */ return r; } - bindata.data = GETDNS_XMALLOC(context->my_mf, uint8_t, - bindata.size); + bindata.data = context + ? GETDNS_XMALLOC(context->my_mf, uint8_t, bindata.size) + : malloc(bindata.size); if (!bindata.data) { return GETDNS_RETURN_MEMORY_ERROR; } @@ -820,7 +821,10 @@ priv_getdns_equip_dict_with_spf_rdfs(struct getdns_dict* rdata, ldns_rr* rr, } bindata.data[num_copied] = 0; r = getdns_dict_set_bindata(rdata, def->rdata[0].name, &bindata); - GETDNS_FREE(context->my_mf, bindata.data); + if (context) + GETDNS_FREE(context->my_mf, bindata.data); + else + free(bindata.data); return r; } @@ -894,7 +898,6 @@ priv_getdns_create_dict_from_rdfs( uint8_t *data_ptr; size_t i; - assert(context); assert(rr); assert(rdata); @@ -907,8 +910,9 @@ priv_getdns_create_dict_from_rdfs( rdata_raw.size = 0; for (i = 0; i < ldns_rr_rd_count(rr); i++) rdata_raw.size += ldns_rdf_size(ldns_rr_rdf(rr, i)); - rdata_raw.data = GETDNS_XMALLOC( - context->mf, uint8_t, rdata_raw.size); + rdata_raw.data = context + ? GETDNS_XMALLOC(context->mf, uint8_t, rdata_raw.size) + : malloc(rdata_raw.size); if (! rdata_raw.data) { r = GETDNS_RETURN_MEMORY_ERROR; break; @@ -924,7 +928,10 @@ priv_getdns_create_dict_from_rdfs( /* Set "rdata_raw" attribute" */ r = getdns_dict_set_bindata(*rdata, "rdata_raw", &rdata_raw); - GETDNS_FREE(context->mf, rdata_raw.data); + if (context) + GETDNS_FREE(context->mf, rdata_raw.data); + else + free(rdata_raw.data); if (r != GETDNS_RETURN_GOOD) break; @@ -945,7 +952,6 @@ priv_getdns_create_dict_from_rr( struct getdns_bindata name; struct getdns_dict *rdata; - assert(context); assert(rr); assert(rr_dict); @@ -995,7 +1001,6 @@ priv_getdns_create_reply_question_dict( ldns_rr *rr; struct getdns_bindata qname; - assert(context); assert(pkt); assert(q_dict); diff --git a/src/sync.c b/src/sync.c index 7a1d260b..a6dea66f 100644 --- a/src/sync.c +++ b/src/sync.c @@ -41,7 +41,7 @@ #include "types-internal.h" #include "util-internal.h" #include -#include "validation-chain.h" +#include "dnssec.h" /* stuff to make it compile pedantically */ #define UNUSED_PARAM(x) ((void)(x)) diff --git a/src/test/tests_dnssec.c b/src/test/tests_dnssec.c index 17aa0082..d3fa0734 100644 --- a/src/test/tests_dnssec.c +++ b/src/test/tests_dnssec.c @@ -46,87 +46,6 @@ #include #include -getdns_return_t create_root_trustanchor_list(struct getdns_list **tas) -{ - static const struct getdns_bindata root_dname = { 1, (uint8_t *) "" }; - static const int root_key_tag = 19036; - static const int root_algorithm = 8; - static const int root_digest_type = 2; - static const struct getdns_bindata root_digest = { 32, (uint8_t *) - "\x49\xaa\xc1\x1d\x7b\x6f\x64\x46\x70\x2e\x54\xa1\x60\x73\x71\x60" - "\x7a\x1a\x41\x85\x52\x00\xfd\x2c\xe1\xcd\xde\x32\xf2\x4e\x8f\xb5" - }; - - getdns_return_t r = GETDNS_RETURN_GOOD; - struct getdns_dict *ta; - struct getdns_dict *rdata; - - if (! tas) - return GETDNS_RETURN_INVALID_PARAMETER; - - ta = getdns_dict_create(); - if (! ta) - return GETDNS_RETURN_MEMORY_ERROR; - do { - r = getdns_dict_set_bindata(ta, "name", - (struct getdns_bindata *)&root_dname); - if (r != GETDNS_RETURN_GOOD) - break; - - r = getdns_dict_set_int(ta, "type", GETDNS_RRTYPE_DS); - if (r != GETDNS_RETURN_GOOD) - break; - - rdata = getdns_dict_create(); - if (! rdata) { - r = GETDNS_RETURN_MEMORY_ERROR; - break; - } - do { - r = getdns_dict_set_int(rdata, - "key_tag", root_key_tag); - if (r != GETDNS_RETURN_GOOD) - break; - - r = getdns_dict_set_int(rdata, - "algorithm", root_algorithm); - if (r != GETDNS_RETURN_GOOD) - break; - - r = getdns_dict_set_int(rdata, - "digest_type", root_digest_type); - if (r != GETDNS_RETURN_GOOD) - break; - - r = getdns_dict_set_bindata(rdata, - "digest", (struct getdns_bindata *)&root_digest); - if (r != GETDNS_RETURN_GOOD) - break; - - r = getdns_dict_set_dict(ta, "rdata", rdata); - } while(0); - - getdns_dict_destroy(rdata); - if (r != GETDNS_RETURN_GOOD) - break; - - *tas = getdns_list_create(); - if (! *tas) { - r = GETDNS_RETURN_MEMORY_ERROR; - break; - } - r = getdns_list_set_dict(*tas, 0, ta); - if (r == GETDNS_RETURN_GOOD) { - getdns_dict_destroy(ta); - return r; - } - - getdns_list_destroy(*tas); - } while(0); - getdns_dict_destroy(ta); - return r; -} - /* Set up the callback function, which will also do the processing of the results */ void callbackfn(struct getdns_context *context, @@ -177,10 +96,10 @@ callbackfn(struct getdns_context *context, " %d\n", r); break; } - r = create_root_trustanchor_list(&trust_anchors); - if (r != GETDNS_RETURN_GOOD) { + trust_anchors = getdns_root_trust_anchor(NULL); + if (! trust_anchors) { fprintf(stderr, - "Error in creating trust_anchor:" + "No root trust anchor present:" " %d\n", r); break; } diff --git a/src/validate_dnssec.c b/src/validate_dnssec.c deleted file mode 100644 index d81fd84f..00000000 --- a/src/validate_dnssec.c +++ /dev/null @@ -1,328 +0,0 @@ -/** - * - * \file validate_dnssec.c - * @brief dnssec validation functions - * - * Originally taken from the getdns API description pseudo implementation. - * - */ - -/* - * Copyright (c) 2013, Versign, Inc. - * All rights reserved. - * - * 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 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 Verisign, Inc. 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. - */ - -#include -#include -#include "rr-dict.h" - -/* stuff to make it compile pedantically */ -#define UNUSED_PARAM(x) ((void)(x)) - -static getdns_return_t -priv_getdns_rr_list_from_list(struct getdns_list *list, ldns_rr_list **rr_list) -{ - getdns_return_t r; - size_t i, l; - struct getdns_dict *rr_dict; - ldns_rr *rr; - - if ((r = getdns_list_get_length(list, &l))) - return r; - - if (! (*rr_list = ldns_rr_list_new())) - return GETDNS_RETURN_MEMORY_ERROR; - - for (i = 0; i < l; i++) { - if ((r = getdns_list_get_dict(list, i, &rr_dict))) - break; - - if ((r = priv_getdns_create_rr_from_dict(rr_dict, &rr))) - break; - - if (! ldns_rr_list_push_rr(*rr_list, rr)) { - ldns_rr_free(rr); - r = GETDNS_RETURN_GENERIC_ERROR; - break; - } - } - if (r) - ldns_rr_list_deep_free(*rr_list); - return r; -} - -static getdns_return_t -priv_getdns_dnssec_zone_from_list(struct getdns_list *list, - ldns_dnssec_zone **zone) -{ - getdns_return_t r; - size_t i, l; - struct getdns_dict *rr_dict; - ldns_rr *rr; - ldns_status s; - - if ((r = getdns_list_get_length(list, &l))) - return r; - - if (! (*zone = ldns_dnssec_zone_new())) - return GETDNS_RETURN_MEMORY_ERROR; - - for (i = 0; i < l; i++) { - if ((r = getdns_list_get_dict(list, i, &rr_dict))) - break; - - if ((r = priv_getdns_create_rr_from_dict(rr_dict, &rr))) - break; - - if ((s = ldns_dnssec_zone_add_rr(*zone, rr))) { - ldns_rr_free(rr); - r = GETDNS_RETURN_GENERIC_ERROR; - break; - } - } - if (r) - ldns_dnssec_zone_free(*zone); - return r; -} - -typedef struct zone_iter { - ldns_dnssec_zone *zone; - ldns_rbnode_t *cur_node; - ldns_dnssec_rrsets *cur_rrset; -} zone_iter; - -static void -rrset_iter_init_zone(zone_iter *i, ldns_dnssec_zone *zone) -{ - assert(i); - - i->zone = zone; - i->cur_node = zone->names ? ldns_rbtree_first(zone->names) - : LDNS_RBTREE_NULL; - i->cur_rrset = i->cur_node != LDNS_RBTREE_NULL - ? ((ldns_dnssec_name *)i->cur_node->data)->rrsets - : NULL; -} - -static ldns_dnssec_rrsets * -rrset_iter_value(zone_iter *i) -{ - assert(i); - - return i->cur_rrset; -} - -static void -rrset_iter_next(zone_iter *i) -{ - assert(i); - - if (! i->cur_rrset) - return; - - if (! (i->cur_rrset = i->cur_rrset->next)) { - i->cur_node = ldns_rbtree_next(i->cur_node); - i->cur_rrset = i->cur_node != LDNS_RBTREE_NULL - ? ((ldns_dnssec_name *)i->cur_node->data)->rrsets - : NULL; - } -} - -static ldns_rr_list * -rrs2rr_list(ldns_dnssec_rrs *rrs) -{ - ldns_rr_list *r = ldns_rr_list_new(); - if (r) - while (rrs) { - (void) ldns_rr_list_push_rr(r, rrs->rr); - rrs = rrs->next; - } - return r; -} - -static ldns_status -verify_rrset(ldns_dnssec_rrsets *rrset_and_sigs, - const ldns_rr_list *keys, ldns_rr_list *good_keys) -{ - ldns_status s; - ldns_rr_list *rrset = rrs2rr_list(rrset_and_sigs->rrs); - ldns_rr_list *sigs = rrs2rr_list(rrset_and_sigs->signatures); - s = ldns_verify(rrset, sigs, keys, good_keys); - ldns_rr_list_free(sigs); - ldns_rr_list_free(rrset); - return s; -} - -static ldns_status -chase(ldns_dnssec_rrsets *rrset, ldns_dnssec_zone *support, - ldns_rr_list *support_keys, ldns_rr_list *trusted) -{ - ldns_status s; - ldns_rr_list *verifying_keys; - size_t i, j; - ldns_rr *rr; - ldns_dnssec_rrsets *key_rrset; - ldns_dnssec_rrs *rrs; - - /* Secure by trusted keys? */ - s = verify_rrset(rrset, trusted, NULL); - if (s == 0) - return s; - - /* No, chase with support records.. - * Is there a verifying key in the support records? - */ - verifying_keys = ldns_rr_list_new(); - s = verify_rrset(rrset, support_keys, verifying_keys); - if (s != 0) - goto done_free_verifying_keys; - - /* Ok, we have verifying keys from the support records. - * Compare them with the *trusted* keys or DSes, - * or chase them further down the validation chain. - */ - for (i = 0; i < ldns_rr_list_rr_count(verifying_keys); i++) { - /* Lookup the rrset for key rr from the support records */ - rr = ldns_rr_list_rr(verifying_keys, i); - key_rrset = ldns_dnssec_zone_find_rrset( - support, ldns_rr_owner(rr), ldns_rr_get_type(rr)); - if (! key_rrset) { - s = LDNS_STATUS_CRYPTO_NO_DNSKEY; - break; - } - /* When we signed ourselves, we have to cross domain border - * and look for a matching DS signed by a parents key - */ - if (rrset == key_rrset) { - /* Is the verifying key trusted? - * (i.e. DS in trusted) - */ - for (j = 0; j < ldns_rr_list_rr_count(trusted); j++) - if (ldns_rr_compare_ds(ldns_rr_list_rr( - trusted, j), rr)) - break; - /* If so, check for the next verifying key - * (or exit SECURE) - */ - if (j < ldns_rr_list_rr_count(trusted)) - continue; - - /* Search for a matching DS in the support records */ - key_rrset = ldns_dnssec_zone_find_rrset( - support, ldns_rr_owner(rr), LDNS_RR_TYPE_DS); - if (! key_rrset) { - s = LDNS_STATUS_CRYPTO_NO_DNSKEY; - break; - } - /* Now check if DS matches the DNSKEY! */ - for (rrs = key_rrset->rrs; rrs; rrs = rrs->next) - if (ldns_rr_compare_ds(rr, rrs->rr)) - break; - if (! rrs) { - s = LDNS_STATUS_CRYPTO_NO_DNSKEY; - break; - } - } - /* Pursue the chase with the verifying key (or its DS) */ - s = chase(key_rrset, support, support_keys, trusted); - if (s != 0) - break; - } -done_free_verifying_keys: - ldns_rr_list_free(verifying_keys); - return s; -} - -/* - * getdns_validate_dnssec - * - */ -getdns_return_t -getdns_validate_dnssec(struct getdns_list *records_to_validate, - struct getdns_list *support_records, - struct getdns_list *trust_anchors) -{ - getdns_return_t r; - ldns_rr_list *trusted; - ldns_dnssec_zone *support; - ldns_rr_list *support_keys; - ldns_dnssec_zone *to_validate; - zone_iter i; - ldns_dnssec_rrsets *rrset; - ldns_dnssec_rrs *rrs; - ldns_status s = LDNS_STATUS_OK; - - if ((r = priv_getdns_rr_list_from_list(trust_anchors, &trusted))) - return r; - - if ((r = priv_getdns_dnssec_zone_from_list( - support_records, &support))) - goto done_free_trusted; - - if ((r = priv_getdns_dnssec_zone_from_list( - records_to_validate, &to_validate))) - goto done_free_support; - - if (! (support_keys = ldns_rr_list_new())) { - r = GETDNS_RETURN_MEMORY_ERROR; - goto done_free_to_validate; - } - /* Create a rr_list of all the keys in the support records */ - for (rrset_iter_init_zone(&i, support); - (rrset = rrset_iter_value(&i)); rrset_iter_next(&i)) - - if (ldns_dnssec_rrsets_type(rrset) == LDNS_RR_TYPE_DS || - ldns_dnssec_rrsets_type(rrset) == LDNS_RR_TYPE_DNSKEY) - - for (rrs = rrset->rrs; rrs; rrs = rrs->next) - (void) ldns_rr_list_push_rr( - support_keys, rrs->rr); - - /* Now walk through the rrsets to validate */ - for (rrset_iter_init_zone(&i, to_validate); - (rrset = rrset_iter_value(&i)); rrset_iter_next(&i)) { - - s |= chase(rrset, support, support_keys, trusted); - if (s != 0) - break; - } - if (s == LDNS_STATUS_CRYPTO_BOGUS) - r = GETDNS_DNSSEC_BOGUS; - else if (s != LDNS_STATUS_OK) - r = GETDNS_DNSSEC_INSECURE; - else - r = GETDNS_DNSSEC_SECURE; - - ldns_rr_list_free(support_keys); -done_free_to_validate: - ldns_dnssec_zone_deep_free(to_validate); -done_free_support: - ldns_dnssec_zone_deep_free(support); -done_free_trusted: - ldns_rr_list_deep_free(trusted); - return r; -} /* getdns_validate_dnssec */ - -/* validate_dnssec.c */