mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-05 09:05:40 +00:00
Implement 'rndc dnssec -checkds'
Add a new 'rndc' command 'dnssec -checkds' that allows the user to signal named that a new DS record has been seen published in the parent, or that an existing DS record has been withdrawn from the parent. Upon the 'checkds' request, 'named' will write out the new state for the key, updating the 'DSPublish' or 'DSRemoved' timing metadata. This replaces the "parent-registration-delay" configuration option, this was unreliable because it was purely time based (if the user did not actually submit the new DS to the parent for example, this could result in an invalid DNSSEC state). Because we cannot rely on the parent registration delay for state transition, we need to replace it with a different guard. Instead, if a key wants its DS state to be moved to RUMOURED, the "DSPublish" time must be set and must not be in the future. If a key wants its DS state to be moved to UNRETENTIVE, the "DSRemoved" time must be set and must not be in the future. By default, with '-checkds' you set the time that the DS has been published or withdrawn to now, but you can set a different time with '-when'. If there is only one KSK for the zone, that key has its DS state moved to RUMOURED. If there are multiple keys for the zone, specify the right key with '-key'.
This commit is contained in:
135
lib/dns/keymgr.c
135
lib/dns/keymgr.c
@@ -128,9 +128,6 @@ keymgr_settime_remove(dns_dnsseckey_t *key, dns_kasp_t *kasp) {
|
||||
dns_kasp_parentpropagationdelay(kasp) +
|
||||
dns_kasp_retiresafety(kasp);
|
||||
}
|
||||
if (zsk && ksk) {
|
||||
ksk_remove += dns_kasp_parentregistrationdelay(kasp);
|
||||
}
|
||||
|
||||
remove = ksk_remove > zsk_remove ? ksk_remove : zsk_remove;
|
||||
dst_key_settime(key->key, DST_TIME_DELETE, remove);
|
||||
@@ -263,12 +260,6 @@ keymgr_prepublication_time(dns_dnsseckey_t *key, dns_kasp_t *kasp,
|
||||
* so ignore the result code.
|
||||
*/
|
||||
(void)dst_key_getbool(key->key, DST_BOOL_ZSK, &zsk);
|
||||
if (!zsk && ksk) {
|
||||
/*
|
||||
* Include registration delay in prepublication time.
|
||||
*/
|
||||
prepub += dns_kasp_parentregistrationdelay(kasp);
|
||||
}
|
||||
|
||||
ret = dst_key_gettime(key->key, DST_TIME_INACTIVE, &retire);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
@@ -965,10 +956,14 @@ keymgr_have_rrsig(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, int type,
|
||||
* - First introduce the DNSKEY record, as well as the KRRSIG records.
|
||||
* - Only if the DNSKEY record is OMNIPRESENT, suggest to introduce the DS.
|
||||
*
|
||||
* Also check the DS Publish or Delete times, to see if the DS record
|
||||
* already reached the parent.
|
||||
*/
|
||||
static bool
|
||||
keymgr_policy_approval(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
|
||||
int type, dst_key_state_t next) {
|
||||
int type, dst_key_state_t next, isc_stdtime_t now) {
|
||||
isc_result_t ret;
|
||||
isc_stdtime_t dstime;
|
||||
dst_key_state_t dnskeystate = HIDDEN;
|
||||
dst_key_state_t ksk_present[4] = { OMNIPRESENT, NA, OMNIPRESENT,
|
||||
OMNIPRESENT };
|
||||
@@ -980,10 +975,10 @@ keymgr_policy_approval(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
|
||||
dst_key_state_t ksk_retired[4] = { UNRETENTIVE, NA, NA, OMNIPRESENT };
|
||||
dst_key_state_t na[4] = { NA, NA, NA, NA }; /* successor n/a */
|
||||
|
||||
if (next != RUMOURED) {
|
||||
if (next != RUMOURED && next != UNRETENTIVE) {
|
||||
/*
|
||||
* Local policy only adds an extra barrier on transitions to
|
||||
* the RUMOURED state.
|
||||
* the RUMOURED and UNRETENTIVE states.
|
||||
*/
|
||||
return (true);
|
||||
}
|
||||
@@ -993,6 +988,9 @@ keymgr_policy_approval(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
|
||||
/* No restrictions. */
|
||||
return (true);
|
||||
case DST_KEY_ZRRSIG:
|
||||
if (next != RUMOURED) {
|
||||
return (true);
|
||||
}
|
||||
/* Make sure the DNSKEY record is OMNIPRESENT. */
|
||||
(void)dst_key_getstate(key->key, DST_KEY_DNSKEY, &dnskeystate);
|
||||
if (dnskeystate == OMNIPRESENT) {
|
||||
@@ -1013,13 +1011,35 @@ keymgr_policy_approval(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
|
||||
keyring, key, type, next, ksk_retired,
|
||||
ksk_rumoured, true, true)));
|
||||
case DST_KEY_KRRSIG:
|
||||
if (next != RUMOURED) {
|
||||
return (true);
|
||||
}
|
||||
/* Only introduce if the DNSKEY is also introduced. */
|
||||
(void)dst_key_getstate(key->key, DST_KEY_DNSKEY, &dnskeystate);
|
||||
return (dnskeystate != HIDDEN);
|
||||
case DST_KEY_DS:
|
||||
/* Make sure the DNSKEY record is OMNIPRESENT. */
|
||||
(void)dst_key_getstate(key->key, DST_KEY_DNSKEY, &dnskeystate);
|
||||
return (dnskeystate == OMNIPRESENT);
|
||||
if (next == RUMOURED) {
|
||||
/* Make sure the DNSKEY record is OMNIPRESENT. */
|
||||
(void)dst_key_getstate(key->key, DST_KEY_DNSKEY,
|
||||
&dnskeystate);
|
||||
if (dnskeystate != OMNIPRESENT) {
|
||||
return (false);
|
||||
}
|
||||
/* Make sure DS has been seen in the parent. */
|
||||
ret = dst_key_gettime(key->key, DST_TIME_DSPUBLISH,
|
||||
&dstime);
|
||||
if (ret != ISC_R_SUCCESS || dstime > now) {
|
||||
return (false);
|
||||
}
|
||||
} else if (next == UNRETENTIVE) {
|
||||
/* Make sure DS has been withdrawn from the parent. */
|
||||
ret = dst_key_gettime(key->key, DST_TIME_DSDELETE,
|
||||
&dstime);
|
||||
if (ret != ISC_R_SUCCESS || dstime > now) {
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
return (true);
|
||||
default:
|
||||
return (false);
|
||||
}
|
||||
@@ -1203,16 +1223,13 @@ keymgr_transition_time(dns_dnsseckey_t *key, int type,
|
||||
*
|
||||
* Iret = DprpP + TTLds
|
||||
*
|
||||
* So we need to wait Dreg + Iret before the DS becomes
|
||||
* OMNIPRESENT. This translates to:
|
||||
* This translates to:
|
||||
*
|
||||
* parent-registration-delay +
|
||||
* parent-propagation-delay + parent-ds-ttl.
|
||||
*
|
||||
* We will also add the retire-safety interval.
|
||||
*/
|
||||
nexttime = lastchange + dns_kasp_dsttl(kasp) +
|
||||
dns_kasp_parentregistrationdelay(kasp) +
|
||||
dns_kasp_parentpropagationdelay(kasp) +
|
||||
dns_kasp_retiresafety(kasp);
|
||||
break;
|
||||
@@ -1302,7 +1319,7 @@ transition:
|
||||
|
||||
/* Is the transition allowed according to policy? */
|
||||
if (!keymgr_policy_approval(keyring, dkey, i,
|
||||
next_state)) {
|
||||
next_state, now)) {
|
||||
/* No, please respect rollover methods. */
|
||||
isc_log_write(
|
||||
dns_lctx, DNS_LOGCATEGORY_DNSSEC,
|
||||
@@ -1433,7 +1450,6 @@ keymgr_key_init(dns_dnsseckey_t *key, dns_kasp_t *kasp, isc_stdtime_t now) {
|
||||
ret = dst_key_gettime(key->key, DST_TIME_SYNCPUBLISH, &syncpub);
|
||||
if (syncpub <= now && ret == ISC_R_SUCCESS) {
|
||||
dns_ttl_t ds_ttl = dns_kasp_dsttl(kasp);
|
||||
ds_ttl += dns_kasp_parentregistrationdelay(kasp);
|
||||
ds_ttl += dns_kasp_parentpropagationdelay(kasp);
|
||||
if ((syncpub + ds_ttl) <= now) {
|
||||
ds_state = OMNIPRESENT;
|
||||
@@ -1854,6 +1870,83 @@ failure:
|
||||
return (result);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring,
|
||||
const char *directory, isc_stdtime_t now, bool dspublish,
|
||||
dns_keytag_t id, bool check_id) {
|
||||
int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE);
|
||||
isc_dir_t dir;
|
||||
isc_result_t result;
|
||||
dns_dnsseckey_t *ksk_key = NULL;
|
||||
|
||||
REQUIRE(DNS_KASP_VALID(kasp));
|
||||
REQUIRE(keyring != NULL);
|
||||
|
||||
for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL;
|
||||
dkey = ISC_LIST_NEXT(dkey, link))
|
||||
{
|
||||
isc_result_t ret;
|
||||
bool ksk = false;
|
||||
|
||||
ret = dst_key_getbool(dkey->key, DST_BOOL_KSK, &ksk);
|
||||
if (ret == ISC_R_SUCCESS && ksk) {
|
||||
if (check_id && dst_key_id(dkey->key) != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ksk_key != NULL) {
|
||||
/*
|
||||
* Only checkds for one key at a time.
|
||||
*/
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
|
||||
ksk_key = dkey;
|
||||
}
|
||||
}
|
||||
|
||||
if (ksk_key == NULL) {
|
||||
return (ISC_R_NOTFOUND);
|
||||
}
|
||||
|
||||
if (dspublish) {
|
||||
dst_key_settime(ksk_key->key, DST_TIME_DSPUBLISH, now);
|
||||
} else {
|
||||
dst_key_settime(ksk_key->key, DST_TIME_DSDELETE, now);
|
||||
}
|
||||
|
||||
/* Store key state and update hints. */
|
||||
isc_dir_init(&dir);
|
||||
if (directory == NULL) {
|
||||
directory = ".";
|
||||
}
|
||||
result = isc_dir_open(&dir, directory);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
dns_dnssec_get_hints(ksk_key, now);
|
||||
result = dst_key_tofile(ksk_key->key, options, directory);
|
||||
isc_dir_close(&dir);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring,
|
||||
const char *directory, isc_stdtime_t now, bool dspublish) {
|
||||
return (keymgr_checkds(kasp, keyring, directory, now, dspublish, 0,
|
||||
false));
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_keymgr_checkds_id(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring,
|
||||
const char *directory, isc_stdtime_t now, bool dspublish,
|
||||
dns_keytag_t id) {
|
||||
return (keymgr_checkds(kasp, keyring, directory, now, dspublish, id,
|
||||
true));
|
||||
}
|
||||
|
||||
static void
|
||||
keytime_status(dst_key_t *key, isc_stdtime_t now, isc_buffer_t *buf,
|
||||
const char *pre, int ks, int kt) {
|
||||
|
Reference in New Issue
Block a user