int main(int argc, char **argv) { size_t i; if(argc != 1) { usage(stderr); return 0; } /* read in */ ldns_rr_list *list = ldns_rr_list_new(); ldns_rdf *qname = 0; read_in(list, &qname, stdin); /* check covers */ covertests(list, qname); for(i=0; i<ldns_rr_list_rr_count(list); ++i) { ldns_rr* rr = ldns_rr_list_rr(list, i); if(!ldns_dname_is_subdomain(qname, ldns_rr_owner(rr))) { covertests(list, ldns_rr_owner(rr)); } } ldns_rr_list_deep_free(list); return 0; }
/* return a clone of the given rr list, without the glue records * rr list should be the complete zone * if present, stripped records are added to the list *glue_records */ ldns_rr_list * ldns_zone_strip_glue_rrs(const ldns_rdf *zone_name, const ldns_rr_list *rrs, ldns_rr_list *glue_rrs) { ldns_rr_list *new_list = ldns_rr_list_new(); /* when do we find glue? It means we find an IP address * (AAAA/A) for a nameserver listed in the zone * * Alg used here: * first find all the zonecuts (NS records) * find all the AAAA or A records (can be done it the * above loop). * * Check if the aaaa/a list are subdomains under the * NS domains. If yes -> glue, if no -> not glue */ ldns_rr_list *zone_cuts; ldns_rr_list *addr; ldns_rr *r, *ns, *a; ldns_rdf *dname_a, *dname_ns, *ns_owner; uint16_t i,j; zone_cuts = ldns_rr_list_new(); addr = ldns_rr_list_new(); for(i = 0; i < ldns_rr_list_rr_count(rrs); i++) { r = ldns_rr_list_rr(rrs, i); if (ldns_rr_get_type(r) == LDNS_RR_TYPE_A || ldns_rr_get_type(r) == LDNS_RR_TYPE_AAAA) { /* possibly glue */ ldns_rr_list_push_rr(addr, r); continue; } if (ldns_rr_get_type(r) == LDNS_RR_TYPE_NS) { /* multiple zones will end up here - * for now; not a problem */ /* don't add NS records for the current zone itself */ if (ldns_rdf_compare(ldns_rr_owner(r), zone_name) != 0) { ldns_rr_list_push_rr(zone_cuts, r); } continue; } } /* will sorting make it quicker ?? */ for(i = 0; i < ldns_rr_list_rr_count(zone_cuts); i++) { ns = ldns_rr_list_rr(zone_cuts, i); ns_owner = ldns_rr_owner(ns); dname_ns = ldns_rr_ns_nsdname(ns); for(j = 0; j < ldns_rr_list_rr_count(addr); j++) { a = ldns_rr_list_rr(addr, j); dname_a = ldns_rr_owner(a); if (ldns_dname_is_subdomain(dname_a, ns_owner) && ldns_rdf_compare(dname_ns, dname_a) == 0) { /* GLUE! */ if (glue_rrs) { ldns_rr_list_push_rr(glue_rrs, a); } break; } else { ldns_rr_list_push_rr(new_list, a); } } } ldns_rr_list_free(addr); ldns_rr_list_free(zone_cuts); return new_list; }
/** * Process RR. * */ static ods_status adapi_process_rr(zone_type* zone, ldns_rr* rr, int add, int backup) { ods_status status = ODS_STATUS_OK; uint32_t tmp = 0; ods_log_assert(rr); ods_log_assert(zone); ods_log_assert(zone->name); ods_log_assert(zone->db); ods_log_assert(zone->signconf); /* We only support IN class */ if (ldns_rr_get_class(rr) != LDNS_RR_CLASS_IN) { ods_log_warning("[%s] only class in is supported, changing class " "to in", adapi_str); ldns_rr_set_class(rr, LDNS_RR_CLASS_IN); } /* RR processing */ if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) { if (ldns_dname_compare(ldns_rr_owner(rr), zone->apex)) { ods_log_error("[%s] unable to %s rr to zone: soa record has " "invalid owner name", adapi_str, add?"add":"delete"); return ODS_STATUS_ERR; } status = adapi_process_soa(zone, rr, add, backup); if (status != ODS_STATUS_OK) { ods_log_error("[%s] unable to %s rr: failed to process soa " "record", adapi_str, add?"add":"delete"); return status; } } else { if (ldns_dname_compare(ldns_rr_owner(rr), zone->apex) && !ldns_dname_is_subdomain(ldns_rr_owner(rr), zone->apex)) { ods_log_warning("[%s] zone %s contains out-of-zone data, " "skipping", adapi_str, zone->name); return ODS_STATUS_UNCHANGED; } else if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_DNSKEY) { adapi_process_dnskey(zone, rr); } else if (util_is_dnssec_rr(rr) && !backup) { ods_log_warning("[%s] zone %s contains dnssec data (type=%u), " "skipping", adapi_str, zone->name, (unsigned) ldns_rr_get_type(rr)); return ODS_STATUS_UNCHANGED; } else if (zone->signconf->max_zone_ttl) { /* Convert MaxZoneTTL */ tmp = (uint32_t) duration2time(zone->signconf->max_zone_ttl); } } /* //MaxZoneTTL. Only set for RRtype != SOA && RRtype != DNSKEY */ if (tmp && tmp < ldns_rr_ttl(rr)) { char* str = ldns_rdf2str(ldns_rr_owner(rr)); if (str) { size_t i = 0; str[(strlen(str))-1] = '\0'; /* replace tabs with white space */ for (i=0; i < strlen(str); i++) { if (str[i] == '\t') { str[i] = ' '; } } ods_log_debug("[%s] capping ttl %u to MaxZoneTTL %u for rrset " "<%s,%s>", adapi_str, ldns_rr_ttl(rr), tmp, str, rrset_type2str(ldns_rr_get_type(rr))); } ldns_rr_set_ttl(rr, tmp); } /* TODO: DNAME and CNAME checks */ /* TODO: NS and DS checks */ if (add) { return zone_add_rr(zone, rr, 1); } else { return zone_del_rr(zone, rr, 1); } /* not reached */ return ODS_STATUS_ERR; }
/** * Marks the names in the zone that are occluded. Those names will be skipped * when walking the tree with the ldns_dnssec_name_node_next_nonglue() * function. But watch out! Names that are partially occluded (like glue with * the same name as the delegation) will not be marked and should specifically * be taken into account separately. * * When glue_list is given (not NULL), in the process of marking the names, all * glue resource records will be pushed to that list, even glue at delegation names. * * \param[in] zone the zone in which to mark the names * \param[in] glue_list the list to which to push the glue rrs * \return LDNS_STATUS_OK on success, an error code otherwise */ ldns_status ldns_dnssec_zone_mark_and_get_glue(ldns_dnssec_zone *zone, ldns_rr_list *glue_list) { ldns_rbnode_t *node; ldns_dnssec_name *name; ldns_rdf *owner; ldns_rdf *cut = NULL; /* keeps track of zone cuts */ /* When the cut is caused by a delegation, below_delegation will be 1. * When caused by a DNAME, below_delegation will be 0. */ int below_delegation = -1; /* init suppresses comiler warning */ ldns_status s; if (!zone || !zone->names) { return LDNS_STATUS_NULL; } for (node = ldns_rbtree_first(zone->names); node != LDNS_RBTREE_NULL; node = ldns_rbtree_next(node)) { name = (ldns_dnssec_name *) node->data; owner = ldns_dnssec_name_name(name); if (cut) { /* The previous node was a zone cut, or a subdomain * below a zone cut. Is this node (still) a subdomain * below the cut? Then the name is occluded. Unless * the name contains a SOA, after which we are * authoritative again. * * FIXME! If there are labels in between the SOA and * the cut, going from the authoritative space (below * the SOA) up into occluded space again, will not be * detected with the contruct below! */ if (ldns_dname_is_subdomain(owner, cut) && !ldns_dnssec_rrsets_contains_type( name->rrsets, LDNS_RR_TYPE_SOA)) { if (below_delegation && glue_list) { s = ldns_dnssec_addresses_on_glue_list( name->rrsets, glue_list); if (s != LDNS_STATUS_OK) { return s; } } name->is_glue = true; /* Mark occluded name! */ continue; } else { cut = NULL; } } /* The node is not below a zone cut. Is it a zone cut itself? * Everything below a SOA is authoritative of course; Except * when the name also contains a DNAME :). */ if (ldns_dnssec_rrsets_contains_type( name->rrsets, LDNS_RR_TYPE_NS) && !ldns_dnssec_rrsets_contains_type( name->rrsets, LDNS_RR_TYPE_SOA)) { cut = owner; below_delegation = 1; if (glue_list) { /* record glue on the zone cut */ s = ldns_dnssec_addresses_on_glue_list( name->rrsets, glue_list); if (s != LDNS_STATUS_OK) { return s; } } } else if (ldns_dnssec_rrsets_contains_type( name->rrsets, LDNS_RR_TYPE_DNAME)) { cut = owner; below_delegation = 0; } } return LDNS_STATUS_OK; }
/* finds entry in list, or returns NULL */ struct entry* find_match(struct entry* entries, ldns_pkt* query_pkt, enum transport_type transport) { struct entry* p = entries; ldns_pkt* reply = NULL; for(p=entries; p; p=p->next) { verbose(3, "comparepkt: "); reply = p->reply_list->reply; if(p->match_opcode && ldns_pkt_get_opcode(query_pkt) != ldns_pkt_get_opcode(reply)) { verbose(3, "bad opcode\n"); continue; } if(p->match_qtype && get_qtype(query_pkt) != get_qtype(reply)) { verbose(3, "bad qtype\n"); continue; } if(p->match_qname) { if(!get_owner(query_pkt) || !get_owner(reply) || ldns_dname_compare( get_owner(query_pkt), get_owner(reply)) != 0) { verbose(3, "bad qname\n"); continue; } } if(p->match_subdomain) { if(!get_owner(query_pkt) || !get_owner(reply) || (ldns_dname_compare(get_owner(query_pkt), get_owner(reply)) != 0 && !ldns_dname_is_subdomain( get_owner(query_pkt), get_owner(reply)))) { verbose(3, "bad subdomain\n"); continue; } } if(p->match_serial && get_serial(query_pkt) != p->ixfr_soa_serial) { verbose(3, "bad serial\n"); continue; } if(p->match_do && !ldns_pkt_edns_do(query_pkt)) { verbose(3, "no DO bit set\n"); continue; } if(p->match_noedns && ldns_pkt_edns(query_pkt)) { verbose(3, "bad; EDNS OPT present\n"); continue; } if(p->match_transport != transport_any && p->match_transport != transport) { verbose(3, "bad transport\n"); continue; } if(p->match_all && !match_all(query_pkt, reply, p->match_ttl)) { verbose(3, "bad allmatch\n"); continue; } verbose(3, "match!\n"); return p; } return NULL; }
int main(int argc, char **argv) { /* Local Vars */ int i; int soa_valid = 0; int ns_valid = 0; ldns_rdf *rd_domain; ldns_rdf *rd_trace; ldns_rdf *rd_cdomain; ldns_pkt *pkt; ldns_resolver *res; ldns_rr *rr; ldns_rr_list *rrl; ldns_rr_list *rrl_domain_soa; ldns_rr_list *rrl_domain_soa_rrsig; ldns_rr_list *rrl_domain_ns; ldns_rr_list *rrl_domain_ns_rrsig; ldns_rr_list *rrl_valid_keys; ldns_status status; /* Set signal handling and alarm */ if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) critical("Setup SIGALRM trap failed!"); /* Process check arguments */ if (process_arguments(argc, argv) != OK) unknown("Parsing arguments failed!"); /* Start plugin timeout */ alarm(mp_timeout); rd_domain = ldns_dname_new_frm_str(domainname); if (!rd_domain) unknown("Illegal domain name"); rd_trace = ldns_dname_new_frm_str(domaintrace); if (!rd_trace) unknown("Illegal trace domain name"); /* Check domain is subdomain from trace start */ if (!ldns_dname_is_subdomain(rd_domain, rd_trace)) { ldns_rr_list_deep_free(trusted_keys); ldns_rdf_deep_free(rd_domain); ldns_rdf_deep_free(rd_trace); unknown("'%s' is not a subdomain of '%s'.", domainname, domaintrace); } /* Add trusted keys for trace domain to rrl_valid_keys. */ rrl_valid_keys = ldns_rr_list_new(); for(i = 0; i < ldns_rr_list_rr_count(trusted_keys); i++) { rr = ldns_rr_list_rr(trusted_keys, i); if (ldns_dname_compare(ldns_rr_owner(rr),rd_trace) == 0) ldns_rr_list_push_rr(rrl_valid_keys, ldns_rr_clone(rr)); } ldns_rr_list_deep_free(trusted_keys); if (ldns_rr_list_rr_count(rrl_valid_keys) == 0) { ldns_rdf_deep_free(rd_domain); ldns_rdf_deep_free(rd_trace); ldns_rr_list_deep_free(rrl_valid_keys); critical("No trusted key for trace start '%s'", domaintrace?domaintrace:"."); } if (mp_verbose >= 2) { printf("--[ Trusted keys used ]-------------------------------------\n"); ldns_rr_list_sort(rrl_valid_keys); ldns_rr_list_print(stdout, rrl_valid_keys); printf("------------------------------------------------------------\n"); } /* create a new resolver with dns_server or server from /etc/resolv.conf */ res = createResolver(hostname); if (!res) { ldns_rdf_deep_free(rd_domain); ldns_rdf_deep_free(rd_trace); ldns_rr_list_deep_free(rrl_valid_keys); unknown("Creating resolver failed."); } resolverEnableDnssec(res); ldns_resolver_set_dnssec_anchors(res, rrl_valid_keys); /* check domain exists */ pkt = mp_ldns_resolver_query(res, rd_domain, LDNS_RR_TYPE_SOA, LDNS_RR_CLASS_IN, LDNS_RD); if (pkt == NULL || ldns_pkt_get_rcode(pkt) != LDNS_RCODE_NOERROR) { ldns_rdf_deep_free(rd_domain); ldns_rdf_deep_free(rd_trace); ldns_resolver_deep_free(res); if (pkt && ldns_pkt_get_rcode(pkt) == LDNS_RCODE_NXDOMAIN) { ldns_pkt_free(pkt); critical("Domain '%s' don't exist.", domainname); } ldns_pkt_free(pkt); critical("Unable to get SOA for %s.", domainname); } rrl_domain_soa = ldns_pkt_rr_list_by_name_and_type(pkt, rd_domain, LDNS_RR_TYPE_SOA, LDNS_SECTION_ANSWER); if (rrl_domain_soa == NULL || ldns_rr_list_rr_count(rrl_domain_soa) == 0) { ldns_rdf_deep_free(rd_domain); ldns_rdf_deep_free(rd_trace); ldns_resolver_deep_free(res); ldns_pkt_free(pkt); critical("Domain '%s' not found.", domainname); } rrl_domain_soa_rrsig = ldns_dnssec_pkt_get_rrsigs_for_name_and_type(pkt, rd_domain, LDNS_RR_TYPE_SOA); if (rrl_domain_soa_rrsig == NULL || ldns_rr_list_rr_count(rrl_domain_soa_rrsig) == 0) { free(domaintrace); ldns_rdf_deep_free(rd_domain); ldns_rdf_deep_free(rd_trace); ldns_resolver_deep_free(res); ldns_pkt_free(pkt); ldns_rr_list_deep_free(rrl_domain_soa); critical("Domain '%s' not signed.", domainname); } ldns_pkt_free(pkt); pkt = ldns_resolver_query(res, rd_domain, LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN, LDNS_RD); rrl_domain_ns = ldns_pkt_rr_list_by_name_and_type(pkt, rd_domain, LDNS_RR_TYPE_NS, LDNS_SECTION_ANSWER); rrl_domain_ns_rrsig = ldns_dnssec_pkt_get_rrsigs_for_name_and_type(pkt, rd_domain, LDNS_RR_TYPE_NS); ldns_pkt_free(pkt); if (mp_verbose >= 2) { printf("--[ Checked Domain ]----------------------------------------\n"); ldns_rr_list_print(stdout, rrl_domain_soa); printf("------------------------------------------------------------\n"); ldns_rr_list_print(stdout, rrl_domain_soa_rrsig); printf("------------------------------------------------------------\n"); ldns_rr_list_print(stdout, rrl_domain_ns); printf("------------------------------------------------------------\n"); ldns_rr_list_print(stdout, rrl_domain_ns_rrsig); printf("------------------------------------------------------------\n"); } /* create a new resolver with dns_server or server from /etc/resolv.conf */ ldns_resolver_free(res); res = createResolver(resolver); if (!res) { ldns_rdf_deep_free(rd_domain); ldns_rdf_deep_free(rd_trace); ldns_rr_list_deep_free(rrl_valid_keys); unknown("Creating resolver failed."); } resolverEnableDnssec(res); ldns_resolver_set_dnssec_anchors(res, rrl_valid_keys); /* Fetch valid keys from top down */ i = ldns_dname_label_count(rd_domain) - ldns_dname_label_count(rd_trace); for (; i>=0; i--) { rd_cdomain = ldns_dname_clone_from(rd_domain, i); if (mp_verbose) { char *str = ldns_rdf2str(rd_cdomain); printf("Trace: %s\n", str); free(str); } rrl = ldns_fetch_valid_domain_keys(res, rd_cdomain, rrl_valid_keys, &status); if (mp_verbose >= 2) { printf("--[ Valid Keys ]----------------------------------------\n"); ldns_rr_list_sort(rrl); ldns_rr_list_print(stdout, rrl); printf("------------------------------------------------------------\n"); } ldns_rr_list_cat(rrl_valid_keys, rrl); ldns_rr_list_free(rrl); ldns_rdf_deep_free(rd_cdomain); } ldns_rdf_deep_free(rd_trace); ldns_rdf_deep_free(rd_domain); /* Validate SOA */ for(i = 0; i < ldns_rr_list_rr_count(rrl_domain_soa_rrsig); i++) { rr = ldns_rr_list_rr(rrl_domain_soa_rrsig, i); status = ldns_verify_rrsig_keylist(rrl_domain_soa, rr, rrl_valid_keys, NULL); if (status == LDNS_STATUS_OK) soa_valid++; else if (mp_verbose > 0) fprintf(stderr, "ldns_verify_rrsig_keylist SOA failed: %s\n", ldns_get_errorstr_by_id(status)); } ldns_rr_list_deep_free(rrl_domain_soa); ldns_rr_list_deep_free(rrl_domain_soa_rrsig); if (soa_valid == 0) { critical("No valid Signatur for SOA of '%s'", domainname); free(domainname); free(domaintrace); ldns_resolver_deep_free(res); ldns_rr_list_deep_free(rrl_domain_ns); ldns_rr_list_deep_free(rrl_domain_ns_rrsig); return checkState; } /* Validate NS */ for(i = 0; i < ldns_rr_list_rr_count(rrl_domain_ns_rrsig); i++) { rr = ldns_rr_list_rr(rrl_domain_ns_rrsig, i); status = ldns_verify_rrsig_keylist(rrl_domain_ns, rr, rrl_valid_keys, NULL); if (status == LDNS_STATUS_OK) ns_valid++; else if (mp_verbose > 0) fprintf(stderr, "ldns_verify_rrsig_keylist NS failed: %s\n", ldns_get_errorstr_by_id(status)); } ldns_rr_list_deep_free(rrl_domain_ns); ldns_rr_list_deep_free(rrl_domain_ns_rrsig); ldns_resolver_deep_free(res); if (ns_valid == 0) { critical("No valid Signatur for NS of '%s'", domainname); free(domainname); free(domaintrace); return checkState; } ok("Trust for '%s' successfull traces from '%s'", domainname, domaintrace); free(domainname); free(domaintrace); return checkState; }