#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_CALLOC(ldns_dnssec_data_chain, 1); if(!nc) return NULL; /* * not needed anymore because CALLOC initalizes everything to zero. 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_fmt(FILE *out, const ldns_output_format *fmt, const ldns_dnssec_data_chain *chain) { ldns_lookup_table *rcode; const ldns_rr_descriptor *rr_descriptor; if (chain) { ldns_dnssec_data_chain_print_fmt(out, fmt, 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_fmt(out, fmt, chain->rrset); fprintf(out, "sigs:\n"); ldns_rr_list_print_fmt(out, fmt, chain->signatures); fprintf(out, "---\n"); } else { fprintf(out, "\n"); } } } void ldns_dnssec_data_chain_print(FILE *out, const ldns_dnssec_data_chain *chain) { ldns_dnssec_data_chain_print_fmt( out, ldns_output_format_default, chain); } 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); if (my_pkt) { 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); if (my_pkt) { 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); if (my_pkt) { 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 (!my_pkt) { return new_chain; } 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(); assert(pkt != NULL); 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); if (my_pkt) { 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); if (my_pkt) { 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) { if (signatures) { ldns_rr_list_deep_free(signatures); } 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); if(!new_tree) return NULL; 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_fmt(FILE *out, const ldns_output_format *fmt, 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 = LDNS_XMALLOC(uint8_t, treedepth); if(!sibmap) return; /* mem err */ 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_fmt(out, fmt, tree-> parent_signature[i]); printf("For RRset:\n"); ldns_rr_list_print_fmt(out, fmt, tree->rrset); printf("With key:\n"); ldns_rr_print_fmt(out, fmt, tree->parents[i]->rr); } ldns_dnssec_trust_tree_print_sm_fmt(out, fmt, tree->parents[i], tabs+1, extended, sibmap, treedepth); } } else { print_tabs(out, tabs, sibmap, treedepth); fprintf(out, "\n"); } } else { fprintf(out, "\n"); } if (mapset) { LDNS_FREE(sibmap); } } 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) { ldns_dnssec_trust_tree_print_sm_fmt(out, ldns_output_format_default, tree, tabs, extended, sibmap, treedepth); } void ldns_dnssec_trust_tree_print_fmt(FILE *out, const ldns_output_format *fmt, ldns_dnssec_trust_tree *tree, size_t tabs, bool extended) { ldns_dnssec_trust_tree_print_sm_fmt(out, fmt, tree, tabs, extended, NULL, 0); } void ldns_dnssec_trust_tree_print(FILE *out, ldns_dnssec_trust_tree *tree, size_t tabs, bool extended) { ldns_dnssec_trust_tree_print_fmt(out, ldns_output_format_default, tree, tabs, extended); } 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_time( ldns_dnssec_data_chain *data_chain, ldns_rr *rr, time_t check_time ) { ldns_rr_list *cur_rrset; ldns_rr_list *cur_sigs; ldns_rr *cur_rr = NULL; ldns_rr *cur_sig_rr; size_t i, j; ldns_dnssec_trust_tree *new_tree = ldns_dnssec_trust_tree_new(); if(!new_tree) return NULL; 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); 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_time( new_tree, data_chain, cur_sig_rr, check_time); } /* option 2 */ ldns_dnssec_derive_trust_tree_dnskey_rrset_time( new_tree, data_chain, cur_rr, cur_sig_rr, check_time); } ldns_dnssec_derive_trust_tree_ds_rrset_time( new_tree, data_chain, cur_rr, check_time); } else { /* no signatures? maybe it's nsec data */ /* just add every rr from parent as new parent */ ldns_dnssec_derive_trust_tree_no_sig_time( new_tree, data_chain, check_time); } } } return new_tree; } ldns_dnssec_trust_tree * ldns_dnssec_derive_trust_tree(ldns_dnssec_data_chain *data_chain, ldns_rr *rr) { return ldns_dnssec_derive_trust_tree_time(data_chain, rr, ldns_time(NULL)); } void ldns_dnssec_derive_trust_tree_normal_rrset_time( ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain, ldns_rr *cur_sig_rr, time_t check_time) { 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); assert(tmp_rrset == cur_rrset); 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_time( tmp_rrset, cur_sig_rr, cur_parent_rr, check_time); if (tmp_rrset && tmp_rrset != cur_rrset ) { ldns_rr_list_deep_free( tmp_rrset); tmp_rrset = NULL; } /* 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_time( data_chain->parent, cur_parent_rr, check_time); (void)ldns_dnssec_trust_tree_add_parent(new_tree, cur_parent_tree, cur_sig_rr, cur_status); } } } } done: ldns_rr_list_deep_free(cur_rrset); } 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) { ldns_dnssec_derive_trust_tree_normal_rrset_time( new_tree, data_chain, cur_sig_rr, ldns_time(NULL)); } void ldns_dnssec_derive_trust_tree_dnskey_rrset_time( ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain, ldns_rr *cur_rr, ldns_rr *cur_sig_rr, time_t check_time) { 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_time( cur_rrset, cur_sig_rr, cur_parent_rr, check_time); (void) ldns_dnssec_trust_tree_add_parent(new_tree, cur_parent_tree, cur_sig_rr, cur_status); } } } } 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) { ldns_dnssec_derive_trust_tree_dnskey_rrset_time( new_tree, data_chain, cur_rr, cur_sig_rr, ldns_time(NULL)); } void ldns_dnssec_derive_trust_tree_ds_rrset_time( ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain, ldns_rr *cur_rr, time_t check_time) { 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_time( data_chain->parent, cur_parent_rr, check_time); (void) ldns_dnssec_trust_tree_add_parent( new_tree, cur_parent_tree, NULL, LDNS_STATUS_OK); } else { /*ldns_rr_print(stdout, cur_parent_rr);*/ } } } } } } void ldns_dnssec_derive_trust_tree_ds_rrset(ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain, ldns_rr *cur_rr) { ldns_dnssec_derive_trust_tree_ds_rrset_time( new_tree, data_chain, cur_rr, ldns_time(NULL)); } void ldns_dnssec_derive_trust_tree_no_sig_time( ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain, time_t check_time) { 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_NSEC) { 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_time( data_chain->parent, cur_parent_rr, check_time); (void) ldns_dnssec_trust_tree_add_parent(new_tree, cur_parent_tree, NULL, result); } } } void ldns_dnssec_derive_trust_tree_no_sig(ldns_dnssec_trust_tree *new_tree, ldns_dnssec_data_chain *data_chain) { ldns_dnssec_derive_trust_tree_no_sig_time( new_tree, data_chain, ldns_time(NULL)); } /* * 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 (tree->rr && 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_time( ldns_rr_list *rrset, ldns_rr_list *rrsig, const ldns_rr_list *keys, time_t check_time, 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_time( rrset, ldns_rr_list_rr(rrsig, i), keys, check_time, 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(ldns_rr_list *rrset, ldns_rr_list *rrsig, const ldns_rr_list *keys, ldns_rr_list *good_keys) { return ldns_verify_time(rrset, rrsig, keys, ldns_time(NULL), good_keys); } 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_time(const ldns_resolver *res, const ldns_rdf *domain, const ldns_rr_list *keys, time_t check_time, ldns_status *status) { ldns_rr_list * trusted_keys = NULL; ldns_rr_list * ds_keys = NULL; ldns_rdf * prev_parent_domain; ldns_rdf * parent_domain; ldns_rr_list * parent_keys = NULL; if (res && domain && keys) { if ((trusted_keys = ldns_validate_domain_dnskey_time(res, domain, keys, check_time))) { *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; parent_domain = ldns_dname_left_chop(domain); while (parent_domain && /* Fail if we are at the root*/ ldns_rdf_size(parent_domain) > 0) { if ((parent_keys = ldns_fetch_valid_domain_keys_time(res, parent_domain, keys, check_time, status))) { /* Check DS records */ if ((ds_keys = ldns_validate_domain_ds_time(res, domain, parent_keys, check_time))) { trusted_keys = ldns_fetch_valid_domain_keys_time( res, domain, ds_keys, check_time, 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); break; } else { parent_domain = ldns_dname_left_chop(( prev_parent_domain = parent_domain )); ldns_rdf_deep_free(prev_parent_domain); } } if (parent_domain) { ldns_rdf_deep_free(parent_domain); } } } return trusted_keys; } ldns_rr_list * ldns_fetch_valid_domain_keys(const ldns_resolver *res, const ldns_rdf *domain, const ldns_rr_list *keys, ldns_status *status) { return ldns_fetch_valid_domain_keys_time( res, domain, keys, ldns_time(NULL), status); } ldns_rr_list * ldns_validate_domain_dnskey_time( const ldns_resolver * res, const ldns_rdf * domain, const ldns_rr_list * keys, time_t check_time ) { 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 */ keypkt = ldns_resolver_query(res, domain, LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, LDNS_RD); if (keypkt) { 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= ldns_dname_label_count(ldns_rr_owner(rr))) { /* Query name *is* the "next closer". */ hashed_next_closer = hashed_name; } else { /* "next closer" has less labels than the query name. * Create the name and hash it. */ next_closer = ldns_dname_clone_from( ldns_rr_owner(rr), ldns_dname_label_count(ldns_rr_owner(rr)) - (ldns_dname_label_count(closest_encloser) + 1) ); hashed_next_closer = ldns_nsec3_hash_name_frm_nsec3( ldns_rr_list_rr(nsecs, 0), next_closer ); (void) ldns_dname_cat(hashed_next_closer, zone_name); } /* Find the NSEC3 that covers the "next closer" */ for (i = 0; i < ldns_rr_list_rr_count(nsecs); i++) { if (ldns_nsec_covers_name(ldns_rr_list_rr(nsecs, i), hashed_next_closer) && ldns_nsec3_optout(ldns_rr_list_rr(nsecs, i))) { result = LDNS_STATUS_OK; if (match) { *match = ldns_rr_list_rr(nsecs, i); } break; } } if (ldns_dname_label_count(closest_encloser) + 1 < ldns_dname_label_count(ldns_rr_owner(rr))) { /* "next closer" has less labels than the query name. * Dispose of the temporary variables that held that name. */ ldns_rdf_deep_free(hashed_next_closer); ldns_rdf_deep_free(next_closer); } ldns_rdf_deep_free(closest_encloser); } done: ldns_rdf_deep_free(zone_name); return result; } ldns_status ldns_dnssec_verify_denial_nsec3(ldns_rr *rr, ldns_rr_list *nsecs, ldns_rr_list *rrsigs, ldns_pkt_rcode packet_rcode, ldns_rr_type packet_qtype, bool packet_nodata) { return ldns_dnssec_verify_denial_nsec3_match( rr, nsecs, rrsigs, packet_rcode, packet_qtype, packet_nodata, NULL ); } #ifdef USE_GOST EVP_PKEY* ldns_gost2pkey_raw(unsigned char* key, size_t keylen) { /* prefix header for X509 encoding */ uint8_t asn[37] = { 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01, 0x03, 0x43, 0x00, 0x04, 0x40}; unsigned char encoded[37+64]; const unsigned char* pp; if(keylen != 64) { /* key wrong size */ return NULL; } /* create evp_key */ memmove(encoded, asn, 37); memmove(encoded+37, key, 64); pp = (unsigned char*)&encoded[0]; return d2i_PUBKEY(NULL, &pp, (int)sizeof(encoded)); } static ldns_status ldns_verify_rrsig_gost_raw(unsigned char* sig, size_t siglen, ldns_buffer* rrset, unsigned char* key, size_t keylen) { EVP_PKEY *evp_key; ldns_status result; (void) ldns_key_EVP_load_gost_id(); evp_key = ldns_gost2pkey_raw(key, keylen); if(!evp_key) { /* could not convert key */ return LDNS_STATUS_CRYPTO_BOGUS; } /* verify signature */ result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, evp_key, EVP_get_digestbyname("md_gost94")); EVP_PKEY_free(evp_key); return result; } #endif #ifdef USE_ECDSA EVP_PKEY* ldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo) { unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */ const unsigned char* pp = buf; EVP_PKEY *evp_key; EC_KEY *ec; /* check length, which uncompressed must be 2 bignums */ if(algo == LDNS_ECDSAP256SHA256) { if(keylen != 2*256/8) return NULL; ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); } else if(algo == LDNS_ECDSAP384SHA384) { if(keylen != 2*384/8) return NULL; ec = EC_KEY_new_by_curve_name(NID_secp384r1); } else ec = NULL; if(!ec) return NULL; if(keylen+1 > sizeof(buf)) return NULL; /* sanity check */ /* prepend the 0x02 (from docs) (or actually 0x04 from implementation * of openssl) for uncompressed data */ buf[0] = POINT_CONVERSION_UNCOMPRESSED; memmove(buf+1, key, keylen); if(!o2i_ECPublicKey(&ec, &pp, (int)keylen+1)) { EC_KEY_free(ec); return NULL; } evp_key = EVP_PKEY_new(); if(!evp_key) { EC_KEY_free(ec); return NULL; } if (!EVP_PKEY_assign_EC_KEY(evp_key, ec)) { EVP_PKEY_free(evp_key); EC_KEY_free(ec); return NULL; } return evp_key; } static ldns_status ldns_verify_rrsig_ecdsa_raw(unsigned char* sig, size_t siglen, ldns_buffer* rrset, unsigned char* key, size_t keylen, uint8_t algo) { EVP_PKEY *evp_key; ldns_status result; const EVP_MD *d; evp_key = ldns_ecdsa2pkey_raw(key, keylen, algo); if(!evp_key) { /* could not convert key */ return LDNS_STATUS_CRYPTO_BOGUS; } if(algo == LDNS_ECDSAP256SHA256) d = EVP_sha256(); else d = EVP_sha384(); /* LDNS_ECDSAP384SHA384 */ result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, evp_key, d); EVP_PKEY_free(evp_key); return result; } #endif ldns_status ldns_verify_rrsig_buffers(ldns_buffer *rawsig_buf, ldns_buffer *verify_buf, ldns_buffer *key_buf, uint8_t algo) { return ldns_verify_rrsig_buffers_raw( (unsigned char*)ldns_buffer_begin(rawsig_buf), ldns_buffer_position(rawsig_buf), verify_buf, (unsigned char*)ldns_buffer_begin(key_buf), ldns_buffer_position(key_buf), algo); } ldns_status ldns_verify_rrsig_buffers_raw(unsigned char* sig, size_t siglen, ldns_buffer *verify_buf, unsigned char* key, size_t keylen, uint8_t algo) { /* check for right key */ switch(algo) { case LDNS_DSA: case LDNS_DSA_NSEC3: return ldns_verify_rrsig_dsa_raw(sig, siglen, verify_buf, key, keylen); break; case LDNS_RSASHA1: case LDNS_RSASHA1_NSEC3: return ldns_verify_rrsig_rsasha1_raw(sig, siglen, verify_buf, key, keylen); break; #ifdef USE_SHA2 case LDNS_RSASHA256: return ldns_verify_rrsig_rsasha256_raw(sig, siglen, verify_buf, key, keylen); break; case LDNS_RSASHA512: return ldns_verify_rrsig_rsasha512_raw(sig, siglen, verify_buf, key, keylen); break; #endif #ifdef USE_GOST case LDNS_ECC_GOST: return ldns_verify_rrsig_gost_raw(sig, siglen, verify_buf, key, keylen); break; #endif #ifdef USE_ECDSA case LDNS_ECDSAP256SHA256: case LDNS_ECDSAP384SHA384: return ldns_verify_rrsig_ecdsa_raw(sig, siglen, verify_buf, key, keylen, algo); break; #endif case LDNS_RSAMD5: return ldns_verify_rrsig_rsamd5_raw(sig, siglen, verify_buf, key, keylen); break; default: /* do you know this alg?! */ return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO; } } /** * Reset the ttl in the rrset with the orig_ttl from the sig * and update owner name if it was wildcard * Also canonicalizes the rrset. * @param rrset: rrset to modify * @param sig: signature to take TTL and wildcard values from */ static void ldns_rrset_use_signature_ttl(ldns_rr_list* rrset_clone, ldns_rr* rrsig) { uint32_t orig_ttl; uint16_t i; uint8_t label_count; ldns_rdf *wildcard_name; ldns_rdf *wildcard_chopped; ldns_rdf *wildcard_chopped_tmp; if ((rrsig == NULL) || ldns_rr_rd_count(rrsig) < 4) { return; } orig_ttl = ldns_rdf2native_int32( ldns_rr_rdf(rrsig, 3)); label_count = ldns_rdf2native_int8(ldns_rr_rdf(rrsig, 2)); for(i = 0; i < ldns_rr_list_rr_count(rrset_clone); i++) { if (label_count < ldns_dname_label_count( ldns_rr_owner(ldns_rr_list_rr(rrset_clone, i)))) { (void) ldns_str2rdf_dname(&wildcard_name, "*"); wildcard_chopped = ldns_rdf_clone(ldns_rr_owner( ldns_rr_list_rr(rrset_clone, i))); while (label_count < ldns_dname_label_count(wildcard_chopped)) { wildcard_chopped_tmp = ldns_dname_left_chop( wildcard_chopped); ldns_rdf_deep_free(wildcard_chopped); wildcard_chopped = wildcard_chopped_tmp; } (void) ldns_dname_cat(wildcard_name, wildcard_chopped); ldns_rdf_deep_free(wildcard_chopped); ldns_rdf_deep_free(ldns_rr_owner(ldns_rr_list_rr( rrset_clone, i))); ldns_rr_set_owner(ldns_rr_list_rr(rrset_clone, i), wildcard_name); } ldns_rr_set_ttl(ldns_rr_list_rr(rrset_clone, i), orig_ttl); /* convert to lowercase */ ldns_rr2canonical(ldns_rr_list_rr(rrset_clone, i)); } } /** * Make raw signature buffer out of rrsig * @param rawsig_buf: raw signature buffer for result * @param rrsig: signature to convert * @return OK or more specific error. */ static ldns_status ldns_rrsig2rawsig_buffer(ldns_buffer* rawsig_buf, ldns_rr* rrsig) { uint8_t sig_algo; if (rrsig == NULL) { return LDNS_STATUS_CRYPTO_NO_RRSIG; } if (ldns_rr_rdf(rrsig, 1) == NULL) { return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG; } sig_algo = ldns_rdf2native_int8(ldns_rr_rdf(rrsig, 1)); /* check for known and implemented algo's now (otherwise * the function could return a wrong error */ /* create a buffer with signature rdata */ /* for some algorithms we need other data than for others... */ /* (the DSA API wants DER encoding for instance) */ switch(sig_algo) { case LDNS_RSAMD5: case LDNS_RSASHA1: case LDNS_RSASHA1_NSEC3: #ifdef USE_SHA2 case LDNS_RSASHA256: case LDNS_RSASHA512: #endif #ifdef USE_GOST case LDNS_ECC_GOST: #endif if (ldns_rr_rdf(rrsig, 8) == NULL) { return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG; } if (ldns_rdf2buffer_wire(rawsig_buf, ldns_rr_rdf(rrsig, 8)) != LDNS_STATUS_OK) { return LDNS_STATUS_MEM_ERR; } break; case LDNS_DSA: case LDNS_DSA_NSEC3: /* EVP takes rfc2459 format, which is a tad longer than dns format */ if (ldns_rr_rdf(rrsig, 8) == NULL) { return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG; } if (ldns_convert_dsa_rrsig_rdf2asn1( rawsig_buf, ldns_rr_rdf(rrsig, 8)) != LDNS_STATUS_OK) { /* if (ldns_rdf2buffer_wire(rawsig_buf, ldns_rr_rdf(rrsig, 8)) != LDNS_STATUS_OK) { */ return LDNS_STATUS_MEM_ERR; } break; #ifdef USE_ECDSA case LDNS_ECDSAP256SHA256: case LDNS_ECDSAP384SHA384: /* EVP produces an ASN prefix on the signature, which is * not used in the DNS */ if (ldns_rr_rdf(rrsig, 8) == NULL) { return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG; } if (ldns_convert_ecdsa_rrsig_rdf2asn1( rawsig_buf, ldns_rr_rdf(rrsig, 8)) != LDNS_STATUS_OK) { return LDNS_STATUS_MEM_ERR; } break; #endif case LDNS_DH: case LDNS_ECC: case LDNS_INDIRECT: return LDNS_STATUS_CRYPTO_ALGO_NOT_IMPL; default: return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO; } return LDNS_STATUS_OK; } /** * Check RRSIG timestamps against the given 'now' time. * @param rrsig: signature to check. * @param now: the current time in seconds epoch. * @return status code LDNS_STATUS_OK if all is fine. */ static ldns_status ldns_rrsig_check_timestamps(ldns_rr* rrsig, time_t now) { int32_t inception, expiration; /* check the signature time stamps */ inception = (int32_t)ldns_rdf2native_time_t( ldns_rr_rrsig_inception(rrsig)); expiration = (int32_t)ldns_rdf2native_time_t( ldns_rr_rrsig_expiration(rrsig)); if (expiration - inception < 0) { /* bad sig, expiration before inception?? Tsssg */ return LDNS_STATUS_CRYPTO_EXPIRATION_BEFORE_INCEPTION; } if (((int32_t) now) - inception < 0) { /* bad sig, inception date has not yet come to pass */ return LDNS_STATUS_CRYPTO_SIG_NOT_INCEPTED; } if (expiration - ((int32_t) now) < 0) { /* bad sig, expiration date has passed */ return LDNS_STATUS_CRYPTO_SIG_EXPIRED; } return LDNS_STATUS_OK; } /** * Prepare for verification. * @param rawsig_buf: raw signature buffer made ready. * @param verify_buf: data for verification buffer made ready. * @param rrset_clone: made ready. * @param rrsig: signature to prepare for. * @return LDNS_STATUS_OK is all went well. Otherwise specific error. */ static ldns_status ldns_prepare_for_verify(ldns_buffer* rawsig_buf, ldns_buffer* verify_buf, ldns_rr_list* rrset_clone, ldns_rr* rrsig) { ldns_status result; /* canonicalize the sig */ ldns_dname2canonical(ldns_rr_owner(rrsig)); /* check if the typecovered is equal to the type checked */ if (ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rrsig)) != ldns_rr_get_type(ldns_rr_list_rr(rrset_clone, 0))) return LDNS_STATUS_CRYPTO_TYPE_COVERED_ERR; /* create a buffer with b64 signature rdata */ result = ldns_rrsig2rawsig_buffer(rawsig_buf, rrsig); if(result != LDNS_STATUS_OK) return result; /* use TTL from signature. Use wildcard names for wildcards */ /* also canonicalizes rrset_clone */ ldns_rrset_use_signature_ttl(rrset_clone, rrsig); /* sort the rrset in canonical order */ ldns_rr_list_sort(rrset_clone); /* put the signature rr (without the b64) to the verify_buf */ if (ldns_rrsig2buffer_wire(verify_buf, rrsig) != LDNS_STATUS_OK) return LDNS_STATUS_MEM_ERR; /* add the rrset in verify_buf */ if(ldns_rr_list2buffer_wire(verify_buf, rrset_clone) != LDNS_STATUS_OK) return LDNS_STATUS_MEM_ERR; return LDNS_STATUS_OK; } /** * Check if a key matches a signature. * Checks keytag, sigalgo and signature. * @param rawsig_buf: raw signature buffer for verify * @param verify_buf: raw data buffer for verify * @param rrsig: the rrsig * @param key: key to attempt. * @return LDNS_STATUS_OK if OK, else some specific error. */ static ldns_status ldns_verify_test_sig_key(ldns_buffer* rawsig_buf, ldns_buffer* verify_buf, ldns_rr* rrsig, ldns_rr* key) { uint8_t sig_algo; if (rrsig == NULL) { return LDNS_STATUS_CRYPTO_NO_RRSIG; } if (ldns_rr_rdf(rrsig, 1) == NULL) { return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG; } sig_algo = ldns_rdf2native_int8(ldns_rr_rdf(rrsig, 1)); /* before anything, check if the keytags match */ if (ldns_calc_keytag(key) == ldns_rdf2native_int16(ldns_rr_rrsig_keytag(rrsig)) ) { ldns_buffer* key_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN); ldns_status result = LDNS_STATUS_ERR; /* put the key-data in a buffer, that's the third rdf, with * the base64 encoded key data */ if (ldns_rr_rdf(key, 3) == NULL) { ldns_buffer_free(key_buf); return LDNS_STATUS_MISSING_RDATA_FIELDS_KEY; } if (ldns_rdf2buffer_wire(key_buf, ldns_rr_rdf(key, 3)) != LDNS_STATUS_OK) { ldns_buffer_free(key_buf); /* returning is bad might screw up good keys later in the list what to do? */ return LDNS_STATUS_ERR; } if (ldns_rr_rdf(key, 2) == NULL) { result = LDNS_STATUS_MISSING_RDATA_FIELDS_KEY; } else if (sig_algo == ldns_rdf2native_int8( ldns_rr_rdf(key, 2))) { result = ldns_verify_rrsig_buffers(rawsig_buf, verify_buf, key_buf, sig_algo); } else { /* No keys with the corresponding algorithm are found */ result = LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY; } ldns_buffer_free(key_buf); return result; } else { /* No keys with the corresponding keytag are found */ return LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY; } } /* * to verify: * - create the wire fmt of the b64 key rdata * - create the wire fmt of the sorted rrset * - create the wire fmt of the b64 sig rdata * - create the wire fmt of the sig without the b64 rdata * - cat the sig data (without b64 rdata) to the rrset * - verify the rrset+sig, with the b64 data and the b64 key data */ ldns_status ldns_verify_rrsig_keylist_time( ldns_rr_list *rrset, ldns_rr *rrsig, const ldns_rr_list *keys, time_t check_time, ldns_rr_list *good_keys) { ldns_status result; ldns_rr_list *valid = ldns_rr_list_new(); if (!valid) return LDNS_STATUS_MEM_ERR; result = ldns_verify_rrsig_keylist_notime(rrset, rrsig, keys, valid); if(result != LDNS_STATUS_OK) { ldns_rr_list_free(valid); return result; } /* check timestamps last; its OK except time */ result = ldns_rrsig_check_timestamps(rrsig, check_time); if(result != LDNS_STATUS_OK) { ldns_rr_list_free(valid); return result; } ldns_rr_list_cat(good_keys, valid); ldns_rr_list_free(valid); return LDNS_STATUS_OK; } /* * to verify: * - create the wire fmt of the b64 key rdata * - create the wire fmt of the sorted rrset * - create the wire fmt of the b64 sig rdata * - create the wire fmt of the sig without the b64 rdata * - cat the sig data (without b64 rdata) to the rrset * - verify the rrset+sig, with the b64 data and the b64 key data */ ldns_status ldns_verify_rrsig_keylist(ldns_rr_list *rrset, ldns_rr *rrsig, const ldns_rr_list *keys, ldns_rr_list *good_keys) { return ldns_verify_rrsig_keylist_time( rrset, rrsig, keys, ldns_time(NULL), good_keys); } ldns_status ldns_verify_rrsig_keylist_notime(ldns_rr_list *rrset, ldns_rr *rrsig, const ldns_rr_list *keys, ldns_rr_list *good_keys) { ldns_buffer *rawsig_buf; ldns_buffer *verify_buf; uint16_t i; ldns_status result, status; ldns_rr_list *rrset_clone; ldns_rr_list *validkeys; if (!rrset) { return LDNS_STATUS_ERR; } validkeys = ldns_rr_list_new(); if (!validkeys) { return LDNS_STATUS_MEM_ERR; } /* clone the rrset so that we can fiddle with it */ rrset_clone = ldns_rr_list_clone(rrset); /* create the buffers which will certainly hold the raw data */ rawsig_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN); verify_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN); result = ldns_prepare_for_verify(rawsig_buf, verify_buf, rrset_clone, rrsig); if(result != LDNS_STATUS_OK) { ldns_buffer_free(verify_buf); ldns_buffer_free(rawsig_buf); ldns_rr_list_deep_free(rrset_clone); ldns_rr_list_free(validkeys); return result; } result = LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY; for(i = 0; i < ldns_rr_list_rr_count(keys); i++) { status = ldns_verify_test_sig_key(rawsig_buf, verify_buf, rrsig, ldns_rr_list_rr(keys, i)); if (status == LDNS_STATUS_OK) { /* one of the keys has matched, don't break * here, instead put the 'winning' key in * the validkey list and return the list * later */ if (!ldns_rr_list_push_rr(validkeys, ldns_rr_list_rr(keys,i))) { /* couldn't push the key?? */ ldns_buffer_free(rawsig_buf); ldns_buffer_free(verify_buf); ldns_rr_list_deep_free(rrset_clone); ldns_rr_list_free(validkeys); return LDNS_STATUS_MEM_ERR; } result = status; } if (result == LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY) { result = status; } } /* no longer needed */ ldns_rr_list_deep_free(rrset_clone); ldns_buffer_free(rawsig_buf); ldns_buffer_free(verify_buf); if (ldns_rr_list_rr_count(validkeys) == 0) { /* no keys were added, return last error */ ldns_rr_list_free(validkeys); return result; } /* do not check timestamps */ ldns_rr_list_cat(good_keys, validkeys); ldns_rr_list_free(validkeys); return LDNS_STATUS_OK; } ldns_status ldns_verify_rrsig_time( ldns_rr_list *rrset, ldns_rr *rrsig, ldns_rr *key, time_t check_time) { ldns_buffer *rawsig_buf; ldns_buffer *verify_buf; ldns_status result; ldns_rr_list *rrset_clone; if (!rrset) { return LDNS_STATUS_NO_DATA; } /* clone the rrset so that we can fiddle with it */ rrset_clone = ldns_rr_list_clone(rrset); /* create the buffers which will certainly hold the raw data */ rawsig_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN); verify_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN); result = ldns_prepare_for_verify(rawsig_buf, verify_buf, rrset_clone, rrsig); if(result != LDNS_STATUS_OK) { ldns_rr_list_deep_free(rrset_clone); ldns_buffer_free(rawsig_buf); ldns_buffer_free(verify_buf); return result; } result = ldns_verify_test_sig_key(rawsig_buf, verify_buf, rrsig, key); /* no longer needed */ ldns_rr_list_deep_free(rrset_clone); ldns_buffer_free(rawsig_buf); ldns_buffer_free(verify_buf); /* check timestamp last, apart from time its OK */ if(result == LDNS_STATUS_OK) result = ldns_rrsig_check_timestamps(rrsig, check_time); return result; } ldns_status ldns_verify_rrsig(ldns_rr_list *rrset, ldns_rr *rrsig, ldns_rr *key) { return ldns_verify_rrsig_time(rrset, rrsig, key, ldns_time(NULL)); } ldns_status ldns_verify_rrsig_evp(ldns_buffer *sig, ldns_buffer *rrset, EVP_PKEY *key, const EVP_MD *digest_type) { return ldns_verify_rrsig_evp_raw( (unsigned char*)ldns_buffer_begin(sig), ldns_buffer_position(sig), rrset, key, digest_type); } ldns_status ldns_verify_rrsig_evp_raw(unsigned char *sig, size_t siglen, ldns_buffer *rrset, EVP_PKEY *key, const EVP_MD *digest_type) { EVP_MD_CTX ctx; int res; EVP_MD_CTX_init(&ctx); EVP_VerifyInit(&ctx, digest_type); EVP_VerifyUpdate(&ctx, ldns_buffer_begin(rrset), ldns_buffer_position(rrset)); res = EVP_VerifyFinal(&ctx, sig, (unsigned int) siglen, key); EVP_MD_CTX_cleanup(&ctx); if (res == 1) { return LDNS_STATUS_OK; } else if (res == 0) { return LDNS_STATUS_CRYPTO_BOGUS; } /* TODO how to communicate internal SSL error? let caller use ssl's get_error() */ return LDNS_STATUS_SSL_ERR; } ldns_status ldns_verify_rrsig_dsa(ldns_buffer *sig, ldns_buffer *rrset, ldns_buffer *key) { return ldns_verify_rrsig_dsa_raw( (unsigned char*) ldns_buffer_begin(sig), ldns_buffer_position(sig), rrset, (unsigned char*) ldns_buffer_begin(key), ldns_buffer_position(key)); } ldns_status ldns_verify_rrsig_rsasha1(ldns_buffer *sig, ldns_buffer *rrset, ldns_buffer *key) { return ldns_verify_rrsig_rsasha1_raw( (unsigned char*)ldns_buffer_begin(sig), ldns_buffer_position(sig), rrset, (unsigned char*) ldns_buffer_begin(key), ldns_buffer_position(key)); } ldns_status ldns_verify_rrsig_rsamd5(ldns_buffer *sig, ldns_buffer *rrset, ldns_buffer *key) { return ldns_verify_rrsig_rsamd5_raw( (unsigned char*)ldns_buffer_begin(sig), ldns_buffer_position(sig), rrset, (unsigned char*) ldns_buffer_begin(key), ldns_buffer_position(key)); } ldns_status ldns_verify_rrsig_dsa_raw(unsigned char* sig, size_t siglen, ldns_buffer* rrset, unsigned char* key, size_t keylen) { EVP_PKEY *evp_key; ldns_status result; evp_key = EVP_PKEY_new(); if (EVP_PKEY_assign_DSA(evp_key, ldns_key_buf2dsa_raw(key, keylen))) { result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, evp_key, EVP_dss1()); } else { result = LDNS_STATUS_SSL_ERR; } EVP_PKEY_free(evp_key); return result; } ldns_status ldns_verify_rrsig_rsasha1_raw(unsigned char* sig, size_t siglen, ldns_buffer* rrset, unsigned char* key, size_t keylen) { EVP_PKEY *evp_key; ldns_status result; evp_key = EVP_PKEY_new(); if (EVP_PKEY_assign_RSA(evp_key, ldns_key_buf2rsa_raw(key, keylen))) { result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, evp_key, EVP_sha1()); } else { result = LDNS_STATUS_SSL_ERR; } EVP_PKEY_free(evp_key); return result; } ldns_status ldns_verify_rrsig_rsasha256_raw(unsigned char* sig, size_t siglen, ldns_buffer* rrset, unsigned char* key, size_t keylen) { #ifdef USE_SHA2 EVP_PKEY *evp_key; ldns_status result; evp_key = EVP_PKEY_new(); if (EVP_PKEY_assign_RSA(evp_key, ldns_key_buf2rsa_raw(key, keylen))) { result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, evp_key, EVP_sha256()); } else { result = LDNS_STATUS_SSL_ERR; } EVP_PKEY_free(evp_key); return result; #else /* touch these to prevent compiler warnings */ (void) sig; (void) siglen; (void) rrset; (void) key; (void) keylen; return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO; #endif } ldns_status ldns_verify_rrsig_rsasha512_raw(unsigned char* sig, size_t siglen, ldns_buffer* rrset, unsigned char* key, size_t keylen) { #ifdef USE_SHA2 EVP_PKEY *evp_key; ldns_status result; evp_key = EVP_PKEY_new(); if (EVP_PKEY_assign_RSA(evp_key, ldns_key_buf2rsa_raw(key, keylen))) { result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, evp_key, EVP_sha512()); } else { result = LDNS_STATUS_SSL_ERR; } EVP_PKEY_free(evp_key); return result; #else /* touch these to prevent compiler warnings */ (void) sig; (void) siglen; (void) rrset; (void) key; (void) keylen; return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO; #endif } ldns_status ldns_verify_rrsig_rsamd5_raw(unsigned char* sig, size_t siglen, ldns_buffer* rrset, unsigned char* key, size_t keylen) { EVP_PKEY *evp_key; ldns_status result; evp_key = EVP_PKEY_new(); if (EVP_PKEY_assign_RSA(evp_key, ldns_key_buf2rsa_raw(key, keylen))) { result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, evp_key, EVP_md5()); } else { result = LDNS_STATUS_SSL_ERR; } EVP_PKEY_free(evp_key); return result; } #endif