From 576b81af48161475459c7d7f3dc7b37b8e92d0b6 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Thu, 6 Feb 2014 22:09:00 +0100 Subject: [PATCH 1/3] More solid rr-dict to ldns_rr conversion First steps in validate_dnssec --- src/rr-dict.c | 174 ++++++++++++++++++++++++++++++++++++++-- src/test/tests_dnssec.c | 140 +++++++++++++++++++++++++++----- src/validate_dnssec.c | 62 ++++++++++++-- 3 files changed, 345 insertions(+), 31 deletions(-) diff --git a/src/rr-dict.c b/src/rr-dict.c index d61bec3e..fc06f59f 100644 --- a/src/rr-dict.c +++ b/src/rr-dict.c @@ -37,6 +37,7 @@ #include "rr-dict.h" #include "types-internal.h" #include "context.h" +#include "dict.h" #define ALEN(a) (sizeof(a)/sizeof(a[0])) @@ -869,15 +870,157 @@ priv_getdns_create_reply_question_dict( return r; } +static getdns_return_t priv_getdns_construct_wire_rdata_from_rdata( + struct getdns_dict *rdata, uint32_t rr_type, + uint8_t **wire, size_t *wire_size) +{ + getdns_return_t r = GETDNS_RETURN_GOOD; + const ldns_rr_descriptor *rr_descript; + const struct rr_def *def; + size_t i, size; + struct getdns_bindata *bindata; + uint32_t value; + uint8_t *ptr; + + assert(rdata); + assert(wire); + assert(wire_size); + + def = rr_def_lookup(rr_type); + rr_descript = ldns_rr_descript(rr_type); + + /* First calculate needed size */ + size = 0; + for (i = 0; i < def->n_rdata_fields && r == GETDNS_RETURN_GOOD; i++) { + switch (def->rdata[i].type) { + case t_bindata: r = getdns_dict_get_bindata(rdata, + def->rdata[i].name, &bindata); + if (r) + break; + size += bindata->size; + break; + case t_int : switch (ldns_rr_descriptor_field_type( + rr_descript, i)) { + + case LDNS_RDF_TYPE_CLASS: + case LDNS_RDF_TYPE_ALG : + case LDNS_RDF_TYPE_INT8 : size += 1; + break; + case LDNS_RDF_TYPE_TYPE : + case LDNS_RDF_TYPE_CERT_ALG: + case LDNS_RDF_TYPE_INT16: size += 2; + break; + case LDNS_RDF_TYPE_TIME : + case LDNS_RDF_TYPE_PERIOD: + case LDNS_RDF_TYPE_INT32: size += 4; + break; + default: r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + break; + default : r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + } + *wire_size = size + 2; + *wire = ptr = GETDNS_XMALLOC(rdata->mf, uint8_t, size + 2); + if (! ptr) + return GETDNS_RETURN_MEMORY_ERROR; + + ptr[0] = (uint8_t) (size >> 8) & 0xff; + ptr[1] = (uint8_t) size & 0xff; + ptr += 2; + for (i = 0; i < def->n_rdata_fields && r == GETDNS_RETURN_GOOD; i++) { + switch (def->rdata[i].type) { + case t_bindata: r = getdns_dict_get_bindata(rdata, + def->rdata[i].name, &bindata); + if (r) + break; + (void) memcpy(ptr, bindata->data, + bindata->size); + ptr += bindata->size; + break; + case t_int : r = getdns_dict_get_int(rdata, + def->rdata[i].name, &value); + if (r) + break; + + switch (ldns_rr_descriptor_field_type( + rr_descript, i)) { + + case LDNS_RDF_TYPE_CLASS: + case LDNS_RDF_TYPE_ALG : + case LDNS_RDF_TYPE_INT8 : ptr[0] = (uint8_t) + value & 0xff; + ptr += 1; + break; + case LDNS_RDF_TYPE_TYPE : + case LDNS_RDF_TYPE_CERT_ALG: + case LDNS_RDF_TYPE_INT16: ptr[0] = (uint8_t) + (value>>8)&0xff; + ptr[1] = (uint8_t) + value & 0xff; + ptr += 2; + break; + case LDNS_RDF_TYPE_TIME : + case LDNS_RDF_TYPE_PERIOD: + case LDNS_RDF_TYPE_INT32: ptr[0] = (uint8_t) + (value>>24)&0xff; + ptr[1] = (uint8_t) + (value>>16)&0xff; + ptr[2] = (uint8_t) + (value>>8)&0xff; + ptr[3] = (uint8_t) + value & 0xff; + ptr += 4; + break; + default: r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + break; + default : r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + } + if (r) + GETDNS_FREE(rdata->mf, ptr); + return r; +} + +static getdns_return_t +priv_getdns_dict_get_raw_rdata(struct getdns_dict *rdata, + uint8_t **wire, size_t *wire_size) +{ + getdns_return_t r; + struct getdns_bindata *bindata; + + if ((r = getdns_dict_get_bindata(rdata, "rdata_raw", &bindata))) + return r; + + *wire_size = bindata->size + 2; + *wire = GETDNS_XMALLOC(rdata->mf, uint8_t, *wire_size); + if (! *wire) + return GETDNS_RETURN_MEMORY_ERROR; + + (*wire)[0] = (uint8_t) (bindata->size >> 8) & 0xff; + (*wire)[1] = (uint8_t) bindata->size & 0xff; + + (void) memcpy(*wire + 2, bindata->data, bindata->size); + return GETDNS_RETURN_GOOD; +} + getdns_return_t priv_getdns_create_rr_from_dict(struct getdns_dict *rr_dict, ldns_rr **rr) { getdns_return_t r = GETDNS_RETURN_GOOD; - struct getdns_bindata *name, *rdata_raw; + struct getdns_bindata *name; struct getdns_dict *rdata; uint32_t rr_type; ldns_rdf *owner; ldns_status s; + size_t pos; + uint8_t *wire; + size_t wire_size; assert(rr_dict); assert(rr); @@ -906,14 +1049,35 @@ priv_getdns_create_rr_from_dict(struct getdns_dict *rr_dict, ldns_rr **rr) if (r != GETDNS_RETURN_GOOD) break; - r = getdns_dict_get_bindata(rdata, "rdata_raw", &rdata_raw); + //r = getdns_dict_get_bindata(rdata, "rdata_raw", &rdata_raw); + r = priv_getdns_dict_get_raw_rdata(rdata, &wire, &wire_size); + if (r == GETDNS_RETURN_NO_SUCH_DICT_NAME) { + r = priv_getdns_construct_wire_rdata_from_rdata( + rdata, rr_type, &wire, &wire_size); + } if (r != GETDNS_RETURN_GOOD) break; - - s = ldns_wire2rdf(*rr, rdata_raw->data, rdata_raw->size, 0); +#if 0 + fprintf(stderr, "wire_size: %d\n", (int)wire_size); + fprintf(stderr, "wire data: "); + for (size_t i = 0; i < wire_size; i++) { + if (i) { + if (i % 24 == 0) + fprintf(stderr, "\n "); + else if (i % 4 == 0) + fprintf(stderr, " "); + } + fprintf(stderr, "%.2x", wire[i]); + } + fprintf(stderr, "\n"); +#endif + pos = 0; + s = ldns_wire2rdf(*rr, wire, wire_size, &pos); + GETDNS_FREE(rr_dict->mf, wire); if (s == LDNS_STATUS_OK) return r; - + fprintf( stderr, "ldns error: %s (pos: %d)\n" + , ldns_get_errorstr_by_id(s), (int)pos); r = GETDNS_RETURN_GENERIC_ERROR; } while (0); ldns_rr_free(*rr); diff --git a/src/test/tests_dnssec.c b/src/test/tests_dnssec.c index f0fe9aae..38edf3c2 100644 --- a/src/test/tests_dnssec.c +++ b/src/test/tests_dnssec.c @@ -46,28 +46,131 @@ #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) + 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 -this_callbackfn(struct getdns_context *this_context, - uint16_t this_callback_type, - struct getdns_dict *this_response, - void *this_userarg, getdns_transaction_t this_transaction_id) +this_callbackfn(struct getdns_context *context, uint16_t callback_type, + struct getdns_dict *response, void *userarg, + getdns_transaction_t transaction_id) { - if (this_callback_type == GETDNS_CALLBACK_COMPLETE) { /* This is a callback with data */ - char *res = getdns_pretty_print_dict(this_response); - fprintf(stdout, "%s\n", res); - free(res); + struct getdns_list *validation_chain; + struct getdns_list *trust_anchors; + getdns_return_t r; - } else if (this_callback_type == GETDNS_CALLBACK_CANCEL) - fprintf(stderr, - "The callback with ID %llu was cancelled. Exiting.", - (unsigned long long)this_transaction_id); - else - fprintf(stderr, - "The callback got a callback_type of %d. Exiting.", - this_callback_type); - getdns_dict_destroy(this_response); - (void) event_base_loopexit((struct event_base *)this_userarg, NULL); + do { + if (callback_type == GETDNS_CALLBACK_CANCEL) { + fprintf(stderr, + "The callback with ID %llu was cancelled.\n", + (long long unsigned int)transaction_id); + break; + } else if (callback_type != GETDNS_CALLBACK_COMPLETE) { + fprintf(stderr, + "The callback got a callback_type of %d.\n", + callback_type); + break; + } + r = getdns_dict_get_list(response, + "validation_chain", &validation_chain); + if (r != GETDNS_RETURN_GOOD) { + fprintf(stderr, + "Could not get \"validation_chain\" from response:" + " %d\n", r); + break; + } + r = create_root_trustanchor_list(&trust_anchors); + if (r != GETDNS_RETURN_GOOD) { + fprintf(stderr, + "Error in creating trust_anchor:" + " %d\n", r); + break; + } + r = getdns_validate_dnssec(NULL, + validation_chain, trust_anchors); + + printf("getdns_validate_dnssec returned: %d\n", r); + + getdns_list_destroy(trust_anchors); + } while (0); + getdns_dict_destroy(response); + (void) event_base_loopexit((struct event_base *)userarg, NULL); } int @@ -83,7 +186,6 @@ main(int argc, char** argv) return (GETDNS_RETURN_GENERIC_ERROR); } getdns_context_set_timeout(this_context, 5000); - struct getdns_dict * this_extensions = getdns_dict_create(); getdns_return_t this_ret = getdns_dict_set_int(this_extensions, "dnssec_return_validation_chain", GETDNS_EXTENSION_TRUE); diff --git a/src/validate_dnssec.c b/src/validate_dnssec.c index 716aaab1..5358c506 100644 --- a/src/validate_dnssec.c +++ b/src/validate_dnssec.c @@ -35,23 +35,71 @@ */ #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; +} + /* * getdns_validate_dnssec * */ getdns_return_t -getdns_validate_dnssec(struct getdns_bindata * record_to_validate, - struct getdns_list * bundle_of_support_records, - struct getdns_list * trust_anchor_rdatas) +getdns_validate_dnssec(struct getdns_bindata *to_validate, + struct getdns_list *support_records, + struct getdns_list *trust_anchors) { - UNUSED_PARAM(record_to_validate); - UNUSED_PARAM(bundle_of_support_records); - UNUSED_PARAM(trust_anchor_rdatas); - return GETDNS_RETURN_GOOD; + getdns_return_t r; + ldns_rr_list *tas; + ldns_rr_list *chain; + + if ((r = priv_getdns_rr_list_from_list(trust_anchors, &tas))) + return r; + + printf(";; trust anchors:\n"); + ldns_rr_list_print(stdout, tas); + + if ((r = priv_getdns_rr_list_from_list(support_records, &chain))) + return r; + + printf(";; support records:\n"); + ldns_rr_list_print(stdout, chain); + + + return r; } /* getdns_validate_dnssec */ /* validate_dnssec.c */ From 649814f0e37a5bbc4f30f80e636fa38762dfa8df Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 7 Feb 2014 15:00:59 +0100 Subject: [PATCH 2/3] validate_dnssec does some chasing But not completely ... --- src/getdns/getdns.h | 6 +- src/test/tests_dnssec.c | 43 +++++++- src/validate_dnssec.c | 225 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 256 insertions(+), 18 deletions(-) diff --git a/src/getdns/getdns.h b/src/getdns/getdns.h index 3cdc1bec..68cc0695 100644 --- a/src/getdns/getdns.h +++ b/src/getdns/getdns.h @@ -779,9 +779,9 @@ char *getdns_convert_ulabel_to_alabel(const char *ulabel); char *getdns_convert_alabel_to_ulabel(const char *alabel); getdns_return_t -getdns_validate_dnssec(struct getdns_bindata *record_to_validate, - struct getdns_list *bundle_of_support_records, - struct getdns_list *trust_anchor_rdatas); +getdns_validate_dnssec(struct getdns_list *to_validate, + struct getdns_list *support_records, + struct getdns_list *trust_anchors); /** * creates a string that describes the dictionary in a human readable form diff --git a/src/test/tests_dnssec.c b/src/test/tests_dnssec.c index 38edf3c2..a58962aa 100644 --- a/src/test/tests_dnssec.c +++ b/src/test/tests_dnssec.c @@ -133,6 +133,10 @@ this_callbackfn(struct getdns_context *context, uint16_t callback_type, { struct getdns_list *validation_chain; struct getdns_list *trust_anchors; + struct getdns_list *replies_tree; + size_t replies_tree_length, i; + struct getdns_dict *reply; + struct getdns_list *answer; getdns_return_t r; do { @@ -155,6 +159,20 @@ this_callbackfn(struct getdns_context *context, uint16_t callback_type, " %d\n", r); break; } + r = getdns_dict_get_list(response, "replies_tree", &replies_tree); + if (r != GETDNS_RETURN_GOOD) { + fprintf(stderr, + "Could not get \"replies_tree\" from response:" + " %d\n", r); + break; + } + r = getdns_list_get_length(replies_tree, &replies_tree_length); + if (r != GETDNS_RETURN_GOOD) { + fprintf(stderr, + "Could not get length of the replies_tree:" + " %d\n", r); + break; + } r = create_root_trustanchor_list(&trust_anchors); if (r != GETDNS_RETURN_GOOD) { fprintf(stderr, @@ -162,11 +180,26 @@ this_callbackfn(struct getdns_context *context, uint16_t callback_type, " %d\n", r); break; } - r = getdns_validate_dnssec(NULL, - validation_chain, trust_anchors); - - printf("getdns_validate_dnssec returned: %d\n", r); - + for (i = 0; i < replies_tree_length; i++) { + r = getdns_list_get_dict(replies_tree, i, &reply); + if (r != GETDNS_RETURN_GOOD) { + fprintf(stderr, + "Could not get \"reply\" from replies_tree:" + " %d\n", r); + break; + } + r = getdns_dict_get_list(reply, "answer", &answer); + if (r != GETDNS_RETURN_GOOD) { + fprintf(stderr, + "Could not get \"answer\" from reply:" + " %d\n", r); + break; + } + r = getdns_validate_dnssec(answer, + validation_chain, trust_anchors); + printf("getdns_validate_dnssec returned: %d\n", r); + } + //printf("%s\n", getdns_pretty_print_dict(response)); getdns_list_destroy(trust_anchors); } while (0); getdns_dict_destroy(response); diff --git a/src/validate_dnssec.c b/src/validate_dnssec.c index 5358c506..b5dfb604 100644 --- a/src/validate_dnssec.c +++ b/src/validate_dnssec.c @@ -73,32 +73,237 @@ priv_getdns_rr_list_from_list(struct getdns_list *list, ldns_rr_list **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 = ldns_rbtree_first(zone->names); + 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; + ldns_rr *rr; + ldns_dnssec_rrsets *key_rrset; + + printf(";; RRSET to validate:\n"); + ldns_dnssec_rrsets_print(stdout, rrset, 0); + + printf(";;\n;; Validating with trust anchors:\n"); + verifying_keys = ldns_rr_list_new(); + s = verify_rrset(rrset, trusted, verifying_keys); + printf(";; status: %s\n", ldns_get_errorstr_by_id(s)); + if (ldns_rr_list_rr_count(verifying_keys)) { ldns_rr_list_print(stdout, verifying_keys); printf(";;\n"); } + ldns_rr_list_free(verifying_keys); + if (s == 0) + return s; + + printf(";; Validating with support keys:\n"); + verifying_keys = ldns_rr_list_new(); + s = verify_rrset(rrset, support_keys, verifying_keys); + printf(";; status: %s\n", ldns_get_errorstr_by_id(s)); + if (ldns_rr_list_rr_count(verifying_keys)) { ldns_rr_list_print(stdout, verifying_keys); printf(";;\n"); } + if (s != 0) + goto done_free_verifying_keys; + + printf(";; Looking up the verifying keys:\n"); + for (i = 0; i < ldns_rr_list_rr_count(verifying_keys); i++) { + 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) { + printf(";; Key not found:\n;;\n"); + s = LDNS_STATUS_CRYPTO_NO_DNSKEY; + break; + } + if (rrset == key_rrset) { + printf(";; Key verifies itself, lookup DS:\n"); + key_rrset = ldns_dnssec_zone_find_rrset( + support, ldns_rr_owner(rr), LDNS_RR_TYPE_DS); + if (! key_rrset) { + printf(";; DS not found:\n;;\n"); + s = LDNS_STATUS_CRYPTO_NO_DNSKEY; + break; + } + /* Now check if DS matches the DNSKEY! */ + } + 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_bindata *to_validate, +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 *tas; - ldns_rr_list *chain; + 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; - if ((r = priv_getdns_rr_list_from_list(trust_anchors, &tas))) + if ((r = priv_getdns_rr_list_from_list(trust_anchors, &trusted))) return r; - printf(";; trust anchors:\n"); - ldns_rr_list_print(stdout, tas); + if ((r = priv_getdns_dnssec_zone_from_list( + support_records, &support))) + goto done_free_trusted; - if ((r = priv_getdns_rr_list_from_list(support_records, &chain))) - return r; + if ((r = priv_getdns_dnssec_zone_from_list( + records_to_validate, &to_validate))) + goto done_free_support; - printf(";; support records:\n"); - ldns_rr_list_print(stdout, chain); + 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; + } + + /* + for(zone_iter_init(&i, to_validate); + (rrset = zone_iter_rrset(&i)); zone_iter_next(&i)) { + + ldns_dnssec_rrsets_print(stdout, rrset, 0); + } + */ + + ldns_rr_list_free(support_keys); +done_free_to_validate: + ldns_dnssec_zone_free(to_validate); +done_free_support: + ldns_dnssec_zone_free(support); +done_free_trusted: + ldns_rr_list_deep_free(trusted); return r; } /* getdns_validate_dnssec */ From e6da267b21d31cbc20cd654e5bcfcd14b729ce81 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 7 Feb 2014 23:02:10 +0100 Subject: [PATCH 3/3] Match DS with DNSKEY and return validation status --- src/test/tests_dnssec.c | 13 +++++++- src/validate_dnssec.c | 73 ++++++++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/test/tests_dnssec.c b/src/test/tests_dnssec.c index a58962aa..1ecbad7c 100644 --- a/src/test/tests_dnssec.c +++ b/src/test/tests_dnssec.c @@ -137,6 +137,7 @@ this_callbackfn(struct getdns_context *context, uint16_t callback_type, size_t replies_tree_length, i; struct getdns_dict *reply; struct getdns_list *answer; + size_t answer_length; getdns_return_t r; do { @@ -195,13 +196,23 @@ this_callbackfn(struct getdns_context *context, uint16_t callback_type, " %d\n", r); break; } + r = getdns_list_get_length(answer, &answer_length); + if (r != GETDNS_RETURN_GOOD) { + fprintf(stderr, + "Could not get length of answer list:" + " %d\n", r); + break; + } + if (answer_length == 0) + continue; + r = getdns_validate_dnssec(answer, validation_chain, trust_anchors); printf("getdns_validate_dnssec returned: %d\n", r); } - //printf("%s\n", getdns_pretty_print_dict(response)); getdns_list_destroy(trust_anchors); } while (0); + //printf("%s\n", getdns_pretty_print_dict(response)); getdns_dict_destroy(response); (void) event_base_loopexit((struct event_base *)userarg, NULL); } diff --git a/src/validate_dnssec.c b/src/validate_dnssec.c index b5dfb604..58b939e7 100644 --- a/src/validate_dnssec.c +++ b/src/validate_dnssec.c @@ -119,7 +119,8 @@ rrset_iter_init_zone(zone_iter *i, ldns_dnssec_zone *zone) assert(i); i->zone = zone; - i->cur_node = ldns_rbtree_first(zone->names); + 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; @@ -180,56 +181,76 @@ chase(ldns_dnssec_rrsets *rrset, ldns_dnssec_zone *support, { ldns_status s; ldns_rr_list *verifying_keys; - size_t i; + size_t i, j; ldns_rr *rr; ldns_dnssec_rrsets *key_rrset; + ldns_dnssec_rrs *rrs; - printf(";; RRSET to validate:\n"); - ldns_dnssec_rrsets_print(stdout, rrset, 0); - - printf(";;\n;; Validating with trust anchors:\n"); + /* Secure by trusted keys? */ verifying_keys = ldns_rr_list_new(); s = verify_rrset(rrset, trusted, verifying_keys); - printf(";; status: %s\n", ldns_get_errorstr_by_id(s)); - if (ldns_rr_list_rr_count(verifying_keys)) { ldns_rr_list_print(stdout, verifying_keys); printf(";;\n"); } - ldns_rr_list_free(verifying_keys); if (s == 0) - return s; + goto done_free_verifying_keys; - printf(";; Validating with support keys:\n"); + /* 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); - printf(";; status: %s\n", ldns_get_errorstr_by_id(s)); - if (ldns_rr_list_rr_count(verifying_keys)) { ldns_rr_list_print(stdout, verifying_keys); printf(";;\n"); } if (s != 0) goto done_free_verifying_keys; - printf(";; Looking up the verifying keys:\n"); + /* 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) { - printf(";; Key not found:\n;;\n"); 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) { - printf(";; Key verifies itself, lookup DS:\n"); + /* 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) { - printf(";; DS not found:\n;;\n"); 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; @@ -252,7 +273,7 @@ getdns_validate_dnssec(struct getdns_list *records_to_validate, zone_iter i; ldns_dnssec_rrsets *rrset; ldns_dnssec_rrs *rrs; - ldns_status s; + ldns_status s = LDNS_STATUS_OK; if ((r = priv_getdns_rr_list_from_list(trust_anchors, &trusted))) return r; @@ -288,14 +309,12 @@ getdns_validate_dnssec(struct getdns_list *records_to_validate, if (s != 0) break; } - - /* - for(zone_iter_init(&i, to_validate); - (rrset = zone_iter_rrset(&i)); zone_iter_next(&i)) { - - ldns_dnssec_rrsets_print(stdout, rrset, 0); - } - */ + 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: