714 lines
18 KiB
C
714 lines
18 KiB
C
|
/*
|
||
|
* read a zone file from disk and prints it, one RR per line
|
||
|
*
|
||
|
* (c) NLnetLabs 2008
|
||
|
*
|
||
|
* See the file LICENSE for the license
|
||
|
*
|
||
|
* Missing from the checks: empty non-terminals
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
#include <unistd.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include <ldns/ldns.h>
|
||
|
|
||
|
#include <errno.h>
|
||
|
|
||
|
#ifdef HAVE_SSL
|
||
|
#include <openssl/err.h>
|
||
|
|
||
|
int verbosity = 3;
|
||
|
|
||
|
/* returns 1 if the list is empty, or if there are only ns rrs in the
|
||
|
* list, 0 otherwise */
|
||
|
static int
|
||
|
only_ns_in_rrsets(ldns_dnssec_rrsets *rrsets) {
|
||
|
ldns_dnssec_rrsets *cur_rrset = rrsets;
|
||
|
|
||
|
while (cur_rrset) {
|
||
|
if (cur_rrset->type != LDNS_RR_TYPE_NS) {
|
||
|
return 0;
|
||
|
}
|
||
|
cur_rrset = cur_rrset->next;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
zone_is_nsec3_optout(ldns_rbtree_t *zone_nodes)
|
||
|
{
|
||
|
/* simply find the first NSEC3 RR and check its flags */
|
||
|
/* TODO: maybe create a general function that uses the active
|
||
|
* NSEC3PARAM RR? */
|
||
|
ldns_rbnode_t *cur_node;
|
||
|
ldns_dnssec_name *cur_name;
|
||
|
cur_node = ldns_rbtree_first(zone_nodes);
|
||
|
while (cur_node != LDNS_RBTREE_NULL) {
|
||
|
cur_name = (ldns_dnssec_name *) cur_node->data;
|
||
|
if (cur_name && cur_name->nsec &&
|
||
|
ldns_rr_get_type(cur_name->nsec) == LDNS_RR_TYPE_NSEC3) {
|
||
|
if (ldns_nsec3_optout(cur_name->nsec)) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
cur_node = ldns_rbtree_next(cur_node);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
ldns_rr_list_contains_name(const ldns_rr_list *rr_list,
|
||
|
const ldns_rdf *name)
|
||
|
{
|
||
|
size_t i;
|
||
|
for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
|
||
|
if (ldns_dname_compare(name,
|
||
|
ldns_rr_owner(ldns_rr_list_rr(rr_list, i))) == 0) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
print_type(ldns_rr_type type)
|
||
|
{
|
||
|
const ldns_rr_descriptor *descriptor;
|
||
|
|
||
|
descriptor = ldns_rr_descript(type);
|
||
|
if (descriptor && descriptor->_name) {
|
||
|
fprintf(stdout, "%s", descriptor->_name);
|
||
|
} else {
|
||
|
fprintf(stdout, "TYPE%u",
|
||
|
type);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
static ldns_dnssec_zone *
|
||
|
create_dnssec_zone(ldns_zone *orig_zone)
|
||
|
{
|
||
|
size_t i;
|
||
|
ldns_dnssec_zone *dnssec_zone;
|
||
|
ldns_rr *cur_rr;
|
||
|
ldns_status status;
|
||
|
|
||
|
/* when reading NSEC3s, there is a chance that we encounter nsecs
|
||
|
for empty nonterminals, whose nonterminals we cannot derive yet
|
||
|
because the needed information is to be read later. in that case
|
||
|
we keep a list of those nsec3's and retry to add them later */
|
||
|
ldns_rr_list *failed_nsec3s = ldns_rr_list_new();
|
||
|
|
||
|
dnssec_zone = ldns_dnssec_zone_new();
|
||
|
if (ldns_dnssec_zone_add_rr(dnssec_zone, ldns_zone_soa(orig_zone)) !=
|
||
|
LDNS_STATUS_OK) {
|
||
|
if (verbosity > 0) {
|
||
|
fprintf(stderr,
|
||
|
"Error adding SOA to dnssec zone, skipping record\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(orig_zone)); i++) {
|
||
|
cur_rr = ldns_rr_list_rr(ldns_zone_rrs(orig_zone), i);
|
||
|
status = ldns_dnssec_zone_add_rr(dnssec_zone, cur_rr);
|
||
|
if (status != LDNS_STATUS_OK) {
|
||
|
if (status == LDNS_STATUS_DNSSEC_NSEC3_ORIGINAL_NOT_FOUND) {
|
||
|
ldns_rr_list_push_rr(failed_nsec3s, cur_rr);
|
||
|
} else {
|
||
|
if (verbosity > 0) {
|
||
|
fprintf(stderr, "Error adding RR to dnssec zone");
|
||
|
fprintf(stderr, ", skipping record:\n");
|
||
|
ldns_rr_print(stderr, cur_rr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ldns_rr_list_rr_count(failed_nsec3s) > 0) {
|
||
|
(void) ldns_dnssec_zone_add_empty_nonterminals(dnssec_zone);
|
||
|
for (i = 0; i < ldns_rr_list_rr_count(failed_nsec3s); i++) {
|
||
|
cur_rr = ldns_rr_list_rr(failed_nsec3s, i);
|
||
|
status = ldns_dnssec_zone_add_rr(dnssec_zone, cur_rr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ldns_rr_list_free(failed_nsec3s);
|
||
|
return dnssec_zone;
|
||
|
}
|
||
|
|
||
|
static ldns_status
|
||
|
verify_dnssec_rrset(ldns_rdf *zone_name,
|
||
|
ldns_rdf *name,
|
||
|
ldns_dnssec_rrsets *rrset,
|
||
|
ldns_rr_list *keys,
|
||
|
ldns_rr_list *glue_rrs)
|
||
|
{
|
||
|
ldns_rr_list *rrset_rrs;
|
||
|
ldns_dnssec_rrs *cur_rr, *cur_sig;
|
||
|
ldns_status status;
|
||
|
ldns_rr_list *good_keys;
|
||
|
ldns_status result = LDNS_STATUS_OK;
|
||
|
|
||
|
if (!rrset->rrs) return LDNS_STATUS_OK;
|
||
|
|
||
|
rrset_rrs = ldns_rr_list_new();
|
||
|
cur_rr = rrset->rrs;
|
||
|
while(cur_rr && cur_rr->rr) {
|
||
|
ldns_rr_list_push_rr(rrset_rrs, cur_rr->rr);
|
||
|
cur_rr = cur_rr->next;
|
||
|
}
|
||
|
cur_sig = rrset->signatures;
|
||
|
if (cur_sig) {
|
||
|
while (cur_sig) {
|
||
|
good_keys = ldns_rr_list_new();
|
||
|
status = ldns_verify_rrsig_keylist(rrset_rrs,
|
||
|
cur_sig->rr,
|
||
|
keys,
|
||
|
good_keys);
|
||
|
if (status != LDNS_STATUS_OK) {
|
||
|
if (verbosity > 0) {
|
||
|
printf("Error: %s",
|
||
|
ldns_get_errorstr_by_id(status));
|
||
|
printf(" for ");
|
||
|
ldns_rdf_print(stdout,
|
||
|
ldns_rr_owner(rrset->rrs->rr));
|
||
|
printf("\t");
|
||
|
print_type(rrset->type);
|
||
|
printf("\n");
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
result = status;
|
||
|
}
|
||
|
if (status == LDNS_STATUS_SSL_ERR) {
|
||
|
ERR_load_crypto_strings();
|
||
|
ERR_print_errors_fp(stdout);
|
||
|
}
|
||
|
if (verbosity >= 4) {
|
||
|
printf("RRSet:\n");
|
||
|
ldns_dnssec_rrs_print(stdout, rrset->rrs);
|
||
|
printf("Signature:\n");
|
||
|
ldns_rr_print(stdout, cur_sig->rr);
|
||
|
printf("\n");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ldns_rr_list_free(good_keys);
|
||
|
|
||
|
cur_sig = cur_sig->next;
|
||
|
}
|
||
|
} else {
|
||
|
/* delegations are unsigned */
|
||
|
if (rrset->type != LDNS_RR_TYPE_NS ||
|
||
|
ldns_dname_compare(name, zone_name) == 0) {
|
||
|
if (verbosity > 0) {
|
||
|
printf("Error: no signatures for ");
|
||
|
ldns_rdf_print(stdout, ldns_rr_owner(rrset->rrs->rr));
|
||
|
printf("\t");
|
||
|
print_type(rrset->type);
|
||
|
printf("\n");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ldns_rr_list_free(rrset_rrs);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static ldns_status
|
||
|
verify_single_rr(ldns_rr *rr,
|
||
|
ldns_dnssec_rrs *signature_rrs,
|
||
|
ldns_rr_list *keys)
|
||
|
{
|
||
|
ldns_rr_list *rrset_rrs;
|
||
|
ldns_rr_list *good_keys;
|
||
|
ldns_dnssec_rrs *cur_sig;
|
||
|
ldns_status status;
|
||
|
ldns_status result = LDNS_STATUS_OK;
|
||
|
|
||
|
rrset_rrs = ldns_rr_list_new();
|
||
|
ldns_rr_list_push_rr(rrset_rrs, rr);
|
||
|
|
||
|
cur_sig = signature_rrs;
|
||
|
while (cur_sig) {
|
||
|
good_keys = ldns_rr_list_new();
|
||
|
status = ldns_verify_rrsig_keylist(rrset_rrs,
|
||
|
cur_sig->rr,
|
||
|
keys,
|
||
|
good_keys);
|
||
|
if (status != LDNS_STATUS_OK) {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("Error: %s ", ldns_get_errorstr_by_id(status));
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
result = status;
|
||
|
}
|
||
|
printf("for ");
|
||
|
ldns_rdf_print(stdout, ldns_rr_owner(rr));
|
||
|
printf("\t");
|
||
|
print_type(ldns_rr_get_type(rr));
|
||
|
printf("\n");
|
||
|
if (status == LDNS_STATUS_SSL_ERR) {
|
||
|
ERR_load_crypto_strings();
|
||
|
ERR_print_errors_fp(stdout);
|
||
|
}
|
||
|
if (verbosity >= 4) {
|
||
|
printf("RRSet:\n");
|
||
|
ldns_rr_list_print(stdout, rrset_rrs);
|
||
|
printf("Signature:\n");
|
||
|
ldns_rr_print(stdout, cur_sig->rr);
|
||
|
printf("\n");
|
||
|
}
|
||
|
}
|
||
|
result = status;
|
||
|
}
|
||
|
ldns_rr_list_free(good_keys);
|
||
|
cur_sig = cur_sig->next;
|
||
|
}
|
||
|
|
||
|
ldns_rr_list_free(rrset_rrs);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static ldns_status
|
||
|
verify_next_hashed_name(ldns_rbtree_t *zone_nodes,
|
||
|
ldns_dnssec_name *name)
|
||
|
{
|
||
|
ldns_rbnode_t *next_node;
|
||
|
ldns_dnssec_name *next_name;
|
||
|
ldns_dnssec_name *cur_next_name = NULL;
|
||
|
ldns_dnssec_name *cur_first_name = NULL;
|
||
|
int cmp;
|
||
|
char *next_owner_str;
|
||
|
ldns_rdf *next_owner_dname;
|
||
|
|
||
|
if (!name->hashed_name) {
|
||
|
name->hashed_name = ldns_nsec3_hash_name_frm_nsec3(name->nsec,
|
||
|
name->name);
|
||
|
}
|
||
|
next_node = ldns_rbtree_first(zone_nodes);
|
||
|
while (next_node != LDNS_RBTREE_NULL) {
|
||
|
next_name = (ldns_dnssec_name *)next_node->data;
|
||
|
/* skip over names that have no NSEC3 records (whether it
|
||
|
* actually should or should not should have been checked
|
||
|
* already */
|
||
|
if (!next_name->nsec) {
|
||
|
next_node = ldns_rbtree_next(next_node);
|
||
|
continue;
|
||
|
}
|
||
|
if (!next_name->hashed_name) {
|
||
|
next_name->hashed_name = ldns_nsec3_hash_name_frm_nsec3(
|
||
|
name->nsec, next_name->name);
|
||
|
}
|
||
|
/* we keep track of what 'so far' is the next hashed name;
|
||
|
* it must of course be 'larger' than the current name
|
||
|
* if we find one that is larger, but smaller than what we
|
||
|
* previously thought was the next one, that one is the next
|
||
|
*/
|
||
|
cmp = ldns_dname_compare(name->hashed_name,
|
||
|
next_name->hashed_name);
|
||
|
if (cmp < 0) {
|
||
|
if (!cur_next_name) {
|
||
|
cur_next_name = next_name;
|
||
|
} else {
|
||
|
cmp = ldns_dname_compare(next_name->hashed_name,
|
||
|
cur_next_name->hashed_name);
|
||
|
if (cmp < 0) {
|
||
|
cur_next_name = next_name;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* in case the hashed name of the nsec we are checking is the
|
||
|
* last one, we need the first hashed name of the zone */
|
||
|
if (!cur_first_name) {
|
||
|
cur_first_name = next_name;
|
||
|
} else {
|
||
|
cmp = ldns_dname_compare(next_name->hashed_name,
|
||
|
cur_first_name->hashed_name);
|
||
|
if (cmp < 0) {
|
||
|
cur_first_name = next_name;
|
||
|
}
|
||
|
}
|
||
|
next_node = ldns_rbtree_next(next_node);
|
||
|
}
|
||
|
if (!cur_next_name) {
|
||
|
cur_next_name = cur_first_name;
|
||
|
}
|
||
|
|
||
|
next_owner_str = ldns_rdf2str(ldns_nsec3_next_owner(name->nsec));
|
||
|
next_owner_dname = ldns_dname_new_frm_str(next_owner_str);
|
||
|
cmp = ldns_dname_compare(next_owner_dname,
|
||
|
cur_next_name->hashed_name);
|
||
|
ldns_rdf_deep_free(next_owner_dname);
|
||
|
LDNS_FREE(next_owner_str);
|
||
|
if (cmp != 0) {
|
||
|
printf("Error: The NSEC3 record for ");
|
||
|
ldns_rdf_print(stdout, name->name);
|
||
|
printf(" points to the wrong next hashed owner name\n");
|
||
|
printf("(should point to ");
|
||
|
ldns_rdf_print(stdout, cur_next_name->name);
|
||
|
printf("(whose hashed name is ");
|
||
|
ldns_rdf_print(stdout, cur_next_name->hashed_name);
|
||
|
printf(")\n");
|
||
|
return LDNS_STATUS_ERR;
|
||
|
} else {
|
||
|
return LDNS_STATUS_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static ldns_rbnode_t *
|
||
|
next_nonglue_node(ldns_rbnode_t *node, ldns_rr_list *glue_rrs)
|
||
|
{
|
||
|
ldns_rbnode_t *cur_node = ldns_rbtree_next(node);
|
||
|
ldns_dnssec_name *cur_name;
|
||
|
while (cur_node != LDNS_RBTREE_NULL) {
|
||
|
cur_name = (ldns_dnssec_name *) cur_node->data;
|
||
|
if (cur_name && cur_name->name) {
|
||
|
if (!ldns_rr_list_contains_name(glue_rrs, cur_name->name)) {
|
||
|
return cur_node;
|
||
|
}
|
||
|
}
|
||
|
cur_node = ldns_rbtree_next(cur_node);
|
||
|
}
|
||
|
return LDNS_RBTREE_NULL;
|
||
|
}
|
||
|
|
||
|
static ldns_status
|
||
|
verify_nsec(ldns_rbtree_t *zone_nodes,
|
||
|
ldns_rbnode_t *cur_node,
|
||
|
ldns_rr_list *keys,
|
||
|
ldns_rr_list *glue_rrs
|
||
|
)
|
||
|
{
|
||
|
ldns_rbnode_t *next_node;
|
||
|
ldns_dnssec_name *name, *next_name;
|
||
|
ldns_status status, result;
|
||
|
result = LDNS_STATUS_OK;
|
||
|
|
||
|
name = (ldns_dnssec_name *) cur_node->data;
|
||
|
if (name->nsec) {
|
||
|
if (name->nsec_signatures) {
|
||
|
status = verify_single_rr(name->nsec,
|
||
|
name->nsec_signatures,
|
||
|
keys);
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
result = status;
|
||
|
}
|
||
|
} else {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("Error: the NSEC(3) record of ");
|
||
|
ldns_rdf_print(stdout, name->name);
|
||
|
printf(" has no signatures\n");
|
||
|
}
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
result = LDNS_STATUS_ERR;
|
||
|
}
|
||
|
}
|
||
|
/* check whether the NSEC record points to the right name */
|
||
|
switch (ldns_rr_get_type(name->nsec)) {
|
||
|
case LDNS_RR_TYPE_NSEC:
|
||
|
/* simply try next name */
|
||
|
next_node = next_nonglue_node(cur_node, glue_rrs);
|
||
|
if (next_node == LDNS_RBTREE_NULL) {
|
||
|
next_node = ldns_rbtree_first(zone_nodes);
|
||
|
}
|
||
|
next_name = (ldns_dnssec_name *)next_node->data;
|
||
|
if (ldns_dname_compare(next_name->name,
|
||
|
ldns_rr_rdf(name->nsec, 0))
|
||
|
!= 0) {
|
||
|
printf("Error: the NSEC record for ");
|
||
|
ldns_rdf_print(stdout, name->name);
|
||
|
printf(" points to the wrong next owner name\n");
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
result = LDNS_STATUS_ERR;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case LDNS_RR_TYPE_NSEC3:
|
||
|
/* find the hashed next name in the tree */
|
||
|
/* this is expensive, do we need to add support
|
||
|
* for this in the structs? (ie. pointer to next
|
||
|
* hashed name?)
|
||
|
*/
|
||
|
status = verify_next_hashed_name(zone_nodes, name);
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
result = status;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
/* todo; do this once and cache result? */
|
||
|
if (zone_is_nsec3_optout(zone_nodes) &&
|
||
|
only_ns_in_rrsets(name->rrsets)) {
|
||
|
/* ok, no problem, but we need to remember to check
|
||
|
* whether the chain does not actually point to this
|
||
|
* name later */
|
||
|
} else {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("Error: there is no NSEC(3) for ");
|
||
|
ldns_rdf_print(stdout, name->name);
|
||
|
printf("\n");
|
||
|
}
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
result = LDNS_STATUS_ERR;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
ldns_dnssec_name_has_only_a(ldns_dnssec_name *cur_name)
|
||
|
{
|
||
|
ldns_dnssec_rrsets *cur_rrset;
|
||
|
cur_rrset = cur_name->rrsets;
|
||
|
while (cur_rrset) {
|
||
|
if (cur_rrset->type != LDNS_RR_TYPE_A &&
|
||
|
cur_rrset->type != LDNS_RR_TYPE_AAAA) {
|
||
|
return 0;
|
||
|
} else {
|
||
|
cur_rrset = cur_rrset->next;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static ldns_status
|
||
|
verify_dnssec_name(ldns_rdf *zone_name,
|
||
|
ldns_dnssec_zone *zone,
|
||
|
ldns_rbtree_t *zone_nodes,
|
||
|
ldns_rbnode_t *cur_node,
|
||
|
ldns_rr_list *keys,
|
||
|
ldns_rr_list *glue_rrs)
|
||
|
{
|
||
|
ldns_status result = LDNS_STATUS_OK;
|
||
|
ldns_status status;
|
||
|
ldns_dnssec_rrsets *cur_rrset;
|
||
|
ldns_dnssec_name *name;
|
||
|
/* for NSEC chain checks */
|
||
|
|
||
|
name = (ldns_dnssec_name *) cur_node->data;
|
||
|
if (verbosity >= 3) {
|
||
|
printf("Checking: ");
|
||
|
ldns_rdf_print(stdout, name->name);
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
if (ldns_rr_list_contains_name(glue_rrs, name->name) &&
|
||
|
ldns_dnssec_name_has_only_a(name)
|
||
|
) {
|
||
|
/* glue */
|
||
|
cur_rrset = name->rrsets;
|
||
|
while (cur_rrset) {
|
||
|
if (cur_rrset->signatures) {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("Error: ");
|
||
|
ldns_rdf_print(stdout, name->name);
|
||
|
printf("\t");
|
||
|
print_type(cur_rrset->type);
|
||
|
printf(" has signature(s), but is glue\n");
|
||
|
}
|
||
|
result = LDNS_STATUS_ERR;
|
||
|
}
|
||
|
cur_rrset = cur_rrset->next;
|
||
|
}
|
||
|
if (name->nsec) {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("Error: ");
|
||
|
ldns_rdf_print(stdout, name->name);
|
||
|
printf("\thas an NSEC(3), but is glue\n");
|
||
|
}
|
||
|
result = LDNS_STATUS_ERR;
|
||
|
}
|
||
|
} else {
|
||
|
/* not glue, do real verify */
|
||
|
cur_rrset = name->rrsets;
|
||
|
while(cur_rrset) {
|
||
|
if (cur_rrset->type != LDNS_RR_TYPE_A ||
|
||
|
!ldns_dnssec_zone_find_rrset(zone, name->name, LDNS_RR_TYPE_NS)) {
|
||
|
status = verify_dnssec_rrset(zone_name, name->name, cur_rrset, keys, glue_rrs);
|
||
|
if (status != LDNS_STATUS_OK && result == LDNS_STATUS_OK) {
|
||
|
result = status;
|
||
|
}
|
||
|
}
|
||
|
cur_rrset = cur_rrset->next;
|
||
|
}
|
||
|
|
||
|
status = verify_nsec(zone_nodes, cur_node, keys, glue_rrs);
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
result = status;
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static ldns_status
|
||
|
verify_dnssec_zone(ldns_dnssec_zone *dnssec_zone,
|
||
|
ldns_rdf *zone_name,
|
||
|
ldns_rr_list *glue_rrs)
|
||
|
{
|
||
|
ldns_rr_list *keys;
|
||
|
ldns_rbnode_t *cur_node;
|
||
|
ldns_dnssec_rrsets *cur_key_rrset;
|
||
|
ldns_dnssec_rrs *cur_key;
|
||
|
ldns_dnssec_name *cur_name;
|
||
|
ldns_status status;
|
||
|
ldns_status result = LDNS_STATUS_OK;
|
||
|
|
||
|
keys = ldns_rr_list_new();
|
||
|
cur_key_rrset = ldns_dnssec_zone_find_rrset(dnssec_zone,
|
||
|
zone_name,
|
||
|
LDNS_RR_TYPE_DNSKEY);
|
||
|
if (!cur_key_rrset || !cur_key_rrset->rrs) {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("No DNSKEY records at zone apex\n");
|
||
|
}
|
||
|
result = LDNS_STATUS_ERR;
|
||
|
} else {
|
||
|
cur_key = cur_key_rrset->rrs;
|
||
|
while (cur_key) {
|
||
|
if (verbosity >= 4) {
|
||
|
printf("DNSKEY: ");
|
||
|
ldns_rr_print(stdout, cur_key->rr);
|
||
|
}
|
||
|
ldns_rr_list_push_rr(keys, cur_key->rr);
|
||
|
cur_key = cur_key->next;
|
||
|
}
|
||
|
|
||
|
cur_node = ldns_rbtree_first(dnssec_zone->names);
|
||
|
if (cur_node == LDNS_RBTREE_NULL) {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("Empty zone?\n");
|
||
|
}
|
||
|
result = LDNS_STATUS_ERR;
|
||
|
}
|
||
|
while (cur_node != LDNS_RBTREE_NULL) {
|
||
|
cur_name = (ldns_dnssec_name *) cur_node->data;
|
||
|
status = verify_dnssec_name(zone_name,
|
||
|
dnssec_zone,
|
||
|
dnssec_zone->names,
|
||
|
cur_node,
|
||
|
keys,
|
||
|
glue_rrs);
|
||
|
if (status != LDNS_STATUS_OK && result == LDNS_STATUS_OK) {
|
||
|
result = status;
|
||
|
}
|
||
|
cur_node = ldns_rbtree_next(cur_node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ldns_rr_list_free(keys);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
char *filename;
|
||
|
FILE *fp;
|
||
|
ldns_zone *z;
|
||
|
int line_nr = 0;
|
||
|
int c;
|
||
|
ldns_status s;
|
||
|
ldns_dnssec_zone *dnssec_zone;
|
||
|
ldns_status result = LDNS_STATUS_ERR;
|
||
|
ldns_rr_list *glue_rrs;
|
||
|
|
||
|
while ((c = getopt(argc, argv, "hvV:")) != -1) {
|
||
|
switch(c) {
|
||
|
case 'h':
|
||
|
printf("Usage: %s [OPTIONS] <zonefile>\n", argv[0]);
|
||
|
printf("\tReads the zonefile and checks for DNSSEC errors.\n");
|
||
|
printf("\nIt checks whether NSEC(3)s are present,");
|
||
|
printf(" and verifies all signatures\n");
|
||
|
printf("It also checks the NSEC(3) chain, but it will error on opted-out delegations\n");
|
||
|
printf("\nOPTIONS:\n");
|
||
|
printf("\t-h show this text\n");
|
||
|
printf("\t-v shows the version and exits\n");
|
||
|
printf("\t-V [0-5]\tset verbosity level (default 3)\n");
|
||
|
printf("\nif no file is given standard input is read\n");
|
||
|
exit(EXIT_SUCCESS);
|
||
|
break;
|
||
|
case 'v':
|
||
|
printf("read zone version %s (ldns version %s)\n",
|
||
|
LDNS_VERSION, ldns_version());
|
||
|
exit(EXIT_SUCCESS);
|
||
|
break;
|
||
|
case 'V':
|
||
|
verbosity = atoi(optarg);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
argc -= optind;
|
||
|
argv += optind;
|
||
|
|
||
|
if (argc == 0) {
|
||
|
fp = stdin;
|
||
|
} else {
|
||
|
filename = argv[0];
|
||
|
|
||
|
fp = fopen(filename, "r");
|
||
|
if (!fp) {
|
||
|
fprintf(stderr,
|
||
|
"Unable to open %s: %s\n",
|
||
|
filename,
|
||
|
strerror(errno));
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
s = ldns_zone_new_frm_fp_l(&z, fp, NULL, 0, LDNS_RR_CLASS_IN, &line_nr);
|
||
|
|
||
|
if (s == LDNS_STATUS_OK) {
|
||
|
if (!ldns_zone_soa(z)) {
|
||
|
fprintf(stderr, "; Error: no SOA in the zone\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
glue_rrs = ldns_zone_glue_rr_list(z);
|
||
|
dnssec_zone = create_dnssec_zone(z);
|
||
|
|
||
|
if (verbosity >= 5) {
|
||
|
ldns_dnssec_zone_print(stdout, dnssec_zone);
|
||
|
}
|
||
|
|
||
|
result = verify_dnssec_zone(dnssec_zone,
|
||
|
ldns_rr_owner(ldns_zone_soa(z)),
|
||
|
glue_rrs);
|
||
|
|
||
|
|
||
|
if (result == LDNS_STATUS_OK) {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("Zone is verified and complete\n");
|
||
|
}
|
||
|
} else {
|
||
|
if (verbosity >= 1) {
|
||
|
printf("There were errors in the zone\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ldns_zone_free(z);
|
||
|
ldns_dnssec_zone_deep_free(dnssec_zone);
|
||
|
} else {
|
||
|
fprintf(stderr, "%s at %d\n",
|
||
|
ldns_get_errorstr_by_id(s),
|
||
|
line_nr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
fclose(fp);
|
||
|
|
||
|
exit(result);
|
||
|
}
|
||
|
#else
|
||
|
int
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
fprintf(stderr, "ldns-verifyzone needs OpenSSL support, which has not been compiled in\n");
|
||
|
return 1;
|
||
|
}
|
||
|
#endif /* HAVE_SSL */
|