From fa782d1043ebc629db19d5b75765f2763f25e9b2 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 18 Mar 2015 14:45:06 +0100 Subject: [PATCH] --enable-broken-native-stub-dnssec Still needs a little more work for wildcards and NODATA answers... --- configure | 22 +++++ configure.ac | 10 +++ src/config.h.in | 4 + src/dnssec.c | 208 ++++++++++++++++++++++++++++++++++++++++---- src/general.c | 19 ++-- src/util-internal.c | 10 +-- src/util-internal.h | 5 +- 7 files changed, 250 insertions(+), 28 deletions(-) diff --git a/configure b/configure index 4664cc0c..c6dfa385 100755 --- a/configure +++ b/configure @@ -754,6 +754,7 @@ with_sysroot enable_libtool_lock enable_rpath enable_tcp_fastopen +enable_broken_native_stub_dnssec with_libidn with_libldns with_libunbound @@ -1396,6 +1397,9 @@ Optional Features: --disable-libtool-lock avoid locking (might break parallel builds) --disable-rpath disable hardcoded rpath (default=enabled) --enable-tcp-fastopen Enable TCP Fast Open + --enable-broken-native-stub-dnssec + Enable very experimental and broken native stub + DNSSEC support Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -11186,6 +11190,24 @@ _ACEOF ;; esac +# Check whether --enable-broken-native-stub-dnssec was given. +if test "${enable_broken_native_stub_dnssec+set}" = set; then : + enableval=$enable_broken_native_stub_dnssec; +fi + +case "$enable_broken_native_stub_dnssec" in + yes) + +cat >>confdefs.h <<_ACEOF +#define STUB_NATIVE_DNSSEC 1 +_ACEOF + + ;; + no|*) + ;; +esac + + # search to set include and library paths right # find libidn diff --git a/configure.ac b/configure.ac index 7c23ad05..e9c41a5c 100755 --- a/configure.ac +++ b/configure.ac @@ -131,6 +131,16 @@ case "$enable_tcp_fastopen" in ;; esac +AC_ARG_ENABLE(broken-native-stub-dnssec, AC_HELP_STRING([--enable-broken-native-stub-dnssec], [Enable very experimental and broken native stub DNSSEC support])) +case "$enable_broken_native_stub_dnssec" in + yes) + AC_DEFINE_UNQUOTED([STUB_NATIVE_DNSSEC], [1], [Define this to enable the very experimental and broken native stub DNSSEC support.]) + ;; + no|*) + ;; +esac + + # search to set include and library paths right # find libidn AC_ARG_WITH(libidn, AS_HELP_STRING([--with-libidn=pathname], diff --git a/src/config.h.in b/src/config.h.in index 61a8a865..0688d4d5 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -130,6 +130,10 @@ /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS +/* Define this to enable the very experimental and broken native stub DNSSEC + support. */ +#undef STUB_NATIVE_DNSSEC + /* System configuration dir */ #undef SYSCONFDIR diff --git a/src/dnssec.c b/src/dnssec.c index 857e1a3d..e9fcb726 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -79,6 +79,57 @@ static void launch_chain_link_lookup(struct validation_chain *chain, priv_getdns_rdf_iter *rdf_dname); static void destroy_chain(struct validation_chain *chain); +#ifdef STUB_NATIVE_DNSSEC +static void +native_stub_validate_dnssec(getdns_dns_req *dns_req, getdns_list *support) +{ + getdns_network_req *netreq, **netreq_p; + getdns_list *trust_anchors; + getdns_dict *reply = NULL; + getdns_dict *header; + getdns_list *to_validate; + uint32_t rcode; + + if (!(trust_anchors = getdns_root_trust_anchor(NULL))) + return; + + for (netreq_p = dns_req->netreqs; (netreq = *netreq_p) ; netreq_p++) { + if (!(reply = priv_getdns_create_reply_dict(dns_req->context, + netreq, NULL, NULL))) + continue; + if (getdns_dict_get_dict(reply, "header", &header)) + break; + if (getdns_dict_get_int(header, "rcode", &rcode)) + break; + if (rcode == GETDNS_RCODE_NXDOMAIN) { + if (getdns_dict_get_list( + reply, "authority", &to_validate)) + break; + } else if (getdns_dict_get_list(reply, "answer", &to_validate)) + break; + switch ((int)getdns_validate_dnssec( + to_validate, support, trust_anchors)) { + case GETDNS_DNSSEC_SECURE: + netreq->secure = 1; + netreq->bogus = 0; + break; + case GETDNS_DNSSEC_BOGUS: + netreq->secure = 0; + netreq->bogus = 1; + break; + default: + /* GETDNS_DNSSEC_INSECURE */ + netreq->secure = 0; + netreq->bogus = 0; + break; + } + getdns_dict_destroy(reply); + reply = NULL; + } + getdns_dict_destroy(reply); +} +#endif + static void callback_on_complete_chain(struct validation_chain *chain) { getdns_context *context = chain->dns_req->context; @@ -101,10 +152,9 @@ static void callback_on_complete_chain(struct validation_chain *chain) if (ongoing > 0) return; - if (!(response = create_getdns_response(chain->dns_req)) || - !(keys = getdns_list_create_with_context(context))) { - - priv_getdns_call_user_callback(chain->dns_req, response); + if (!(keys = getdns_list_create_with_context(context))) { + priv_getdns_call_user_callback(chain->dns_req, + create_getdns_response(chain->dns_req)); destroy_chain(chain); return; } @@ -117,7 +167,13 @@ static void callback_on_complete_chain(struct validation_chain *chain) , i, &rr_dict); i++) (void) getdns_list_append_dict(keys, rr_dict); } - (void) getdns_dict_set_list(response, "validation_chain", keys); +#ifdef STUB_NATIVE_DNSSEC + native_stub_validate_dnssec(chain->dns_req, keys); +#endif + if ((response = create_getdns_response(chain->dns_req)) && + chain->dns_req->dnssec_return_validation_chain) { + (void)getdns_dict_set_list(response, "validation_chain", keys); + } getdns_list_destroy(keys); priv_getdns_call_user_callback(chain->dns_req, response); destroy_chain(chain); @@ -385,6 +441,82 @@ priv_getdns_rr_list_from_list(struct getdns_list *list, ldns_rr_list **rr_list) return r; } +static int +priv_getdns_rr_dict_with_compressed_names(getdns_dict *rr_dict) +{ + uint32_t rr_type; + getdns_dict *rdata; + + if (getdns_dict_get_int(rr_dict, "type", &rr_type)) + return 0; + if (rr_type == GETDNS_RRTYPE_RRSIG) { + if (getdns_dict_get_dict(rr_dict, "rdata", &rdata)) + return 0; + if (getdns_dict_get_int(rdata, "type_covered", &rr_type)) + return 0; + } + switch (rr_type) { + case GETDNS_RRTYPE_NS: + case GETDNS_RRTYPE_MD: + case GETDNS_RRTYPE_CNAME: + case GETDNS_RRTYPE_SOA: + case GETDNS_RRTYPE_MG: + case GETDNS_RRTYPE_MR: + case GETDNS_RRTYPE_PTR: + case GETDNS_RRTYPE_MINFO: + case GETDNS_RRTYPE_MX: + return 1; + default: + return 0; + } +} + +static int +ldns_dname_compare_v(const void *a, const void *b) { + return ldns_dname_compare((ldns_rdf *)a, (ldns_rdf *)b); +} + +ldns_status +priv_getdns_ldns_dnssec_zone_add_rr(ldns_dnssec_zone *zone, ldns_rr *rr) +{ + ldns_dnssec_name *new_name; + ldns_rbnode_t *new_node; + + if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_NSEC3) + return ldns_dnssec_zone_add_rr(zone, rr); + + if (!(new_name = ldns_dnssec_name_new())) + return LDNS_STATUS_MEM_ERR; + + new_name->name = ldns_rdf_clone(ldns_rr_owner(rr)); + new_name->hashed_name = ldns_dname_label(ldns_rr_owner(rr), 0); + new_name->name_alloced = true; + + if (!(new_node = LDNS_MALLOC(ldns_rbnode_t))) { + ldns_dnssec_name_free(new_name); + return LDNS_STATUS_MEM_ERR; + } + new_node->key = new_name->name; + new_node->data = new_name; + if (!zone->names) + zone->names = ldns_rbtree_create(ldns_dname_compare_v); + (void)ldns_rbtree_insert(zone->names, new_node); + + if (!(new_node = LDNS_MALLOC(ldns_rbnode_t))) { + ldns_dnssec_name_free(new_name); + return LDNS_STATUS_MEM_ERR; + } + new_node->key = new_name->hashed_name; + new_node->data = new_name; + if (!zone->hashed_names) { + zone->_nsec3params = rr; + zone->hashed_names = ldns_rbtree_create(ldns_dname_compare_v); + } + (void)ldns_rbtree_insert(zone->hashed_names, new_node); + + return ldns_dnssec_zone_add_rr(zone, rr); +} + static getdns_return_t priv_getdns_dnssec_zone_from_list(struct getdns_list *list, ldns_dnssec_zone **zone) @@ -405,10 +537,13 @@ priv_getdns_dnssec_zone_from_list(struct getdns_list *list, if ((r = getdns_list_get_dict(list, i, &rr_dict))) break; + if (priv_getdns_rr_dict_with_compressed_names(rr_dict)) + continue; + if ((r = priv_getdns_create_rr_from_dict(rr_dict, &rr))) break; - if ((s = ldns_dnssec_zone_add_rr(*zone, rr))) { + if ((s = priv_getdns_ldns_dnssec_zone_add_rr(*zone, rr))) { ldns_rr_free(rr); r = GETDNS_RETURN_GENERIC_ERROR; break; @@ -423,19 +558,41 @@ typedef struct zone_iter { ldns_dnssec_zone *zone; ldns_rbnode_t *cur_node; ldns_dnssec_rrsets *cur_rrset; + + ldns_dnssec_rrsets nsec_rrset; + ldns_dnssec_rrs nsec_rrs; } zone_iter; static void rrset_iter_init_zone(zone_iter *i, ldns_dnssec_zone *zone) { + ldns_dnssec_name *name; 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; + if ((i->cur_node = zone->names + ? ldns_rbtree_first(zone->names) + : LDNS_RBTREE_NULL) == LDNS_RBTREE_NULL) { + + i->cur_rrset = NULL; + return; + } + + i->cur_rrset = ((ldns_dnssec_name *)i->cur_node->data)->rrsets; + if (!i->cur_rrset) { + name = ((ldns_dnssec_name *)i->cur_node->data); + if (name->nsec && name->nsec_signatures) { + i->cur_rrset = &i->nsec_rrset; + i->nsec_rrset.rrs = &i->nsec_rrs; + i->nsec_rrs.rr = name->nsec; + i->nsec_rrs.next = NULL; + i->nsec_rrset.type = ldns_rr_get_type(name->nsec); + i->nsec_rrset.signatures = + name->nsec_signatures; + i->nsec_rrset.next = NULL; + return; + } + } } static ldns_dnssec_rrsets * @@ -449,12 +606,30 @@ rrset_iter_value(zone_iter *i) static void rrset_iter_next(zone_iter *i) { + int was_nsec_rrset; + ldns_dnssec_name *name; assert(i); if (! i->cur_rrset) return; + was_nsec_rrset = (i->cur_rrset == &i->nsec_rrset); if (! (i->cur_rrset = i->cur_rrset->next)) { + + if (!was_nsec_rrset) { + name = ((ldns_dnssec_name *)i->cur_node->data); + if (name->nsec && name->nsec_signatures) { + i->cur_rrset = &i->nsec_rrset; + i->nsec_rrset.rrs = &i->nsec_rrs; + i->nsec_rrs.rr = name->nsec; + i->nsec_rrs.next = NULL; + i->nsec_rrset.type = ldns_rr_get_type(name->nsec); + i->nsec_rrset.signatures = + name->nsec_signatures; + i->nsec_rrset.next = NULL; + return; + } + } 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 @@ -572,9 +747,9 @@ done_free_verifying_keys: * */ getdns_return_t -getdns_validate_dnssec(struct getdns_list *records_to_validate, - struct getdns_list *support_records, - struct getdns_list *trust_anchors) +getdns_validate_dnssec(getdns_list *records_to_validate, + getdns_list *support_records, + getdns_list *trust_anchors) { getdns_return_t r; ldns_rr_list *trusted; @@ -584,7 +759,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_OK; + ldns_status s = LDNS_STATUS_ERR; if ((r = priv_getdns_rr_list_from_list(trust_anchors, &trusted))) return r; @@ -616,8 +791,7 @@ getdns_validate_dnssec(struct getdns_list *records_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) + if ((s = chase(rrset, support, support_keys, trusted))) break; } if (s == LDNS_STATUS_CRYPTO_BOGUS) diff --git a/src/general.c b/src/general.c index dc81b8f1..12616068 100644 --- a/src/general.c +++ b/src/general.c @@ -93,7 +93,13 @@ priv_getdns_check_dns_req_complete(getdns_dns_req *dns_req) if (! results_found) priv_getdns_call_user_callback(dns_req, NULL); - else if (dns_req->dnssec_return_validation_chain) + else if (dns_req->dnssec_return_validation_chain +#ifdef STUB_NATIVE_DNSSEC + || (dns_req->context->resolution_type == GETDNS_RESOLUTION_STUB + && (dns_req->dnssec_return_status || + dns_req->dnssec_return_only_secure)) +#endif + ) priv_getdns_get_validation_chain(dns_req); else priv_getdns_call_user_callback( @@ -130,13 +136,16 @@ submit_network_request(getdns_network_req *netreq) getdns_return_t r; getdns_dns_req *dns_req = netreq->owner; - if (dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING || + if (dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING /* TODO: Until DNSSEC with the new async stub resolver is finished, * use unbound when we need DNSSEC. */ - dns_req->dnssec_return_status || - dns_req->dnssec_return_only_secure || - dns_req->dnssec_return_validation_chain) { +#ifndef STUB_NATIVE_DNSSEC + || dns_req->dnssec_return_status + || dns_req->dnssec_return_only_secure + || dns_req->dnssec_return_validation_chain +#endif + ) { /* schedule the timeout */ if (! dns_req->timeout.timeout_cb) { diff --git a/src/util-internal.c b/src/util-internal.c index 1a734828..4038de2a 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -477,8 +477,8 @@ set_dict(getdns_dict **var, getdns_dict *value) #define SET_WIRE_CNT(X,Y) if (getdns_dict_set_int(header, #X , (int) \ GLDNS_ ## Y (req->response))) goto error -static getdns_dict * -create_reply_dict(getdns_context *context, getdns_network_req *req, +getdns_dict * +priv_getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, getdns_list *just_addrs, int *rrsigs_in_answer) { /* turn a packet into this glorious structure @@ -595,7 +595,7 @@ create_reply_dict(getdns_context *context, getdns_network_req *req, rr_type = gldns_read_uint16(rr_iter->rr_type); if (section > GLDNS_SECTION_QUESTION && - rr_type == GETDNS_RRTYPE_RRSIG) + rr_type == GETDNS_RRTYPE_RRSIG && rrsigs_in_answer) *rrsigs_in_answer = 1; if (section != GLDNS_SECTION_ANSWER) @@ -635,7 +635,7 @@ create_reply_dict(getdns_context *context, getdns_network_req *req, getdns_dict_set_bindata(rr_dict,"address_data",&bindata) || - getdns_list_append_dict(just_addrs, rr_dict)) { + (just_addrs && getdns_list_append_dict(just_addrs, rr_dict))) { goto error; } @@ -775,7 +775,7 @@ create_getdns_response(getdns_dns_req *completed_request) && ! netreq->secure) continue; } - if (!(reply = create_reply_dict(context, + if (!(reply = priv_getdns_create_reply_dict(context, netreq, just_addrs, &rrsigs_in_answer))) goto error; diff --git a/src/util-internal.h b/src/util-internal.h index 0c610139..af963e97 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -43,7 +43,7 @@ #include "rr-iter.h" #define SCHED_DEBUG 0 -#define WIRE_DEBUG 1 +#define WIRE_DEBUG 0 #ifdef S_SPLINT_S # define INLINE @@ -127,6 +127,9 @@ priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i); struct getdns_dns_req; struct getdns_dict *create_getdns_response(struct getdns_dns_req *completed_request); +getdns_dict *priv_getdns_create_reply_dict(getdns_context *context, + getdns_network_req *req, getdns_list *just_addrs, int *rrsigs_in_answer); + getdns_return_t validate_dname(const char* dname); /**