diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 42f2eef2ab..a79a871cc6 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -700,7 +700,7 @@ grep "status: NOERROR" dig.out.$DIR.test$n > /dev/null || log_error "mismatch st grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${qtype}.*257.*.3.*${KEY1[$ALG_NUM]}" dig.out.$DIR.test$n > /dev/null || log_error "missing ${qtype} record in response" lines=$(get_keys_which_signed $qtype dig.out.$DIR.test$n | wc -l) test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" -get_keys_which_signed $qtype dig.out.$DIR.test$n | grep "^${KEY_ID}$" > /dev/null || log_error "${qtype} RRset not signed with ${KEY_ID}" +get_keys_which_signed $qtype dig.out.$DIR.test$n | grep "^${KEY_ID}$" > /dev/null || log_error "${qtype} RRset not signed with key ${KEY_ID}" test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) @@ -714,7 +714,7 @@ grep "status: NOERROR" dig.out.$DIR.test$n > /dev/null || log_error "mismatch st grep "${ZONE}\..*${DEFAULT_TTL}.*IN.*${qtype}.*mname1\..*\." dig.out.$DIR.test$n > /dev/null || log_error "missing ${qtype} record in response" lines=$(get_keys_which_signed $qtype dig.out.$DIR.test$n | wc -l) test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" -get_keys_which_signed $qtype dig.out.$DIR.test$n | grep "^${KEY_ID}$" > /dev/null || log_error "${qtype} RRset not signed with ${KEY_ID}" +get_keys_which_signed $qtype dig.out.$DIR.test$n | grep "^${KEY_ID}$" > /dev/null || log_error "${qtype} RRset not signed with key ${KEY_ID}" test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) @@ -735,14 +735,14 @@ do grep "a.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.11" dig.out.$DIR.test$n.a > /dev/null || log_error "missing a.${ZONE} A record in response" lines=$(get_keys_which_signed A dig.out.$DIR.test$n.a | wc -l) test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" - get_keys_which_signed A dig.out.$DIR.test$n.a | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with ${KEY_ID}" + get_keys_which_signed A dig.out.$DIR.test$n.a | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with key ${KEY_ID}" dig_with_opts "d.${ZONE}" @10.53.0.3 A > dig.out.$DIR.test$n.d || log_error "dig d.${ZONE} A failed" grep "status: NOERROR" dig.out.$DIR.test$n.d > /dev/null || log_error "mismatch status in DNS response" grep "d.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.4" dig.out.$DIR.test$n.d > /dev/null || log_error "missing d.${ZONE} A record in response" lines=$(get_keys_which_signed A dig.out.$DIR.test$n.d | wc -l) test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" - get_keys_which_signed A dig.out.$DIR.test$n.d | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with ${KEY_ID}" + get_keys_which_signed A dig.out.$DIR.test$n.d | grep "^${KEY_ID}$" > /dev/null || log_error "A RRset not signed with key ${KEY_ID}" i=`expr $i + 1` if [ $ret = 0 ]; then break; fi @@ -870,22 +870,59 @@ check_signatures() { if [ "${KEY1[$_expect_type]}" == "yes" ] && [ "${KEY1[$_role]}" == "yes" ]; then get_keys_which_signed $_qtype $_file | grep "^${KEY1[$ID]}$" > /dev/null || log_error "${_qtype} RRset not signed with key ${KEY1[$ID]}" elif [ "${KEY1[$EXPECT]}" == "yes" ]; then - get_keys_which_signed $_qtype $_file | grep "^${KEY1[$ID]}$" > /dev/null && log_error "${_qtype} RRset signed unexpectedly with ${KEY1[$ID]}" + get_keys_which_signed $_qtype $_file | grep "^${KEY1[$ID]}$" > /dev/null && log_error "${_qtype} RRset signed unexpectedly with key ${KEY1[$ID]}" fi if [ "${KEY2[$_expect_type]}" == "yes" ] && [ "${KEY2[$_role]}" == "yes" ]; then - get_keys_which_signed $_qtype $_file | grep "^${KEY2[$ID]}$" > /dev/null || log_error "${_qtype} RRset not signed with ${KEY2[$ID]}" + get_keys_which_signed $_qtype $_file | grep "^${KEY2[$ID]}$" > /dev/null || log_error "${_qtype} RRset not signed with key ${KEY2[$ID]}" elif [ "${KEY2[$EXPECT]}" == "yes" ]; then - get_keys_which_signed $_qtype $_file | grep "^${KEY2[$ID]}$" > /dev/null && log_error "${_qtype} RRset signed unexpectedly with ${KEY2[$ID]}" + get_keys_which_signed $_qtype $_file | grep "^${KEY2[$ID]}$" > /dev/null && log_error "${_qtype} RRset signed unexpectedly with key ${KEY2[$ID]}" fi if [ "${KEY3[$_expect_type]}" == "yes" ] && [ "${KEY3[$_role]}" == "yes" ]; then - get_keys_which_signed $_qtype $_file | grep "^${KEY3[$ID]}$" > /dev/null || log_error "${_qtype} RRset not signed with ${KEY3[$ID]}" + get_keys_which_signed $_qtype $_file | grep "^${KEY3[$ID]}$" > /dev/null || log_error "${_qtype} RRset not signed with key ${KEY3[$ID]}" elif [ "${KEY3[$EXPECT]}" == "yes" ]; then - get_keys_which_signed $_qtype $_file | grep "^${KEY3[$ID]}$" > /dev/null && log_error "${_qtype} RRset signed unexpectedly with ${KEY3[$ID]}" + get_keys_which_signed $_qtype $_file | grep "^${KEY3[$ID]}$" > /dev/null && log_error "${_qtype} RRset signed unexpectedly with key ${KEY3[$ID]}" fi } +# Test CDS and CDNSKEY publication. +check_cds() { + + _qtype="CDS" + _key_algnum="${KEY1[$ALG_NUM]}" + + n=$((n+1)) + echo_i "check ${_qtype} rrset is signed correctly for zone ${ZONE} ($n)" + ret=0 + dig_with_opts $ZONE @10.53.0.3 $_qtype > dig.out.$DIR.test$n || log_error "dig ${ZONE} ${_qtype} failed" + grep "status: NOERROR" dig.out.$DIR.test$n > /dev/null || log_error "mismatch status in DNS response" + + if [ "${KEY1[$STATE_DS]}" == "rumoured" ] || [ "${KEY1[$STATE_DS]}" == "omnipresent" ]; then + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*${KEY1[$ID]}.*${_key_algnum}.*2" dig.out.$DIR.test$n > /dev/null || log_error "missing ${_qtype} record in response for key ${KEY1[$ID]}" + check_signatures $_qtype dig.out.$DIR.test$n $KSK + elif [ "${KEY1[$EXPECT]}" == "yes" ]; then + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*${KEY1[$ID]}.*${_key_algnum}.*2" dig.out.$DIR.test$n > /dev/null && log_error "unexpected ${_qtype} record in response for key ${KEY1[$ID]}" + fi + + if [ "${KEY2[$STATE_DS]}" == "rumoured" ] || [ "${KEY2[$STATE_DS]}" == "omnipresent" ]; then + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*${KEY2[$ID]}.*${_key_algnum}.*2" dig.out.$DIR.test$n > /dev/null || log_error "missing ${_qtype} record in response for key ${KEY2[$ID]}" + check_signatures $_qtype dig.out.$DIR.test$n $KSK + elif [ "${KEY2[$EXPECT]}" == "yes" ]; then + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*${KEY2[$ID]}.*${_key_algnum}.*2" dig.out.$DIR.test$n > /dev/null && log_error "unexpected ${_qtype} record in response for key ${KEY2[$ID]}" + fi + + if [ "${KEY3[$STATE_DS]}" == "rumoured" ] || [ "${KEY3[$STATE_DS]}" == "omnipresent" ]; then + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*${KEY3[$ID]}.*${_key_algnum}.*2" dig.out.$DIR.test$n > /dev/null || log_error "missing ${_qtype} record in response for key ${KEY3[$ID]}" + check_signatures $_qtype dig.out.$DIR.test$n $KSK + elif [ "${KEY3[$EXPECT]}" == "yes" ]; then + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*${KEY3[$ID]}.*${_key_algnum}.*2" dig.out.$DIR.test$n > /dev/null && log_error "unexpected ${_qtype} record in response for key ${KEY3[$ID]}" + fi + + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +} + # Test the apex of a configured zone. This checks that the SOA and DNSKEY # RRsets are signed correctly and with the appropriate keys. check_apex() { @@ -916,6 +953,9 @@ check_apex() { check_signatures $_qtype dig.out.$DIR.test$n $ZSK test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) + + # Test CDS publication. + check_cds } # Test an RRset below the apex and verify it is signed correctly. diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 388689144b..9e21cc35b1 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -635,7 +635,10 @@ dns_dnssec_keyactive(dst_key_t *key, isc_stdtime_t now) { * Indicate whether a key is scheduled to to have CDS/CDNSKEY records * published now. * - * Returns true iff. + * Returns true if. + * - kasp says the DS record should be published (e.g. the DS state is in + * RUMOURED or OMNIPRESENT state). + * Or: * - SyncPublish is set and in the past, AND * - SyncDelete is unset or in the future */ @@ -643,6 +646,7 @@ static bool syncpublish(dst_key_t *key, isc_stdtime_t now) { isc_result_t result; isc_stdtime_t when; + dst_key_state_t state; int major, minor; /* @@ -654,18 +658,29 @@ syncpublish(dst_key_t *key, isc_stdtime_t now) { /* * Smart signing started with key format 1.3 */ - if (major == 1 && minor <= 2) + if (major == 1 && minor <= 2) { return (false); + } + /* Check kasp state first. */ + result = dst_key_getstate(key, DST_KEY_DS, &state); + if (result == ISC_R_SUCCESS) { + return (state == DST_KEY_STATE_RUMOURED || + state == DST_KEY_STATE_OMNIPRESENT); + } + + /* If no kasp state, check timings. */ result = dst_key_gettime(key, DST_TIME_SYNCPUBLISH, &when); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (false); - + } result = dst_key_gettime(key, DST_TIME_SYNCDELETE, &when); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (true); - if (when <= now) + } + if (when <= now) { return (false); + } return (true); } @@ -673,12 +688,17 @@ syncpublish(dst_key_t *key, isc_stdtime_t now) { * Indicate whether a key is scheduled to to have CDS/CDNSKEY records * deleted now. * - * Returns true iff. SyncDelete is set and in the past. + * Returns true if: + * - kasp says the DS record should be unpublished (e.g. the DS state is in + * UNRETENTIVE or HIDDEN state). + * Or: + * - SyncDelete is set and in the past. */ static bool syncdelete(dst_key_t *key, isc_stdtime_t now) { isc_result_t result; isc_stdtime_t when; + dst_key_state_t state; int major, minor; /* @@ -690,14 +710,25 @@ syncdelete(dst_key_t *key, isc_stdtime_t now) { /* * Smart signing started with key format 1.3. */ - if (major == 1 && minor <= 2) + if (major == 1 && minor <= 2) { return (false); + } + /* Check kasp state first. */ + result = dst_key_getstate(key, DST_KEY_DS, &state); + if (result == ISC_R_SUCCESS) { + return (state == DST_KEY_STATE_UNRETENTIVE || + state == DST_KEY_STATE_HIDDEN); + } + + /* If no kasp state, check timings. */ result = dst_key_gettime(key, DST_TIME_SYNCDELETE, &when); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (false); - if (when <= now) + } + if (when <= now) { return (true); + } return (false); } @@ -2135,6 +2166,9 @@ dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, /* Printable version of key2 (the old key, if any) */ dst_key_format(key2->key, keystr2, sizeof(keystr2)); + /* Copy key metadata. */ + dst_key_copy_metadata(key2->key, key1->key); + /* Match found: remove or update it as needed */ if (key1->hint_remove) { RETERR(remove_key(diff, key2, origin, ttl, mctx,