diff --git a/CHANGES b/CHANGES index a041b0d5ac..52981c221f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +5372. [bug] Fix migration from existing DNSSEC key files using + auto-dnssec maintain to dnssec-policy. [GL #1706] + 5371. [bug] Improve incremental updates of the RPZ summary database to reduce delays that could occur when a policy zone update included a large number of diff --git a/bin/tests/system/kasp/ns6/named.conf.in b/bin/tests/system/kasp/ns6/named.conf.in index 5a6ca042c4..943c95f120 100644 --- a/bin/tests/system/kasp/ns6/named.conf.in +++ b/bin/tests/system/kasp/ns6/named.conf.in @@ -24,6 +24,7 @@ options { listen-on-v6 { none; }; allow-transfer { any; }; recursion no; + key-directory "."; }; key rndc_key { @@ -35,6 +36,35 @@ controls { inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; }; +/* These are zones that migrate to dnssec-policy. */ +zone "migrate.kasp" { + type master; + file "migrate.kasp.db"; + auto-dnssec maintain; + allow-update { any; }; + dnssec-dnskey-kskonly yes; + update-check-ksk yes; +}; + +zone "migrate-nomatch-algnum.kasp" { + type master; + file "migrate-nomatch-algnum.kasp.db"; + auto-dnssec maintain; + allow-update { any; }; + dnssec-dnskey-kskonly yes; + update-check-ksk yes; +}; + +zone "migrate-nomatch-alglen.kasp" { + type master; + file "migrate-nomatch-alglen.kasp.db"; + auto-dnssec maintain; + allow-update { any; }; + dnssec-dnskey-kskonly yes; + update-check-ksk yes; +}; + +/* These are alorithm rollover test zones. */ zone "step1.algorithm-roll.kasp" { type master; file "step1.algorithm-roll.kasp.db"; diff --git a/bin/tests/system/kasp/ns6/named2.conf.in b/bin/tests/system/kasp/ns6/named2.conf.in index 52660c6084..4405939321 100644 --- a/bin/tests/system/kasp/ns6/named2.conf.in +++ b/bin/tests/system/kasp/ns6/named2.conf.in @@ -35,6 +35,31 @@ controls { inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; }; +/* These are zones that migrate to dnssec-policy. */ +zone "migrate.kasp" { + type master; + file "migrate.kasp.db"; + allow-update { any; }; + dnssec-policy "migrate"; +}; + +zone "migrate-nomatch-algnum.kasp" { + type master; + file "migrate-nomatch-algnum.kasp.db"; + allow-update { any; }; + dnssec-policy "migrate-nomatch-algnum"; +}; + +zone "migrate-nomatch-alglen.kasp" { + type master; + file "migrate-nomatch-alglen.kasp.db"; + allow-update { any; }; + dnssec-policy "migrate-nomatch-alglen"; +}; + +/* + * Zones for testing KSK/ZSK algorithm roll. + */ zone "step1.algorithm-roll.kasp" { type master; file "step1.algorithm-roll.kasp.db"; diff --git a/bin/tests/system/kasp/ns6/policies/kasp.conf b/bin/tests/system/kasp/ns6/policies/kasp.conf index ad7028ed0c..6b58eaf825 100644 --- a/bin/tests/system/kasp/ns6/policies/kasp.conf +++ b/bin/tests/system/kasp/ns6/policies/kasp.conf @@ -48,3 +48,56 @@ dnssec-policy "ecdsa256" { parent-propagation-delay pt1h; parent-ds-ttl 7200; }; + +dnssec-policy "migrate" { + dnskey-ttl 300; + + keys { + ksk key-directory lifetime unlimited algorithm ECDSAP256SHA256; + zsk key-directory lifetime P60D algorithm ECDSAP256SHA256; + }; +}; + +/* + * This policy tests migration from existing keys with 1024 bits RSASHA1 keys + * to ECDSAP256SHA256 keys. + */ +dnssec-policy "migrate-nomatch-algnum" { + dnskey-ttl 300; + + keys { + ksk key-directory lifetime unlimited algorithm ecdsa256; + zsk key-directory lifetime P60D algorithm ecdsa256; + }; + + // Together 12h + zone-propagation-delay 3600; + max-zone-ttl 11h; + + // Together 24h + parent-registration-delay 21h; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; + +/* + * This policy tests migration from existing keys with 1024 bits RSASHA1 keys + * to 2048 bits RSASHA1 keys. + */ +dnssec-policy "migrate-nomatch-alglen" { + dnskey-ttl 300; + + keys { + ksk key-directory lifetime unlimited algorithm rsasha1 2048; + zsk key-directory lifetime P60D algorithm rsasha1 2048; + }; + + // Together 12h + zone-propagation-delay 3600; + max-zone-ttl 11h; + + // Together 24h + parent-registration-delay 21h; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; diff --git a/bin/tests/system/kasp/ns6/setup.sh b/bin/tests/system/kasp/ns6/setup.sh index cae3475535..430e1c9e3e 100644 --- a/bin/tests/system/kasp/ns6/setup.sh +++ b/bin/tests/system/kasp/ns6/setup.sh @@ -39,6 +39,54 @@ R="RUMOURED" O="OMNIPRESENT" U="UNRETENTIVE" +# Set up a zone with auto-dnssec maintain to migrate to dnssec-policy. +setup migrate.kasp +echo "$zone" >> zones +KSK=$($KEYGEN -a ECDSAP256SHA256 -f KSK -L 300 $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a ECDSAP256SHA256 -L 300 $zone 2> keygen.out.$zone.2) +$SETTIME -P now -P sync now -A now "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -P now -A now "$ZSK" > settime.out.$zone.2 2>&1 +cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile" +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 + +# Set up a zone with auto-dnssec maintain to migrate to dnssec-policy, but this +# time the existing keys do not match the policy. The existing keys are +# RSASHA1 keys, and will be migrated to a dnssec-policy that dictates +# ECDSAP256SHA256 keys. +setup migrate-nomatch-algnum.kasp +echo "$zone" >> zones +KSK=$($KEYGEN -a RSASHA1 -b 2048 -f KSK -L 300 $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a RSASHA1 -b 1024 -L 300 $zone 2> keygen.out.$zone.2) +Tds="now-24h" # Time according to dnssec-policy that DS will be OMNIPRESENT +Tkey="now-3900s" # DNSKEY TTL + propagation delay +Tsig="now-12h" # Zone's maximum TTL + propagation delay +$SETTIME -P $Tkey -P sync $Tds -A $Tkey "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -P $Tsig -A $Tsig "$ZSK" > settime.out.$zone.2 2>&1 +cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile" +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 + +# Set up a zone with auto-dnssec maintain to migrate to dnssec-policy, but this +# time the existing keys do not match the policy. The existing keys are +# 1024 bits RSASHA1 keys, and will be migrated to a dnssec-policy that +# dictates 2048 bits RSASHA1 keys. +setup migrate-nomatch-alglen.kasp +echo "$zone" >> zones +KSK=$($KEYGEN -a RSASHA1 -b 1024 -f KSK -L 300 $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a RSASHA1 -b 1024 -L 300 $zone 2> keygen.out.$zone.2) +Tds="now-24h" # Time according to dnssec-policy that DS will be OMNIPRESENT +Tkey="now-3900s" # DNSKEY TTL + propagation delay +Tsig="now-12h" # Zone's maximum TTL + propagation delay +$SETTIME -P $Tkey -P sync $Tds -A $Tkey "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -P $Tsig -A $Tsig "$ZSK" > settime.out.$zone.2 2>&1 +cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile" +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 zones at algorithm-roll.kasp represent the various steps of a ZSK/KSK # algorithm rollover. @@ -47,6 +95,7 @@ U="UNRETENTIVE" # Step 1: # Introduce the first key. This will immediately be active. setup step1.algorithm-roll.kasp +echo "$zone" >> zones KSK=$($KEYGEN -a RSASHA1 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) ZSK=$($KEYGEN -a RSASHA1 -L 3600 $zone 2> keygen.out.$zone.2) TactN="now" diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 896731e5b5..cda9d87241 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -55,6 +55,7 @@ VIEW2="4xILSZQnuO1UKubXHkYUsvBRPu8=" # STATE_DS=18 # EXPECT_ZRRSIG=19 # EXPECT_KRRSIG=20 +# LEGACY=21 key_key() { echo "${1}__${2}" @@ -77,7 +78,7 @@ key_clear() { key_set "$1" "ROLE" 'none' key_set "$1" "KSK" 'no' key_set "$1" "ZSK" 'no' - key_set "$1" "LIFETIME" '0' + key_set "$1" "LIFETIME" 'none' key_set "$1" "ALG_NUM" '0' key_set "$1" "ALG_STR" 'none' key_set "$1" "ALG_LEN" '0' @@ -93,6 +94,7 @@ key_clear() { key_set "$1" "STATE_DS" 'none' key_set "$1" "EXPECT_ZRRSIG" 'no' key_set "$1" "EXPECT_KRRSIG" 'no' + key_set "$1" "LEGACY" 'no' } # Start clear. @@ -238,6 +240,7 @@ check_key() { _length=$(key_get "$1" "ALG_LEN") _dnskey_ttl="$DNSKEY_TTL" _lifetime=$(key_get "$1" LIFETIME) + _legacy=$(key_get "$1" LEGACY) _published=$(key_get "$1" PUBLISHED) _active=$(key_get "$1" ACTIVE) @@ -277,10 +280,13 @@ check_key() { # Check file existence. [ -s "$KEY_FILE" ] || ret=1 [ -s "$PRIVATE_FILE" ] || ret=1 - [ -s "$STATE_FILE" ] || ret=1 + if [ "$_legacy" == "no" ]; then + [ -s "$STATE_FILE" ] || ret=1 + fi + [ "$ret" -eq 0 ] || log_error "${BASE_FILE} files missing" [ "$ret" -eq 0 ] || return - test $_log -eq 1 && echo_i "check key $BASE_FILE" + test $_log -eq 1 && echo_i "check key file $BASE_FILE" # Check the public key file. grep "This is a ${_role2} key, keyid ${_key_id}, for ${_zone}." "$KEY_FILE" > /dev/null || log_error "mismatch top comment in $KEY_FILE" @@ -289,106 +295,134 @@ check_key() { grep "Private-key-format: v1.3" "$PRIVATE_FILE" > /dev/null || log_error "mismatch private key format in $PRIVATE_FILE" grep "Algorithm: ${_alg_num} (${_alg_string})" "$PRIVATE_FILE" > /dev/null || log_error "mismatch algorithm in $PRIVATE_FILE" # Now check the key state file. - grep "This is the state of key ${_key_id}, for ${_zone}." "$STATE_FILE" > /dev/null || log_error "mismatch top comment in $STATE_FILE" - grep "Lifetime: ${_lifetime}" "$STATE_FILE" > /dev/null || log_error "mismatch lifetime in $STATE_FILE" - grep "Algorithm: ${_alg_num}" "$STATE_FILE" > /dev/null || log_error "mismatch algorithm in $STATE_FILE" - grep "Length: ${_length}" "$STATE_FILE" > /dev/null || log_error "mismatch length in $STATE_FILE" - grep "KSK: ${_ksk}" "$STATE_FILE" > /dev/null || log_error "mismatch ksk in $STATE_FILE" - grep "ZSK: ${_zsk}" "$STATE_FILE" > /dev/null || log_error "mismatch zsk in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "This is the state of key ${_key_id}, for ${_zone}." "$STATE_FILE" > /dev/null || log_error "mismatch top comment in $STATE_FILE" + if [ "$_lifetime" == "none" ]; then + grep "Lifetime: " "$STATE_FILE" > /dev/null && log_error "unexpected lifetime in $STATE_FILE" + else + grep "Lifetime: ${_lifetime}" "$STATE_FILE" > /dev/null || log_error "mismatch lifetime in $STATE_FILE" + fi + grep "Algorithm: ${_alg_num}" "$STATE_FILE" > /dev/null || log_error "mismatch algorithm in $STATE_FILE" + grep "Length: ${_length}" "$STATE_FILE" > /dev/null || log_error "mismatch length in $STATE_FILE" + grep "KSK: ${_ksk}" "$STATE_FILE" > /dev/null || log_error "mismatch ksk in $STATE_FILE" + grep "ZSK: ${_zsk}" "$STATE_FILE" > /dev/null || log_error "mismatch zsk in $STATE_FILE" - # Check key states. - if [ "$_goal" = "none" ]; then - grep "GoalState: " "$STATE_FILE" > /dev/null && log_error "unexpected goal state in $STATE_FILE" - else - grep "GoalState: ${_goal}" "$STATE_FILE" > /dev/null || log_error "mismatch goal state in $STATE_FILE" - fi + # Check key states. + if [ "$_goal" = "none" ]; then + grep "GoalState: " "$STATE_FILE" > /dev/null && log_error "unexpected goal state in $STATE_FILE" + else + grep "GoalState: ${_goal}" "$STATE_FILE" > /dev/null || log_error "mismatch goal state in $STATE_FILE" + fi - if [ "$_state_dnskey" = "none" ]; then - grep "DNSKEYState: " "$STATE_FILE" > /dev/null && log_error "unexpected dnskey state in $STATE_FILE" - grep "DNSKEYChange: " "$STATE_FILE" > /dev/null && log_error "unexpected dnskey change in $STATE_FILE" - else - grep "DNSKEYState: ${_state_dnskey}" "$STATE_FILE" > /dev/null || log_error "mismatch dnskey state in $STATE_FILE" - grep "DNSKEYChange: " "$STATE_FILE" > /dev/null || log_error "mismatch dnskey change in $STATE_FILE" - fi + if [ "$_state_dnskey" = "none" ]; then + grep "DNSKEYState: " "$STATE_FILE" > /dev/null && log_error "unexpected dnskey state in $STATE_FILE" + grep "DNSKEYChange: " "$STATE_FILE" > /dev/null && log_error "unexpected dnskey change in $STATE_FILE" + else + grep "DNSKEYState: ${_state_dnskey}" "$STATE_FILE" > /dev/null || log_error "mismatch dnskey state in $STATE_FILE" + grep "DNSKEYChange: " "$STATE_FILE" > /dev/null || log_error "mismatch dnskey change in $STATE_FILE" + fi - if [ "$_state_zrrsig" = "none" ]; then - grep "ZRRSIGState: " "$STATE_FILE" > /dev/null && log_error "unexpected zrrsig state in $STATE_FILE" - grep "ZRRSIGChange: " "$STATE_FILE" > /dev/null && log_error "unexpected zrrsig change in $STATE_FILE" - else - grep "ZRRSIGState: ${_state_zrrsig}" "$STATE_FILE" > /dev/null || log_error "mismatch zrrsig state in $STATE_FILE" - grep "ZRRSIGChange: " "$STATE_FILE" > /dev/null || log_error "mismatch zrrsig change in $STATE_FILE" - fi + if [ "$_state_zrrsig" = "none" ]; then + grep "ZRRSIGState: " "$STATE_FILE" > /dev/null && log_error "unexpected zrrsig state in $STATE_FILE" + grep "ZRRSIGChange: " "$STATE_FILE" > /dev/null && log_error "unexpected zrrsig change in $STATE_FILE" + else + grep "ZRRSIGState: ${_state_zrrsig}" "$STATE_FILE" > /dev/null || log_error "mismatch zrrsig state in $STATE_FILE" + grep "ZRRSIGChange: " "$STATE_FILE" > /dev/null || log_error "mismatch zrrsig change in $STATE_FILE" + fi - if [ "$_state_krrsig" = "none" ]; then - grep "KRRSIGState: " "$STATE_FILE" > /dev/null && log_error "unexpected krrsig state in $STATE_FILE" - grep "KRRSIGChange: " "$STATE_FILE" > /dev/null && log_error "unexpected krrsig change in $STATE_FILE" - else - grep "KRRSIGState: ${_state_krrsig}" "$STATE_FILE" > /dev/null || log_error "mismatch krrsig state in $STATE_FILE" - grep "KRRSIGChange: " "$STATE_FILE" > /dev/null || log_error "mismatch krrsig change in $STATE_FILE" - fi + if [ "$_state_krrsig" = "none" ]; then + grep "KRRSIGState: " "$STATE_FILE" > /dev/null && log_error "unexpected krrsig state in $STATE_FILE" + grep "KRRSIGChange: " "$STATE_FILE" > /dev/null && log_error "unexpected krrsig change in $STATE_FILE" + else + grep "KRRSIGState: ${_state_krrsig}" "$STATE_FILE" > /dev/null || log_error "mismatch krrsig state in $STATE_FILE" + grep "KRRSIGChange: " "$STATE_FILE" > /dev/null || log_error "mismatch krrsig change in $STATE_FILE" + fi - if [ "$_state_ds" = "none" ]; then - grep "DSState: " "$STATE_FILE" > /dev/null && log_error "unexpected ds state in $STATE_FILE" - grep "DSChange: " "$STATE_FILE" > /dev/null && log_error "unexpected ds change in $STATE_FILE" - else - grep "DSState: ${_state_ds}" "$STATE_FILE" > /dev/null || log_error "mismatch ds state in $STATE_FILE" - grep "DSChange: " "$STATE_FILE" > /dev/null || log_error "mismatch ds change in $STATE_FILE" + if [ "$_state_ds" = "none" ]; then + grep "DSState: " "$STATE_FILE" > /dev/null && log_error "unexpected ds state in $STATE_FILE" + grep "DSChange: " "$STATE_FILE" > /dev/null && log_error "unexpected ds change in $STATE_FILE" + else + grep "DSState: ${_state_ds}" "$STATE_FILE" > /dev/null || log_error "mismatch ds state in $STATE_FILE" + grep "DSChange: " "$STATE_FILE" > /dev/null || log_error "mismatch ds change in $STATE_FILE" + fi fi # Check timing metadata. if [ "$_published" = "none" ]; then grep "; Publish:" "$KEY_FILE" > /dev/null && log_error "unexpected publish comment in $KEY_FILE" grep "Publish:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected publish in $PRIVATE_FILE" - grep "Published: " "$STATE_FILE" > /dev/null && log_error "unexpected publish in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Published: " "$STATE_FILE" > /dev/null && log_error "unexpected publish in $STATE_FILE" + fi else - grep "; Publish:" "$KEY_FILE" > /dev/null || log_error "mismatch publish comment in $KEY_FILE ($KEY_PUBLISHED)" - grep "Publish:" "$PRIVATE_FILE" > /dev/null || log_error "mismatch publish in $PRIVATE_FILE ($KEY_PUBLISHED)" - grep "Published:" "$STATE_FILE" > /dev/null || log_error "mismatch publish in $STATE_FILE ($KEY_PUBLISHED)" + grep "; Publish:" "$KEY_FILE" > /dev/null || log_error "mismatch publish comment in $KEY_FILE" + grep "Publish:" "$PRIVATE_FILE" > /dev/null || log_error "mismatch publish in $PRIVATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Published:" "$STATE_FILE" > /dev/null || log_error "mismatch publish in $STATE_FILE" + fi fi if [ "$_active" = "none" ]; then grep "; Activate:" "$KEY_FILE" > /dev/null && log_error "unexpected active comment in $KEY_FILE" grep "Activate:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected active in $PRIVATE_FILE" - grep "Active: " "$STATE_FILE" > /dev/null && log_error "unexpected active in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Active: " "$STATE_FILE" > /dev/null && log_error "unexpected active in $STATE_FILE" + fi else grep "; Activate:" "$KEY_FILE" > /dev/null || log_error "mismatch active comment in $KEY_FILE" grep "Activate:" "$PRIVATE_FILE" > /dev/null || log_error "mismatch active in $PRIVATE_FILE" - grep "Active: " "$STATE_FILE" > /dev/null || log_error "mismatch active in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Active: " "$STATE_FILE" > /dev/null || log_error "mismatch active in $STATE_FILE" + fi fi if [ "$_retired" = "none" ]; then grep "; Inactive:" "$KEY_FILE" > /dev/null && log_error "unexpected retired comment in $KEY_FILE" grep "Inactive:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected retired in $PRIVATE_FILE" - grep "Retired: " "$STATE_FILE" > /dev/null && log_error "unexpected retired in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Retired: " "$STATE_FILE" > /dev/null && log_error "unexpected retired in $STATE_FILE" + fi else grep "; Inactive:" "$KEY_FILE" > /dev/null || log_error "mismatch retired comment in $KEY_FILE" grep "Inactive:" "$PRIVATE_FILE" > /dev/null || log_error "mismatch retired in $PRIVATE_FILE" - grep "Retired: " "$STATE_FILE" > /dev/null || log_error "mismatch retired in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Retired: " "$STATE_FILE" > /dev/null || log_error "mismatch retired in $STATE_FILE" + fi fi if [ "$_revoked" = "none" ]; then grep "; Revoke:" "$KEY_FILE" > /dev/null && log_error "unexpected revoked comment in $KEY_FILE" grep "Revoke:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected revoked in $PRIVATE_FILE" - grep "Revoked: " "$STATE_FILE" > /dev/null && log_error "unexpected revoked in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Revoked: " "$STATE_FILE" > /dev/null && log_error "unexpected revoked in $STATE_FILE" + fi else grep "; Revoke:" "$KEY_FILE" > /dev/null || log_error "mismatch revoked comment in $KEY_FILE" grep "Revoke:" "$PRIVATE_FILE" > /dev/null || log_error "mismatch revoked in $PRIVATE_FILE" - grep "Revoked: " "$STATE_FILE" > /dev/null || log_error "mismatch revoked in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Revoked: " "$STATE_FILE" > /dev/null || log_error "mismatch revoked in $STATE_FILE" + fi fi if [ "$_removed" = "none" ]; then grep "; Delete:" "$KEY_FILE" > /dev/null && log_error "unexpected removed comment in $KEY_FILE" grep "Delete:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected removed in $PRIVATE_FILE" - grep "Removed: " "$STATE_FILE" > /dev/null && log_error "unexpected removed in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Removed: " "$STATE_FILE" > /dev/null && log_error "unexpected removed in $STATE_FILE" + fi else grep "; Delete:" "$KEY_FILE" > /dev/null || log_error "mismatch removed comment in $KEY_FILE" grep "Delete:" "$PRIVATE_FILE" > /dev/null || log_error "mismatch removed in $PRIVATE_FILE" - grep "Removed: " "$STATE_FILE" > /dev/null || log_error "mismatch removed in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Removed: " "$STATE_FILE" > /dev/null || log_error "mismatch removed in $STATE_FILE" + fi fi grep "; Created:" "$KEY_FILE" > /dev/null || log_error "mismatch created comment in $KEY_FILE" grep "Created:" "$PRIVATE_FILE" > /dev/null || log_error "mismatch created in $PRIVATE_FILE" - grep "Generated: " "$STATE_FILE" > /dev/null || log_error "mismatch generated in $STATE_FILE" + if [ "$_legacy" == "no" ]; then + grep "Generated: " "$STATE_FILE" > /dev/null || log_error "mismatch generated in $STATE_FILE" + fi } # Check the key with key id $1 and see if it is unused. @@ -424,20 +458,24 @@ key_unused() { # Check timing metadata. grep "; Publish:" "$KEY_FILE" > /dev/null && log_error "unexpected publish comment in $KEY_FILE" - grep "Publish:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected publish in $PRIVATE_FILE" - grep "Published: " "$STATE_FILE" > /dev/null && log_error "unexpected publish in $STATE_FILE" grep "; Activate:" "$KEY_FILE" > /dev/null && log_error "unexpected active comment in $KEY_FILE" - grep "Activate:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected active in $PRIVATE_FILE" - grep "Active: " "$STATE_FILE" > /dev/null && log_error "unexpected active in $STATE_FILE" grep "; Inactive:" "$KEY_FILE" > /dev/null && log_error "unexpected retired comment in $KEY_FILE" - grep "Inactive:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected retired in $PRIVATE_FILE" - grep "Retired: " "$STATE_FILE" > /dev/null && log_error "unexpected retired in $STATE_FILE" grep "; Revoke:" "$KEY_FILE" > /dev/null && log_error "unexpected revoked comment in $KEY_FILE" - grep "Revoke:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected revoked in $PRIVATE_FILE" - grep "Revoked: " "$STATE_FILE" > /dev/null && log_error "unexpected revoked in $STATE_FILE" grep "; Delete:" "$KEY_FILE" > /dev/null && log_error "unexpected removed comment in $KEY_FILE" + + grep "Publish:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected publish in $PRIVATE_FILE" + grep "Activate:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected active in $PRIVATE_FILE" + grep "Inactive:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected retired in $PRIVATE_FILE" + grep "Revoke:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected revoked in $PRIVATE_FILE" grep "Delete:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected removed in $PRIVATE_FILE" - grep "Removed: " "$STATE_FILE" > /dev/null && log_error "unexpected removed in $STATE_FILE" + + if [ "$_legacy" == "no" ]; then + grep "Published: " "$STATE_FILE" > /dev/null && log_error "unexpected publish in $STATE_FILE" + grep "Active: " "$STATE_FILE" > /dev/null && log_error "unexpected active in $STATE_FILE" + grep "Retired: " "$STATE_FILE" > /dev/null && log_error "unexpected retired in $STATE_FILE" + grep "Revoked: " "$STATE_FILE" > /dev/null && log_error "unexpected revoked in $STATE_FILE" + grep "Removed: " "$STATE_FILE" > /dev/null && log_error "unexpected removed in $STATE_FILE" + fi } # Test: dnssec-verify zone $1. @@ -848,7 +886,7 @@ check_keys() for _id in $_ids; do # There are three key files with the same algorithm. # Check them until a match is found. - echo_i "check key $_id" + echo_i "check key id $_id" if [ "no" = "$(key_get KEY1 ID)" ] && [ "$(key_get KEY1 EXPECT)" = "yes" ]; then ret=0 @@ -863,19 +901,19 @@ check_keys() if [ "no" = "$(key_get KEY3 ID)" ] && [ "$(key_get KEY3 EXPECT)" = "yes" ]; then ret=0 check_key "KEY3" "$_id" - test "$ret" -eq 0 && key_set KEY3 ID "$KEY_ID" && continue + test "$ret" -eq 0 && key_set KEY3 "ID" "$KEY_ID" && continue fi if [ "no" = "$(key_get KEY4 ID)" ] && [ "$(key_get KEY4 EXPECT)" = "yes" ]; then ret=0 check_key "KEY4" "$_id" - test "$ret" -eq 0 && key_set KEY4 ID "$KEY_ID" && continue + test "$ret" -eq 0 && key_set KEY4 "ID" "$KEY_ID" && continue fi # This may be an unused key. Assume algorithm of KEY1. ret=0 && key_unused "$_id" "$(key_get KEY1 ALG_NUM)" test "$ret" -eq 0 && continue - # If ret is still non-zero, non of the files matched. + # If ret is still non-zero, none of the files matched. test "$ret" -eq 0 || echo_i "failed" status=$((status+1)) done @@ -885,15 +923,19 @@ check_keys() ret=0 if [ "$(key_get KEY1 EXPECT)" = "yes" ]; then + echo_i "KEY1 ID $(key_get KEY1 ID)" test "no" = "$(key_get KEY1 ID)" && log_error "No KEY1 found for zone ${ZONE}" fi if [ "$(key_get KEY2 EXPECT)" = "yes" ]; then + echo_i "KEY2 ID $(key_get KEY2 ID)" test "no" = "$(key_get KEY2 ID)" && log_error "No KEY2 found for zone ${ZONE}" fi if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then + echo_i "KEY3 ID $(key_get KEY3 ID)" test "no" = "$(key_get KEY3 ID)" && log_error "No KEY3 found for zone ${ZONE}" fi if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then + echo_i "KEY4 ID $(key_get KEY4 ID)" test "no" = "$(key_get KEY4 ID)" && log_error "No KEY4 found for zone ${ZONE}" fi test "$ret" -eq 0 || echo_i "failed" @@ -2821,7 +2863,166 @@ dnssec_verify # interval. check_next_key_event 3600 -# Reconfig dnssec-policy (triggering algorithm roll). +# +# Testing good migration. +# +set_zone "migrate.kasp" +set_policy "none" "2" "300" +set_server "ns6" "10.53.0.6" + +init_migration_match() { + key_clear "KEY1" + key_set "KEY1" "LEGACY" "yes" + set_keyrole "KEY1" "ksk" + set_keylifetime "KEY1" "0" + set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" + set_keysigning "KEY1" "yes" + set_zonesigning "KEY1" "no" + + key_clear "KEY2" + key_set "KEY2" "LEGACY" "yes" + set_keyrole "KEY2" "zsk" + set_keylifetime "KEY2" "5184000" + set_keyalgorithm "KEY2" "13" "ECDSAP256SHA256" "256" + set_keysigning "KEY2" "no" + set_zonesigning "KEY2" "yes" + + key_clear "KEY3" + key_clear "KEY4" + + set_keytime "KEY1" "PUBLISHED" "yes" + set_keytime "KEY1" "ACTIVE" "yes" + set_keytime "KEY1" "RETIRED" "none" + set_keystate "KEY1" "GOAL" "omnipresent" + set_keystate "KEY1" "STATE_DNSKEY" "rumoured" + set_keystate "KEY1" "STATE_KRRSIG" "rumoured" + set_keystate "KEY1" "STATE_DS" "rumoured" + + set_keytime "KEY2" "PUBLISHED" "yes" + set_keytime "KEY2" "ACTIVE" "yes" + set_keytime "KEY2" "RETIRED" "none" + set_keystate "KEY2" "GOAL" "omnipresent" + set_keystate "KEY2" "STATE_DNSKEY" "rumoured" + set_keystate "KEY2" "STATE_ZRRSIG" "rumoured" +} +init_migration_match + +# Make sure the zone is signed with legacy keys. +check_keys +check_apex +check_subdomain +dnssec_verify + +# Remember legacy key tags. +_migrate_ksk=$(key_get KEY1 ID) +_migrate_zsk=$(key_get KEY2 ID) + +# +# Testing migration with unmatched existing keys (different algorithm). +# +set_zone "migrate-nomatch-algnum.kasp" +set_policy "none" "2" "300" +set_server "ns6" "10.53.0.6" + +init_migration_nomatch_algnum() { + key_clear "KEY1" + key_set "KEY1" "LEGACY" "yes" + set_keyrole "KEY1" "ksk" + set_keyalgorithm "KEY1" "5" "RSASHA1" "2048" + set_keysigning "KEY1" "yes" + set_zonesigning "KEY1" "no" + + key_clear "KEY2" + key_set "KEY2" "LEGACY" "yes" + set_keyrole "KEY2" "zsk" + set_keyalgorithm "KEY2" "5" "RSASHA1" "1024" + set_keysigning "KEY2" "no" + set_zonesigning "KEY2" "yes" + + key_clear "KEY3" + key_clear "KEY4" + + set_keytime "KEY1" "PUBLISHED" "yes" + set_keytime "KEY1" "ACTIVE" "yes" + set_keytime "KEY1" "RETIRED" "none" + set_keystate "KEY1" "GOAL" "omnipresent" + set_keystate "KEY1" "STATE_DNSKEY" "omnipresent" + set_keystate "KEY1" "STATE_KRRSIG" "omnipresent" + set_keystate "KEY1" "STATE_DS" "omnipresent" + + set_keytime "KEY2" "PUBLISHED" "yes" + set_keytime "KEY2" "ACTIVE" "yes" + set_keytime "KEY2" "RETIRED" "none" + set_keystate "KEY2" "GOAL" "omnipresent" + set_keystate "KEY2" "STATE_DNSKEY" "omnipresent" + set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent" +} +init_migration_nomatch_algnum + +# Make sure the zone is signed with legacy keys. +check_keys +check_apex +check_subdomain +dnssec_verify + +# Remember legacy key tags. +_migratenomatch_algnum_ksk=$(key_get KEY1 ID) +_migratenomatch_algnum_zsk=$(key_get KEY2 ID) + +# +# Testing migration with unmatched existing keys (different length). +# +set_zone "migrate-nomatch-alglen.kasp" +set_policy "none" "2" "300" +set_server "ns6" "10.53.0.6" + +init_migration_nomatch_alglen() { + key_clear "KEY1" + key_set "KEY1" "LEGACY" "yes" + set_keyrole "KEY1" "ksk" + set_keyalgorithm "KEY1" "5" "RSASHA1" "1024" + set_keysigning "KEY1" "yes" + set_zonesigning "KEY1" "no" + + key_clear "KEY2" + key_set "KEY2" "LEGACY" "yes" + set_keyrole "KEY2" "zsk" + set_keyalgorithm "KEY2" "5" "RSASHA1" "1024" + set_keysigning "KEY2" "no" + set_zonesigning "KEY2" "yes" + + key_clear "KEY3" + key_clear "KEY4" + + set_keytime "KEY1" "PUBLISHED" "yes" + set_keytime "KEY1" "ACTIVE" "yes" + set_keytime "KEY1" "RETIRED" "none" + set_keystate "KEY1" "GOAL" "omnipresent" + set_keystate "KEY1" "STATE_DNSKEY" "omnipresent" + set_keystate "KEY1" "STATE_KRRSIG" "omnipresent" + set_keystate "KEY1" "STATE_DS" "omnipresent" + + set_keytime "KEY2" "PUBLISHED" "yes" + set_keytime "KEY2" "ACTIVE" "yes" + set_keytime "KEY2" "RETIRED" "none" + set_keystate "KEY2" "GOAL" "omnipresent" + set_keystate "KEY2" "STATE_DNSKEY" "omnipresent" + set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent" +} +init_migration_nomatch_alglen + +# Make sure the zone is signed with legacy keys. +check_keys +check_apex +check_subdomain +dnssec_verify + +# Remember legacy key tags. +_migratenomatch_alglen_ksk=$(key_get KEY1 ID) +_migratenomatch_alglen_zsk=$(key_get KEY2 ID) + +# Reconfig dnssec-policy (triggering algorithm roll and other dnssec-policy +# changes). echo_i "reconfig dnssec-policy to trigger algorithm rollover" copy_setports ns6/named2.conf.in ns6/named.conf rndc_reconfig ns6 10.53.0.6 @@ -2854,6 +3055,150 @@ status=$((status+ret)) next_key_event_threshold=$((next_key_event_threshold+i)) +# +# Testing migration. +# +set_zone "migrate.kasp" +set_policy "migrate" "2" "300" +set_server "ns6" "10.53.0.6" + +# Key properties, timings and metadata should be the same as legacy keys above. +# However, because the zsk has a lifetime, kasp will set the retired time. +init_migration_match + +key_set "KEY1" "LEGACY" "no" + +key_set "KEY2" "LEGACY" "no" +set_keytime "KEY2" "RETIRED" "yes" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Check key tags, should be the same. +n=$((n+1)) +echo_i "check that of zone ${ZONE} migration to dnssec-policy uses the same keys ($n)" +ret=0 +[ $_migrate_ksk == $(key_get KEY1 ID) ] || log_error "mismatch ksk tag" +[ $_migrate_zsk == $(key_get KEY2 ID) ] || log_error "mismatch zsk tag" +status=$((status+ret)) + +# Test migration to dnssec-policy, existing keys do not match key algorithm. +set_zone "migrate-nomatch-algnum.kasp" +set_policy "migrate-nomatch-algnum" "4" "300" +set_server "ns6" "10.53.0.6" + +# The legacy keys need to be retired, but otherwise stay present until the +# new keys are omnipresent, and can be used to construct a chain of trust. +init_migration_nomatch_algnum + +key_set "KEY1" "LEGACY" "no" +set_keytime "KEY1" "RETIRED" "yes" +set_keystate "KEY1" "GOAL" "hidden" + +key_set "KEY2" "LEGACY" "no" +set_keytime "KEY2" "RETIRED" "yes" +set_keystate "KEY2" "GOAL" "hidden" + +set_keyrole "KEY3" "ksk" +set_keylifetime "KEY3" "0" +set_keyalgorithm "KEY3" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY3" "yes" +set_zonesigning "KEY3" "no" + +set_keyrole "KEY4" "zsk" +set_keylifetime "KEY4" "5184000" +set_keyalgorithm "KEY4" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY4" "no" +set_zonesigning "KEY4" "yes" + +set_keytime "KEY3" "PUBLISHED" "yes" +set_keytime "KEY3" "ACTIVE" "yes" +set_keytime "KEY3" "RETIRED" "none" +set_keystate "KEY3" "GOAL" "omnipresent" +set_keystate "KEY3" "STATE_DNSKEY" "rumoured" +set_keystate "KEY3" "STATE_KRRSIG" "rumoured" +set_keystate "KEY3" "STATE_DS" "hidden" + +set_keytime "KEY4" "PUBLISHED" "yes" +set_keytime "KEY4" "ACTIVE" "yes" +set_keytime "KEY4" "RETIRED" "yes" +set_keystate "KEY4" "GOAL" "omnipresent" +set_keystate "KEY4" "STATE_DNSKEY" "rumoured" +set_keystate "KEY4" "STATE_ZRRSIG" "rumoured" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Check key tags, should be the same. +n=$((n+1)) +echo_i "check that of zone ${ZONE} migration to dnssec-policy keeps existing keys ($n)" +ret=0 +[ $_migratenomatch_algnum_ksk == $(key_get KEY1 ID) ] || log_error "mismatch ksk tag" +[ $_migratenomatch_algnum_zsk == $(key_get KEY2 ID) ] || log_error "mismatch zsk tag" +status=$((status+ret)) + +# Test migration to dnssec-policy, existing keys do not match key length. +set_zone "migrate-nomatch-alglen.kasp" +set_policy "migrate-nomatch-alglen" "4" "300" +set_server "ns6" "10.53.0.6" + +# The legacy keys need to be retired, but otherwise stay present until the +# new keys are omnipresent, and can be used to construct a chain of trust. +init_migration_nomatch_alglen + +key_set "KEY1" "LEGACY" "no" +set_keytime "KEY1" "RETIRED" "yes" +set_keystate "KEY1" "GOAL" "hidden" + +key_set "KEY2" "LEGACY" "no" +set_keytime "KEY2" "RETIRED" "yes" +set_keystate "KEY2" "GOAL" "hidden" + +set_keyrole "KEY3" "ksk" +set_keylifetime "KEY3" "0" +set_keyalgorithm "KEY3" "5" "RSASHA1" "2048" +set_keysigning "KEY3" "yes" +set_zonesigning "KEY3" "no" + +set_keyrole "KEY4" "zsk" +set_keylifetime "KEY4" "5184000" +set_keyalgorithm "KEY4" "5" "RSASHA1" "2048" +set_keysigning "KEY4" "no" +# This key is considered to be prepublished, so it is not yet signing. +set_zonesigning "KEY4" "no" + +set_keytime "KEY3" "PUBLISHED" "yes" +set_keytime "KEY3" "ACTIVE" "yes" +set_keytime "KEY3" "RETIRED" "none" +set_keystate "KEY3" "GOAL" "omnipresent" +set_keystate "KEY3" "STATE_DNSKEY" "rumoured" +set_keystate "KEY3" "STATE_KRRSIG" "rumoured" +set_keystate "KEY3" "STATE_DS" "hidden" + +set_keytime "KEY4" "PUBLISHED" "yes" +set_keytime "KEY4" "ACTIVE" "yes" +set_keytime "KEY4" "RETIRED" "yes" +set_keystate "KEY4" "GOAL" "omnipresent" +set_keystate "KEY4" "STATE_DNSKEY" "rumoured" +set_keystate "KEY4" "STATE_ZRRSIG" "hidden" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Check key tags, should be the same. +n=$((n+1)) +echo_i "check that of zone ${ZONE} migration to dnssec-policy keeps existing keys ($n)" +ret=0 +[ $_migratenomatch_alglen_ksk == $(key_get KEY1 ID) ] || log_error "mismatch ksk tag" +[ $_migratenomatch_alglen_zsk == $(key_get KEY2 ID) ] || log_error "mismatch zsk tag" +status=$((status+ret)) + # # Testing KSK/ZSK algorithm rollover. # diff --git a/doc/arm/notes-9.17.1.xml b/doc/arm/notes-9.17.1.xml index 1e6c5a28b2..aaad722969 100644 --- a/doc/arm/notes-9.17.1.xml +++ b/doc/arm/notes-9.17.1.xml @@ -63,6 +63,14 @@ reducing such delays. [GL #1447] + + + Migration to dnssec-policy from existing DNSSEC strategy with + auto-dnssec maintain did not work due to bad initializing of the + key states. Fixed by looking closely at the time metadata to + set the key states to the correct values. [GL #1706] + + diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 8a54ea9771..09ba4dc9c4 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -43,13 +43,13 @@ * Set key state to HIDDEN and change last changed to now, * only if key state has not been set before. */ -#define INITIALIZE_STATE(key, state, time) \ +#define INITIALIZE_STATE(key, state, time, target) \ do { \ dst_key_state_t s; \ if (dst_key_getstate((key), (state), &s) == ISC_R_NOTFOUND) { \ isc_stdtime_t t; \ dst_key_gettime((key), DST_TIME_CREATED, &t); \ - dst_key_setstate((key), (state), HIDDEN); \ + dst_key_setstate((key), (state), target); \ dst_key_settime((key), (time), t); \ } \ } while (0) @@ -105,7 +105,7 @@ static isc_stdtime_t keymgr_prepublication_time(dns_dnsseckey_t *key, dns_kasp_t *kasp, uint32_t lifetime, isc_stdtime_t now) { isc_result_t ret; - isc_stdtime_t active, retire, prepub; + isc_stdtime_t active, retire, pub, prepub; bool ksk = false; REQUIRE(key != NULL); @@ -125,7 +125,8 @@ keymgr_prepublication_time(dns_dnsseckey_t *key, dns_kasp_t *kasp, if (ret != ISC_R_SUCCESS) { uint32_t klifetime = 0; /* - * An active key must have an activate timing metadata. + * An active key must have publish and activate timing + * metadata. */ ret = dst_key_gettime(key->key, DST_TIME_ACTIVATE, &active); if (ret != ISC_R_SUCCESS) { @@ -133,6 +134,12 @@ keymgr_prepublication_time(dns_dnsseckey_t *key, dns_kasp_t *kasp, dst_key_settime(key->key, DST_TIME_ACTIVATE, now); active = now; } + ret = dst_key_gettime(key->key, DST_TIME_PUBLISH, &pub); + if (ret != ISC_R_SUCCESS) { + /* Super weird, but if it happens, set it to now. */ + dst_key_settime(key->key, DST_TIME_PUBLISH, now); + pub = now; + } ret = dst_key_getnum(key->key, DST_NUM_LIFETIME, &klifetime); if (ret != ISC_R_SUCCESS) { @@ -1214,19 +1221,25 @@ transition: } /* - * See if this key needs to be initialized with a role. A key created and - * derived from a dnssec-policy will have the required metadata available, - * otherwise these may be missing and need to be initialized. + * See if this key needs to be initialized with properties. A key created + * and derived from a dnssec-policy will have the required metadata available, + * otherwise these may be missing and need to be initialized. The key states + * will be initialized according to existing timing metadata. * */ static void -keymgr_key_init_role(dns_dnsseckey_t *key) { +keymgr_key_init(dns_dnsseckey_t *key, dns_kasp_t *kasp, isc_stdtime_t now) { bool ksk, zsk; isc_result_t ret; + isc_stdtime_t active = 0, pub = 0, syncpub = 0; + dst_key_state_t dnskey_state = HIDDEN; + dst_key_state_t ds_state = HIDDEN; + dst_key_state_t zrrsig_state = HIDDEN; REQUIRE(key != NULL); REQUIRE(key->key != NULL); + /* Initialize role. */ ret = dst_key_getbool(key->key, DST_BOOL_KSK, &ksk); if (ret != ISC_R_SUCCESS) { ksk = ((dst_key_flags(key->key) & DNS_KEYFLAG_KSK) != 0); @@ -1237,6 +1250,52 @@ keymgr_key_init_role(dns_dnsseckey_t *key) { zsk = ((dst_key_flags(key->key) & DNS_KEYFLAG_KSK) == 0); dst_key_setbool(key->key, DST_BOOL_ZSK, zsk); } + + /* Get time metadata. */ + ret = dst_key_gettime(key->key, DST_TIME_ACTIVATE, &active); + if (active <= now && ret == ISC_R_SUCCESS) { + dns_ttl_t key_ttl = dst_key_getttl(key->key); + key_ttl += dns_kasp_zonepropagationdelay(kasp); + if ((active + key_ttl) <= now) { + dnskey_state = OMNIPRESENT; + } else { + dnskey_state = RUMOURED; + } + } + ret = dst_key_gettime(key->key, DST_TIME_PUBLISH, &pub); + if (pub <= now && ret == ISC_R_SUCCESS) { + dns_ttl_t zone_ttl = dns_kasp_zonemaxttl(kasp); + zone_ttl += dns_kasp_zonepropagationdelay(kasp); + if ((pub + zone_ttl) <= now) { + zrrsig_state = OMNIPRESENT; + } else { + zrrsig_state = RUMOURED; + } + } + 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; + } else { + ds_state = RUMOURED; + } + } + + /* Set key states for all keys that do not have them. */ + INITIALIZE_STATE(key->key, DST_KEY_DNSKEY, DST_TIME_DNSKEY, + dnskey_state); + if (ksk) { + INITIALIZE_STATE(key->key, DST_KEY_KRRSIG, DST_TIME_KRRSIG, + dnskey_state); + INITIALIZE_STATE(key->key, DST_KEY_DS, DST_TIME_DS, ds_state); + } + if (zsk) { + INITIALIZE_STATE(key->key, DST_KEY_ZRRSIG, DST_TIME_ZRRSIG, + zrrsig_state); + } } /* @@ -1302,23 +1361,13 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, { bool found_match = false; - /* Make sure this key knows about roles. */ - keymgr_key_init_role(dkey); + keymgr_key_init(dkey, kasp, now); for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) { if (keymgr_dnsseckey_kaspkey_match(dkey, kkey)) { found_match = true; - dst_key_format(dkey->key, keystr, - sizeof(keystr)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, - DNS_LOGMODULE_DNSSEC, - ISC_LOG_DEBUG(1), - "keymgr: DNSKEY %s (%s) matches " - "policy %s", - keystr, keymgr_keyrole(dkey->key), - dns_kasp_getname(kasp)); break; } } @@ -1343,8 +1392,17 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, { if (keymgr_dnsseckey_kaspkey_match(dkey, kkey)) { /* Found a match. */ + dst_key_format(dkey->key, keystr, + sizeof(keystr)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, + ISC_LOG_DEBUG(1), + "keymgr: DNSKEY %s (%s) matches " + "policy %s", + keystr, keymgr_keyrole(dkey->key), + dns_kasp_getname(kasp)); - /* Initialize lifetime. */ + /* Initialize lifetime if not set. */ uint32_t l; if (dst_key_getnum(dkey->key, DST_NUM_LIFETIME, &l) != ISC_R_SUCCESS) { @@ -1353,18 +1411,49 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, lifetime); } - if (dst_key_goal(dkey->key) == OMNIPRESENT) { - if (active_key != NULL) { + if (active_key) { + /* We already have an active key that + * matches the kasp policy. + */ + if (!dst_key_is_unused(dkey->key) && + (dst_key_goal(dkey->key) == + OMNIPRESENT) && + !keymgr_key_is_successor( + dkey->key, + active_key->key) && + !keymgr_key_is_successor( + active_key->key, dkey->key)) + { /* * Multiple signing keys match * the kasp key configuration. - * Retire excess keys. + * Retire excess keys in use. */ keymgr_key_retire(dkey, now); - } else { - /* Save the matched key. */ - active_key = dkey; } + continue; + } + + /* + * This is possibly an active key created + * outside dnssec-policy. Initialize goal, + * if not set. + */ + dst_key_state_t goal; + if (dst_key_getstate(dkey->key, DST_KEY_GOAL, + &goal) != ISC_R_SUCCESS) { + dst_key_setstate(dkey->key, + DST_KEY_GOAL, + OMNIPRESENT); + } + + /* + * Save the matched key only if it is active + * or desires to be active. + */ + if (dst_key_goal(dkey->key) == OMNIPRESENT || + dst_key_is_active(dkey->key, now)) { + active_key = dkey; } } } @@ -1377,7 +1466,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, isc_log_write( dns_lctx, DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(1), - "keymgr: DNSKEY %s (%s) matches " + "keymgr: DNSKEY %s (%s) is active in " "policy %s", keystr, keymgr_keyrole(active_key->key), dns_kasp_getname(kasp)); @@ -1446,6 +1535,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, dst_key_setttl(dst_key, dns_kasp_dnskeyttl(kasp)); dst_key_settime(dst_key, DST_TIME_CREATED, now); RETERR(dns_dnsseckey_create(mctx, &dst_key, &newkey)); + keymgr_key_init(newkey, kasp, now); } else { newkey = candidate; dst_key_setnum(newkey->key, DST_NUM_LIFETIME, lifetime); @@ -1512,27 +1602,6 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, ISC_LIST_APPENDLIST(*keyring, newkeys, link); } - /* Initialize key states (for keys that don't have them yet). */ - for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; - dkey = ISC_LIST_NEXT(dkey, link)) - { - bool ksk = false, zsk = false; - - /* Set key states for all keys that do not have them. */ - INITIALIZE_STATE(dkey->key, DST_KEY_DNSKEY, DST_TIME_DNSKEY); - (void)dst_key_getbool(dkey->key, DST_BOOL_KSK, &ksk); - if (ksk) { - INITIALIZE_STATE(dkey->key, DST_KEY_KRRSIG, - DST_TIME_KRRSIG); - INITIALIZE_STATE(dkey->key, DST_KEY_DS, DST_TIME_DS); - } - (void)dst_key_getbool(dkey->key, DST_BOOL_ZSK, &zsk); - if (zsk) { - INITIALIZE_STATE(dkey->key, DST_KEY_ZRRSIG, - DST_TIME_ZRRSIG); - } - } - /* Read to update key states. */ keymgr_update(keyring, kasp, now, nexttime);