diff options
author | markm <markm@FreeBSD.org> | 2013-08-23 07:39:32 +0000 |
---|---|---|
committer | markm <markm@FreeBSD.org> | 2013-08-23 07:39:32 +0000 |
commit | 7f2ed01ffff8c77447d4868a6d43851fd9380c47 (patch) | |
tree | 60c569e471154195770e3119302c3c7a03686914 /contrib/bind9/bin/dnssec/dnssectool.c | |
parent | ebd919f034b5492d5d2693c8cf79e707b25a52bb (diff) | |
parent | 7b6ce3ea58e65e172ca1ad959eccacbed50fab30 (diff) | |
download | FreeBSD-src-7f2ed01ffff8c77447d4868a6d43851fd9380c47.zip FreeBSD-src-7f2ed01ffff8c77447d4868a6d43851fd9380c47.tar.gz |
MFC
Diffstat (limited to 'contrib/bind9/bin/dnssec/dnssectool.c')
-rw-r--r-- | contrib/bind9/bin/dnssec/dnssectool.c | 1336 |
1 files changed, 1334 insertions, 2 deletions
diff --git a/contrib/bind9/bin/dnssec/dnssectool.c b/contrib/bind9/bin/dnssec/dnssectool.c index 882b042..7c8c6ce 100644 --- a/contrib/bind9/bin/dnssec/dnssectool.c +++ b/contrib/bind9/bin/dnssec/dnssectool.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009-2011 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnssectool.c,v 1.60.162.3 2011/10/21 03:56:32 marka Exp $ */ +/* $Id: dnssectool.c,v 1.63 2011/10/21 03:55:33 marka Exp $ */ /*! \file */ @@ -27,9 +27,11 @@ #include <stdlib.h> +#include <isc/base32.h> #include <isc/buffer.h> #include <isc/dir.h> #include <isc/entropy.h> +#include <isc/heap.h> #include <isc/list.h> #include <isc/mem.h> #include <isc/string.h> @@ -37,12 +39,19 @@ #include <isc/util.h> #include <isc/print.h> +#include <dns/db.h> +#include <dns/dbiterator.h> #include <dns/dnssec.h> +#include <dns/fixedname.h> #include <dns/keyvalues.h> #include <dns/log.h> #include <dns/name.h> +#include <dns/nsec.h> +#include <dns/nsec3.h> #include <dns/rdatastruct.h> #include <dns/rdataclass.h> +#include <dns/rdataset.h> +#include <dns/rdatasetiter.h> #include <dns/rdatatype.h> #include <dns/result.h> #include <dns/secalg.h> @@ -50,6 +59,18 @@ #include "dnssectool.h" +static isc_heap_t *expected_chains, *found_chains; + +struct nsec3_chain_fixed { + isc_uint8_t hash; + isc_uint8_t salt_length; + isc_uint8_t next_length; + isc_uint16_t iterations; + /* unsigned char salt[0]; */ + /* unsigned char owner[0]; */ + /* unsigned char next[0]; */ +}; + extern int verbose; extern const char *program; @@ -467,3 +488,1314 @@ key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir, return (conflict); } + +isc_boolean_t +is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp) +{ + dns_rdataset_t nsset; + isc_result_t result; + + if (dns_name_equal(name, origin)) + return (ISC_FALSE); + + dns_rdataset_init(&nsset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns, + 0, 0, &nsset, NULL); + if (dns_rdataset_isassociated(&nsset)) { + if (ttlp != NULL) + *ttlp = nsset.ttl; + dns_rdataset_disassociate(&nsset); + } + + return (ISC_TF(result == ISC_R_SUCCESS)); +} + +static isc_boolean_t +goodsig(dns_name_t *origin, dns_rdata_t *sigrdata, dns_name_t *name, + dns_rdataset_t *keyrdataset, dns_rdataset_t *rdataset, isc_mem_t *mctx) +{ + dns_rdata_dnskey_t key; + dns_rdata_rrsig_t sig; + dst_key_t *dstkey = NULL; + isc_result_t result; + + result = dns_rdata_tostruct(sigrdata, &sig, NULL); + check_result(result, "dns_rdata_tostruct()"); + + for (result = dns_rdataset_first(keyrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(keyrdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(keyrdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &key, NULL); + check_result(result, "dns_rdata_tostruct()"); + result = dns_dnssec_keyfromrdata(origin, &rdata, mctx, + &dstkey); + if (result != ISC_R_SUCCESS) + return (ISC_FALSE); + if (sig.algorithm != key.algorithm || + sig.keyid != dst_key_id(dstkey) || + !dns_name_equal(&sig.signer, origin)) { + dst_key_free(&dstkey); + continue; + } + result = dns_dnssec_verify(name, rdataset, dstkey, ISC_FALSE, + mctx, sigrdata); + dst_key_free(&dstkey); + if (result == ISC_R_SUCCESS) + return(ISC_TRUE); + } + return (ISC_FALSE); +} + +static isc_result_t +verifynsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_dbnode_t *node, dns_name_t *nextname) +{ + unsigned char buffer[DNS_NSEC_BUFFERSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char nextbuf[DNS_NAME_FORMATSIZE]; + char found[DNS_NAME_FORMATSIZE]; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_t tmprdata = DNS_RDATA_INIT; + dns_rdata_nsec_t nsec; + isc_result_t result; + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Missing NSEC record for %s\n", namebuf); + goto failure; + } + + result = dns_rdataset_first(&rdataset); + check_result(result, "dns_rdataset_first()"); + + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec, NULL); + check_result(result, "dns_rdata_tostruct()"); + /* Check bit next name is consistent */ + if (!dns_name_equal(&nsec.next, nextname)) { + dns_name_format(name, namebuf, sizeof(namebuf)); + dns_name_format(nextname, nextbuf, sizeof(nextbuf)); + dns_name_format(&nsec.next, found, sizeof(found)); + fprintf(stderr, "Bad NSEC record for %s, next name " + "mismatch (expected:%s, found:%s)\n", namebuf, + nextbuf, found); + goto failure; + } + /* Check bit map is consistent */ + result = dns_nsec_buildrdata(db, ver, node, nextname, buffer, + &tmprdata); + check_result(result, "dns_nsec_buildrdata()"); + if (dns_rdata_compare(&rdata, &tmprdata) != 0) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Bad NSEC record for %s, bit map " + "mismatch\n", namebuf); + goto failure; + } + result = dns_rdataset_next(&rdataset); + if (result != ISC_R_NOMORE) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Multipe NSEC records for %s\n", namebuf); + goto failure; + + } + dns_rdataset_disassociate(&rdataset); + return (ISC_R_SUCCESS); + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + return (ISC_R_FAILURE); +} + +static void +check_no_rrsig(dns_db_t *db, dns_dbversion_t *ver, dns_rdataset_t *rdataset, + dns_name_t *name, dns_dbnode_t *node) +{ + char namebuf[DNS_NAME_FORMATSIZE]; + char typebuf[80]; + dns_rdataset_t sigrdataset; + dns_rdatasetiter_t *rdsiter = NULL; + isc_result_t result; + + dns_rdataset_init(&sigrdataset); + result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) { + dns_rdatasetiter_current(rdsiter, &sigrdataset); + if (sigrdataset.type == dns_rdatatype_rrsig && + sigrdataset.covers == rdataset->type) + break; + dns_rdataset_disassociate(&sigrdataset); + } + if (result == ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + type_format(rdataset->type, typebuf, sizeof(typebuf)); + fprintf(stderr, "Warning: Found unexpected signatures for " + "%s/%s\n", namebuf, typebuf); + } + if (dns_rdataset_isassociated(&sigrdataset)) + dns_rdataset_disassociate(&sigrdataset); + dns_rdatasetiter_destroy(&rdsiter); +} + +static isc_boolean_t +chain_compare(void *arg1, void *arg2) { + struct nsec3_chain_fixed *e1 = arg1, *e2 = arg2; + size_t len; + + /* + * Do each element in turn to get a stable sort. + */ + if (e1->hash < e2->hash) + return (ISC_TRUE); + if (e1->hash > e2->hash) + return (ISC_FALSE); + if (e1->iterations < e2->iterations) + return (ISC_TRUE); + if (e1->iterations > e2->iterations) + return (ISC_FALSE); + if (e1->salt_length < e2->salt_length) + return (ISC_TRUE); + if (e1->salt_length > e2->salt_length) + return (ISC_FALSE); + if (e1->next_length < e2->next_length) + return (ISC_TRUE); + if (e1->next_length > e2->next_length) + return (ISC_FALSE); + len = e1->salt_length + 2 * e1->next_length; + if (memcmp(e1 + 1, e2 + 1, len) < 0) + return (ISC_TRUE); + return (ISC_FALSE); +} + +static isc_boolean_t +chain_equal(struct nsec3_chain_fixed *e1, struct nsec3_chain_fixed *e2) { + size_t len; + + if (e1->hash != e2->hash) + return (ISC_FALSE); + if (e1->iterations != e2->iterations) + return (ISC_FALSE); + if (e1->salt_length != e2->salt_length) + return (ISC_FALSE); + if (e1->next_length != e2->next_length) + return (ISC_FALSE); + len = e1->salt_length + 2 * e1->next_length; + if (memcmp(e1 + 1, e2 + 1, len) != 0) + return (ISC_FALSE); + return (ISC_TRUE); +} + +static isc_result_t +record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3, + isc_mem_t *mctx, isc_heap_t *chains) +{ + struct nsec3_chain_fixed *element; + size_t len; + unsigned char *cp; + isc_result_t result; + + len = sizeof(*element) + nsec3->next_length * 2 + nsec3->salt_length; + + element = isc_mem_get(mctx, len); + if (element == NULL) + return (ISC_R_NOMEMORY); + memset(element, 0, len); + element->hash = nsec3->hash; + element->salt_length = nsec3->salt_length; + element->next_length = nsec3->next_length; + element->iterations = nsec3->iterations; + cp = (unsigned char *)(element + 1); + memcpy(cp, nsec3->salt, nsec3->salt_length); + cp += nsec3->salt_length; + memcpy(cp, rawhash, nsec3->next_length); + cp += nsec3->next_length; + memcpy(cp, nsec3->next, nsec3->next_length); + result = isc_heap_insert(chains, element); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "isc_heap_insert failed: %s\n", + isc_result_totext(result)); + isc_mem_put(mctx, element, len); + } + return (result); +} + +static isc_result_t +match_nsec3(dns_name_t *name, isc_mem_t *mctx, + dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset, + unsigned char types[8192], unsigned int maxtype, + unsigned char *rawhash, size_t rhsize) +{ + unsigned char cbm[8244]; + char namebuf[DNS_NAME_FORMATSIZE]; + dns_rdata_nsec3_t nsec3; + isc_result_t result; + unsigned int len; + + /* + * Find matching NSEC3 record. + */ + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (nsec3.hash == nsec3param->hash && + nsec3.next_length == rhsize && + nsec3.iterations == nsec3param->iterations && + nsec3.salt_length == nsec3param->salt_length && + memcmp(nsec3.salt, nsec3param->salt, + nsec3param->salt_length) == 0) + break; + } + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Missing NSEC3 record for %s\n", namebuf); + return (result); + } + + /* + * Check the type list. + */ + len = dns_nsec_compressbitmap(cbm, types, maxtype); + if (nsec3.len != len || memcmp(cbm, nsec3.typebits, len) != 0) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Bad NSEC3 record for %s, bit map " + "mismatch\n", namebuf); + return (ISC_R_FAILURE); + } + + /* + * Record chain. + */ + result = record_nsec3(rawhash, &nsec3, mctx, expected_chains); + check_result(result, "record_nsec3()"); + + /* + * Make sure there is only one NSEC3 record with this set of + * parameters. + */ + for (result = dns_rdataset_next(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (nsec3.hash == nsec3param->hash && + nsec3.iterations == nsec3param->iterations && + nsec3.salt_length == nsec3param->salt_length && + memcmp(nsec3.salt, nsec3param->salt, + nsec3.salt_length) == 0) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Multiple NSEC3 records with the " + "same parameter set for %s", namebuf); + result = DNS_R_DUPLICATE; + break; + } + } + if (result != ISC_R_NOMORE) + return (result); + + result = ISC_R_SUCCESS; + return (result); +} + +static isc_boolean_t +innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { + dns_rdata_nsec3param_t nsec3param; + isc_result_t result; + + for (result = dns_rdataset_first(nsec3paramset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(nsec3paramset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(nsec3paramset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3param, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (nsec3param.flags == 0 && + nsec3param.hash == nsec3->hash && + nsec3param.iterations == nsec3->iterations && + nsec3param.salt_length == nsec3->salt_length && + memcmp(nsec3param.salt, nsec3->salt, + nsec3->salt_length) == 0) + return (ISC_TRUE); + } + return (ISC_FALSE); +} + +static isc_result_t +record_found(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, + dns_name_t *name, dns_dbnode_t *node, + dns_rdataset_t *nsec3paramset) +{ + unsigned char owner[NSEC3_MAX_HASH_LENGTH]; + dns_rdata_nsec3_t nsec3; + dns_rdataset_t rdataset; + dns_label_t hashlabel; + isc_buffer_t b; + isc_result_t result; + + if (nsec3paramset == NULL || !dns_rdataset_isassociated(nsec3paramset)) + return (ISC_R_SUCCESS); + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + + dns_name_getlabel(name, 0, &hashlabel); + isc_region_consume(&hashlabel, 1); + isc_buffer_init(&b, owner, sizeof(owner)); + result = isc_base32hex_decoderegion(&hashlabel, &b); + if (result != ISC_R_SUCCESS) + goto cleanup; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (nsec3.next_length != isc_buffer_usedlength(&b)) + continue; + /* + * We only care about NSEC3 records that match a NSEC3PARAM + * record. + */ + if (!innsec3params(&nsec3, nsec3paramset)) + continue; + + /* + * Record chain. + */ + result = record_nsec3(owner, &nsec3, mctx, found_chains); + check_result(result, "record_nsec3()"); + } + + cleanup: + dns_rdataset_disassociate(&rdataset); + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +isoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + dns_rdata_t *nsec3rdata) +{ + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_nsec3_t nsec3; + dns_rdata_nsec3param_t nsec3param; + dns_fixedname_t fixed; + dns_name_t *hashname; + isc_result_t result; + dns_dbnode_t *node = NULL; + unsigned char rawhash[NSEC3_MAX_HASH_LENGTH]; + size_t rhsize = sizeof(rawhash); + isc_boolean_t ret; + + result = dns_rdata_tostruct(nsec3rdata, &nsec3param, NULL); + check_result(result, "dns_rdata_tostruct()"); + + dns_fixedname_init(&fixed); + result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, origin, origin, + nsec3param.hash, nsec3param.iterations, + nsec3param.salt, nsec3param.salt_length); + check_result(result, "dns_nsec3_hashname()"); + + dns_rdataset_init(&rdataset); + hashname = dns_fixedname_name(&fixed); + result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node); + if (result == ISC_R_SUCCESS) + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + return (ISC_FALSE); + + result = dns_rdataset_first(&rdataset); + check_result(result, "dns_rdataset_first()"); + + dns_rdataset_current(&rdataset, &rdata); + + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + if (result != ISC_R_SUCCESS) + ret = ISC_FALSE; + else + ret = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0); + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + + return (ret); +} + +static isc_result_t +verifynsec3(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_name_t *name, dns_rdata_t *rdata, + isc_boolean_t delegation, isc_boolean_t empty, + unsigned char types[8192], unsigned int maxtype) +{ + char namebuf[DNS_NAME_FORMATSIZE]; + char hashbuf[DNS_NAME_FORMATSIZE]; + dns_rdataset_t rdataset; + dns_rdata_nsec3param_t nsec3param; + dns_fixedname_t fixed; + dns_name_t *hashname; + isc_result_t result; + dns_dbnode_t *node = NULL; + unsigned char rawhash[NSEC3_MAX_HASH_LENGTH]; + size_t rhsize = sizeof(rawhash); + isc_boolean_t optout; + + result = dns_rdata_tostruct(rdata, &nsec3param, NULL); + check_result(result, "dns_rdata_tostruct()"); + + if (nsec3param.flags != 0) + return (ISC_R_SUCCESS); + + if (!dns_nsec3_supportedhash(nsec3param.hash)) + return (ISC_R_SUCCESS); + + optout = isoptout(db, ver, origin, rdata); + + dns_fixedname_init(&fixed); + result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, name, origin, + nsec3param.hash, nsec3param.iterations, + nsec3param.salt, nsec3param.salt_length); + check_result(result, "dns_nsec3_hashname()"); + + /* + * We don't use dns_db_find() here as it works with the choosen + * nsec3 chain and we may also be called with uncommitted data + * from dnssec-signzone so the secure status of the zone may not + * be up to date. + */ + dns_rdataset_init(&rdataset); + hashname = dns_fixedname_name(&fixed); + result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node); + if (result == ISC_R_SUCCESS) + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS && + (!delegation || (empty && !optout) || + (!empty && dns_nsec_isset(types, dns_rdatatype_ds)))) + { + dns_name_format(name, namebuf, sizeof(namebuf)); + dns_name_format(hashname, hashbuf, sizeof(hashbuf)); + fprintf(stderr, "Missing NSEC3 record for %s (%s)\n", + namebuf, hashbuf); + } else if (result == ISC_R_NOTFOUND && + delegation && (!empty || optout)) + { + result = ISC_R_SUCCESS; + } else if (result == ISC_R_SUCCESS) { + result = match_nsec3(name, mctx, &nsec3param, &rdataset, + types, maxtype, rawhash, rhsize); + } + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + + return (result); +} + +static isc_result_t +verifynsec3s(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *nsec3paramset, + isc_boolean_t delegation, isc_boolean_t empty, + unsigned char types[8192], unsigned int maxtype) +{ + isc_result_t result; + + for (result = dns_rdataset_first(nsec3paramset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(nsec3paramset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(nsec3paramset, &rdata); + result = verifynsec3(db, ver, origin, mctx, name, &rdata, + delegation, empty, types, maxtype); + if (result != ISC_R_SUCCESS) + break; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + return (result); +} + +static void +verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_rdataset_t *rdataset, dns_name_t *name, + dns_dbnode_t *node, dns_rdataset_t *keyrdataset, + unsigned char *act_algorithms, unsigned char *bad_algorithms) +{ + unsigned char set_algorithms[256]; + char namebuf[DNS_NAME_FORMATSIZE]; + char algbuf[80]; + char typebuf[80]; + dns_rdataset_t sigrdataset; + dns_rdatasetiter_t *rdsiter = NULL; + isc_result_t result; + int i; + + dns_rdataset_init(&sigrdataset); + result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) { + dns_rdatasetiter_current(rdsiter, &sigrdataset); + if (sigrdataset.type == dns_rdatatype_rrsig && + sigrdataset.covers == rdataset->type) + break; + dns_rdataset_disassociate(&sigrdataset); + } + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + type_format(rdataset->type, typebuf, sizeof(typebuf)); + fprintf(stderr, "No signatures for %s/%s\n", namebuf, typebuf); + for (i = 0; i < 256; i++) + if (act_algorithms[i] != 0) + bad_algorithms[i] = 1; + dns_rdatasetiter_destroy(&rdsiter); + return; + } + + memset(set_algorithms, 0, sizeof(set_algorithms)); + for (result = dns_rdataset_first(&sigrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&sigrdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_rrsig_t sig; + + dns_rdataset_current(&sigrdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &sig, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (rdataset->ttl != sig.originalttl) { + dns_name_format(name, namebuf, sizeof(namebuf)); + type_format(rdataset->type, typebuf, sizeof(typebuf)); + fprintf(stderr, "TTL mismatch for %s %s keytag %u\n", + namebuf, typebuf, sig.keyid); + continue; + } + if ((set_algorithms[sig.algorithm] != 0) || + (act_algorithms[sig.algorithm] == 0)) + continue; + if (goodsig(origin, &rdata, name, keyrdataset, rdataset, mctx)) + set_algorithms[sig.algorithm] = 1; + } + dns_rdatasetiter_destroy(&rdsiter); + if (memcmp(set_algorithms, act_algorithms, sizeof(set_algorithms))) { + dns_name_format(name, namebuf, sizeof(namebuf)); + type_format(rdataset->type, typebuf, sizeof(typebuf)); + for (i = 0; i < 256; i++) + if ((act_algorithms[i] != 0) && + (set_algorithms[i] == 0)) { + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, "No correct %s signature for " + "%s %s\n", algbuf, namebuf, typebuf); + bad_algorithms[i] = 1; + } + } + dns_rdataset_disassociate(&sigrdataset); +} + +static isc_result_t +verifynode(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_name_t *name, dns_dbnode_t *node, + isc_boolean_t delegation, dns_rdataset_t *keyrdataset, + unsigned char *act_algorithms, unsigned char *bad_algorithms, + dns_rdataset_t *nsecset, dns_rdataset_t *nsec3paramset, + dns_name_t *nextname) +{ + unsigned char types[8192]; + unsigned int maxtype = 0; + dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter = NULL; + isc_result_t result, tresult; + + memset(types, 0, sizeof(types)); + result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + result = dns_rdatasetiter_first(rdsiter); + dns_rdataset_init(&rdataset); + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(rdsiter, &rdataset); + /* + * If we are not at a delegation then everything should be + * signed. If we are at a delegation then only the DS set + * is signed. The NS set is not signed at a delegation but + * its existance is recorded in the bit map. Anything else + * other than NSEC and DS is not signed at a delegation. + */ + if (rdataset.type != dns_rdatatype_rrsig && + rdataset.type != dns_rdatatype_dnskey && + (!delegation || rdataset.type == dns_rdatatype_ds || + rdataset.type == dns_rdatatype_nsec)) { + verifyset(db, ver, origin, mctx, &rdataset, + name, node, keyrdataset, + act_algorithms, bad_algorithms); + dns_nsec_setbit(types, rdataset.type, 1); + if (rdataset.type > maxtype) + maxtype = rdataset.type; + } else if (rdataset.type != dns_rdatatype_rrsig && + rdataset.type != dns_rdatatype_dnskey) { + if (rdataset.type == dns_rdatatype_ns) + dns_nsec_setbit(types, rdataset.type, 1); + check_no_rrsig(db, ver, &rdataset, name, node); + } else + dns_nsec_setbit(types, rdataset.type, 1); + dns_rdataset_disassociate(&rdataset); + result = dns_rdatasetiter_next(rdsiter); + } + if (result != ISC_R_NOMORE) + fatal("rdataset iteration failed: %s", + isc_result_totext(result)); + dns_rdatasetiter_destroy(&rdsiter); + + result = ISC_R_SUCCESS; + + if (nsecset != NULL && dns_rdataset_isassociated(nsecset)) + result = verifynsec(db, ver, name, node, nextname); + + if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { + tresult = verifynsec3s(db, ver, origin, mctx, name, + nsec3paramset, delegation, ISC_FALSE, + types, maxtype); + if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) + result = tresult; + } + return (result); +} + +static isc_boolean_t +is_empty(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { + dns_rdatasetiter_t *rdsiter = NULL; + isc_result_t result; + + result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + result = dns_rdatasetiter_first(rdsiter); + dns_rdatasetiter_destroy(&rdsiter); + if (result == ISC_R_NOMORE) + return (ISC_TRUE); + return (ISC_FALSE); +} + +static void +check_no_nsec(dns_name_t *name, dns_dbnode_t *node, dns_db_t *db, + dns_dbversion_t *ver) +{ + dns_rdataset_t rdataset; + isc_result_t result; + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, + 0, 0, &rdataset, NULL); + if (result != ISC_R_NOTFOUND) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namebuf, sizeof(namebuf)); + fatal("unexpected NSEC RRset at %s\n", namebuf); + } + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); +} + +static isc_boolean_t +newchain(const struct nsec3_chain_fixed *first, + const struct nsec3_chain_fixed *e) +{ + if (first->hash != e->hash || + first->iterations != e->iterations || + first->salt_length != e->salt_length || + first->next_length != e->next_length || + memcmp(first + 1, e + 1, first->salt_length) != 0) + return (ISC_TRUE); + return (ISC_FALSE); +} + +static void +free_element(isc_mem_t *mctx, struct nsec3_chain_fixed *e) { + size_t len; + + len = sizeof(*e) + e->salt_length + 2 * e->next_length; + isc_mem_put(mctx, e, len); +} + +static isc_boolean_t +checknext(const struct nsec3_chain_fixed *first, + const struct nsec3_chain_fixed *e) +{ + char buf[512]; + const unsigned char *d1 = (const unsigned char *)(first + 1); + const unsigned char *d2 = (const unsigned char *)(e + 1); + isc_buffer_t b; + isc_region_t sr; + + d1 += first->salt_length + first->next_length; + d2 += e->salt_length; + + if (memcmp(d1, d2, first->next_length) == 0) + return (ISC_TRUE); + + DE_CONST(d1 - first->next_length, sr.base); + sr.length = first->next_length; + isc_buffer_init(&b, buf, sizeof(buf)); + isc_base32hex_totext(&sr, 1, "", &b); + fprintf(stderr, "Break in NSEC3 chain at: %.*s\n", + (int) isc_buffer_usedlength(&b), buf); + + DE_CONST(d1, sr.base); + sr.length = first->next_length; + isc_buffer_init(&b, buf, sizeof(buf)); + isc_base32hex_totext(&sr, 1, "", &b); + fprintf(stderr, "Expected: %.*s\n", (int) isc_buffer_usedlength(&b), + buf); + + DE_CONST(d2, sr.base); + sr.length = first->next_length; + isc_buffer_init(&b, buf, sizeof(buf)); + isc_base32hex_totext(&sr, 1, "", &b); + fprintf(stderr, "Found: %.*s\n", (int) isc_buffer_usedlength(&b), buf); + + return (ISC_FALSE); +} + +#define EXPECTEDANDFOUND "Expected and found NSEC3 chains not equal\n" + +static isc_result_t +verify_nsec3_chains(isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS; + struct nsec3_chain_fixed *e, *f = NULL; + struct nsec3_chain_fixed *first = NULL, *prev = NULL; + + while ((e = isc_heap_element(expected_chains, 1)) != NULL) { + isc_heap_delete(expected_chains, 1); + if (f == NULL) + f = isc_heap_element(found_chains, 1); + if (f != NULL) { + isc_heap_delete(found_chains, 1); + + /* + * Check that they match. + */ + if (chain_equal(e, f)) { + free_element(mctx, f); + f = NULL; + } else { + if (result == ISC_R_SUCCESS) + fprintf(stderr, EXPECTEDANDFOUND); + result = ISC_R_FAILURE; + /* + * Attempt to resync found_chain. + */ + while (f != NULL && !chain_compare(e, f)) { + free_element(mctx, f); + f = isc_heap_element(found_chains, 1); + if (f != NULL) + isc_heap_delete(found_chains, 1); + if (f != NULL && chain_equal(e, f)) { + free_element(mctx, f); + f = NULL; + break; + } + } + } + } else if (result == ISC_R_SUCCESS) { + fprintf(stderr, EXPECTEDANDFOUND); + result = ISC_R_FAILURE; + } + if (first == NULL || newchain(first, e)) { + if (prev != NULL) { + if (!checknext(prev, first)) + result = ISC_R_FAILURE; + if (prev != first) + free_element(mctx, prev); + } + if (first != NULL) + free_element(mctx, first); + prev = first = e; + continue; + } + if (!checknext(prev, e)) + result = ISC_R_FAILURE; + if (prev != first) + free_element(mctx, prev); + prev = e; + } + if (prev != NULL) { + if (!checknext(prev, first)) + result = ISC_R_FAILURE; + if (prev != first) + free_element(mctx, prev); + } + if (first != NULL) + free_element(mctx, first); + do { + if (f != NULL) { + if (result == ISC_R_SUCCESS) { + fprintf(stderr, EXPECTEDANDFOUND); + result = ISC_R_FAILURE; + } + free_element(mctx, f); + } + f = isc_heap_element(found_chains, 1); + if (f != NULL) + isc_heap_delete(found_chains, 1); + } while (f != NULL); + + return (result); +} + +static isc_result_t +verifyemptynodes(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_name_t *name, dns_name_t *prevname, + isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset) +{ + dns_namereln_t reln; + int order; + unsigned int labels, nlabels, i; + dns_name_t suffix; + isc_result_t result = ISC_R_SUCCESS, tresult; + + reln = dns_name_fullcompare(prevname, name, &order, &labels); + if (order >= 0) + return (result); + + nlabels = dns_name_countlabels(name); + + if (reln == dns_namereln_commonancestor || + reln == dns_namereln_contains) { + dns_name_init(&suffix, NULL); + for (i = labels + 1; i < nlabels; i++) { + dns_name_getlabelsequence(name, nlabels - i, i, + &suffix); + if (nsec3paramset != NULL && + dns_rdataset_isassociated(nsec3paramset)) { + tresult = verifynsec3s(db, ver, origin, mctx, + &suffix, nsec3paramset, + isdelegation, ISC_TRUE, + NULL, 0); + if (result == ISC_R_SUCCESS && + tresult != ISC_R_SUCCESS) + result = tresult; + } + } + } + return (result); +} + +/*% + * Verify that certain things are sane: + * + * The apex has a DNSKEY record with at least one KSK, and at least + * one ZSK if the -x flag was not used. + * + * The DNSKEY record was signed with at least one of the KSKs in this + * set. + * + * The rest of the zone was signed with at least one of the ZSKs + * present in the DNSKEY RRSET. + */ +void +verifyzone(dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *origin, isc_mem_t *mctx, + isc_boolean_t ignore_kskflag, isc_boolean_t keyset_kskonly) +{ + char algbuf[80]; + dns_dbiterator_t *dbiter = NULL; + dns_dbnode_t *node = NULL, *nextnode = NULL; + dns_fixedname_t fname, fnextname, fprevname, fzonecut; + dns_name_t *name, *nextname, *prevname, *zonecut; + dns_rdata_dnskey_t dnskey; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t keyset, soaset; + dns_rdataset_t keysigs, soasigs; + dns_rdataset_t nsecset, nsecsigs; + dns_rdataset_t nsec3paramset, nsec3paramsigs; + int i; + isc_boolean_t done = ISC_FALSE; + isc_boolean_t first = ISC_TRUE; + isc_boolean_t goodksk = ISC_FALSE; + isc_boolean_t goodzsk = ISC_FALSE; + isc_result_t result, vresult = ISC_R_UNSET; + unsigned char revoked_ksk[256]; + unsigned char revoked_zsk[256]; + unsigned char standby_ksk[256]; + unsigned char standby_zsk[256]; + unsigned char ksk_algorithms[256]; + unsigned char zsk_algorithms[256]; + unsigned char bad_algorithms[256]; + unsigned char act_algorithms[256]; + + result = isc_heap_create(mctx, chain_compare, NULL, 1024, + &expected_chains); + check_result(result, "isc_heap_create()"); + result = isc_heap_create(mctx, chain_compare, NULL, 1024, + &found_chains); + check_result(result, "isc_heap_create()"); + + result = dns_db_findnode(db, origin, ISC_FALSE, &node); + if (result != ISC_R_SUCCESS) + fatal("failed to find the zone's origin: %s", + isc_result_totext(result)); + + dns_rdataset_init(&keyset); + dns_rdataset_init(&keysigs); + dns_rdataset_init(&soaset); + dns_rdataset_init(&soasigs); + dns_rdataset_init(&nsecset); + dns_rdataset_init(&nsecsigs); + dns_rdataset_init(&nsec3paramset); + dns_rdataset_init(&nsec3paramsigs); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, + 0, 0, &keyset, &keysigs); + if (result != ISC_R_SUCCESS) + fatal("Zone contains no DNSSEC keys\n"); + + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, + 0, 0, &soaset, &soasigs); + if (result != ISC_R_SUCCESS) + fatal("Zone contains no SOA record\n"); + + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, + 0, 0, &nsecset, &nsecsigs); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + fatal("NSEC lookup failed\n"); + + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, + 0, 0, &nsec3paramset, &nsec3paramsigs); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + fatal("NSEC3PARAM lookup failed\n"); + + if (!dns_rdataset_isassociated(&keysigs)) + fatal("DNSKEY is not signed (keys offline or inactive?)\n"); + + if (!dns_rdataset_isassociated(&soasigs)) + fatal("SOA is not signed (keys offline or inactive?)\n"); + + if (dns_rdataset_isassociated(&nsecset) && + !dns_rdataset_isassociated(&nsecsigs)) + fatal("NSEC is not signed (keys offline or inactive?)\n"); + + if (dns_rdataset_isassociated(&nsec3paramset) && + !dns_rdataset_isassociated(&nsec3paramsigs)) + fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n"); + + if (!dns_rdataset_isassociated(&nsecset) && + !dns_rdataset_isassociated(&nsec3paramset)) + fatal("No valid NSEC/NSEC3 chain for testing\n"); + + dns_db_detachnode(db, &node); + + memset(revoked_ksk, 0, sizeof(revoked_ksk)); + memset(revoked_zsk, 0, sizeof(revoked_zsk)); + memset(standby_ksk, 0, sizeof(standby_ksk)); + memset(standby_zsk, 0, sizeof(standby_zsk)); + memset(ksk_algorithms, 0, sizeof(ksk_algorithms)); + memset(zsk_algorithms, 0, sizeof(zsk_algorithms)); + memset(bad_algorithms, 0, sizeof(bad_algorithms)); + memset(act_algorithms, 0, sizeof(act_algorithms)); + + /* + * Check that the DNSKEY RR has at least one self signing KSK + * and one ZSK per algorithm in it (or, if -x was used, one + * self-signing KSK). + */ + for (result = dns_rdataset_first(&keyset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&keyset)) { + dns_rdataset_current(&keyset, &rdata); + result = dns_rdata_tostruct(&rdata, &dnskey, NULL); + check_result(result, "dns_rdata_tostruct"); + + if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0) + ; + else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { + if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && + !dns_dnssec_selfsigns(&rdata, origin, &keyset, + &keysigs, ISC_FALSE, + mctx)) { + char namebuf[DNS_NAME_FORMATSIZE]; + char buffer[1024]; + isc_buffer_t buf; + + dns_name_format(origin, namebuf, + sizeof(namebuf)); + isc_buffer_init(&buf, buffer, sizeof(buffer)); + result = dns_rdata_totext(&rdata, NULL, &buf); + check_result(result, "dns_rdata_totext"); + fatal("revoked KSK is not self signed:\n" + "%s DNSKEY %.*s", namebuf, + (int)isc_buffer_usedlength(&buf), buffer); + } + if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && + revoked_ksk[dnskey.algorithm] != 255) + revoked_ksk[dnskey.algorithm]++; + else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && + revoked_zsk[dnskey.algorithm] != 255) + revoked_zsk[dnskey.algorithm]++; + } else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) { + if (dns_dnssec_selfsigns(&rdata, origin, &keyset, + &keysigs, ISC_FALSE, mctx)) { + if (ksk_algorithms[dnskey.algorithm] != 255) + ksk_algorithms[dnskey.algorithm]++; + goodksk = ISC_TRUE; + } else { + if (standby_ksk[dnskey.algorithm] != 255) + standby_ksk[dnskey.algorithm]++; + } + } else if (dns_dnssec_selfsigns(&rdata, origin, &keyset, + &keysigs, ISC_FALSE, mctx)) { + if (zsk_algorithms[dnskey.algorithm] != 255) + zsk_algorithms[dnskey.algorithm]++; + goodzsk = ISC_TRUE; + } else if (dns_dnssec_signs(&rdata, origin, &soaset, + &soasigs, ISC_FALSE, mctx)) { + if (zsk_algorithms[dnskey.algorithm] != 255) + zsk_algorithms[dnskey.algorithm]++; + } else { + if (standby_zsk[dnskey.algorithm] != 255) + standby_zsk[dnskey.algorithm]++; + } + dns_rdata_freestruct(&dnskey); + dns_rdata_reset(&rdata); + } + dns_rdataset_disassociate(&keysigs); + dns_rdataset_disassociate(&soaset); + dns_rdataset_disassociate(&soasigs); + if (dns_rdataset_isassociated(&nsecsigs)) + dns_rdataset_disassociate(&nsecsigs); + if (dns_rdataset_isassociated(&nsec3paramsigs)) + dns_rdataset_disassociate(&nsec3paramsigs); + + if (ignore_kskflag ) { + if (!goodksk && !goodzsk) + fatal("No self-signed DNSKEY found."); + } else if (!goodksk) + fatal("No self-signed KSK DNSKEY found. Supply an active\n" + "key with the KSK flag set, or use '-P'."); + + fprintf(stderr, "Verifying the zone using the following algorithms:"); + for (i = 0; i < 256; i++) { + if (ignore_kskflag) + act_algorithms[i] = (ksk_algorithms[i] != 0 || + zsk_algorithms[i] != 0) ? 1 : 0; + else + act_algorithms[i] = ksk_algorithms[i] != 0 ? 1 : 0; + if (act_algorithms[i] != 0) { + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, " %s", algbuf); + } + } + fprintf(stderr, ".\n"); + + if (!ignore_kskflag && !keyset_kskonly) { + for (i = 0; i < 256; i++) { + /* + * The counts should both be zero or both be non-zero. + * Mark the algorithm as bad if this is not met. + */ + if ((ksk_algorithms[i] != 0) == + (zsk_algorithms[i] != 0)) + continue; + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, "Missing %s for algorithm %s\n", + (ksk_algorithms[i] != 0) + ? "ZSK" + : "self-signed KSK", + algbuf); + bad_algorithms[i] = 1; + } + } + + /* + * Check that all the other records were signed by keys that are + * present in the DNSKEY RRSET. + */ + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_fixedname_init(&fnextname); + nextname = dns_fixedname_name(&fnextname); + dns_fixedname_init(&fprevname); + prevname = NULL; + dns_fixedname_init(&fzonecut); + zonecut = NULL; + + result = dns_db_createiterator(db, DNS_DB_NONSEC3, &dbiter); + check_result(result, "dns_db_createiterator()"); + + result = dns_dbiterator_first(dbiter); + check_result(result, "dns_dbiterator_first()"); + + while (!done) { + isc_boolean_t isdelegation = ISC_FALSE; + + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + if (!dns_name_issubdomain(name, origin)) { + check_no_nsec(name, node, db, ver); + dns_db_detachnode(db, &node); + result = dns_dbiterator_next(dbiter); + if (result == ISC_R_NOMORE) + done = ISC_TRUE; + else + check_result(result, "dns_dbiterator_next()"); + continue; + } + if (is_delegation(db, ver, origin, name, node, NULL)) { + zonecut = dns_fixedname_name(&fzonecut); + dns_name_copy(name, zonecut, NULL); + isdelegation = ISC_TRUE; + } + nextnode = NULL; + result = dns_dbiterator_next(dbiter); + while (result == ISC_R_SUCCESS) { + result = dns_dbiterator_current(dbiter, &nextnode, + nextname); + check_dns_dbiterator_current(result); + if (!dns_name_issubdomain(nextname, origin) || + (zonecut != NULL && + dns_name_issubdomain(nextname, zonecut))) + { + check_no_nsec(nextname, nextnode, db, ver); + dns_db_detachnode(db, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + if (is_empty(db, ver, nextnode)) { + dns_db_detachnode(db, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + dns_db_detachnode(db, &nextnode); + break; + } + if (result == ISC_R_NOMORE) { + done = ISC_TRUE; + nextname = origin; + } else if (result != ISC_R_SUCCESS) + fatal("iterating through the database failed: %s", + isc_result_totext(result)); + result = verifynode(db, ver, origin, mctx, name, node, + isdelegation, &keyset, act_algorithms, + bad_algorithms, &nsecset, &nsec3paramset, + nextname); + if (vresult == ISC_R_UNSET) + vresult = ISC_R_SUCCESS; + if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS) + vresult = result; + if (prevname != NULL) { + result = verifyemptynodes(db, ver, origin, mctx, name, + prevname, isdelegation, + &nsec3paramset); + } else + prevname = dns_fixedname_name(&fprevname); + dns_name_copy(name, prevname, NULL); + if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS) + vresult = result; + dns_db_detachnode(db, &node); + } + + dns_dbiterator_destroy(&dbiter); + + result = dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbiter); + check_result(result, "dns_db_createiterator()"); + + for (result = dns_dbiterator_first(dbiter); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiter) ) { + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + result = verifynode(db, ver, origin, mctx, name, node, + ISC_FALSE, &keyset, act_algorithms, + bad_algorithms, NULL, NULL, NULL); + check_result(result, "verifynode"); + record_found(db, ver, mctx, name, node, &nsec3paramset); + dns_db_detachnode(db, &node); + } + dns_dbiterator_destroy(&dbiter); + + dns_rdataset_disassociate(&keyset); + if (dns_rdataset_isassociated(&nsecset)) + dns_rdataset_disassociate(&nsecset); + if (dns_rdataset_isassociated(&nsec3paramset)) + dns_rdataset_disassociate(&nsec3paramset); + + result = verify_nsec3_chains(mctx); + if (vresult == ISC_R_UNSET) + vresult = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS) + vresult = result; + isc_heap_destroy(&expected_chains); + isc_heap_destroy(&found_chains); + + /* + * If we made it this far, we have what we consider a properly signed + * zone. Set the good flag. + */ + for (i = 0; i < 256; i++) { + if (bad_algorithms[i] != 0) { + if (first) + fprintf(stderr, "The zone is not fully signed " + "for the following algorithms:"); + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, " %s", algbuf); + first = ISC_FALSE; + } + } + if (!first) { + fprintf(stderr, ".\n"); + fatal("DNSSEC completeness test failed."); + } + + if (vresult != ISC_R_SUCCESS) + fatal("DNSSEC completeness test failed (%s).", + dns_result_totext(vresult)); + + if (goodksk || ignore_kskflag) { + /* + * Print the success summary. + */ + fprintf(stderr, "Zone fully signed:\n"); + for (i = 0; i < 256; i++) { + if ((ksk_algorithms[i] != 0) || + (standby_ksk[i] != 0) || + (revoked_zsk[i] != 0) || + (zsk_algorithms[i] != 0) || + (standby_zsk[i] != 0) || + (revoked_zsk[i] != 0)) { + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, "Algorithm: %s: KSKs: " + "%u active, %u stand-by, %u revoked\n", + algbuf, ksk_algorithms[i], + standby_ksk[i], revoked_ksk[i]); + fprintf(stderr, "%*sZSKs: " + "%u active, %u %s, %u revoked\n", + (int) strlen(algbuf) + 13, "", + zsk_algorithms[i], + standby_zsk[i], + keyset_kskonly ? "present" : "stand-by", + revoked_zsk[i]); + } + } + } +} |