2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 06:25:31 +00:00

dnssec-signzone now updates ZONEMD records

If the zone already contains ZONEMD records, they will be updated
when signing the zone. If not, ZONEMD records can be added by using
the "-Z" option: for instance, "dnssec-signzone -Z - -S example.com".

Also, dnssec-verify now verifies the ZONEMD records, if any are
present. If run with the "-Z" flag, ZONEMD is mandatory, and the
zone will be rejected if it is not present.

Some zones in the mirror system test are now signed with ZONEMD.
This commit is contained in:
Mark Andrews
2025-06-10 16:17:35 +10:00
committed by Evan Hunt
parent 4b3f3bc38a
commit 22c0fcdc9f
13 changed files with 421 additions and 33 deletions

View File

@@ -173,6 +173,7 @@ static bool set_maxttl = false;
static dns_ttl_t maxttl = 0;
static bool no_max_check = false;
static const char *sync_records = "cdnskey,cds:sha-256";
static bool add_zonemd = false;
#define INCSTAT(counter) \
if (printstats) { \
@@ -1215,6 +1216,8 @@ signname(dns_dbnode_t *node, bool apex, dns_name_t *name) {
char namebuf[DNS_NAME_FORMATSIZE];
dns_name_format(name, namebuf, sizeof(namebuf));
fatal("'%s': Non-apex DNSKEY RRset\n", namebuf);
} else if (rdataset.type == dns_rdatatype_zonemd && apex) {
goto skip;
}
signset(&del, &add, node, name, &rdataset);
@@ -1472,10 +1475,42 @@ presign(void) {
/*%
* Clean up the iterator and global state after the tasks complete.
*
* Update ZONEMD if present.
*/
static void
postsign(void) {
isc_result_t result;
dns_diff_t diff;
dns_dbnode_t *node = NULL;
dns_rdataset_t rdataset = DNS_RDATASET_INIT;
dns_dbiterator_destroy(&gdbiter);
dns_diff_init(isc_g_mctx, &diff);
result = dns_update_zonemd(gdb, gversion, &diff);
check_result(result, "dns_update_zonemd");
dns_diff_clear(&diff);
result = dns_db_getoriginnode(gdb, &node);
check_result(result, "dns_db_findnode");
result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_zonemd,
0, (isc_stdtime_t)0, &rdataset, NULL);
if (result == ISC_R_SUCCESS) {
signset(&diff, &diff, node, gorigin, &rdataset);
}
result = dns_diff_applysilently(&diff, gdb, gversion);
check_result(result, "dns_diff_applysilently");
if (dns_rdataset_isassociated(&rdataset)) {
dns_rdataset_disassociate(&rdataset);
}
dns_db_detachnode(&node);
dns_diff_clear(&diff);
}
/*%
@@ -2100,6 +2135,88 @@ nsec3clean(dns_name_t *name, dns_dbnode_t *node, unsigned int hashalg,
}
}
static void
addzonemd(uint8_t scheme, uint8_t digest_type) {
dns_dbnode_t *node = NULL;
dns_rdata_zonemd_t zonemd;
dns_rdatalist_t zmlist;
dns_rdataset_t zmset = DNS_RDATASET_INIT;
dns_rdata_t rdata = DNS_RDATA_INIT;
unsigned char digest[64] = { 0 };
unsigned char zmbuf[6 + sizeof(digest)];
isc_buffer_t b;
isc_result_t result;
dns_ttl_t ttl = 0;
/* Is there already a ZONEMD? */
result = dns_db_getoriginnode(gdb, &node);
check_result(result, "dns_db_getoriginnode()");
result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_zonemd,
0, 0, &zmset, NULL);
if (result == ISC_R_SUCCESS) {
DNS_RDATASET_FOREACH(&zmset) {
dns_rdataset_current(&zmset, &rdata);
(void)dns_rdata_tostruct(&rdata, &zonemd, NULL);
/*
* Does the scheme/digest_type pair already exist?
*/
if (zonemd.scheme == scheme &&
zonemd.digest_type == digest_type)
{
dns_rdataset_disassociate(&zmset);
dns_db_detachnode(&node);
return;
}
dns_rdata_init(&rdata);
}
/*
* Remember the rdataset's ttl.
*/
ttl = zmset.ttl;
dns_rdataset_disassociate(&zmset);
}
/* No, so add a dummy */
zonemd.common.rdclass = gclass;
zonemd.common.rdtype = dns_rdatatype_zonemd;
zonemd.mctx = NULL;
zonemd.serial = 0;
zonemd.scheme = scheme;
zonemd.digest_type = digest_type;
zonemd.digest = digest;
switch (digest_type) {
case DNS_ZONEMD_DIGEST_SHA384:
zonemd.length = ISC_SHA384_DIGESTLENGTH;
break;
case DNS_ZONEMD_DIGEST_SHA512:
zonemd.length = ISC_SHA512_DIGESTLENGTH;
break;
default:
UNREACHABLE();
}
isc_buffer_init(&b, zmbuf, sizeof(zmbuf));
result = dns_rdata_fromstruct(&rdata, gclass, dns_rdatatype_zonemd,
&zonemd, &b);
check_result(result, "dns_rdata_fromstruct()");
dns_rdatalist_init(&zmlist);
zmlist.rdclass = rdata.rdclass;
zmlist.type = rdata.type;
zmlist.ttl = ttl;
ISC_LIST_APPEND(zmlist.rdata, &rdata, link);
dns_rdatalist_tordataset(&zmlist, &zmset);
result = dns_db_addrdataset(gdb, node, gversion, 0, &zmset,
DNS_DBADD_MERGE, NULL);
if (result == DNS_R_UNCHANGED) {
result = ISC_R_SUCCESS;
}
check_result(result, "addnsec3param: dns_db_addrdataset()");
dns_db_detachnode(&node);
}
static void
rrset_cleanup(dns_name_t *name, dns_rdataset_t *rdataset, dns_diff_t *add,
dns_diff_t *del) {
@@ -3272,6 +3389,7 @@ main(int argc, char *argv[]) {
bool set_optout = false;
bool set_iter = false;
bool nonsecify = false;
uint8_t zonemd_scheme[2] = { 0 }, zonemd_digest[2] = { 0 };
atomic_init(&shuttingdown, false);
atomic_init(&finished, false);
@@ -3282,8 +3400,8 @@ main(int argc, char *argv[]) {
* Unused letters: Bb G J l q Yy (and F is reserved).
* l was previously used for DLV lookaside.
*/
#define CMDLINE_FLAGS \
"3:AaCc:Dd:E:e:f:FgG:hH:i:I:j:J:K:k:L:m:M:n:N:o:O:PpQqRr:s:ST:tuUv:" \
#define CMDLINE_FLAGS \
"3:AaCc:Dd:E:e:f:FgG:hH:i:I:j:J:K:k:L:m:M:n:N:o:O:PpQqRr:s:ST:tuU:v:" \
"VX:xzZ:"
/*
@@ -3529,8 +3647,13 @@ main(int argc, char *argv[]) {
printstats = true;
break;
case 'U': /* Undocumented for testing only. */
unknownalg = true;
case 'U': /* Undocumented test options */
if (!strcmp(isc_commandline_argument, "nonsecify")) {
nonsecify = true;
}
if (!strcmp(isc_commandline_argument, "unknownalg")) {
unknownalg = true;
}
break;
case 'u':
@@ -3561,6 +3684,57 @@ main(int argc, char *argv[]) {
ignore_kskflag = true;
break;
case 'Z':
add_zonemd = true;
if (strcasecmp(isc_commandline_argument,
"simple-sha384") == 0 ||
strcasecmp(isc_commandline_argument, "sha384") ==
0 ||
strcmp(isc_commandline_argument, "-") == 0)
{
for (size_t i = 0;
i < ARRAY_SIZE(zonemd_scheme); i++)
{
if (zonemd_scheme[i] ==
DNS_ZONEMD_SCHEME_SIMPLE &&
zonemd_digest[i] ==
DNS_ZONEMD_DIGEST_SHA384)
{
break;
} else if (zonemd_scheme[i] == 0) {
zonemd_scheme[i] =
DNS_ZONEMD_SCHEME_SIMPLE;
zonemd_digest[i] =
DNS_ZONEMD_DIGEST_SHA384;
}
}
} else if (strcasecmp(isc_commandline_argument,
"simple-sha512") == 0 ||
strcasecmp(isc_commandline_argument,
"sha512") == 0)
{
for (size_t i = 0;
i < ARRAY_SIZE(zonemd_scheme); i++)
{
if (zonemd_scheme[i] ==
DNS_ZONEMD_SCHEME_SIMPLE &&
zonemd_digest[i] ==
DNS_ZONEMD_DIGEST_SHA512)
{
break;
} else if (zonemd_scheme[i] == 0) {
zonemd_scheme[i] =
DNS_ZONEMD_SCHEME_SIMPLE;
zonemd_digest[i] =
DNS_ZONEMD_DIGEST_SHA512;
}
}
} else {
fatal("unknown ZONEMD scheme/digest");
}
break;
case 'F':
if (isc_crypto_fips_enable() != ISC_R_SUCCESS) {
fatal("setting FIPS mode failed");
@@ -3582,12 +3756,6 @@ main(int argc, char *argv[]) {
/* Does not return. */
version(isc_commandline_progname);
case 'Z': /* Undocumented test options */
if (!strcmp(isc_commandline_argument, "nonsecify")) {
nonsecify = true;
}
break;
default:
fprintf(stderr, "%s: unhandled option -%c\n",
isc_commandline_progname,
@@ -3875,6 +4043,14 @@ main(int argc, char *argv[]) {
/* Remove duplicates and cap TTLs at maxttl */
cleanup_zone();
if (add_zonemd) {
for (size_t i = 0; i < ARRAY_SIZE(zonemd_scheme); i++) {
if (zonemd_scheme[i] != 0) {
addzonemd(zonemd_scheme[i], zonemd_digest[i]);
}
}
}
if (!nonsecify) {
if (IS_NSEC3) {
nsec3ify(dns_hash_sha1, nsec3iter, gsalt, salt_length,

View File

@@ -21,7 +21,7 @@ dnssec-signzone - DNSSEC zone signing tool
Synopsis
~~~~~~~~
:program:`dnssec-signzone` [**-a**] [**-c** class] [**-d** directory] [**-D**] [**-e** end-time] [**-f** output-file] [**-F**] [**-g**] [**-G sync-records**] [**-h**] [**-i** interval] [**-I** input-format] [**-j** jitter] [**-J** filename] [**-K** directory] [**-k** key] [**-L** serial] [**-M** maxttl] [**-N** soa-serial-format] [**-o** origin] [**-O** output-format] [**-P**] [**-Q**] [**-q**] [**-R**] [**-S**] [**-s** start-time] [**-T** ttl] [**-t**] [**-u**] [**-v** level] [**-V**] [**-X** extended end-time] [**-x**] [**-z**] [**-3** salt] [**-H** iterations] [**-A**] {zonefile} [key...]
:program:`dnssec-signzone` [**-a**] [**-c** class] [**-d** directory] [**-D**] [**-e** end-time] [**-f** output-file] [**-F**] [**-g**] [**-G sync-records**] [**-h**] [**-i** interval] [**-I** input-format] [**-j** jitter] [**-J** filename] [**-K** directory] [**-k** key] [**-L** serial] [**-M** maxttl] [**-N** soa-serial-format] [**-o** origin] [**-O** output-format] [**-P**] [**-Q**] [**-q**] [**-R**] [**-S**] [**-s** start-time] [**-T** ttl] [**-t**] [**-u**] [**-v** level] [**-V**] [**-X** extended end-time] [**-x**] [**-z**] [**-Z**] [**-3** salt] [**-H** iterations] [**-A**] {zonefile} [key...]
Description
~~~~~~~~~~~
@@ -401,6 +401,16 @@ Options
all records. This is useful when using the :option:`-u` option to modify an
NSEC3 chain which previously had OPTOUT set.
.. option:: -Z method
This option causes a ZONEMD record to be added to the signed zone,
if there wasn't a ZONEMD already present. The ``method`` parameter
indicates the scheme and digest type to use: valid options are
``simple-sha384`` (or ``sha384``, or ``-``) for scheme SIMPLE
and digest type SHA384, and ``simple-sha512`` (or ``sha512``)
for scheme SIMPLE and digest type SHA512. Multiple ``-Z`` options
can be used simultaneously to add multiple ZONEMD records.
.. option:: zonefile
This option sets the file containing the zone to be signed.

View File

@@ -57,6 +57,7 @@
#include <dns/rdatatype.h>
#include <dns/soa.h>
#include <dns/time.h>
#include <dns/zone.h>
#include <dns/zoneverify.h>
#include <dst/dst.h>
@@ -134,6 +135,37 @@ loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) {
}
}
static isc_result_t
check_zonemd(const char *file, const char *jfile, const dns_name_t *origin,
dns_rdataclass_t rdclass, bool required) {
dns_zone_t *zone = NULL;
dns_zoneopt_t options = DNS_ZONEOPT_ZONEMD_CHECK |
DNS_ZONEOPT_ZONEMD_DNSSEC;
isc_result_t result;
if (required) {
options |= DNS_ZONEOPT_ZONEMD_REQUIRED;
}
dns_zone_create(&zone, isc_g_mctx, 0);
dns_zone_settype(zone, dns_zone_primary);
dns_zone_setoption(zone, options, true);
dns_zone_setorigin(zone, origin);
dns_zone_setclass(zone, rdclass);
const char *dbtype[] = { ZONEDB_DEFAULT };
dns_zone_setdbtype(zone, 1, (const char *const *)dbtype);
dns_zone_setfile(zone, file, NULL, inputformat,
&dns_master_style_default);
dns_zone_setjournal(zone, jfile);
result = dns_zone_load(zone, false);
dns_zone_detach(&zone);
return result;
}
ISC_NORETURN static void
usage(void);
@@ -167,14 +199,15 @@ main(int argc, char *argv[]) {
char *origin = NULL, *file = NULL;
char *inputformatstr = NULL;
isc_result_t result;
char *classname = NULL;
dns_rdataclass_t rdclass;
char *endp;
dns_rdataclass_t rdclass = dns_rdataclass_in;
bool zonemd = false, require_zonemd = false;
dns_dbnode_t *node = NULL;
char *endp = NULL;
int ch;
isc_commandline_init(argc, argv);
#define CMDLINE_FLAGS "c:E:hJ:m:o:I:qv:Vxz"
#define CMDLINE_FLAGS "c:E:hJ:m:o:I:qv:VxzZ"
/*
* Process memory debugging argument first.
@@ -206,7 +239,7 @@ main(int argc, char *argv[]) {
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
switch (ch) {
case 'c':
classname = isc_commandline_argument;
rdclass = strtoclass(isc_commandline_argument);
break;
case 'E':
@@ -248,6 +281,10 @@ main(int argc, char *argv[]) {
ignore_kskflag = true;
break;
case 'Z':
require_zonemd = true;
break;
case '?':
if (isc_commandline_option != '?') {
fprintf(stderr, "%s: invalid argument -%c\n",
@@ -274,8 +311,6 @@ main(int argc, char *argv[]) {
now = isc_stdtime_now();
rdclass = strtoclass(classname);
setup_logging();
argc -= isc_commandline_index;
@@ -320,6 +355,17 @@ main(int argc, char *argv[]) {
result = dns_db_newversion(gdb, &gversion);
check_result(result, "dns_db_newversion()");
dns_rdataset_t zmset = DNS_RDATASET_INIT;
result = dns_db_getoriginnode(gdb, &node);
check_result(result, "dns_db_getoriginnode()");
result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_zonemd,
0, 0, &zmset, NULL);
if (result == ISC_R_SUCCESS) {
zonemd = true;
dns_rdataset_disassociate(&zmset);
}
dns_db_detachnode(&node);
result = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, NULL,
isc_g_mctx, ignore_kskflag,
keyset_kskonly, report);
@@ -327,6 +373,14 @@ main(int argc, char *argv[]) {
dns_db_closeversion(gdb, &gversion, false);
dns_db_detach(&gdb);
if (zonemd && result == ISC_R_SUCCESS) {
result = check_zonemd(file, journal, gorigin, gclass,
require_zonemd);
report("ZONEMD verification: %s", isc_result_totext(result));
} else if (!zonemd && require_zonemd) {
fatal("Valid ZONEMD required but not present");
}
if (verbose > 10) {
isc_mem_stats(isc_g_mctx, stdout);
}

View File

@@ -27,8 +27,9 @@ Description
~~~~~~~~~~~
:program:`dnssec-verify` verifies that a zone is fully signed for each
algorithm found in the DNSKEY RRset for the zone, and that the
NSEC/NSEC3 chains are complete.
algorithm found in the DNSKEY RRset for the zone, that the
NSEC/NSEC3 chains are complete, and, if a ZONEMD record is present,
that it is valid.
Options
~~~~~~~
@@ -94,6 +95,11 @@ Options
key; the same key may be used for both purposes. This corresponds to
the :option:`-z option in dnssec-signzone <dnssec-signzone -z>`.
.. option:: -Z
This option indicates that a valid ZONEMD record must be present
in the zone.
.. option:: zonefile
This option indicates the file containing the zone to be signed.

View File

@@ -0,0 +1,16 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* 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 https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
dnssec-policy "wrong" {
zonemd sha384;
};

View File

@@ -253,7 +253,7 @@ keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
cat "$infile" "$keyname.key" >"$zonefile"
"$SIGNER" -z -3 - -PU -o "$zone" "$zonefile" >/dev/null
"$SIGNER" -z -3 - -P -U unknownalg -o "$zone" "$zonefile" >/dev/null
#
# A optout nsec3 zone with a unknown nsec3 hash algorithm (-U).
@@ -266,7 +266,7 @@ keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
cat "$infile" "$keyname.key" >"$zonefile"
"$SIGNER" -z -3 - -PU -A -o "$zone" "$zonefile" >/dev/null
"$SIGNER" -z -3 - -P -U unknownalg -A -o "$zone" "$zonefile" >/dev/null
#
# A zone that is signed with an unknown DNSKEY algorithm.
@@ -375,7 +375,7 @@ keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
cat "$infile" "$keyname.key" >"$zonefile"
"$SIGNER" -z -3 - -o "$zone" -PU -O full -f ${zonefile}.tmp "$zonefile" >/dev/null
"$SIGNER" -z -3 - -o "$zone" -P -U unknownalg -O full -f ${zonefile}.tmp "$zonefile" >/dev/null
awk '$4 == "DNSKEY" { $7 = 100; print } $4 == "RRSIG" { $6 = 100; print } { print }' ${zonefile}.tmp >${zonefile}.signed

View File

@@ -0,0 +1,20 @@
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
;
; SPDX-License-Identifier: MPL-2.0
;
; 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 https://mozilla.org/MPL/2.0/.
;
; See the COPYRIGHT file distributed with this work for additional
; information regarding copyright ownership.
; This is a zone which has two DNSKEY records, both of which have
; existing private key files available. They should be loaded automatically
; and the zone correctly signed.
;
$TTL 3600
example.com. IN SOA ns hostmaster 00090000 1200 3600 604800 300
example.com. IN ZONEMD 0 1 1 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
$include Kexample.com.+010+18240.key
$include Kexample.com.+010+28633.key

View File

@@ -0,0 +1,21 @@
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
;
; SPDX-License-Identifier: MPL-2.0
;
; 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 https://mozilla.org/MPL/2.0/.
;
; See the COPYRIGHT file distributed with this work for additional
; information regarding copyright ownership.
; This is a zone which has two DNSKEY records, both of which have
; existing private key files available. They should be loaded automatically
; and the zone correctly signed.
;
$TTL 3600
example.com. IN SOA ns hostmaster 00090000 1200 3600 604800 300
example.com. IN ZONEMD 0 1 1 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
example.com. IN ZONEMD 0 1 2 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
$include Kexample.com.+010+18240.key
$include Kexample.com.+010+28633.key

View File

@@ -245,6 +245,40 @@ n=$((n + 1))
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
echo_ic "check that dnssec-signzone updates a ZONEMD record ($n)"
ret=0
(
cd signer/general || exit 1
rm -f signed.zone
$SIGNER -f signed.zone -o example.com. test13.zone >signer.out.$n
count=$(grep -c "ZONEMD.90000 1 1 " signed.zone)
test $count -eq 1 || exit 1
count=$(grep -c "RRSIG.ZONEMD" signed.zone)
test $count -eq 1 || exit 1
test -f signed.zone
) || ret=1
n=$((n + 1))
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
echo_ic "check that dnssec-signzone updates both ZONEMD records ($n)"
ret=0
(
cd signer/general || exit 1
rm -f signed.zone
$SIGNER -f signed.zone -o example.com. test14.zone >signer.out.$n
count=$(grep -c "ZONEMD.90000 1 1 " signed.zone)
test $count -eq 1 || exit 1
count=$(grep -c "ZONEMD.90000 1 2 " signed.zone)
test $count -eq 1 || exit 1
count=$(grep -c "RRSIG.ZONEMD" signed.zone)
test $count -eq 1 || exit 1
test -f signed.zone
) || ret=1
n=$((n + 1))
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
get_default_algorithm_key_ids_from_sigs() {
zone=$1

View File

@@ -59,8 +59,13 @@ for variant in addzone axfr ixfr load reconfig untrusted; do
cat $infile $keyname1.key $keyname2.key >$zonefile
# for axfr and ixfr variants, add a ZONEMD record
if [ "$variant" = "axfr" -o "$variant" = "ixfr" ]; then
zopt="-Z-"
fi
# Prepare a properly signed version of the zone ("*.original.signed").
$SIGNER -P -o $zone $zonefile >/dev/null
$SIGNER $zopt -P -o $zone $zonefile >/dev/null
cp $zonefile.signed $zonefile.original.signed
# Prepare a version of the zone with a bogus SOA RRSIG ("*.bad.signed").
sed "s/${ORIGINAL_SERIAL}/${UPDATED_SERIAL_BAD}/;" $zonefile.signed >$zonefile.bad.signed

View File

@@ -62,7 +62,7 @@ keyname=$($KEYGEN -q -a ${DEFAULT_ALGORITHM} $zone)
cat "$infile" "$keyname.key" >"$zonefile"
# do not regenerate NSEC chain as there in a minimal NSEC record present
$SIGNER -P -Z nonsecify -o $zone $zonefile >/dev/null
$SIGNER -P -U nonsecify -o $zone $zonefile >/dev/null
zone=soa-without-dnskey
infile=soa-without-dnskey.db.in
@@ -72,7 +72,7 @@ keyname=$($KEYGEN -q -a ${DEFAULT_ALGORITHM} $zone)
cat "$infile" "$keyname.key" >"$zonefile"
# do not regenerate NSEC chain as there in a minimal NSEC record present
$SIGNER -P -Z nonsecify -o $zone $zonefile >/dev/null
$SIGNER -P -U nonsecify -o $zone $zonefile >/dev/null
zone=.
infile=root.db.in

View File

@@ -184,3 +184,37 @@ def test_verify_j_reads_journal_file():
]
).stdout.decode("utf-8")
assert "Loading zone 'updated' from file 'zones/updated.other'" in output
@pytest.mark.parametrize(
"zone",
[
"ksk+zsk.zonemd.nsec",
"ksk+zsk.zonemd.nsec3",
],
)
def test_verify_zonemd(zone):
output = isctest.run.cmd(
[VERIFY, "-o", zone, f"zones/{zone}.good"],
raise_on_exception=False,
log_stdout=True,
)
stream = (output.stdout + output.stderr).decode("utf-8").replace("\n", "")
assert "ZONEMD verification: success" in stream
@pytest.mark.parametrize(
"zone",
[
"ksk+zsk.nsec",
"ksk+zsk.nsec3",
],
)
def test_verify_no_zonemd(zone):
output = isctest.run.cmd(
[VERIFY, "-Zo", zone, f"zones/{zone}.good"],
raise_on_exception=False,
log_stdout=True,
)
stream = (output.stdout + output.stderr).decode("utf-8").replace("\n", "")
assert "Valid ZONEMD required but not present" in stream

View File

@@ -169,7 +169,7 @@ ksk=$($KEYGEN -a ${DEFAULT_ALGORITHM} -fK ${zone} 2>kg2.out$n) || dumpit kg2.out
cat unsigned.db $ksk.key $zsk.key >$file
$SIGNER -P -O full -o ${zone} -f ${file} ${file} $ksk >s.out$n || dumpit s.out$n
awk '$4 == "NSEC" { $5 = "'$zone'."; print } { print }' ${file} >${file}.tmp
$SIGNER -Px -Z nonsecify -o ${zone} -f ${file} ${file}.tmp $zsk >s.out$n || dumpit s.out$n
$SIGNER -Px -U nonsecify -o ${zone} -f ${file} ${file}.tmp $zsk >s.out$n || dumpit s.out$n
# bad nsec bitmap
setup ksk+zsk.nsec.bad-bitmap bad
@@ -178,7 +178,7 @@ ksk=$($KEYGEN -a ${DEFAULT_ALGORITHM} -fK ${zone} 2>kg2.out$n) || dumpit kg2.out
cat unsigned.db $ksk.key $zsk.key >$file
$SIGNER -P -O full -o ${zone} -f ${file} ${file} $ksk >s.out$n || dumpit s.out$n
awk '$4 == "NSEC" && /SOA/ { $6=""; print } { print }' ${file} >${file}.tmp
$SIGNER -Px -Z nonsecify -o ${zone} -f ${file} ${file}.tmp $zsk >s.out$n || dumpit s.out$n
$SIGNER -Px -U nonsecify -o ${zone} -f ${file} ${file}.tmp $zsk >s.out$n || dumpit s.out$n
# extra NSEC record out side of zone
setup ksk+zsk.nsec.out-of-zone-nsec bad
@@ -187,7 +187,7 @@ ksk=$($KEYGEN -a ${DEFAULT_ALGORITHM} -fK ${zone} 2>kg2.out$n) || dumpit kg2.out
cat unsigned.db $ksk.key $zsk.key >$file
$SIGNER -P -O full -o ${zone} -f ${file} ${file} $ksk >s.out$n || dumpit s.out$n
echo "out-of-zone. 3600 IN NSEC ${zone}. A" >>${file}
$SIGNER -Px -Z nonsecify -O full -o ${zone} -f ${file} ${file} $zsk >s.out$n || dumpit s.out$n
$SIGNER -Px -U nonsecify -O full -o ${zone} -f ${file} ${file} $zsk >s.out$n || dumpit s.out$n
# extra NSEC record below bottom of zone
setup ksk+zsk.nsec.below-bottom-of-zone-nsec bad
@@ -196,7 +196,7 @@ ksk=$($KEYGEN -a ${DEFAULT_ALGORITHM} -fK ${zone} 2>kg2.out$n) || dumpit kg2.out
cat unsigned.db $ksk.key $zsk.key >$file
$SIGNER -P -O full -o ${zone} -f ${file} ${file} $ksk >s.out$n || dumpit s.out$n
echo "ns.sub.${zone}. 3600 IN NSEC ${zone}. A AAAA" >>${file}
$SIGNER -Px -Z nonsecify -O full -o ${zone} -f ${file}.tmp ${file} $zsk >s.out$n || dumpit s.out$n
$SIGNER -Px -U nonsecify -O full -o ${zone} -f ${file}.tmp ${file} $zsk >s.out$n || dumpit s.out$n
# dnssec-signzone signs any node with a NSEC record.
awk '$1 ~ /^ns.sub/ && $4 == "RRSIG" && $5 != "NSEC" { next; } { print; }' ${file}.tmp >${file}
@@ -207,7 +207,7 @@ ksk=$($KEYGEN -a ${DEFAULT_ALGORITHM} -fK ${zone} 2>kg2.out$n) || dumpit kg2.out
cat unsigned.db $ksk.key $zsk.key >$file
$SIGNER -P -O full -o ${zone} -f ${file} ${file} $ksk >s.out$n || dumpit s.out$n
echo "sub.dname.${zone}. 3600 IN NSEC ${zone}. TXT" >>${file}
$SIGNER -Px -Z nonsecify -O full -o ${zone} -f ${file} ${file} $zsk >s.out$n || dumpit s.out$n
$SIGNER -Px -U nonsecify -O full -o ${zone} -f ${file} ${file} $zsk >s.out$n || dumpit s.out$n
# missing NSEC3 record at empty node
# extract the hash fields from the empty node's NSEC 3 record then fix up
@@ -223,7 +223,7 @@ awk '
$4 == "NSEC3" && $9 == "'$a'" { $9 = "'$b'"; print; next; }
$4 == "NSEC3" && NF == 9 { next; }
{ print; }' ${file} >${file}.tmp
$SIGNER -3 - -Px -Z nonsecify -O full -o ${zone} -f ${file} ${file}.tmp $zsk >s.out$n || dumpit s.out$n
$SIGNER -3 - -Px -U nonsecify -O full -o ${zone} -f ${file} ${file}.tmp $zsk >s.out$n || dumpit s.out$n
# extra NSEC3 record
setup ksk+zsk.nsec3.extra-nsec3 bad
@@ -242,7 +242,7 @@ $4 == "NSEC3" && NF == 9 {
}' ${file} >${file}.tmp
cat ${file}.tmp >>${file}
rm -f ${file}.tmp
$SIGNER -3 - -Px -Z nonsecify -O full -o ${zone} -f ${file} ${file} $zsk >s.out$n || dumpit s.out$n
$SIGNER -3 - -Px -U nonsecify -O full -o ${zone} -f ${file} ${file} $zsk >s.out$n || dumpit s.out$n
# sign and verify with journal file
setup updated other
@@ -254,3 +254,15 @@ sed -e '/serial/s/0/1/' $file >${file}.update
echo "extra 3600 IN A 4.3.2.1" >>${file}.update
$SIGNER -SPx -o ${zone} -f ${file}.update ${file}.update >s.out$n || dumpit s.out$n
$MAKEJOURNAL updated ${file} ${file}.update ${file}.jnl >mj.out$n 2>&1 || dumpit mj.out$n
# NSEC zone with ZONEMD.
setup ksk+zsk.zonemd.nsec good
$KEYGEN -a ${DEFAULT_ALGORITHM} ${zone} >kg1.out$n 2>&1 || dumpit kg1.out$n
$KEYGEN -a ${DEFAULT_ALGORITHM} -fK ${zone} >kg2.out$n 2>&1 || dumpit kg2.out$n
$SIGNER -Z simple-sha384 -SPx -o ${zone} -f ${file} unsigned.db >s.out$n || dumpit s.out$n
# NSEC3 zone with ZONEMD.
setup ksk+zsk.zonemd.nsec3 good
$KEYGEN -a ${DEFAULT_ALGORITHM} ${zone} >kg1.out$n 2>&1 || dumpit kg1.out$n
$KEYGEN -a ${DEFAULT_ALGORITHM} -fK ${zone} >kg2.out$n 2>&1 || dumpit kg2.out$n
$SIGNER -3 - -Z simple-sha512 -SPx -o ${zone} -f ${file} unsigned.db >s.out$n || dumpit s.out$n