From 96b9f095a7988a3822b7424360145a590a4023f3 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 19 Feb 2014 16:56:00 +0100 Subject: [PATCH] Implement getdns_root_trust_anchor --- src/Makefile.in | 5 +- src/context.c | 61 +---- src/{validation-chain.c => dnssec.c} | 369 ++++++++++++++++++++++++++- src/{validation-chain.h => dnssec.h} | 10 +- src/general.c | 2 +- src/getdns/getdns.h | 3 + src/rr-dict.c | 23 +- src/sync.c | 2 +- src/test/tests_dnssec.c | 87 +------ src/validate_dnssec.c | 328 ------------------------ 10 files changed, 404 insertions(+), 486 deletions(-) rename src/{validation-chain.c => dnssec.c} (52%) rename src/{validation-chain.h => dnssec.h} (93%) delete mode 100644 src/validate_dnssec.c 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..0cf17f0b 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; @@ -366,58 +367,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 * @@ -504,8 +453,12 @@ getdns_context_create_with_extended_memory_functions( 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); + /* Set default trust anchor */ + result->has_ta = priv_getdns_parse_ta_file(NULL, NULL); + if (result->has_ta) { + (void) ub_ctx_add_ta_file( + result->unbound_ctx, TRUST_ANCHOR_FILE); + } return GETDNS_RETURN_GOOD; } /* getdns_context_create_with_extended_memory_functions */ 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..b63cee1a 100644 --- a/src/getdns/getdns.h +++ b/src/getdns/getdns.h @@ -1013,6 +1013,9 @@ 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 */