diff --git a/CHANGES b/CHANGES index c18cc63dfc..615a84f0b0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +4928. [func] The "dnskey-sig-validity" option allows + "sig-validity-interval" to be overriden for signatures + covering DNSKEY RRsets. [GL #145] + 4927. [placeholder] 4926. [func] Add root key sentinel support. To disable, add diff --git a/bin/named/config.c b/bin/named/config.c index a2d2eb7ea7..c2ff14a975 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -248,6 +248,7 @@ options {\n\ sig-signing-signatures 10;\n\ sig-signing-type 65534;\n\ sig-validity-interval 30; /* days */\n\ + dnskey-sig-validity 0; /* default: sig-validity-interval */\n\ transfer-source *;\n\ transfer-source-v6 *;\n\ try-tcp-refresh yes; /* BIND 8 compat */\n\ diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index d54de81e1e..0a15234d4a 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1440,6 +1440,12 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, if (ztype == dns_zone_master || raw != NULL) { isc_boolean_t allow = ISC_FALSE, maint = ISC_FALSE; + obj = NULL; + result = named_config_get(maps, "dnskey-sig-validity", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + seconds = cfg_obj_asuint32(obj) * 86400; + dns_zone_setkeyvalidityinterval(zone, seconds); + obj = NULL; result = named_config_get(maps, "sig-validity-interval", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); diff --git a/bin/tests/system/checkconf/bad-dnskey-validity.conf b/bin/tests/system/checkconf/bad-dnskey-validity.conf new file mode 100644 index 0000000000..16beccf527 --- /dev/null +++ b/bin/tests/system/checkconf/bad-dnskey-validity.conf @@ -0,0 +1,14 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + dnskey-sig-validity 5000; /* maximum value 10 years, this is 14 */ +}; diff --git a/bin/tests/system/checkconf/bad-sig-validity.conf b/bin/tests/system/checkconf/bad-sig-validity.conf new file mode 100644 index 0000000000..3934e15390 --- /dev/null +++ b/bin/tests/system/checkconf/bad-sig-validity.conf @@ -0,0 +1,14 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + sig-validity-interval 5000; +}; diff --git a/bin/tests/system/dnssec/clean.sh b/bin/tests/system/dnssec/clean.sh index 29ac1695b9..a8b2ae12f2 100644 --- a/bin/tests/system/dnssec/clean.sh +++ b/bin/tests/system/dnssec/clean.sh @@ -96,3 +96,4 @@ rm -f signer/general/signed.zone rm -f signer/general/signer.out.* rm -f signer/general/dsset* rm -f signing.out* +rm -f python.out.* diff --git a/bin/tests/system/dnssec/ns3/siginterval2.conf b/bin/tests/system/dnssec/ns3/siginterval2.conf index 9fab130f47..eeeddcc3f3 100644 --- a/bin/tests/system/dnssec/ns3/siginterval2.conf +++ b/bin/tests/system/dnssec/ns3/siginterval2.conf @@ -13,6 +13,7 @@ zone "siginterval.example" { type master; allow-update { any; }; sig-validity-interval 35 28; + dnskey-sig-validity 90; auto-dnssec maintain; file "siginterval.example.db"; }; diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 70b69e44c8..bf73aa21fd 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -2997,6 +2997,28 @@ n=`expr $n + 1` if test "$before" = "$after" ; then echo_i "failed"; ret=1; fi status=`expr $status + $ret` +if [ -x "$PYTHON" ]; then + echo_i "check dnskey-sig-validity sets longer expiry for DNSKEY ($n)" + ret=0 + $RNDCCMD 10.53.0.3 sign siginterval.example 2>&1 | sed 's/^/ns3 /' | cat_i + # convert expiry date to a comma-separated list of integers python can + # use as input to date(). strip leading 0s in months and days so + # python3 will recognize them as integers. + soaexpire=`$DIG +dnssec +short -p ${PORT} @10.53.0.3 soa siginterval.example | awk '$1 ~ /SOA/ { print $5 }' | sed 's/\(....\)\(..\)\(..\).*/\1, \2, \3/' | sed 's/ 0/ /'` + dnskeyexpire=`$DIG +dnssec +short -p ${PORT} @10.53.0.3 dnskey siginterval.example | awk '$1 ~ /DNSKEY/ { print $5; exit 0 }' | sed 's/\(....\)\(..\)\(..\).*/\1, \2, \3/' | sed 's/ 0/ /'` + $PYTHON > python.out.$n <&1 | sed 's/^/ns4 /' | cat_i sleep 3 diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index d1e8a8d621..3f025f15a7 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -8973,6 +8973,11 @@ avoid-v6-udp-ports { 40000; range 50000 60000; }; set to one hour before the current time to allow for a limited amount of clock skew. + + The sig-validity-interval can be + overridden for DNSKEY records by setting + dnskey-sig-validity. + The sig-validity-interval should be, at least, several multiples of the SOA @@ -8982,6 +8987,24 @@ avoid-v6-udp-ports { 40000; range 50000 60000; }; + + dnskey-sig-validity + + + Specifies the number of days into the future when + DNSSEC signatures that are automatically generated + for DNSKEY RRsets as a result of dynamic updates + () will expire. + If set to a non-zero value, this overrides the + value set by sig-validity-interval. + The default is zero, meaning + sig-validity-interval is used. + The maximum value is 3660 days (10 years), and + higher values will be rejected. + + + + sig-signing-nodes diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 574ede8c1d..9a9c0df887 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -78,6 +78,13 @@ 'root-key-sentinel no;' to named.conf. + + + The dnskey-sig-validity option allows the + sig-validity-interval to be overriden for + signatures covering DNSKEY RRsets. [GL #145] + + diff --git a/lib/bind9/check.c b/lib/bind9/check.c index db9226907c..d57d4ee00e 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1084,6 +1084,22 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, } } + obj = NULL; + cfg_map_get(options, "dnskey-sig-validity", &obj); + if (obj != NULL) { + isc_uint32_t keyvalidity; + + keyvalidity = cfg_obj_asuint32(obj); + if (keyvalidity > 3660 || keyvalidity == 0) { /* 10 years */ + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%u' is out of range (1..3660)", + "dnskey-sig-validity", + keyvalidity); + result = ISC_R_RANGE; + } + + } + obj = NULL; (void)cfg_map_get(options, "preferred-glue", &obj); if (obj != NULL) { diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index d3fed857c3..22fc47df9c 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1444,9 +1444,9 @@ dns_zone_getmgr(dns_zone_t *zone); void dns_zone_setsigvalidityinterval(dns_zone_t *zone, isc_uint32_t interval); /*%< - * Set the zone's RRSIG validity interval. This is the length of time - * for which DNSSEC signatures created as a result of dynamic updates - * to secure zones will remain valid, in seconds. + * Set the zone's general signature validity interval. This is the length + * of time for which DNSSEC signatures created as a result of dynamic + * updates to secure zones will remain valid, in seconds. * * Requires: * \li 'zone' to be a valid zone. @@ -1455,7 +1455,33 @@ dns_zone_setsigvalidityinterval(dns_zone_t *zone, isc_uint32_t interval); isc_uint32_t dns_zone_getsigvalidityinterval(dns_zone_t *zone); /*%< - * Get the zone's RRSIG validity interval. + * Get the zone's general signature validity interval. + * + * Requires: + * \li 'zone' to be a valid zone. + */ + +void +dns_zone_setkeyvalidityinterval(dns_zone_t *zone, isc_uint32_t interval); +/*%< + * Set the zone's DNSKEY signature validity interval. This is the length + * of time for which DNSSEC signatures created for DNSKEY records + * will remain valid, in seconds. + * + * If this value is set to zero, then the regular signature validity + * interval (see dns_zone_setsigvalidityinterval(), above) is used + * for all RRSIGs. However, if this value is nonzero, then it is used + * as the validity interval for RRSIGs covering DNSKEY and CDNSKEY + * RRsets. + * + * Requires: + * \li 'zone' to be a valid zone. + */ + +isc_uint32_t +dns_zone_getkeyvalidityinterval(dns_zone_t *zone); +/*%< + * Get the zone's DNSKEY signature validity interval. * * Requires: * \li 'zone' to be a valid zone. diff --git a/lib/dns/update.c b/lib/dns/update.c index b2934e2f91..cb703bdbe0 100644 --- a/lib/dns/update.c +++ b/lib/dns/update.c @@ -1362,7 +1362,7 @@ struct dns_update_state { dns_diff_t work; dst_key_t *zone_keys[DNS_MAXZONEKEYS]; unsigned int nkeys; - isc_stdtime_t inception, expire; + isc_stdtime_t inception, expire, keyexpire; dns_ttl_t nsecttl; isc_boolean_t check_ksk, keyset_kskonly, build_nsec3; enum { sign_updates, remove_orphaned, build_chain, process_nsec, @@ -1423,6 +1423,12 @@ dns_update_signaturesinc(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, isc_stdtime_get(&now); state->inception = now - 3600; /* Allow for some clock skew. */ state->expire = now + sigvalidityinterval; + state->keyexpire = dns_zone_getkeyvalidityinterval(zone); + if (state->keyexpire == 0) { + state->keyexpire = state->expire; + } else { + state->keyexpire += now; + } /* * Do we look at the KSK flag on the DNSKEY to determining which @@ -1518,13 +1524,22 @@ dns_update_signaturesinc(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, CHECK(rrset_visible(db, newver, name, type, &flag)); if (flag) { + isc_stdtime_t exp; + if (type == dns_rdatatype_dnskey || + type == dns_rdatatype_cdnskey || + type == dns_rdatatype_cds) + { + exp = state->keyexpire; + } else { + exp = state->expire; + } + CHECK(add_sigs(log, zone, db, newver, name, type, &state->sig_diff, state->zone_keys, state->nkeys, - state->inception, - state->expire, + state->inception, exp, state->check_ksk, state->keyset_kskonly)); sigs++; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 4ba32d5801..78082fbbe9 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -301,6 +301,7 @@ struct dns_zone { isc_event_t ctlevent; dns_ssutable_t *ssutable; isc_uint32_t sigvalidityinterval; + isc_uint32_t keyvalidityinterval; isc_uint32_t sigresigninginterval; dns_view_t *view; dns_view_t *prev_view; @@ -1013,6 +1014,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->maxxfrout = MAX_XFER_TIME; zone->ssutable = NULL; zone->sigvalidityinterval = 30 * 24 * 3600; + zone->keyvalidityinterval = 0; zone->sigresigninginterval = 7 * 24 * 3600; zone->view = NULL; zone->prev_view = NULL; @@ -7286,7 +7288,8 @@ need_nsec_chain(dns_db_t *db, dns_dbversion_t *ver, static isc_result_t update_sigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version, dst_key_t *zone_keys[], unsigned int nkeys, dns_zone_t *zone, - isc_stdtime_t inception, isc_stdtime_t expire, isc_stdtime_t now, + isc_stdtime_t inception, isc_stdtime_t expire, + isc_stdtime_t keyexpire, isc_stdtime_t now, isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly, zonediff_t *zonediff) { @@ -7296,6 +7299,16 @@ update_sigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version, for (tuple = ISC_LIST_HEAD(diff->tuples); tuple != NULL; tuple = ISC_LIST_HEAD(diff->tuples)) { + 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)) + { + exp = keyexpire; + } + result = del_sigs(zone, db, version, &tuple->name, tuple->rdata.type, zonediff, zone_keys, nkeys, now, ISC_FALSE); @@ -7308,7 +7321,7 @@ update_sigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version, result = add_sigs(db, version, &tuple->name, tuple->rdata.type, zonediff->diff, zone_keys, nkeys, zone->mctx, inception, - expire, check_ksk, keyset_kskonly); + exp, check_ksk, keyset_kskonly); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "update_sigs:add_sigs -> %s", @@ -7961,7 +7974,7 @@ zone_nsec3chain(dns_zone_t *zone) { if (nsec3chain != NULL) dns_dbiterator_pause(nsec3chain->dbiterator); result = update_sigs(&nsec3_diff, db, version, zone_keys, - nkeys, zone, inception, expire, now, + nkeys, zone, inception, expire, 0, now, check_ksk, keyset_kskonly, &zonediff); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" @@ -7974,7 +7987,7 @@ zone_nsec3chain(dns_zone_t *zone) { * above so we need to update the signatures. */ result = update_sigs(¶m_diff, db, version, zone_keys, - nkeys, zone, inception, expire, now, + nkeys, zone, inception, expire, 0, now, check_ksk, keyset_kskonly, &zonediff); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" @@ -7994,7 +8007,7 @@ zone_nsec3chain(dns_zone_t *zone) { } result = update_sigs(&nsec_diff, db, version, zone_keys, - nkeys, zone, inception, expire, now, + nkeys, zone, inception, expire, 0, now, check_ksk, keyset_kskonly, &zonediff); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" @@ -8577,7 +8590,7 @@ zone_sign(dns_zone_t *zone) { if (ISC_LIST_HEAD(post_diff.tuples) != NULL) { result = update_sigs(&post_diff, db, version, zone_keys, - nkeys, zone, inception, expire, now, + nkeys, zone, inception, expire, 0, now, check_ksk, keyset_kskonly, &zonediff); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "zone_sign:" @@ -15333,6 +15346,20 @@ dns_zone_getsigvalidityinterval(dns_zone_t *zone) { return (zone->sigvalidityinterval); } +void +dns_zone_setkeyvalidityinterval(dns_zone_t *zone, isc_uint32_t interval) { + REQUIRE(DNS_ZONE_VALID(zone)); + + zone->keyvalidityinterval = interval; +} + +isc_uint32_t +dns_zone_getkeyvalidityinterval(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + + return (zone->keyvalidityinterval); +} + void dns_zone_setsigresigninginterval(dns_zone_t *zone, isc_uint32_t interval) { isc_time_t now; @@ -17477,7 +17504,7 @@ sign_apex(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_t now, dns_diff_t *diff, zonediff_t *zonediff) { isc_result_t result; - isc_stdtime_t inception, soaexpire; + isc_stdtime_t inception, soaexpire, keyexpire; isc_boolean_t check_ksk, keyset_kskonly; dst_key_t *zone_keys[DNS_MAXZONEKEYS]; unsigned int nkeys = 0, i; @@ -17495,6 +17522,13 @@ sign_apex(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, inception = now - 3600; /* Allow for clock skew. */ soaexpire = now + dns_zone_getsigvalidityinterval(zone); + keyexpire = dns_zone_getkeyvalidityinterval(zone); + if (keyexpire == 0) { + keyexpire = soaexpire; + } else { + keyexpire += now; + } + check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK); keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY); @@ -17523,7 +17557,7 @@ sign_apex(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, } result = add_sigs(db, ver, &zone->origin, dns_rdatatype_dnskey, zonediff->diff, zone_keys, nkeys, zone->mctx, - inception, soaexpire, check_ksk, + inception, keyexpire, check_ksk, keyset_kskonly); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -17534,8 +17568,8 @@ sign_apex(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, } result = update_sigs(diff, db, ver, zone_keys, nkeys, zone, - inception, soaexpire, now, check_ksk, - keyset_kskonly, zonediff); + inception, soaexpire, keyexpire, now, + check_ksk, keyset_kskonly, zonediff); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 07feeb3713..870c61df59 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2188,6 +2188,9 @@ zone_clauses[] = { { "sig-validity-interval", &cfg_type_validityinterval, CFG_ZONE_MASTER | CFG_ZONE_SLAVE }, + { "dnskey-sig-validity", &cfg_type_uint32, + CFG_ZONE_MASTER | CFG_ZONE_SLAVE + }, { "transfer-source", &cfg_type_sockaddr4wild, CFG_ZONE_SLAVE | CFG_ZONE_STUB }, diff --git a/util/copyrights b/util/copyrights index 84485efe45..bf3d26b062 100644 --- a/util/copyrights +++ b/util/copyrights @@ -633,6 +633,7 @@ ./bin/tests/system/checkconf/bad-acl.conf CONF-C 2016,2018 ./bin/tests/system/checkconf/bad-also-notify.conf CONF-C 2012,2013,2016,2018 ./bin/tests/system/checkconf/bad-catz-zone.conf CONF-C 2016,2018 +./bin/tests/system/checkconf/bad-dnskey-validity.conf CONF-C 2018 ./bin/tests/system/checkconf/bad-dnssec.conf CONF-C 2012,2013,2016,2018 ./bin/tests/system/checkconf/bad-glue-cache-bogus.conf CONF-C 2017,2018 ./bin/tests/system/checkconf/bad-hint.conf CONF-C 2014,2016,2018 @@ -674,6 +675,7 @@ ./bin/tests/system/checkconf/bad-sharedwritable2.conf CONF-C 2014,2016,2018 ./bin/tests/system/checkconf/bad-sharedzone1.conf CONF-C 2013,2016,2018 ./bin/tests/system/checkconf/bad-sharedzone2.conf CONF-C 2013,2016,2018 +./bin/tests/system/checkconf/bad-sig-validity.conf CONF-C 2018 ./bin/tests/system/checkconf/bad-tsig.conf CONF-C 2012,2013,2016,2018 ./bin/tests/system/checkconf/bad-update-policy1.conf CONF-C 2018 ./bin/tests/system/checkconf/bad-update-policy2.conf CONF-C 2018