From 0667bf7ae71e4ae5203c85901fdfe3ec0e070038 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 3 May 2018 16:43:15 +1000 Subject: [PATCH 1/4] only sign with other keys when deleting a key if there are not already existing signature for the deleted algorithm --- lib/dns/zone.c | 79 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 227b9de56a..22ba83bad1 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -805,7 +805,8 @@ static void zone_maintenance(dns_zone_t *zone); static void zone_notify(dns_zone_t *zone, isc_time_t *now); static void dump_done(void *arg, isc_result_t result); static isc_result_t zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, - isc_uint16_t keyid, isc_boolean_t deleteit); + isc_uint16_t keyid, + isc_boolean_t deleteit); static isc_result_t delete_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node, dns_name_t *name, dns_diff_t *diff); @@ -8209,15 +8210,26 @@ zone_nsec3chain(dns_zone_t *zone) { INSIST(version == NULL); } +/*% + * Delete all RRSIG records with the given algorithm and keyid. + * Remove the NSEC record and RRSIGs if nkeys is zero. + * If all remaining RRsets are signed with the given algorithm + * set *has_algp to ISC_TRUE. + */ static isc_result_t del_sig(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_dbnode_t *node, unsigned int nkeys, dns_secalg_t algorithm, - isc_uint16_t keyid, dns_diff_t *diff) + isc_uint16_t keyid, isc_boolean_t *has_algp, dns_diff_t *diff) { dns_rdata_rrsig_t rrsig; dns_rdataset_t rdataset; dns_rdatasetiter_t *iterator = NULL; isc_result_t result; + isc_boolean_t alg_missed = ISC_FALSE; + isc_boolean_t alg_found = ISC_FALSE; + + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namebuf, sizeof(namebuf)); result = dns_db_allrdatasets(db, node, version, 0, &iterator); if (result != ISC_R_SUCCESS) { @@ -8230,6 +8242,7 @@ del_sig(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, for (result = dns_rdatasetiter_first(iterator); result == ISC_R_SUCCESS; result = dns_rdatasetiter_next(iterator)) { + isc_boolean_t has_alg = ISC_FALSE; dns_rdatasetiter_current(iterator, &rdataset); if (nkeys == 0 && rdataset.type == dns_rdatatype_nsec) { for (result = dns_rdataset_first(&rdataset); @@ -8252,13 +8265,20 @@ del_sig(dns_db_t *db, dns_dbversion_t *version, 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); CHECK(dns_rdata_tostruct(&rdata, &rrsig, NULL)); - if (rrsig.algorithm != algorithm || - rrsig.keyid != keyid) + if (nkeys != 0 && + (rrsig.algorithm != algorithm || + rrsig.keyid != keyid)) + { + if (rrsig.algorithm == algorithm) { + has_alg = ISC_TRUE; + } continue; + } CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_DELRESIGN, name, rdataset.ttl, &rdata)); @@ -8266,9 +8286,25 @@ del_sig(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_rdataset_disassociate(&rdataset); if (result != ISC_R_NOMORE) break; + + /* + * After deleting, if there's still a signature for + * 'algorithm', set alg_found; if not, set alg_missed. + */ + if (has_alg) { + alg_found = ISC_TRUE; + } else { + alg_missed = ISC_TRUE; + } } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; + + /* + * Set `has_algp` if the algorithm was found in every RRset: + * i.e., found in at least one, and not missing from any. + */ + *has_algp = ISC_TF(alg_found && !alg_missed); failure: if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); @@ -8298,6 +8334,7 @@ zone_sign(dns_zone_t *zone) { dst_key_t *zone_keys[DNS_MAXZONEKEYS]; isc_int32_t signatures; isc_boolean_t check_ksk, keyset_kskonly, is_ksk; + isc_boolean_t with_ksk, with_zsk; isc_boolean_t commit = ISC_FALSE; isc_boolean_t delegation; isc_boolean_t build_nsec = ISC_FALSE; @@ -8387,6 +8424,7 @@ zone_sign(dns_zone_t *zone) { build_nsec = ISC_TRUE; while (signing != NULL && nodes-- > 0 && signatures > 0) { + isc_boolean_t has_alg = ISC_FALSE; nextsigning = ISC_LIST_NEXT(signing, link); ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); @@ -8426,6 +8464,9 @@ zone_sign(dns_zone_t *zone) { zone_keys[j] = zone_keys[i]; j++; } + for (i = j; i < nkeys; i++) { + zone_keys[i] = NULL; + } nkeys = j; } @@ -8435,7 +8476,7 @@ zone_sign(dns_zone_t *zone) { dns_dbiterator_pause(signing->dbiterator); CHECK(del_sig(db, version, name, node, nkeys, signing->algorithm, signing->keyid, - zonediff.diff)); + &has_alg, zonediff.diff)); } /* @@ -8466,8 +8507,10 @@ zone_sign(dns_zone_t *zone) { /* * Process one node. */ + with_ksk = ISC_FALSE; + with_zsk = ISC_FALSE; dns_dbiterator_pause(signing->dbiterator); - for (i = 0; i < nkeys; i++) { + for (i = 0; !has_alg && i < nkeys; i++) { isc_boolean_t both = ISC_FALSE; /* @@ -8537,6 +8580,19 @@ zone_sign(dns_zone_t *zone) { else is_ksk = ISC_FALSE; + /* + * If deleting signatures, we need to ensure that + * the RRset is still signed at least once by a + * KSK and a ZSK. + */ + if (signing->deleteit && !is_ksk && with_zsk) { + continue; + } + + if (signing->deleteit && is_ksk && with_ksk) { + continue; + } + CHECK(sign_a_node(db, name, node, version, build_nsec3, build_nsec, zone_keys[i], inception, expire, zone->minimum, is_ksk, @@ -8547,8 +8603,15 @@ zone_sign(dns_zone_t *zone) { * If we are adding we are done. Look for other keys * of the same algorithm if deleting. */ - if (!signing->deleteit) + if (!signing->deleteit) { break; + } + if (!is_ksk) { + with_zsk = ISC_TRUE; + } + if (KSK(zone_keys[i])) { + with_ksk = ISC_TRUE; + } } /* From 87a3dc8ab930ce4b3f338905903ffa08e4113159 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 3 May 2018 16:43:15 +1000 Subject: [PATCH 2/4] add support -T sigvalinsecs --- bin/named/main.c | 5 ++++ bin/named/zoneconf.c | 41 ++++++++++++++++++--------------- lib/dns/zone.c | 47 ++++++++++++++++++++++++++++++++------ lib/ns/include/ns/server.h | 1 + 4 files changed, 69 insertions(+), 25 deletions(-) diff --git a/bin/named/main.c b/bin/named/main.c index 341a8c794d..6838d3c4c4 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -129,6 +129,7 @@ static unsigned int delay = 0; static isc_boolean_t nonearest = ISC_FALSE; static isc_boolean_t notcp = ISC_FALSE; static isc_boolean_t fixedlocal = ISC_FALSE; +static isc_boolean_t sigvalinsecs = ISC_FALSE; /* * -4 and -6 @@ -541,6 +542,8 @@ parse_T_opt(char *option) { if (dns_zone_mkey_month < dns_zone_mkey_day) { named_main_earlyfatal("bad mkeytimer"); } + } else if (!strcmp(option, "sigvalinsecs")) { + sigvalinsecs = ISC_TRUE; } else if (!strncmp(option, "tat=", 4)) { named_g_tat_interval = atoi(option + 4); } else { @@ -1111,6 +1114,8 @@ setup(void) { ns_server_setoption(sctx, NS_SERVER_DISABLE4, ISC_TRUE); if (disable6) ns_server_setoption(sctx, NS_SERVER_DISABLE6, ISC_TRUE); + if (sigvalinsecs) + ns_server_setoption(sctx, NS_SERVER_SIGVALINSECS, ISC_TRUE); named_g_server->sctx->delay = delay; } diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index a6a59c3231..81040e5e24 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1439,7 +1439,9 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, } if (ztype == dns_zone_master || raw != NULL) { + const cfg_obj_t *validity, *resign; isc_boolean_t allow = ISC_FALSE, maint = ISC_FALSE; + isc_boolean_t sigvalinsecs; obj = NULL; result = named_config_get(maps, "dnskey-sig-validity", &obj); @@ -1450,26 +1452,29 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, obj = NULL; result = named_config_get(maps, "sig-validity-interval", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - { - const cfg_obj_t *validity, *resign; - validity = cfg_tuple_get(obj, "validity"); - seconds = cfg_obj_asuint32(validity) * 86400; - dns_zone_setsigvalidityinterval(zone, seconds); - - resign = cfg_tuple_get(obj, "re-sign"); - if (cfg_obj_isvoid(resign)) { - seconds /= 4; - } else { - if (seconds > 7 * 86400) - seconds = cfg_obj_asuint32(resign) * - 86400; - else - seconds = cfg_obj_asuint32(resign) * - 3600; - } - dns_zone_setsigresigninginterval(zone, seconds); + sigvalinsecs = ns_server_getoption(named_g_server->sctx, + NS_SERVER_SIGVALINSECS); + validity = cfg_tuple_get(obj, "validity"); + seconds = cfg_obj_asuint32(validity); + if (!sigvalinsecs) { + seconds *= 86400; } + dns_zone_setsigvalidityinterval(zone, seconds); + + resign = cfg_tuple_get(obj, "re-sign"); + if (cfg_obj_isvoid(resign)) { + seconds /= 4; + } else if (!sigvalinsecs) { + if (seconds > 7 * 86400) { + seconds = cfg_obj_asuint32(resign) * 86400; + } else { + seconds = cfg_obj_asuint32(resign) * 3600; + } + } else { + seconds = cfg_obj_asuint32(resign); + } + dns_zone_setsigresigninginterval(zone, seconds); obj = NULL; result = named_config_get(maps, "key-directory", &obj); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 22ba83bad1..adb76a5971 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -6412,6 +6412,7 @@ zone_resigninc(dns_zone_t *zone) { isc_boolean_t check_ksk, keyset_kskonly = ISC_FALSE; isc_result_t result; isc_stdtime_t now, inception, soaexpire, expire, stop; + isc_uint32_t jitter, sigvalidityinterval; unsigned int i; unsigned int nkeys = 0; unsigned int resign; @@ -6456,14 +6457,24 @@ zone_resigninc(dns_zone_t *zone) { goto failure; } + sigvalidityinterval = zone->sigvalidityinterval; inception = now - 3600; /* Allow for clock skew. */ - soaexpire = now + dns_zone_getsigvalidityinterval(zone); + soaexpire = now + sigvalidityinterval; /* * Spread out signatures over time if they happen to be * clumped. We don't do this for each add_sigs() call as * we still want some clustering to occur. */ - expire = soaexpire - isc_random_uniform(3600) - 1; + if (sigvalidityinterval >= 3600U) { + if (sigvalidityinterval > 7200U) { + jitter = isc_random_uniform(3600); + } else { + jitter = isc_random_uniform(1200); + } + expire = soaexpire - jitter - 1; + } else { + expire = soaexpire - 1; + } stop = now + 5; check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK); @@ -7406,6 +7417,7 @@ zone_nsec3chain(dns_zone_t *zone) { isc_boolean_t first; isc_result_t result; isc_stdtime_t now, inception, soaexpire, expire; + isc_uint32_t jitter, sigvalidityinterval; unsigned int i; unsigned int nkeys = 0; isc_uint32_t nodes; @@ -7474,15 +7486,25 @@ zone_nsec3chain(dns_zone_t *zone) { goto failure; } + sigvalidityinterval = dns_zone_getsigvalidityinterval(zone); inception = now - 3600; /* Allow for clock skew. */ - soaexpire = now + dns_zone_getsigvalidityinterval(zone); + soaexpire = now + sigvalidityinterval; /* * Spread out signatures over time if they happen to be * clumped. We don't do this for each add_sigs() call as * we still want some clustering to occur. */ - expire = soaexpire - isc_random_uniform(3600); + if (sigvalidityinterval >= 3600U) { + if (sigvalidityinterval > 7200U) { + jitter = isc_random_uniform(3600); + } else { + jitter = isc_random_uniform(1200); + } + expire = soaexpire - jitter - 1; + } else { + expire = soaexpire - 1; + } check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK); keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY); @@ -8342,6 +8364,7 @@ zone_sign(dns_zone_t *zone) { isc_boolean_t first; isc_result_t result; isc_stdtime_t now, inception, soaexpire, expire; + isc_uint32_t jitter, sigvalidityinterval; unsigned int i, j; unsigned int nkeys = 0; isc_uint32_t nodes; @@ -8392,15 +8415,25 @@ zone_sign(dns_zone_t *zone) { goto failure; } + sigvalidityinterval = dns_zone_getsigvalidityinterval(zone); inception = now - 3600; /* Allow for clock skew. */ - soaexpire = now + dns_zone_getsigvalidityinterval(zone); + soaexpire = now + sigvalidityinterval; /* * Spread out signatures over time if they happen to be * clumped. We don't do this for each add_sigs() call as * we still want some clustering to occur. */ - expire = soaexpire - isc_random_uniform(3600); + if (sigvalidityinterval >= 3600U) { + if (sigvalidityinterval > 7200U) { + jitter = isc_random_uniform(3600); + } else { + jitter = isc_random_uniform(1200); + } + expire = soaexpire - jitter - 1; + } else { + expire = soaexpire - 1; + } /* * We keep pulling nodes off each iterator in turn until @@ -17633,7 +17666,7 @@ sign_apex(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, keyexpire = dns_zone_getkeyvalidityinterval(zone); if (keyexpire == 0) { - keyexpire = soaexpire; + keyexpire = soaexpire - 1; } else { keyexpire += now; } diff --git a/lib/ns/include/ns/server.h b/lib/ns/include/ns/server.h index 880de7dad2..bcfb69a92c 100644 --- a/lib/ns/include/ns/server.h +++ b/lib/ns/include/ns/server.h @@ -40,6 +40,7 @@ #define NS_SERVER_DISABLE4 0x00000100U /*%< -6 */ #define NS_SERVER_DISABLE6 0x00000200U /*%< -4 */ #define NS_SERVER_FIXEDLOCAL 0x00000400U /*%< -T fixedlocal */ +#define NS_SERVER_SIGVALINSECS 0x00000800U /*%< -T sigvalinsecs */ /*% * Type for callback function to get hostname. From 0db5b087ed2d5bedfe5ef09ad9ea0fa347eeb18b Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 3 May 2018 16:50:32 +1000 Subject: [PATCH 3/4] add duplicate signature test --- bin/tests/system/conf.sh.in | 4 +- bin/tests/system/conf.sh.win32 | 3 +- bin/tests/system/dupsigs/check_journal.pl | 209 ++++++++++++++++++ bin/tests/system/dupsigs/clean.sh | 19 ++ bin/tests/system/dupsigs/ns1/named.args | 1 + bin/tests/system/dupsigs/ns1/named.conf.in | 31 +++ bin/tests/system/dupsigs/ns1/reset_keys.sh | 97 ++++++++ .../system/dupsigs/ns1/signing.test.db.in | 16 ++ bin/tests/system/dupsigs/prereq.sh | 15 ++ bin/tests/system/dupsigs/setup.sh | 20 ++ bin/tests/system/dupsigs/tests.sh | 35 +++ util/copyrights | 9 + 12 files changed, 456 insertions(+), 3 deletions(-) create mode 100644 bin/tests/system/dupsigs/check_journal.pl create mode 100644 bin/tests/system/dupsigs/clean.sh create mode 100644 bin/tests/system/dupsigs/ns1/named.args create mode 100644 bin/tests/system/dupsigs/ns1/named.conf.in create mode 100644 bin/tests/system/dupsigs/ns1/reset_keys.sh create mode 100644 bin/tests/system/dupsigs/ns1/signing.test.db.in create mode 100644 bin/tests/system/dupsigs/prereq.sh create mode 100644 bin/tests/system/dupsigs/setup.sh create mode 100644 bin/tests/system/dupsigs/tests.sh diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 5518c7918f..e4d60eaa18 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -71,8 +71,8 @@ KRB5_CONFIG=/dev/null # The "stress" test is not run by default since it creates enough # load on the machine to make it unusable to other users. -# The "dialup" and "delzone" tests are also not run by default because -# they take a very long time to complete. +# The "dialup", "delzone", and "dupsigs" tests are also not run by +# default because they take a very long time to complete. # # List of tests hard-coded to use ports 5300 and 9953. For this # reason, these must be run sequentially. diff --git a/bin/tests/system/conf.sh.win32 b/bin/tests/system/conf.sh.win32 index 4da318fca8..6bfac1357c 100644 --- a/bin/tests/system/conf.sh.win32 +++ b/bin/tests/system/conf.sh.win32 @@ -77,7 +77,8 @@ KRB5_CONFIG=NUL # The "stress" test is not run by default since it creates enough # load on the machine to make it unusable to other users. -# v6synth +# The "dialup", "delzone", and "dupsigs" tests are also not run by +# default because they take a very long time to complete. # # List of tests that use ports 5300 and 9953. For this reason, these must # be run sequentially. diff --git a/bin/tests/system/dupsigs/check_journal.pl b/bin/tests/system/dupsigs/check_journal.pl new file mode 100644 index 0000000000..972ea8b008 --- /dev/null +++ b/bin/tests/system/dupsigs/check_journal.pl @@ -0,0 +1,209 @@ +#!/usr/bin/env perl +# +# 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. + +use strict; +use warnings; + +sub process_changeset; + +my @changeset; + +while( my $line = <> ) { + chomp $line; + + if( $line =~ /^(?add|del) (?