diff --git a/lib/dns/include/dns/keymgr.h b/lib/dns/include/dns/keymgr.h index eadb0ea877..61cf3df7aa 100644 --- a/lib/dns/include/dns/keymgr.h +++ b/lib/dns/include/dns/keymgr.h @@ -52,6 +52,29 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, *\li On error, keypool is unchanged */ +isc_result_t +dns_keymgr_offline(const dns_name_t *origin, dns_dnsseckeylist_t *keyring, + dns_kasp_t *kasp, isc_stdtime_t now, + isc_stdtime_t *nexttime); +/*%< + * Manage keys in 'keyring' when in offline-ksk mode, and update timing data + * according to the metadata in the key files. KSKs are skipped. + * + * Update key states and store changes back to disk. Store when to run next + * in 'nexttime'. + * + * Requires: + *\li 'origin' is a valid FQDN. + *\li 'mctx' is a valid memory context. + *\li 'keyring' is not NULL. + *\li 'kasp' is not NULL. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li any error returned by dst_key_getstate(), dst_key_gettime(), + * isc_dir_open(), or dst_key_to_file(). + */ + isc_result_t dns_keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, isc_stdtime_t now, isc_stdtime_t when, bool dspublish); diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 35e3a83565..9e0f808dd8 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -2661,3 +2661,177 @@ dns_keymgr_rollover(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, return (result); } + +isc_result_t +dns_keymgr_offline(const dns_name_t *origin, dns_dnsseckeylist_t *keyring, + dns_kasp_t *kasp, isc_stdtime_t now, + isc_stdtime_t *nexttime) { + isc_result_t result = ISC_R_SUCCESS; + int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); + char keystr[DST_KEY_FORMATSIZE]; + + *nexttime = 0; + + /* Store key states and update hints. */ + for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; + dkey = ISC_LIST_NEXT(dkey, link)) + { + bool modified; + bool ksk = false, zsk = false; + isc_stdtime_t active = 0, published = 0, inactive = 0, + remove = 0; + isc_stdtime_t lastchange = 0, nextchange = 0; + dst_key_state_t dnskey_state = HIDDEN, zrrsig_state = HIDDEN, + goal_state = HIDDEN; + dst_key_state_t current_dnskey, current_zrrsig, current_goal; + + (void)dst_key_role(dkey->key, &ksk, &zsk); + if (ksk || !zsk) { + continue; + } + + keymgr_key_init(dkey, kasp, now, false); + + /* Get current metadata */ + RETERR(dst_key_getstate(dkey->key, DST_KEY_DNSKEY, + ¤t_dnskey)); + RETERR(dst_key_getstate(dkey->key, DST_KEY_ZRRSIG, + ¤t_zrrsig)); + RETERR(dst_key_getstate(dkey->key, DST_KEY_GOAL, + ¤t_goal)); + RETERR(dst_key_gettime(dkey->key, DST_TIME_PUBLISH, + &published)); + RETERR(dst_key_gettime(dkey->key, DST_TIME_ACTIVATE, &active)); + RETERR(dst_key_gettime(dkey->key, DST_TIME_INACTIVE, + &inactive)); + RETERR(dst_key_gettime(dkey->key, DST_TIME_DELETE, &remove)); + + /* Determine key states from the metadata. */ + if (active <= now) { + dns_ttl_t ttlsig = dns_kasp_zonemaxttl(kasp, true); + ttlsig += dns_kasp_zonepropagationdelay(kasp); + if ((active + ttlsig) <= now) { + zrrsig_state = OMNIPRESENT; + } else { + zrrsig_state = RUMOURED; + (void)dst_key_gettime(dkey->key, + DST_TIME_ZRRSIG, + &lastchange); + nextchange = lastchange + ttlsig + + dns_kasp_retiresafety(kasp); + } + goal_state = OMNIPRESENT; + } + + if (published <= now) { + dns_ttl_t key_ttl = dst_key_getttl(dkey->key); + key_ttl += dns_kasp_zonepropagationdelay(kasp); + if ((published + key_ttl) <= now) { + dnskey_state = OMNIPRESENT; + } else { + dnskey_state = RUMOURED; + (void)dst_key_gettime(dkey->key, + DST_TIME_DNSKEY, + &lastchange); + nextchange = lastchange + key_ttl + + dns_kasp_publishsafety(kasp); + } + goal_state = OMNIPRESENT; + } + + if (inactive <= now) { + dns_ttl_t ttlsig = dns_kasp_zonemaxttl(kasp, true); + ttlsig += dns_kasp_zonepropagationdelay(kasp); + if ((inactive + ttlsig) <= now) { + zrrsig_state = HIDDEN; + } else { + zrrsig_state = UNRETENTIVE; + (void)dst_key_gettime(dkey->key, + DST_TIME_ZRRSIG, + &lastchange); + nextchange = lastchange + ttlsig + + dns_kasp_retiresafety(kasp); + } + goal_state = HIDDEN; + } + + if (remove <= now) { + dns_ttl_t key_ttl = dst_key_getttl(dkey->key); + key_ttl += dns_kasp_zonepropagationdelay(kasp); + if ((remove + key_ttl) <= now) { + dnskey_state = HIDDEN; + } else { + dnskey_state = UNRETENTIVE; + (void)dst_key_gettime(dkey->key, + DST_TIME_DNSKEY, + &lastchange); + nextchange = + lastchange + key_ttl + + dns_kasp_zonepropagationdelay(kasp); + } + zrrsig_state = HIDDEN; + goal_state = HIDDEN; + } + + if ((*nexttime == 0 || *nexttime > nextchange) && + nextchange > 0) + { + *nexttime = nextchange; + } + + /* Update key states if necessary. */ + if (goal_state != current_goal) { + dst_key_setstate(dkey->key, DST_KEY_GOAL, goal_state); + } + if (dnskey_state != current_dnskey) { + dst_key_setstate(dkey->key, DST_KEY_DNSKEY, + dnskey_state); + dst_key_settime(dkey->key, DST_TIME_DNSKEY, now); + } + if (zrrsig_state != current_zrrsig) { + dst_key_setstate(dkey->key, DST_KEY_ZRRSIG, + zrrsig_state); + dst_key_settime(dkey->key, DST_TIME_ZRRSIG, now); + if (zrrsig_state == RUMOURED) { + dkey->first_sign = true; + } + } + modified = dst_key_ismodified(dkey->key); + + if (modified) { + const char *directory = dst_key_directory(dkey->key); + if (directory == NULL) { + directory = "."; + } + + dns_dnssec_get_hints(dkey, now); + + RETERR(dst_key_tofile(dkey->key, options, directory)); + dst_key_setmodified(dkey->key, false); + + if (!isc_log_wouldlog(ISC_LOG_DEBUG(3))) { + continue; + } + dst_key_format(dkey->key, keystr, sizeof(keystr)); + isc_log_write(DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(3), + "keymgr: DNSKEY %s (%s) " + "saved to directory %s, policy %s", + keystr, keymgr_keyrole(dkey->key), + directory, dns_kasp_getname(kasp)); + } + dst_key_setmodified(dkey->key, false); + } + + result = ISC_R_SUCCESS; + +failure: + if (isc_log_wouldlog(ISC_LOG_DEBUG(3))) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(origin, namebuf, sizeof(namebuf)); + isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, + ISC_LOG_DEBUG(3), "keymgr: %s (offline-ksk) done", + namebuf); + } + return (result); +} diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 33c2dca112..e64a96869b 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -22177,7 +22177,7 @@ zone_rekey(dns_zone_t *zone) { isc_result_totext(result)); } - if (kasp != NULL) { + if (kasp != NULL && !offlineksk) { /* * Check DS at parental agents. Clear ongoing checks. */ @@ -22217,6 +22217,15 @@ zone_rekey(dns_zone_t *zone) { goto failure; } } + } else if (offlineksk) { + /* + * With offline-ksk enabled we don't run the keymgr. + * Instead we derive the states from the timing metadata. + */ + dns_zone_lock_keyfiles(zone); + result = dns_keymgr_offline(&zone->origin, &keys, kasp, now, + &nexttime); + dns_zone_unlock_keyfiles(zone); } KASP_UNLOCK(kasp); @@ -22235,6 +22244,25 @@ zone_rekey(dns_zone_t *zone) { bool sane_diff, sane_dnskey; isc_stdtime_t when; + result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys, + &zone->origin, ttl, &diff, mctx, + dnssec_report); + /* + * Keys couldn't be updated for some reason; + * try again later. + */ + if (result != ISC_R_SUCCESS) { + dnssec_log(zone, ISC_LOG_ERROR, + "zone_rekey:couldn't update zone keys: %s", + isc_result_totext(result)); + goto failure; + } + + if (offlineksk) { + /* We can skip a lot of things */ + goto post_sync; + } + /* * Publish CDS/CDNSKEY DELETE records if the zone is * transitioning from secure to insecure. @@ -22300,20 +22328,6 @@ zone_rekey(dns_zone_t *zone) { digests = dns_kasp_digests(zone->defaultkasp); } - result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys, - &zone->origin, ttl, &diff, mctx, - dnssec_report); - /* - * Keys couldn't be updated for some reason; - * try again later. - */ - if (result != ISC_R_SUCCESS) { - dnssec_log(zone, ISC_LOG_ERROR, - "zone_rekey:couldn't update zone keys: %s", - isc_result_totext(result)); - goto failure; - } - /* * Update CDS / CDNSKEY records. */ @@ -22366,6 +22380,7 @@ zone_rekey(dns_zone_t *zone) { goto failure; } + post_sync: /* * See if any pre-existing keys have newly become active; * also, see if any new key is for a new algorithm, as in that