diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 54bbd1fd6a..97316b7d33 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -1290,6 +1290,7 @@ dns_dnsseckey_create(isc_mem_t *mctx, dst_key_t **dstkey, dk->hint_remove = false; dk->first_sign = false; dk->is_active = false; + dk->purge = false; dk->prepublish = 0; dk->source = dns_keysource_unknown; dk->index = 0; diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h index c51b2c9aad..e74ec4798e 100644 --- a/lib/dns/include/dns/dnssec.h +++ b/lib/dns/include/dns/dnssec.h @@ -59,6 +59,7 @@ struct dns_dnsseckey { bool hint_remove; /*% metadata says *don't* publish */ bool is_active; /*% key is already active */ bool first_sign; /*% key is newly becoming active */ + bool purge; /*% remove key files */ unsigned int prepublish; /*% how long until active? */ dns_keysource_t source; /*% how the key was found */ bool ksk; /*% this is a key-signing key */ diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index ae952f7325..009c06b195 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -1823,6 +1824,94 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, return (ISC_R_SUCCESS); } +static bool +keymgr_key_may_be_purged(dst_key_t *key, uint32_t after, isc_stdtime_t now) { + bool ksk = false; + bool zsk = false; + dst_key_state_t hidden[NUM_KEYSTATES] = { HIDDEN, NA, NA, NA }; + isc_stdtime_t lastchange = 0; + + char keystr[DST_KEY_FORMATSIZE]; + dst_key_format(key, keystr, sizeof(keystr)); + + /* If 'purge-keys' is disabled, always retain keys. */ + if (after == 0) { + return (false); + } + + /* Don't purge keys with goal OMNIPRESENT */ + if (dst_key_goal(key) == OMNIPRESENT) { + return (false); + } + + /* Don't purge unused keys. */ + if (dst_key_is_unused(key)) { + return (false); + } + + /* If this key is completely HIDDEN it may be purged. */ + (void)dst_key_getbool(key, DST_BOOL_KSK, &ksk); + (void)dst_key_getbool(key, DST_BOOL_ZSK, &zsk); + if (ksk) { + hidden[DST_KEY_KRRSIG] = HIDDEN; + hidden[DST_KEY_DS] = HIDDEN; + } + if (zsk) { + hidden[DST_KEY_ZRRSIG] = HIDDEN; + } + if (!keymgr_key_match_state(key, key, 0, NA, hidden)) { + return (false); + } + + /* + * Check 'purge-keys' interval. If the interval has passed since + * the last key change, it may be purged. + */ + for (int i = 0; i < NUM_KEYSTATES; i++) { + isc_stdtime_t change = 0; + (void)dst_key_gettime(key, keystatetimes[i], &change); + if (change > lastchange) { + lastchange = change; + } + } + + return ((lastchange + after) < now); +} + +static void +keymgr_purge_keyfile(dst_key_t *key, const char *dir, int type) { + isc_result_t ret; + isc_buffer_t fileb; + char filename[NAME_MAX]; + + /* + * Make the filename. + */ + isc_buffer_init(&fileb, filename, sizeof(filename)); + ret = dst_key_buildfilename(key, type, dir, &fileb); + if (ret != ISC_R_SUCCESS) { + char keystr[DST_KEY_FORMATSIZE]; + dst_key_format(key, keystr, sizeof(keystr)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, + "keymgr: failed to purge DNSKEY %s (%s): cannot " + "build filename (%s)", + keystr, keymgr_keyrole(key), + isc_result_totext(ret)); + return; + } + + if (unlink(filename) < 0) { + char keystr[DST_KEY_FORMATSIZE]; + dst_key_format(key, keystr, sizeof(keystr)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, + "keymgr: failed to purge DNSKEY %s (%s): unlink " + "'%s' failed", + keystr, keymgr_keyrole(key), filename); + } +} + /* * Examine 'keys' and match 'kasp' policy. * @@ -1901,6 +1990,27 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, if (!found_match) { keymgr_key_retire(dkey, kasp, now); } + + /* Check purge-keys interval. */ + if (keymgr_key_may_be_purged(dkey->key, + dns_kasp_purgekeys(kasp), now)) { + dst_key_format(dkey->key, keystr, sizeof(keystr)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, + "keymgr: purge DNSKEY %s (%s) according " + "to policy %s", + keystr, keymgr_keyrole(dkey->key), + dns_kasp_getname(kasp)); + + keymgr_purge_keyfile(dkey->key, directory, + DST_TYPE_PUBLIC); + keymgr_purge_keyfile(dkey->key, directory, + DST_TYPE_PRIVATE); + keymgr_purge_keyfile(dkey->key, directory, + DST_TYPE_STATE); + + dkey->purge = true; + } } /* Create keys according to the policy, if come in short. */ @@ -1993,8 +2103,10 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; dkey = ISC_LIST_NEXT(dkey, link)) { - dns_dnssec_get_hints(dkey, now); - RETERR(dst_key_tofile(dkey->key, options, directory)); + if (!dkey->purge) { + dns_dnssec_get_hints(dkey, now); + RETERR(dst_key_tofile(dkey->key, options, directory)); + } } result = ISC_R_SUCCESS;