summaryrefslogtreecommitdiffstats
path: root/contrib/bind9/bin/dnssec/dnssectool.c
diff options
context:
space:
mode:
authormarkm <markm@FreeBSD.org>2013-08-23 07:39:32 +0000
committermarkm <markm@FreeBSD.org>2013-08-23 07:39:32 +0000
commit7f2ed01ffff8c77447d4868a6d43851fd9380c47 (patch)
tree60c569e471154195770e3119302c3c7a03686914 /contrib/bind9/bin/dnssec/dnssectool.c
parentebd919f034b5492d5d2693c8cf79e707b25a52bb (diff)
parent7b6ce3ea58e65e172ca1ad959eccacbed50fab30 (diff)
downloadFreeBSD-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.c1336
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]);
+ }
+ }
+ }
+}
OpenPOWER on IntegriCloud