#include #include #include #include #ifdef HAVE_SSL /* this entire file is rather useless when you don't have * crypto... */ #include #include #include #include #include ldns_dnssec_data_chain * ldns_dnssec_data_chain_new() { ldns_dnssec_data_chain *nc = LDNS_XMALLOC(ldns_dnssec_data_chain, 1); nc->rrset = NULL; nc->parent_type = 0; nc->parent = NULL; nc->signatures = NULL; nc->packet_rcode = 0; nc->packet_qtype = 0; nc->packet_nodata = false; return nc; } void ldns_dnssec_data_chain_free(ldns_dnssec_data_chain *chain) { LDNS_FREE(chain); } void ldns_dnssec_data_chain_deep_free(ldns_dnssec_data_chain *chain) { ldns_rr_list_deep_free(chain->rrset); ldns_rr_list_deep_free(chain->signatures); if (chain->parent) { ldns_dnssec_data_chain_deep_free(chain->parent); } LDNS_FREE(chain); } void ldns_dnssec_data_chain_print(FILE *out, const ldns_dnssec_data_chain *chain) { ldns_lookup_table *rcode; const ldns_rr_descriptor *rr_descriptor; if (chain) { ldns_dnssec_data_chain_print(out, chain->parent); if (ldns_rr_list_rr_count(chain->rrset) > 0) { rcode = ldns_lookup_by_id(ldns_rcodes, (int) chain->packet_rcode); if (rcode) { fprintf(out, ";; rcode: %s\n", rcode->name); } rr_descriptor = ldns_rr_descript(chain->packet_qtype); if (rr_descriptor && rr_descriptor->_name) { fprintf(out, ";; qtype: %s\n", rr_descriptor->_name); } else if (chain->packet_qtype != 0) { fprintf(out, "TYPE%u", chain->packet_qtype); } if (chain->packet_nodata) { fprintf(out, ";; NODATA response\n"); } fprintf(out, "rrset:\n"); ldns_rr_list_print(out, chain->rrset); fprintf(out, "sigs:\n"); ldns_rr_list_print(out, chain->signatures); fprintf(out, "---\n"); } else { fprintf(out, "\n"); } } } static void ldns_dnssec_build_data_chain_dnskey(ldns_resolver *res, uint16_t qflags, const ldns_pkt *pkt, ldns_rr_list *signatures, ldns_dnssec_data_chain *new_chain, ldns_rdf *key_name, ldns_rr_class c) { ldns_rr_list *keys; ldns_pkt *my_pkt; if (signatures && ldns_rr_list_rr_count(signatures) > 0) { new_chain->signatures = ldns_rr_list_clone(signatures); new_chain->parent_type = 0; keys = ldns_pkt_rr_list_by_name_and_type( pkt, key_name, LDNS_RR_TYPE_DNSKEY, LDNS_SECTION_ANY_NOQUESTION ); if (!keys) { my_pkt = ldns_resolver_query(res, key_name, LDNS_RR_TYPE_DNSKEY, c, qflags); keys = ldns_pkt_rr_list_by_name_and_type( my_pkt, key_name, LDNS_RR_TYPE_DNSKEY, LDNS_SECTION_ANY_NOQUESTION ); new_chain->parent = ldns_dnssec_build_data_chain(res, qflags, keys, my_pkt, NULL); new_chain->parent->packet_qtype = LDNS_RR_TYPE_DNSKEY; ldns_pkt_free(my_pkt); } else { new_chain->parent = ldns_dnssec_build_data_chain(res, qflags, keys, pkt, NULL); new_chain->parent->packet_qtype = LDNS_RR_TYPE_DNSKEY; } ldns_rr_list_deep_free(keys); } } static void ldns_dnssec_build_data_chain_other(ldns_resolver *res, uint16_t qflags, ldns_dnssec_data_chain *new_chain, ldns_rdf *key_name, ldns_rr_class c, ldns_rr_list *dss) { /* 'self-signed', parent is a DS */ /* okay, either we have other keys signing the current one, * or the current * one should have a DS record in the parent zone. * How do we find this out? Try both? * * request DNSKEYS for current zone, * add all signatures to current level */ ldns_pkt *my_pkt; ldns_rr_list *signatures2; new_chain->parent_type = 1; my_pkt = ldns_resolver_query(res, key_name, LDNS_RR_TYPE_DS, c, qflags); dss = ldns_pkt_rr_list_by_name_and_type(my_pkt, key_name, LDNS_RR_TYPE_DS, LDNS_SECTION_ANY_NOQUESTION ); if (dss) { new_chain->parent = ldns_dnssec_build_data_chain(res, qflags, dss, my_pkt, NULL); new_chain->parent->packet_qtype = LDNS_RR_TYPE_DS; ldns_rr_list_deep_free(dss); } ldns_pkt_free(my_pkt); my_pkt = ldns_resolver_query(res, key_name, LDNS_RR_TYPE_DNSKEY, c, qflags); signatures2 = ldns_pkt_rr_list_by_name_and_type(my_pkt, key_name, LDNS_RR_TYPE_RRSIG, LDNS_SECTION_ANSWER); if (signatures2) { if (new_chain->signatures) { printf("There were already sigs!\n"); ldns_rr_list_deep_free(new_chain->signatures); printf("replacing the old sigs\n"); } new_chain->signatures = signatures2; } ldns_pkt_free(my_pkt); } ldns_dnssec_data_chain * ldns_dnssec_build_data_chain_nokeyname(ldns_resolver *res, uint16_t qflags, ldns_rr *orig_rr, const ldns_rr_list *rrset, ldns_dnssec_data_chain *new_chain) { ldns_rdf *possible_parent_name; ldns_pkt *my_pkt; /* apparently we were not able to find a signing key, so we assume the chain ends here */ /* try parents for auth denial of DS */ if (orig_rr) { possible_parent_name = ldns_rr_owner(orig_rr); } else if (rrset && ldns_rr_list_rr_count(rrset) > 0) { possible_parent_name = ldns_rr_owner(ldns_rr_list_rr(rrset, 0)); } else { /* no information to go on, give up */ return new_chain; } my_pkt = ldns_resolver_query(res, possible_parent_name, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN, qflags); if (ldns_pkt_ancount(my_pkt) > 0) { /* add error, no sigs but DS in parent */ /*ldns_pkt_print(stdout, my_pkt);*/ ldns_pkt_free(my_pkt); } else { /* are there signatures? */ new_chain->parent = ldns_dnssec_build_data_chain(res, qflags, NULL, my_pkt, NULL); new_chain->parent->packet_qtype = LDNS_RR_TYPE_DS; } return new_chain; } ldns_dnssec_data_chain * ldns_dnssec_build_data_chain(ldns_resolver *res, uint16_t qflags, const ldns_rr_list *rrset, const ldns_pkt *pkt, ldns_rr *orig_rr) { ldns_rr_list *signatures = NULL; ldns_rr_list *dss = NULL; ldns_rr_list *my_rrset; ldns_pkt *my_pkt; ldns_rdf *name = NULL, *key_name = NULL; ldns_rr_type type = 0; ldns_rr_class c = 0; bool other_rrset = false; ldns_dnssec_data_chain *new_chain = ldns_dnssec_data_chain_new(); if (!ldns_dnssec_pkt_has_rrsigs(pkt)) { /* hmm. no dnssec data in the packet. go up to try and deny * DS? */ return new_chain; } if (orig_rr) { new_chain->rrset = ldns_rr_list_new(); ldns_rr_list_push_rr(new_chain->rrset, orig_rr); new_chain->parent = ldns_dnssec_build_data_chain(res, qflags, rrset, pkt, NULL); new_chain->packet_rcode = ldns_pkt_get_rcode(pkt); new_chain->packet_qtype = ldns_rr_get_type(orig_rr); if (ldns_pkt_ancount(pkt) == 0) { new_chain->packet_nodata = true; } return new_chain; } if (!rrset || ldns_rr_list_rr_count(rrset) < 1) { /* hmm, no data, do we have denial? only works if pkt was given, otherwise caller has to do the check himself */ new_chain->packet_nodata = true; if (pkt) { my_rrset = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_NSEC, LDNS_SECTION_ANY_NOQUESTION ); if (my_rrset) { if (ldns_rr_list_rr_count(my_rrset) > 0) { type = LDNS_RR_TYPE_NSEC; other_rrset = true; } else { ldns_rr_list_deep_free(my_rrset); my_rrset = NULL; } } else { /* nothing, try nsec3 */ my_rrset = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_NSEC3, LDNS_SECTION_ANY_NOQUESTION); if (my_rrset) { if (ldns_rr_list_rr_count(my_rrset) > 0) { type = LDNS_RR_TYPE_NSEC3; other_rrset = true; } else { ldns_rr_list_deep_free(my_rrset); my_rrset = NULL; } } else { /* nothing, stop */ /* try parent zone? for denied insecure? */ return new_chain; } } } else { return new_chain; } } else { my_rrset = (ldns_rr_list *) rrset; } if (my_rrset && ldns_rr_list_rr_count(my_rrset) > 0) { new_chain->rrset = ldns_rr_list_clone(my_rrset); name = ldns_rr_owner(ldns_rr_list_rr(my_rrset, 0)); type = ldns_rr_get_type(ldns_rr_list_rr(my_rrset, 0)); c = ldns_rr_get_class(ldns_rr_list_rr(my_rrset, 0)); } if (other_rrset) { ldns_rr_list_deep_free(my_rrset); } /* normally there will only be 1 signature 'set' but there can be more than 1 denial (wildcards) so check for NSEC */ if (type == LDNS_RR_TYPE_NSEC || type == LDNS_RR_TYPE_NSEC3) { /* just throw in all signatures, the tree builder must sort this out */ if (pkt) { signatures = ldns_dnssec_pkt_get_rrsigs_for_type(pkt, type); } else { my_pkt = ldns_resolver_query(res, name, type, c, qflags); signatures = ldns_dnssec_pkt_get_rrsigs_for_type(pkt, type); ldns_pkt_free(my_pkt); } } else { if (pkt) { signatures = ldns_dnssec_pkt_get_rrsigs_for_name_and_type(pkt, name, type); } if (!signatures) { my_pkt = ldns_resolver_query(res, name, type, c, qflags); signatures = ldns_dnssec_pkt_get_rrsigs_for_name_and_type(my_pkt, name, type); ldns_pkt_free(my_pkt); } } if (signatures && ldns_rr_list_rr_count(signatures) > 0) { key_name = ldns_rr_rdf(ldns_rr_list_rr(signatures, 0), 7); } if (!key_name) { return ldns_dnssec_build_data_chain_nokeyname(res, qflags, orig_rr, rrset, new_chain); } if (type != LDNS_RR_TYPE_DNSKEY) { ldns_dnssec_build_data_chain_dnskey(res, qflags, pkt, signatures, new_chain, key_name, c ); } else { ldns_dnssec_build_data_chain_other(res, qflags, new_chain, key_name, c, dss ); } if (signatures) { ldns_rr_list_deep_free(signatures); } return new_chain; } ldns_dnssec_trust_tree * ldns_dnssec_trust_tree_new() { ldns_dnssec_trust_tree *new_tree = LDNS_XMALLOC(ldns_dnssec_trust_tree, 1); new_tree->rr = NULL; new_tree->rrset = NULL; new_tree->parent_count = 0; return new_tree; } void ldns_dnssec_trust_tree_free(ldns_dnssec_trust_tree *tree) { size_t i; if (tree) { for (i = 0; i < tree->parent_count; i++) { ldns_dnssec_trust_tree_free(tree->parents[i]); } } LDNS_FREE(tree); } size_t ldns_dnssec_trust_tree_depth(ldns_dnssec_trust_tree *tree) { size_t result = 0; size_t parent = 0; size_t i; for (i = 0; i < tree->parent_count; i++) { parent = ldns_dnssec_trust_tree_depth(tree->parents[i]); if (parent > result) { result = parent; } } return 1 + result; } /* TODO ldns_ */ static void print_tabs(FILE *out, size_t nr, uint8_t *map, size_t treedepth) { size_t i; for (i = 0; i < nr; i++) { if (i == nr - 1) { fprintf(out, "|---"); } else if (map && i < treedepth && map[i] == 1) { fprintf(out, "| "); } else { fprintf(out, " "); } } } void ldns_dnssec_trust_tree_print_sm(FILE *out, ldns_dnssec_trust_tree *tree, size_t tabs, bool extended, uint8_t *sibmap, size_t treedepth) { size_t i; const ldns_rr_descriptor *descriptor; bool mapset = false; if (!sibmap) { treedepth = ldns_dnssec_trust_tree_depth(tree); sibmap = malloc(treedepth); memset(sibmap, 0, treedepth); mapset = true; } if (tree) { if (tree->rr) { print_tabs(out, tabs, sibmap, treedepth); ldns_rdf_print(out, ldns_rr_owner(tree->rr)); descriptor = ldns_rr_descript(ldns_rr_get_type(tree->rr)); if (descriptor->_name) { fprintf(out, " (%s", descriptor->_name); } else { fprintf(out, " (TYPE%d", ldns_rr_get_type(tree->rr)); } if (tabs > 0) { if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_DNSKEY) { fprintf(out, " keytag: %u", (unsigned int) ldns_calc_keytag(tree->rr)); fprintf(out, " alg: "); ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 2)); fprintf(out, " flags: "); ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 0)); } else if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_DS) { fprintf(out, " keytag: "); ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 0)); fprintf(out, " digest type: "); ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 2)); } if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_NSEC) { fprintf(out, " "); ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 0)); fprintf(out, " "); ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 1)); } } fprintf(out, ")\n"); for (i = 0; i < tree->parent_count; i++) { if (tree->parent_count > 1 && i < tree->parent_count - 1) { sibmap[tabs] = 1; } else { sibmap[tabs] = 0; } /* only print errors */ if (ldns_rr_get_type(tree->parents[i]->rr) == LDNS_RR_TYPE_NSEC || ldns_rr_get_type(tree->parents[i]->rr) == LDNS_RR_TYPE_NSEC3) { if (tree->parent_status[i] == LDNS_STATUS_OK) { print_tabs(out, tabs + 1, sibmap, treedepth); if (tabs == 0 && ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_NS && ldns_rr_rd_count(tree->rr) > 0) { fprintf(out, "Existence of DS is denied by:\n"); } else { fprintf(out, "Existence is denied by:\n"); } } else { /* NS records aren't signed */ if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_NS) { fprintf(out, "Existence of DS is denied by:\n"); } else { print_tabs(out, tabs + 1, sibmap, treedepth); fprintf(out, "Error in denial of existence: %s\n", ldns_get_errorstr_by_id( tree->parent_status[i])); } } } else if (tree->parent_status[i] != LDNS_STATUS_OK) { print_tabs(out, tabs + 1, sibmap, treedepth); fprintf(out, "%s:\n", ldns_get_errorstr_by_id( tree->parent_status[i])); if (tree->parent_status[i] == LDNS_STATUS_SSL_ERR) { printf("; SSL Error: "); ERR_load_crypto_strings(); ERR_print_errors_fp(stdout); printf("\n"); } ldns_rr_print(out, tree->parent_signature[i]); printf("For RRset:\n"); ldns_rr_list_print(out, tree->rrset); printf("With key:\n"); ldns_rr_print(out, tree->parents[i]->rr); } ldns_dnssec_trust_tree_print_sm(out, tree->parents[i], tabs+1, extended, sibmap, treedepth); } } else { print_tabs(out, tabs, sibmap, treedepth); fprintf(out, "\n"); } } else { fprintf(out, "\n"); } if (mapset) { free(sibmap); } } void ldns_dnssec_trust_tree_print(FILE *out, ldns_dnssec_trust_tree *tree, size_t tabs, bool extended) { ldns_dnssec_trust_tree_print_sm(out, tree, tabs, extended, NULL, 0); } ldns_status ldns_dnssec_trust_tree_add_parent(ldns_dnssec_trust_tree *tree, const ldns_dnssec_trust_tree *parent, const ldns_rr *signature, const ldns_status parent_status) { if (tree && parent && tree->parent_count < LDNS_DNSSEC_TRUST_TREE_MAX_PARENTS) { /* printf("Add parent for: "); ldns_rr_print(stdout, tree->rr); printf("parent: "); ldns_rr_print(stdout, parent->rr); */ tree->parents[tree->parent_count] = (ldns_dnssec_trust_tree *) parent; tree->parent_status[tree->parent_count] = parent_status; tree->parent_signature[tree->parent_count] = (ldns_rr *) signature; tree->parent_count++; return LDNS_STATUS_OK; } else { return LDNS_STATUS_ERR; } } /* if rr is null, take the first from the rrset */ ldns_dnssec_trust_tree * ldns_dnssec_derive_trust_tree(ldns_dnssec_data_chain *data_chain, ldns_rr *rr) { ldns_rr_list *cur_rrset; ldns_rr_list *cur_sigs; ldns_rr *cur_rr = NULL; ldns_rr *cur_sig_rr; uint16_t cur_keytag; size_t i, j; ldns_dnssec_trust_tree *new_tree = ldns_dnssec_trust_tree_new(); if (data_chain && data_chain->rrset) { cur_rrset = data_chain->rrset; cur_sigs = data_chain->signatures; if (rr) { cur_rr = rr; } if (!cur_rr && ldns_rr_list_rr_count(cur_rrset) > 0) { cur_rr = ldns_rr_list_rr(cur_rrset, 0); } if (cur_rr) { new_tree->rr = cur_rr; new_tree->rrset = cur_rrset; /* there are three possibilities: 1 - 'normal' rrset, signed by a key 2 - dnskey signed by other dnskey 3 - dnskey proven by higher level DS (data denied by nsec is a special case that can occur in multiple places) */ if (cur_sigs) { for (i = 0; i < ldns_rr_list_rr_count(cur_sigs); i++) { /* find the appropriate key in the parent list */ cur_sig_rr = ldns_rr_list_rr(cur_sigs, i); cur_keytag = ldns_rdf2native_int16( ldns_rr_rrsig_keytag(cur_sig_rr)); if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_NSEC) { if (ldns_dname_compare(ldns_rr_owner(cur_sig_rr), ldns_rr_owner(cur_rr))) { /* find first that does match */ for (j = 0; j < ldns_rr_list_rr_count(cur_rrset) && ldns_dname_compare(ldns_rr_owner(cur_sig_rr),ldns_rr_owner(cur_rr)) != 0; j++) { cur_rr = ldns_rr_list_rr(cur_rrset, j); } if (ldns_dname_compare(ldns_rr_owner(cur_sig_rr), ldns_rr_owner(cur_rr))) { break; } } } /* option 1 */ if (data_chain->parent) { ldns_dnssec_derive_trust_tree_normal_rrset( new_tree, data_chain, cur_sig_rr); } /* option 2 */ ldns_dnssec_derive_trust_tree_dnskey_rrset( new_tree, data_chain, cur_rr, cur_sig_rr); } ldns_dnssec_derive_trust_tree_ds_rrset(new_tree, data_chain, cur_rr); } else { /* no signatures? maybe it's nsec data */ /* just add every rr from parent as new parent */ ldns_dnssec_derive_trust_tree_no_sig(new_tree, data_chain); } } } return new_tree; } void ldns_dnssec_derive_trust_tree_normal_rrset(ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain, ldns_rr *cur_sig_rr) { size_t i, j; ldns_rr_list *cur_rrset = ldns_rr_list_clone(data_chain->rrset); ldns_dnssec_trust_tree *cur_parent_tree; ldns_rr *cur_parent_rr; uint16_t cur_keytag; ldns_rr_list *tmp_rrset = NULL; ldns_status cur_status; cur_keytag = ldns_rdf2native_int16(ldns_rr_rrsig_keytag(cur_sig_rr)); for (j = 0; j < ldns_rr_list_rr_count(data_chain->parent->rrset); j++) { cur_parent_rr = ldns_rr_list_rr(data_chain->parent->rrset, j); if (ldns_rr_get_type(cur_parent_rr) == LDNS_RR_TYPE_DNSKEY) { if (ldns_calc_keytag(cur_parent_rr) == cur_keytag) { /* TODO: check wildcard nsec too */ if (cur_rrset && ldns_rr_list_rr_count(cur_rrset) > 0) { tmp_rrset = cur_rrset; if (ldns_rr_get_type(ldns_rr_list_rr(cur_rrset, 0)) == LDNS_RR_TYPE_NSEC || ldns_rr_get_type(ldns_rr_list_rr(cur_rrset, 0)) == LDNS_RR_TYPE_NSEC3) { /* might contain different names! sort and split */ ldns_rr_list_sort(cur_rrset); if (tmp_rrset && tmp_rrset != cur_rrset) { ldns_rr_list_deep_free(tmp_rrset); tmp_rrset = NULL; } tmp_rrset = ldns_rr_list_pop_rrset(cur_rrset); /* with nsecs, this might be the wrong one */ while (tmp_rrset && ldns_rr_list_rr_count(cur_rrset) > 0 && ldns_dname_compare( ldns_rr_owner(ldns_rr_list_rr( tmp_rrset, 0)), ldns_rr_owner(cur_sig_rr)) != 0) { ldns_rr_list_deep_free(tmp_rrset); tmp_rrset = ldns_rr_list_pop_rrset(cur_rrset); } } cur_status = ldns_verify_rrsig(tmp_rrset, cur_sig_rr, cur_parent_rr); /* avoid dupes */ for (i = 0; i < new_tree->parent_count; i++) { if (cur_parent_rr == new_tree->parents[i]->rr) { goto done; } } cur_parent_tree = ldns_dnssec_derive_trust_tree(data_chain->parent, cur_parent_rr); (void)ldns_dnssec_trust_tree_add_parent(new_tree, cur_parent_tree, cur_sig_rr, cur_status); } } } } done: if (tmp_rrset && tmp_rrset != cur_rrset) { ldns_rr_list_deep_free(tmp_rrset); } ldns_rr_list_deep_free(cur_rrset); } void ldns_dnssec_derive_trust_tree_dnskey_rrset(ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain, ldns_rr *cur_rr, ldns_rr *cur_sig_rr) { size_t j; ldns_rr_list *cur_rrset = data_chain->rrset; ldns_dnssec_trust_tree *cur_parent_tree; ldns_rr *cur_parent_rr; uint16_t cur_keytag; ldns_status cur_status; cur_keytag = ldns_rdf2native_int16(ldns_rr_rrsig_keytag(cur_sig_rr)); for (j = 0; j < ldns_rr_list_rr_count(cur_rrset); j++) { cur_parent_rr = ldns_rr_list_rr(cur_rrset, j); if (cur_parent_rr != cur_rr && ldns_rr_get_type(cur_parent_rr) == LDNS_RR_TYPE_DNSKEY) { if (ldns_calc_keytag(cur_parent_rr) == cur_keytag ) { cur_parent_tree = ldns_dnssec_trust_tree_new(); cur_parent_tree->rr = cur_parent_rr; cur_parent_tree->rrset = cur_rrset; cur_status = ldns_verify_rrsig(cur_rrset, cur_sig_rr, cur_parent_rr); (void) ldns_dnssec_trust_tree_add_parent(new_tree, cur_parent_tree, cur_sig_rr, cur_status); } } } } void ldns_dnssec_derive_trust_tree_ds_rrset(ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain, ldns_rr *cur_rr) { size_t j, h; ldns_rr_list *cur_rrset = data_chain->rrset; ldns_dnssec_trust_tree *cur_parent_tree; ldns_rr *cur_parent_rr; /* try the parent to see whether there are DSs there */ if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_DNSKEY && data_chain->parent && data_chain->parent->rrset ) { for (j = 0; j < ldns_rr_list_rr_count(data_chain->parent->rrset); j++) { cur_parent_rr = ldns_rr_list_rr(data_chain->parent->rrset, j); if (ldns_rr_get_type(cur_parent_rr) == LDNS_RR_TYPE_DS) { for (h = 0; h < ldns_rr_list_rr_count(cur_rrset); h++) { cur_rr = ldns_rr_list_rr(cur_rrset, h); if (ldns_rr_compare_ds(cur_rr, cur_parent_rr)) { cur_parent_tree = ldns_dnssec_derive_trust_tree( data_chain->parent, cur_parent_rr); (void) ldns_dnssec_trust_tree_add_parent( new_tree, cur_parent_tree, NULL, LDNS_STATUS_OK); } else { /*ldns_rr_print(stdout, cur_parent_rr);*/ } } cur_rr = ldns_rr_list_rr(cur_rrset, 0); } } } } void ldns_dnssec_derive_trust_tree_no_sig(ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain) { size_t i; ldns_rr_list *cur_rrset; ldns_rr *cur_parent_rr; ldns_dnssec_trust_tree *cur_parent_tree; ldns_status result; if (data_chain->parent && data_chain->parent->rrset) { cur_rrset = data_chain->parent->rrset; /* nsec? */ if (cur_rrset && ldns_rr_list_rr_count(cur_rrset) > 0) { if (ldns_rr_get_type(ldns_rr_list_rr(cur_rrset, 0)) == LDNS_RR_TYPE_NSEC3) { result = ldns_dnssec_verify_denial_nsec3( new_tree->rr, cur_rrset, data_chain->parent->signatures, data_chain->packet_rcode, data_chain->packet_qtype, data_chain->packet_nodata); } else if (ldns_rr_get_type(ldns_rr_list_rr(cur_rrset, 0)) == LDNS_RR_TYPE_NSEC3) { result = ldns_dnssec_verify_denial( new_tree->rr, cur_rrset, data_chain->parent->signatures); } else { /* unsigned zone, unsigned parent */ result = LDNS_STATUS_OK; } } else { result = LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED; } for (i = 0; i < ldns_rr_list_rr_count(cur_rrset); i++) { cur_parent_rr = ldns_rr_list_rr(cur_rrset, i); cur_parent_tree = ldns_dnssec_derive_trust_tree(data_chain->parent, cur_parent_rr); (void) ldns_dnssec_trust_tree_add_parent(new_tree, cur_parent_tree, NULL, result); } } } /* * returns OK if there is a path from tree to key with only OK * the (first) error in between otherwise * or NOT_FOUND if the key wasn't present at all */ ldns_status ldns_dnssec_trust_tree_contains_keys(ldns_dnssec_trust_tree *tree, ldns_rr_list *trusted_keys) { size_t i; ldns_status result = LDNS_STATUS_CRYPTO_NO_DNSKEY; bool equal; ldns_status parent_result; if (tree && trusted_keys && ldns_rr_list_rr_count(trusted_keys) > 0) { if (tree->rr) { for (i = 0; i < ldns_rr_list_rr_count(trusted_keys); i++) { equal = ldns_rr_compare_ds( tree->rr, ldns_rr_list_rr(trusted_keys, i)); if (equal) { result = LDNS_STATUS_OK; return result; } } } for (i = 0; i < tree->parent_count; i++) { parent_result = ldns_dnssec_trust_tree_contains_keys(tree->parents[i], trusted_keys); if (parent_result != LDNS_STATUS_CRYPTO_NO_DNSKEY) { if (tree->parent_status[i] != LDNS_STATUS_OK) { result = tree->parent_status[i]; } else { if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_NSEC && parent_result == LDNS_STATUS_OK ) { result = LDNS_STATUS_DNSSEC_EXISTENCE_DENIED; } else { result = parent_result; } } } } } else { result = LDNS_STATUS_ERR; } return result; } ldns_status ldns_verify(ldns_rr_list *rrset, ldns_rr_list *rrsig, const ldns_rr_list *keys, ldns_rr_list *good_keys) { uint16_t i; ldns_status verify_result = LDNS_STATUS_ERR; if (!rrset || !rrsig || !keys) { return LDNS_STATUS_ERR; } if (ldns_rr_list_rr_count(rrset) < 1) { return LDNS_STATUS_ERR; } if (ldns_rr_list_rr_count(rrsig) < 1) { return LDNS_STATUS_CRYPTO_NO_RRSIG; } if (ldns_rr_list_rr_count(keys) < 1) { verify_result = LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY; } else { for (i = 0; i < ldns_rr_list_rr_count(rrsig); i++) { ldns_status s = ldns_verify_rrsig_keylist(rrset, ldns_rr_list_rr(rrsig, i), keys, good_keys); /* try a little to get more descriptive error */ if(s == LDNS_STATUS_OK) { verify_result = LDNS_STATUS_OK; } else if(verify_result == LDNS_STATUS_ERR) verify_result = s; else if(s != LDNS_STATUS_ERR && verify_result == LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY) verify_result = s; } } return verify_result; } ldns_status ldns_verify_notime(ldns_rr_list *rrset, ldns_rr_list *rrsig, const ldns_rr_list *keys, ldns_rr_list *good_keys) { uint16_t i; ldns_status verify_result = LDNS_STATUS_ERR; if (!rrset || !rrsig || !keys) { return LDNS_STATUS_ERR; } if (ldns_rr_list_rr_count(rrset) < 1) { return LDNS_STATUS_ERR; } if (ldns_rr_list_rr_count(rrsig) < 1) { return LDNS_STATUS_CRYPTO_NO_RRSIG; } if (ldns_rr_list_rr_count(keys) < 1) { verify_result = LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY; } else { for (i = 0; i < ldns_rr_list_rr_count(rrsig); i++) { ldns_status s = ldns_verify_rrsig_keylist_notime(rrset, ldns_rr_list_rr(rrsig, i), keys, good_keys); /* try a little to get more descriptive error */ if (s == LDNS_STATUS_OK) { verify_result = LDNS_STATUS_OK; } else if (verify_result == LDNS_STATUS_ERR) { verify_result = s; } else if (s != LDNS_STATUS_ERR && verify_result == LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY) { verify_result = s; } } } return verify_result; } ldns_rr_list * ldns_fetch_valid_domain_keys(const ldns_resolver *res, const ldns_rdf *domain, const ldns_rr_list *keys, ldns_status *status) { ldns_rr_list * trusted_keys = NULL; ldns_rr_list * ds_keys = NULL; if (res && domain && keys) { if ((trusted_keys = ldns_validate_domain_dnskey(res, domain, keys))) { *status = LDNS_STATUS_OK; } else { /* No trusted keys in this domain, we'll have to find some in the parent domain */ *status = LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY; if (ldns_rdf_size(domain) > 1) { /* Fail if we are at the root */ ldns_rr_list * parent_keys; ldns_rdf * parent_domain = ldns_dname_left_chop(domain); if ((parent_keys = ldns_fetch_valid_domain_keys(res, parent_domain, keys, status))) { /* Check DS records */ if ((ds_keys = ldns_validate_domain_ds(res, domain, parent_keys))) { trusted_keys = ldns_fetch_valid_domain_keys(res, domain, ds_keys, status); ldns_rr_list_deep_free(ds_keys); } else { /* No valid DS at the parent -- fail */ *status = LDNS_STATUS_CRYPTO_NO_TRUSTED_DS ; } ldns_rr_list_deep_free(parent_keys); } ldns_rdf_deep_free(parent_domain); } } } return trusted_keys; } ldns_rr_list * ldns_validate_domain_dnskey(const ldns_resolver * res, const ldns_rdf * domain, const ldns_rr_list * keys) { ldns_status status; ldns_pkt * keypkt; ldns_rr * cur_key; uint16_t key_i; uint16_t key_j; uint16_t key_k; uint16_t sig_i; ldns_rr * cur_sig; ldns_rr_list * domain_keys = NULL; ldns_rr_list * domain_sigs = NULL; ldns_rr_list * trusted_keys = NULL; /* Fetch keys for the domain */ if ((keypkt = ldns_resolver_query(res, domain, LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, LDNS_RD))) { domain_keys = ldns_pkt_rr_list_by_type(keypkt, LDNS_RR_TYPE_DNSKEY, LDNS_SECTION_ANSWER); domain_sigs = ldns_pkt_rr_list_by_type(keypkt, LDNS_RR_TYPE_RRSIG, LDNS_SECTION_ANSWER); /* Try to validate the record using our keys */ for (key_i=0; key_i< ldns_rr_list_rr_count(domain_keys); key_i++) { cur_key = ldns_rr_list_rr(domain_keys, key_i); for (key_j=0; key_j