From 01169b7ffc442499244c88526ff95f54f2579106 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 3 Sep 2024 16:02:24 +0200 Subject: [PATCH 1/5] Add -f option to dnssec-ksr documentation This was previously left out by error. --- bin/dnssec/dnssec-ksr.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/dnssec/dnssec-ksr.rst b/bin/dnssec/dnssec-ksr.rst index 8a0f507770..e4ae2ce887 100644 --- a/bin/dnssec/dnssec-ksr.rst +++ b/bin/dnssec/dnssec-ksr.rst @@ -21,7 +21,7 @@ dnssec-ksr - Create signed key response (SKR) files for offline KSK setups Synopsis ~~~~~~~~ -:program:`dnssec-ksr` [**-e** date/offset] [**-F**] [**-h**] [**-i** date/offset] [**-K** directory] [**-k** policy] [**-l** file] [**-V**] [**-v** level] {command} {zone} +:program:`dnssec-ksr` [**-e** date/offset] [**-F**] [**-f** file] [**-h**] [**-i** date/offset] [**-K** directory] [**-k** policy] [**-l** file] [**-V**] [**-v** level] {command} {zone} Description ~~~~~~~~~~~ @@ -51,6 +51,10 @@ Options mode if the underlying crytographic library supports running in FIPS mode. +.. option:: -f + + This option sets the SKR file to be signed when issuing a ``sign`` command. + .. option:: -h This option prints a short summary of the options and arguments to From 680aedb595917868118bf312834fc388787c5b4e Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 3 Sep 2024 17:24:22 +0200 Subject: [PATCH 2/5] dnssec-ksr keygen -o to create KSKs Add an option to dnssec-ksr keygen, -o, to create KSKs instead of ZSKs. This way, we can create a set of KSKS for a given period too. For KSKs we also need to set timing metadata, including "SyncPublish" and "SyncDelete". This functionality already exists in keymgr.c so let's make the function accessible. Replace dnssec-keygen calls with dnssec-ksr keygen for KSK in the ksr system test and check keys for created KSKs as well. This requires a slight modification of the check_keys function to take into account KSK timings and metadata. --- bin/dnssec/dnssec-ksr.c | 74 ++++++++++++----- bin/dnssec/dnssec-ksr.rst | 20 +++-- bin/tests/system/ksr/tests_ksr.py | 129 +++++++++++++++++++----------- lib/dns/include/dns/keymgr.h | 11 +++ lib/dns/keymgr.c | 17 ++-- 5 files changed, 168 insertions(+), 83 deletions(-) diff --git a/bin/dnssec/dnssec-ksr.c b/bin/dnssec/dnssec-ksr.c index 9919fd4210..633e2f7e50 100644 --- a/bin/dnssec/dnssec-ksr.c +++ b/bin/dnssec/dnssec-ksr.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -61,16 +62,19 @@ struct ksr_ctx { bool setstart; bool setend; /* keygen */ + bool ksk; dns_ttl_t ttl; dns_secalg_t alg; int size; time_t lifetime; + time_t parentpropagation; time_t propagation; time_t publishsafety; time_t retiresafety; time_t sigrefresh; time_t sigvalidity; time_t signdelay; + time_t ttlds; time_t ttlsig; }; typedef struct ksr_ctx ksr_ctx_t; @@ -239,6 +243,7 @@ get_dnskeys(ksr_ctx_t *ksr, dns_dnsseckeylist_t *keys) { static void setcontext(ksr_ctx_t *ksr, dns_kasp_t *kasp) { + ksr->parentpropagation = dns_kasp_parentpropagationdelay(kasp); ksr->propagation = dns_kasp_zonepropagationdelay(kasp); ksr->publishsafety = dns_kasp_publishsafety(kasp); ksr->retiresafety = dns_kasp_retiresafety(kasp); @@ -246,6 +251,7 @@ setcontext(ksr_ctx_t *ksr, dns_kasp_t *kasp) { ksr->sigrefresh = dns_kasp_sigrefresh(kasp); ksr->signdelay = dns_kasp_signdelay(kasp); ksr->ttl = dns_kasp_dnskeyttl(kasp); + ksr->ttlds = dns_kasp_dsttl(kasp); ksr->ttlsig = dns_kasp_zonemaxttl(kasp, true); } @@ -313,12 +319,13 @@ freerrset(dns_rdataset_t *rdataset) { } static void -create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys, - isc_stdtime_t inception, isc_stdtime_t active, - isc_stdtime_t *expiration) { +create_key(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_kasp_key_t *kaspkey, + dns_dnsseckeylist_t *keys, isc_stdtime_t inception, + isc_stdtime_t active, isc_stdtime_t *expiration) { bool conflict = false; bool freekey = false; bool show_progress = true; + bool first = true; char algstr[DNS_SECALG_FORMATSIZE]; char filename[PATH_MAX + 1]; char timestr[26]; /* Minimal buf as per ctime_r() spec. */ @@ -327,9 +334,15 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys, isc_buffer_t buf; isc_result_t ret; isc_stdtime_t prepub; + uint16_t flags = DNS_KEYOWNER_ZONE; isc_stdtime_tostring(inception, timestr, sizeof(timestr)); + /* ZSK or KSK? */ + if (ksr->ksk) { + flags |= DNS_KEYFLAG_KSK; + } + /* Check algorithm and size. */ dns_secalg_format(ksr->alg, algstr, sizeof(algstr)); if (!dst_algorithm_supported(ksr->alg)) { @@ -403,6 +416,7 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys, "Selecting key pair for bundle %s: ", timestr); fflush(stderr); } + first = false; key = dk->key; *expiration = inact; goto output; @@ -420,18 +434,18 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys, ret = dns_keystore_keygen( ksr->keystore, name, ksr->policy, dns_rdataclass_in, mctx, ksr->alg, ksr->size, - DNS_KEYOWNER_ZONE, &key); + flags, &key); } else if (show_progress) { - ret = dst_key_generate( - name, ksr->alg, ksr->size, 0, DNS_KEYOWNER_ZONE, - DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, NULL, - mctx, &key, &progress); + ret = dst_key_generate(name, ksr->alg, ksr->size, 0, + flags, DNS_KEYPROTO_DNSSEC, + dns_rdataclass_in, NULL, mctx, + &key, &progress); fflush(stderr); } else { - ret = dst_key_generate( - name, ksr->alg, ksr->size, 0, DNS_KEYOWNER_ZONE, - DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, NULL, - mctx, &key, NULL); + ret = dst_key_generate(name, ksr->alg, ksr->size, 0, + flags, DNS_KEYPROTO_DNSSEC, + dns_rdataclass_in, NULL, mctx, + &key, NULL); } if (ret != ISC_R_SUCCESS) { @@ -468,15 +482,27 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys, prepub = ksr->ttl + ksr->publishsafety + ksr->propagation; dst_key_setttl(key, ksr->ttl); dst_key_setnum(key, DST_NUM_LIFETIME, ksr->lifetime); - dst_key_setbool(key, DST_BOOL_KSK, false); - dst_key_setbool(key, DST_BOOL_ZSK, true); + dst_key_setbool(key, DST_BOOL_KSK, ksr->ksk); + dst_key_setbool(key, DST_BOOL_ZSK, !ksr->ksk); dst_key_settime(key, DST_TIME_CREATED, ksr->now); dst_key_settime(key, DST_TIME_PUBLISH, (active - prepub)); dst_key_settime(key, DST_TIME_ACTIVATE, active); + if (ksr->ksk) { + dns_keymgr_settime_syncpublish(key, kasp, first); + } + if (ksr->lifetime > 0) { isc_stdtime_t inactive = (active + ksr->lifetime); - isc_stdtime_t remove = ksr->ttlsig + ksr->propagation + - ksr->retiresafety + ksr->signdelay; + isc_stdtime_t remove; + + if (ksr->ksk) { + remove = ksr->ttlds + ksr->parentpropagation + + ksr->retiresafety; + dst_key_settime(key, DST_TIME_SYNCDELETE, inactive); + } else { + remove = ksr->ttlsig + ksr->propagation + + ksr->retiresafety + ksr->signdelay; + } dst_key_settime(key, DST_TIME_INACTIVE, inactive); dst_key_settime(key, DST_TIME_DELETE, (inactive + remove)); *expiration = inactive; @@ -492,6 +518,8 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys, isc_result_totext(ret)); } + first = false; + output: isc_buffer_clear(&buf); ret = dst_key_buildfilename(key, 0, NULL, &buf); @@ -907,9 +935,12 @@ keygen(ksr_ctx_t *ksr) { for (dns_kasp_key_t *kk = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kk != NULL; kk = ISC_LIST_NEXT(kk, link)) { - if (dns_kasp_key_ksk(kk)) { + if (dns_kasp_key_ksk(kk) && !ksr->ksk) { /* only ZSKs allowed */ continue; + } else if (dns_kasp_key_zsk(kk) && ksr->ksk) { + /* only KSKs allowed */ + continue; } ksr->alg = dns_kasp_key_algorithm(kk); ksr->lifetime = dns_kasp_key_lifetime(kk); @@ -920,7 +951,7 @@ keygen(ksr_ctx_t *ksr) { for (isc_stdtime_t inception = ksr->start, act = ksr->start; inception < ksr->end; inception += ksr->lifetime) { - create_zsk(ksr, kk, &keys, inception, act, &act); + create_key(ksr, kasp, kk, &keys, inception, act, &act); if (ksr->lifetime == 0) { /* unlimited lifetime, but not infinite loop */ break; @@ -928,7 +959,7 @@ keygen(ksr_ctx_t *ksr) { } } if (noop) { - fatal("policy '%s' has no zsks", ksr->policy); + fatal("no keys created for policy '%s'", ksr->policy); } /* Cleanup */ cleanup(&keys, kasp); @@ -1212,7 +1243,7 @@ main(int argc, char *argv[]) { isc_commandline_errprint = false; -#define OPTIONS "E:e:Ff:hi:K:k:l:v:V" +#define OPTIONS "E:e:Ff:hi:K:k:l:ov:V" while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) { switch (ch) { case 'E': @@ -1249,6 +1280,9 @@ main(int argc, char *argv[]) { case 'l': ksr.configfile = isc_commandline_argument; break; + case 'o': + ksr.ksk = true; + break; case 'V': version(program); break; diff --git a/bin/dnssec/dnssec-ksr.rst b/bin/dnssec/dnssec-ksr.rst index e4ae2ce887..b8d6c43d95 100644 --- a/bin/dnssec/dnssec-ksr.rst +++ b/bin/dnssec/dnssec-ksr.rst @@ -21,7 +21,7 @@ dnssec-ksr - Create signed key response (SKR) files for offline KSK setups Synopsis ~~~~~~~~ -:program:`dnssec-ksr` [**-e** date/offset] [**-F**] [**-f** file] [**-h**] [**-i** date/offset] [**-K** directory] [**-k** policy] [**-l** file] [**-V**] [**-v** level] {command} {zone} +:program:`dnssec-ksr` [**-e** date/offset] [**-F**] [**-f** file] [**-h**] [**-i** date/offset] [**-K** directory] [**-k** policy] [**-l** file] [**-o**] [**-V**] [**-v** level] {command} {zone} Description ~~~~~~~~~~~ @@ -80,6 +80,11 @@ Options This option provides a configuration file that contains a ``dnssec-policy`` statement (matching the policy set with :option:`-k`). +.. option:: -o + + Normally when pregenerating keys, ZSKs are created. When this option is + set, create KSKs instead. + .. option:: -V This option prints version information. @@ -102,9 +107,8 @@ Commands .. option:: keygen - Pregenerate a number of zone signing keys (ZSKs), given a DNSSEC policy and - an interval. The number of generated keys depends on the interval and the - ZSK lifetime. + Pregenerate a number of keys, given a DNSSEC policy and an interval. The + number of generated keys depends on the interval and the key lifetime. .. option:: request @@ -127,7 +131,7 @@ occurred. Examples ~~~~~~~~ -When you need to generate keys for the zone "example.com" for the next year, +When you need to generate ZSKs for the zone "example.com" for the next year, given a ``dnssec-policy`` named "mypolicy": :: @@ -140,7 +144,8 @@ Creating a KSR for the same zone and period can be done with: dnssec-ksr -i now -e +1y -k mypolicy -l named.conf request example.com > ksr.txt -Typically you would now transfer the KSR to the system that has access to the KSK. +Typically you would now transfer the KSR to the system that has access to +the KSK. Signing the KSR created above can be done with: @@ -148,7 +153,8 @@ Signing the KSR created above can be done with: dnssec-ksr -i now -e +1y -k kskpolicy -l named.conf -f ksr.txt sign example.com -Make sure that the DNSSEC parameters in ``kskpolicy`` match those in ``mypolicy``. +Make sure that the DNSSEC parameters in ``kskpolicy`` match those +in ``mypolicy``. See Also ~~~~~~~~ diff --git a/bin/tests/system/ksr/tests_ksr.py b/bin/tests/system/ksr/tests_ksr.py index 793df81c4c..3790003f33 100644 --- a/bin/tests/system/ksr/tests_ksr.py +++ b/bin/tests/system/ksr/tests_ksr.py @@ -55,28 +55,6 @@ def keystr_to_keylist(keystr: str, keydir: Optional[str] = None) -> List[Key]: return [Key(name, keydir) for name in keystr.split()] -def keygen(zone, policy, keydir, when="now"): - keygen_command = [ - os.environ.get("KEYGEN"), - "-l", - "ns1/named.conf", - "-fK", - "-K", - keydir, - "-k", - policy, - "-P", - when, - "-A", - when, - "-P", - "sync", - when, - zone, - ] - return isctest.run.cmd(keygen_command, log_stdout=True).stdout.decode("utf-8") - - def ksr(zone, policy, action, options="", raise_on_exception=True): ksr_command = [ os.environ.get("KSR"), @@ -123,12 +101,22 @@ def check_keys( # retired: zsk-lifetime if lifetime is not None: retired = active + lifetime - # removed: ttlsig + retire-safety + sign-delay + propagation - removed = retired + timedelta(days=10, hours=1, minutes=5) + + if key.is_ksk(): + # removed: ttlds + retire-safety + parent-propagation + removed = retired + timedelta(days=1, hours=2) + else: + # removed: ttlsig + retire-safety + sign-delay + propagation + removed = retired + timedelta(days=10, hours=1, minutes=5) else: retired = None removed = None + goal = "hidden" + state_dnskey = "hidden" + state_zrrsig = "hidden" + state_krrsig = "hidden" + state_ds = "hidden" if retired is None or between(now, published, retired): goal = "omnipresent" pubdelay = published + timedelta(hours=2, minutes=5) @@ -136,24 +124,34 @@ def check_keys( if between(now, published, pubdelay): state_dnskey = "rumoured" + state_krrsig = "rumoured" else: state_dnskey = "omnipresent" + state_krrsig = "omnipresent" - if between(now, active, signdelay): - state_zrrsig = "rumoured" + if key.is_ksk(): + state_ds = "hidden" else: - state_zrrsig = "omnipresent" - else: - goal = "hidden" - state_dnskey = "hidden" - state_zrrsig = "hidden" + if between(now, active, signdelay): + state_zrrsig = "rumoured" + else: + state_zrrsig = "omnipresent" with open(key.statefile, "r", encoding="utf-8") as file: metadata = file.read() assert f"Algorithm: {alg}" in metadata assert f"Length: {size}" in metadata - assert "KSK: no" in metadata - assert "ZSK: yes" in metadata + + if key.is_ksk(): + assert "KSK: yes" in metadata + else: + assert "KSK: no" in metadata + + if key.is_zsk(): + assert "ZSK: yes" in metadata + else: + assert "ZSK: no" in metadata + assert f"Published: {published}" in metadata assert f"Active: {active}" in metadata @@ -169,9 +167,18 @@ def check_keys( if with_state: assert f"GoalState: {goal}" in metadata assert f"DNSKEYState: {state_dnskey}" in metadata - assert f"ZRRSIGState: {state_zrrsig}" in metadata - assert "KRRSIGState:" not in metadata - assert "DSState:" not in metadata + + if key.is_ksk(): + assert f"KRRSIGState: {state_krrsig}" in metadata + assert f"DSState: {state_ds}" in metadata + else: + assert "KRRSIGState:" not in metadata + assert "DSState:" not in metadata + + if key.is_zsk(): + assert f"ZRRSIGState: {state_zrrsig}" in metadata + else: + assert "ZRRSIGState:" not in metadata num += 1 @@ -264,6 +271,9 @@ def check_signedkeyresponse( # expect ksks for key in sorted(ksks): published = key.get_timing("Publish") + if between(published, inception, next_bundle): + next_bundle = published + removed = key.get_timing("Delete", must_exist=False) if published > inception: @@ -271,6 +281,9 @@ def check_signedkeyresponse( if removed is not None and inception >= removed: continue + if between(removed, inception, next_bundle): + next_bundle = removed + # this ksk must be in the ksr assert key.dnskey_equals(lines[line_no]) line_no += 1 @@ -399,7 +412,7 @@ def test_ksr_errors(): _, err = ksr( "csk.test", "csk", "keygen", options="-K ns1 -e +2y", raise_on_exception=False ) - assert "dnssec-ksr: fatal: policy 'csk' has no zsks" in err + assert "dnssec-ksr: fatal: no keys created for policy 'csk'" in err # check that 'dnssec-ksr request' errors on missing end date _, err = ksr("common.test", "common", "request", raise_on_exception=False) @@ -424,10 +437,12 @@ def test_ksr_common(servers): # create ksk kskdir = "ns1/offline" - out = keygen(zone, policy, kskdir) + out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i now -e +1y -o") ksks = keystr_to_keylist(out, kskdir) assert len(ksks) == 1 + check_keys(ksks, None) + # check that 'dnssec-ksr keygen' pregenerates right amount of keys out, _ = ksr(zone, policy, "keygen", options="-i now -e +1y") zsks = keystr_to_keylist(out) @@ -611,13 +626,13 @@ def test_ksr_lastbundle(servers): # create ksk kskdir = "ns1/offline" - now = KeyTimingMetadata.now() offset = -timedelta(days=365) - when = now + offset - timedelta(days=1) - out = keygen(zone, policy, kskdir, when=str(when)) + out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i -1y -e +1d -o") ksks = keystr_to_keylist(out, kskdir) assert len(ksks) == 1 + check_keys(ksks, None, offset=offset) + # check that 'dnssec-ksr keygen' pregenerates right amount of keys zskdir = "ns1" out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i -1y -e +1d") @@ -690,13 +705,13 @@ def test_ksr_inthemiddle(servers): # create ksk kskdir = "ns1/offline" - now = KeyTimingMetadata.now() offset = -timedelta(days=365) - when = now + offset - timedelta(days=1) - out = keygen(zone, policy, kskdir, when=str(when)) + out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i -1y -e +1y -o") ksks = keystr_to_keylist(out, kskdir) assert len(ksks) == 1 + check_keys(ksks, None, offset=offset) + # check that 'dnssec-ksr keygen' pregenerates right amount of keys zskdir = "ns1" out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i -1y -e +1y") @@ -771,7 +786,7 @@ def check_ksr_rekey_logs_error(server, zone, policy, offset, end): now = KeyTimingMetadata.now() then = now + offset until = now + end - out = keygen(zone, policy, kskdir, when=str(then)) + out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i {then} -e {until} -o") ksks = keystr_to_keylist(out, kskdir) assert len(ksks) == 1 @@ -844,10 +859,12 @@ def test_ksr_unlimited(servers): # create ksk kskdir = "ns1/offline" - out = keygen(zone, policy, kskdir) + out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i now -e +2y -o") ksks = keystr_to_keylist(out, kskdir) assert len(ksks) == 1 + check_keys(ksks, None) + # check that 'dnssec-ksr keygen' pregenerates right amount of keys zskdir = "ns1" out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i now -e +2y") @@ -959,10 +976,28 @@ def test_ksr_twotone(servers): # create ksk kskdir = "ns1/offline" - out = keygen(zone, policy, kskdir) + out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i now -e +1y -o") ksks = keystr_to_keylist(out, kskdir) assert len(ksks) == 2 + ksks_defalg = [] + ksks_altalg = [] + for ksk in ksks: + alg = ksk.get_metadata("Algorithm") + if alg == os.environ.get("DEFAULT_ALGORITHM_NUMBER"): + ksks_defalg.append(ksk) + elif alg == os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER"): + ksks_altalg.append(ksk) + + assert len(ksks_defalg) == 1 + assert len(ksks_altalg) == 1 + + check_keys(ksks_defalg, None) + + alg = os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER") + size = os.environ.get("ALTERNATIVE_BITS") + check_keys(ksks_altalg, None, alg, size) + # check that 'dnssec-ksr keygen' pregenerates right amount of keys zskdir = "ns1" out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i now -e +1y") diff --git a/lib/dns/include/dns/keymgr.h b/lib/dns/include/dns/keymgr.h index 61cf3df7aa..deb007623f 100644 --- a/lib/dns/include/dns/keymgr.h +++ b/lib/dns/include/dns/keymgr.h @@ -24,6 +24,17 @@ ISC_LANG_BEGINDECLS +void +dns_keymgr_settime_syncpublish(dst_key_t *key, dns_kasp_t *kasp, bool first); +/*%< + * Set the SyncPublish time (when the DS may be submitted to the parent). + * If 'first' is true, also make sure that the zone signatures are omnipresent. + * + * Requires: + *\li 'key' is a valid DNSSEC key. + *\li 'kasp' is a valid DNSSEC policy. + */ + isc_result_t dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, isc_mem_t *mctx, dns_dnsseckeylist_t *keyring, diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index f1cc28ed25..1f5ab70c9b 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -163,26 +163,25 @@ keymgr_settime_remove(dns_dnsseckey_t *key, dns_kasp_t *kasp) { * Set the SyncPublish time (when the DS may be submitted to the parent). * */ -static void -keymgr_settime_syncpublish(dns_dnsseckey_t *key, dns_kasp_t *kasp, bool first) { +void +dns_keymgr_settime_syncpublish(dst_key_t *key, dns_kasp_t *kasp, bool first) { isc_stdtime_t published, syncpublish; bool ksk = false; isc_result_t ret; REQUIRE(key != NULL); - REQUIRE(key->key != NULL); - ret = dst_key_gettime(key->key, DST_TIME_PUBLISH, &published); + ret = dst_key_gettime(key, DST_TIME_PUBLISH, &published); if (ret != ISC_R_SUCCESS) { return; } - ret = dst_key_getbool(key->key, DST_BOOL_KSK, &ksk); + ret = dst_key_getbool(key, DST_BOOL_KSK, &ksk); if (ret != ISC_R_SUCCESS || !ksk) { return; } - syncpublish = published + dst_key_getttl(key->key) + + syncpublish = published + dst_key_getttl(key) + dns_kasp_zonepropagationdelay(kasp) + dns_kasp_publishsafety(kasp); if (first) { @@ -196,7 +195,7 @@ keymgr_settime_syncpublish(dns_dnsseckey_t *key, dns_kasp_t *kasp, bool first) { syncpublish = zrrsig_present; } } - dst_key_settime(key->key, DST_TIME_SYNCPUBLISH, syncpublish); + dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncpublish); } /* @@ -1862,7 +1861,7 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, */ dst_key_settime(new_key->key, DST_TIME_PUBLISH, now); dst_key_settime(new_key->key, DST_TIME_ACTIVATE, now); - keymgr_settime_syncpublish(new_key, kasp, true); + dns_keymgr_settime_syncpublish(new_key->key, kasp, true); active = now; } else { /* @@ -1894,7 +1893,7 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, } dst_key_settime(new_key->key, DST_TIME_PUBLISH, prepub); dst_key_settime(new_key->key, DST_TIME_ACTIVATE, active); - keymgr_settime_syncpublish(new_key, kasp, false); + dns_keymgr_settime_syncpublish(new_key->key, kasp, false); /* * Retire predecessor. From 708927e03d152983557b6a2d0d40888e0ccffad5 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 4 Sep 2024 15:54:53 +0200 Subject: [PATCH 3/5] Allow empty CDNSKEY/CDS RRset in ksr system test When the zone is initially signed, the CDNSKEY/CDS RRset is not immediately published. The DNSKEY and signatures must propagate first. Adjust the test to allow for this case. --- bin/tests/system/isctest/kasp.py | 19 +++++++++++-------- bin/tests/system/ksr/tests_ksr.py | 26 ++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/bin/tests/system/isctest/kasp.py b/bin/tests/system/isctest/kasp.py index 7dd2c1d502..1fbb489319 100644 --- a/bin/tests/system/isctest/kasp.py +++ b/bin/tests/system/isctest/kasp.py @@ -431,8 +431,11 @@ def _check_dnskeys(dnskeys, keys, cdnskey=False): has_dnskey = True break - assert has_dnskey - numkeys += 1 + if not cdnskey: + assert has_dnskey + + if has_dnskey: + numkeys += 1 return numkeys @@ -541,17 +544,17 @@ def check_apex(server, zone, ksks, zsks): # test cdnskey query cdnskeys, rrsigs = _query_rrset(server, fqdn, dns.rdatatype.CDNSKEY) - assert len(cdnskeys) > 0 check_dnskeys(cdnskeys, ksks, zsks, cdnskey=True) - assert len(rrsigs) > 0 - check_signatures(rrsigs, dns.rdatatype.CDNSKEY, fqdn, ksks, zsks) + if len(cdnskeys) > 0: + assert len(rrsigs) > 0 + check_signatures(rrsigs, dns.rdatatype.CDNSKEY, fqdn, ksks, zsks) # test cds query cds, rrsigs = _query_rrset(server, fqdn, dns.rdatatype.CDS) - assert len(cds) > 0 check_cds(cds, ksks) - assert len(rrsigs) > 0 - check_signatures(rrsigs, dns.rdatatype.CDS, fqdn, ksks, zsks) + if len(cds) > 0: + assert len(rrsigs) > 0 + check_signatures(rrsigs, dns.rdatatype.CDS, fqdn, ksks, zsks) def check_subdomain(server, zone, ksks, zsks): diff --git a/bin/tests/system/ksr/tests_ksr.py b/bin/tests/system/ksr/tests_ksr.py index 3790003f33..3c9f4ee85e 100644 --- a/bin/tests/system/ksr/tests_ksr.py +++ b/bin/tests/system/ksr/tests_ksr.py @@ -324,10 +324,17 @@ def check_signedkeyresponse( line_no += 1 # expect cdnskey + have_cdnskey = False if cdnskey: for key in sorted(ksks): - published = key.get_timing("Publish") - removed = key.get_timing("Delete", must_exist=False) + published = key.get_timing("SyncPublish") + if between(published, inception, next_bundle): + next_bundle = published + + removed = key.get_timing("SyncDelete", must_exist=False) + if between(removed, inception, next_bundle): + next_bundle = removed + if published > inception: continue if removed is not None and inception >= removed: @@ -336,7 +343,9 @@ def check_signedkeyresponse( # the cdnskey of this ksk must be in the ksr assert key.dnskey_equals(lines[line_no], cdnskey=True) line_no += 1 + have_cdnskey = True + if have_cdnskey: # expect rrsig(cdnskey) for key in sorted(ksks): active = key.get_timing("Activate") @@ -354,10 +363,17 @@ def check_signedkeyresponse( line_no += 1 # expect cds + have_cds = False if cds != "": for key in sorted(ksks): - published = key.get_timing("Publish") - removed = key.get_timing("Delete", must_exist=False) + published = key.get_timing("SyncPublish") + if between(published, inception, next_bundle): + next_bundle = published + + removed = key.get_timing("SyncDelete", must_exist=False) + if between(removed, inception, next_bundle): + next_bundle = removed + if published > inception: continue if removed is not None and inception >= removed: @@ -368,7 +384,9 @@ def check_signedkeyresponse( for alg in expected_cds: assert key.cds_equals(lines[line_no], alg.strip()) line_no += 1 + have_cds = True + if have_cds: # expect rrsig(cds) for key in sorted(ksks): active = key.get_timing("Activate") From 8cf5f972f4e92b732d34a7715a789c64cdf997e4 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 4 Sep 2024 15:57:55 +0200 Subject: [PATCH 4/5] Add KSK roll test case Add a test case for Offline KSK where during the lifespan of the Signed Key Response a KSK rollover happens. Ensure that the correct DNSKEY, CDNSKEY, and CDS records are published at the right times. --- bin/tests/system/ksr/ns1/named.conf.in | 8 +++ bin/tests/system/ksr/ns1/setup.sh | 1 + bin/tests/system/ksr/tests_ksr.py | 74 ++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/bin/tests/system/ksr/ns1/named.conf.in b/bin/tests/system/ksr/ns1/named.conf.in index 75710b42dc..7283069321 100644 --- a/bin/tests/system/ksr/ns1/named.conf.in +++ b/bin/tests/system/ksr/ns1/named.conf.in @@ -85,3 +85,11 @@ dnssec-policy "two-tone" { zsk lifetime P3M algorithm @DEFAULT_ALGORITHM@; }; }; + +dnssec-policy "ksk-roll" { + offline-ksk yes; + keys { + ksk lifetime P6M algorithm @DEFAULT_ALGORITHM@; + zsk lifetime unlimited algorithm @DEFAULT_ALGORITHM@; + }; +}; diff --git a/bin/tests/system/ksr/ns1/setup.sh b/bin/tests/system/ksr/ns1/setup.sh index a04b01a23e..2179ab251d 100644 --- a/bin/tests/system/ksr/ns1/setup.sh +++ b/bin/tests/system/ksr/ns1/setup.sh @@ -26,3 +26,4 @@ cp template.db.in last-bundle.test.db cp template.db.in in-the-middle.test.db cp template.db.in unlimited.test.db cp template.db.in two-tone.test.db +cp template.db.in ksk-roll.test.db diff --git a/bin/tests/system/ksr/tests_ksr.py b/bin/tests/system/ksr/tests_ksr.py index 3c9f4ee85e..8fcdbdb7d8 100644 --- a/bin/tests/system/ksr/tests_ksr.py +++ b/bin/tests/system/ksr/tests_ksr.py @@ -1103,3 +1103,77 @@ def test_ksr_twotone(servers): isctest.kasp.check_apex(ns1, zone, ksks, zsks) # - check subdomain isctest.kasp.check_subdomain(ns1, zone, ksks, zsks) + + +def test_ksr_kskroll(servers): + zone = "ksk-roll.test" + policy = "ksk-roll" + n = 1 + + # create ksk + kskdir = "ns1/offline" + out, _ = ksr(zone, policy, "keygen", options=f"-K {kskdir} -i now -e +1y -o") + ksks = keystr_to_keylist(out, kskdir) + assert len(ksks) == 2 + + lifetime = timedelta(days=31 * 6) + check_keys(ksks, lifetime) + + # check that 'dnssec-ksr keygen' pregenerates right amount of keys + zskdir = "ns1" + out, _ = ksr(zone, policy, "keygen", options=f"-K {zskdir} -i now -e +1y") + zsks = keystr_to_keylist(out, zskdir) + assert len(zsks) == 1 + + check_keys(zsks, None) + + # check that 'dnssec-ksr request' creates correct ksr + now = zsks[0].get_timing("Created") + until = now + timedelta(days=365) + out, _ = ksr(zone, policy, "request", options=f"-K {zskdir} -i {now} -e +1y") + + fname = f"{zone}.ksr.{n}" + with open(fname, "w", encoding="utf-8") as file: + file.write(out) + + check_keysigningrequest(out, zsks, now, until) + + # check that 'dnssec-ksr sign' creates correct skr + out, _ = ksr( + zone, policy, "sign", options=f"-K {kskdir} -f {fname} -i {now} -e +1y" + ) + + skrfile = f"{zone}.skr.{n}" + with open(skrfile, "w", encoding="utf-8") as file: + file.write(out) + + refresh = -432000 # 5 days + check_signedkeyresponse(out, zone, ksks, zsks, now, until, refresh) + + # add zone + ns1 = servers["ns1"] + ns1.rndc( + f"addzone {zone} " + + "{ type primary; file " + + f'"{zone}.db"; dnssec-policy {policy}; ' + + "};", + log=False, + ) + + # import skr + shutil.copyfile(skrfile, f"ns1/{skrfile}") + ns1.rndc(f"skr -import {skrfile} {zone}", log=False) + + # test zone is correctly signed + # - check rndc dnssec -status output + isctest.kasp.check_dnssecstatus(ns1, zone, zsks, policy=policy) + # - zone is signed + isctest.kasp.check_zone_is_signed(ns1, zone) + # - dnssec_verify + isctest.kasp.check_dnssec_verify(ns1, zone) + # - check keys + check_keys(zsks, None, with_state=True) + # - check apex + isctest.kasp.check_apex(ns1, zone, ksks, zsks) + # - check subdomain + isctest.kasp.check_subdomain(ns1, zone, ksks, zsks) From d7f2a2f43781285381ea49771aac31f06ce337c4 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 4 Sep 2024 16:00:13 +0200 Subject: [PATCH 5/5] Fix dnssec-ksr to support KSK rollovers dnssec-ksr can now sign KSR files with multiple KSKs. A planned KSK rollover is supported, meaning the KSR will first be signed with one KSK and later with another. The timing metadata for CDS and CDNSKEY records are also taken into account, so these records are only published when the time is between "SyncPublish" and "SyncDelete". --- bin/dnssec/dnssec-ksr.c | 243 +++++++++++++++++++++++++++++----------- 1 file changed, 179 insertions(+), 64 deletions(-) diff --git a/bin/dnssec/dnssec-ksr.c b/bin/dnssec/dnssec-ksr.c index 633e2f7e50..10982f6c22 100644 --- a/bin/dnssec/dnssec-ksr.c +++ b/bin/dnssec/dnssec-ksr.c @@ -143,6 +143,15 @@ usage(int ret) { exit(ret); } +static isc_stdtime_t +between(isc_stdtime_t t, isc_stdtime_t start, isc_stdtime_t end) { + isc_stdtime_t r = end; + if (t > 0 && t > start && t < end) { + r = t; + } + return (r); +} + static void checkparams(ksr_ctx_t *ksr, const char *command) { if (ksr->configfile == NULL) { @@ -325,7 +334,6 @@ create_key(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_kasp_key_t *kaspkey, bool conflict = false; bool freekey = false; bool show_progress = true; - bool first = true; char algstr[DNS_SECALG_FORMATSIZE]; char filename[PATH_MAX + 1]; char timestr[26]; /* Minimal buf as per ctime_r() spec. */ @@ -416,7 +424,6 @@ create_key(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_kasp_key_t *kaspkey, "Selecting key pair for bundle %s: ", timestr); fflush(stderr); } - first = false; key = dk->key; *expiration = inact; goto output; @@ -488,7 +495,8 @@ create_key(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_kasp_key_t *kaspkey, dst_key_settime(key, DST_TIME_PUBLISH, (active - prepub)); dst_key_settime(key, DST_TIME_ACTIVATE, active); if (ksr->ksk) { - dns_keymgr_settime_syncpublish(key, kasp, first); + dns_keymgr_settime_syncpublish(key, kasp, + (inception == ksr->start)); } if (ksr->lifetime > 0) { @@ -518,8 +526,6 @@ create_key(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_kasp_key_t *kaspkey, isc_result_totext(ret)); } - first = false; - output: isc_buffer_clear(&buf); ret = dst_key_buildfilename(key, 0, NULL, &buf); @@ -638,12 +644,13 @@ fail: return (next_bundle); } -static void +static isc_stdtime_t sign_rrset(ksr_ctx_t *ksr, isc_stdtime_t inception, isc_stdtime_t expiration, dns_rdataset_t *rrset, dns_dnsseckeylist_t *keys) { dns_rdatalist_t *rrsiglist = NULL; dns_rdataset_t rrsigset = DNS_RDATASET_INIT; isc_result_t ret; + isc_stdtime_t next_bundle = expiration; UNUSED(ksr); @@ -687,6 +694,25 @@ sign_rrset(ksr_ctx_t *ksr, isc_stdtime_t inception, isc_stdtime_t expiration, unsigned char rdatabuf[SIG_FORMATSIZE]; isc_stdtime_t clockskew = inception - 3600; + isc_stdtime_t pub = 0, act = 0, inact = 0, del = 0; + + /* Determine next bundle. */ + (void)dst_key_gettime(dk->key, DST_TIME_PUBLISH, &pub); + (void)dst_key_gettime(dk->key, DST_TIME_ACTIVATE, &act); + (void)dst_key_gettime(dk->key, DST_TIME_INACTIVE, &inact); + (void)dst_key_gettime(dk->key, DST_TIME_DELETE, &del); + next_bundle = between(pub, inception, next_bundle); + next_bundle = between(act, inception, next_bundle); + next_bundle = between(inact, inception, next_bundle); + next_bundle = between(del, inception, next_bundle); + + if (act > inception) { + continue; + } + if (inact != 0 && inception >= inact) { + continue; + } + rrsig = isc_mem_get(mctx, sizeof(*rrsig)); dns_rdata_init(rrsig); isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf)); @@ -708,21 +734,25 @@ sign_rrset(ksr_ctx_t *ksr, isc_stdtime_t inception, isc_stdtime_t expiration, dns_rdatalist_tordataset(rrsiglist, &rrsigset); print_rdata(&rrsigset); freerrset(&rrsigset); + + return (next_bundle); } /* * Create the DNSKEY, CDS, and CDNSKEY records beloing to the KSKs * listed in 'keys'. */ -static void -create_ksk(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys, - dns_rdataset_t *dnskeyset, dns_rdataset_t *cdnskeyset, - dns_rdataset_t *cdsset) { +static isc_stdtime_t +get_keymaterial(ksr_ctx_t *ksr, dns_kasp_t *kasp, isc_stdtime_t inception, + isc_stdtime_t next_inception, dns_dnsseckeylist_t *keys, + dns_rdataset_t *dnskeyset, dns_rdataset_t *cdnskeyset, + dns_rdataset_t *cdsset) { + dns_kasp_digestlist_t digests = dns_kasp_digests(kasp); dns_rdatalist_t *dnskeylist = isc_mem_get(mctx, sizeof(*dnskeylist)); dns_rdatalist_t *cdnskeylist = isc_mem_get(mctx, sizeof(*cdnskeylist)); dns_rdatalist_t *cdslist = isc_mem_get(mctx, sizeof(*cdslist)); isc_result_t ret = ISC_R_SUCCESS; - dns_kasp_digestlist_t digests = dns_kasp_digests(kasp); + isc_stdtime_t next_bundle = next_inception; dns_rdatalist_init(dnskeylist); dnskeylist->rdclass = dns_rdataclass_in; @@ -742,31 +772,73 @@ create_ksk(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys, for (dns_dnsseckey_t *dk = ISC_LIST_HEAD(*keys); dk != NULL; dk = ISC_LIST_NEXT(dk, link)) { + bool published = true; isc_buffer_t buf; isc_buffer_t *newbuf; dns_rdata_t *rdata; isc_region_t r; isc_region_t rcds; + isc_stdtime_t pub = 0, del = 0; unsigned char kskbuf[DST_KEY_MAXSIZE]; unsigned char cdnskeybuf[DST_KEY_MAXSIZE]; unsigned char cdsbuf[DNS_DS_BUFFERSIZE]; /* KSK */ - newbuf = NULL; - rdata = isc_mem_get(mctx, sizeof(*rdata)); - dns_rdata_init(rdata); + (void)dst_key_gettime(dk->key, DST_TIME_PUBLISH, &pub); + (void)dst_key_gettime(dk->key, DST_TIME_DELETE, &del); + next_bundle = between(pub, inception, next_bundle); + next_bundle = between(del, inception, next_bundle); - isc_buffer_init(&buf, kskbuf, sizeof(kskbuf)); - CHECK(dst_key_todns(dk->key, &buf)); - isc_buffer_usedregion(&buf, &r); - isc_buffer_allocate(mctx, &newbuf, r.length); - isc_buffer_putmem(newbuf, r.base, r.length); - isc_buffer_usedregion(newbuf, &r); - dns_rdata_fromregion(rdata, dns_rdataclass_in, - dns_rdatatype_dnskey, &r); - ISC_LIST_APPEND(dnskeylist->rdata, rdata, link); - ISC_LIST_APPEND(cleanup_list, newbuf, link); - isc_buffer_clear(newbuf); + if (pub > inception) { + published = false; + } + if (del != 0 && inception >= del) { + published = false; + } + + if (published) { + newbuf = NULL; + rdata = isc_mem_get(mctx, sizeof(*rdata)); + dns_rdata_init(rdata); + + isc_buffer_init(&buf, kskbuf, sizeof(kskbuf)); + CHECK(dst_key_todns(dk->key, &buf)); + isc_buffer_usedregion(&buf, &r); + isc_buffer_allocate(mctx, &newbuf, r.length); + isc_buffer_putmem(newbuf, r.base, r.length); + isc_buffer_usedregion(newbuf, &r); + dns_rdata_fromregion(rdata, dns_rdataclass_in, + dns_rdatatype_dnskey, &r); + ISC_LIST_APPEND(dnskeylist->rdata, rdata, link); + ISC_LIST_APPEND(cleanup_list, newbuf, link); + isc_buffer_clear(newbuf); + } + + published = true; + if (dns_kasp_cdnskey(kasp) || !ISC_LIST_EMPTY(digests)) { + pub = 0; + del = 0; + (void)dst_key_gettime(dk->key, DST_TIME_SYNCPUBLISH, + &pub); + (void)dst_key_gettime(dk->key, DST_TIME_SYNCDELETE, + &del); + + next_bundle = between(pub, inception, next_bundle); + next_bundle = between(del, inception, next_bundle); + + if (pub != 0 && pub > inception) { + published = false; + } + if (del != 0 && inception >= del) { + published = false; + } + } else { + published = false; + } + + if (!published) { + continue; + } /* CDNSKEY */ newbuf = NULL; @@ -820,35 +892,98 @@ create_ksk(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys, dns_rdatalist_tordataset(dnskeylist, dnskeyset); dns_rdatalist_tordataset(cdnskeylist, cdnskeyset); dns_rdatalist_tordataset(cdslist, cdsset); - return; + + return (next_bundle); fail: fatal("failed to create KSK/CDS/CDNSKEY"); + return (0); } static void -sign_bundle(ksr_ctx_t *ksr, isc_stdtime_t inception, - isc_stdtime_t next_inception, dns_rdatalist_t *rdatalist, - dns_rdataset_t *cds, dns_rdataset_t *cdnskey, +sign_bundle(ksr_ctx_t *ksr, dns_kasp_t *kasp, isc_stdtime_t inception, + isc_stdtime_t next_inception, dns_rdatalist_t *zsklist, dns_dnsseckeylist_t *keys) { - dns_rdataset_t rrset = DNS_RDATASET_INIT; - isc_stdtime_t expiration; + isc_stdtime_t expiration = inception + ksr->sigvalidity; + isc_stdtime_t next_bundle = next_inception; + dns_rdataset_t zsk; + + dns_rdataset_init(&zsk); + dns_rdatalist_tordataset(zsklist, &zsk); - dns_rdataset_init(&rrset); - dns_rdatalist_tordataset(rdatalist, &rrset); - expiration = inception + ksr->sigvalidity; while (inception <= next_inception) { - sign_rrset(ksr, inception, expiration, &rrset, keys); - if (dns_rdataset_count(cdnskey) > 0) { - sign_rrset(ksr, inception, expiration, cdnskey, keys); + isc_stdtime_t next_time = next_bundle; + + /* DNSKEY RRset */ + dns_rdatalist_t *dnskeylist; + dnskeylist = isc_mem_get(mctx, sizeof(*dnskeylist)); + dns_rdatalist_init(dnskeylist); + dnskeylist->rdclass = dns_rdataclass_in; + dnskeylist->type = dns_rdatatype_dnskey; + dnskeylist->ttl = ksr->ttl; + + dns_rdataset_t ksk, cdnskey, cds, rrset; + dns_rdataset_init(&ksk); + dns_rdataset_init(&cdnskey); + dns_rdataset_init(&cds); + dns_rdataset_init(&rrset); + next_time = get_keymaterial(ksr, kasp, inception, next_time, + keys, &ksk, &cdnskey, &cds); + if (next_bundle > next_time) { + next_bundle = next_time; } - if (dns_rdataset_count(cds) > 0) { - sign_rrset(ksr, inception, expiration, cds, keys); + + for (isc_result_t r = dns_rdatalist_first(&ksk); + r == ISC_R_SUCCESS; r = dns_rdatalist_next(&ksk)) + { + dns_rdata_t *clone = isc_mem_get(mctx, sizeof(*clone)); + dns_rdata_init(clone); + dns_rdatalist_current(&ksk, clone); + ISC_LIST_APPEND(dnskeylist->rdata, clone, link); } + + for (isc_result_t r = dns_rdatalist_first(&zsk); + r == ISC_R_SUCCESS; r = dns_rdatalist_next(&zsk)) + { + dns_rdata_t *clone = isc_mem_get(mctx, sizeof(*clone)); + dns_rdata_init(clone); + dns_rdatalist_current(&zsk, clone); + ISC_LIST_APPEND(dnskeylist->rdata, clone, link); + } + + dns_rdatalist_tordataset(dnskeylist, &rrset); + next_time = sign_rrset(ksr, inception, expiration, &rrset, + keys); + if (next_bundle > next_time) { + next_bundle = next_time; + } + freerrset(&ksk); + freerrset(&rrset); + + /* CDNSKEY */ + if (dns_rdataset_count(&cdnskey) > 0) { + (void)sign_rrset(ksr, inception, expiration, &cdnskey, + keys); + } + freerrset(&cdnskey); + + /* CDS */ + if (dns_rdataset_count(&cds) > 0) { + (void)sign_rrset(ksr, inception, expiration, &cds, + keys); + } + freerrset(&cds); + + /* Next response bundle. */ inception = expiration - ksr->sigrefresh; + if (inception > next_bundle) { + inception = next_bundle; + } expiration = inception + ksr->sigvalidity; + next_bundle = expiration; } - freerrset(&rrset); + + freerrset(&zsk); } static isc_result_t @@ -1038,9 +1173,6 @@ sign(ksr_ctx_t *ksr) { dns_dnsseckeylist_t keys; dns_kasp_t *kasp = NULL; dns_rdatalist_t *rdatalist = NULL; - dns_rdataset_t ksk = DNS_RDATASET_INIT; - dns_rdataset_t cdnskey = DNS_RDATASET_INIT; - dns_rdataset_t cds = DNS_RDATASET_INIT; isc_result_t ret; isc_stdtime_t inception; isc_lex_t *lex = NULL; @@ -1073,9 +1205,6 @@ sign(ksr_ctx_t *ksr) { isc_result_totext(ret)); } - /* KSK, CDS and CDNSKEY */ - create_ksk(ksr, kasp, &keys, &ksk, &cdnskey, &cds); - for (ret = isc_lex_gettoken(lex, opt, &token); ret == ISC_R_SUCCESS; ret = isc_lex_gettoken(lex, opt, &token)) { @@ -1125,8 +1254,8 @@ sign(ksr_ctx_t *ksr) { if (have_bundle) { /* Sign previous bundle */ - sign_bundle(ksr, inception, next_inception, - rdatalist, &cds, &cdnskey, &keys); + sign_bundle(ksr, kasp, inception, + next_inception, rdatalist, &keys); fprintf(stdout, "\n"); } @@ -1136,15 +1265,7 @@ sign(ksr_ctx_t *ksr) { rdatalist->rdclass = dns_rdataclass_in; rdatalist->type = dns_rdatatype_dnskey; rdatalist->ttl = ksr->ttl; - for (isc_result_t r = dns_rdatalist_first(&ksk); - r == ISC_R_SUCCESS; r = dns_rdatalist_next(&ksk)) - { - dns_rdata_t *clone = - isc_mem_get(mctx, sizeof(*clone)); - dns_rdata_init(clone); - dns_rdatalist_current(&ksk, clone); - ISC_LIST_APPEND(rdatalist->rdata, clone, link); - } + inception = next_inception; have_bundle = true; @@ -1203,8 +1324,7 @@ sign(ksr_ctx_t *ksr) { /* Final bundle */ if (have_bundle && rdatalist != NULL) { - sign_bundle(ksr, inception, ksr->end, rdatalist, &cds, &cdnskey, - &keys); + sign_bundle(ksr, kasp, inception, ksr->end, rdatalist, &keys); } else { fatal("bad KSR file %s(%lu): no bundles", ksr->file, isc_lex_getsourceline(lex)); @@ -1216,11 +1336,6 @@ sign(ksr_ctx_t *ksr) { timestr, PACKAGE_VERSION); fail: - /* Clean up */ - freerrset(&ksk); - freerrset(&cdnskey); - freerrset(&cds); - isc_lex_destroy(&lex); cleanup(&keys, kasp); }