Merge branch '1750-dnssec-policy-none' into 'main'

Resolve 'dnssec-policy' graceful transition to insecure

Closes #2341 and #1750

See merge request isc-projects/bind9!4474
This commit is contained in:
Matthijs Mekking 2020-12-23 10:55:20 +00:00
commit 1c26ab64a4
24 changed files with 869 additions and 182 deletions

View file

@ -1,3 +1,12 @@
5553. [bug] When reconfiguring named, removing "auto-dnssec"
did not actually turn off DNSSEC maintenance.
This has been fixed. [GL #2341]
5552. [func] When switching to "dnssec-policy none;", named
now permits a safe transition to insecure mode
and publishes the CDS and CDNSKEY DELETE
records, as described in RFC 8078. [GL #1750]
5551. [bug] Only assign threads to CPUs in the CPU affinity set.
Thanks to Ole Bjørn Hessen. [GL #2245]

View file

@ -269,8 +269,8 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name,
continue;
}
result = cfg_kasp_fromconfig(kconfig, mctx, lctx, &kasplist,
&kasp);
result = cfg_kasp_fromconfig(kconfig, NULL, mctx, lctx,
&kasplist, &kasp);
if (result != ISC_R_SUCCESS) {
fatal("failed to configure dnssec-policy '%s': %s",
cfg_obj_asstring(cfg_tuple_get(kconfig, "name")),
@ -284,7 +284,7 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name,
*kaspp = kasp;
/*
* Same cleanup for kasp list.
* Cleanup kasp list.
*/
for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) {
kasp_next = ISC_LIST_NEXT(kasp, link);
@ -782,7 +782,7 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) {
}
/* Set dnssec-policy related metadata */
if (ctx->policy) {
if (ctx->policy != NULL) {
dst_key_setnum(key, DST_NUM_LIFETIME, ctx->lifetime);
dst_key_setbool(key, DST_BOOL_KSK, ctx->ksk);
dst_key_setbool(key, DST_BOOL_ZSK, ctx->zsk);

View file

@ -9121,18 +9121,25 @@ load_configuration(const char *filename, named_server_t *server,
{
cfg_obj_t *kconfig = cfg_listelt_value(element);
kasp = NULL;
CHECK(cfg_kasp_fromconfig(kconfig, named_g_mctx, named_g_lctx,
&kasplist, &kasp));
CHECK(cfg_kasp_fromconfig(kconfig, NULL, named_g_mctx,
named_g_lctx, &kasplist, &kasp));
INSIST(kasp != NULL);
dns_kasp_freeze(kasp);
dns_kasp_detach(&kasp);
}
/*
* Create the default kasp.
* Create the built-in kasp policies ("default", "none").
*/
kasp = NULL;
CHECK(cfg_kasp_fromconfig(NULL, named_g_mctx, named_g_lctx, &kasplist,
&kasp));
CHECK(cfg_kasp_fromconfig(NULL, "default", named_g_mctx, named_g_lctx,
&kasplist, &kasp));
INSIST(kasp != NULL);
dns_kasp_freeze(kasp);
dns_kasp_detach(&kasp);
kasp = NULL;
CHECK(cfg_kasp_fromconfig(NULL, "none", named_g_mctx, named_g_lctx,
&kasplist, &kasp));
INSIST(kasp != NULL);
dns_kasp_freeze(kasp);
dns_kasp_detach(&kasp);
@ -14528,7 +14535,6 @@ named_server_signing(named_server_t *server, isc_lex_t *lex,
isc_buffer_t **text) {
isc_result_t result = ISC_R_SUCCESS;
dns_zone_t *zone = NULL;
dns_kasp_t *kasp = NULL;
dns_name_t *origin;
dns_db_t *db = NULL;
dns_dbnode_t *node = NULL;
@ -14647,8 +14653,7 @@ named_server_signing(named_server_t *server, isc_lex_t *lex,
CHECK(ISC_R_UNEXPECTEDEND);
}
kasp = dns_zone_getkasp(zone);
if (kasp != NULL) {
if (dns_zone_use_kasp(zone)) {
(void)putstr(text, "zone uses dnssec-policy, use rndc dnssec "
"command instead");
(void)putnull(text);

View file

@ -913,6 +913,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
bool check = false, fail = false;
bool warn = false, ignore = false;
bool ixfrdiff;
bool use_kasp = false;
dns_masterformat_t masterformat;
const dns_master_style_t *masterstyle = &dns_master_style_default;
isc_stats_t *zoneqrystats;
@ -1248,19 +1249,15 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
result = named_config_get(maps, "dnssec-policy", &obj);
if (result == ISC_R_SUCCESS) {
kaspname = cfg_obj_asstring(obj);
if (strcmp(kaspname, "none") != 0) {
result = dns_kasplist_find(kasplist, kaspname,
&kasp);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(obj, named_g_lctx,
ISC_LOG_ERROR,
"'dnssec-policy '%s' not "
"found ",
kaspname);
RETERR(result);
}
dns_zone_setkasp(zone, kasp);
result = dns_kasplist_find(kasplist, kaspname, &kasp);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
"'dnssec-policy '%s' not found ",
kaspname);
RETERR(result);
}
dns_zone_setkasp(zone, kasp);
use_kasp = dns_zone_use_kasp(zone);
}
obj = NULL;
@ -1561,7 +1558,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
bool allow = false, maint = false;
bool sigvalinsecs;
if (kasp != NULL) {
if (use_kasp) {
if (dns_kasp_nsec3(kasp)) {
result = dns_zone_setnsec3param(
zone, 1, dns_kasp_nsec3flags(kasp),
@ -1575,7 +1572,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
INSIST(result == ISC_R_SUCCESS);
}
if (kasp != NULL) {
if (use_kasp) {
seconds = (uint32_t)dns_kasp_sigvalidity_dnskey(kasp);
} else {
obj = NULL;
@ -1586,7 +1583,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
}
dns_zone_setkeyvalidityinterval(zone, seconds);
if (kasp != NULL) {
if (use_kasp) {
seconds = (uint32_t)dns_kasp_sigvalidity(kasp);
dns_zone_setsigvalidityinterval(zone, seconds);
seconds = (uint32_t)dns_kasp_sigrefresh(kasp);
@ -1673,7 +1670,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
obj = NULL;
result = cfg_map_get(zoptions, "auto-dnssec", &obj);
if (dns_zone_getkasp(zone) != NULL) {
if (kasp != NULL && strcmp(dns_kasp_getname(kasp), "none") != 0)
{
dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, true);
dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, true);
dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, true);
@ -1692,6 +1690,11 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, allow);
dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, false);
dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, maint);
} else {
bool s2i = dns_zone_secure_to_insecure(zone, false);
dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, s2i);
dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, false);
dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, s2i);
}
}
@ -2139,11 +2142,15 @@ named_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig,
bool
named_zone_inlinesigning(dns_zone_t *zone, const cfg_obj_t *zconfig,
const cfg_obj_t *vconfig, const cfg_obj_t *config) {
isc_result_t res;
const cfg_obj_t *zoptions = NULL;
const cfg_obj_t *voptions = NULL;
const cfg_obj_t *options = NULL;
const cfg_obj_t *signing = NULL;
bool inline_signing;
const cfg_obj_t *allowupdate = NULL;
const cfg_obj_t *updatepolicy = NULL;
bool zone_is_dynamic = false;
bool inline_signing = false;
(void)cfg_map_get(config, "options", &options);
@ -2155,6 +2162,38 @@ named_zone_inlinesigning(dns_zone_t *zone, const cfg_obj_t *zconfig,
inline_signing = (cfg_map_get(zoptions, "inline-signing", &signing) ==
ISC_R_SUCCESS &&
cfg_obj_asboolean(signing));
if (inline_signing) {
return (true);
}
if (cfg_map_get(zoptions, "update-policy", &updatepolicy) ==
ISC_R_SUCCESS) {
zone_is_dynamic = true;
} else {
res = cfg_map_get(zoptions, "allow-update", &allowupdate);
if (res != ISC_R_SUCCESS && voptions != NULL) {
res = cfg_map_get(voptions, "allow-update",
&allowupdate);
}
if (res != ISC_R_SUCCESS && options != NULL) {
res = cfg_map_get(options, "allow-update",
&allowupdate);
}
if (res == ISC_R_SUCCESS) {
dns_acl_t *acl = NULL;
cfg_aclconfctx_t *actx = NULL;
res = cfg_acl_fromconfig(
allowupdate, config, named_g_lctx, actx,
dns_zone_getmctx(zone), 0, &acl);
if (res == ISC_R_SUCCESS && acl != NULL &&
!dns_acl_isnone(acl)) {
zone_is_dynamic = true;
}
if (acl != NULL) {
dns_acl_detach(&acl);
}
}
}
/*
* If inline-signing is not set, perhaps implictly through a
@ -2164,51 +2203,24 @@ named_zone_inlinesigning(dns_zone_t *zone, const cfg_obj_t *zconfig,
* inline-signing.
*/
signing = NULL;
if (!inline_signing &&
if (!inline_signing && !zone_is_dynamic &&
cfg_map_get(zoptions, "dnssec-policy", &signing) == ISC_R_SUCCESS &&
signing != NULL && strcmp(cfg_obj_asstring(signing), "none") != 0)
signing != NULL)
{
{
isc_result_t res;
bool zone_is_dynamic = false;
const cfg_obj_t *au = NULL;
const cfg_obj_t *up = NULL;
if (cfg_map_get(zoptions, "update-policy", &up) ==
ISC_R_SUCCESS) {
zone_is_dynamic = true;
} else {
res = cfg_map_get(zoptions, "allow-update",
&au);
if (res != ISC_R_SUCCESS && voptions != NULL) {
res = cfg_map_get(voptions,
"allow-update", &au);
}
if (res != ISC_R_SUCCESS && options != NULL) {
res = cfg_map_get(options,
"allow-update", &au);
}
if (res == ISC_R_SUCCESS) {
dns_acl_t *acl = NULL;
cfg_aclconfctx_t *actx = NULL;
res = cfg_acl_fromconfig(
au, config, named_g_lctx, actx,
dns_zone_getmctx(zone), 0,
&acl);
if (res == ISC_R_SUCCESS &&
acl != NULL && !dns_acl_isnone(acl))
{
zone_is_dynamic = true;
}
if (acl != NULL) {
dns_acl_detach(&acl);
}
}
}
if (!zone_is_dynamic) {
inline_signing = true;
}
if (strcmp(cfg_obj_asstring(signing), "none") != 0) {
inline_signing = true;
dns_zone_log(
zone, ISC_LOG_DEBUG(1), "inline-signing: %s",
inline_signing
? "implicitly through dnssec-policy"
: "no");
} else {
inline_signing = dns_zone_secure_to_insecure(zone,
true);
dns_zone_log(
zone, ISC_LOG_DEBUG(1), "inline-signing: %s",
inline_signing ? "transitioning to insecure"
: "no");
}
}

View file

@ -64,6 +64,20 @@ zone "migrate-nomatch-alglen.kasp" {
update-check-ksk yes;
};
/* These zones are going insecure. */
zone "step1.going-insecure.kasp" {
type master;
file "step1.going-insecure.kasp.db";
dnssec-policy "migrate";
};
zone "step1.going-insecure-dynamic.kasp" {
type master;
file "step1.going-insecure-dynamic.kasp.db";
dnssec-policy "migrate";
allow-update { any; };
};
/* These are alorithm rollover test zones. */
zone "step1.algorithm-roll.kasp" {
type primary;

View file

@ -57,6 +57,33 @@ zone "migrate-nomatch-alglen.kasp" {
dnssec-policy "migrate-nomatch-alglen";
};
/* Zones for testing going insecure. */
zone "step1.going-insecure.kasp" {
type master;
file "step1.going-insecure.kasp.db";
dnssec-policy "none";
};
zone "step2.going-insecure.kasp" {
type master;
file "step2.going-insecure.kasp.db";
dnssec-policy "none";
};
zone "step1.going-insecure-dynamic.kasp" {
type master;
file "step1.going-insecure-dynamic.kasp.db";
dnssec-policy "none";
allow-update { any; };
};
zone "step2.going-insecure-dynamic.kasp" {
type master;
file "step2.going-insecure-dynamic.kasp.db";
dnssec-policy "none";
allow-update { any; };
};
/*
* Zones for testing KSK/ZSK algorithm roll.
*/

View file

@ -86,6 +86,46 @@ private_type_record $zone 5 "$KSK" >> "$infile"
private_type_record $zone 5 "$ZSK" >> "$infile"
$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
# The child zones (step1, step2) beneath these zones represent the various
# steps of unsigning a zone.
for zn in going-insecure.kasp going-insecure-dynamic.kasp
do
# Step 1:
# Set up a zone with dnssec-policy that is going insecure.
setup step1.$zn
echo "$zone" >> zones
T="now-10d"
ksktimes="-P $T -A $T -P sync $T"
zsktimes="-P $T -A $T"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2> keygen.out.$zone.1)
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2> keygen.out.$zone.2)
cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >> "$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >> "$infile"
$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
# Step 2:
# Set up a zone with dnssec-policy that is going insecure. Don't add
# this zone to the zones file, because this zone is no longer expected
# to be fully signed.
setup step2.$zn
# The DS was withdrawn from the parent zone 26 hours ago.
Trem="now-26h"
ksktimes="-P $T -A $T -P sync $T"
zsktimes="-P $T -A $T"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2> keygen.out.$zone.1)
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2> keygen.out.$zone.2)
$SETTIME -s -g $H -k $O $T -r $O $T -d $U $Trem -D ds $Trem "$KSK" > settime.out.$zone.1 2>&1
$SETTIME -s -g $H -k $O $T -z $O $T "$ZSK" > settime.out.$zone.2 2>&1
# Fake lifetime of old algorithm keys.
echo "Lifetime: 0" >> "${KSK}.state"
echo "Lifetime: 5184000" >> "${ZSK}.state"
cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >> "$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >> "$infile"
$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
done
#
# The zones at algorithm-roll.kasp represent the various steps of a ZSK/KSK
# algorithm rollover.

View file

@ -172,12 +172,26 @@ set_server() {
# Set zone name for testing keys.
set_zone() {
ZONE=$1
DYNAMIC="no"
}
# By default zones are considered static.
# When testing dynamic zones, call 'set_dynamic' after 'set_zone'.
set_dynamic() {
DYNAMIC="yes"
}
# Set policy settings (name $1, number of keys $2, dnskey ttl $3) for testing keys.
set_policy() {
POLICY=$1
NUM_KEYS=$2
DNSKEY_TTL=$3
CDS_DELETE="no"
}
# By default policies are considered to be secure.
# If a zone sets its policy to "none", call 'set_cdsdelete' to tell the system
# test to expect a CDS and CDNSKEY Delete record.
set_cdsdelete() {
CDS_DELETE="yes"
}
# Set key properties for testing keys.
@ -946,22 +960,18 @@ check_dnssecstatus() {
rndccmd $_server dnssec -status $_zone in $_view > rndc.dnssec.status.out.$_zone.$n || log_error "rndc dnssec -status zone ${_zone} failed"
if [ "$_policy" = "none" ]; then
grep "Zone does not have dnssec-policy" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "bad dnssec status for unsigned zone ${_zone}"
else
grep "dnssec-policy: ${_policy}" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "bad dnssec status for signed zone ${_zone}"
if [ "$(key_get KEY1 EXPECT)" = "yes" ]; then
grep "key: $(key_get KEY1 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "missing key $(key_get KEY1 ID) from dnssec status"
fi
if [ "$(key_get KEY2 EXPECT)" = "yes" ]; then
grep "key: $(key_get KEY2 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "missing key $(key_get KEY2 ID) from dnssec status"
fi
if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then
grep "key: $(key_get KEY3 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "missing key $(key_get KEY3 ID) from dnssec status"
fi
if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then
grep "key: $(key_get KEY4 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "missing key $(key_get KEY4 ID) from dnssec status"
fi
grep "dnssec-policy: ${_policy}" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "bad dnssec status for signed zone ${_zone}"
if [ "$(key_get KEY1 EXPECT)" = "yes" ]; then
grep "key: $(key_get KEY1 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "missing key $(key_get KEY1 ID) from dnssec status"
fi
if [ "$(key_get KEY2 EXPECT)" = "yes" ]; then
grep "key: $(key_get KEY2 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "missing key $(key_get KEY2 ID) from dnssec status"
fi
if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then
grep "key: $(key_get KEY3 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "missing key $(key_get KEY3 ID) from dnssec status"
fi
if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then
grep "key: $(key_get KEY4 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "missing key $(key_get KEY4 ID) from dnssec status"
fi
test "$ret" -eq 0 || echo_i "failed"
@ -1044,6 +1054,14 @@ check_cds() {
dig_with_opts "$ZONE" "@${SERVER}" "CDNSKEY" > "dig.out.$DIR.test$n.cdnskey" || log_error "dig ${ZONE} CDNSKEY failed"
grep "status: NOERROR" "dig.out.$DIR.test$n.cdnskey" > /dev/null || log_error "mismatch status in DNS response"
if [ "$CDS_DELETE" = "no" ]; then
grep "CDS.*0 0 0 00" "dig.out.$DIR.test$n.cds" > /dev/null && log_error "unexpected CDS DELETE record in DNS response"
grep "CDNSKEY.*0 3 0 AA==" "dig.out.$DIR.test$n.cdnskey" > /dev/null && log_error "unexpected CDNSKEY DELETE record in DNS response"
else
grep "CDS.*0 0 0 00" "dig.out.$DIR.test$n.cds" > /dev/null || log_error "missing CDS DELETE record in DNS response"
grep "CDNSKEY.*0 3 0 AA==" "dig.out.$DIR.test$n.cdnskey" > /dev/null || log_error "missing CDNSKEY DELETE record in DNS response"
fi
if [ "$(key_get KEY1 STATE_DS)" = "rumoured" ] || [ "$(key_get KEY1 STATE_DS)" = "omnipresent" ]; then
response_has_cds_for_key KEY1 "dig.out.$DIR.test$n.cds" || log_error "missing CDS record in response for key $(key_get KEY1 ID)"
check_signatures "CDS" "dig.out.$DIR.test$n.cds" "KSK"
@ -1210,7 +1228,13 @@ _loadkeys_on() {
nextpart $_dir/named.run > /dev/null
rndccmd $_server loadkeys $_zone in $_view > rndc.dnssec.loadkeys.out.$_zone.$n
wait_for_log 20 "zone ${_zone}/IN (signed): next key event" $_dir/named.run || return 1
if [ "${DYNAMIC}" = "yes" ]; then
wait_for_log 20 "zone ${_zone}/IN: next key event" $_dir/named.run || return 1
else
# inline-signing zone adds "(signed)"
wait_for_log 20 "zone ${_zone}/IN (signed): next key event" $_dir/named.run || return 1
fi
}
# Tell named that the DS for the key in given zone has been seen in the
@ -1228,21 +1252,23 @@ rndc_checkds() {
_keycmd=""
if [ "${_key}" != "-" ]; then
_keyid=$(key_get $_key ID)
_keycmd="-key ${_keyid}"
_keycmd=" -key ${_keyid}"
fi
_whencmd=""
if [ "${_when}" != "now" ]; then
_whencmd="-when ${_when}"
_whencmd=" -when ${_when}"
fi
n=$((n+1))
echo_i "calling rndc dnssec -checkds ${_keycmd} ${_whencmd} ${_what} zone ${_zone} ($n)"
echo_i "calling rndc dnssec -checkds${_keycmd}${_whencmd} ${_what} zone ${_zone} in ${_view} ($n)"
ret=0
rndccmd $_server dnssec -checkds $_keycmd $_whencmd $_what $_zone in $_view > rndc.dnssec.checkds.out.$_zone.$n || log_error "rndc dnssec -checkds (${_keycmd} ${_whencmd} ${_what} zone ${_zone} failed"
rndccmd $_server dnssec -checkds $_keycmd $_whencmd $_what $_zone in $_view > rndc.dnssec.checkds.out.$_zone.$n || log_error "rndc dnssec -checkds${_keycmd}${_whencmd} ${_what} zone ${_zone} failed"
_loadkeys_on $_server $_dir $_zone || log_error "loadkeys zone ${_zone} failed ($n)"
if [ "$ret" -eq 0 ]; then
_loadkeys_on $_server $_dir $_zone || log_error "loadkeys zone ${_zone} failed ($n)"
fi
test "$ret" -eq 0 || echo_i "failed"
status=$((status+ret))
@ -1346,6 +1372,7 @@ status=$((status+ret))
# Zone: dynamic.kasp
#
set_zone "dynamic.kasp"
set_dynamic
set_policy "default" "1" "3600"
set_server "ns3" "10.53.0.3"
# Key properties, timings and states same as above.
@ -1378,6 +1405,7 @@ status=$((status+ret))
# Zone: dynamic-inline-signing.kasp
#
set_zone "dynamic-inline-signing.kasp"
set_dynamic
set_policy "default" "1" "3600"
set_server "ns3" "10.53.0.3"
# Key properties, timings and states same as above.
@ -2937,7 +2965,12 @@ check_next_key_event() {
grep "zone ${ZONE}.*: next key event in .* seconds" "${DIR}/named.run" > "keyevent.out.$ZONE.test$n" || log_error "no next key event for zone ${ZONE}"
# Get the latest next key event.
_time=$(awk '{print $10}' < "keyevent.out.$ZONE.test$n" | tail -1)
if [ "${DYNAMIC}" = "yes" ]; then
_time=$(awk '{print $9}' < "keyevent.out.$ZONE.test$n" | tail -1)
else
# inline-signing zone adds "(signed)"
_time=$(awk '{print $10}' < "keyevent.out.$ZONE.test$n" | tail -1)
fi
# The next key event time must within threshold of the
# expected time.
@ -4452,6 +4485,90 @@ dnssec_verify
_migratenomatch_alglen_ksk=$(key_get KEY1 ID)
_migratenomatch_alglen_zsk=$(key_get KEY2 ID)
#
# Testing going insecure.
#
#
# Zone step1.going-insecure.kasp
#
set_zone "step1.going-insecure.kasp"
set_policy "migrate" "2" "7200"
set_server "ns6" "10.53.0.6"
# Policy parameters.
# Lksk: 0
# Lzsk: 60 days (5184000 seconds)
# Iret(KSK): DS TTL (1d) + DprpP (1h) + retire-safety (1h)
# Iret(KSK): 1d2h (93600 seconds)
# Iret(ZSK): RRSIG TTL (1d) + Dprp (5m) + Dsgn (9d) + retire-safety (1h)
# Iret(ZSK): 10d1h5m (867900 seconds)
Lksk=0
Lzsk=5184000
IretKSK=93600
IretZSK=867900
init_migration_insecure() {
key_clear "KEY1"
set_keyrole "KEY1" "ksk"
set_keylifetime "KEY1" "${Lksk}"
set_keyalgorithm "KEY1" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
set_keysigning "KEY1" "yes"
set_zonesigning "KEY1" "no"
set_keystate "KEY1" "GOAL" "omnipresent"
set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_DS" "omnipresent"
key_clear "KEY2"
set_keyrole "KEY2" "zsk"
set_keylifetime "KEY2" "${Lzsk}"
set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
set_keysigning "KEY2" "no"
set_zonesigning "KEY2" "yes"
set_keystate "KEY2" "GOAL" "omnipresent"
set_keystate "KEY2" "STATE_DNSKEY" "omnipresent"
set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent"
key_clear "KEY3"
key_clear "KEY4"
}
init_migration_insecure
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
# We have set the timing metadata to now - 10 days (864000 seconds).
rollover_predecessor_keytimes -864000
check_keytimes
check_apex
check_subdomain
dnssec_verify
#
# Zone step1.going-insecure-dynamic.kasp
#
set_zone "step1.going-insecure-dynamic.kasp"
set_dynamic
set_policy "migrate" "2" "7200"
set_server "ns6" "10.53.0.6"
init_migration_insecure
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
# We have set the timing metadata to now - 10 days (864000 seconds).
rollover_predecessor_keytimes -864000
check_keytimes
check_apex
check_subdomain
dnssec_verify
# Reconfig dnssec-policy (triggering algorithm roll and other dnssec-policy
# changes).
echo_i "reconfig dnssec-policy to trigger algorithm rollover"
@ -4501,6 +4618,148 @@ wait_for_done_signing() {
status=$((status+ret))
}
#
# Testing going insecure.
#
#
# Zone: step1.going-insecure.kasp
#
set_zone "step1.going-insecure.kasp"
set_policy "none" "2" "7200"
set_server "ns6" "10.53.0.6"
# Expect a CDS/CDNSKEY Delete Record.
set_cdsdelete
# Key goal states should be HIDDEN.
init_migration_insecure
set_keystate "KEY1" "GOAL" "hidden"
set_keystate "KEY2" "GOAL" "hidden"
# The DS may be removed if we are going insecure.
set_keystate "KEY1" "STATE_DS" "unretentive"
# Various signing policy checks.
check_keys
wait_for_done_signing
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
dnssec_verify
# Tell named that the DS has been removed.
rndc_checkds "$SERVER" "$DIR" "KEY1" "now" "withdrawn" "$ZONE"
wait_for_done_signing
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
dnssec_verify
# Next key event is when the DS becomes HIDDEN. This happens after the
# parent propagation delay, retire safety delay, and DS TTL:
# 1h + 1h + 1d = 26h = 93600 seconds.
check_next_key_event 93600
#
# Zone: step2.going-insecure.kasp
#
set_zone "step2.going-insecure.kasp"
set_policy "none" "2" "7200"
set_server "ns6" "10.53.0.6"
# Expect a CDS/CDNSKEY Delete Record.
set_cdsdelete
# The DS is long enough removed from the zone to be considered HIDDEN.
# This means the DNSKEY and the KSK signatures can be removed.
set_keystate "KEY1" "STATE_DS" "hidden"
set_keystate "KEY1" "STATE_DNSKEY" "unretentive"
set_keystate "KEY1" "STATE_KRRSIG" "unretentive"
set_keysigning "KEY1" "no"
set_keystate "KEY2" "STATE_DNSKEY" "unretentive"
set_keystate "KEY2" "STATE_ZRRSIG" "unretentive"
set_zonesigning "KEY2" "no"
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
# Next key event is when the DNSKEY becomes HIDDEN. This happens after the
# propagation delay, plus DNSKEY TTL:
# 5m + 2h = 125m = 7500 seconds.
check_next_key_event 7500
#
# Zone: step1.going-insecure-dynamic.kasp
#
set_zone "step1.going-insecure-dynamic.kasp"
set_dynamic
set_policy "none" "2" "7200"
set_server "ns6" "10.53.0.6"
# Expect a CDS/CDNSKEY Delete Record.
set_cdsdelete
# Key goal states should be HIDDEN.
init_migration_insecure
set_keystate "KEY1" "GOAL" "hidden"
set_keystate "KEY2" "GOAL" "hidden"
# The DS may be removed if we are going insecure.
set_keystate "KEY1" "STATE_DS" "unretentive"
# Various signing policy checks.
check_keys
wait_for_done_signing
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
dnssec_verify
# Tell named that the DS has been removed.
rndc_checkds "$SERVER" "$DIR" "KEY1" "now" "withdrawn" "$ZONE"
wait_for_done_signing
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
dnssec_verify
# Next key event is when the DS becomes HIDDEN. This happens after the
# parent propagation delay, retire safety delay, and DS TTL:
# 1h + 1h + 1d = 26h = 93600 seconds.
check_next_key_event 93600
#
# Zone: step2.going-insecure-dynamic.kasp
#
set_zone "step2.going-insecure-dynamic.kasp"
set_dynamic
set_policy "none" "2" "7200"
set_server "ns6" "10.53.0.6"
# Expect a CDS/CDNSKEY Delete Record.
set_cdsdelete
# The DS is long enough removed from the zone to be considered HIDDEN.
# This means the DNSKEY and the KSK signatures can be removed.
set_keystate "KEY1" "STATE_DS" "hidden"
set_keystate "KEY1" "STATE_DNSKEY" "unretentive"
set_keystate "KEY1" "STATE_KRRSIG" "unretentive"
set_keysigning "KEY1" "no"
set_keystate "KEY2" "STATE_DNSKEY" "unretentive"
set_keystate "KEY2" "STATE_ZRRSIG" "unretentive"
set_zonesigning "KEY2" "no"
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
# Next key event is when the DNSKEY becomes HIDDEN. This happens after the
# propagation delay, plus DNSKEY TTL:
# 5m + 2h = 125m = 7500 seconds.
check_next_key_event 7500
#
# Testing migration.
#

View file

@ -462,7 +462,9 @@ grep "example..*.RRSIG..*TXT" dig.out.ns2.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
echo_i "reset the root server"
n=$((n+1))
echo_i "reset the root server ($n)"
ret=0
$SETTIME -D none -R none -K ns1 "$original" > /dev/null
$SETTIME -D now -K ns1 "$standby1" > /dev/null
$SETTIME -D now -K ns1 "$standby2" > /dev/null
@ -470,6 +472,9 @@ $SIGNER -Sg -K ns1 -N unixtime -o . ns1/root.db > /dev/null 2>/dev/null
copy_setports ns1/named2.conf.in ns1/named.conf
rm -f ns1/root.db.signed.jnl
mkeys_reconfig_on 1 || ret=1
mkeys_reload_on 1 || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
echo_i "reinitialize trust anchors"
stop_server --use-rndc --port "${CONTROLPORT}" mkeys ns2

View file

@ -98,6 +98,7 @@ or Best Current Practice (BCP) documents. The list is non exhaustive.
RFC7793
RFC7830 [15]
RFC7929
RFC8078 [20]
RFC8080
RFC8880

View file

@ -34,7 +34,11 @@ Removed Features
Feature Changes
~~~~~~~~~~~~~~~
- ``ipv4only.arpa`` is now served when ``dns64`` is configured. [GL #385]
- It is now possible to transition a zone from secure to insecure mode
without making it bogus in the process: changing to ``dnssec-policy
none;`` also causes CDS and CDNSKEY DELETE records to be published, to
signal that the entire DS RRset at the parent must be removed, as
described in RFC 8078. [GL #1750]
- When using the ``unixtime`` or ``date`` method to update the SOA
serial number, ``named`` and ``dnssec-signzone`` silently fell back to
@ -46,6 +50,5 @@ Feature Changes
Bug Fixes
~~~~~~~~~
- Only assign threads to CPUs in the CPU affinity set, so that ``named`` no
longer attempts to run threads on CPUs outside the affinity set. Thanks to
Ole Bjørn Hessen. [GL #2245]
- When reconfiguring ``named``, removing ``auto-dnssec`` did actually not turn
off DNSSEC maintenance. This has been fixed. [GL #2341]

View file

@ -1015,9 +1015,9 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx,
continue;
}
ret = cfg_kasp_fromconfig(kconfig, mctx,
logctx, &list,
&kasp);
ret = cfg_kasp_fromconfig(kconfig, NULL,
mctx, logctx,
&list, &kasp);
if (ret != ISC_R_SUCCESS) {
if (result == ISC_R_SUCCESS) {
result = ret;

View file

@ -2010,24 +2010,50 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
}
}
if (dns_rdataset_isassociated(cds) && syncdelete(key->key, now))
{
/* Delete both SHA-1 and SHA-256 */
if (exists(cds, &cds_sha1)) {
RETERR(delrdata(&cds_sha1, diff, origin,
cds->ttl, mctx));
}
if (exists(cds, &cds_sha256)) {
RETERR(delrdata(&cds_sha256, diff, origin,
cds->ttl, mctx));
}
}
if (syncdelete(key->key, now)) {
char keystr[DST_KEY_FORMATSIZE];
dst_key_format(key->key, keystr, sizeof(keystr));
if (dns_rdataset_isassociated(cdnskey) &&
syncdelete(key->key, now)) {
if (exists(cdnskey, &cdnskeyrdata)) {
RETERR(delrdata(&cdnskeyrdata, diff, origin,
cdnskey->ttl, mctx));
if (dns_rdataset_isassociated(cds)) {
/* Delete both SHA-1 and SHA-256 */
if (exists(cds, &cds_sha1)) {
isc_log_write(dns_lctx,
DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC,
ISC_LOG_INFO,
"CDS (SHA-1) for key %s "
"is now deleted",
keystr);
RETERR(delrdata(&cds_sha1, diff, origin,
cds->ttl, mctx));
}
if (exists(cds, &cds_sha256)) {
isc_log_write(dns_lctx,
DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC,
ISC_LOG_INFO,
"CDS (SHA-256) for key "
"%s is now deleted",
keystr);
RETERR(delrdata(&cds_sha256, diff,
origin, cds->ttl,
mctx));
}
}
if (dns_rdataset_isassociated(cdnskey)) {
if (exists(cdnskey, &cdnskeyrdata)) {
isc_log_write(dns_lctx,
DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC,
ISC_LOG_INFO,
"CDNSKEY for key %s is "
"now deleted",
keystr);
RETERR(delrdata(&cdnskeyrdata, diff,
origin, cdnskey->ttl,
mctx));
}
}
}
}
@ -2047,6 +2073,9 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT;
dns_name_t *origin = dst_key_name(key->key);
char keystr[DST_KEY_FORMATSIZE];
dst_key_format(key->key, keystr, sizeof(keystr));
RETERR(make_dnskey(key->key, keybuf, sizeof(keybuf),
&cdnskeyrdata));
@ -2058,10 +2087,21 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
DNS_DSDIGEST_SHA256, dsbuf2,
&cds_sha256));
if (exists(cds, &cds_sha1)) {
isc_log_write(
dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
"CDS (SHA-1) for key %s is now deleted",
keystr);
RETERR(delrdata(&cds_sha1, diff, origin,
cds->ttl, mctx));
}
if (exists(cds, &cds_sha256)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC,
ISC_LOG_INFO,
"CDS (SHA-256) for key %s is now "
"deleted",
keystr);
RETERR(delrdata(&cds_sha256, diff, origin,
cds->ttl, mctx));
}
@ -2069,6 +2109,11 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
if (dns_rdataset_isassociated(cdnskey)) {
if (exists(cdnskey, &cdnskeyrdata)) {
isc_log_write(
dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
"CDNSKEY for key %s is now deleted",
keystr);
RETERR(delrdata(&cdnskeyrdata, diff, origin,
cdnskey->ttl, mctx));
}
@ -2081,6 +2126,81 @@ failure:
return (result);
}
isc_result_t
dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
dns_name_t *origin, dns_rdataclass_t zclass,
dns_ttl_t ttl, dns_diff_t *diff, isc_mem_t *mctx,
bool dnssec_insecure) {
unsigned char dsbuf[5] = { 0, 0, 0, 0, 0 }; /* CDS DELETE rdata */
unsigned char keybuf[5] = { 0, 0, 3, 0, 0 }; /* CDNSKEY DELETE rdata */
char namebuf[DNS_NAME_FORMATSIZE];
dns_rdata_t cds_delete = DNS_RDATA_INIT;
dns_rdata_t cdnskey_delete = DNS_RDATA_INIT;
isc_region_t r;
isc_result_t result;
r.base = keybuf;
r.length = sizeof(keybuf);
dns_rdata_fromregion(&cdnskey_delete, zclass, dns_rdatatype_cdnskey,
&r);
r.base = dsbuf;
r.length = sizeof(dsbuf);
dns_rdata_fromregion(&cds_delete, zclass, dns_rdatatype_cds, &r);
dns_name_format(origin, namebuf, sizeof(namebuf));
if (dnssec_insecure) {
if (!dns_rdataset_isassociated(cdnskey) ||
!exists(cdnskey, &cdnskey_delete)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
"CDNSKEY (DELETE) for zone %s is now "
"published",
namebuf);
RETERR(addrdata(&cdnskey_delete, diff, origin, ttl,
mctx));
}
if (!dns_rdataset_isassociated(cds) ||
!exists(cds, &cds_delete)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
"CDS (DELETE) for zone %s is now "
"published",
namebuf);
RETERR(addrdata(&cds_delete, diff, origin, ttl, mctx));
}
} else {
if (dns_rdataset_isassociated(cdnskey) &&
exists(cdnskey, &cdnskey_delete)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
"CDNSKEY (DELETE) for zone %s is now "
"deleted",
namebuf);
RETERR(delrdata(&cdnskey_delete, diff, origin,
cdnskey->ttl, mctx));
}
if (dns_rdataset_isassociated(cds) && exists(cds, &cds_delete))
{
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
"CDS (DELETE) for zone %s is now "
"deleted",
namebuf);
RETERR(delrdata(&cds_delete, diff, origin, cds->ttl,
mctx));
}
}
result = ISC_R_SUCCESS;
failure:
return (result);
}
/*
* Update 'keys' with information from 'newkeys'.
*

View file

@ -649,12 +649,14 @@ dst_key_fromnamedfile(const char *filename, const char *dirname, int type,
filename, ".state");
INSIST(result == ISC_R_SUCCESS);
key->kasp = false;
result = dst_key_read_state(newfilename, mctx, &key);
if (result == ISC_R_FILENOTFOUND) {
if (result == ISC_R_SUCCESS) {
key->kasp = true;
} else if (result == ISC_R_FILENOTFOUND) {
/* Having no state is valid. */
result = ISC_R_SUCCESS;
}
isc_mem_put(mctx, newfilename, newfilenamelen);
newfilename = NULL;
RETERR(result);
@ -2600,6 +2602,8 @@ dst_key_goal(dst_key_t *key) {
dst_key_state_t state;
isc_result_t result;
REQUIRE(VALID_KEY(key));
result = dst_key_getstate(key, DST_KEY_GOAL, &state);
if (result == ISC_R_SUCCESS) {
return (state);
@ -2607,6 +2611,13 @@ dst_key_goal(dst_key_t *key) {
return (DST_KEY_STATE_HIDDEN);
}
bool
dst_key_haskasp(dst_key_t *key) {
REQUIRE(VALID_KEY(key));
return (key->kasp);
}
void
dst_key_copy_metadata(dst_key_t *to, dst_key_t *from) {
dst_key_state_t state;

View file

@ -123,6 +123,7 @@ struct dst_key {
bool keystateset[DST_MAX_KEYSTATES + 1]; /*%< data
* set? */
bool kasp; /*%< key has kasp state */
bool inactive; /*%< private key not present as it is
* inactive */
bool external; /*%< external key */

View file

@ -357,6 +357,25 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
isc_mem_t *mctx);
/*%<
* Update the CDS and CDNSKEY RRsets, adding and removing keys as needed.
*
* Returns:
*\li ISC_R_SUCCESS
*\li Other values indicate error
*/
isc_result_t
dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
dns_name_t *origin, dns_rdataclass_t zclass,
dns_ttl_t ttl, dns_diff_t *diff, isc_mem_t *mctx,
bool dnssec_insecure);
/*%<
* Add or remove the CDS DELETE record and the CDNSKEY DELETE record.
* If 'dnssec_insecure' is true, the DELETE records should be present.
* Otherwise, the DELETE records must be removed from the RRsets (if present).
*
* Returns:
*\li ISC_R_SUCCESS
*\li Other values indicate error
*/
isc_result_t

View file

@ -694,6 +694,32 @@ dns_zone_getkasp(dns_zone_t *zone);
*\li 'zone' to be a valid zone.
*/
bool
dns_zone_secure_to_insecure(dns_zone_t *zone, bool reconfig);
/*%<
* Returns true if the zone is transitioning to insecure.
* Only can happen if a zone previously used a dnssec-policy,
* but changed the value to "none" (or removed the configuration
* option). If 'reconfig' is true, only check the key files,
* because the zone structure is not yet updated with the
* newest configuration.
*
* Require:
*\li 'zone' to be a valid zone.
*/
bool
dns_zone_use_kasp(dns_zone_t *zone);
/*%<
* Check if zone needs to use kasp.
* True if there is a policy that is not "none",
* or if there are state files associated with the keys
* related to this zone.
*
* Require:
*\li 'zone' to be a valid zone.
*/
void
dns_zone_setkasp(dns_zone_t *zone, dns_kasp_t *kasp);
/*%<

View file

@ -1103,6 +1103,15 @@ dst_key_isexternal(dst_key_t *key);
* 'key' to be valid.
*/
bool
dst_key_haskasp(dst_key_t *key);
/*%<
* Check if this key has state (and thus uses KASP).
*
* Requires:
* 'key' to be valid.
*/
bool
dst_key_is_unused(dst_key_t *key);
/*%<

View file

@ -777,7 +777,7 @@ keymgr_dnskey_hidden_or_chained(dns_dnsseckeylist_t *keyring,
*/
static bool
keymgr_have_ds(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, int type,
dst_key_state_t next_state) {
dst_key_state_t next_state, bool secure_to_insecure) {
dst_key_state_t states[2][4] = {
/* DNSKEY, ZRRSIG, KRRSIG, DS */
{ NA, NA, NA, OMNIPRESENT }, /* DS present */
@ -792,7 +792,10 @@ keymgr_have_ds(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, int type,
return (keymgr_key_exists_with_state(keyring, key, type, next_state,
states[0], na, false, false) ||
keymgr_key_exists_with_state(keyring, key, type, next_state,
states[1], na, false, false));
states[1], na, false, false) ||
(secure_to_insecure &&
keymgr_key_exists_with_state(keyring, key, type, next_state,
na, na, false, false)));
}
/*
@ -1022,14 +1025,17 @@ keymgr_policy_approval(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
*/
static bool
keymgr_transition_allowed(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
int type, dst_key_state_t next_state) {
int type, dst_key_state_t next_state,
bool secure_to_insecure) {
/* Debug logging. */
if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) {
bool rule1a, rule1b, rule2a, rule2b, rule3a, rule3b;
char keystr[DST_KEY_FORMATSIZE];
dst_key_format(key->key, keystr, sizeof(keystr));
rule1a = keymgr_have_ds(keyring, key, type, NA);
rule1b = keymgr_have_ds(keyring, key, type, next_state);
rule1a = keymgr_have_ds(keyring, key, type, NA,
secure_to_insecure);
rule1b = keymgr_have_ds(keyring, key, type, next_state,
secure_to_insecure);
rule2a = keymgr_have_dnskey(keyring, key, type, NA);
rule2b = keymgr_have_dnskey(keyring, key, type, next_state);
rule3a = keymgr_have_rrsig(keyring, key, type, NA);
@ -1054,8 +1060,9 @@ keymgr_transition_allowed(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
* invalid state. If the rule check passes, also check if
* the next state is also still a valid situation.
*/
(!keymgr_have_ds(keyring, key, type, NA) ||
keymgr_have_ds(keyring, key, type, next_state)) &&
(!keymgr_have_ds(keyring, key, type, NA, secure_to_insecure) ||
keymgr_have_ds(keyring, key, type, next_state,
secure_to_insecure)) &&
/*
* Rule 2: There must be a DNSKEY at all times. Again, first
* check the current situation, then assess the next state.
@ -1246,7 +1253,7 @@ keymgr_transition_time(dns_dnsseckey_t *key, int type,
*/
static isc_result_t
keymgr_update(dns_dnsseckeylist_t *keyring, dns_kasp_t *kasp, isc_stdtime_t now,
isc_stdtime_t *nexttime) {
isc_stdtime_t *nexttime, bool secure_to_insecure) {
bool changed;
/* Repeat until nothing changed. */
@ -1328,7 +1335,9 @@ transition:
/* Is the transition DNSSEC safe? */
if (!keymgr_transition_allowed(keyring, dkey, i,
next_state)) {
next_state,
secure_to_insecure))
{
/* No, this would make the zone bogus. */
isc_log_write(
dns_lctx, DNS_LOGCATEGORY_DNSSEC,
@ -1677,6 +1686,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
dns_dnsseckey_t *newkey = NULL;
isc_dir_t dir;
bool dir_open = false;
bool secure_to_insecure = false;
int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE);
char keystr[DST_KEY_FORMATSIZE];
@ -1832,8 +1842,14 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
ISC_LIST_APPENDLIST(*keyring, newkeys, link);
}
/*
* If the policy has an empty key list, this means the zone is going
* back to unsigned.
*/
secure_to_insecure = dns_kasp_keylist_empty(kasp);
/* Read to update key states. */
keymgr_update(keyring, kasp, now, nexttime);
keymgr_update(keyring, kasp, now, nexttime, secure_to_insecure);
/* Store key states and update hints. */
for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL;

View file

@ -1087,7 +1087,6 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
bool keyset_kskonly) {
isc_result_t result;
dns_dbnode_t *node = NULL;
dns_kasp_t *kasp = dns_zone_getkasp(zone);
dns_rdataset_t rdataset;
dns_rdata_t sig_rdata = DNS_RDATA_INIT;
dns_stats_t *dnssecsignstats = dns_zone_getdnssecsignstats(zone);
@ -1095,11 +1094,13 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
unsigned char data[1024]; /* XXX */
unsigned int i, j;
bool added_sig = false;
bool use_kasp = false;
isc_mem_t *mctx = diff->mctx;
if (kasp != NULL) {
if (dns_zone_use_kasp(zone)) {
check_ksk = false;
keyset_kskonly = true;
use_kasp = true;
}
dns_rdataset_init(&rdataset);
@ -1174,7 +1175,7 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
}
}
if (kasp != NULL) {
if (use_kasp) {
/*
* A dnssec-policy is found. Check what RRsets this
* key should sign.

View file

@ -332,7 +332,7 @@ dns_dnssec_selfsigns
dns_dnssec_sign
dns_dnssec_signmessage
dns_dnssec_signs
dns_dnssec_syncupdate
dns_dnssec_syncdelete
dns_dnssec_syncupdate
dns_dnssec_updatekeys
dns_dnssec_verify
@ -1278,6 +1278,7 @@ dns_zone_rekey
dns_zone_replacedb
dns_zone_rpz_enable
dns_zone_rpz_enable_db
dns_zone_secure_to_insecure
dns_zone_set_parentcatz
dns_zone_setadded
dns_zone_setalsonotify
@ -1366,6 +1367,7 @@ dns_zone_setzeronosoattl
dns_zone_signwithkey
dns_zone_synckeyzone
dns_zone_unload
dns_zone_use_kasp
dns_zone_verifydb
dns_zonekey_iszonekey
dns_zonemgr_attach
@ -1452,6 +1454,7 @@ dst_key_getstate
dst_key_gettime
dst_key_getttl
dst_key_goal
dst_key_haskasp
dst_key_id
dst_key_is_active
dst_key_is_published

View file

@ -1830,7 +1830,7 @@ dns_zone_isdynamic(dns_zone_t *zone, bool ignore_freeze) {
}
/* Kasp zones are always dynamic. */
if (dns_zone_getkasp(zone) != NULL) {
if (dns_zone_use_kasp(zone)) {
return (true);
}
@ -5833,6 +5833,82 @@ dns_zone_getkasp(dns_zone_t *zone) {
return (zone->kasp);
}
static bool
statefile_exist(dns_zone_t *zone) {
isc_result_t ret;
dns_dnsseckeylist_t keys;
dns_dnsseckey_t *key = NULL;
isc_stdtime_t now;
isc_time_t timenow;
bool found = false;
TIME_NOW(&timenow);
now = isc_time_seconds(&timenow);
ISC_LIST_INIT(keys);
ret = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone),
dns_zone_getkeydirectory(zone), now,
dns_zone_getmctx(zone), &keys);
if (ret == ISC_R_SUCCESS) {
for (key = ISC_LIST_HEAD(keys); key != NULL;
key = ISC_LIST_NEXT(key, link)) {
if (dst_key_haskasp(key->key)) {
found = true;
break;
}
}
}
/* Clean up keys */
while (!ISC_LIST_EMPTY(keys)) {
key = ISC_LIST_HEAD(keys);
ISC_LIST_UNLINK(keys, key, link);
dns_dnsseckey_destroy(dns_zone_getmctx(zone), &key);
}
return (found);
}
bool
dns_zone_secure_to_insecure(dns_zone_t *zone, bool reconfig) {
REQUIRE(DNS_ZONE_VALID(zone));
/*
* If checking during reconfig, the zone is not yet updated
* with the new kasp configuration, so only check the key
* files.
*/
if (reconfig) {
return (statefile_exist(zone));
}
if (zone->kasp == NULL) {
return (false);
}
if (strcmp(dns_kasp_getname(zone->kasp), "none") != 0) {
return (false);
}
/*
* "dnssec-policy none", but if there are key state files
* this zone used to be secure but is transitioning back to
* insecure.
*/
return (statefile_exist(zone));
}
bool
dns_zone_use_kasp(dns_zone_t *zone) {
dns_kasp_t *kasp = dns_zone_getkasp(zone);
if (kasp == NULL) {
return (false);
} else if (strcmp(dns_kasp_getname(kasp), "none") != 0) {
return (true);
}
return dns_zone_secure_to_insecure(zone, false);
}
void
dns_zone_setoption(dns_zone_t *zone, dns_zoneopt_t option, bool value) {
REQUIRE(DNS_ZONE_VALID(zone));
@ -6784,17 +6860,18 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_zone_t *zone,
isc_stdtime_t expire, bool check_ksk, bool keyset_kskonly) {
isc_result_t result;
dns_dbnode_t *node = NULL;
dns_kasp_t *kasp = dns_zone_getkasp(zone);
dns_stats_t *dnssecsignstats;
dns_rdataset_t rdataset;
dns_rdata_t sig_rdata = DNS_RDATA_INIT;
unsigned char data[1024]; /* XXX */
isc_buffer_t buffer;
unsigned int i, j;
bool use_kasp = false;
if (kasp != NULL) {
if (dns_zone_use_kasp(zone)) {
check_ksk = false;
keyset_kskonly = true;
use_kasp = true;
}
dns_rdataset_init(&rdataset);
@ -6872,8 +6949,7 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_zone_t *zone,
}
}
}
if (kasp != NULL) {
if (use_kasp) {
/*
* A dnssec-policy is found. Check what RRsets this
* key should sign.
@ -7308,7 +7384,7 @@ signed_with_good_key(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node,
dns_rdata_reset(&rdata);
}
if (kasp) {
if (dns_zone_use_kasp(zone)) {
dns_kasp_key_t *kkey;
int zsk_count = 0;
bool approved;
@ -7425,7 +7501,6 @@ sign_a_node(dns_db_t *db, dns_zone_t *zone, dns_name_t *name,
bool is_zsk, bool keyset_kskonly, bool is_bottom_of_zone,
dns_diff_t *diff, int32_t *signatures, isc_mem_t *mctx) {
isc_result_t result;
dns_kasp_t *kasp = dns_zone_getkasp(zone);
dns_rdatasetiter_t *iterator = NULL;
dns_rdataset_t rdataset;
dns_rdata_t rdata = DNS_RDATA_INIT;
@ -7521,7 +7596,7 @@ sign_a_node(dns_db_t *db, dns_zone_t *zone, dns_name_t *name,
} else if (is_zsk && !dst_key_is_signing(key, DST_BOOL_ZSK,
inception, &when)) {
/* Only applies to dnssec-policy. */
if (kasp != NULL) {
if (dns_zone_use_kasp(zone)) {
goto next_rdataset;
}
}
@ -9165,6 +9240,7 @@ zone_sign(dns_zone_t *zone) {
bool is_bottom_of_zone;
bool build_nsec = false;
bool build_nsec3 = false;
bool use_kasp = false;
bool first;
isc_result_t result;
isc_stdtime_t now, inception, soaexpire, expire;
@ -9221,7 +9297,6 @@ zone_sign(dns_zone_t *zone) {
}
kasp = dns_zone_getkasp(zone);
sigvalidityinterval = dns_zone_getsigvalidityinterval(zone);
inception = now - 3600; /* Allow for clock skew. */
soaexpire = now + sigvalidityinterval;
@ -9258,16 +9333,20 @@ zone_sign(dns_zone_t *zone) {
signing = ISC_LIST_HEAD(zone->signing);
first = true;
check_ksk = (kasp != NULL)
? false
: DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
keyset_kskonly = (kasp != NULL)
? true
: DNS_ZONE_OPTION(zone,
DNS_ZONEOPT_DNSKEYKSKONLY);
if (dns_zone_use_kasp(zone)) {
check_ksk = false;
keyset_kskonly = true;
use_kasp = true;
} else {
check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
keyset_kskonly = DNS_ZONE_OPTION(zone,
DNS_ZONEOPT_DNSKEYKSKONLY);
}
dnssec_log(zone, ISC_LOG_DEBUG(3), "zone_sign:use kasp -> %s",
use_kasp ? "yes" : "no");
/* Determine which type of chain to build */
if (kasp != NULL) {
if (use_kasp) {
build_nsec3 = dns_kasp_nsec3(kasp);
build_nsec = !build_nsec3;
} else {
@ -9452,7 +9531,7 @@ zone_sign(dns_zone_t *zone) {
}
}
}
if (kasp != NULL) {
if (use_kasp) {
/*
* A dnssec-policy is found. Check what
* RRsets this key can sign.
@ -16462,7 +16541,7 @@ copy_non_dnssec_records(dns_zone_t *zone, dns_db_t *db, dns_db_t *version,
* Allow DNSSEC records with dnssec-policy.
* WMM: Perhaps add config option for it.
*/
if (dns_zone_getkasp(zone) == NULL) {
if (!dns_zone_use_kasp(zone)) {
dns_rdataset_disassociate(&rdataset);
continue;
}
@ -19747,7 +19826,7 @@ zone_rekey(dns_zone_t *zone) {
dns__zonediff_t zonediff;
bool commit = false, newactive = false;
bool newalg = false;
bool fullsign;
bool fullsign, use_kasp;
dns_ttl_t ttl = 3600;
const char *dir = NULL;
isc_mem_t *mctx = NULL;
@ -19818,9 +19897,10 @@ zone_rekey(dns_zone_t *zone) {
* True when called from "rndc sign". Indicates the zone should be
* fully signed now.
*/
kasp = dns_zone_getkasp(zone);
fullsign = DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_FULLSIGN);
kasp = dns_zone_getkasp(zone);
use_kasp = dns_zone_use_kasp(zone);
if (kasp != NULL) {
LOCK(&kasp->lock);
}
@ -19833,9 +19913,7 @@ zone_rekey(dns_zone_t *zone) {
isc_result_totext(result));
}
if (kasp != NULL &&
(result == ISC_R_SUCCESS || result == ISC_R_NOTFOUND)) {
ttl = dns_kasp_dnskeyttl(kasp);
if (use_kasp && (result == ISC_R_SUCCESS || result == ISC_R_NOTFOUND)) {
result = dns_keymgr_run(&zone->origin, zone->rdclass, dir, mctx,
&keys, kasp, now, &nexttime);
if (result != ISC_R_SUCCESS) {
@ -19851,6 +19929,16 @@ zone_rekey(dns_zone_t *zone) {
}
if (result == ISC_R_SUCCESS) {
bool insecure = dns_zone_secure_to_insecure(zone, false);
/*
* Only update DNSKEY TTL if we have a policy.
*/
if (kasp != NULL && strcmp(dns_kasp_getname(kasp), "none") != 0)
{
ttl = dns_kasp_dnskeyttl(kasp);
}
result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys,
&zone->origin, ttl, &diff, mctx,
dnssec_report);
@ -19878,6 +19966,17 @@ zone_rekey(dns_zone_t *zone) {
goto failure;
}
result = dns_dnssec_syncdelete(&cdsset, &cdnskeyset,
&zone->origin, zone->rdclass,
ttl, &diff, mctx, insecure);
if (result != ISC_R_SUCCESS) {
dnssec_log(zone, ISC_LOG_ERROR,
"zone_rekey:couldn't update CDS/CDNSKEY "
"DELETE records: %s",
isc_result_totext(result));
goto failure;
}
/*
* See if any pre-existing keys have newly become active;
* also, see if any new key is for a new algorithm, as in that
@ -20063,7 +20162,7 @@ zone_rekey(dns_zone_t *zone) {
/*
* If keymgr provided a next time, use the calculated next rekey time.
*/
if (kasp != NULL) {
if (use_kasp) {
isc_time_t timenext;
uint32_t nexttime_seconds;

View file

@ -25,22 +25,23 @@
ISC_LANG_BEGINDECLS
isc_result_t
cfg_kasp_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *logctx,
dns_kasplist_t *kasplist, dns_kasp_t **kaspp);
cfg_kasp_fromconfig(const cfg_obj_t *config, const char *name, isc_mem_t *mctx,
isc_log_t *logctx, dns_kasplist_t *kasplist,
dns_kasp_t **kaspp);
/*%<
* Create and configure a KASP. If 'config' is NULL, the default configuration
* is used. If a 'kasplist' is provided, a lookup happens and if a KASP
* already exists with the same name, no new KASP is created, and no attach to
* 'kaspp' happens.
* Create and configure a KASP. If 'config' is NULL, a built-in configuration
* is used, referred to by 'name'. If a 'kasplist' is provided, a lookup
* happens and if a KASP already exists with the same name, no new KASP is
* created, and no attach to 'kaspp' happens.
*
* Requires:
*
*\li 'name' is either NULL, or a valid C string.
*
*\li 'mctx' is a valid memory context.
*
*\li 'logctx' is a valid logging context.
*
*\li 'name' is a valid C string.
*
*\li kaspp != NULL && *kaspp == NULL
*
* Returns:

View file

@ -253,8 +253,9 @@ cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
}
isc_result_t
cfg_kasp_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *logctx,
dns_kasplist_t *kasplist, dns_kasp_t **kaspp) {
cfg_kasp_fromconfig(const cfg_obj_t *config, const char *name, isc_mem_t *mctx,
isc_log_t *logctx, dns_kasplist_t *kasplist,
dns_kasp_t **kaspp) {
isc_result_t result;
const cfg_obj_t *maps[2];
const cfg_obj_t *koptions = NULL;
@ -267,11 +268,10 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *logctx,
REQUIRE(kaspp != NULL && *kaspp == NULL);
kaspname = (config != NULL)
kaspname = (name == NULL)
? cfg_obj_asstring(cfg_tuple_get(config, "name"))
: "default";
REQUIRE(strcmp(kaspname, "none") != 0);
: name;
INSIST(kaspname != NULL);
result = dns_kasplist_find(kasplist, kaspname, &kasp);
@ -317,12 +317,7 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *logctx,
DNS_KASP_RETIRE_SAFETY));
(void)confget(maps, "keys", &keys);
if (keys == NULL) {
result = cfg_kaspkey_fromconfig(NULL, kasp, logctx);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
} else {
if (keys != NULL) {
for (element = cfg_list_first(keys); element != NULL;
element = cfg_list_next(element))
{
@ -332,8 +327,19 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *logctx,
goto cleanup;
}
}
INSIST(!(dns_kasp_keylist_empty(kasp)));
} else if (strcmp(kaspname, "none") == 0) {
/* "dnssec-policy none": key list must be empty */
INSIST(strcmp(kaspname, "none") == 0);
INSIST(dns_kasp_keylist_empty(kasp));
} else {
/* No keys clause configured, use the "default". */
result = cfg_kaspkey_fromconfig(NULL, kasp, logctx);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
INSIST(!(dns_kasp_keylist_empty(kasp)));
}
INSIST(!(dns_kasp_keylist_empty(kasp)));
/* Configuration: NSEC3 */
(void)confget(maps, "nsec3param", &nsec3);