diff --git a/CHANGES b/CHANGES index d9a0c0e083..45bbc7a4be 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,11 @@ 'host -C' commands when one of the name servers returns SERVFAIL. [GL #4508] +6313. [bug] When dnssec-policy is in effect the DNSKEY's TTLs in + the zone where not being updated to match the policy. + This lead to failures when DNSKEYs where updated as the + TTLs mismatched. [GL #4466] + 6312. [bug] Conversion from NSEC3 signed to NSEC signed could temporarily put the zone into a state where it was treated as unsigned until the NSEC chain was built. diff --git a/bin/tests/system/autosign/ns3/nozsk.example.db.in b/bin/tests/system/autosign/ns3/nozsk.example.db.in index 1376922f0a..5d244c3fc6 100644 --- a/bin/tests/system/autosign/ns3/nozsk.example.db.in +++ b/bin/tests/system/autosign/ns3/nozsk.example.db.in @@ -9,7 +9,7 @@ ; See the COPYRIGHT file distributed with this work for additional ; information regarding copyright ownership. -$TTL 300 ; 5 minutes +$TTL 3600 ; 1 hour @ IN SOA mname1. . ( 1 ; serial 20 ; refresh (20 seconds) diff --git a/bin/tests/system/kasp.sh b/bin/tests/system/kasp.sh index 26487cdbc8..60d77335ec 100644 --- a/bin/tests/system/kasp.sh +++ b/bin/tests/system/kasp.sh @@ -213,6 +213,7 @@ set_policy() { POLICY=$1 NUM_KEYS=$2 DNSKEY_TTL=$3 + KEYFILE_TTL=$3 CDS_DELETE="no" } # By default policies are considered to be secure. @@ -329,7 +330,7 @@ check_key() { _alg_numpad=$(printf "%03d" "$_alg_num") _alg_string=$(key_get "$1" ALG_STR) _length=$(key_get "$1" "ALG_LEN") - _dnskey_ttl="$DNSKEY_TTL" + _dnskey_ttl="$KEYFILE_TTL" _lifetime=$(key_get "$1" LIFETIME) _legacy=$(key_get "$1" LEGACY) _private=$(key_get "$1" PRIVATE) @@ -1055,7 +1056,7 @@ _find_dnskey() { _flags="$(key_get $1 FLAGS)" _key_file="$(key_get $1 BASEFILE).key" - awk '$1 == "'"$_owner"'" && $2 == "'"$DNSKEY_TTL"'" && $3 == "IN" && $4 == "DNSKEY" && $5 == "'"$_flags"'" && $6 == "3" && $7 == "'"$_alg"'" { print $8 }' <"$_key_file" + awk '$1 == "'"$_owner"'" && $2 == "'"$KEYFILE_TTL"'" && $3 == "IN" && $4 == "DNSKEY" && $5 == "'"$_flags"'" && $6 == "3" && $7 == "'"$_alg"'" { print $8 }' <"$_key_file" } # Test DNSKEY query. diff --git a/bin/tests/system/kasp/ns3/named-fips.conf.in b/bin/tests/system/kasp/ns3/named-fips.conf.in index 8debcd2b51..54ce749d8e 100644 --- a/bin/tests/system/kasp/ns3/named-fips.conf.in +++ b/bin/tests/system/kasp/ns3/named-fips.conf.in @@ -287,6 +287,16 @@ zone "expired-sigs.autosign" { dnssec-policy "autosign"; }; +/* + * Zone that has DNSKEY TTL mismatch with the dnssec-policy. + */ +zone "dnskey-ttl-mismatch.autosign" { + type primary; + file "dnskey-ttl-mismatch.autosign.db"; + inline-signing yes; + dnssec-policy "autosign"; +}; + /* * Zone that has valid, fresh signatures. */ diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index 41cf379734..3ab2f0da2f 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -198,6 +198,14 @@ private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" cp $infile $zonefile $SIGNER -PS -x -s now-2mo -e now-1mo -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +# The DNSKEY's TTLs do not match the policy. +setup dnskey-ttl-mismatch.autosign +KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 30 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) +ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 30 $zsktimes $zone 2>keygen.out.$zone.2) +cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" +cp $infile $zonefile +$SIGNER -PS -x -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # These signatures are still good, and can be reused. setup fresh-sigs.autosign T="now-6mo" diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 611c7177aa..c16f3cd542 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -1375,6 +1375,48 @@ check_rrsig_refresh() { check_rrsig_refresh +# +# Zone: dnskey-ttl-mismatch.autosign +# +set_zone "dnskey-ttl-mismatch.autosign" +set_policy "autosign" "2" "300" +set_server "ns3" "10.53.0.3" +# Key properties. +key_clear "KEY1" +set_keyrole "KEY1" "ksk" +set_keylifetime "KEY1" "63072000" +set_keyalgorithm "KEY1" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS" +set_keysigning "KEY1" "yes" +set_zonesigning "KEY1" "no" + +key_clear "KEY2" +set_keyrole "KEY2" "zsk" +set_keylifetime "KEY2" "31536000" +set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS" +set_keysigning "KEY2" "no" +set_zonesigning "KEY2" "yes" + +# Both KSK and ZSK stay OMNIPRESENT. +set_keystate "KEY1" "GOAL" "omnipresent" +set_keystate "KEY1" "STATE_DNSKEY" "omnipresent" +set_keystate "KEY1" "STATE_KRRSIG" "omnipresent" +set_keystate "KEY1" "STATE_DS" "omnipresent" + +set_keystate "KEY2" "GOAL" "omnipresent" +set_keystate "KEY2" "STATE_DNSKEY" "omnipresent" +set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent" +# Expect only two keys. +key_clear "KEY3" +key_clear "KEY4" + +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +set_keytimes_autosign_policy +check_keytimes +check_apex +check_subdomain +dnssec_verify + # # Zone: fresh-sigs.autosign. # @@ -3970,7 +4012,7 @@ dnssec_verify # Zone: step1.going-insecure.kasp # set_zone "step1.going-insecure.kasp" -set_policy "insecure" "2" "7200" +set_policy "insecure" "2" "3600" set_server "ns6" "10.53.0.6" # Expect a CDS/CDNSKEY Delete Record. set_cdsdelete @@ -4007,7 +4049,7 @@ check_next_key_event 93600 # Zone: step2.going-insecure.kasp # set_zone "step2.going-insecure.kasp" -set_policy "insecure" "2" "7200" +set_policy "insecure" "2" "3600" set_server "ns6" "10.53.0.6" # The DS is long enough removed from the zone to be considered HIDDEN. @@ -4037,7 +4079,7 @@ check_next_key_event 7500 # set_zone "step1.going-insecure-dynamic.kasp" set_dynamic -set_policy "insecure" "2" "7200" +set_policy "insecure" "2" "3600" set_server "ns6" "10.53.0.6" # Expect a CDS/CDNSKEY Delete Record. set_cdsdelete @@ -4075,7 +4117,7 @@ check_next_key_event 93600 # set_zone "step2.going-insecure-dynamic.kasp" set_dynamic -set_policy "insecure" "2" "7200" +set_policy "insecure" "2" "3600" set_server "ns6" "10.53.0.6" # The DS is long enough removed from the zone to be considered HIDDEN. diff --git a/bin/tests/system/keymgr2kasp/tests.sh b/bin/tests/system/keymgr2kasp/tests.sh index 57c1445f19..e1ca49d2f7 100644 --- a/bin/tests/system/keymgr2kasp/tests.sh +++ b/bin/tests/system/keymgr2kasp/tests.sh @@ -499,7 +499,7 @@ status=$((status + ret)) # Testing a good migration (CSK). # set_zone "csk.kasp" -set_policy "default" "1" "7200" +set_policy "default" "1" "3600" set_server "ns3" "10.53.0.3" key_clear "KEY1" @@ -549,7 +549,7 @@ status=$((status + ret)) # Testing a good migration (CSK, no SEP). # set_zone "csk-nosep.kasp" -set_policy "default" "1" "7200" +set_policy "default" "1" "3600" set_server "ns3" "10.53.0.3" key_clear "KEY1" diff --git a/bin/tests/system/nsec3/tests.sh b/bin/tests/system/nsec3/tests.sh index fc864a437b..6cb28583c6 100644 --- a/bin/tests/system/nsec3/tests.sh +++ b/bin/tests/system/nsec3/tests.sh @@ -40,6 +40,7 @@ set_zone_policy() { POLICY=$2 NUM_KEYS=$3 DNSKEY_TTL=$4 + KEYFILE_TTL=$4 } # Set expected NSEC3 parameters: flags ($1), iterations ($2), and # salt length ($3). diff --git a/bin/tests/system/statschannel/ns2/sign.sh b/bin/tests/system/statschannel/ns2/sign.sh index 5a507daa63..d2da0128fe 100644 --- a/bin/tests/system/statschannel/ns2/sign.sh +++ b/bin/tests/system/statschannel/ns2/sign.sh @@ -19,8 +19,8 @@ set -e zone=dnssec. infile=dnssec.db.in zonefile=dnssec.db.signed -ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "$zone") -zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone") +ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -L 3600 -b "$DEFAULT_BITS" -f KSK "$zone") +zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -L 3600 -b "$DEFAULT_BITS" "$zone") # Sign deliberately with a very short expiration date. "$SIGNER" -P -S -x -O full -e "now"+1s -o "$zone" -f "$zonefile" "$infile" >"signzone.out.$zone" 2>&1 keyfile_to_key_id "$ksk" >dnssec.ksk.id @@ -29,12 +29,12 @@ keyfile_to_key_id "$zsk" >dnssec.zsk.id zone=manykeys. infile=manykeys.db.in zonefile=manykeys.db.signed -ksk8=$("$KEYGEN" -q -a RSASHA256 -b 2048 -f KSK "$zone") -zsk8=$("$KEYGEN" -q -a RSASHA256 -b 2048 "$zone") -ksk13=$("$KEYGEN" -q -a ECDSAP256SHA256 -b 256 -f KSK "$zone") -zsk13=$("$KEYGEN" -q -a ECDSAP256SHA256 -b 256 "$zone") -ksk14=$("$KEYGEN" -q -a ECDSAP384SHA384 -b 384 -f KSK "$zone") -zsk14=$("$KEYGEN" -q -a ECDSAP384SHA384 -b 384 "$zone") +ksk8=$("$KEYGEN" -q -a RSASHA256 -L 3600 -b 2048 -f KSK "$zone") +zsk8=$("$KEYGEN" -q -a RSASHA256 -L 3600 -b 2048 "$zone") +ksk13=$("$KEYGEN" -q -a ECDSAP256SHA256 -L 3600 -b 256 -f KSK "$zone") +zsk13=$("$KEYGEN" -q -a ECDSAP256SHA256 -L 3600 -b 256 "$zone") +ksk14=$("$KEYGEN" -q -a ECDSAP384SHA384 -L 3600 -b 384 -f KSK "$zone") +zsk14=$("$KEYGEN" -q -a ECDSAP384SHA384 -L 3600 -b 384 "$zone") # Sign deliberately with a very short expiration date. "$SIGNER" -S -x -O full -e "now"+1s -o "$zone" -f "$zonefile" "$infile" >"signzone.out.$zone" 2>&1 keyfile_to_key_id "$ksk8" >manykeys.ksk8.id diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 5678c05cbd..88b4ebd9e2 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -1846,9 +1846,9 @@ failure: return (result); } -static isc_result_t -make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize, - dns_rdata_t *target) { +isc_result_t +dns_dnssec_make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize, + dns_rdata_t *target) { isc_result_t result; isc_buffer_t b; isc_region_t r; @@ -1904,7 +1904,7 @@ publish_key(dns_diff_t *diff, dns_dnsseckey_t *key, const dns_name_t *origin, dns_rdata_t dnskey = DNS_RDATA_INIT; dns_rdata_reset(&dnskey); - RETERR(make_dnskey(key->key, buf, sizeof(buf), &dnskey)); + RETERR(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); dst_key_format(key->key, keystr, sizeof(keystr)); report("Fetching %s (%s) from key %s.", keystr, @@ -1944,7 +1944,7 @@ remove_key(dns_diff_t *diff, dns_dnsseckey_t *key, const dns_name_t *origin, report("Removing %s key %s/%d/%s from DNSKEY RRset.", reason, namebuf, dst_key_id(key->key), alg); - RETERR(make_dnskey(key->key, buf, sizeof(buf), &dnskey)); + RETERR(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); result = delrdata(&dnskey, diff, origin, ttl, mctx); failure: @@ -1983,6 +1983,19 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, unsigned char keybuf[DST_KEY_MAXSIZE]; isc_result_t result; dns_dnsseckey_t *key; + dns_ttl_t cdsttl = ttl; + dns_ttl_t cdnskeyttl = ttl; + + REQUIRE(keys != NULL); + REQUIRE(rmkeys != NULL); + + if (dns_rdataset_isassociated(cds)) { + cdsttl = cds->ttl; + } + + if (dns_rdataset_isassociated(cdnskey)) { + cdnskeyttl = cdnskey->ttl; + } for (key = ISC_LIST_HEAD(*keys); key != NULL; key = ISC_LIST_NEXT(key, link)) @@ -1992,8 +2005,8 @@ 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); - RETERR(make_dnskey(key->key, keybuf, sizeof(keybuf), - &cdnskeyrdata)); + RETERR(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), + &cdnskeyrdata)); /* * We construct the SHA-1 version of the record so we can @@ -2030,7 +2043,7 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, "CDS for key %s is now published", keystr); RETERR(addrdata(&cdnskeyrdata, diff, origin, - ttl, mctx)); + cdnskeyttl, mctx)); } /* Only publish SHA-256 (SHA-1 is deprecated) */ if (!dns_rdataset_isassociated(cds) || @@ -2041,8 +2054,8 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, "CDNSKEY for key %s is now published", keystr); - RETERR(addrdata(&cds_sha256, diff, origin, ttl, - mctx)); + RETERR(addrdata(&cds_sha256, diff, origin, + cdsttl, mctx)); } } @@ -2114,8 +2127,8 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, char keystr[DST_KEY_FORMATSIZE]; dst_key_format(key->key, keystr, sizeof(keystr)); - RETERR(make_dnskey(key->key, keybuf, sizeof(keybuf), - &cdnskeyrdata)); + RETERR(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), + &cdnskeyrdata)); if (dns_rdataset_isassociated(cds)) { RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata, diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h index 6add7d50d1..cb8fd9dc20 100644 --- a/lib/dns/include/dns/dnssec.h +++ b/lib/dns/include/dns/dnssec.h @@ -95,6 +95,23 @@ dns_dnssec_keyfromrdata(const dns_name_t *name, const dns_rdata_t *rdata, *\li various errors from dns_name_totext */ +isc_result_t +dns_dnssec_make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize, + dns_rdata_t *target); +/*%< + * Convert a DST key into a DNS record. + * + * Requires: + *\li 'key' is not NULL + *\li 'buf' is not NULL + *\li 'bufsize' equals DST_KEY_MAXSIZE + *\li 'target' is not NULL + * + * Returns: + *\li #ISC_R_SUCCESS + *\li various errors from dst_key_todns + */ + isc_result_t dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, isc_stdtime_t *inception, isc_stdtime_t *expire, diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h index 257a22c9fd..51dc0804b8 100644 --- a/lib/dns/include/dns/rdata.h +++ b/lib/dns/include/dns/rdata.h @@ -572,6 +572,13 @@ dns_rdatatype_isdnssec(dns_rdatatype_t type); * \li 'type' is a valid rdata type. */ +bool +dns_rdatatype_iskeymaterial(dns_rdatatype_t type); +/*%< + * Return true iff the rdata type 'type' is a DNSSEC key + * related type, like DNSKEY, CDNSKEY, or CDS. + */ + bool dns_rdatatype_iszonecutauth(dns_rdatatype_t type); /*%< diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 10ed86c4e3..597265393c 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -392,6 +392,24 @@ dns_zone_unlock_keyfiles(dns_zone_t *zone); *\li 'zone' to be a valid zone. */ +isc_result_t +dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse); +/*%< + * Check if the DNSKEY record 'rdata' is used by 'zone' for zone signing. + * Store the result in 'inuse'. + * + * Require: + *\li 'zone' to be a valid zone. + *\li 'rdata' to represent a DNSKEY, CDNSKEY, or CDS record. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li Any error result from dns_dnssec_keyfromrdata, dns_rdata_tostruct, + * dns_dnssec_make_dnskey, dns_ds_buildrdata, or + * dns_dnssec_findmatchingkeys. + * + */ + isc_result_t dns_zone_load(dns_zone_t *zone, bool newonly); diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 106b376ef0..34fa1dd0cc 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -2218,11 +2218,16 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; dkey = ISC_LIST_NEXT(dkey, link)) { - if (dst_key_ismodified(dkey->key) && !dkey->purge) { + bool modified = dst_key_ismodified(dkey->key); + if (dst_key_getttl(dkey->key) != dns_kasp_dnskeyttl(kasp)) { + dst_key_setttl(dkey->key, dns_kasp_dnskeyttl(kasp)); + modified = true; + } + if (modified && !dkey->purge) { dns_dnssec_get_hints(dkey, now); RETERR(dst_key_tofile(dkey->key, options, directory)); - dst_key_setmodified(dkey->key, false); } + dst_key_setmodified(dkey->key, false); } result = ISC_R_SUCCESS; diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c index 592b9746da..227dfbc795 100644 --- a/lib/dns/rdata.c +++ b/lib/dns/rdata.c @@ -2262,6 +2262,12 @@ dns_rdatatype_isdnssec(dns_rdatatype_t type) { return (false); } +bool +dns_rdatatype_iskeymaterial(dns_rdatatype_t type) { + return (type == dns_rdatatype_dnskey || type == dns_rdatatype_cdnskey || + type == dns_rdatatype_cds); +} + bool dns_rdatatype_iszonecutauth(dns_rdatatype_t type) { if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ZONECUTAUTH) != diff --git a/lib/dns/update.c b/lib/dns/update.c index c3f162ad10..a94d35276e 100644 --- a/lib/dns/update.c +++ b/lib/dns/update.c @@ -1205,10 +1205,7 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, } } - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { /* * DNSKEY RRset is signed with KSK. * CDS and CDNSKEY RRsets too (RFC 7344, 4.1). @@ -1242,10 +1239,7 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, /* * CDS and CDNSKEY are signed with KSK (RFC 7344, 4.1). */ - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { if (!KSK(keys[i]) && keyset_kskonly) { continue; } @@ -1675,10 +1669,7 @@ next_state: &flag)); if (flag) { isc_stdtime_t exp; - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { exp = state->keyexpire; } else if (type == dns_rdatatype_soa) { exp = state->soaexpire; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 729adcb6e4..712cb2c023 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -6777,9 +6777,7 @@ del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, result = dns_rdata_tostruct(&rdata, &rrsig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if (type != dns_rdatatype_dnskey && type != dns_rdatatype_cds && - type != dns_rdatatype_cdnskey) - { + if (!dns_rdatatype_iskeymaterial(type)) { bool warn = false, deleted = false; if (delsig_ok(&rrsig, keys, nkeys, kasp, &warn)) { result = update_one_rr(db, ver, zonediff->diff, @@ -7097,10 +7095,7 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_zone_t *zone, both = have_ksk && have_zsk; } - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { /* * DNSKEY RRset is signed with KSK. * CDS and CDNSKEY RRsets too (RFC 7344, 4.1). @@ -7140,10 +7135,7 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_zone_t *zone, /* * CDS and CDNSKEY are signed with KSK (RFC 7344, 4.1). */ - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { if (!KSK(keys[i]) && keyset_kskonly) { continue; } @@ -7545,9 +7537,7 @@ signed_with_good_key(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, } KASP_UNLOCK(kasp); - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { /* * CDS and CDNSKEY are signed with KSK like DNSKEY. * (RFC 7344, section 4.1 specifies that they must @@ -7723,10 +7713,7 @@ sign_a_node(dns_db_t *db, dns_zone_t *zone, dns_name_t *name, { goto next_rdataset; } - if (rdataset.type == dns_rdatatype_dnskey || - rdataset.type == dns_rdatatype_cdnskey || - rdataset.type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(rdataset.type)) { /* * CDS and CDNSKEY are signed with KSK like DNSKEY. * (RFC 7344, section 4.1 specifies that they must @@ -8324,9 +8311,7 @@ dns__zone_updatesigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version, isc_stdtime_t exp = expire; if (keyexpire != 0 && - (tuple->rdata.type == dns_rdatatype_dnskey || - tuple->rdata.type == dns_rdatatype_cdnskey || - tuple->rdata.type == dns_rdatatype_cds)) + dns_rdatatype_iskeymaterial(tuple->rdata.type)) { exp = keyexpire; } @@ -16344,6 +16329,186 @@ update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) { dns_zone_log(zone, level, "%s", message); } +static isc_result_t +dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, isc_mem_t *mctx, + dns_dnsseckeylist_t *keylist, bool *inuse) { + isc_result_t result; + dst_key_t *dstkey = NULL; + + result = dns_dnssec_keyfromrdata(dns_zone_getorigin(zone), rdata, mctx, + &dstkey); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_dnssec_keyfromrdata() failed: %s", + isc_result_totext(result)); + return (result); + } + + for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL; + k = ISC_LIST_NEXT(k, link)) + { + if (dst_key_pubcompare(k->key, dstkey, false)) { + *inuse = true; + break; + } + } + + dst_key_free(&dstkey); + return (ISC_R_SUCCESS); +} + +static isc_result_t +cdnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, + dns_dnsseckeylist_t *keylist, bool *inuse) { + isc_result_t result; + dns_rdata_cdnskey_t cdnskey; + + result = dns_rdata_tostruct(rdata, &cdnskey, NULL); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_rdata_tostruct(cdnskey) failed: %s", + isc_result_totext(result)); + return (result); + } + + for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL; + k = ISC_LIST_NEXT(k, link)) + { + dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT; + unsigned char keybuf[DST_KEY_MAXSIZE]; + + result = dns_dnssec_make_dnskey(k->key, keybuf, sizeof(keybuf), + &cdnskeyrdata); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_dnssec_make_dnskey() failed: %s", + isc_result_totext(result)); + return (result); + } + + cdnskeyrdata.type = dns_rdatatype_cdnskey; + if (dns_rdata_compare(rdata, &cdnskeyrdata) == 0) { + *inuse = true; + break; + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +cds_inuse(dns_zone_t *zone, dns_rdata_t *rdata, dns_dnsseckeylist_t *keylist, + bool *inuse) { + isc_result_t result; + dns_rdata_ds_t cds; + + result = dns_rdata_tostruct(rdata, &cds, NULL); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_rdata_tostruct(cds) failed: %s", + isc_result_totext(result)); + return (result); + } + + for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL; + k = ISC_LIST_NEXT(k, link)) + { + dns_rdata_t dnskey = DNS_RDATA_INIT; + dns_rdata_t cdsrdata = DNS_RDATA_INIT; + unsigned char keybuf[DST_KEY_MAXSIZE]; + unsigned char cdsbuf[DNS_DS_BUFFERSIZE]; + + if (dst_key_id(k->key) != cds.key_tag || + dst_key_alg(k->key) != cds.algorithm) + { + continue; + } + result = dns_dnssec_make_dnskey(k->key, keybuf, sizeof(keybuf), + &dnskey); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_dnssec_make_dnskey() failed: %s", + isc_result_totext(result)); + return (result); + } + result = dns_ds_buildrdata(dns_zone_getorigin(zone), &dnskey, + cds.digest_type, cdsbuf, &cdsrdata); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_ds_buildrdata(keytag=%d, algo=%d, " + "digest=%d) failed: %s", + cds.key_tag, cds.algorithm, + cds.digest_type, + isc_result_totext(result)); + return (result); + } + + cdsrdata.type = dns_rdatatype_cds; + if (dns_rdata_compare(rdata, &cdsrdata) == 0) { + *inuse = true; + break; + } + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse) { + dns_dnsseckeylist_t keylist; + dns_dnsseckey_t *key = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_stdtime_t now = 0; + isc_mem_t *mctx; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(dns_rdatatype_iskeymaterial(rdata->type)); + + mctx = zone->mctx; + + isc_stdtime_get(&now); + + ISC_LIST_INIT(keylist); + + *inuse = false; + + dns_zone_lock_keyfiles(zone); + result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), + dns_zone_getkeydirectory(zone), + now, mctx, &keylist); + dns_zone_unlock_keyfiles(zone); + if (result == ISC_R_NOTFOUND) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_dnssec_findmatchingkeys() failed: %s", + isc_result_totext(result)); + return (result); + } + + switch (rdata->type) { + case dns_rdatatype_dnskey: + result = dnskey_inuse(zone, rdata, mctx, &keylist, inuse); + break; + case dns_rdatatype_cdnskey: + result = cdnskey_inuse(zone, rdata, &keylist, inuse); + break; + case dns_rdatatype_cds: + result = cds_inuse(zone, rdata, &keylist, inuse); + break; + default: + UNREACHABLE(); + break; + } + + while (!ISC_LIST_EMPTY(keylist)) { + key = ISC_LIST_HEAD(keylist); + ISC_LIST_UNLINK(keylist, key, link); + dns_dnsseckey_destroy(mctx, &key); + } + return (result); +} + static isc_result_t sync_secure_journal(dns_zone_t *zone, dns_zone_t *raw, dns_journal_t *journal, uint32_t start, uint32_t end, dns_difftuple_t **soatuplep, @@ -16423,6 +16588,77 @@ failure: return (result); } +/* + * Filter the key material preserving TTL changes. If kasp in effect honour the + * existing ttl. The lists returned by sync_secure_db/dns_db_diffx should be + * DNSSEC RRset order so we can process 'del' and 'add' in parallel rather than + * searching for TTL only changes first and processing them, then checking the + * 'in use' status on a subsequent pass. + */ + +static void +filter_keymaterial(dns_zone_t *zone, dns_difftuplelist_t *del, + dns_difftuplelist_t *add, bool kasp, dns_ttl_t ttl) { + dns_difftuple_t *deltuple = ISC_LIST_HEAD(*del); + dns_difftuple_t *addtuple = ISC_LIST_HEAD(*add); + isc_result_t result; + + while (deltuple != NULL || addtuple != NULL) { + dns_difftuple_t *delnext = NULL, *addnext = NULL; + bool inuse = false; + if (deltuple != NULL) { + delnext = ISC_LIST_NEXT(deltuple, link); + } + if (addtuple != NULL) { + addnext = ISC_LIST_NEXT(addtuple, link); + } + if (deltuple != NULL && addtuple != NULL) { + int n = dns_rdata_compare(&deltuple->rdata, + &addtuple->rdata); + if (n == 0) { + /* + * If the rdata is equal then the only + * difference will be a TTL change. + */ + if (kasp) { + /* TTL is managed by dnssec-policy */ + ISC_LIST_UNLINK(*del, deltuple, link); + dns_difftuple_free(&deltuple); + ISC_LIST_UNLINK(*add, addtuple, link); + dns_difftuple_free(&addtuple); + } + deltuple = delnext; + addtuple = addnext; + continue; + } + if (n < 0) { + goto checkdel; + } + goto checkadd; + } else if (deltuple != NULL) { + checkdel: + result = dns_zone_dnskey_inuse(zone, &deltuple->rdata, + &inuse); + if (result == ISC_R_SUCCESS && inuse) { + ISC_LIST_UNLINK(*del, deltuple, link); + dns_difftuple_free(&deltuple); + } + deltuple = delnext; + } else { + checkadd: + result = dns_zone_dnskey_inuse(zone, &addtuple->rdata, + &inuse); + if (result == ISC_R_SUCCESS && inuse) { + ISC_LIST_UNLINK(*add, addtuple, link); + dns_difftuple_free(&addtuple); + } else if (kasp) { + addtuple->ttl = ttl; + } + addtuple = addnext; + } + } +} + static isc_result_t sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, dns_dbversion_t *secver, dns_difftuple_t **soatuple, @@ -16433,6 +16669,22 @@ sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, dns_difftuple_t *tuple = NULL, *next; dns_difftuple_t *oldtuple = NULL, *newtuple = NULL; dns_rdata_soa_t oldsoa, newsoa; + dns_difftuplelist_t add = ISC_LIST_INITIALIZER; + dns_difftuplelist_t del = ISC_LIST_INITIALIZER; +#if 0 + dns_difftuplelist_t keyadd = ISC_LIST_INITIALIZER; + dns_difftuplelist_t keydel = ISC_LIST_INITIALIZER; +#endif + dns_difftuplelist_t ckeyadd = ISC_LIST_INITIALIZER; + dns_difftuplelist_t ckeydel = ISC_LIST_INITIALIZER; + dns_difftuplelist_t cdsadd = ISC_LIST_INITIALIZER; + dns_difftuplelist_t cdsdel = ISC_LIST_INITIALIZER; + dns_kasp_t *kasp = NULL; +#if 0 + dns_ttl_t keyttl = 0; +#endif + dns_ttl_t ckeyttl = 0; + dns_ttl_t cdsttl = 0; REQUIRE(DNS_ZONE_VALID(seczone)); REQUIRE(soatuple != NULL && *soatuple == NULL); @@ -16451,10 +16703,60 @@ sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, return (result); } + /* + * If kasp is in effect honour the existing DNSKEY, CDNSKEY and CDS + * TTLs. + */ + kasp = seczone->kasp; + if (kasp != NULL) { + dns_rdataset_t rdataset; + dns_dbnode_t *node = NULL; + dns_ttl_t ttl = dns_kasp_dnskeyttl(kasp); + + dns_rdataset_init(&rdataset); + + result = dns_db_getoriginnode(secdb, &node); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + +#if 0 + result = dns_db_findrdataset( + secdb, node, secver, dns_rdatatype_dnskey, + dns_rdatatype_none, 0, &rdataset, NULL); + keyttl = (result == ISC_R_SUCCESS) ? rdataset.ttl : ttl; + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } +#endif + + result = dns_db_findrdataset( + secdb, node, secver, dns_rdatatype_cdnskey, + dns_rdatatype_none, 0, &rdataset, NULL); + ckeyttl = (result == ISC_R_SUCCESS) ? rdataset.ttl : ttl; + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + + result = dns_db_findrdataset( + secdb, node, secver, dns_rdatatype_cds, + dns_rdatatype_none, 0, &rdataset, NULL); + cdsttl = (result == ISC_R_SUCCESS) ? rdataset.ttl : ttl; + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + dns_db_detachnode(secdb, &node); + } + for (tuple = ISC_LIST_HEAD(diff->tuples); tuple != NULL; tuple = next) { + dns_difftuplelist_t *al = &add, *dl = &del; + next = ISC_LIST_NEXT(tuple, link); + + /* + * Skip DNSSEC records that BIND maintains with inline-signing. + */ if (tuple->rdata.type == dns_rdatatype_nsec || tuple->rdata.type == dns_rdatatype_rrsig || + /* if dnskey is removed adjust switch below */ tuple->rdata.type == dns_rdatatype_dnskey || tuple->rdata.type == dns_rdatatype_nsec3 || tuple->rdata.type == dns_rdatatype_nsec3param) @@ -16463,6 +16765,34 @@ sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, dns_difftuple_free(&tuple); continue; } + + /* + * Apex DNSKEY, CDNSKEY and CDS need special processing so + * split them out. + */ + if (dns_rdatatype_iskeymaterial(tuple->rdata.type) && + dns_name_equal(&tuple->name, &seczone->origin)) + { + switch (tuple->rdata.type) { +#if 0 + case dns_rdatatype_dnskey: + al = &keyadd; + dl = &keydel; + break; +#endif + case dns_rdatatype_cdnskey: + al = &ckeyadd; + dl = &ckeydel; + break; + case dns_rdatatype_cds: + al = &cdsadd; + dl = &cdsdel; + break; + default: + UNREACHABLE(); + } + } + if (tuple->rdata.type == dns_rdatatype_soa) { if (tuple->op == DNS_DIFFOP_DEL) { INSIST(oldtuple == NULL); @@ -16473,6 +16803,23 @@ sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, newtuple = tuple; } } + + /* + * Split into deletions and additions. + */ + ISC_LIST_UNLINK(diff->tuples, tuple, link); + switch (tuple->op) { + case DNS_DIFFOP_DEL: + case DNS_DIFFOP_DELRESIGN: + ISC_LIST_APPEND(*dl, tuple, link); + break; + case DNS_DIFFOP_ADD: + case DNS_DIFFOP_ADDRESIGN: + ISC_LIST_APPEND(*al, tuple, link); + break; + default: + UNREACHABLE(); + } } if (oldtuple != NULL && newtuple != NULL) { @@ -16494,13 +16841,38 @@ sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, dns_name_equal(&oldsoa.origin, &newsoa.origin) && dns_name_equal(&oldsoa.contact, &newsoa.contact)) { - ISC_LIST_UNLINK(diff->tuples, oldtuple, link); + ISC_LIST_UNLINK(del, oldtuple, link); dns_difftuple_free(&oldtuple); - ISC_LIST_UNLINK(diff->tuples, newtuple, link); + ISC_LIST_UNLINK(add, newtuple, link); dns_difftuple_free(&newtuple); } } + /* + * Filter out keys we manage but still allow TTL changes. + */ +#if 0 + filter_keymaterial(seczone, &keydel, &keyadd, kasp != NULL, keyttl); +#endif + filter_keymaterial(seczone, &ckeydel, &ckeyadd, kasp != NULL, ckeyttl); + filter_keymaterial(seczone, &cdsdel, &cdsadd, kasp != NULL, cdsttl); + + /* + * Rebuild the diff now that we have filtered it + */ + ISC_LIST_APPENDLIST(diff->tuples, del, link); +#if 0 + ISC_LIST_APPENDLIST(diff->tuples, keydel, link); +#endif + ISC_LIST_APPENDLIST(diff->tuples, ckeydel, link); + ISC_LIST_APPENDLIST(diff->tuples, cdsdel, link); + ISC_LIST_APPENDLIST(diff->tuples, add, link); +#if 0 + ISC_LIST_APPENDLIST(diff->tuples, keyadd, link); +#endif + ISC_LIST_APPENDLIST(diff->tuples, ckeyadd, link); + ISC_LIST_APPENDLIST(diff->tuples, cdsadd, link); + if (ISC_LIST_EMPTY(diff->tuples)) { return (DNS_R_UNCHANGED); } @@ -20382,7 +20754,8 @@ failure: static isc_result_t add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, dns_dbversion_t *ver, dns_diff_t *diff, bool sign_all) { - dns_difftuple_t *tuple, *newtuple = NULL; + dns_difftuple_t *tuple = NULL, *newtuple = NULL, *next = NULL; + dns_difftuple_t *addtuple = NULL, *deltuple = NULL; dns_rdata_dnskey_t dnskey; dns_rdata_t rdata = DNS_RDATA_INIT; bool flag; @@ -20391,11 +20764,20 @@ add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, uint16_t keyid; unsigned char buf[5]; dns_name_t *name = dns_db_origin(db); + dns_difftuplelist_t add = ISC_LIST_INITIALIZER; + dns_difftuplelist_t del = ISC_LIST_INITIALIZER; + dns_difftuplelist_t tuples = ISC_LIST_INITIALIZER; + /* + * Move non DNSKEY and not DNSSEC DNSKEY records to tuples + * and sort the remaining DNSKEY records to add and del. + */ for (tuple = ISC_LIST_HEAD(diff->tuples); tuple != NULL; - tuple = ISC_LIST_NEXT(tuple, link)) + tuple = ISC_LIST_HEAD(diff->tuples)) { if (tuple->rdata.type != dns_rdatatype_dnskey) { + ISC_LIST_UNLINK(diff->tuples, tuple, link); + ISC_LIST_APPEND(tuples, tuple, link); continue; } @@ -20404,9 +20786,65 @@ add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, if ((dnskey.flags & (DNS_KEYFLAG_OWNERMASK | DNS_KEYTYPE_NOAUTH)) != DNS_KEYOWNER_ZONE) { + ISC_LIST_UNLINK(diff->tuples, tuple, link); + ISC_LIST_APPEND(tuples, tuple, link); continue; } + ISC_LIST_UNLINK(diff->tuples, tuple, link); + switch (tuple->op) { + case DNS_DIFFOP_DEL: + case DNS_DIFFOP_DELRESIGN: + ISC_LIST_APPEND(del, tuple, link); + break; + case DNS_DIFFOP_ADD: + case DNS_DIFFOP_ADDRESIGN: + ISC_LIST_APPEND(add, tuple, link); + break; + default: + UNREACHABLE(); + } + } + + /* + * Put the tuples that don't need more processing back onto + * diff->tuples. + */ + ISC_LIST_APPENDLIST(diff->tuples, tuples, link); + + /* + * Filter out DNSKEY TTL changes and put them back onto diff->tuples. + */ + for (deltuple = ISC_LIST_HEAD(del); deltuple != NULL; deltuple = next) { + next = ISC_LIST_NEXT(deltuple, link); + for (addtuple = ISC_LIST_HEAD(add); addtuple != NULL; + addtuple = ISC_LIST_NEXT(addtuple, link)) + { + int n = dns_rdata_compare(&deltuple->rdata, + &addtuple->rdata); + if (n == 0) { + ISC_LIST_UNLINK(del, deltuple, link); + ISC_LIST_APPEND(diff->tuples, deltuple, link); + ISC_LIST_UNLINK(add, addtuple, link); + ISC_LIST_APPEND(diff->tuples, addtuple, link); + break; + } + } + } + + /* + * Combine any remaining DNSKEY changes together. + */ + ISC_LIST_APPENDLIST(tuples, add, link); + ISC_LIST_APPENDLIST(tuples, del, link); + + /* + * Add private records for keys that have been removed + * or added. + */ + for (tuple = ISC_LIST_HEAD(tuples); tuple != NULL; + tuple = ISC_LIST_NEXT(tuple, link)) + { dns_rdata_toregion(&tuple->rdata, &r); keyid = dst_region_computeid(&r); @@ -20446,7 +20884,15 @@ add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, INSIST(newtuple == NULL); } } + failure: + /* + * Put the DNSKEY changes we cared about back on diff->tuples. + */ + ISC_LIST_APPENDLIST(diff->tuples, tuples, link); + INSIST(ISC_LIST_EMPTY(add)); + INSIST(ISC_LIST_EMPTY(del)); + INSIST(ISC_LIST_EMPTY(tuples)); return (result); } @@ -21437,6 +21883,55 @@ zone_checkds(dns_zone_t *zone) { } } +static isc_result_t +update_ttl(dns_rdataset_t *rdataset, dns_name_t *name, dns_ttl_t ttl, + dns_diff_t *diff) { + isc_result_t result; + + /* + * Delete everything using the existing TTL. + */ + for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + dns_difftuple_t *tuple = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(rdataset, &rdata); + result = dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, name, + rdataset->ttl, &rdata, &tuple); + if (result != ISC_R_SUCCESS) { + return (result); + } + dns_diff_appendminimal(diff, &tuple); + } + if (result != ISC_R_NOMORE) { + return (result); + } + + /* + * Add everything using the new TTL. + */ + for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + dns_difftuple_t *tuple = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(rdataset, &rdata); + result = dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, + ttl, &rdata, &tuple); + if (result != ISC_R_SUCCESS) { + return (result); + } + dns_diff_appendminimal(diff, &tuple); + } + if (result != ISC_R_NOMORE) { + return (result); + } + return (ISC_R_SUCCESS); +} + static void zone_rekey(dns_zone_t *zone) { isc_result_t result; @@ -21494,11 +21989,39 @@ zone_rekey(dns_zone_t *zone) { ttl = soaset.ttl; dns_rdataset_disassociate(&soaset); + /* + * Only update DNSKEY TTL if we have a policy. + */ + if (kasp != NULL) { + ttl = dns_kasp_dnskeyttl(kasp); + } + /* Get the DNSKEY rdataset */ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, dns_rdatatype_none, 0, &keyset, &keysigs); if (result == ISC_R_SUCCESS) { - ttl = keyset.ttl; + /* + * If we don't have a policy then use the DNSKEY ttl + * if it exists. Otherwise update the DNSKEY ttl if + * needed. + */ + if (kasp == NULL) { + ttl = keyset.ttl; + } else if (ttl != keyset.ttl) { + result = update_ttl(&keyset, &zone->origin, ttl, &diff); + if (result != ISC_R_SUCCESS) { + dnssec_log(zone, ISC_LOG_ERROR, + "Updating DNSKEY TTL from %u to %u " + "failed: %s", + keyset.ttl, ttl, + isc_result_totext(result)); + goto failure; + } + dnssec_log(zone, ISC_LOG_INFO, + "Updating DNSKEY TTL from %u to %u", + keyset.ttl, ttl); + keyset.ttl = ttl; + } dns_zone_lock_keyfiles(zone); @@ -21520,6 +22043,18 @@ zone_rekey(dns_zone_t *zone) { dns_rdatatype_none, 0, &cdsset, NULL); if (result != ISC_R_SUCCESS && dns_rdataset_isassociated(&cdsset)) { dns_rdataset_disassociate(&cdsset); + } else if (result == ISC_R_SUCCESS && kasp != NULL && ttl != cdsset.ttl) + { + result = update_ttl(&cdsset, &zone->origin, ttl, &diff); + if (result != ISC_R_SUCCESS) { + dnssec_log(zone, ISC_LOG_ERROR, + "Updating CDS TTL from %u to %u failed: %s", + cdsset.ttl, ttl, isc_result_totext(result)); + goto failure; + } + dnssec_log(zone, ISC_LOG_INFO, "Updating CDS TTL from %u to %u", + cdsset.ttl, ttl); + cdsset.ttl = ttl; } /* Get the CDNSKEY rdataset */ @@ -21527,6 +22062,21 @@ zone_rekey(dns_zone_t *zone) { dns_rdatatype_none, 0, &cdnskeyset, NULL); if (result != ISC_R_SUCCESS && dns_rdataset_isassociated(&cdnskeyset)) { dns_rdataset_disassociate(&cdnskeyset); + } else if (result == ISC_R_SUCCESS && kasp != NULL && + ttl != cdnskeyset.ttl) + { + result = update_ttl(&cdnskeyset, &zone->origin, ttl, &diff); + if (result != ISC_R_SUCCESS) { + dnssec_log( + zone, ISC_LOG_ERROR, + "Updating CDNSKEY TTL from %u to %u failed: %s", + cdnskeyset.ttl, ttl, isc_result_totext(result)); + goto failure; + } + dnssec_log(zone, ISC_LOG_INFO, + "Updating CDNSKEY TTL from %u to %u", cdnskeyset.ttl, + ttl); + cdnskeyset.ttl = ttl; } /* @@ -21659,13 +22209,6 @@ zone_rekey(dns_zone_t *zone) { } } - /* - * Only update DNSKEY TTL if we have a policy. - */ - if (kasp != NULL) { - ttl = dns_kasp_dnskeyttl(kasp); - } - result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys, &zone->origin, ttl, &diff, mctx, dnssec_report); diff --git a/lib/ns/query.c b/lib/ns/query.c index c1e91484cf..7c98d1ab47 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -12352,9 +12352,7 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) { /* * Turn on minimal response for (C)DNSKEY and (C)DS queries. */ - if (qtype == dns_rdatatype_dnskey || qtype == dns_rdatatype_ds || - qtype == dns_rdatatype_cdnskey || qtype == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(qtype) || qtype == dns_rdatatype_ds) { client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); } else if (qtype == dns_rdatatype_ns) { diff --git a/lib/ns/update.c b/lib/ns/update.c index 983ca84e26..d404dfa786 100644 --- a/lib/ns/update.c +++ b/lib/ns/update.c @@ -3390,6 +3390,27 @@ update_action(isc_task_t *task, isc_event_t *event) { continue; } } + /* + * Don't remove DNSKEY, CDNSKEY, CDS records + * that are in use (under our control). + */ + if (dns_rdatatype_iskeymaterial(rdata.type)) { + isc_result_t r; + bool inuse = false; + r = dns_zone_dnskey_inuse(zone, &rdata, + &inuse); + if (r != ISC_R_SUCCESS) { + FAIL(r); + } + if (inuse) { + update_log(client, zone, + LOGLEVEL_PROTOCOL, + "attempt to " + "delete in use " + "DNSKEY ignored"); + continue; + } + } } dns_name_format(name, namestr, sizeof(namestr)); dns_rdatatype_format(rdata.type, typestr,