From ffe8ddd95f8c7b13cc3023ced443670abde6de34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 01/38] Replace type_format() and TYPE_FORMATSIZE with their libdns counterparts Rather than use custom functions and macros local to bin/dnssec/, use their counterparts provided by libdns. --- bin/dnssec/dnssec-signzone.c | 8 ++++---- bin/dnssec/dnssectool.c | 22 +++++----------------- bin/dnssec/dnssectool.h | 4 ---- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index 7259254dc4..d68eaa4260 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -498,11 +498,11 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, dns_ttl_t ttl; int i; char namestr[DNS_NAME_FORMATSIZE]; - char typestr[TYPE_FORMATSIZE]; + char typestr[DNS_RDATATYPE_FORMATSIZE]; char sigstr[SIG_FORMATSIZE]; dns_name_format(name, namestr, sizeof(namestr)); - type_format(set->type, typestr, sizeof(typestr)); + dns_rdatatype_format(set->type, typestr, sizeof(typestr)); ttl = ISC_MIN(set->ttl, endtime - starttime); @@ -2090,10 +2090,10 @@ rrset_cleanup(dns_name_t *name, dns_rdataset_t *rdataset, unsigned int count1 = 0; dns_rdataset_t tmprdataset; char namestr[DNS_NAME_FORMATSIZE]; - char typestr[TYPE_FORMATSIZE]; + char typestr[DNS_RDATATYPE_FORMATSIZE]; dns_name_format(name, namestr, sizeof(namestr)); - type_format(rdataset->type, typestr, sizeof(typestr)); + dns_rdatatype_format(rdataset->type, typestr, sizeof(typestr)); dns_rdataset_init(&tmprdataset); for (result = dns_rdataset_first(rdataset); diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c index 88a0c77fd0..aa80b869d0 100644 --- a/bin/dnssec/dnssectool.c +++ b/bin/dnssec/dnssectool.c @@ -116,19 +116,6 @@ version(const char *name) { exit(0); } -void -type_format(const dns_rdatatype_t type, char *cp, unsigned int size) { - isc_buffer_t b; - isc_region_t r; - isc_result_t result; - - isc_buffer_init(&b, cp, size - 1); - result = dns_rdatatype_totext(type, &b); - check_result(result, "dns_rdatatype_totext()"); - isc_buffer_usedregion(&b, &r); - r.base[r.length] = 0; -} - void sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) { char namestr[DNS_NAME_FORMATSIZE]; @@ -667,7 +654,7 @@ check_no_rrsig(dns_db_t *db, dns_dbversion_t *ver, dns_rdataset_t *rdataset, } if (result == ISC_R_SUCCESS) { dns_name_format(name, namebuf, sizeof(namebuf)); - type_format(rdataset->type, typebuf, sizeof(typebuf)); + dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); fprintf(stderr, "Warning: Found unexpected signatures for " "%s/%s\n", namebuf, typebuf); } @@ -1099,7 +1086,7 @@ verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, } if (result != ISC_R_SUCCESS) { dns_name_format(name, namebuf, sizeof(namebuf)); - type_format(rdataset->type, typebuf, sizeof(typebuf)); + dns_rdatatype_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) @@ -1120,7 +1107,8 @@ verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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)); + dns_rdatatype_format(rdataset->type, typebuf, + sizeof(typebuf)); fprintf(stderr, "TTL mismatch for %s %s keytag %u\n", namebuf, typebuf, sig.keyid); continue; @@ -1134,7 +1122,7 @@ verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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)); + dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); for (i = 0; i < 256; i++) if ((act_algorithms[i] != 0) && (set_algorithms[i] == 0)) { diff --git a/bin/dnssec/dnssectool.h b/bin/dnssec/dnssectool.h index 189292b713..98263774ea 100644 --- a/bin/dnssec/dnssectool.h +++ b/bin/dnssec/dnssectool.h @@ -41,10 +41,6 @@ vbprintf(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); ISC_PLATFORM_NORETURN_PRE void version(const char *program) ISC_PLATFORM_NORETURN_POST; -void -type_format(const dns_rdatatype_t type, char *cp, unsigned int size); -#define TYPE_FORMATSIZE 20 - void sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size); #define SIG_FORMATSIZE (DNS_NAME_FORMATSIZE + DNS_SECALG_FORMATSIZE + sizeof("65535")) From 3a14450d39bfbfbbc493d512d07d2125d348e171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 02/38] Move verifyzone() and its dependencies into lib/dns/zoneverify.c This commit only moves code around, with the following exceptions: - the check_dns_dbiterator_current() macro and functions is_delegation() and has_dname() were removed from bin/dnssec/dnssectool.{c,h} and duplicated in two locations: bin/dnssec/dnssec-signzone.c and lib/dns/zoneverify.c; these functions are used both by the code in bin/dnssec/dnssec-signzone.c and verifyzone(), but are not a good fit for being exported by a code module responsible for zone verification, - fatal() and check_result() were duplicated in lib/dns/zoneverify.c as static functions which do not use the "program" variable any more (as it is only set by the tools in bin/dnssec/); this is a temporary step which only aims to prevent compilation from breaking - these duplicate functions will be removed once lib/dns/zoneverify.c is refactored not to use them, - the list of header files included by lib/dns/zoneverify.c was expanded to encompass all header files that are actually used by the code in that file, - a description of the purpose of the commented out "fields" inside struct nsec3_chain_fixed was added. --- bin/dnssec/dnssec-signzone.c | 46 + bin/dnssec/dnssec-verify.c | 1 + bin/dnssec/dnssectool.c | 1341 ---------------------------- bin/dnssec/dnssectool.h | 21 - lib/dns/Makefile.in | 6 +- lib/dns/include/dns/Makefile.in | 2 +- lib/dns/include/dns/zoneverify.h | 39 + lib/dns/win32/libdns.def.in | 1 + lib/dns/zoneverify.c | 1408 ++++++++++++++++++++++++++++++ util/copyrights | 2 + 10 files changed, 1502 insertions(+), 1365 deletions(-) create mode 100644 lib/dns/include/dns/zoneverify.h create mode 100644 lib/dns/zoneverify.c diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index d68eaa4260..ed8e8f3a08 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -76,6 +76,7 @@ #include #include #include +#include #include @@ -96,6 +97,10 @@ typedef struct hashlist hashlist_t; static int nsec_datatype = dns_rdatatype_nsec; +#define check_dns_dbiterator_current(result) \ + check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \ + "dns_dbiterator_current()") + #define IS_NSEC3 (nsec_datatype == dns_rdatatype_nsec3) #define OPTOUT(x) (((x) & DNS_NSEC3FLAG_OPTOUT) != 0) @@ -1042,6 +1047,47 @@ secure(dns_name_t *name, dns_dbnode_t *node) { return (ISC_TF(result == ISC_R_SUCCESS)); } +static 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)); +} + +/*% + * Return ISC_TRUE if version 'ver' of database 'db' contains a DNAME RRset at + * 'node'; return ISC_FALSE otherwise. + */ +static isc_boolean_t +has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { + dns_rdataset_t dnameset; + isc_result_t result; + + dns_rdataset_init(&dnameset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dname, 0, 0, + &dnameset, NULL); + if (dns_rdataset_isassociated(&dnameset)) { + dns_rdataset_disassociate(&dnameset); + } + + return (ISC_TF(result == ISC_R_SUCCESS)); +} + /*% * Signs all records at a name. */ diff --git a/bin/dnssec/dnssec-verify.c b/bin/dnssec/dnssec-verify.c index 09e8e30c2d..42207a148a 100644 --- a/bin/dnssec/dnssec-verify.c +++ b/bin/dnssec/dnssec-verify.c @@ -58,6 +58,7 @@ #include #include #include +#include #include diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c index aa80b869d0..e7ea7d522d 100644 --- a/bin/dnssec/dnssectool.c +++ b/bin/dnssec/dnssectool.c @@ -57,18 +57,6 @@ #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; @@ -490,1335 +478,6 @@ 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)); -} - -isc_boolean_t -has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { - dns_rdataset_t dnameset; - isc_result_t result; - - dns_rdataset_init(&dnameset); - result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dname, 0, 0, - &dnameset, NULL); - if (dns_rdataset_isassociated(&dnameset)) { - dns_rdataset_disassociate(&dnameset); - } - - 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, - 0, mctx, sigrdata, NULL); - dst_key_free(&dstkey); - if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { - 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)); - dns_rdatatype_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); - memmove(cp, nsec3->salt, nsec3->salt_length); - cp += nsec3->salt_length; - memmove(cp, rawhash, nsec3->next_length); - cp += nsec3->next_length; - memmove(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)); - dns_rdatatype_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)); - dns_rdatatype_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)); - dns_rdatatype_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. - */ - - name = dns_fixedname_initname(&fname); - nextname = dns_fixedname_initname(&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; - } else if (has_dname(db, ver, node)) { - zonecut = dns_fixedname_name(&fzonecut); - dns_name_copy(name, zonecut, NULL); - } - 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_ksk[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]); - } - } - } -} - isc_boolean_t isoptarg(const char *arg, char **argv, void(*usage)(void)) { if (!strcasecmp(isc_commandline_argument, arg)) { diff --git a/bin/dnssec/dnssectool.h b/bin/dnssec/dnssectool.h index 98263774ea..0ce686c393 100644 --- a/bin/dnssec/dnssectool.h +++ b/bin/dnssec/dnssectool.h @@ -18,11 +18,6 @@ #include #include -#define check_dns_dbiterator_current(result) \ - check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \ - "dns_dbiterator_current()") - - typedef void (fatalcallback_t)(void); ISC_PLATFORM_NORETURN_PRE void @@ -76,22 +71,6 @@ isc_boolean_t key_collision(dst_key_t *key, dns_name_t *name, const char *dir, isc_mem_t *mctx, isc_boolean_t *exact); -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); - -/*% - * Return ISC_TRUE if version 'ver' of database 'db' contains a DNAME RRset at - * 'node'; return ISC_FALSE otherwise. - */ -isc_boolean_t -has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node); - -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); - isc_boolean_t isoptarg(const char *arg, char **argv, void (*usage)(void)); diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index 22d2a313ee..85c422d8d3 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -77,7 +77,8 @@ DNSOBJS = acl.@O@ adb.@O@ badcache.@O@ byaddr.@O@ \ sdlz.@O@ soa.@O@ ssu.@O@ ssu_external.@O@ \ stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \ tsec.@O@ tsig.@O@ ttl.@O@ update.@O@ validator.@O@ \ - version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ zt.@O@ + version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ \ + zoneverify.@O@ zt.@O@ PORTDNSOBJS = client.@O@ ecdb.@O@ OBJS= @DNSTAPOBJS@ ${DNSOBJS} ${OTHEROBJS} ${DSTOBJS} \ @@ -119,7 +120,8 @@ DNSSRCS = acl.c adb.c badcache. byaddr.c \ sdb.c sdlz.c soa.c ssu.c ssu_external.c \ stats.c tcpmsg.c time.c timer.c tkey.c \ tsec.c tsig.c ttl.c update.c validator.c \ - version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS} + version.c view.c xfrin.c zone.c zoneverify.c \ + zonekey.c zt.c ${OTHERSRCS} PORTDNSSRCS = client.c ecdb.c SRCS = ${DSTSRCS} ${DNSSRCS} ${PORTDNSSRCS} @DNSTAPSRCS@ @GEOIPLINKSRCS@ diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in index ab63d83277..91ca18b5ad 100644 --- a/lib/dns/include/dns/Makefile.in +++ b/lib/dns/include/dns/Makefile.in @@ -31,7 +31,7 @@ HEADERS = acl.h adb.h badcache.h bit.h byaddr.h \ sdb.h sdlz.h secalg.h secproto.h soa.h ssu.h stats.h \ tcpmsg.h time.h timer.h tkey.h tsec.h tsig.h ttl.h types.h \ update.h validator.h version.h view.h xfrin.h \ - zone.h zonekey.h zt.h + zone.h zonekey.h zoneverify.h zt.h GENHEADERS = @DNSTAP_PB_C_H@ enumclass.h enumtype.h rdatastruct.h diff --git a/lib/dns/include/dns/zoneverify.h b/lib/dns/include/dns/zoneverify.h new file mode 100644 index 0000000000..6b7463e552 --- /dev/null +++ b/lib/dns/include/dns/zoneverify.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file dns/zoneverify.h */ + +#include + +#include + +ISC_LANG_BEGINDECLS + +/*% + * 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); + +ISC_LANG_ENDDECLS diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index ce44becd63..0cedd31b95 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -1334,6 +1334,7 @@ dns_zonemgr_shutdown dns_zonemgr_unreachable dns_zonemgr_unreachableadd dns_zonemgr_unreachabledel +verifyzone dns_zt_apply dns_zt_asyncload dns_zt_attach diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c new file mode 100644 index 0000000000..fdaee1f952 --- /dev/null +++ b/lib/dns/zoneverify.c @@ -0,0 +1,1408 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define check_dns_dbiterator_current(result) \ + check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \ + "dns_dbiterator_current()") + +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; + /* + * The following non-fixed-length data is stored in memory after the + * fields declared above for each NSEC3 chain element: + * + * unsigned char salt[salt_length]; + * unsigned char owner[next_length]; + * unsigned char next[next_length]; + */ +}; + +static void +fatal(const char *format, ...) { + va_list args; + + fprintf(stderr, "fatal: "); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} + +static void +check_result(isc_result_t result, const char *message) { + if (result != ISC_R_SUCCESS) + fatal("%s: %s", message, isc_result_totext(result)); +} + +static 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)); +} + +/*% + * Return ISC_TRUE if version 'ver' of database 'db' contains a DNAME RRset at + * 'node'; return ISC_FALSE otherwise. + */ +static isc_boolean_t +has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { + dns_rdataset_t dnameset; + isc_result_t result; + + dns_rdataset_init(&dnameset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dname, 0, 0, + &dnameset, NULL); + if (dns_rdataset_isassociated(&dnameset)) { + dns_rdataset_disassociate(&dnameset); + } + + 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, + 0, mctx, sigrdata, NULL); + dst_key_free(&dstkey); + if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { + 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)); + dns_rdatatype_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); + memmove(cp, nsec3->salt, nsec3->salt_length); + cp += nsec3->salt_length; + memmove(cp, rawhash, nsec3->next_length); + cp += nsec3->next_length; + memmove(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)); + dns_rdatatype_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)); + dns_rdatatype_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)); + dns_rdatatype_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); +} + +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. + */ + + name = dns_fixedname_initname(&fname); + nextname = dns_fixedname_initname(&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; + } else if (has_dname(db, ver, node)) { + zonecut = dns_fixedname_name(&fzonecut); + dns_name_copy(name, zonecut, NULL); + } + 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_ksk[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]); + } + } + } +} diff --git a/util/copyrights b/util/copyrights index 3d2aa50290..d948050617 100644 --- a/util/copyrights +++ b/util/copyrights @@ -3081,6 +3081,7 @@ ./lib/dns/include/dns/xfrin.h C 1999,2000,2001,2003,2004,2005,2006,2007,2009,2013,2016,2018 ./lib/dns/include/dns/zone.h C 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 ./lib/dns/include/dns/zonekey.h C 2001,2004,2005,2006,2007,2016,2018 +./lib/dns/include/dns/zoneverify.h C 2018 ./lib/dns/include/dns/zt.h C 1999,2000,2001,2002,2004,2005,2006,2007,2011,2016,2017,2018 ./lib/dns/include/dst/Makefile.in MAKE 1998,1999,2000,2001,2004,2007,2012,2015,2016,2018 ./lib/dns/include/dst/dst.h C 2000,2001,2002,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 @@ -3421,6 +3422,7 @@ ./lib/dns/zone.c C 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 ./lib/dns/zone_p.h C 2018 ./lib/dns/zonekey.c C 2001,2003,2004,2005,2007,2016,2018 +./lib/dns/zoneverify.c C 2018 ./lib/dns/zt.c C 1999,2000,2001,2002,2004,2005,2006,2007,2011,2012,2013,2014,2015,2016,2017,2018 ./lib/irs/Atffile X 2016,2018 ./lib/irs/Kyuafile X 2017,2018 From 7554e8d2ca4ccafd63013f1e9633c90264157614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 03/38] Rename verifyzone() to dns_zoneverify_dnssec() This makes the function's name match the naming convention used for libdns functions. --- bin/dnssec/dnssec-signzone.c | 4 ++-- bin/dnssec/dnssec-verify.c | 4 ++-- lib/dns/include/dns/zoneverify.h | 6 +++--- lib/dns/win32/libdns.def.in | 2 +- lib/dns/zoneverify.c | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index ed8e8f3a08..d857c844d0 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -3913,8 +3913,8 @@ main(int argc, char *argv[]) { TIME_NOW(&sign_finish); if (!disable_zone_check) - verifyzone(gdb, gversion, gorigin, mctx, - ignore_kskflag, keyset_kskonly); + dns_zoneverify_dnssec(gdb, gversion, gorigin, mctx, + ignore_kskflag, keyset_kskonly); if (outputformat != dns_masterformat_text) { dns_masterrawheader_t header; diff --git a/bin/dnssec/dnssec-verify.c b/bin/dnssec/dnssec-verify.c index 42207a148a..c236235750 100644 --- a/bin/dnssec/dnssec-verify.c +++ b/bin/dnssec/dnssec-verify.c @@ -323,8 +323,8 @@ main(int argc, char *argv[]) { result = dns_db_newversion(gdb, &gversion); check_result(result, "dns_db_newversion()"); - verifyzone(gdb, gversion, gorigin, mctx, - ignore_kskflag, keyset_kskonly); + dns_zoneverify_dnssec(gdb, gversion, gorigin, mctx, ignore_kskflag, + keyset_kskonly); dns_db_closeversion(gdb, &gversion, ISC_FALSE); dns_db_detach(&gdb); diff --git a/lib/dns/include/dns/zoneverify.h b/lib/dns/include/dns/zoneverify.h index 6b7463e552..facdcf9dbb 100644 --- a/lib/dns/include/dns/zoneverify.h +++ b/lib/dns/include/dns/zoneverify.h @@ -32,8 +32,8 @@ ISC_LANG_BEGINDECLS * 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); +dns_zoneverify_dnssec(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); ISC_LANG_ENDDECLS diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 0cedd31b95..796dccb9e2 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -1334,7 +1334,7 @@ dns_zonemgr_shutdown dns_zonemgr_unreachable dns_zonemgr_unreachableadd dns_zonemgr_unreachabledel -verifyzone +dns_zoneverify_dnssec dns_zt_apply dns_zt_asyncload dns_zt_attach diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index fdaee1f952..6b45bb7b62 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1021,9 +1021,9 @@ verifyemptynodes(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, } 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) +dns_zoneverify_dnssec(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; From ffc79977238692504875e8ab505aafc98810d257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 04/38] Do not use static variables in lib/dns/zoneverify.c Make dns_zoneverify_dnssec() eligible for multithreaded use by replacing the static variables it accesses with a stack-allocated structure containing these variables. Implement setup and cleanup routines for that structure, ensuring no error in these routines causes exit() to be called any more. Pass a pointer to that structure to functions requiring access to variables which were previously static. --- lib/dns/zoneverify.c | 133 +++++++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 50 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 6b45bb7b62..69ef20fd98 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -51,7 +51,10 @@ check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \ "dns_dbiterator_current()") -static isc_heap_t *expected_chains, *found_chains; +typedef struct vctx { + isc_heap_t * expected_chains; + isc_heap_t * found_chains; +} vctx_t; struct nsec3_chain_fixed { isc_uint8_t hash; @@ -346,7 +349,7 @@ record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3, } static isc_result_t -match_nsec3(dns_name_t *name, isc_mem_t *mctx, +match_nsec3(const vctx_t *vctx, 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) @@ -395,7 +398,7 @@ match_nsec3(dns_name_t *name, isc_mem_t *mctx, /* * Record chain. */ - result = record_nsec3(rawhash, &nsec3, mctx, expected_chains); + result = record_nsec3(rawhash, &nsec3, mctx, vctx->expected_chains); check_result(result, "record_nsec3()"); /* @@ -453,8 +456,8 @@ innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { } 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, +record_found(const vctx_t *vctx, 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]; @@ -499,7 +502,7 @@ record_found(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, /* * Record chain. */ - result = record_nsec3(owner, &nsec3, mctx, found_chains); + result = record_nsec3(owner, &nsec3, mctx, vctx->found_chains); check_result(result, "record_nsec3()"); } @@ -562,9 +565,9 @@ isoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, } 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, +verifynsec3(const vctx_t *vctx, 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]; @@ -621,7 +624,7 @@ verifynsec3(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, { result = ISC_R_SUCCESS; } else if (result == ISC_R_SUCCESS) { - result = match_nsec3(name, mctx, &nsec3param, &rdataset, + result = match_nsec3(vctx, name, mctx, &nsec3param, &rdataset, types, maxtype, rawhash, rhsize); } @@ -634,10 +637,11 @@ verifynsec3(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, } 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) +verifynsec3s(const vctx_t *vctx, 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; @@ -647,7 +651,7 @@ verifynsec3s(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(nsec3paramset, &rdata); - result = verifynsec3(db, ver, origin, mctx, name, &rdata, + result = verifynsec3(vctx, db, ver, origin, mctx, name, &rdata, delegation, empty, types, maxtype); if (result != ISC_R_SUCCESS) break; @@ -736,12 +740,12 @@ verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, } 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) +verifynode(const vctx_t *vctx, 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; @@ -793,7 +797,7 @@ verifynode(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, result = verifynsec(db, ver, name, node, nextname); if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { - tresult = verifynsec3s(db, ver, origin, mctx, name, + tresult = verifynsec3s(vctx, db, ver, origin, mctx, name, nsec3paramset, delegation, ISC_FALSE, types, maxtype); if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) @@ -899,17 +903,17 @@ checknext(const struct nsec3_chain_fixed *first, #define EXPECTEDANDFOUND "Expected and found NSEC3 chains not equal\n" static isc_result_t -verify_nsec3_chains(isc_mem_t *mctx) { +verify_nsec3_chains(const vctx_t *vctx, 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); + while ((e = isc_heap_element(vctx->expected_chains, 1)) != NULL) { + isc_heap_delete(vctx->expected_chains, 1); if (f == NULL) - f = isc_heap_element(found_chains, 1); + f = isc_heap_element(vctx->found_chains, 1); if (f != NULL) { - isc_heap_delete(found_chains, 1); + isc_heap_delete(vctx->found_chains, 1); /* * Check that they match. @@ -926,9 +930,9 @@ verify_nsec3_chains(isc_mem_t *mctx) { */ while (f != NULL && !chain_compare(e, f)) { free_element(mctx, f); - f = isc_heap_element(found_chains, 1); + f = isc_heap_element(vctx->found_chains, 1); if (f != NULL) - isc_heap_delete(found_chains, 1); + isc_heap_delete(vctx->found_chains, 1); if (f != NULL && chain_equal(e, f)) { free_element(mctx, f); f = NULL; @@ -974,18 +978,19 @@ verify_nsec3_chains(isc_mem_t *mctx) { } free_element(mctx, f); } - f = isc_heap_element(found_chains, 1); + f = isc_heap_element(vctx->found_chains, 1); if (f != NULL) - isc_heap_delete(found_chains, 1); + isc_heap_delete(vctx->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) +verifyemptynodes(const vctx_t *vctx, 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; @@ -1007,8 +1012,9 @@ verifyemptynodes(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, &suffix); if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { - tresult = verifynsec3s(db, ver, origin, mctx, - &suffix, nsec3paramset, + tresult = verifynsec3s(vctx, db, ver, origin, + mctx, &suffix, + nsec3paramset, isdelegation, ISC_TRUE, NULL, 0); if (result == ISC_R_SUCCESS && @@ -1020,6 +1026,34 @@ verifyemptynodes(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, return (result); } +static isc_result_t +vctx_init(vctx_t *vctx, isc_mem_t *mctx) { + isc_result_t result; + + vctx->expected_chains = NULL; + result = isc_heap_create(mctx, chain_compare, NULL, 1024, + &vctx->expected_chains); + if (result != ISC_R_SUCCESS) { + return (result); + } + + vctx->found_chains = NULL; + result = isc_heap_create(mctx, chain_compare, NULL, 1024, + &vctx->found_chains); + if (result != ISC_R_SUCCESS) { + isc_heap_destroy(&vctx->expected_chains); + return (result); + } + + return (result); +} + +static void +vctx_destroy(vctx_t *vctx) { + isc_heap_destroy(&vctx->expected_chains); + isc_heap_destroy(&vctx->found_chains); +} + void dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_mem_t *mctx, isc_boolean_t ignore_kskflag, @@ -1050,13 +1084,12 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, unsigned char zsk_algorithms[256]; unsigned char bad_algorithms[256]; unsigned char act_algorithms[256]; + vctx_t vctx; - 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 = vctx_init(&vctx, mctx); + if (result != ISC_R_SUCCESS) { + return; + } result = dns_db_findnode(db, origin, ISC_FALSE, &node); if (result != ISC_R_SUCCESS) @@ -1302,7 +1335,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *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, + result = verifynode(&vctx, db, ver, origin, mctx, name, node, isdelegation, &keyset, act_algorithms, bad_algorithms, &nsecset, &nsec3paramset, nextname); @@ -1311,8 +1344,8 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS) vresult = result; if (prevname != NULL) { - result = verifyemptynodes(db, ver, origin, mctx, name, - prevname, isdelegation, + result = verifyemptynodes(&vctx, db, ver, origin, mctx, + name, prevname, isdelegation, &nsec3paramset); } else prevname = dns_fixedname_name(&fprevname); @@ -1332,11 +1365,11 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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, + result = verifynode(&vctx, 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); + record_found(&vctx, db, ver, mctx, name, node, &nsec3paramset); dns_db_detachnode(db, &node); } dns_dbiterator_destroy(&dbiter); @@ -1347,13 +1380,11 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, if (dns_rdataset_isassociated(&nsec3paramset)) dns_rdataset_disassociate(&nsec3paramset); - result = verify_nsec3_chains(mctx); + result = verify_nsec3_chains(&vctx, 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 @@ -1405,4 +1436,6 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, } } } + + vctx_destroy(&vctx); } From 43d0fb84e7744852cdfbfc4be816de453ebf9fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 05/38] Move commonly used variables to the verification context structure Move variables commonly used throughout dns_zoneverify_dnssec() and its helper functions to the structure representing a verification context in order to reduce the number of arguments passed between functions. --- lib/dns/zoneverify.c | 298 ++++++++++++++++++++++--------------------- 1 file changed, 156 insertions(+), 142 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 69ef20fd98..c8b4ce3d27 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -52,6 +52,12 @@ "dns_dbiterator_current()") typedef struct vctx { + isc_mem_t * mctx; + dns_db_t * db; + dns_dbversion_t * ver; + dns_name_t * origin; + isc_boolean_t goodksk; + isc_boolean_t goodzsk; isc_heap_t * expected_chains; isc_heap_t * found_chains; } vctx_t; @@ -90,18 +96,18 @@ check_result(isc_result_t result, const char *message) { } static 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) +is_delegation(const vctx_t *vctx, 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)) + if (dns_name_equal(name, vctx->origin)) return (ISC_FALSE); dns_rdataset_init(&nsset); - result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns, - 0, 0, &nsset, NULL); + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_ns, 0, 0, &nsset, NULL); if (dns_rdataset_isassociated(&nsset)) { if (ttlp != NULL) *ttlp = nsset.ttl; @@ -116,13 +122,14 @@ is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, * 'node'; return ISC_FALSE otherwise. */ static isc_boolean_t -has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { +has_dname(const vctx_t *vctx, dns_dbnode_t *node) { dns_rdataset_t dnameset; isc_result_t result; dns_rdataset_init(&dnameset); - result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dname, 0, 0, - &dnameset, NULL); + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_dname, 0, 0, &dnameset, + NULL); if (dns_rdataset_isassociated(&dnameset)) { dns_rdataset_disassociate(&dnameset); } @@ -131,8 +138,8 @@ has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { } 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) +goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, dns_name_t *name, + dns_rdataset_t *keyrdataset, dns_rdataset_t *rdataset) { dns_rdata_dnskey_t key; dns_rdata_rrsig_t sig; @@ -149,18 +156,18 @@ goodsig(dns_name_t *origin, dns_rdata_t *sigrdata, dns_name_t *name, 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); + result = dns_dnssec_keyfromrdata(vctx->origin, &rdata, + vctx->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)) { + !dns_name_equal(&sig.signer, vctx->origin)) { dst_key_free(&dstkey); continue; } result = dns_dnssec_verify(name, rdataset, dstkey, ISC_FALSE, - 0, mctx, sigrdata, NULL); + 0, vctx->mctx, sigrdata, NULL); dst_key_free(&dstkey); if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { return(ISC_TRUE); @@ -170,8 +177,8 @@ goodsig(dns_name_t *origin, dns_rdata_t *sigrdata, dns_name_t *name, } 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) +verifynsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, + dns_name_t *nextname) { unsigned char buffer[DNS_NSEC_BUFFERSIZE]; char namebuf[DNS_NAME_FORMATSIZE]; @@ -184,8 +191,9 @@ verifynsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, isc_result_t result; dns_rdataset_init(&rdataset); - result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, - 0, 0, &rdataset, NULL); + result = dns_db_findrdataset(vctx->db, node, vctx->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); @@ -209,8 +217,8 @@ verifynsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, goto failure; } /* Check bit map is consistent */ - result = dns_nsec_buildrdata(db, ver, node, nextname, buffer, - &tmprdata); + result = dns_nsec_buildrdata(vctx->db, vctx->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)); @@ -234,8 +242,8 @@ verifynsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, } 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) +check_no_rrsig(const vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, + dns_dbnode_t *node) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[80]; @@ -244,7 +252,7 @@ check_no_rrsig(dns_db_t *db, dns_dbversion_t *ver, dns_rdataset_t *rdataset, isc_result_t result; dns_rdataset_init(&sigrdataset); - result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + result = dns_db_allrdatasets(vctx->db, node, vctx->ver, 0, &rdsiter); check_result(result, "dns_db_allrdatasets()"); for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; @@ -349,7 +357,7 @@ record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3, } static isc_result_t -match_nsec3(const vctx_t *vctx, dns_name_t *name, isc_mem_t *mctx, +match_nsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset, unsigned char types[8192], unsigned int maxtype, unsigned char *rawhash, size_t rhsize) @@ -398,7 +406,8 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, isc_mem_t *mctx, /* * Record chain. */ - result = record_nsec3(rawhash, &nsec3, mctx, vctx->expected_chains); + result = record_nsec3(rawhash, &nsec3, vctx->mctx, + vctx->expected_chains); check_result(result, "record_nsec3()"); /* @@ -456,8 +465,7 @@ innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { } static isc_result_t -record_found(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, - isc_mem_t *mctx, dns_name_t *name, dns_dbnode_t *node, +record_found(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, dns_rdataset_t *nsec3paramset) { unsigned char owner[NSEC3_MAX_HASH_LENGTH]; @@ -471,8 +479,9 @@ record_found(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, return (ISC_R_SUCCESS); dns_rdataset_init(&rdataset); - result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, - 0, 0, &rdataset, NULL); + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_nsec3, 0, 0, &rdataset, + NULL); if (result != ISC_R_SUCCESS) return (ISC_R_SUCCESS); @@ -502,7 +511,8 @@ record_found(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, /* * Record chain. */ - result = record_nsec3(owner, &nsec3, mctx, vctx->found_chains); + result = record_nsec3(owner, &nsec3, vctx->mctx, + vctx->found_chains); check_result(result, "record_nsec3()"); } @@ -512,9 +522,7 @@ record_found(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, } static isc_boolean_t -isoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, - dns_rdata_t *nsec3rdata) -{ +isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata) { dns_rdataset_t rdataset; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_nsec3_t nsec3; @@ -531,17 +539,19 @@ isoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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); + result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, vctx->origin, + vctx->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); + result = dns_db_findnsec3node(vctx->db, hashname, ISC_FALSE, &node); if (result == ISC_R_SUCCESS) - result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, - 0, 0, &rdataset, NULL); + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_nsec3, 0, 0, + &rdataset, NULL); if (result != ISC_R_SUCCESS) return (ISC_FALSE); @@ -559,15 +569,14 @@ isoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); if (node != NULL) - dns_db_detachnode(db, &node); + dns_db_detachnode(vctx->db, &node); return (ret); } static isc_result_t -verifynsec3(const vctx_t *vctx, 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, +verifynsec3(const vctx_t *vctx, 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]; @@ -591,12 +600,13 @@ verifynsec3(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, if (!dns_nsec3_supportedhash(nsec3param.hash)) return (ISC_R_SUCCESS); - optout = isoptout(db, ver, origin, rdata); + optout = isoptout(vctx, rdata); dns_fixedname_init(&fixed); - result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, name, origin, - nsec3param.hash, nsec3param.iterations, - nsec3param.salt, nsec3param.salt_length); + result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, name, + vctx->origin, nsec3param.hash, + nsec3param.iterations, nsec3param.salt, + nsec3param.salt_length); check_result(result, "dns_nsec3_hashname()"); /* @@ -607,10 +617,11 @@ verifynsec3(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, */ dns_rdataset_init(&rdataset); hashname = dns_fixedname_name(&fixed); - result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node); + result = dns_db_findnsec3node(vctx->db, hashname, ISC_FALSE, &node); if (result == ISC_R_SUCCESS) - result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, - 0, 0, &rdataset, NULL); + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_nsec3, 0, 0, + &rdataset, NULL); if (result != ISC_R_SUCCESS && (!delegation || (empty && !optout) || (!empty && dns_nsec_isset(types, dns_rdatatype_ds)))) @@ -624,21 +635,20 @@ verifynsec3(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, { result = ISC_R_SUCCESS; } else if (result == ISC_R_SUCCESS) { - result = match_nsec3(vctx, name, mctx, &nsec3param, &rdataset, - types, maxtype, rawhash, rhsize); + result = match_nsec3(vctx, name, &nsec3param, &rdataset, types, + maxtype, rawhash, rhsize); } if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); if (node != NULL) - dns_db_detachnode(db, &node); + dns_db_detachnode(vctx->db, &node); return (result); } static isc_result_t -verifynsec3s(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, - dns_name_t *origin, isc_mem_t *mctx, dns_name_t *name, +verifynsec3s(const vctx_t *vctx, dns_name_t *name, dns_rdataset_t *nsec3paramset, isc_boolean_t delegation, isc_boolean_t empty, unsigned char types[8192], unsigned int maxtype) @@ -651,8 +661,8 @@ verifynsec3s(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(nsec3paramset, &rdata); - result = verifynsec3(vctx, db, ver, origin, mctx, name, &rdata, - delegation, empty, types, maxtype); + result = verifynsec3(vctx, name, &rdata, delegation, empty, + types, maxtype); if (result != ISC_R_SUCCESS) break; } @@ -662,8 +672,7 @@ verifynsec3s(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, } 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, +verifyset(const vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, dns_dbnode_t *node, dns_rdataset_t *keyrdataset, unsigned char *act_algorithms, unsigned char *bad_algorithms) { @@ -677,7 +686,7 @@ verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, int i; dns_rdataset_init(&sigrdataset); - result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + result = dns_db_allrdatasets(vctx->db, node, vctx->ver, 0, &rdsiter); check_result(result, "dns_db_allrdatasets()"); for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; @@ -720,7 +729,7 @@ verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, if ((set_algorithms[sig.algorithm] != 0) || (act_algorithms[sig.algorithm] == 0)) continue; - if (goodsig(origin, &rdata, name, keyrdataset, rdataset, mctx)) + if (goodsig(vctx, &rdata, name, keyrdataset, rdataset)) set_algorithms[sig.algorithm] = 1; } dns_rdatasetiter_destroy(&rdsiter); @@ -740,12 +749,11 @@ verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, } static isc_result_t -verifynode(const vctx_t *vctx, 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) +verifynode(const vctx_t *vctx, 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; @@ -753,7 +761,7 @@ verifynode(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, isc_result_t result, tresult; memset(types, 0, sizeof(types)); - result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + result = dns_db_allrdatasets(vctx->db, node, vctx->ver, 0, &rdsiter); check_result(result, "dns_db_allrdatasets()"); result = dns_rdatasetiter_first(rdsiter); dns_rdataset_init(&rdataset); @@ -770,8 +778,7 @@ verifynode(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, 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, + verifyset(vctx, &rdataset, name, node, keyrdataset, act_algorithms, bad_algorithms); dns_nsec_setbit(types, rdataset.type, 1); if (rdataset.type > maxtype) @@ -780,7 +787,7 @@ verifynode(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, 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); + check_no_rrsig(vctx, &rdataset, name, node); } else dns_nsec_setbit(types, rdataset.type, 1); dns_rdataset_disassociate(&rdataset); @@ -794,12 +801,11 @@ verifynode(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, result = ISC_R_SUCCESS; if (nsecset != NULL && dns_rdataset_isassociated(nsecset)) - result = verifynsec(db, ver, name, node, nextname); + result = verifynsec(vctx, name, node, nextname); if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { - tresult = verifynsec3s(vctx, db, ver, origin, mctx, name, - nsec3paramset, delegation, ISC_FALSE, - types, maxtype); + tresult = verifynsec3s(vctx, name, nsec3paramset, delegation, + ISC_FALSE, types, maxtype); if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) result = tresult; } @@ -807,11 +813,11 @@ verifynode(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, } static isc_boolean_t -is_empty(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { +is_empty(const vctx_t *vctx, dns_dbnode_t *node) { dns_rdatasetiter_t *rdsiter = NULL; isc_result_t result; - result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + result = dns_db_allrdatasets(vctx->db, node, vctx->ver, 0, &rdsiter); check_result(result, "dns_db_allrdatasets()"); result = dns_rdatasetiter_first(rdsiter); dns_rdatasetiter_destroy(&rdsiter); @@ -821,15 +827,14 @@ is_empty(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { } static void -check_no_nsec(dns_name_t *name, dns_dbnode_t *node, dns_db_t *db, - dns_dbversion_t *ver) -{ +check_no_nsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node) { 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); + result = dns_db_findrdataset(vctx->db, node, vctx->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)); @@ -987,10 +992,8 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { } static isc_result_t -verifyemptynodes(const vctx_t *vctx, 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) +verifyemptynodes(const vctx_t *vctx, dns_name_t *name, dns_name_t *prevname, + isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset) { dns_namereln_t reln; int order; @@ -1012,8 +1015,7 @@ verifyemptynodes(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, &suffix); if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { - tresult = verifynsec3s(vctx, db, ver, origin, - mctx, &suffix, + tresult = verifynsec3s(vctx, &suffix, nsec3paramset, isdelegation, ISC_TRUE, NULL, 0); @@ -1027,9 +1029,18 @@ verifyemptynodes(const vctx_t *vctx, dns_db_t *db, dns_dbversion_t *ver, } static isc_result_t -vctx_init(vctx_t *vctx, isc_mem_t *mctx) { +vctx_init(vctx_t *vctx, isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *origin) +{ isc_result_t result; + vctx->mctx = mctx; + vctx->db = db; + vctx->ver = ver; + vctx->origin = origin; + vctx->goodksk = ISC_FALSE; + vctx->goodzsk = ISC_FALSE; + vctx->expected_chains = NULL; result = isc_heap_create(mctx, chain_compare, NULL, 1024, &vctx->expected_chains); @@ -1073,8 +1084,6 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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]; @@ -1086,12 +1095,12 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, unsigned char act_algorithms[256]; vctx_t vctx; - result = vctx_init(&vctx, mctx); + result = vctx_init(&vctx, mctx, db, ver, origin); if (result != ISC_R_SUCCESS) { return; } - result = dns_db_findnode(db, origin, ISC_FALSE, &node); + result = dns_db_findnode(vctx.db, vctx.origin, ISC_FALSE, &node); if (result != ISC_R_SUCCESS) fatal("failed to find the zone's origin: %s", isc_result_totext(result)); @@ -1104,23 +1113,27 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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); + result = dns_db_findrdataset(vctx.db, node, vctx.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); + result = dns_db_findrdataset(vctx.db, node, vctx.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); + result = dns_db_findrdataset(vctx.db, node, vctx.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); + result = dns_db_findrdataset(vctx.db, node, vctx.ver, + dns_rdatatype_nsec3param, 0, 0, + &nsec3paramset, &nsec3paramsigs); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) fatal("NSEC3PARAM lookup failed\n"); @@ -1142,7 +1155,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, !dns_rdataset_isassociated(&nsec3paramset)) fatal("No valid NSEC/NSEC3 chain for testing\n"); - dns_db_detachnode(db, &node); + dns_db_detachnode(vctx.db, &node); memset(revoked_ksk, 0, sizeof(revoked_ksk)); memset(revoked_zsk, 0, sizeof(revoked_zsk)); @@ -1169,14 +1182,14 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, ; else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && - !dns_dnssec_selfsigns(&rdata, origin, &keyset, + !dns_dnssec_selfsigns(&rdata, vctx.origin, &keyset, &keysigs, ISC_FALSE, - mctx)) { + vctx.mctx)) { char namebuf[DNS_NAME_FORMATSIZE]; char buffer[1024]; isc_buffer_t buf; - dns_name_format(origin, namebuf, + dns_name_format(vctx.origin, namebuf, sizeof(namebuf)); isc_buffer_init(&buf, buffer, sizeof(buffer)); result = dns_rdata_totext(&rdata, NULL, &buf); @@ -1192,22 +1205,24 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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 (dns_dnssec_selfsigns(&rdata, vctx.origin, &keyset, + &keysigs, ISC_FALSE, + vctx.mctx)) { if (ksk_algorithms[dnskey.algorithm] != 255) ksk_algorithms[dnskey.algorithm]++; - goodksk = ISC_TRUE; + vctx.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)) { + } else if (dns_dnssec_selfsigns(&rdata, vctx.origin, &keyset, + &keysigs, ISC_FALSE, + vctx.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)) { + vctx.goodzsk = ISC_TRUE; + } else if (dns_dnssec_signs(&rdata, vctx.origin, &soaset, + &soasigs, ISC_FALSE, vctx.mctx)) { if (zsk_algorithms[dnskey.algorithm] != 255) zsk_algorithms[dnskey.algorithm]++; } else { @@ -1226,9 +1241,9 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, dns_rdataset_disassociate(&nsec3paramsigs); if (ignore_kskflag ) { - if (!goodksk && !goodzsk) + if (!vctx.goodksk && !vctx.goodzsk) fatal("No self-signed DNSKEY found."); - } else if (!goodksk) + } else if (!vctx.goodksk) fatal("No self-signed KSK DNSKEY found. Supply an active\n" "key with the KSK flag set, or use '-P'."); @@ -1277,7 +1292,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, dns_fixedname_init(&fzonecut); zonecut = NULL; - result = dns_db_createiterator(db, DNS_DB_NONSEC3, &dbiter); + result = dns_db_createiterator(vctx.db, DNS_DB_NONSEC3, &dbiter); check_result(result, "dns_db_createiterator()"); result = dns_dbiterator_first(dbiter); @@ -1288,9 +1303,9 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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); + if (!dns_name_issubdomain(name, vctx.origin)) { + check_no_nsec(&vctx, name, node); + dns_db_detachnode(vctx.db, &node); result = dns_dbiterator_next(dbiter); if (result == ISC_R_NOMORE) done = ISC_TRUE; @@ -1298,11 +1313,11 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, check_result(result, "dns_dbiterator_next()"); continue; } - if (is_delegation(db, ver, origin, name, node, NULL)) { + if (is_delegation(&vctx, name, node, NULL)) { zonecut = dns_fixedname_name(&fzonecut); dns_name_copy(name, zonecut, NULL); isdelegation = ISC_TRUE; - } else if (has_dname(db, ver, node)) { + } else if (has_dname(&vctx, node)) { zonecut = dns_fixedname_name(&fzonecut); dns_name_copy(name, zonecut, NULL); } @@ -1312,52 +1327,51 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, result = dns_dbiterator_current(dbiter, &nextnode, nextname); check_dns_dbiterator_current(result); - if (!dns_name_issubdomain(nextname, origin) || + if (!dns_name_issubdomain(nextname, vctx.origin) || (zonecut != NULL && dns_name_issubdomain(nextname, zonecut))) { - check_no_nsec(nextname, nextnode, db, ver); - dns_db_detachnode(db, &nextnode); + check_no_nsec(&vctx, nextname, nextnode); + dns_db_detachnode(vctx.db, &nextnode); result = dns_dbiterator_next(dbiter); continue; } - if (is_empty(db, ver, nextnode)) { - dns_db_detachnode(db, &nextnode); + if (is_empty(&vctx, nextnode)) { + dns_db_detachnode(vctx.db, &nextnode); result = dns_dbiterator_next(dbiter); continue; } - dns_db_detachnode(db, &nextnode); + dns_db_detachnode(vctx.db, &nextnode); break; } if (result == ISC_R_NOMORE) { done = ISC_TRUE; - nextname = origin; + nextname = vctx.origin; } else if (result != ISC_R_SUCCESS) fatal("iterating through the database failed: %s", isc_result_totext(result)); - result = verifynode(&vctx, db, ver, origin, mctx, name, node, - isdelegation, &keyset, act_algorithms, - bad_algorithms, &nsecset, &nsec3paramset, - nextname); + result = verifynode(&vctx, 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(&vctx, db, ver, origin, mctx, - name, prevname, isdelegation, + result = verifyemptynodes(&vctx, 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_db_detachnode(vctx.db, &node); } dns_dbiterator_destroy(&dbiter); - result = dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbiter); + result = dns_db_createiterator(vctx.db, DNS_DB_NSEC3ONLY, &dbiter); check_result(result, "dns_db_createiterator()"); for (result = dns_dbiterator_first(dbiter); @@ -1365,12 +1379,12 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, result = dns_dbiterator_next(dbiter) ) { result = dns_dbiterator_current(dbiter, &node, name); check_dns_dbiterator_current(result); - result = verifynode(&vctx, db, ver, origin, mctx, name, node, - ISC_FALSE, &keyset, act_algorithms, - bad_algorithms, NULL, NULL, NULL); + result = verifynode(&vctx, name, node, ISC_FALSE, &keyset, + act_algorithms, bad_algorithms, NULL, NULL, + NULL); check_result(result, "verifynode"); - record_found(&vctx, db, ver, mctx, name, node, &nsec3paramset); - dns_db_detachnode(db, &node); + record_found(&vctx, name, node, &nsec3paramset); + dns_db_detachnode(vctx.db, &node); } dns_dbiterator_destroy(&dbiter); @@ -1409,7 +1423,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, fatal("DNSSEC completeness test failed (%s).", dns_result_totext(vresult)); - if (goodksk || ignore_kskflag) { + if (vctx.goodksk || ignore_kskflag) { /* * Print the success summary. */ From 5d666f533bafafc38573882791e7221dc7fe7ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 06/38] Move commonly used dns_rdataset_t structures to the verification context structure Eight structures representing four RRsets and their signatures are commonly accessed throughout dns_zoneverify_dnssec(). Move them into the structure representing a verification context. While this does not really simplify currently existing code, it will facilitate passing data around between smaller functions that dns_zoneverify_dnssec() is about to get split into. --- lib/dns/zoneverify.c | 139 ++++++++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index c8b4ce3d27..82c016d595 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -58,6 +58,14 @@ typedef struct vctx { dns_name_t * origin; isc_boolean_t goodksk; isc_boolean_t goodzsk; + dns_rdataset_t keyset; + dns_rdataset_t keysigs; + dns_rdataset_t soaset; + dns_rdataset_t soasigs; + dns_rdataset_t nsecset; + dns_rdataset_t nsecsigs; + dns_rdataset_t nsec3paramset; + dns_rdataset_t nsec3paramsigs; isc_heap_t * expected_chains; isc_heap_t * found_chains; } vctx_t; @@ -1041,6 +1049,15 @@ vctx_init(vctx_t *vctx, isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, vctx->goodksk = ISC_FALSE; vctx->goodzsk = ISC_FALSE; + dns_rdataset_init(&vctx->keyset); + dns_rdataset_init(&vctx->keysigs); + dns_rdataset_init(&vctx->soaset); + dns_rdataset_init(&vctx->soasigs); + dns_rdataset_init(&vctx->nsecset); + dns_rdataset_init(&vctx->nsecsigs); + dns_rdataset_init(&vctx->nsec3paramset); + dns_rdataset_init(&vctx->nsec3paramsigs); + vctx->expected_chains = NULL; result = isc_heap_create(mctx, chain_compare, NULL, 1024, &vctx->expected_chains); @@ -1061,6 +1078,30 @@ vctx_init(vctx_t *vctx, isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, static void vctx_destroy(vctx_t *vctx) { + if (dns_rdataset_isassociated(&vctx->keyset)) { + dns_rdataset_disassociate(&vctx->keyset); + } + if (dns_rdataset_isassociated(&vctx->keysigs)) { + dns_rdataset_disassociate(&vctx->keysigs); + } + if (dns_rdataset_isassociated(&vctx->soaset)) { + dns_rdataset_disassociate(&vctx->soaset); + } + if (dns_rdataset_isassociated(&vctx->soasigs)) { + dns_rdataset_disassociate(&vctx->soasigs); + } + if (dns_rdataset_isassociated(&vctx->nsecset)) { + dns_rdataset_disassociate(&vctx->nsecset); + } + if (dns_rdataset_isassociated(&vctx->nsecsigs)) { + dns_rdataset_disassociate(&vctx->nsecsigs); + } + if (dns_rdataset_isassociated(&vctx->nsec3paramset)) { + dns_rdataset_disassociate(&vctx->nsec3paramset); + } + if (dns_rdataset_isassociated(&vctx->nsec3paramsigs)) { + dns_rdataset_disassociate(&vctx->nsec3paramsigs); + } isc_heap_destroy(&vctx->expected_chains); isc_heap_destroy(&vctx->found_chains); } @@ -1077,10 +1118,6 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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; @@ -1105,54 +1142,47 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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(vctx.db, node, vctx.ver, - dns_rdatatype_dnskey, 0, 0, &keyset, - &keysigs); + dns_rdatatype_dnskey, 0, 0, &vctx.keyset, + &vctx.keysigs); if (result != ISC_R_SUCCESS) fatal("Zone contains no DNSSEC keys\n"); result = dns_db_findrdataset(vctx.db, node, vctx.ver, - dns_rdatatype_soa, 0, 0, &soaset, - &soasigs); + dns_rdatatype_soa, 0, 0, &vctx.soaset, + &vctx.soasigs); if (result != ISC_R_SUCCESS) fatal("Zone contains no SOA record\n"); result = dns_db_findrdataset(vctx.db, node, vctx.ver, - dns_rdatatype_nsec, 0, 0, &nsecset, - &nsecsigs); + dns_rdatatype_nsec, 0, 0, &vctx.nsecset, + &vctx.nsecsigs); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) fatal("NSEC lookup failed\n"); result = dns_db_findrdataset(vctx.db, node, vctx.ver, dns_rdatatype_nsec3param, 0, 0, - &nsec3paramset, &nsec3paramsigs); + &vctx.nsec3paramset, + &vctx.nsec3paramsigs); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) fatal("NSEC3PARAM lookup failed\n"); - if (!dns_rdataset_isassociated(&keysigs)) + if (!dns_rdataset_isassociated(&vctx.keysigs)) fatal("DNSKEY is not signed (keys offline or inactive?)\n"); - if (!dns_rdataset_isassociated(&soasigs)) + if (!dns_rdataset_isassociated(&vctx.soasigs)) fatal("SOA is not signed (keys offline or inactive?)\n"); - if (dns_rdataset_isassociated(&nsecset) && - !dns_rdataset_isassociated(&nsecsigs)) + if (dns_rdataset_isassociated(&vctx.nsecset) && + !dns_rdataset_isassociated(&vctx.nsecsigs)) fatal("NSEC is not signed (keys offline or inactive?)\n"); - if (dns_rdataset_isassociated(&nsec3paramset) && - !dns_rdataset_isassociated(&nsec3paramsigs)) + if (dns_rdataset_isassociated(&vctx.nsec3paramset) && + !dns_rdataset_isassociated(&vctx.nsec3paramsigs)) fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n"); - if (!dns_rdataset_isassociated(&nsecset) && - !dns_rdataset_isassociated(&nsec3paramset)) + if (!dns_rdataset_isassociated(&vctx.nsecset) && + !dns_rdataset_isassociated(&vctx.nsec3paramset)) fatal("No valid NSEC/NSEC3 chain for testing\n"); dns_db_detachnode(vctx.db, &node); @@ -1171,10 +1201,10 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, * and one ZSK per algorithm in it (or, if -x was used, one * self-signing KSK). */ - for (result = dns_rdataset_first(&keyset); + for (result = dns_rdataset_first(&vctx.keyset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(&keyset)) { - dns_rdataset_current(&keyset, &rdata); + result = dns_rdataset_next(&vctx.keyset)) { + dns_rdataset_current(&vctx.keyset, &rdata); result = dns_rdata_tostruct(&rdata, &dnskey, NULL); check_result(result, "dns_rdata_tostruct"); @@ -1182,9 +1212,9 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, ; else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && - !dns_dnssec_selfsigns(&rdata, vctx.origin, &keyset, - &keysigs, ISC_FALSE, - vctx.mctx)) { + !dns_dnssec_selfsigns(&rdata, vctx.origin, + &vctx.keyset, &vctx.keysigs, + ISC_FALSE, vctx.mctx)) { char namebuf[DNS_NAME_FORMATSIZE]; char buffer[1024]; isc_buffer_t buf; @@ -1205,9 +1235,9 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, revoked_zsk[dnskey.algorithm] != 255) revoked_zsk[dnskey.algorithm]++; } else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) { - if (dns_dnssec_selfsigns(&rdata, vctx.origin, &keyset, - &keysigs, ISC_FALSE, - vctx.mctx)) { + if (dns_dnssec_selfsigns(&rdata, vctx.origin, + &vctx.keyset, &vctx.keysigs, + ISC_FALSE, vctx.mctx)) { if (ksk_algorithms[dnskey.algorithm] != 255) ksk_algorithms[dnskey.algorithm]++; vctx.goodksk = ISC_TRUE; @@ -1215,14 +1245,15 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, if (standby_ksk[dnskey.algorithm] != 255) standby_ksk[dnskey.algorithm]++; } - } else if (dns_dnssec_selfsigns(&rdata, vctx.origin, &keyset, - &keysigs, ISC_FALSE, - vctx.mctx)) { + } else if (dns_dnssec_selfsigns(&rdata, vctx.origin, + &vctx.keyset, &vctx.keysigs, + ISC_FALSE, vctx.mctx)) { if (zsk_algorithms[dnskey.algorithm] != 255) zsk_algorithms[dnskey.algorithm]++; vctx.goodzsk = ISC_TRUE; - } else if (dns_dnssec_signs(&rdata, vctx.origin, &soaset, - &soasigs, ISC_FALSE, vctx.mctx)) { + } else if (dns_dnssec_signs(&rdata, vctx.origin, &vctx.soaset, + &vctx.soasigs, ISC_FALSE, + vctx.mctx)) { if (zsk_algorithms[dnskey.algorithm] != 255) zsk_algorithms[dnskey.algorithm]++; } else { @@ -1232,13 +1263,6 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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 (!vctx.goodksk && !vctx.goodzsk) @@ -1350,9 +1374,10 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, } else if (result != ISC_R_SUCCESS) fatal("iterating through the database failed: %s", isc_result_totext(result)); - result = verifynode(&vctx, name, node, isdelegation, &keyset, - act_algorithms, bad_algorithms, &nsecset, - &nsec3paramset, nextname); + result = verifynode(&vctx, name, node, isdelegation, + &vctx.keyset, act_algorithms, + bad_algorithms, &vctx.nsecset, + &vctx.nsec3paramset, nextname); if (vresult == ISC_R_UNSET) vresult = ISC_R_SUCCESS; if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS) @@ -1360,7 +1385,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, if (prevname != NULL) { result = verifyemptynodes(&vctx, name, prevname, isdelegation, - &nsec3paramset); + &vctx.nsec3paramset); } else prevname = dns_fixedname_name(&fprevname); dns_name_copy(name, prevname, NULL); @@ -1379,21 +1404,15 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, result = dns_dbiterator_next(dbiter) ) { result = dns_dbiterator_current(dbiter, &node, name); check_dns_dbiterator_current(result); - result = verifynode(&vctx, name, node, ISC_FALSE, &keyset, + result = verifynode(&vctx, name, node, ISC_FALSE, &vctx.keyset, act_algorithms, bad_algorithms, NULL, NULL, NULL); check_result(result, "verifynode"); - record_found(&vctx, name, node, &nsec3paramset); + record_found(&vctx, name, node, &vctx.nsec3paramset); dns_db_detachnode(vctx.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(&vctx, mctx); if (vresult == ISC_R_UNSET) vresult = ISC_R_SUCCESS; From 730cc3e3c501839bd5e0c626b5e177d6f421c3cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 07/38] Move algorithm tables to the verification context structure Tables representing algorithm use in the verified zone are commonly accessed throughout dns_zoneverify_dnssec(). Move them into the structure representing a verification context. While this does not really simplify currently existing code, it will facilitate passing data around between smaller functions that dns_zoneverify_dnssec() is about to get split into. --- lib/dns/zoneverify.c | 128 ++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 68 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 82c016d595..193f7dc4a6 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -66,6 +66,14 @@ typedef struct vctx { dns_rdataset_t nsecsigs; dns_rdataset_t nsec3paramset; dns_rdataset_t nsec3paramsigs; + 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]; isc_heap_t * expected_chains; isc_heap_t * found_chains; } vctx_t; @@ -680,9 +688,8 @@ verifynsec3s(const vctx_t *vctx, dns_name_t *name, } static void -verifyset(const vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, - dns_dbnode_t *node, dns_rdataset_t *keyrdataset, - unsigned char *act_algorithms, unsigned char *bad_algorithms) +verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, + dns_dbnode_t *node, dns_rdataset_t *keyrdataset) { unsigned char set_algorithms[256]; char namebuf[DNS_NAME_FORMATSIZE]; @@ -710,8 +717,8 @@ verifyset(const vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, dns_rdatatype_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; + if (vctx->act_algorithms[i] != 0) + vctx->bad_algorithms[i] = 1; dns_rdatasetiter_destroy(&rdsiter); return; } @@ -735,31 +742,31 @@ verifyset(const vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, continue; } if ((set_algorithms[sig.algorithm] != 0) || - (act_algorithms[sig.algorithm] == 0)) + (vctx->act_algorithms[sig.algorithm] == 0)) continue; if (goodsig(vctx, &rdata, name, keyrdataset, rdataset)) set_algorithms[sig.algorithm] = 1; } dns_rdatasetiter_destroy(&rdsiter); - if (memcmp(set_algorithms, act_algorithms, sizeof(set_algorithms))) { + if (memcmp(set_algorithms, vctx->act_algorithms, + sizeof(set_algorithms))) { dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); for (i = 0; i < 256; i++) - if ((act_algorithms[i] != 0) && + if ((vctx->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; + vctx->bad_algorithms[i] = 1; } } dns_rdataset_disassociate(&sigrdataset); } static isc_result_t -verifynode(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, +verifynode(vctx_t *vctx, 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) { @@ -786,8 +793,7 @@ verifynode(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, rdataset.type != dns_rdatatype_dnskey && (!delegation || rdataset.type == dns_rdatatype_ds || rdataset.type == dns_rdatatype_nsec)) { - verifyset(vctx, &rdataset, name, node, keyrdataset, - act_algorithms, bad_algorithms); + verifyset(vctx, &rdataset, name, node, keyrdataset); dns_nsec_setbit(types, rdataset.type, 1); if (rdataset.type > maxtype) maxtype = rdataset.type; @@ -1042,6 +1048,8 @@ vctx_init(vctx_t *vctx, isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, { isc_result_t result; + memset(vctx, 0, sizeof(*vctx)); + vctx->mctx = mctx; vctx->db = db; vctx->ver = ver; @@ -1122,14 +1130,6 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_boolean_t done = ISC_FALSE; isc_boolean_t first = ISC_TRUE; 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]; vctx_t vctx; result = vctx_init(&vctx, mctx, db, ver, origin); @@ -1187,15 +1187,6 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, dns_db_detachnode(vctx.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 @@ -1229,36 +1220,36 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, (int)isc_buffer_usedlength(&buf), buffer); } if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && - revoked_ksk[dnskey.algorithm] != 255) - revoked_ksk[dnskey.algorithm]++; + vctx.revoked_ksk[dnskey.algorithm] != 255) + vctx.revoked_ksk[dnskey.algorithm]++; else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && - revoked_zsk[dnskey.algorithm] != 255) - revoked_zsk[dnskey.algorithm]++; + vctx.revoked_zsk[dnskey.algorithm] != 255) + vctx.revoked_zsk[dnskey.algorithm]++; } else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) { if (dns_dnssec_selfsigns(&rdata, vctx.origin, &vctx.keyset, &vctx.keysigs, ISC_FALSE, vctx.mctx)) { - if (ksk_algorithms[dnskey.algorithm] != 255) - ksk_algorithms[dnskey.algorithm]++; + if (vctx.ksk_algorithms[dnskey.algorithm] != 255) + vctx.ksk_algorithms[dnskey.algorithm]++; vctx.goodksk = ISC_TRUE; } else { - if (standby_ksk[dnskey.algorithm] != 255) - standby_ksk[dnskey.algorithm]++; + if (vctx.standby_ksk[dnskey.algorithm] != 255) + vctx.standby_ksk[dnskey.algorithm]++; } } else if (dns_dnssec_selfsigns(&rdata, vctx.origin, &vctx.keyset, &vctx.keysigs, ISC_FALSE, vctx.mctx)) { - if (zsk_algorithms[dnskey.algorithm] != 255) - zsk_algorithms[dnskey.algorithm]++; + if (vctx.zsk_algorithms[dnskey.algorithm] != 255) + vctx.zsk_algorithms[dnskey.algorithm]++; vctx.goodzsk = ISC_TRUE; } else if (dns_dnssec_signs(&rdata, vctx.origin, &vctx.soaset, &vctx.soasigs, ISC_FALSE, vctx.mctx)) { - if (zsk_algorithms[dnskey.algorithm] != 255) - zsk_algorithms[dnskey.algorithm]++; + if (vctx.zsk_algorithms[dnskey.algorithm] != 255) + vctx.zsk_algorithms[dnskey.algorithm]++; } else { - if (standby_zsk[dnskey.algorithm] != 255) - standby_zsk[dnskey.algorithm]++; + if (vctx.standby_zsk[dnskey.algorithm] != 255) + vctx.standby_zsk[dnskey.algorithm]++; } dns_rdata_freestruct(&dnskey); dns_rdata_reset(&rdata); @@ -1274,11 +1265,13 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, 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; + vctx.act_algorithms[i] = + (vctx.ksk_algorithms[i] != 0 || + vctx.zsk_algorithms[i] != 0) ? 1 : 0; else - act_algorithms[i] = ksk_algorithms[i] != 0 ? 1 : 0; - if (act_algorithms[i] != 0) { + vctx.act_algorithms[i] = + vctx.ksk_algorithms[i] != 0 ? 1 : 0; + if (vctx.act_algorithms[i] != 0) { dns_secalg_format(i, algbuf, sizeof(algbuf)); fprintf(stderr, " %s", algbuf); } @@ -1291,16 +1284,16 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, * 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)) + if ((vctx.ksk_algorithms[i] != 0) == + (vctx.zsk_algorithms[i] != 0)) continue; dns_secalg_format(i, algbuf, sizeof(algbuf)); fprintf(stderr, "Missing %s for algorithm %s\n", - (ksk_algorithms[i] != 0) + (vctx.ksk_algorithms[i] != 0) ? "ZSK" : "self-signed KSK", algbuf); - bad_algorithms[i] = 1; + vctx.bad_algorithms[i] = 1; } } @@ -1375,8 +1368,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, fatal("iterating through the database failed: %s", isc_result_totext(result)); result = verifynode(&vctx, name, node, isdelegation, - &vctx.keyset, act_algorithms, - bad_algorithms, &vctx.nsecset, + &vctx.keyset, &vctx.nsecset, &vctx.nsec3paramset, nextname); if (vresult == ISC_R_UNSET) vresult = ISC_R_SUCCESS; @@ -1405,8 +1397,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, result = dns_dbiterator_current(dbiter, &node, name); check_dns_dbiterator_current(result); result = verifynode(&vctx, name, node, ISC_FALSE, &vctx.keyset, - act_algorithms, bad_algorithms, NULL, NULL, - NULL); + NULL, NULL, NULL); check_result(result, "verifynode"); record_found(&vctx, name, node, &vctx.nsec3paramset); dns_db_detachnode(vctx.db, &node); @@ -1424,7 +1415,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, * zone. Set the good flag. */ for (i = 0; i < 256; i++) { - if (bad_algorithms[i] != 0) { + if (vctx.bad_algorithms[i] != 0) { if (first) fprintf(stderr, "The zone is not fully signed " "for the following algorithms:"); @@ -1448,24 +1439,25 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, */ fprintf(stderr, "Zone fully signed:\n"); for (i = 0; i < 256; i++) { - if ((ksk_algorithms[i] != 0) || - (standby_ksk[i] != 0) || - (revoked_ksk[i] != 0) || - (zsk_algorithms[i] != 0) || - (standby_zsk[i] != 0) || - (revoked_zsk[i] != 0)) { + if ((vctx.ksk_algorithms[i] != 0) || + (vctx.standby_ksk[i] != 0) || + (vctx.revoked_ksk[i] != 0) || + (vctx.zsk_algorithms[i] != 0) || + (vctx.standby_zsk[i] != 0) || + (vctx.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]); + algbuf, vctx.ksk_algorithms[i], + vctx.standby_ksk[i], + vctx.revoked_ksk[i]); fprintf(stderr, "%*sZSKs: " "%u active, %u %s, %u revoked\n", (int) strlen(algbuf) + 13, "", - zsk_algorithms[i], - standby_zsk[i], + vctx.zsk_algorithms[i], + vctx.standby_zsk[i], keyset_kskonly ? "present" : "stand-by", - revoked_zsk[i]); + vctx.revoked_zsk[i]); } } } From d949a5d83cb5cab80a2b246703d0c95fa3274a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 08/38] Implement zoneverify_log_error() and zoneverify_print() These functions will be used in the process of replacing fatal(), check_result(), and fprintf() calls throughout lib/dns/zoneverify.c with code that does not call exit(). They are intended for: - zoneverify_log_error(): logging problems encountered while performing zone verification, - zoneverify_print(): printing status messages and reports which are only useful in standalone tools. To make using dns_zone_logv() possible, add a new "zone" argument to dns_zoneverify_dnssec() that standalone tools are expected to set to NULL. --- bin/dnssec/dnssec-signzone.c | 2 +- bin/dnssec/dnssec-verify.c | 4 +-- lib/dns/include/dns/zoneverify.h | 5 +-- lib/dns/zoneverify.c | 54 +++++++++++++++++++++++++++++--- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index d857c844d0..3d3e6f80f1 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -3913,7 +3913,7 @@ main(int argc, char *argv[]) { TIME_NOW(&sign_finish); if (!disable_zone_check) - dns_zoneverify_dnssec(gdb, gversion, gorigin, mctx, + dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, mctx, ignore_kskflag, keyset_kskonly); if (outputformat != dns_masterformat_text) { diff --git a/bin/dnssec/dnssec-verify.c b/bin/dnssec/dnssec-verify.c index c236235750..48b9e0b186 100644 --- a/bin/dnssec/dnssec-verify.c +++ b/bin/dnssec/dnssec-verify.c @@ -323,8 +323,8 @@ main(int argc, char *argv[]) { result = dns_db_newversion(gdb, &gversion); check_result(result, "dns_db_newversion()"); - dns_zoneverify_dnssec(gdb, gversion, gorigin, mctx, ignore_kskflag, - keyset_kskonly); + dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, mctx, + ignore_kskflag, keyset_kskonly); dns_db_closeversion(gdb, &gversion, ISC_FALSE); dns_db_detach(&gdb); diff --git a/lib/dns/include/dns/zoneverify.h b/lib/dns/include/dns/zoneverify.h index facdcf9dbb..090fcc2f9b 100644 --- a/lib/dns/include/dns/zoneverify.h +++ b/lib/dns/include/dns/zoneverify.h @@ -32,8 +32,9 @@ ISC_LANG_BEGINDECLS * present in the DNSKEY RRSET. */ void -dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, - isc_mem_t *mctx, isc_boolean_t ignore_kskflag, +dns_zoneverify_dnssec(dns_zone_t *zone, 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); ISC_LANG_ENDDECLS diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 193f7dc4a6..c13b83a51b 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -41,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +56,7 @@ typedef struct vctx { isc_mem_t * mctx; + dns_zone_t * zone; dns_db_t * db; dns_dbversion_t * ver; dns_name_t * origin; @@ -105,6 +109,44 @@ fatal(const char *format, ...) { exit(1); } +/*% + * Log a zone verification error described by 'fmt' and the variable arguments + * following it. Either use dns_zone_logv() or print to stderr, depending on + * whether the function was invoked from within named or by a standalone tool, + * respectively. + */ +static void +zoneverify_log_error(const vctx_t *vctx, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (vctx->zone != NULL) { + dns_zone_logv(vctx->zone, DNS_LOGCATEGORY_GENERAL, + ISC_LOG_ERROR, NULL, fmt, ap); + } else { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } + va_end(ap); +} + +/*% + * If invoked from a standalone tool, print a message described by 'fmt' and + * the variable arguments following it to stderr. + */ +static void +zoneverify_print(const vctx_t *vctx, const char *fmt, ...) { + va_list ap; + + if (vctx->zone != NULL) { + return; + } + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + static void check_result(isc_result_t result, const char *message) { if (result != ISC_R_SUCCESS) @@ -1043,14 +1085,15 @@ verifyemptynodes(const vctx_t *vctx, dns_name_t *name, dns_name_t *prevname, } static isc_result_t -vctx_init(vctx_t *vctx, isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, - dns_name_t *origin) +vctx_init(vctx_t *vctx, isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *origin) { isc_result_t result; memset(vctx, 0, sizeof(*vctx)); vctx->mctx = mctx; + vctx->zone = zone; vctx->db = db; vctx->ver = ver; vctx->origin = origin; @@ -1115,8 +1158,9 @@ vctx_destroy(vctx_t *vctx) { } void -dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, - isc_mem_t *mctx, isc_boolean_t ignore_kskflag, +dns_zoneverify_dnssec(dns_zone_t *zone, 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]; @@ -1132,7 +1176,7 @@ dns_zoneverify_dnssec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_result_t result, vresult = ISC_R_UNSET; vctx_t vctx; - result = vctx_init(&vctx, mctx, db, ver, origin); + result = vctx_init(&vctx, mctx, zone, db, ver, origin); if (result != ISC_R_SUCCESS) { return; } From 097b57744ae1a47e92919c1ae837226cf9eeeac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 09/38] Extract check_apex_rrsets() from dns_zoneverify_dnssec() Extract the part of dns_zoneverify_dnssec() responsible for fetching and preliminarily checking DNSKEY, SOA, NSEC, and NSEC3PARAM RRsets from zone apex to a separate function. --- lib/dns/zoneverify.c | 106 +++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 49 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index c13b83a51b..5e4da21569 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1157,6 +1157,62 @@ vctx_destroy(vctx_t *vctx) { isc_heap_destroy(&vctx->found_chains); } +static void +check_apex_rrsets(vctx_t *vctx) { + dns_dbnode_t *node = NULL; + isc_result_t result; + + result = dns_db_findnode(vctx->db, vctx->origin, ISC_FALSE, &node); + if (result != ISC_R_SUCCESS) + fatal("failed to find the zone's origin: %s", + isc_result_totext(result)); + + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_dnskey, 0, 0, + &vctx->keyset, &vctx->keysigs); + if (result != ISC_R_SUCCESS) + fatal("Zone contains no DNSSEC keys\n"); + + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_soa, 0, 0, + &vctx->soaset, &vctx->soasigs); + if (result != ISC_R_SUCCESS) + fatal("Zone contains no SOA record\n"); + + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_nsec, 0, 0, + &vctx->nsecset, &vctx->nsecsigs); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + fatal("NSEC lookup failed\n"); + + result = dns_db_findrdataset(vctx->db, node, vctx->ver, + dns_rdatatype_nsec3param, 0, 0, + &vctx->nsec3paramset, + &vctx->nsec3paramsigs); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + fatal("NSEC3PARAM lookup failed\n"); + + if (!dns_rdataset_isassociated(&vctx->keysigs)) + fatal("DNSKEY is not signed (keys offline or inactive?)\n"); + + if (!dns_rdataset_isassociated(&vctx->soasigs)) + fatal("SOA is not signed (keys offline or inactive?)\n"); + + if (dns_rdataset_isassociated(&vctx->nsecset) && + !dns_rdataset_isassociated(&vctx->nsecsigs)) + fatal("NSEC is not signed (keys offline or inactive?)\n"); + + if (dns_rdataset_isassociated(&vctx->nsec3paramset) && + !dns_rdataset_isassociated(&vctx->nsec3paramsigs)) + fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n"); + + if (!dns_rdataset_isassociated(&vctx->nsecset) && + !dns_rdataset_isassociated(&vctx->nsec3paramset)) + fatal("No valid NSEC/NSEC3 chain for testing\n"); + + dns_db_detachnode(vctx->db, &node); +} + void dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_mem_t *mctx, @@ -1181,55 +1237,7 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, return; } - result = dns_db_findnode(vctx.db, vctx.origin, ISC_FALSE, &node); - if (result != ISC_R_SUCCESS) - fatal("failed to find the zone's origin: %s", - isc_result_totext(result)); - - result = dns_db_findrdataset(vctx.db, node, vctx.ver, - dns_rdatatype_dnskey, 0, 0, &vctx.keyset, - &vctx.keysigs); - if (result != ISC_R_SUCCESS) - fatal("Zone contains no DNSSEC keys\n"); - - result = dns_db_findrdataset(vctx.db, node, vctx.ver, - dns_rdatatype_soa, 0, 0, &vctx.soaset, - &vctx.soasigs); - if (result != ISC_R_SUCCESS) - fatal("Zone contains no SOA record\n"); - - result = dns_db_findrdataset(vctx.db, node, vctx.ver, - dns_rdatatype_nsec, 0, 0, &vctx.nsecset, - &vctx.nsecsigs); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) - fatal("NSEC lookup failed\n"); - - result = dns_db_findrdataset(vctx.db, node, vctx.ver, - dns_rdatatype_nsec3param, 0, 0, - &vctx.nsec3paramset, - &vctx.nsec3paramsigs); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) - fatal("NSEC3PARAM lookup failed\n"); - - if (!dns_rdataset_isassociated(&vctx.keysigs)) - fatal("DNSKEY is not signed (keys offline or inactive?)\n"); - - if (!dns_rdataset_isassociated(&vctx.soasigs)) - fatal("SOA is not signed (keys offline or inactive?)\n"); - - if (dns_rdataset_isassociated(&vctx.nsecset) && - !dns_rdataset_isassociated(&vctx.nsecsigs)) - fatal("NSEC is not signed (keys offline or inactive?)\n"); - - if (dns_rdataset_isassociated(&vctx.nsec3paramset) && - !dns_rdataset_isassociated(&vctx.nsec3paramsigs)) - fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n"); - - if (!dns_rdataset_isassociated(&vctx.nsecset) && - !dns_rdataset_isassociated(&vctx.nsec3paramset)) - fatal("No valid NSEC/NSEC3 chain for testing\n"); - - dns_db_detachnode(vctx.db, &node); + check_apex_rrsets(&vctx); /* * Check that the DNSKEY RR has at least one self signing KSK From d4f3b14c787ccc3e8278436f2b2de9a6aa540b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 10/38] Extract check_dnskey() from dns_zoneverify_dnssec() Extract the part of dns_zoneverify_dnssec() responsible for checking the DNSKEY RRset at zone apex to a separate function. --- lib/dns/zoneverify.c | 145 +++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 69 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 5e4da21569..04f5a94856 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1213,6 +1213,81 @@ check_apex_rrsets(vctx_t *vctx) { dns_db_detachnode(vctx->db, &node); } +/*% + * 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). + */ +static void +check_dnskey(vctx_t *vctx) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_dnskey_t dnskey; + isc_result_t result; + + for (result = dns_rdataset_first(&vctx->keyset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&vctx->keyset)) { + dns_rdataset_current(&vctx->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, vctx->origin, + &vctx->keyset, + &vctx->keysigs, ISC_FALSE, + vctx->mctx)) { + char namebuf[DNS_NAME_FORMATSIZE]; + char buffer[1024]; + isc_buffer_t buf; + + dns_name_format(vctx->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 && + vctx->revoked_ksk[dnskey.algorithm] != 255) + vctx->revoked_ksk[dnskey.algorithm]++; + else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && + vctx->revoked_zsk[dnskey.algorithm] != 255) + vctx->revoked_zsk[dnskey.algorithm]++; + } else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) { + if (dns_dnssec_selfsigns(&rdata, vctx->origin, + &vctx->keyset, &vctx->keysigs, + ISC_FALSE, vctx->mctx)) { + if (vctx->ksk_algorithms[dnskey.algorithm] != 255) + vctx->ksk_algorithms[dnskey.algorithm]++; + vctx->goodksk = ISC_TRUE; + } else { + if (vctx->standby_ksk[dnskey.algorithm] != 255) + vctx->standby_ksk[dnskey.algorithm]++; + } + } else if (dns_dnssec_selfsigns(&rdata, vctx->origin, + &vctx->keyset, &vctx->keysigs, + ISC_FALSE, vctx->mctx)) { + if (vctx->zsk_algorithms[dnskey.algorithm] != 255) + vctx->zsk_algorithms[dnskey.algorithm]++; + vctx->goodzsk = ISC_TRUE; + } else if (dns_dnssec_signs(&rdata, vctx->origin, + &vctx->soaset, &vctx->soasigs, + ISC_FALSE, vctx->mctx)) { + if (vctx->zsk_algorithms[dnskey.algorithm] != 255) + vctx->zsk_algorithms[dnskey.algorithm]++; + } else { + if (vctx->standby_zsk[dnskey.algorithm] != 255) + vctx->standby_zsk[dnskey.algorithm]++; + } + dns_rdata_freestruct(&dnskey); + dns_rdata_reset(&rdata); + } +} + void dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_mem_t *mctx, @@ -1224,8 +1299,6 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, 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; int i; isc_boolean_t done = ISC_FALSE; isc_boolean_t first = ISC_TRUE; @@ -1239,73 +1312,7 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, check_apex_rrsets(&vctx); - /* - * 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(&vctx.keyset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(&vctx.keyset)) { - dns_rdataset_current(&vctx.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, vctx.origin, - &vctx.keyset, &vctx.keysigs, - ISC_FALSE, vctx.mctx)) { - char namebuf[DNS_NAME_FORMATSIZE]; - char buffer[1024]; - isc_buffer_t buf; - - dns_name_format(vctx.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 && - vctx.revoked_ksk[dnskey.algorithm] != 255) - vctx.revoked_ksk[dnskey.algorithm]++; - else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && - vctx.revoked_zsk[dnskey.algorithm] != 255) - vctx.revoked_zsk[dnskey.algorithm]++; - } else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) { - if (dns_dnssec_selfsigns(&rdata, vctx.origin, - &vctx.keyset, &vctx.keysigs, - ISC_FALSE, vctx.mctx)) { - if (vctx.ksk_algorithms[dnskey.algorithm] != 255) - vctx.ksk_algorithms[dnskey.algorithm]++; - vctx.goodksk = ISC_TRUE; - } else { - if (vctx.standby_ksk[dnskey.algorithm] != 255) - vctx.standby_ksk[dnskey.algorithm]++; - } - } else if (dns_dnssec_selfsigns(&rdata, vctx.origin, - &vctx.keyset, &vctx.keysigs, - ISC_FALSE, vctx.mctx)) { - if (vctx.zsk_algorithms[dnskey.algorithm] != 255) - vctx.zsk_algorithms[dnskey.algorithm]++; - vctx.goodzsk = ISC_TRUE; - } else if (dns_dnssec_signs(&rdata, vctx.origin, &vctx.soaset, - &vctx.soasigs, ISC_FALSE, - vctx.mctx)) { - if (vctx.zsk_algorithms[dnskey.algorithm] != 255) - vctx.zsk_algorithms[dnskey.algorithm]++; - } else { - if (vctx.standby_zsk[dnskey.algorithm] != 255) - vctx.standby_zsk[dnskey.algorithm]++; - } - dns_rdata_freestruct(&dnskey); - dns_rdata_reset(&rdata); - } + check_dnskey(&vctx); if (ignore_kskflag ) { if (!vctx.goodksk && !vctx.goodzsk) From f06a755dd253dbf0c4841d18b66a257de5d05cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 11/38] Extract check_dnskey_sigs() from check_dnskey() Extract the part of check_dnskey() responsible for determining active algorithms in the verified zone based on the signatures at zone apex to a separate function. --- lib/dns/zoneverify.c | 68 ++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 04f5a94856..da90b8e016 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1213,6 +1213,47 @@ check_apex_rrsets(vctx_t *vctx) { dns_db_detachnode(vctx->db, &node); } +/*% + * Update 'vctx' tables tracking active and standby key algorithms used in the + * verified zone based on the signatures made using 'dnskey' (prepared from + * 'rdata') found at zone apex. Set 'vctx->goodksk' or 'vctx->goodzsk' to true + * if 'dnskey' correctly signs the DNSKEY RRset at zone apex. + * + * The variables to update are chosen based on 'is_ksk', which is true when + * 'dnskey' is a KSK and false otherwise. + */ +static void +check_dnskey_sigs(vctx_t *vctx, dns_rdata_dnskey_t *dnskey, dns_rdata_t *rdata, + isc_boolean_t is_ksk) +{ + unsigned char *active_keys, *standby_keys; + isc_boolean_t *goodkey; + + active_keys = (is_ksk ? vctx->ksk_algorithms : vctx->zsk_algorithms); + standby_keys = (is_ksk ? vctx->standby_ksk : vctx->standby_zsk); + goodkey = (is_ksk ? &vctx->goodksk : &vctx->goodzsk); + + if (dns_dnssec_selfsigns(rdata, vctx->origin, &vctx->keyset, + &vctx->keysigs, ISC_FALSE, vctx->mctx)) + { + if (active_keys[dnskey->algorithm] != 255) { + active_keys[dnskey->algorithm]++; + } + *goodkey = ISC_TRUE; + } else if (!is_ksk && + dns_dnssec_signs(rdata, vctx->origin, &vctx->soaset, + &vctx->soasigs, ISC_FALSE, vctx->mctx)) + { + if (active_keys[dnskey->algorithm] != 255) { + active_keys[dnskey->algorithm]++; + } + } else { + if (standby_keys[dnskey->algorithm] != 255) { + standby_keys[dnskey->algorithm]++; + } + } +} + /*% * 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). @@ -1222,6 +1263,7 @@ check_dnskey(vctx_t *vctx) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_dnskey_t dnskey; isc_result_t result; + isc_boolean_t is_ksk; for (result = dns_rdataset_first(&vctx->keyset); result == ISC_R_SUCCESS; @@ -1229,6 +1271,7 @@ check_dnskey(vctx_t *vctx) { dns_rdataset_current(&vctx->keyset, &rdata); result = dns_rdata_tostruct(&rdata, &dnskey, NULL); check_result(result, "dns_rdata_tostruct"); + is_ksk = ISC_TF((dnskey.flags & DNS_KEYFLAG_KSK) != 0); if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0) ; @@ -1257,31 +1300,8 @@ check_dnskey(vctx_t *vctx) { else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && vctx->revoked_zsk[dnskey.algorithm] != 255) vctx->revoked_zsk[dnskey.algorithm]++; - } else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) { - if (dns_dnssec_selfsigns(&rdata, vctx->origin, - &vctx->keyset, &vctx->keysigs, - ISC_FALSE, vctx->mctx)) { - if (vctx->ksk_algorithms[dnskey.algorithm] != 255) - vctx->ksk_algorithms[dnskey.algorithm]++; - vctx->goodksk = ISC_TRUE; - } else { - if (vctx->standby_ksk[dnskey.algorithm] != 255) - vctx->standby_ksk[dnskey.algorithm]++; - } - } else if (dns_dnssec_selfsigns(&rdata, vctx->origin, - &vctx->keyset, &vctx->keysigs, - ISC_FALSE, vctx->mctx)) { - if (vctx->zsk_algorithms[dnskey.algorithm] != 255) - vctx->zsk_algorithms[dnskey.algorithm]++; - vctx->goodzsk = ISC_TRUE; - } else if (dns_dnssec_signs(&rdata, vctx->origin, - &vctx->soaset, &vctx->soasigs, - ISC_FALSE, vctx->mctx)) { - if (vctx->zsk_algorithms[dnskey.algorithm] != 255) - vctx->zsk_algorithms[dnskey.algorithm]++; } else { - if (vctx->standby_zsk[dnskey.algorithm] != 255) - vctx->standby_zsk[dnskey.algorithm]++; + check_dnskey_sigs(vctx, &dnskey, &rdata, is_ksk); } dns_rdata_freestruct(&dnskey); dns_rdata_reset(&rdata); From dc81d8cb672112f88375e7bb71b548d7140f2f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 12/38] Extract determine_active_algorithms() from dns_zoneverify_dnssec() Extract the part of dns_zoneverify_dnssec() responsible for determining and printing a list of DNSSEC algorithms active in the verified zone to a separate function. --- lib/dns/zoneverify.c | 83 ++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index da90b8e016..1befc54cec 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1308,6 +1308,54 @@ check_dnskey(vctx_t *vctx) { } } +static void +determine_active_algorithms(vctx_t *vctx, isc_boolean_t ignore_kskflag, + isc_boolean_t keyset_kskonly) +{ + char algbuf[DNS_SECALG_FORMATSIZE]; + int i; + + zoneverify_print(vctx, + "Verifying the zone using the following algorithms:"); + + for (i = 0; i < 256; i++) { + if (ignore_kskflag) + vctx->act_algorithms[i] = + (vctx->ksk_algorithms[i] != 0 || + vctx->zsk_algorithms[i] != 0) ? 1 : 0; + else + vctx->act_algorithms[i] = + vctx->ksk_algorithms[i] != 0 ? 1 : 0; + if (vctx->act_algorithms[i] != 0) { + dns_secalg_format(i, algbuf, sizeof(algbuf)); + zoneverify_print(vctx, " %s", algbuf); + } + } + zoneverify_print(vctx, ".\n"); + + if (ignore_kskflag || keyset_kskonly) { + return; + } + + 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 ((vctx->ksk_algorithms[i] != 0) == + (vctx->zsk_algorithms[i] != 0)) + continue; + dns_secalg_format(i, algbuf, sizeof(algbuf)); + zoneverify_log_error(vctx, + "Missing %s for algorithm %s", + (vctx->ksk_algorithms[i] != 0) + ? "ZSK" + : "self-signed KSK", + algbuf); + vctx->bad_algorithms[i] = 1; + } +} + void dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_mem_t *mctx, @@ -1341,40 +1389,7 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, 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) - vctx.act_algorithms[i] = - (vctx.ksk_algorithms[i] != 0 || - vctx.zsk_algorithms[i] != 0) ? 1 : 0; - else - vctx.act_algorithms[i] = - vctx.ksk_algorithms[i] != 0 ? 1 : 0; - if (vctx.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 ((vctx.ksk_algorithms[i] != 0) == - (vctx.zsk_algorithms[i] != 0)) - continue; - dns_secalg_format(i, algbuf, sizeof(algbuf)); - fprintf(stderr, "Missing %s for algorithm %s\n", - (vctx.ksk_algorithms[i] != 0) - ? "ZSK" - : "self-signed KSK", - algbuf); - vctx.bad_algorithms[i] = 1; - } - } + determine_active_algorithms(&vctx, ignore_kskflag, keyset_kskonly); /* * Check that all the other records were signed by keys that are From eb17957c3d9dd015c35a5979739599029d17c2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 13/38] Extract verify_nodes() from dns_zoneverify_dnssec() Extract the part of dns_zoneverify_dnssec() responsible for verifying DNSSEC signatures against the DNSKEY RRset at zone apex and checking consistency of NSEC/NSEC3 chains to a separate function. --- lib/dns/zoneverify.c | 229 ++++++++++++++++++++++--------------------- 1 file changed, 118 insertions(+), 111 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 1befc54cec..e3668c5a39 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1356,6 +1356,123 @@ determine_active_algorithms(vctx_t *vctx, isc_boolean_t ignore_kskflag, } } +/*% + * Check that all the records not yet verified were signed by keys that are + * present in the DNSKEY RRset. + */ +static void +verify_nodes(vctx_t *vctx, isc_result_t *vresult) { + dns_fixedname_t fname, fnextname, fprevname, fzonecut; + dns_name_t *name, *nextname, *prevname, *zonecut; + dns_dbnode_t *node = NULL, *nextnode; + dns_dbiterator_t *dbiter = NULL; + isc_boolean_t done = ISC_FALSE; + isc_result_t result; + + name = dns_fixedname_initname(&fname); + nextname = dns_fixedname_initname(&fnextname); + dns_fixedname_init(&fprevname); + prevname = NULL; + dns_fixedname_init(&fzonecut); + zonecut = NULL; + + result = dns_db_createiterator(vctx->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, vctx->origin)) { + check_no_nsec(vctx, name, node); + dns_db_detachnode(vctx->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(vctx, name, node, NULL)) { + zonecut = dns_fixedname_name(&fzonecut); + dns_name_copy(name, zonecut, NULL); + isdelegation = ISC_TRUE; + } else if (has_dname(vctx, node)) { + zonecut = dns_fixedname_name(&fzonecut); + dns_name_copy(name, zonecut, NULL); + } + 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, vctx->origin) || + (zonecut != NULL && + dns_name_issubdomain(nextname, zonecut))) + { + check_no_nsec(vctx, nextname, nextnode); + dns_db_detachnode(vctx->db, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + if (is_empty(vctx, nextnode)) { + dns_db_detachnode(vctx->db, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + dns_db_detachnode(vctx->db, &nextnode); + break; + } + if (result == ISC_R_NOMORE) { + done = ISC_TRUE; + nextname = vctx->origin; + } else if (result != ISC_R_SUCCESS) + fatal("iterating through the database failed: %s", + isc_result_totext(result)); + result = verifynode(vctx, name, node, isdelegation, + &vctx->keyset, &vctx->nsecset, + &vctx->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(vctx, name, prevname, + isdelegation, + &vctx->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(vctx->db, &node); + } + + dns_dbiterator_destroy(&dbiter); + + result = dns_db_createiterator(vctx->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(vctx, name, node, ISC_FALSE, &vctx->keyset, + NULL, NULL, NULL); + check_result(result, "verifynode"); + record_found(vctx, name, node, &vctx->nsec3paramset); + dns_db_detachnode(vctx->db, &node); + } + + dns_dbiterator_destroy(&dbiter); +} + void dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_mem_t *mctx, @@ -1363,12 +1480,7 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, 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; int i; - isc_boolean_t done = ISC_FALSE; isc_boolean_t first = ISC_TRUE; isc_result_t result, vresult = ISC_R_UNSET; vctx_t vctx; @@ -1391,112 +1503,7 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, determine_active_algorithms(&vctx, ignore_kskflag, keyset_kskonly); - /* - * Check that all the other records were signed by keys that are - * present in the DNSKEY RRSET. - */ - - name = dns_fixedname_initname(&fname); - nextname = dns_fixedname_initname(&fnextname); - dns_fixedname_init(&fprevname); - prevname = NULL; - dns_fixedname_init(&fzonecut); - zonecut = NULL; - - result = dns_db_createiterator(vctx.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, vctx.origin)) { - check_no_nsec(&vctx, name, node); - dns_db_detachnode(vctx.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(&vctx, name, node, NULL)) { - zonecut = dns_fixedname_name(&fzonecut); - dns_name_copy(name, zonecut, NULL); - isdelegation = ISC_TRUE; - } else if (has_dname(&vctx, node)) { - zonecut = dns_fixedname_name(&fzonecut); - dns_name_copy(name, zonecut, NULL); - } - 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, vctx.origin) || - (zonecut != NULL && - dns_name_issubdomain(nextname, zonecut))) - { - check_no_nsec(&vctx, nextname, nextnode); - dns_db_detachnode(vctx.db, &nextnode); - result = dns_dbiterator_next(dbiter); - continue; - } - if (is_empty(&vctx, nextnode)) { - dns_db_detachnode(vctx.db, &nextnode); - result = dns_dbiterator_next(dbiter); - continue; - } - dns_db_detachnode(vctx.db, &nextnode); - break; - } - if (result == ISC_R_NOMORE) { - done = ISC_TRUE; - nextname = vctx.origin; - } else if (result != ISC_R_SUCCESS) - fatal("iterating through the database failed: %s", - isc_result_totext(result)); - result = verifynode(&vctx, name, node, isdelegation, - &vctx.keyset, &vctx.nsecset, - &vctx.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(&vctx, name, prevname, - isdelegation, - &vctx.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(vctx.db, &node); - } - - dns_dbiterator_destroy(&dbiter); - - result = dns_db_createiterator(vctx.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(&vctx, name, node, ISC_FALSE, &vctx.keyset, - NULL, NULL, NULL); - check_result(result, "verifynode"); - record_found(&vctx, name, node, &vctx.nsec3paramset); - dns_db_detachnode(vctx.db, &node); - } - dns_dbiterator_destroy(&dbiter); + verify_nodes(&vctx, &vresult); result = verify_nsec3_chains(&vctx, mctx); if (vresult == ISC_R_UNSET) From b3d2ab442cfb7c3feea5d8157769b7a998c2711a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 14/38] Extract check_bad_algorithms() from dns_zoneverify_dnssec() Extract the part of dns_zoneverify_dnssec() responsible for checking whether the zone is fully signed using all active algorithms to a separate function. --- lib/dns/zoneverify.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index e3668c5a39..7568d1ab3f 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1473,6 +1473,28 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { dns_dbiterator_destroy(&dbiter); } +static void +check_bad_algorithms(const vctx_t *vctx) { + char algbuf[DNS_SECALG_FORMATSIZE]; + isc_boolean_t first = ISC_TRUE; + int i; + + for (i = 0; i < 256; i++) { + if (vctx->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."); + } +} + void dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_mem_t *mctx, @@ -1481,7 +1503,6 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, { char algbuf[80]; int i; - isc_boolean_t first = ISC_TRUE; isc_result_t result, vresult = ISC_R_UNSET; vctx_t vctx; @@ -1511,24 +1532,7 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS) vresult = result; - /* - * 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 (vctx.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."); - } + check_bad_algorithms(&vctx); if (vresult != ISC_R_SUCCESS) fatal("DNSSEC completeness test failed (%s).", From fc6b5ad58550a4e67aee577848e3c165df8a6d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 15/38] Extract print_summary() from dns_zoneverify_dnssec() Extract the part of dns_zoneverify_dnssec() responsible for printing a summary for a fully signed zone to a separate function. --- lib/dns/zoneverify.c | 59 +++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 7568d1ab3f..c12eb81d33 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1495,14 +1495,42 @@ check_bad_algorithms(const vctx_t *vctx) { } } +static void +print_summary(const vctx_t *vctx, isc_boolean_t keyset_kskonly) { + char algbuf[DNS_SECALG_FORMATSIZE]; + int i; + + fprintf(stderr, "Zone fully signed:\n"); + for (i = 0; i < 256; i++) { + if ((vctx->ksk_algorithms[i] != 0) || + (vctx->standby_ksk[i] != 0) || + (vctx->revoked_ksk[i] != 0) || + (vctx->zsk_algorithms[i] != 0) || + (vctx->standby_zsk[i] != 0) || + (vctx->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, vctx->ksk_algorithms[i], + vctx->standby_ksk[i], + vctx->revoked_ksk[i]); + fprintf(stderr, "%*sZSKs: " + "%u active, %u %s, %u revoked\n", + (int) strlen(algbuf) + 13, "", + vctx->zsk_algorithms[i], + vctx->standby_zsk[i], + keyset_kskonly ? "present" : "stand-by", + vctx->revoked_zsk[i]); + } + } +} + void dns_zoneverify_dnssec(dns_zone_t *zone, 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]; - int i; isc_result_t result, vresult = ISC_R_UNSET; vctx_t vctx; @@ -1539,32 +1567,7 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_result_totext(vresult)); if (vctx.goodksk || ignore_kskflag) { - /* - * Print the success summary. - */ - fprintf(stderr, "Zone fully signed:\n"); - for (i = 0; i < 256; i++) { - if ((vctx.ksk_algorithms[i] != 0) || - (vctx.standby_ksk[i] != 0) || - (vctx.revoked_ksk[i] != 0) || - (vctx.zsk_algorithms[i] != 0) || - (vctx.standby_zsk[i] != 0) || - (vctx.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, vctx.ksk_algorithms[i], - vctx.standby_ksk[i], - vctx.revoked_ksk[i]); - fprintf(stderr, "%*sZSKs: " - "%u active, %u %s, %u revoked\n", - (int) strlen(algbuf) + 13, "", - vctx.zsk_algorithms[i], - vctx.standby_zsk[i], - keyset_kskonly ? "present" : "stand-by", - vctx.revoked_zsk[i]); - } - } + print_summary(&vctx, keyset_kskonly); } vctx_destroy(&vctx); From ee06182057073bb3d4831bba00bea1277d01b9ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 16/38] Use RUNTIME_CHECK instead of check_result() where it is safe to do so Replace calls to check_result() with RUNTIME_CHECK assertions for all dns_rdata_tostruct() calls in lib/dns/zoneverify.c as this function cannot fail when the "mctx" argument is NULL (and that is the case for all call sites of this function throughout lib/dns/zoneverify.c). --- lib/dns/zoneverify.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index c12eb81d33..e7b532317e 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -205,7 +205,7 @@ goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, dns_name_t *name, isc_result_t result; result = dns_rdata_tostruct(sigrdata, &sig, NULL); - check_result(result, "dns_rdata_tostruct()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); for (result = dns_rdataset_first(keyrdataset); result == ISC_R_SUCCESS; @@ -213,7 +213,7 @@ goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, dns_name_t *name, 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()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); result = dns_dnssec_keyfromrdata(vctx->origin, &rdata, vctx->mctx, &dstkey); if (result != ISC_R_SUCCESS) @@ -263,7 +263,7 @@ verifynsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, dns_rdataset_current(&rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec, NULL); - check_result(result, "dns_rdata_tostruct()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); /* Check bit next name is consistent */ if (!dns_name_equal(&nsec.next, nextname)) { dns_name_format(name, namebuf, sizeof(namebuf)); @@ -435,7 +435,7 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, 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()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (nsec3.hash == nsec3param->hash && nsec3.next_length == rhsize && nsec3.iterations == nsec3param->iterations && @@ -478,7 +478,7 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, 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()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (nsec3.hash == nsec3param->hash && nsec3.iterations == nsec3param->iterations && nsec3.salt_length == nsec3param->salt_length && @@ -510,7 +510,7 @@ innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { dns_rdataset_current(nsec3paramset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec3param, NULL); - check_result(result, "dns_rdata_tostruct()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (nsec3param.flags == 0 && nsec3param.hash == nsec3->hash && nsec3param.iterations == nsec3->iterations && @@ -556,7 +556,7 @@ record_found(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, 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()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (nsec3.next_length != isc_buffer_usedlength(&b)) continue; /* @@ -594,7 +594,7 @@ isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata) { isc_boolean_t ret; result = dns_rdata_tostruct(nsec3rdata, &nsec3param, NULL); - check_result(result, "dns_rdata_tostruct()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_fixedname_init(&fixed); result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, vctx->origin, @@ -619,10 +619,8 @@ isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata) { 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); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + ret = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0); if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); @@ -650,7 +648,7 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, isc_boolean_t optout; result = dns_rdata_tostruct(rdata, &nsec3param, NULL); - check_result(result, "dns_rdata_tostruct()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (nsec3param.flags != 0) return (ISC_R_SUCCESS); @@ -774,7 +772,7 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, dns_rdataset_current(&sigrdataset, &rdata); result = dns_rdata_tostruct(&rdata, &sig, NULL); - check_result(result, "dns_rdata_tostruct()"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (rdataset->ttl != sig.originalttl) { dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(rdataset->type, typebuf, @@ -1270,7 +1268,7 @@ check_dnskey(vctx_t *vctx) { result = dns_rdataset_next(&vctx->keyset)) { dns_rdataset_current(&vctx->keyset, &rdata); result = dns_rdata_tostruct(&rdata, &dnskey, NULL); - check_result(result, "dns_rdata_tostruct"); + RUNTIME_CHECK(result == ISC_R_SUCCESS); is_ksk = ISC_TF((dnskey.flags & DNS_KEYFLAG_KSK) != 0); if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0) From 1a6525ffa2fe7b8c2ad369402a76b60263b8c744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 17/38] Do not call exit() upon check_apex_rrsets() errors Replace all fatal() calls inside check_apex_rrsets() with zoneverify_log_error() calls and error handling code. Enable check_apex_rrsets() to signal errors to the caller using its return value. Modify the call site of check_apex_rrsets() so that its errors are properly handled. --- lib/dns/zoneverify.c | 87 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index e7b532317e..22c33c8254 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1155,60 +1155,103 @@ vctx_destroy(vctx_t *vctx) { isc_heap_destroy(&vctx->found_chains); } -static void +static isc_result_t check_apex_rrsets(vctx_t *vctx) { dns_dbnode_t *node = NULL; isc_result_t result; result = dns_db_findnode(vctx->db, vctx->origin, ISC_FALSE, &node); - if (result != ISC_R_SUCCESS) - fatal("failed to find the zone's origin: %s", - isc_result_totext(result)); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, + "failed to find the zone's origin: %s", + isc_result_totext(result)); + return (result); + } result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_dnskey, 0, 0, &vctx->keyset, &vctx->keysigs); - if (result != ISC_R_SUCCESS) - fatal("Zone contains no DNSSEC keys\n"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "Zone contains no DNSSEC keys"); + goto done; + } result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_soa, 0, 0, &vctx->soaset, &vctx->soasigs); - if (result != ISC_R_SUCCESS) - fatal("Zone contains no SOA record\n"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "Zone contains no SOA record"); + goto done; + } result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_nsec, 0, 0, &vctx->nsecset, &vctx->nsecsigs); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) - fatal("NSEC lookup failed\n"); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { + zoneverify_log_error(vctx, "NSEC lookup failed"); + goto done; + } result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_nsec3param, 0, 0, &vctx->nsec3paramset, &vctx->nsec3paramsigs); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) - fatal("NSEC3PARAM lookup failed\n"); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { + zoneverify_log_error(vctx, "NSEC3PARAM lookup failed"); + goto done; + } - if (!dns_rdataset_isassociated(&vctx->keysigs)) - fatal("DNSKEY is not signed (keys offline or inactive?)\n"); + if (!dns_rdataset_isassociated(&vctx->keysigs)) { + zoneverify_log_error(vctx, + "DNSKEY is not signed " + "(keys offline or inactive?)"); + result = ISC_R_FAILURE; + goto done; + } - if (!dns_rdataset_isassociated(&vctx->soasigs)) - fatal("SOA is not signed (keys offline or inactive?)\n"); + if (!dns_rdataset_isassociated(&vctx->soasigs)) { + zoneverify_log_error(vctx, + "SOA is not signed " + "(keys offline or inactive?)"); + result = ISC_R_FAILURE; + goto done; + } if (dns_rdataset_isassociated(&vctx->nsecset) && !dns_rdataset_isassociated(&vctx->nsecsigs)) - fatal("NSEC is not signed (keys offline or inactive?)\n"); + { + zoneverify_log_error(vctx, + "NSEC is not signed " + "(keys offline or inactive?)"); + result = ISC_R_FAILURE; + goto done; + } if (dns_rdataset_isassociated(&vctx->nsec3paramset) && !dns_rdataset_isassociated(&vctx->nsec3paramsigs)) - fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n"); + { + zoneverify_log_error(vctx, + "NSEC3PARAM is not signed " + "(keys offline or inactive?)"); + result = ISC_R_FAILURE; + goto done; + } if (!dns_rdataset_isassociated(&vctx->nsecset) && !dns_rdataset_isassociated(&vctx->nsec3paramset)) - fatal("No valid NSEC/NSEC3 chain for testing\n"); + { + zoneverify_log_error(vctx, + "No valid NSEC/NSEC3 chain for testing"); + result = ISC_R_FAILURE; + goto done; + } + result = ISC_R_SUCCESS; + + done: dns_db_detachnode(vctx->db, &node); + + return (result); } /*% @@ -1537,7 +1580,10 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, return; } - check_apex_rrsets(&vctx); + result = check_apex_rrsets(&vctx); + if (result != ISC_R_SUCCESS) { + goto done; + } check_dnskey(&vctx); @@ -1568,5 +1614,6 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, print_summary(&vctx, keyset_kskonly); } + done: vctx_destroy(&vctx); } From 7c3f653112b1288341662c09a21d1d32dd24cf3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 18/38] Do not call exit() upon check_dnskey() errors Replace all fatal() and check_result() calls inside check_dnskey() with zoneverify_log_error() calls and error handling code. Enable check_dnskey() to signal errors to the caller using its return value. Modify the call site of check_dnskey() so that its errors are properly handled. --- lib/dns/zoneverify.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 22c33c8254..4d592424c8 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1299,7 +1299,7 @@ check_dnskey_sigs(vctx_t *vctx, dns_rdata_dnskey_t *dnskey, dns_rdata_t *rdata, * 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). */ -static void +static isc_result_t check_dnskey(vctx_t *vctx) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_dnskey_t dnskey; @@ -1330,10 +1330,20 @@ check_dnskey(vctx_t *vctx) { 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 (result != ISC_R_SUCCESS) { + zoneverify_log_error( + vctx, + "dns_rdata_totext: %s", + isc_result_totext(result)); + return (ISC_R_FAILURE); + } + zoneverify_log_error( + vctx, + "revoked KSK is not self signed:\n" + "%s DNSKEY %.*s", namebuf, + (int)isc_buffer_usedlength(&buf), + buffer); + return (ISC_R_FAILURE); } if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && vctx->revoked_ksk[dnskey.algorithm] != 255) @@ -1347,6 +1357,8 @@ check_dnskey(vctx_t *vctx) { dns_rdata_freestruct(&dnskey); dns_rdata_reset(&rdata); } + + return (ISC_R_SUCCESS); } static void @@ -1585,7 +1597,10 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, goto done; } - check_dnskey(&vctx); + result = check_dnskey(&vctx); + if (result != ISC_R_SUCCESS) { + goto done; + } if (ignore_kskflag ) { if (!vctx.goodksk && !vctx.goodzsk) From 00ecbad2d0aeb3d7833efdfa10afbeed584c6be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 19/38] Do not call exit() upon check_bad_algorithms() errors Replace all fatal() and fprintf() calls inside check_bad_algorithms() with zoneverify_print() calls and error handling code. Enable check_bad_algorithms() to signal errors to the caller using its return value. Modify the call site of check_bad_algorithms() so that its errors are properly handled. --- lib/dns/zoneverify.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 4d592424c8..2f724f5931 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1526,26 +1526,31 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { dns_dbiterator_destroy(&dbiter); } -static void +static isc_result_t check_bad_algorithms(const vctx_t *vctx) { char algbuf[DNS_SECALG_FORMATSIZE]; isc_boolean_t first = ISC_TRUE; int i; for (i = 0; i < 256; i++) { - if (vctx->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 (vctx->bad_algorithms[i] == 0) { + continue; } + if (first) { + zoneverify_print(vctx, + "The zone is not fully signed for " + "the following algorithms:"); + } + dns_secalg_format(i, algbuf, sizeof(algbuf)); + zoneverify_print(vctx, " %s", algbuf); + first = ISC_FALSE; } + if (!first) { - fprintf(stderr, ".\n"); - fatal("DNSSEC completeness test failed."); + zoneverify_print(vctx, ".\n"); } + + return (first ? ISC_R_SUCCESS : ISC_R_FAILURE); } static void @@ -1619,7 +1624,11 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS) vresult = result; - check_bad_algorithms(&vctx); + result = check_bad_algorithms(&vctx); + if (result != ISC_R_SUCCESS) { + zoneverify_print(&vctx, "DNSSEC completeness test failed.\n"); + goto done; + } if (vresult != ISC_R_SUCCESS) fatal("DNSSEC completeness test failed (%s).", From 4354f44d9c7608cea9e585500ba04cdb263e20d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 20/38] Do not call exit() upon verify_nodes() errors Replace all fatal(), check_result(), and check_dns_dbiterator_current() calls inside verify_nodes() with zoneverify_log_error() calls and error handling code. Enable verify_nodes() to signal errors to the caller using its return value. Modify the call site of verify_nodes() so that its errors are properly handled. Free all heap elements upon verification context cleanup as a verification error may prevent them from being freed elsewhere. Remove the check_dns_dbiterator_current() macro as it is no longer used anywhere in lib/dns/zoneverify.c. --- lib/dns/zoneverify.c | 98 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 2f724f5931..7ba9fedeaa 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -50,10 +50,6 @@ #include #include -#define check_dns_dbiterator_current(result) \ - check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \ - "dns_dbiterator_current()") - typedef struct vctx { isc_mem_t * mctx; dns_zone_t * zone; @@ -920,6 +916,14 @@ free_element(isc_mem_t *mctx, struct nsec3_chain_fixed *e) { isc_mem_put(mctx, e, len); } +static void +free_element_heap(void *element, void *uap) { + struct nsec3_chain_fixed *e = (struct nsec3_chain_fixed *)element; + isc_mem_t *mctx = (isc_mem_t *)uap; + + free_element(mctx, e); +} + static isc_boolean_t checknext(const struct nsec3_chain_fixed *first, const struct nsec3_chain_fixed *e) @@ -1151,7 +1155,9 @@ vctx_destroy(vctx_t *vctx) { if (dns_rdataset_isassociated(&vctx->nsec3paramsigs)) { dns_rdataset_disassociate(&vctx->nsec3paramsigs); } + isc_heap_foreach(vctx->expected_chains, free_element_heap, vctx->mctx); isc_heap_destroy(&vctx->expected_chains); + isc_heap_foreach(vctx->found_chains, free_element_heap, vctx->mctx); isc_heap_destroy(&vctx->found_chains); } @@ -1413,7 +1419,7 @@ determine_active_algorithms(vctx_t *vctx, isc_boolean_t ignore_kskflag, * Check that all the records not yet verified were signed by keys that are * present in the DNSKEY RRset. */ -static void +static isc_result_t verify_nodes(vctx_t *vctx, isc_result_t *vresult) { dns_fixedname_t fname, fnextname, fprevname, fzonecut; dns_name_t *name, *nextname, *prevname, *zonecut; @@ -1430,24 +1436,42 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { zonecut = NULL; result = dns_db_createiterator(vctx->db, DNS_DB_NONSEC3, &dbiter); - check_result(result, "dns_db_createiterator()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_db_createiterator(): %s", + isc_result_totext(result)); + return (result); + } result = dns_dbiterator_first(dbiter); - check_result(result, "dns_dbiterator_first()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_dbiterator_first(): %s", + isc_result_totext(result)); + goto done; + } while (!done) { isc_boolean_t isdelegation = ISC_FALSE; result = dns_dbiterator_current(dbiter, &node, name); - check_dns_dbiterator_current(result); + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { + zoneverify_log_error(vctx, + "dns_dbiterator_current(): %s", + isc_result_totext(result)); + goto done; + } if (!dns_name_issubdomain(name, vctx->origin)) { check_no_nsec(vctx, name, node); dns_db_detachnode(vctx->db, &node); result = dns_dbiterator_next(dbiter); - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { done = ISC_TRUE; - else - check_result(result, "dns_dbiterator_next()"); + } else if (result != ISC_R_SUCCESS) { + zoneverify_log_error( + vctx, + "dns_dbiterator_next(): %s", + isc_result_totext(result)); + goto done; + } continue; } if (is_delegation(vctx, name, node, NULL)) { @@ -1463,7 +1487,16 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { while (result == ISC_R_SUCCESS) { result = dns_dbiterator_current(dbiter, &nextnode, nextname); - check_dns_dbiterator_current(result); + if (result != ISC_R_SUCCESS && + result != DNS_R_NEWORIGIN) + { + zoneverify_log_error( + vctx, + "dns_dbiterator_current(): %s", + isc_result_totext(result)); + dns_db_detachnode(vctx->db, &node); + goto done; + } if (!dns_name_issubdomain(nextname, vctx->origin) || (zonecut != NULL && dns_name_issubdomain(nextname, zonecut))) @@ -1484,9 +1517,14 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { if (result == ISC_R_NOMORE) { done = ISC_TRUE; nextname = vctx->origin; - } else if (result != ISC_R_SUCCESS) - fatal("iterating through the database failed: %s", - isc_result_totext(result)); + } else if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, + "iterating through the database " + "failed: %s", + isc_result_totext(result)); + dns_db_detachnode(vctx->db, &node); + goto done; + } result = verifynode(vctx, name, node, isdelegation, &vctx->keyset, &vctx->nsecset, &vctx->nsec3paramset, nextname); @@ -1509,21 +1547,40 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { dns_dbiterator_destroy(&dbiter); result = dns_db_createiterator(vctx->db, DNS_DB_NSEC3ONLY, &dbiter); - check_result(result, "dns_db_createiterator()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_db_createiterator(): %s", + isc_result_totext(result)); + return (result); + } 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); + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { + zoneverify_log_error(vctx, + "dns_dbiterator_current(): %s", + isc_result_totext(result)); + goto done; + } result = verifynode(vctx, name, node, ISC_FALSE, &vctx->keyset, NULL, NULL, NULL); - check_result(result, "verifynode"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "verifynode: %s", + isc_result_totext(result)); + dns_db_detachnode(vctx->db, &node); + goto done; + } record_found(vctx, name, node, &vctx->nsec3paramset); dns_db_detachnode(vctx->db, &node); } + result = ISC_R_SUCCESS; + + done: dns_dbiterator_destroy(&dbiter); + + return (result); } static isc_result_t @@ -1616,7 +1673,10 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, determine_active_algorithms(&vctx, ignore_kskflag, keyset_kskonly); - verify_nodes(&vctx, &vresult); + result = verify_nodes(&vctx, &vresult); + if (result != ISC_R_SUCCESS) { + goto done; + } result = verify_nsec3_chains(&vctx, mctx); if (vresult == ISC_R_UNSET) From 04038baf1a32b644d9e2ecfab1d7a68bc5f15fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 21/38] Do not call exit() upon check_no_nsec() errors Replace the fatal() call inside check_no_nsec() with a zoneverify_log_error() call. Enable check_no_nsec() to signal errors to the caller using its return value. Modify all call sites of check_no_nsec() so that its errors are properly handled. --- lib/dns/zoneverify.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 7ba9fedeaa..0c0dbe7e57 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -876,8 +876,9 @@ is_empty(const vctx_t *vctx, dns_dbnode_t *node) { return (ISC_FALSE); } -static void +static isc_result_t check_no_nsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node) { + isc_boolean_t nsec_exists = ISC_FALSE; dns_rdataset_t rdataset; isc_result_t result; @@ -888,11 +889,15 @@ check_no_nsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node) { 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); + zoneverify_log_error(vctx, "unexpected NSEC RRset at %s", + namebuf); + nsec_exists = ISC_TRUE; } if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); + + return (nsec_exists ? ISC_R_FAILURE : ISC_R_SUCCESS); } static isc_boolean_t @@ -1460,7 +1465,11 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { goto done; } if (!dns_name_issubdomain(name, vctx->origin)) { - check_no_nsec(vctx, name, node); + result = check_no_nsec(vctx, name, node); + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(vctx->db, &node); + goto done; + } dns_db_detachnode(vctx->db, &node); result = dns_dbiterator_next(dbiter); if (result == ISC_R_NOMORE) { @@ -1501,7 +1510,13 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { (zonecut != NULL && dns_name_issubdomain(nextname, zonecut))) { - check_no_nsec(vctx, nextname, nextnode); + result = check_no_nsec(vctx, nextname, + nextnode); + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(vctx->db, &node); + dns_db_detachnode(vctx->db, &nextnode); + goto done; + } dns_db_detachnode(vctx->db, &nextnode); result = dns_dbiterator_next(dbiter); continue; From 7a996f0c0de9fd3f2ba2ad0983a0773cc2a5764b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 22/38] Do not call exit() upon is_empty() errors Replace the check_result() call inside is_empty() with a zoneverify_log_error() call and error handling code. Enable is_empty() to signal errors to the caller using its return value. Modify the call site of is_empty() so that its errors are properly handled. --- lib/dns/zoneverify.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 0c0dbe7e57..3627f38a80 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -862,18 +862,23 @@ verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, return (result); } -static isc_boolean_t -is_empty(const vctx_t *vctx, dns_dbnode_t *node) { +static isc_result_t +is_empty(const vctx_t *vctx, dns_dbnode_t *node, isc_boolean_t *empty) { dns_rdatasetiter_t *rdsiter = NULL; isc_result_t result; result = dns_db_allrdatasets(vctx->db, node, vctx->ver, 0, &rdsiter); - check_result(result, "dns_db_allrdatasets()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_db_allrdatasets(): %s", + isc_result_totext(result)); + return (result); + } result = dns_rdatasetiter_first(rdsiter); dns_rdatasetiter_destroy(&rdsiter); - if (result == ISC_R_NOMORE) - return (ISC_TRUE); - return (ISC_FALSE); + + *empty = ISC_TF(result == ISC_R_NOMORE); + + return (ISC_R_SUCCESS); } static isc_result_t @@ -1494,6 +1499,7 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { nextnode = NULL; result = dns_dbiterator_next(dbiter); while (result == ISC_R_SUCCESS) { + isc_boolean_t empty; result = dns_dbiterator_current(dbiter, &nextnode, nextname); if (result != ISC_R_SUCCESS && @@ -1521,12 +1527,16 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { result = dns_dbiterator_next(dbiter); continue; } - if (is_empty(vctx, nextnode)) { - dns_db_detachnode(vctx->db, &nextnode); + result = is_empty(vctx, nextnode, &empty); + dns_db_detachnode(vctx->db, &nextnode); + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(vctx->db, &node); + goto done; + } + if (empty) { result = dns_dbiterator_next(dbiter); continue; } - dns_db_detachnode(vctx->db, &nextnode); break; } if (result == ISC_R_NOMORE) { From d782fcc63822dfe75ccbad00200de2e39a9cb928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 23/38] Do not call exit() upon verifynode() errors Make verifynode() return the verification result through a separate pointer, thus making it possible to signal errors using function return value. Replace all fatal() and check_result() calls inside verifynode() with zoneverify_log_error() calls and error handling code. Add a REQUIRE assertion to emphasize verifynode() may be called with some of its arguments set to NULL. Modify all call sites of verifynode() so that its errors are properly handled. --- lib/dns/zoneverify.c | 51 +++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 3627f38a80..e29646cdbd 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -804,16 +804,22 @@ static isc_result_t verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, isc_boolean_t delegation, dns_rdataset_t *keyrdataset, dns_rdataset_t *nsecset, dns_rdataset_t *nsec3paramset, - dns_name_t *nextname) + dns_name_t *nextname, isc_result_t *vresult) { unsigned char types[8192]; unsigned int maxtype = 0; dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter = NULL; isc_result_t result, tresult; + REQUIRE(vresult != NULL || (nsecset == NULL && nsec3paramset == NULL)); + memset(types, 0, sizeof(types)); result = dns_db_allrdatasets(vctx->db, node, vctx->ver, 0, &rdsiter); - check_result(result, "dns_db_allrdatasets()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_db_allrdatasets(): %s", + isc_result_totext(result)); + return (result); + } result = dns_rdatasetiter_first(rdsiter); dns_rdataset_init(&rdataset); while (result == ISC_R_SUCCESS) { @@ -843,23 +849,32 @@ verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, 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); + if (result != ISC_R_NOMORE) { + zoneverify_log_error(vctx, "rdataset iteration failed: %s", + isc_result_totext(result)); + return (result); + } - result = ISC_R_SUCCESS; + if (vresult == NULL) { + return (ISC_R_SUCCESS); + } - if (nsecset != NULL && dns_rdataset_isassociated(nsecset)) - result = verifynsec(vctx, name, node, nextname); + *vresult = ISC_R_SUCCESS; + + if (nsecset != NULL && dns_rdataset_isassociated(nsecset)) { + *vresult = verifynsec(vctx, name, node, nextname); + } if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { tresult = verifynsec3s(vctx, name, nsec3paramset, delegation, ISC_FALSE, types, maxtype); - if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) - result = tresult; + if (*vresult == ISC_R_SUCCESS) { + *vresult = tresult; + } } - return (result); + + return (ISC_R_SUCCESS); } static isc_result_t @@ -1436,6 +1451,7 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { dns_dbnode_t *node = NULL, *nextnode; dns_dbiterator_t *dbiter = NULL; isc_boolean_t done = ISC_FALSE; + isc_result_t tvresult; isc_result_t result; name = dns_fixedname_initname(&fname); @@ -1552,11 +1568,16 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { } result = verifynode(vctx, name, node, isdelegation, &vctx->keyset, &vctx->nsecset, - &vctx->nsec3paramset, nextname); + &vctx->nsec3paramset, nextname, &tvresult); + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(vctx->db, &node); + goto done; + } if (*vresult == ISC_R_UNSET) *vresult = ISC_R_SUCCESS; - if (*vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS) - *vresult = result; + if (*vresult == ISC_R_SUCCESS) { + *vresult = tvresult; + } if (prevname != NULL) { result = verifyemptynodes(vctx, name, prevname, isdelegation, @@ -1589,7 +1610,7 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { goto done; } result = verifynode(vctx, name, node, ISC_FALSE, &vctx->keyset, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); if (result != ISC_R_SUCCESS) { zoneverify_log_error(vctx, "verifynode: %s", isc_result_totext(result)); From 30e837f31aec59570bb174767166b1046b36d520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 24/38] Do not call exit() upon verifyset() errors Replace all check_result() and fprintf() calls inside verifyset() with zoneverify_log_error() calls and error handling code. Enable verifyset() to signal errors to the caller using its return value. Modify the call site of verifyset() so that its errors are properly handled. Define buffer sizes using named constants rather than plain integers. --- lib/dns/zoneverify.c | 51 ++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index e29646cdbd..07ccc05915 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -723,14 +723,14 @@ verifynsec3s(const vctx_t *vctx, dns_name_t *name, return (result); } -static void +static isc_result_t verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, dns_dbnode_t *node, dns_rdataset_t *keyrdataset) { unsigned char set_algorithms[256]; char namebuf[DNS_NAME_FORMATSIZE]; - char algbuf[80]; - char typebuf[80]; + char algbuf[DNS_SECALG_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; dns_rdataset_t sigrdataset; dns_rdatasetiter_t *rdsiter = NULL; isc_result_t result; @@ -738,7 +738,11 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, dns_rdataset_init(&sigrdataset); result = dns_db_allrdatasets(vctx->db, node, vctx->ver, 0, &rdsiter); - check_result(result, "dns_db_allrdatasets()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_db_allrdatasets(): %s", + isc_result_totext(result)); + return (result); + } for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; result = dns_rdatasetiter_next(rdsiter)) { @@ -751,12 +755,13 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, if (result != ISC_R_SUCCESS) { dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); - fprintf(stderr, "No signatures for %s/%s\n", namebuf, typebuf); + zoneverify_log_error(vctx, "No signatures for %s/%s", + namebuf, typebuf); for (i = 0; i < 256; i++) if (vctx->act_algorithms[i] != 0) vctx->bad_algorithms[i] = 1; - dns_rdatasetiter_destroy(&rdsiter); - return; + result = ISC_R_SUCCESS; + goto done; } memset(set_algorithms, 0, sizeof(set_algorithms)); @@ -773,8 +778,10 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); - fprintf(stderr, "TTL mismatch for %s %s keytag %u\n", - namebuf, typebuf, sig.keyid); + zoneverify_log_error(vctx, + "TTL mismatch for " + "%s %s keytag %u", + namebuf, typebuf, sig.keyid); continue; } if ((set_algorithms[sig.algorithm] != 0) || @@ -783,7 +790,8 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, if (goodsig(vctx, &rdata, name, keyrdataset, rdataset)) set_algorithms[sig.algorithm] = 1; } - dns_rdatasetiter_destroy(&rdsiter); + result = ISC_R_SUCCESS; + if (memcmp(set_algorithms, vctx->act_algorithms, sizeof(set_algorithms))) { dns_name_format(name, namebuf, sizeof(namebuf)); @@ -792,12 +800,21 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, if ((vctx->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); + zoneverify_log_error(vctx, + "No correct %s signature " + "for %s %s", + algbuf, namebuf, typebuf); vctx->bad_algorithms[i] = 1; } } - dns_rdataset_disassociate(&sigrdataset); + + done: + if (dns_rdataset_isassociated(&sigrdataset)) { + dns_rdataset_disassociate(&sigrdataset); + } + dns_rdatasetiter_destroy(&rdsiter); + + return (result); } static isc_result_t @@ -835,7 +852,13 @@ verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, rdataset.type != dns_rdatatype_dnskey && (!delegation || rdataset.type == dns_rdatatype_ds || rdataset.type == dns_rdatatype_nsec)) { - verifyset(vctx, &rdataset, name, node, keyrdataset); + result = verifyset(vctx, &rdataset, name, node, + keyrdataset); + if (result != ISC_R_SUCCESS) { + dns_rdataset_disassociate(&rdataset); + dns_rdatasetiter_destroy(&rdsiter); + return (result); + } dns_nsec_setbit(types, rdataset.type, 1); if (rdataset.type > maxtype) maxtype = rdataset.type; From 0ed9ec49ed6d8570ecdcbf0b25585175615433a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 25/38] Do not call exit() upon check_no_rrsig() errors Replace all check_result() and fprintf() calls inside check_no_rrsig() with zoneverify_log_error() calls and error handling code. Enable check_no_rrsig() to signal errors to the caller using its return value. Modify the call site of check_no_rrsig() so that its errors are properly handled. Define buffer size using a named constant rather than a plain integer. --- lib/dns/zoneverify.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 07ccc05915..6db3355866 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -295,19 +295,23 @@ verifynsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, return (ISC_R_FAILURE); } -static void +static isc_result_t check_no_rrsig(const vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, dns_dbnode_t *node) { char namebuf[DNS_NAME_FORMATSIZE]; - char typebuf[80]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; dns_rdataset_t sigrdataset; dns_rdatasetiter_t *rdsiter = NULL; isc_result_t result; dns_rdataset_init(&sigrdataset); result = dns_db_allrdatasets(vctx->db, node, vctx->ver, 0, &rdsiter); - check_result(result, "dns_db_allrdatasets()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_db_allrdatasets(): %s", + isc_result_totext(result)); + return (result); + } for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; result = dns_rdatasetiter_next(rdsiter)) { @@ -320,12 +324,16 @@ check_no_rrsig(const vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, if (result == ISC_R_SUCCESS) { dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); - fprintf(stderr, "Warning: Found unexpected signatures for " - "%s/%s\n", namebuf, typebuf); + zoneverify_log_error(vctx, + "Warning: Found unexpected signatures " + "for %s/%s", + namebuf, typebuf); } if (dns_rdataset_isassociated(&sigrdataset)) dns_rdataset_disassociate(&sigrdataset); dns_rdatasetiter_destroy(&rdsiter); + + return (ISC_R_SUCCESS); } static isc_boolean_t @@ -866,7 +874,12 @@ verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, rdataset.type != dns_rdatatype_dnskey) { if (rdataset.type == dns_rdatatype_ns) dns_nsec_setbit(types, rdataset.type, 1); - check_no_rrsig(vctx, &rdataset, name, node); + result = check_no_rrsig(vctx, &rdataset, name, node); + if (result != ISC_R_SUCCESS) { + dns_rdataset_disassociate(&rdataset); + dns_rdatasetiter_destroy(&rdsiter); + return (result); + } } else dns_nsec_setbit(types, rdataset.type, 1); dns_rdataset_disassociate(&rdataset); From 8448691159a3c2d8e5a0a9d0a5f745893265f4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 26/38] Do not call exit() upon verifynsec() errors Make verifynsec() return the verification result through a separate pointer, thus making it possible to signal errors using function return value. Replace all check_result() and fprintf() calls inside verifynsec() with zoneverify_log_error() calls and error handling code. Modify the call site of verifynsec() so that its errors are properly handled. Rename "tresult" to "tvresult" in order to improve variable naming consistency between functions. --- lib/dns/zoneverify.c | 76 +++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 6db3355866..7d368e7202 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -232,7 +232,7 @@ goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, dns_name_t *name, static isc_result_t verifynsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, - dns_name_t *nextname) + dns_name_t *nextname, isc_result_t *vresult) { unsigned char buffer[DNS_NSEC_BUFFERSIZE]; char namebuf[DNS_NAME_FORMATSIZE]; @@ -250,49 +250,71 @@ verifynsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, NULL); if (result != ISC_R_SUCCESS) { dns_name_format(name, namebuf, sizeof(namebuf)); - fprintf(stderr, "Missing NSEC record for %s\n", namebuf); - goto failure; + zoneverify_log_error(vctx, "Missing NSEC record for %s", + namebuf); + *vresult = ISC_R_FAILURE; + result = ISC_R_SUCCESS; + goto done; } result = dns_rdataset_first(&rdataset); - check_result(result, "dns_rdataset_first()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_rdataset_first(): %s", + isc_result_totext(result)); + goto done; + } dns_rdataset_current(&rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - /* Check bit next name is consistent */ + /* Check 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; + zoneverify_log_error(vctx, + "Bad NSEC record for %s, next name " + "mismatch (expected:%s, found:%s)", + namebuf, nextbuf, found); + *vresult = ISC_R_FAILURE; + goto done; } /* Check bit map is consistent */ result = dns_nsec_buildrdata(vctx->db, vctx->ver, node, nextname, buffer, &tmprdata); - check_result(result, "dns_nsec_buildrdata()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_nsec_buildrdata(): %s", + isc_result_totext(result)); + goto done; + } 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; + zoneverify_log_error(vctx, + "Bad NSEC record for %s, bit map " + "mismatch", + namebuf); + *vresult = ISC_R_FAILURE; + goto done; } + 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; - + zoneverify_log_error(vctx, "Multiple NSEC records for %s", + namebuf); + *vresult = ISC_R_FAILURE; + goto done; } - dns_rdataset_disassociate(&rdataset); - return (ISC_R_SUCCESS); - failure: - if (dns_rdataset_isassociated(&rdataset)) + + *vresult = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; + + done: + if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); - return (ISC_R_FAILURE); + } + + return (result); } static isc_result_t @@ -834,7 +856,7 @@ verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, unsigned char types[8192]; unsigned int maxtype = 0; dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter = NULL; - isc_result_t result, tresult; + isc_result_t result, tvresult; REQUIRE(vresult != NULL || (nsecset == NULL && nsec3paramset == NULL)); @@ -899,14 +921,18 @@ verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, *vresult = ISC_R_SUCCESS; if (nsecset != NULL && dns_rdataset_isassociated(nsecset)) { - *vresult = verifynsec(vctx, name, node, nextname); + result = verifynsec(vctx, name, node, nextname, &tvresult); + if (result != ISC_R_SUCCESS) { + return (result); + } + *vresult = tvresult; } if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { - tresult = verifynsec3s(vctx, name, nsec3paramset, delegation, + tvresult = verifynsec3s(vctx, name, nsec3paramset, delegation, ISC_FALSE, types, maxtype); if (*vresult == ISC_R_SUCCESS) { - *vresult = tresult; + *vresult = tvresult; } } From c76fcdd2a2ce8ff936c996785812651da94f5714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 27/38] Do not call exit() upon NSEC3 verification errors Make verifynsec3(), verifynsec3s(), and verifyemptynodes() return the verification result through a separate pointer, thus making it possible to signal errors using function return values. Replace all check_result() and fprintf() calls inside these functions with zoneverify_log_error() calls and error handling code. Modify all call sites of verifynsec3(), verifynsec3s(), and verifyemptynodes() so that their errors are properly handled. --- lib/dns/zoneverify.c | 78 ++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 7d368e7202..20e0c41fd2 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -659,7 +659,8 @@ isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata) { static isc_result_t verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, isc_boolean_t delegation, isc_boolean_t empty, - unsigned char types[8192], unsigned int maxtype) + unsigned char types[8192], unsigned int maxtype, + isc_result_t *vresult) { char namebuf[DNS_NAME_FORMATSIZE]; char hashbuf[DNS_NAME_FORMATSIZE]; @@ -689,7 +690,11 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, vctx->origin, nsec3param.hash, nsec3param.iterations, nsec3param.salt, nsec3param.salt_length); - check_result(result, "dns_nsec3_hashname()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_nsec3_hashname(): %s", + isc_result_totext(result)); + return (result); + } /* * We don't use dns_db_find() here as it works with the choosen @@ -710,8 +715,8 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, { 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); + zoneverify_log_error(vctx, "Missing NSEC3 record for %s (%s)", + namebuf, hashbuf); } else if (result == ISC_R_NOTFOUND && delegation && (!empty || optout)) { @@ -721,19 +726,21 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, maxtype, rawhash, rhsize); } + *vresult = result; + if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); if (node != NULL) dns_db_detachnode(vctx->db, &node); - return (result); + return (ISC_R_SUCCESS); } static isc_result_t verifynsec3s(const vctx_t *vctx, dns_name_t *name, dns_rdataset_t *nsec3paramset, isc_boolean_t delegation, isc_boolean_t empty, unsigned char types[8192], - unsigned int maxtype) + unsigned int maxtype, isc_result_t *vresult) { isc_result_t result; @@ -744,9 +751,13 @@ verifynsec3s(const vctx_t *vctx, dns_name_t *name, dns_rdataset_current(nsec3paramset, &rdata); result = verifynsec3(vctx, name, &rdata, delegation, empty, - types, maxtype); - if (result != ISC_R_SUCCESS) + types, maxtype, vresult); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (*vresult != ISC_R_SUCCESS) { break; + } } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; @@ -929,8 +940,11 @@ verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, } if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { - tvresult = verifynsec3s(vctx, name, nsec3paramset, delegation, - ISC_FALSE, types, maxtype); + result = verifynsec3s(vctx, name, nsec3paramset, delegation, + ISC_FALSE, types, maxtype, &tvresult); + if (result != ISC_R_SUCCESS) { + return (result); + } if (*vresult == ISC_R_SUCCESS) { *vresult = tvresult; } @@ -1138,17 +1152,21 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { static isc_result_t verifyemptynodes(const vctx_t *vctx, dns_name_t *name, dns_name_t *prevname, - isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset) + isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset, + isc_result_t *vresult) { dns_namereln_t reln; int order; unsigned int labels, nlabels, i; dns_name_t suffix; - isc_result_t result = ISC_R_SUCCESS, tresult; + isc_result_t result, tvresult; + + *vresult = ISC_R_SUCCESS; reln = dns_name_fullcompare(prevname, name, &order, &labels); - if (order >= 0) - return (result); + if (order >= 0) { + return (ISC_R_SUCCESS); + } nlabels = dns_name_countlabels(name); @@ -1160,17 +1178,21 @@ verifyemptynodes(const vctx_t *vctx, dns_name_t *name, dns_name_t *prevname, &suffix); if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { - tresult = verifynsec3s(vctx, &suffix, - nsec3paramset, - isdelegation, ISC_TRUE, - NULL, 0); - if (result == ISC_R_SUCCESS && - tresult != ISC_R_SUCCESS) - result = tresult; + result = verifynsec3s(vctx, &suffix, + nsec3paramset, + isdelegation, ISC_TRUE, + NULL, 0, &tvresult); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (*vresult == ISC_R_SUCCESS) { + *vresult = tvresult; + } } } } - return (result); + + return (ISC_R_SUCCESS); } static isc_result_t @@ -1643,12 +1665,18 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { if (prevname != NULL) { result = verifyemptynodes(vctx, name, prevname, isdelegation, - &vctx->nsec3paramset); + &vctx->nsec3paramset, + &tvresult); + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(vctx->db, &node); + goto done; + } } else prevname = dns_fixedname_name(&fprevname); dns_name_copy(name, prevname, NULL); - if (*vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS) - *vresult = result; + if (*vresult == ISC_R_SUCCESS) { + *vresult = tvresult; + } dns_db_detachnode(vctx->db, &node); } From 0ed3a2b269e67e3347057e0bd4a840af5e4bc474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 28/38] Do not call exit() upon isoptout() errors Replace all check_result() calls inside isoptout() with zoneverify_log_error() calls and error handling code. Enable isoptout() to signal errors to the caller using its return value. Modify the call site of isoptout() so that its errors are properly handled. --- lib/dns/zoneverify.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 20e0c41fd2..f06dd062d3 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -605,8 +605,8 @@ record_found(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, return (ISC_R_SUCCESS); } -static isc_boolean_t -isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata) { +static isc_result_t +isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata, isc_boolean_t *optout) { dns_rdataset_t rdataset; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_nsec3_t nsec3; @@ -617,7 +617,6 @@ isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata) { 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); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -627,7 +626,11 @@ isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata) { vctx->origin, nsec3param.hash, nsec3param.iterations, nsec3param.salt, nsec3param.salt_length); - check_result(result, "dns_nsec3_hashname()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_nsec3_hashname(): %s", + isc_result_totext(result)); + return (result); + } dns_rdataset_init(&rdataset); hashname = dns_fixedname_name(&fixed); @@ -636,24 +639,32 @@ isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata) { result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_nsec3, 0, 0, &rdataset, NULL); - if (result != ISC_R_SUCCESS) - return (ISC_FALSE); + if (result != ISC_R_SUCCESS) { + *optout = ISC_FALSE; + result = ISC_R_SUCCESS; + goto done; + } result = dns_rdataset_first(&rdataset); - check_result(result, "dns_rdataset_first()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "dns_rdataset_first(): %s", + isc_result_totext(result)); + goto done; + } dns_rdataset_current(&rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec3, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - ret = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0); + *optout = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0); + done: if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); if (node != NULL) dns_db_detachnode(vctx->db, &node); - return (ret); + return (result); } static isc_result_t @@ -672,7 +683,7 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, dns_dbnode_t *node = NULL; unsigned char rawhash[NSEC3_MAX_HASH_LENGTH]; size_t rhsize = sizeof(rawhash); - isc_boolean_t optout; + isc_boolean_t optout = ISC_FALSE; result = dns_rdata_tostruct(rdata, &nsec3param, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -683,7 +694,10 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, if (!dns_nsec3_supportedhash(nsec3param.hash)) return (ISC_R_SUCCESS); - optout = isoptout(vctx, rdata); + result = isoptout(vctx, rdata, &optout); + if (result != ISC_R_SUCCESS) { + return (result); + } dns_fixedname_init(&fixed); result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, name, From 0d07de9228531557990282deb7226d17f74daef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 29/38] Do not call exit() upon match_nsec3() errors Make match_nsec3() return the verification result through a separate pointer, thus making it possible to signal errors using function return value. Replace all check_result() and fprintf() calls inside match_nsec3() with zoneverify_log_error() calls and error handling code. Modify all call sites of match_nsec3() so that its errors are properly handled. --- lib/dns/zoneverify.c | 50 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index f06dd062d3..67e8bf787b 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -444,7 +444,7 @@ static isc_result_t match_nsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset, unsigned char types[8192], unsigned int maxtype, - unsigned char *rawhash, size_t rhsize) + unsigned char *rawhash, size_t rhsize, isc_result_t *vresult) { unsigned char cbm[8244]; char namebuf[DNS_NAME_FORMATSIZE]; @@ -472,8 +472,10 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, } if (result != ISC_R_SUCCESS) { dns_name_format(name, namebuf, sizeof(namebuf)); - fprintf(stderr, "Missing NSEC3 record for %s\n", namebuf); - return (result); + zoneverify_log_error(vctx, "Missing NSEC3 record for %s", + namebuf); + *vresult = result; + return (ISC_R_SUCCESS); } /* @@ -482,9 +484,12 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, 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); + zoneverify_log_error(vctx, + "Bad NSEC3 record for %s, bit map " + "mismatch", + namebuf); + *vresult = ISC_R_FAILURE; + return (ISC_R_SUCCESS); } /* @@ -492,7 +497,11 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, */ result = record_nsec3(rawhash, &nsec3, vctx->mctx, vctx->expected_chains); - check_result(result, "record_nsec3()"); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "record_nsec3(): %s", + isc_result_totext(result)); + return (result); + } /* * Make sure there is only one NSEC3 record with this set of @@ -511,17 +520,20 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, 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; + zoneverify_log_error(vctx, + "Multiple NSEC3 records with the " + "same parameter set for %s", + namebuf); + *vresult = DNS_R_DUPLICATE; + return (ISC_R_SUCCESS); } } if (result != ISC_R_NOMORE) return (result); - result = ISC_R_SUCCESS; - return (result); + *vresult = ISC_R_SUCCESS; + + return (ISC_R_SUCCESS); } static isc_boolean_t @@ -679,7 +691,7 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, dns_rdata_nsec3param_t nsec3param; dns_fixedname_t fixed; dns_name_t *hashname; - isc_result_t result; + isc_result_t result, tvresult; dns_dbnode_t *node = NULL; unsigned char rawhash[NSEC3_MAX_HASH_LENGTH]; size_t rhsize = sizeof(rawhash); @@ -737,17 +749,23 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, result = ISC_R_SUCCESS; } else if (result == ISC_R_SUCCESS) { result = match_nsec3(vctx, name, &nsec3param, &rdataset, types, - maxtype, rawhash, rhsize); + maxtype, rawhash, rhsize, &tvresult); + if (result != ISC_R_SUCCESS) { + goto done; + } + result = tvresult; } *vresult = result; + result = ISC_R_SUCCESS; + done: if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); if (node != NULL) dns_db_detachnode(vctx->db, &node); - return (ISC_R_SUCCESS); + return (result); } static isc_result_t From bf65f729344180c52e6f94b67182db2d7cfe5c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 30/38] Do not call exit() upon record_nsec3() errors Replace the fprintf() call inside record_nsec3() with a zoneverify_log_error() call. Remove the "mctx" argument of record_nsec3() as it can be extracted from "vctx". Modify one of the record_nsec3() call sites so that its errors are properly handled. --- lib/dns/zoneverify.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 67e8bf787b..b83157a6f3 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -407,8 +407,8 @@ chain_equal(struct nsec3_chain_fixed *e1, struct nsec3_chain_fixed *e2) { } static isc_result_t -record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3, - isc_mem_t *mctx, isc_heap_t *chains) +record_nsec3(const vctx_t *vctx, const unsigned char *rawhash, + const dns_rdata_nsec3_t *nsec3, isc_heap_t *chains) { struct nsec3_chain_fixed *element; size_t len; @@ -417,7 +417,7 @@ record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3, len = sizeof(*element) + nsec3->next_length * 2 + nsec3->salt_length; - element = isc_mem_get(mctx, len); + element = isc_mem_get(vctx->mctx, len); if (element == NULL) return (ISC_R_NOMEMORY); memset(element, 0, len); @@ -433,9 +433,9 @@ record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3, memmove(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); + zoneverify_log_error(vctx, "isc_heap_insert failed: %s", + isc_result_totext(result)); + isc_mem_put(vctx->mctx, element, len); } return (result); } @@ -495,8 +495,7 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, /* * Record chain. */ - result = record_nsec3(rawhash, &nsec3, vctx->mctx, - vctx->expected_chains); + result = record_nsec3(vctx, rawhash, &nsec3, vctx->expected_chains); if (result != ISC_R_SUCCESS) { zoneverify_log_error(vctx, "record_nsec3(): %s", isc_result_totext(result)); @@ -585,8 +584,10 @@ record_found(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, isc_region_consume(&hashlabel, 1); isc_buffer_init(&b, owner, sizeof(owner)); result = isc_base32hex_decoderegion(&hashlabel, &b); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + result = ISC_R_SUCCESS; goto cleanup; + } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; @@ -607,14 +608,18 @@ record_found(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, /* * Record chain. */ - result = record_nsec3(owner, &nsec3, vctx->mctx, - vctx->found_chains); - check_result(result, "record_nsec3()"); + result = record_nsec3(vctx, owner, &nsec3, vctx->found_chains); + if (result != ISC_R_SUCCESS) { + zoneverify_log_error(vctx, "record_nsec3(): %s", + isc_result_totext(result)); + goto cleanup; + } } + result = ISC_R_SUCCESS; cleanup: dns_rdataset_disassociate(&rdataset); - return (ISC_R_SUCCESS); + return (result); } static isc_result_t From 5ac14cb75342e20a265e717530a1781855aa9c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 31/38] Do not call exit() upon dns_zoneverify_dnssec() errors Replace the remaining fatal() calls inside dns_zoneverify_dnssec() with zoneverify_log_error() and zoneverify_print() calls, ensuring proper cleanup. --- lib/dns/zoneverify.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index b83157a6f3..b6eb433f9e 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1837,12 +1837,18 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, goto done; } - if (ignore_kskflag ) { - if (!vctx.goodksk && !vctx.goodzsk) - fatal("No self-signed DNSKEY found."); - } else if (!vctx.goodksk) - fatal("No self-signed KSK DNSKEY found. Supply an active\n" - "key with the KSK flag set, or use '-P'."); + if (ignore_kskflag) { + if (!vctx.goodksk && !vctx.goodzsk) { + zoneverify_log_error(&vctx, + "No self-signed DNSKEY found"); + result = ISC_R_FAILURE; + goto done; + } + } else if (!vctx.goodksk) { + zoneverify_log_error(&vctx, "No self-signed KSK DNSKEY found"); + result = ISC_R_FAILURE; + goto done; + } determine_active_algorithms(&vctx, ignore_kskflag, keyset_kskonly); @@ -1863,9 +1869,13 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, goto done; } - if (vresult != ISC_R_SUCCESS) - fatal("DNSSEC completeness test failed (%s).", - dns_result_totext(vresult)); + result = vresult; + if (result != ISC_R_SUCCESS) { + zoneverify_print(&vctx, + "DNSSEC completeness test failed (%s).\n", + dns_result_totext(result)); + goto done; + } if (vctx.goodksk || ignore_kskflag) { print_summary(&vctx, keyset_kskonly); From 11a552a61457aa49c68d9da98db2fca21ee84c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 32/38] Properly handle record_found() errors record_found() returns an isc_result_t, but its value is not checked. Modify the only call site of record_found() so that its errors are properly handled. --- lib/dns/zoneverify.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index b6eb433f9e..7ab3906e52 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1744,8 +1744,11 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { dns_db_detachnode(vctx->db, &node); goto done; } - record_found(vctx, name, node, &vctx->nsec3paramset); + result = record_found(vctx, name, node, &vctx->nsec3paramset); dns_db_detachnode(vctx->db, &node); + if (result != ISC_R_SUCCESS) { + goto done; + } } result = ISC_R_SUCCESS; From 5609472fbe513d292c3e76ea034e9fe46a3b8f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 33/38] Replace remaining fprintf() calls with zoneverify_*() calls Replace all fprintf() calls inside lib/dns/zoneverify.c, but outside of zoneverify_log_error() and zoneverify_print() with calls to these functions. --- lib/dns/zoneverify.c | 81 +++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 7ab3906e52..eda5ea610a 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1063,7 +1063,7 @@ free_element_heap(void *element, void *uap) { } static isc_boolean_t -checknext(const struct nsec3_chain_fixed *first, +checknext(const vctx_t *vctx, const struct nsec3_chain_fixed *first, const struct nsec3_chain_fixed *e) { char buf[512]; @@ -1082,27 +1082,26 @@ checknext(const struct nsec3_chain_fixed *first, 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); + zoneverify_log_error(vctx, "Break in NSEC3 chain at: %.*s", + (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); + zoneverify_log_error(vctx, "Expected: %.*s", + (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); + zoneverify_log_error(vctx, "Found: %.*s", + (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(const vctx_t *vctx, isc_mem_t *mctx) { isc_result_t result = ISC_R_SUCCESS; @@ -1123,8 +1122,12 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { free_element(mctx, f); f = NULL; } else { - if (result == ISC_R_SUCCESS) - fprintf(stderr, EXPECTEDANDFOUND); + if (result == ISC_R_SUCCESS) { + zoneverify_log_error( + vctx, + "Expected and found NSEC3 " + "chains not equal"); + } result = ISC_R_FAILURE; /* * Attempt to resync found_chain. @@ -1142,12 +1145,14 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { } } } else if (result == ISC_R_SUCCESS) { - fprintf(stderr, EXPECTEDANDFOUND); + zoneverify_log_error(vctx, + "Expected and found NSEC3 chains " + "not equal"); result = ISC_R_FAILURE; } if (first == NULL || newchain(first, e)) { if (prev != NULL) { - if (!checknext(prev, first)) + if (!checknext(vctx, prev, first)) result = ISC_R_FAILURE; if (prev != first) free_element(mctx, prev); @@ -1157,14 +1162,14 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { prev = first = e; continue; } - if (!checknext(prev, e)) + if (!checknext(vctx, prev, e)) result = ISC_R_FAILURE; if (prev != first) free_element(mctx, prev); prev = e; } if (prev != NULL) { - if (!checknext(prev, first)) + if (!checknext(vctx, prev, first)) result = ISC_R_FAILURE; if (prev != first) free_element(mctx, prev); @@ -1174,7 +1179,9 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { do { if (f != NULL) { if (result == ISC_R_SUCCESS) { - fprintf(stderr, EXPECTEDANDFOUND); + zoneverify_log_error(vctx, + "Expected and found " + "NSEC3 chains not equal"); result = ISC_R_FAILURE; } free_element(mctx, f); @@ -1791,28 +1798,32 @@ print_summary(const vctx_t *vctx, isc_boolean_t keyset_kskonly) { char algbuf[DNS_SECALG_FORMATSIZE]; int i; - fprintf(stderr, "Zone fully signed:\n"); + zoneverify_print(vctx, "Zone fully signed:\n"); for (i = 0; i < 256; i++) { - if ((vctx->ksk_algorithms[i] != 0) || - (vctx->standby_ksk[i] != 0) || - (vctx->revoked_ksk[i] != 0) || - (vctx->zsk_algorithms[i] != 0) || - (vctx->standby_zsk[i] != 0) || - (vctx->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, vctx->ksk_algorithms[i], - vctx->standby_ksk[i], - vctx->revoked_ksk[i]); - fprintf(stderr, "%*sZSKs: " - "%u active, %u %s, %u revoked\n", - (int) strlen(algbuf) + 13, "", - vctx->zsk_algorithms[i], - vctx->standby_zsk[i], - keyset_kskonly ? "present" : "stand-by", - vctx->revoked_zsk[i]); + if ((vctx->ksk_algorithms[i] == 0) && + (vctx->standby_ksk[i] == 0) && + (vctx->revoked_ksk[i] == 0) && + (vctx->zsk_algorithms[i] == 0) && + (vctx->standby_zsk[i] == 0) && + (vctx->revoked_zsk[i] == 0)) + { + continue; } + dns_secalg_format(i, algbuf, sizeof(algbuf)); + zoneverify_print(vctx, + "Algorithm: %s: KSKs: " + "%u active, %u stand-by, %u revoked\n", + algbuf, vctx->ksk_algorithms[i], + vctx->standby_ksk[i], + vctx->revoked_ksk[i]); + zoneverify_print(vctx, + "%*sZSKs: " + "%u active, %u %s, %u revoked\n", + (int)strlen(algbuf) + 13, "", + vctx->zsk_algorithms[i], + vctx->standby_zsk[i], + keyset_kskonly ? "present" : "stand-by", + vctx->revoked_zsk[i]); } } From a7ae61574341de922f0282f091f6e3c941cdc3a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 34/38] Remove fatal() and check_result() from lib/dns/zoneverify.c Since no function in lib/dns/zoneverify.c uses fatal() or check_result() any more, remove them. --- lib/dns/zoneverify.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index eda5ea610a..7c847d17c3 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -93,18 +93,6 @@ struct nsec3_chain_fixed { */ }; -static void -fatal(const char *format, ...) { - va_list args; - - fprintf(stderr, "fatal: "); - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - fprintf(stderr, "\n"); - exit(1); -} - /*% * Log a zone verification error described by 'fmt' and the variable arguments * following it. Either use dns_zone_logv() or print to stderr, depending on @@ -143,12 +131,6 @@ zoneverify_print(const vctx_t *vctx, const char *fmt, ...) { va_end(ap); } -static void -check_result(isc_result_t result, const char *message) { - if (result != ISC_R_SUCCESS) - fatal("%s: %s", message, isc_result_totext(result)); -} - static isc_boolean_t is_delegation(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp) From 24bca1c4b44abf029024f1790878d0366567bf20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 35/38] Propagate dns_zoneverify_dnssec() errors to callers Since exit() is no longer called upon any dns_zoneverify_dnssec() error, verification failures should be signalled to callers. Make dns_zoneverify_dnssec() return an isc_result_t and handle both success and error appropriately in bin/dnssec/dnssec-signzone.c and bin/dnssec/dnssec-verify.c. This enables memory leak detection during shutdown of these tools and causes dnssec-signzone to print signing statistics even when zone verification fails. --- bin/dnssec/dnssec-signzone.c | 35 ++++++++++++++++++++++---------- bin/dnssec/dnssec-verify.c | 6 +++--- lib/dns/include/dns/zoneverify.h | 2 +- lib/dns/zoneverify.c | 6 ++++-- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index 3d3e6f80f1..2747c627fd 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -3227,7 +3227,7 @@ main(int argc, char *argv[]) { isc_time_t timer_start, timer_finish; isc_time_t sign_start, sign_finish; dns_dnsseckey_t *key; - isc_result_t result; + isc_result_t result, vresult; isc_log_t *log = NULL; #ifdef USE_PKCS11 const char *engine = PKCS11_ENGINE; @@ -3912,9 +3912,18 @@ main(int argc, char *argv[]) { postsign(); TIME_NOW(&sign_finish); - if (!disable_zone_check) - dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, mctx, - ignore_kskflag, keyset_kskonly); + if (disable_zone_check) { + vresult = ISC_R_SUCCESS; + } else { + vresult = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, + mctx, ignore_kskflag, + keyset_kskonly); + if (vresult != ISC_R_SUCCESS) { + fprintf(output_stdout ? stderr : stdout, + "Zone verification failed (%s)\n", + isc_result_totext(vresult)); + } + } if (outputformat != dns_masterformat_text) { dns_masterrawheader_t header; @@ -3940,12 +3949,16 @@ main(int argc, char *argv[]) { check_result(result, "isc_stdio_close"); removefile = ISC_FALSE; - result = isc_file_rename(tempfile, output); - if (result != ISC_R_SUCCESS) - fatal("failed to rename temp file to %s: %s", - output, isc_result_totext(result)); - - printf("%s\n", output); + if (vresult == ISC_R_SUCCESS) { + result = isc_file_rename(tempfile, output); + if (result != ISC_R_SUCCESS) { + fatal("failed to rename temp file to %s: %s", + output, isc_result_totext(result)); + } + printf("%s\n", output); + } else { + isc_file_remove(tempfile); + } } dns_db_closeversion(gdb, &gversion, ISC_FALSE); @@ -3985,5 +3998,5 @@ main(int argc, char *argv[]) { #ifdef _WIN32 DestroySockets(); #endif - return (0); + return (vresult == ISC_R_SUCCESS ? 0 : 1); } diff --git a/bin/dnssec/dnssec-verify.c b/bin/dnssec/dnssec-verify.c index 48b9e0b186..3fb5fa2c49 100644 --- a/bin/dnssec/dnssec-verify.c +++ b/bin/dnssec/dnssec-verify.c @@ -323,8 +323,8 @@ main(int argc, char *argv[]) { result = dns_db_newversion(gdb, &gversion); check_result(result, "dns_db_newversion()"); - dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, mctx, - ignore_kskflag, keyset_kskonly); + result = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, mctx, + ignore_kskflag, keyset_kskonly); dns_db_closeversion(gdb, &gversion, ISC_FALSE); dns_db_detach(&gdb); @@ -338,5 +338,5 @@ main(int argc, char *argv[]) { (void) isc_app_finish(); - return (0); + return (result == ISC_R_SUCCESS ? 0 : 1); } diff --git a/lib/dns/include/dns/zoneverify.h b/lib/dns/include/dns/zoneverify.h index 090fcc2f9b..0e491c23dd 100644 --- a/lib/dns/include/dns/zoneverify.h +++ b/lib/dns/include/dns/zoneverify.h @@ -31,7 +31,7 @@ ISC_LANG_BEGINDECLS * The rest of the zone was signed with at least one of the ZSKs * present in the DNSKEY RRSET. */ -void +isc_result_t dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_mem_t *mctx, isc_boolean_t ignore_kskflag, diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 7c847d17c3..b3437156e9 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -1809,7 +1809,7 @@ print_summary(const vctx_t *vctx, isc_boolean_t keyset_kskonly) { } } -void +isc_result_t dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, isc_mem_t *mctx, isc_boolean_t ignore_kskflag, @@ -1820,7 +1820,7 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, result = vctx_init(&vctx, mctx, zone, db, ver, origin); if (result != ISC_R_SUCCESS) { - return; + return (result); } result = check_apex_rrsets(&vctx); @@ -1879,4 +1879,6 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, done: vctx_destroy(&vctx); + + return (result); } From c094d1e4f37cde79f8852b96edfbfbb19f449d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 36/38] Constify function arguments throughout lib/dns/zoneverify.c Where possible, apply the const qualifier to arguments of functions present in lib/dns/zoneverify.c. --- lib/dns/zoneverify.c | 62 +++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index b3437156e9..22c90ff307 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -132,7 +132,7 @@ zoneverify_print(const vctx_t *vctx, const char *fmt, ...) { } static isc_boolean_t -is_delegation(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, +is_delegation(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp) { dns_rdataset_t nsset; @@ -174,7 +174,7 @@ has_dname(const vctx_t *vctx, dns_dbnode_t *node) { } static isc_boolean_t -goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, dns_name_t *name, +goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, const dns_name_t *name, dns_rdataset_t *keyrdataset, dns_rdataset_t *rdataset) { dns_rdata_dnskey_t key; @@ -213,8 +213,8 @@ goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, dns_name_t *name, } static isc_result_t -verifynsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, - dns_name_t *nextname, isc_result_t *vresult) +verifynsec(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, + const dns_name_t *nextname, isc_result_t *vresult) { unsigned char buffer[DNS_NSEC_BUFFERSIZE]; char namebuf[DNS_NAME_FORMATSIZE]; @@ -300,8 +300,8 @@ verifynsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, } static isc_result_t -check_no_rrsig(const vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, - dns_dbnode_t *node) +check_no_rrsig(const vctx_t *vctx, const dns_rdataset_t *rdataset, + const dns_name_t *name, dns_dbnode_t *node) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; @@ -371,7 +371,9 @@ chain_compare(void *arg1, void *arg2) { } static isc_boolean_t -chain_equal(struct nsec3_chain_fixed *e1, struct nsec3_chain_fixed *e2) { +chain_equal(const struct nsec3_chain_fixed *e1, + const struct nsec3_chain_fixed *e2) +{ size_t len; if (e1->hash != e2->hash) @@ -423,10 +425,10 @@ record_nsec3(const vctx_t *vctx, const unsigned char *rawhash, } static isc_result_t -match_nsec3(const vctx_t *vctx, dns_name_t *name, - dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset, - unsigned char types[8192], unsigned int maxtype, - unsigned char *rawhash, size_t rhsize, isc_result_t *vresult) +match_nsec3(const vctx_t *vctx, const dns_name_t *name, + const dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset, + const unsigned char types[8192], unsigned int maxtype, + const unsigned char *rawhash, size_t rhsize, isc_result_t *vresult) { unsigned char cbm[8244]; char namebuf[DNS_NAME_FORMATSIZE]; @@ -518,7 +520,7 @@ match_nsec3(const vctx_t *vctx, dns_name_t *name, } static isc_boolean_t -innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { +innsec3params(const dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { dns_rdata_nsec3param_t nsec3param; isc_result_t result; @@ -542,7 +544,7 @@ innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { } static isc_result_t -record_found(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, +record_found(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, dns_rdataset_t *nsec3paramset) { unsigned char owner[NSEC3_MAX_HASH_LENGTH]; @@ -605,7 +607,9 @@ record_found(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, } static isc_result_t -isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata, isc_boolean_t *optout) { +isoptout(const vctx_t *vctx, const dns_rdata_t *nsec3rdata, + isc_boolean_t *optout) +{ dns_rdataset_t rdataset; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_nsec3_t nsec3; @@ -667,10 +671,10 @@ isoptout(const vctx_t *vctx, dns_rdata_t *nsec3rdata, isc_boolean_t *optout) { } static isc_result_t -verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, - isc_boolean_t delegation, isc_boolean_t empty, - unsigned char types[8192], unsigned int maxtype, - isc_result_t *vresult) +verifynsec3(const vctx_t *vctx, const dns_name_t *name, + const dns_rdata_t *rdata, isc_boolean_t delegation, + isc_boolean_t empty, const unsigned char types[8192], + unsigned int maxtype, isc_result_t *vresult) { char namebuf[DNS_NAME_FORMATSIZE]; char hashbuf[DNS_NAME_FORMATSIZE]; @@ -756,9 +760,9 @@ verifynsec3(const vctx_t *vctx, dns_name_t *name, dns_rdata_t *rdata, } static isc_result_t -verifynsec3s(const vctx_t *vctx, dns_name_t *name, +verifynsec3s(const vctx_t *vctx, const dns_name_t *name, dns_rdataset_t *nsec3paramset, isc_boolean_t delegation, - isc_boolean_t empty, unsigned char types[8192], + isc_boolean_t empty, const unsigned char types[8192], unsigned int maxtype, isc_result_t *vresult) { isc_result_t result; @@ -784,7 +788,7 @@ verifynsec3s(const vctx_t *vctx, dns_name_t *name, } static isc_result_t -verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, +verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, dns_dbnode_t *node, dns_rdataset_t *keyrdataset) { unsigned char set_algorithms[256]; @@ -878,10 +882,10 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, dns_name_t *name, } static isc_result_t -verifynode(vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node, +verifynode(vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, isc_boolean_t delegation, dns_rdataset_t *keyrdataset, dns_rdataset_t *nsecset, dns_rdataset_t *nsec3paramset, - dns_name_t *nextname, isc_result_t *vresult) + const dns_name_t *nextname, isc_result_t *vresult) { unsigned char types[8192]; unsigned int maxtype = 0; @@ -992,7 +996,7 @@ is_empty(const vctx_t *vctx, dns_dbnode_t *node, isc_boolean_t *empty) { } static isc_result_t -check_no_nsec(const vctx_t *vctx, dns_name_t *name, dns_dbnode_t *node) { +check_no_nsec(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node) { isc_boolean_t nsec_exists = ISC_FALSE; dns_rdataset_t rdataset; isc_result_t result; @@ -1177,9 +1181,9 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { } static isc_result_t -verifyemptynodes(const vctx_t *vctx, dns_name_t *name, dns_name_t *prevname, - isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset, - isc_result_t *vresult) +verifyemptynodes(const vctx_t *vctx, const dns_name_t *name, + const dns_name_t *prevname, isc_boolean_t isdelegation, + dns_rdataset_t *nsec3paramset, isc_result_t *vresult) { dns_namereln_t reln; int order; @@ -1405,8 +1409,8 @@ check_apex_rrsets(vctx_t *vctx) { * 'dnskey' is a KSK and false otherwise. */ static void -check_dnskey_sigs(vctx_t *vctx, dns_rdata_dnskey_t *dnskey, dns_rdata_t *rdata, - isc_boolean_t is_ksk) +check_dnskey_sigs(vctx_t *vctx, const dns_rdata_dnskey_t *dnskey, + dns_rdata_t *rdata, isc_boolean_t is_ksk) { unsigned char *active_keys, *standby_keys; isc_boolean_t *goodkey; From 8649c59a758be5f4d540af7569ef54e5c219cd80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 37/38] Fix coding style issues in lib/dns/zoneverify.c --- lib/dns/zoneverify.c | 277 +++++++++++++++++++++++++++++-------------- 1 file changed, 189 insertions(+), 88 deletions(-) diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 22c90ff307..52c37a72a7 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -138,15 +138,17 @@ is_delegation(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, dns_rdataset_t nsset; isc_result_t result; - if (dns_name_equal(name, vctx->origin)) + if (dns_name_equal(name, vctx->origin)) { return (ISC_FALSE); + } dns_rdataset_init(&nsset); result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_ns, 0, 0, &nsset, NULL); if (dns_rdataset_isassociated(&nsset)) { - if (ttlp != NULL) + if (ttlp != NULL) { *ttlp = nsset.ttl; + } dns_rdataset_disassociate(&nsset); } @@ -187,18 +189,21 @@ goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, const dns_name_t *name, for (result = dns_rdataset_first(keyrdataset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(keyrdataset)) { + result = dns_rdataset_next(keyrdataset)) + { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(keyrdataset, &rdata); result = dns_rdata_tostruct(&rdata, &key, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); result = dns_dnssec_keyfromrdata(vctx->origin, &rdata, vctx->mctx, &dstkey); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (ISC_FALSE); + } if (sig.algorithm != key.algorithm || sig.keyid != dst_key_id(dstkey) || - !dns_name_equal(&sig.signer, vctx->origin)) { + !dns_name_equal(&sig.signer, vctx->origin)) + { dst_key_free(&dstkey); continue; } @@ -206,7 +211,7 @@ goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, const dns_name_t *name, 0, vctx->mctx, sigrdata, NULL); dst_key_free(&dstkey); if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { - return(ISC_TRUE); + return (ISC_TRUE); } } return (ISC_FALSE); @@ -318,11 +323,14 @@ check_no_rrsig(const vctx_t *vctx, const dns_rdataset_t *rdataset, } for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(rdsiter)) { + 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) { @@ -333,8 +341,9 @@ check_no_rrsig(const vctx_t *vctx, const dns_rdataset_t *rdataset, "for %s/%s", namebuf, typebuf); } - if (dns_rdataset_isassociated(&sigrdataset)) + if (dns_rdataset_isassociated(&sigrdataset)) { dns_rdataset_disassociate(&sigrdataset); + } dns_rdatasetiter_destroy(&rdsiter); return (ISC_R_SUCCESS); @@ -348,25 +357,34 @@ chain_compare(void *arg1, void *arg2) { /* * Do each element in turn to get a stable sort. */ - if (e1->hash < e2->hash) + if (e1->hash < e2->hash) { return (ISC_TRUE); - if (e1->hash > e2->hash) + } + if (e1->hash > e2->hash) { return (ISC_FALSE); - if (e1->iterations < e2->iterations) + } + if (e1->iterations < e2->iterations) { return (ISC_TRUE); - if (e1->iterations > e2->iterations) + } + if (e1->iterations > e2->iterations) { return (ISC_FALSE); - if (e1->salt_length < e2->salt_length) + } + if (e1->salt_length < e2->salt_length) { return (ISC_TRUE); - if (e1->salt_length > e2->salt_length) + } + if (e1->salt_length > e2->salt_length) { return (ISC_FALSE); - if (e1->next_length < e2->next_length) + } + if (e1->next_length < e2->next_length) { return (ISC_TRUE); - if (e1->next_length > e2->next_length) + } + 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) + if (memcmp(e1 + 1, e2 + 1, len) < 0) { return (ISC_TRUE); + } return (ISC_FALSE); } @@ -376,17 +394,22 @@ chain_equal(const struct nsec3_chain_fixed *e1, { size_t len; - if (e1->hash != e2->hash) + if (e1->hash != e2->hash) { return (ISC_FALSE); - if (e1->iterations != e2->iterations) + } + if (e1->iterations != e2->iterations) { return (ISC_FALSE); - if (e1->salt_length != e2->salt_length) + } + if (e1->salt_length != e2->salt_length) { return (ISC_FALSE); - if (e1->next_length != e2->next_length) + } + 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) + if (memcmp(e1 + 1, e2 + 1, len) != 0) { return (ISC_FALSE); + } return (ISC_TRUE); } @@ -402,8 +425,9 @@ record_nsec3(const vctx_t *vctx, const unsigned char *rawhash, len = sizeof(*element) + nsec3->next_length * 2 + nsec3->salt_length; element = isc_mem_get(vctx->mctx, len); - if (element == NULL) + if (element == NULL) { return (ISC_R_NOMEMORY); + } memset(element, 0, len); element->hash = nsec3->hash; element->salt_length = nsec3->salt_length; @@ -441,7 +465,8 @@ match_nsec3(const vctx_t *vctx, const dns_name_t *name, */ for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(rdataset)) { + result = dns_rdataset_next(rdataset)) + { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec3, NULL); @@ -452,7 +477,9 @@ match_nsec3(const vctx_t *vctx, const dns_name_t *name, 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)); @@ -492,7 +519,8 @@ match_nsec3(const vctx_t *vctx, const dns_name_t *name, */ for (result = dns_rdataset_next(rdataset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(rdataset)) { + result = dns_rdataset_next(rdataset)) + { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec3, NULL); @@ -501,7 +529,8 @@ match_nsec3(const vctx_t *vctx, const dns_name_t *name, nsec3.iterations == nsec3param->iterations && nsec3.salt_length == nsec3param->salt_length && memcmp(nsec3.salt, nsec3param->salt, - nsec3.salt_length) == 0) { + nsec3.salt_length) == 0) + { dns_name_format(name, namebuf, sizeof(namebuf)); zoneverify_log_error(vctx, "Multiple NSEC3 records with the " @@ -511,8 +540,9 @@ match_nsec3(const vctx_t *vctx, const dns_name_t *name, return (ISC_R_SUCCESS); } } - if (result != ISC_R_NOMORE) + if (result != ISC_R_NOMORE) { return (result); + } *vresult = ISC_R_SUCCESS; @@ -526,7 +556,8 @@ innsec3params(const dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { for (result = dns_rdataset_first(nsec3paramset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(nsec3paramset)) { + result = dns_rdataset_next(nsec3paramset)) + { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(nsec3paramset, &rdata); @@ -538,7 +569,9 @@ innsec3params(const dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { nsec3param.salt_length == nsec3->salt_length && memcmp(nsec3param.salt, nsec3->salt, nsec3->salt_length) == 0) + { return (ISC_TRUE); + } } return (ISC_FALSE); } @@ -554,15 +587,19 @@ record_found(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, isc_buffer_t b; isc_result_t result; - if (nsec3paramset == NULL || !dns_rdataset_isassociated(nsec3paramset)) + if (nsec3paramset == NULL || + !dns_rdataset_isassociated(nsec3paramset)) + { return (ISC_R_SUCCESS); + } dns_rdataset_init(&rdataset); result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_nsec3, 0, 0, &rdataset, NULL); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (ISC_R_SUCCESS); + } dns_name_getlabel(name, 0, &hashlabel); isc_region_consume(&hashlabel, 1); @@ -575,19 +612,22 @@ record_found(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(&rdataset)) { + result = dns_rdataset_next(&rdataset)) + { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(&rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec3, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if (nsec3.next_length != isc_buffer_usedlength(&b)) + if (nsec3.next_length != isc_buffer_usedlength(&b)) { continue; + } /* * We only care about NSEC3 records that match a NSEC3PARAM * record. */ - if (!innsec3params(&nsec3, nsec3paramset)) + if (!innsec3params(&nsec3, nsec3paramset)) { continue; + } /* * Record chain. @@ -638,10 +678,11 @@ isoptout(const vctx_t *vctx, const dns_rdata_t *nsec3rdata, dns_rdataset_init(&rdataset); hashname = dns_fixedname_name(&fixed); result = dns_db_findnsec3node(vctx->db, hashname, ISC_FALSE, &node); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_nsec3, 0, 0, &rdataset, NULL); + } if (result != ISC_R_SUCCESS) { *optout = ISC_FALSE; result = ISC_R_SUCCESS; @@ -662,10 +703,12 @@ isoptout(const vctx_t *vctx, const dns_rdata_t *nsec3rdata, *optout = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0); done: - if (dns_rdataset_isassociated(&rdataset)) + if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); - if (node != NULL) + } + if (node != NULL) { dns_db_detachnode(vctx->db, &node); + } return (result); } @@ -691,11 +734,13 @@ verifynsec3(const vctx_t *vctx, const dns_name_t *name, result = dns_rdata_tostruct(rdata, &nsec3param, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if (nsec3param.flags != 0) + if (nsec3param.flags != 0) { return (ISC_R_SUCCESS); + } - if (!dns_nsec3_supportedhash(nsec3param.hash)) + if (!dns_nsec3_supportedhash(nsec3param.hash)) { return (ISC_R_SUCCESS); + } result = isoptout(vctx, rdata, &optout); if (result != ISC_R_SUCCESS) { @@ -722,10 +767,11 @@ verifynsec3(const vctx_t *vctx, const dns_name_t *name, dns_rdataset_init(&rdataset); hashname = dns_fixedname_name(&fixed); result = dns_db_findnsec3node(vctx->db, hashname, ISC_FALSE, &node); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { result = dns_db_findrdataset(vctx->db, node, vctx->ver, dns_rdatatype_nsec3, 0, 0, &rdataset, NULL); + } if (result != ISC_R_SUCCESS && (!delegation || (empty && !optout) || (!empty && dns_nsec_isset(types, dns_rdatatype_ds)))) @@ -751,10 +797,12 @@ verifynsec3(const vctx_t *vctx, const dns_name_t *name, result = ISC_R_SUCCESS; done: - if (dns_rdataset_isassociated(&rdataset)) + if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); - if (node != NULL) + } + if (node != NULL) { dns_db_detachnode(vctx->db, &node); + } return (result); } @@ -769,7 +817,8 @@ verifynsec3s(const vctx_t *vctx, const dns_name_t *name, for (result = dns_rdataset_first(nsec3paramset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(nsec3paramset)) { + result = dns_rdataset_next(nsec3paramset)) + { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(nsec3paramset, &rdata); @@ -782,8 +831,9 @@ verifynsec3s(const vctx_t *vctx, const dns_name_t *name, break; } } - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; + } return (result); } @@ -809,11 +859,14 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, } for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(rdsiter)) { + 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) { @@ -821,9 +874,11 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); zoneverify_log_error(vctx, "No signatures for %s/%s", namebuf, typebuf); - for (i = 0; i < 256; i++) - if (vctx->act_algorithms[i] != 0) + for (i = 0; i < 256; i++) { + if (vctx->act_algorithms[i] != 0) { vctx->bad_algorithms[i] = 1; + } + } result = ISC_R_SUCCESS; goto done; } @@ -831,7 +886,8 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, memset(set_algorithms, 0, sizeof(set_algorithms)); for (result = dns_rdataset_first(&sigrdataset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(&sigrdataset)) { + result = dns_rdataset_next(&sigrdataset)) + { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_rrsig_t sig; @@ -850,19 +906,24 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, } if ((set_algorithms[sig.algorithm] != 0) || (vctx->act_algorithms[sig.algorithm] == 0)) + { continue; - if (goodsig(vctx, &rdata, name, keyrdataset, rdataset)) + } + if (goodsig(vctx, &rdata, name, keyrdataset, rdataset)) { set_algorithms[sig.algorithm] = 1; + } } result = ISC_R_SUCCESS; if (memcmp(set_algorithms, vctx->act_algorithms, - sizeof(set_algorithms))) { + sizeof(set_algorithms))) + { dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); - for (i = 0; i < 256; i++) + for (i = 0; i < 256; i++) { if ((vctx->act_algorithms[i] != 0) && - (set_algorithms[i] == 0)) { + (set_algorithms[i] == 0)) + { dns_secalg_format(i, algbuf, sizeof(algbuf)); zoneverify_log_error(vctx, "No correct %s signature " @@ -870,6 +931,7 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, algbuf, namebuf, typebuf); vctx->bad_algorithms[i] = 1; } + } } done: @@ -915,7 +977,8 @@ verifynode(vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, if (rdataset.type != dns_rdatatype_rrsig && rdataset.type != dns_rdatatype_dnskey && (!delegation || rdataset.type == dns_rdatatype_ds || - rdataset.type == dns_rdatatype_nsec)) { + rdataset.type == dns_rdatatype_nsec)) + { result = verifyset(vctx, &rdataset, name, node, keyrdataset); if (result != ISC_R_SUCCESS) { @@ -924,20 +987,24 @@ verifynode(vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, return (result); } dns_nsec_setbit(types, rdataset.type, 1); - if (rdataset.type > maxtype) + 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) + rdataset.type != dns_rdatatype_dnskey) + { + if (rdataset.type == dns_rdatatype_ns) { dns_nsec_setbit(types, rdataset.type, 1); + } result = check_no_rrsig(vctx, &rdataset, name, node); if (result != ISC_R_SUCCESS) { dns_rdataset_disassociate(&rdataset); dns_rdatasetiter_destroy(&rdsiter); return (result); } - } else + } else { dns_nsec_setbit(types, rdataset.type, 1); + } dns_rdataset_disassociate(&rdataset); result = dns_rdatasetiter_next(rdsiter); } @@ -962,7 +1029,9 @@ verifynode(vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node, *vresult = tvresult; } - if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { + if (nsec3paramset != NULL && + dns_rdataset_isassociated(nsec3paramset)) + { result = verifynsec3s(vctx, name, nsec3paramset, delegation, ISC_FALSE, types, maxtype, &tvresult); if (result != ISC_R_SUCCESS) { @@ -1013,8 +1082,9 @@ check_no_nsec(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node) { nsec_exists = ISC_TRUE; } - if (dns_rdataset_isassociated(&rdataset)) + if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); + } return (nsec_exists ? ISC_R_FAILURE : ISC_R_SUCCESS); } @@ -1028,7 +1098,9 @@ newchain(const struct nsec3_chain_fixed *first, 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); } @@ -1061,8 +1133,9 @@ checknext(const vctx_t *vctx, const struct nsec3_chain_fixed *first, d1 += first->salt_length + first->next_length; d2 += e->salt_length; - if (memcmp(d1, d2, first->next_length) == 0) + if (memcmp(d1, d2, first->next_length) == 0) { return (ISC_TRUE); + } DE_CONST(d1 - first->next_length, sr.base); sr.length = first->next_length; @@ -1096,8 +1169,9 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { while ((e = isc_heap_element(vctx->expected_chains, 1)) != NULL) { isc_heap_delete(vctx->expected_chains, 1); - if (f == NULL) + if (f == NULL) { f = isc_heap_element(vctx->found_chains, 1); + } if (f != NULL) { isc_heap_delete(vctx->found_chains, 1); @@ -1120,9 +1194,12 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { */ while (f != NULL && !chain_compare(e, f)) { free_element(mctx, f); - f = isc_heap_element(vctx->found_chains, 1); - if (f != NULL) - isc_heap_delete(vctx->found_chains, 1); + f = isc_heap_element( + vctx->found_chains, 1); + if (f != NULL) { + isc_heap_delete( + vctx->found_chains, 1); + } if (f != NULL && chain_equal(e, f)) { free_element(mctx, f); f = NULL; @@ -1138,30 +1215,38 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { } if (first == NULL || newchain(first, e)) { if (prev != NULL) { - if (!checknext(vctx, prev, first)) + if (!checknext(vctx, prev, first)) { result = ISC_R_FAILURE; - if (prev != first) + } + if (prev != first) { free_element(mctx, prev); + } } - if (first != NULL) + if (first != NULL) { free_element(mctx, first); + } prev = first = e; continue; } - if (!checknext(vctx, prev, e)) + if (!checknext(vctx, prev, e)) { result = ISC_R_FAILURE; - if (prev != first) + } + if (prev != first) { free_element(mctx, prev); + } prev = e; } if (prev != NULL) { - if (!checknext(vctx, prev, first)) + if (!checknext(vctx, prev, first)) { result = ISC_R_FAILURE; - if (prev != first) + } + if (prev != first) { free_element(mctx, prev); + } } - if (first != NULL) + if (first != NULL) { free_element(mctx, first); + } do { if (f != NULL) { if (result == ISC_R_SUCCESS) { @@ -1173,8 +1258,9 @@ verify_nsec3_chains(const vctx_t *vctx, isc_mem_t *mctx) { free_element(mctx, f); } f = isc_heap_element(vctx->found_chains, 1); - if (f != NULL) + if (f != NULL) { isc_heap_delete(vctx->found_chains, 1); + } } while (f != NULL); return (result); @@ -1201,13 +1287,15 @@ verifyemptynodes(const vctx_t *vctx, const dns_name_t *name, nlabels = dns_name_countlabels(name); if (reln == dns_namereln_commonancestor || - reln == dns_namereln_contains) { + 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)) { + dns_rdataset_isassociated(nsec3paramset)) + { result = verifynsec3s(vctx, &suffix, nsec3paramset, isdelegation, ISC_TRUE, @@ -1453,20 +1541,22 @@ check_dnskey(vctx_t *vctx) { for (result = dns_rdataset_first(&vctx->keyset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(&vctx->keyset)) { + result = dns_rdataset_next(&vctx->keyset)) + { dns_rdataset_current(&vctx->keyset, &rdata); result = dns_rdata_tostruct(&rdata, &dnskey, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); is_ksk = ISC_TF((dnskey.flags & DNS_KEYFLAG_KSK) != 0); - if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0) + if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0) { ; - else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { + } else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && !dns_dnssec_selfsigns(&rdata, vctx->origin, &vctx->keyset, &vctx->keysigs, ISC_FALSE, - vctx->mctx)) { + vctx->mctx)) + { char namebuf[DNS_NAME_FORMATSIZE]; char buffer[1024]; isc_buffer_t buf; @@ -1491,11 +1581,14 @@ check_dnskey(vctx_t *vctx) { return (ISC_R_FAILURE); } if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && - vctx->revoked_ksk[dnskey.algorithm] != 255) + vctx->revoked_ksk[dnskey.algorithm] != 255) + { vctx->revoked_ksk[dnskey.algorithm]++; - else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && - vctx->revoked_zsk[dnskey.algorithm] != 255) + } else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && + vctx->revoked_zsk[dnskey.algorithm] != 255) + { vctx->revoked_zsk[dnskey.algorithm]++; + } } else { check_dnskey_sigs(vctx, &dnskey, &rdata, is_ksk); } @@ -1517,13 +1610,14 @@ determine_active_algorithms(vctx_t *vctx, isc_boolean_t ignore_kskflag, "Verifying the zone using the following algorithms:"); for (i = 0; i < 256; i++) { - if (ignore_kskflag) + if (ignore_kskflag) { vctx->act_algorithms[i] = (vctx->ksk_algorithms[i] != 0 || vctx->zsk_algorithms[i] != 0) ? 1 : 0; - else + } else { vctx->act_algorithms[i] = vctx->ksk_algorithms[i] != 0 ? 1 : 0; + } if (vctx->act_algorithms[i] != 0) { dns_secalg_format(i, algbuf, sizeof(algbuf)); zoneverify_print(vctx, " %s", algbuf); @@ -1542,7 +1636,9 @@ determine_active_algorithms(vctx_t *vctx, isc_boolean_t ignore_kskflag, */ if ((vctx->ksk_algorithms[i] != 0) == (vctx->zsk_algorithms[i] != 0)) + { continue; + } dns_secalg_format(i, algbuf, sizeof(algbuf)); zoneverify_log_error(vctx, "Missing %s for algorithm %s", @@ -1687,8 +1783,9 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { dns_db_detachnode(vctx->db, &node); goto done; } - if (*vresult == ISC_R_UNSET) + if (*vresult == ISC_R_UNSET) { *vresult = ISC_R_SUCCESS; + } if (*vresult == ISC_R_SUCCESS) { *vresult = tvresult; } @@ -1701,8 +1798,9 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { dns_db_detachnode(vctx->db, &node); goto done; } - } else + } else { prevname = dns_fixedname_name(&fprevname); + } dns_name_copy(name, prevname, NULL); if (*vresult == ISC_R_SUCCESS) { *vresult = tvresult; @@ -1721,7 +1819,8 @@ verify_nodes(vctx_t *vctx, isc_result_t *vresult) { for (result = dns_dbiterator_first(dbiter); result == ISC_R_SUCCESS; - result = dns_dbiterator_next(dbiter) ) { + result = dns_dbiterator_next(dbiter)) + { result = dns_dbiterator_current(dbiter, &node, name); if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { zoneverify_log_error(vctx, @@ -1858,10 +1957,12 @@ dns_zoneverify_dnssec(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, } result = verify_nsec3_chains(&vctx, mctx); - if (vresult == ISC_R_UNSET) + if (vresult == ISC_R_UNSET) { vresult = ISC_R_SUCCESS; - if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS) + } + if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS) { vresult = result; + } result = check_bad_algorithms(&vctx); if (result != ISC_R_SUCCESS) { From ad118d6eef649d2ed711080f65da276e75d31d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Fri, 15 Jun 2018 09:59:20 +0200 Subject: [PATCH 38/38] Add CHANGES entry 4973. [func] verifyzone() and the functions it uses were moved to libdns and refactored to prevent exit() from being called upon failure. A side effect of that is that dnssec-signzone and dnssec-verify now check for memory leaks upon shutdown. [GL #266] --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 3333080a61..4e66137b0a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +4973. [func] verifyzone() and the functions it uses were moved to + libdns and refactored to prevent exit() from being + called upon failure. A side effect of that is that + dnssec-signzone and dnssec-verify now check for memory + leaks upon shutdown. [GL #266] + 4972. [func] Declare the 'rdata' argument for dns_rdata_tostruct() to be const. [GL #341]