From f383c9fcc75d9b4d0e0ffb6ca2405d0b64018c32 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Sat, 16 Jul 2016 12:55:31 +0200 Subject: [PATCH] getdns_validate_dnssec3 that returns the DNSSEC... ... authentication chain canonical form and order. --- src/dnssec.c | 189 ++++++++++++++++++++--------------- src/getdns/getdns.h.in | 6 +- src/getdns/getdns_extra.h.in | 44 +++++++- src/util-internal.c | 6 +- src/util-internal.h | 2 +- 5 files changed, 156 insertions(+), 91 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index f567b96b..2622701d 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -2881,7 +2881,7 @@ static void append_rrset2val_chain_list( GETDNS_FREE(val_chain_list->mf, val_rrset); } -static void append_rrs2val_chain_list(getdns_context *ctxt, +static void append_rrs2val_chain_list( getdns_list *val_chain_list, getdns_network_req *netreq, int signer) { _getdns_rrset_iter *i, i_spc; @@ -2921,7 +2921,7 @@ static void append_rrs2val_chain_list(getdns_context *ctxt, ; rr; rr = _getdns_rrtype_iter_next(rr)) { if (!(rr_dict = _getdns_rr_iter2rr_dict( - &ctxt->mf, &rr->rr_i))) + &val_chain_list->mf, &rr->rr_i))) continue; if (_getdns_list_append_this_dict(val_chain_list, rr_dict)) @@ -2931,7 +2931,7 @@ static void append_rrs2val_chain_list(getdns_context *ctxt, ; rrsig; rrsig = _getdns_rrsig_iter_next(rrsig)) { if (!(rr_dict = _getdns_rr_iter2rr_dict( - &ctxt->mf, &rrsig->rr_i))) + &val_chain_list->mf, &rrsig->rr_i))) continue; if (_getdns_list_append_this_dict(val_chain_list, rr_dict)) @@ -2941,13 +2941,14 @@ static void append_rrs2val_chain_list(getdns_context *ctxt, } static void append_empty_ds2val_chain_list( - getdns_context *context, getdns_list *val_chain_list, _getdns_rrset *ds) + getdns_list *val_chain_list, _getdns_rrset *ds) { getdns_dict *rr_dict; getdns_bindata bindata; getdns_dict *rdata_dict; - if (!(rr_dict = getdns_dict_create_with_context(context))) + if (!(rr_dict = _getdns_dict_create_with_mf( + &val_chain_list->mf))) return; bindata.size = _dname_len(ds->name); @@ -2957,7 +2958,8 @@ static void append_empty_ds2val_chain_list( (void) getdns_dict_set_int(rr_dict, "type", ds->rr_type); (void) getdns_dict_set_int(rr_dict, "ttl", 0); - if (!(rdata_dict = getdns_dict_create_with_context(context))) { + if (!(rdata_dict = _getdns_dict_create_with_mf( + &val_chain_list->mf))) { getdns_dict_destroy(rr_dict); return; } @@ -2970,13 +2972,79 @@ static void append_empty_ds2val_chain_list( getdns_dict_destroy(rr_dict); } +static void _cleanup_chain(chain_head *chain, + getdns_list *val_chain_list, int full) +{ + chain_head *head, *next, *same_chain; + chain_node *node; + size_t node_count; + + if (!val_chain_list) { + for ( head = chain; head ; head = next ) { + next = head->next; + GETDNS_FREE(head->my_mf, head); + } + return; + } + /* Walk chain to add values to val_chain_list and to cleanup */ + for ( head = chain; head ; head = next ) { + next = head->next; + if (full && head->node_count && head->signer > 0) { + + append_rrset2val_chain_list( + val_chain_list, &head->rrset, head->signer); + + for ( same_chain = next + ; same_chain && same_chain->signer == head->signer + ; same_chain = same_chain->next) { + append_rrset2val_chain_list(val_chain_list, + &same_chain->rrset, same_chain->signer); + same_chain->signer = -1; + } + } + for ( node_count = head->node_count, node = head->parent + ; node_count + ; node_count--, node = node->parent ) { + + if (node->dnskey_req) { + if (val_chain_list) + append_rrs2val_chain_list( + val_chain_list, + node->dnskey_req, + node->dnskey_signer); + _getdns_dns_req_free(node->dnskey_req->owner); + } + if (node->ds_req) { + if (val_chain_list) + append_rrs2val_chain_list( + val_chain_list, + node->ds_req, node->ds_signer); + + if (val_chain_list && node->ds_signer == -1 && + !_getdns_rrset_has_rrs(&node->ds)) { + /* Add empty DS, to prevent less + * specific to be able to authenticate + * below a zone cut (closer to head) + */ + append_empty_ds2val_chain_list( + val_chain_list, + &node->ds); + } + _getdns_dns_req_free(node->ds_req->owner); + } + if (node->soa_req) { + _getdns_dns_req_free(node->soa_req->owner); + } + } + GETDNS_FREE(head->my_mf, head); + } +} + static void check_chain_complete(chain_head *chain) { getdns_dns_req *dnsreq; getdns_context *context; - size_t o, node_count; - chain_head *head, *next, *same_chain; - chain_node *node; + size_t o; getdns_list *val_chain_list; getdns_dict *response_dict; _getdns_rrset_iter tas_iter; @@ -3038,59 +3106,8 @@ static void check_chain_complete(chain_head *chain) val_chain_list = dnsreq->dnssec_return_validation_chain ? getdns_list_create_with_context(context) : NULL; - /* Walk chain to add values to val_chain_list and to cleanup */ - for ( head = chain; head ; head = next ) { - next = head->next; - if (dnsreq->dnssec_return_full_validation_chain && - head->node_count && head->signer > 0) { - - append_rrset2val_chain_list( - val_chain_list, &head->rrset, head->signer); - - for ( same_chain = next - ; same_chain && same_chain->signer == head->signer - ; same_chain = same_chain->next) { - append_rrset2val_chain_list(val_chain_list, - &same_chain->rrset, same_chain->signer); - same_chain->signer = -1; - } - } - for ( node_count = head->node_count, node = head->parent - ; node_count - ; node_count--, node = node->parent ) { - - if (node->dnskey_req) { - if (val_chain_list) - append_rrs2val_chain_list( - context, val_chain_list, - node->dnskey_req, - node->dnskey_signer); - _getdns_dns_req_free(node->dnskey_req->owner); - } - if (node->ds_req) { - if (val_chain_list) - append_rrs2val_chain_list( - context, val_chain_list, - node->ds_req, node->ds_signer); - - if (val_chain_list && node->ds_signer == -1 && - !_getdns_rrset_has_rrs(&node->ds)) { - /* Add empty DS, to prevent less - * specific to be able to authenticate - * below a zone cut (closer to head) - */ - append_empty_ds2val_chain_list( - context, val_chain_list, - &node->ds); - } - _getdns_dns_req_free(node->ds_req->owner); - } - if (node->soa_req) { - _getdns_dns_req_free(node->soa_req->owner); - } - } - GETDNS_FREE(head->my_mf, head); - } + _cleanup_chain(chain, val_chain_list, + dnsreq->dnssec_return_full_validation_chain); response_dict = _getdns_create_getdns_response(dnsreq); if (val_chain_list) { @@ -3156,9 +3173,10 @@ void _getdns_get_validation_chain(getdns_dns_req *dnsreq) static int wire_validate_dnssec(struct mem_funcs *mf, time_t now, uint32_t skew, uint8_t *to_val, size_t to_val_len, - uint8_t *support, size_t support_len, uint8_t *tas, size_t tas_len) + uint8_t *support, size_t support_len, uint8_t *tas, size_t tas_len, + getdns_list *validation_chain) { - chain_head *chain, *head, *next_head; + chain_head *chain, *head; chain_node *node; uint8_t qname_spc[256]; @@ -3223,11 +3241,8 @@ static int wire_validate_dnssec(struct mem_funcs *mf, _getdns_rrset_iter_init( &tas_iter, tas, tas_len, SECTION_ANSWER)); - /* Cleanup the chain */ - for (head = chain; head; head = next_head) { - next_head = head->next; - GETDNS_FREE(*mf, head); - } + _cleanup_chain(chain, validation_chain, 1); + return s; } @@ -3236,10 +3251,11 @@ static int wire_validate_dnssec(struct mem_funcs *mf, * */ getdns_return_t -getdns_validate_dnssec2(getdns_list *records_to_validate, - getdns_list *support_records, - getdns_list *trust_anchors, - time_t now, uint32_t skew) +getdns_validate_dnssec3(const getdns_list *records_to_validate, + const getdns_list *support_records, + const getdns_list *trust_anchors, + time_t now, uint32_t skew, + getdns_list *validation_chain) { uint8_t to_val_buf[4096], *to_val, support_buf[4096], *support, @@ -3261,7 +3277,7 @@ getdns_validate_dnssec2(getdns_list *records_to_validate, if (!records_to_validate || !support_records || !trust_anchors) return GETDNS_RETURN_INVALID_PARAMETER; - mf = &records_to_validate->mf; + mf = (struct mem_funcs *)&records_to_validate->mf; /* First convert everything to wire format */ @@ -3278,7 +3294,8 @@ getdns_validate_dnssec2(getdns_list *records_to_validate, goto exit_free_tas; if ((r = wire_validate_dnssec(mf, now, skew, to_val, to_val_len, - support,support_len, tas,tas_len)) != GETDNS_RETURN_GENERIC_ERROR) + support,support_len, tas, tas_len, validation_chain)) + != GETDNS_RETURN_GENERIC_ERROR) goto exit_free_to_val; for (i = 0; !getdns_list_get_dict(records_to_validate,i,&reply); i++) { @@ -3294,7 +3311,8 @@ getdns_validate_dnssec2(getdns_list *records_to_validate, r = GETDNS_DNSSEC_INDETERMINATE; switch (wire_validate_dnssec(mf, now, skew, - to_val, to_val_len, support, support_len, tas, tas_len)) { + to_val, to_val_len, support, support_len, tas, tas_len, + validation_chain)) { case GETDNS_DNSSEC_SECURE: if (r == GETDNS_DNSSEC_INDETERMINATE) r = GETDNS_DNSSEC_SECURE; @@ -3325,14 +3343,23 @@ exit_free_support: return r; } +getdns_return_t +getdns_validate_dnssec2(const getdns_list *records_to_validate, + const getdns_list *support_records, + const getdns_list *trust_anchors, + time_t now, uint32_t skew) +{ + return getdns_validate_dnssec3(records_to_validate, support_records, + trust_anchors, now, skew, NULL); +} getdns_return_t -getdns_validate_dnssec(getdns_list *records_to_validate, - getdns_list *support_records, - getdns_list *trust_anchors) +getdns_validate_dnssec(const getdns_list *records_to_validate, + const getdns_list *support_records, + const getdns_list *trust_anchors) { - return getdns_validate_dnssec2(records_to_validate, support_records, - trust_anchors, time(NULL), 0); + return getdns_validate_dnssec3(records_to_validate, support_records, + trust_anchors, time(NULL), 0, NULL); } /****************** getdns_root_trust_anchor() Function ******************** diff --git a/src/getdns/getdns.h.in b/src/getdns/getdns.h.in index 2a8f7f2a..048c2056 100644 --- a/src/getdns/getdns.h.in +++ b/src/getdns/getdns.h.in @@ -1125,9 +1125,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(getdns_list *to_validate, - getdns_list *support_records, - getdns_list *trust_anchors); +getdns_validate_dnssec(const getdns_list *to_validate, + const getdns_list *support_records, + const getdns_list *trust_anchors); /* Get root trust anchor */ getdns_list *getdns_root_trust_anchor(time_t *utc_date_of_anchor); diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index d48d13b6..5fd10afb 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -429,15 +429,53 @@ getdns_return_t getdns_dict_util_get_string(struct getdns_dict * dict, * @param skew The numer of seconds of skew that is allowed in * either direction when checking an RRSIG's * Expiration and Inception fields + * @param validation_chain The full DNSSEC authentication chain used to validate + * the RR-dicts to validate, including those records, + * all in canonical form and order. * @return The dnssec status of validated records or replies, * GETDNS_DNSSEC_SECURE, GETDNS_DNSSEC_INSECURE, * GETDNS_DNSSEC_INDETERMINATE or GETDNS_DNSSEC_BOGUS, or an error * return code. */ getdns_return_t -getdns_validate_dnssec2(getdns_list *to_validate, - getdns_list *support_records, - getdns_list *trust_anchors, +getdns_validate_dnssec3(const getdns_list *to_validate, + const getdns_list *support_records, + const getdns_list *trust_anchors, + time_t validation_time, uint32_t skew, + getdns_list *validation_chain); + +/** + * Validate replies or resource records. + * + * @param to_validate A list of RR-dicts with companion RRSIG-RR-dicts + * which will be validated. Or a list of reply-dicts + * that will be validated. The "replies_tree" list + * of a response dict can be used directly here. + * @param support_records A list of DS's RR-dicts and DNSKEY RR-dicts with + * companion RRSIG-RR-dicts that lead up from one of + * the trust_anchors to the RR-dicts or replies to + * validate. The "validation_chain" list of a response + * dict (with the dnssec_return_validation_chain + * extension) can be used directly here. + * @param trust_anchors The list of trusted DNSKEYs or DS'es RR-dicts. + * The result of the getdns_root_trust_anchor() or the + * getdns_context_get_dnssec_trust_anchors() function + * can be used directly here. + * @param validation_time The point in time in seconds since 1 January 1970 + * 00:00:00 UTC, ignoring leap seconds, wrapping using + * "Serial number arithmetic", as defined in RFC1982. + * @param skew The numer of seconds of skew that is allowed in + * either direction when checking an RRSIG's + * Expiration and Inception fields + * @return The dnssec status of validated records or replies, + * GETDNS_DNSSEC_SECURE, GETDNS_DNSSEC_INSECURE, + * GETDNS_DNSSEC_INDETERMINATE or GETDNS_DNSSEC_BOGUS, or an error + * return code. + */ +getdns_return_t +getdns_validate_dnssec2(const getdns_list *to_validate, + const getdns_list *support_records, + const getdns_list *trust_anchors, time_t validation_time, uint32_t skew); /** diff --git a/src/util-internal.c b/src/util-internal.c index 1b154b3a..2f1ab088 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -1431,7 +1431,7 @@ static void _getdns_reply2wire_buf(gldns_buffer *buf, getdns_dict *reply) } } -static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l) +static void _getdns_list2wire_buf(gldns_buffer *buf, const getdns_list *l) { getdns_dict *rr_dict; size_t i, pkt_start, ancount; @@ -1468,8 +1468,8 @@ static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l) gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_ANCOUNT_OFF, ancount); } -uint8_t *_getdns_list2wire( - getdns_list *l, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf) +uint8_t *_getdns_list2wire(const getdns_list *l, + uint8_t *buf, size_t *buf_len, struct mem_funcs *mf) { gldns_buffer gbuf; size_t sz; diff --git a/src/util-internal.h b/src/util-internal.h index 54c81d9d..6ff1b0e2 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -152,7 +152,7 @@ struct getdns_dict *_getdns_create_getdns_response(struct getdns_dns_req *comple getdns_return_t _getdns_validate_dname(const char* dname); uint8_t *_getdns_list2wire( - getdns_list *l, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf); + const getdns_list *l, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf); uint8_t *_getdns_reply2wire( getdns_dict *r, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf);