From a50d707fdcf702e9e07a3ad5ea74761cb6e3d08d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 2 Sep 2019 16:24:48 +0200 Subject: [PATCH] Introduce dnssec-policy configuration This commit introduces the initial `dnssec-policy` configuration statement. It has an initial set of options to deal with signature and key maintenance. Add some checks to ensure that dnssec-policy is configured at the right locations, and that policies referenced to in zone statements actually exist. Add some checks that when a user adds the new `dnssec-policy` configuration, it will no longer contain existing DNSSEC configuration options. Specifically: `inline-signing`, `auto-dnssec`, `dnssec-dnskey-kskonly`, `dnssec-secure-to-insecure`, `update-check-ksk`, `dnssec-update-mode`, `dnskey-sig-validity`, and `sig-validity-interval`. Test a good kasp configuration, and some bad configurations. --- bin/named/named.conf.docbook | 17 ++ bin/tests/system/checkconf/bad-kasp1.conf | 20 +++ bin/tests/system/checkconf/bad-kasp2.conf | 22 +++ bin/tests/system/checkconf/bad-kasp3.conf | 22 +++ bin/tests/system/checkconf/bad-kasp4.conf | 23 +++ bin/tests/system/checkconf/clean.sh | 1 + bin/tests/system/checkconf/good-kasp.conf | 47 +++++ .../kasp-and-other-dnssec-options.conf | 28 +++ bin/tests/system/checkconf/tests.sh | 33 ++++ doc/arm/Bv9ARM-book.xml | 162 ++++++++++++++++++ doc/arm/dnssec-policy.grammar.xml | 25 +++ doc/arm/master.zoneopt.xml | 1 + doc/arm/slave.zoneopt.xml | 1 + doc/design/dnssec-policy | 1 - doc/misc/master.zoneopt | 1 + doc/misc/options | 11 ++ doc/misc/slave.zoneopt | 1 + lib/bind9/check.c | 157 +++++++++++++++-- lib/isccfg/include/isccfg/namedconf.h | 3 + lib/isccfg/namedconf.c | 87 ++++++++++ util/copyrights | 1 + 21 files changed, 653 insertions(+), 11 deletions(-) create mode 100644 bin/tests/system/checkconf/bad-kasp1.conf create mode 100644 bin/tests/system/checkconf/bad-kasp2.conf create mode 100644 bin/tests/system/checkconf/bad-kasp3.conf create mode 100644 bin/tests/system/checkconf/bad-kasp4.conf create mode 100644 bin/tests/system/checkconf/good-kasp.conf create mode 100644 bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf create mode 100644 doc/arm/dnssec-policy.grammar.xml diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook index ecae8014cc..8221d4cce5 100644 --- a/bin/named/named.conf.docbook +++ b/bin/named/named.conf.docbook @@ -842,6 +842,7 @@ view string [ class ] { dnskey-sig-validity integer; dnssec-dnskey-kskonly boolean; dnssec-loadkeys-interval integer; + dnssec-policy string; dnssec-secure-to-insecure boolean; dnssec-update-mode ( maintain | no-resign ); file quoted_string; @@ -943,6 +944,7 @@ zone string [ class ] { dnskey-sig-validity integer; dnssec-dnskey-kskonly boolean; dnssec-loadkeys-interval integer; + dnssec-policy string; dnssec-secure-to-insecure boolean; dnssec-update-mode ( maintain | no-resign ); file quoted_string; @@ -1008,6 +1010,21 @@ zone string [ class ] { + DNSSEC-POLICY + + +dnssec-policy string { + dnskey-ttl ttlval; + keys { ( csk | ksk | zsk ) key-directory duration integer [ integer ] ; ... }; + publish-safety duration; + retire-safety duration; + signatures-refresh duration; + signatures-validity duration; + signatures-validity-dnskey duration; +}; + + + FILES /etc/named.conf diff --git a/bin/tests/system/checkconf/bad-kasp1.conf b/bin/tests/system/checkconf/bad-kasp1.conf new file mode 100644 index 0000000000..bad8ff2090 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp1.conf @@ -0,0 +1,20 @@ +/* + * 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 { + dnssec-policy "notatzonelevel"; +}; + +zone "example.net" { + type master; + file "example.db"; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp2.conf b/bin/tests/system/checkconf/bad-kasp2.conf new file mode 100644 index 0000000000..a7b44ab6d0 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp2.conf @@ -0,0 +1,22 @@ +/* + * 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. + */ + +include "good-kasp.conf"; + +// Bad zone configuration because this has dnssec-policy and other DNSSEC sign +// configuration options (auto-dnssec). +zone "example.net" { + type master; + file "example.db"; + dnssec-policy "test"; + auto-dnssec maintain; + allow-update { any; }; +}; diff --git a/bin/tests/system/checkconf/bad-kasp3.conf b/bin/tests/system/checkconf/bad-kasp3.conf new file mode 100644 index 0000000000..104100dc59 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp3.conf @@ -0,0 +1,22 @@ +/* + * 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. + */ + +include "good-kasp.conf"; + +// Bad zone configuration because this has dnssec-policy with no matching +// dnssec-policy configuration (good-kasp.conf has "test", zone refers to +// "nosuchpolicy". +zone "example.net" { + type master; + file "example.db"; + dnssec-policy "nosuchpolicy"; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp4.conf b/bin/tests/system/checkconf/bad-kasp4.conf new file mode 100644 index 0000000000..efb2cbefa8 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp4.conf @@ -0,0 +1,23 @@ +/* + * 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. + */ + +// Bad kasp configuration because this has an invalid duration for +// signatures-refresh. +dnssec-policy "badduration" { + signatures-refresh PT20Sabcd; +}; + +zone "example.net" { + type master; + file "example.db"; + dnssec-policy "badduration"; +}; + diff --git a/bin/tests/system/checkconf/clean.sh b/bin/tests/system/checkconf/clean.sh index 9ac839b09d..989daff341 100644 --- a/bin/tests/system/checkconf/clean.sh +++ b/bin/tests/system/checkconf/clean.sh @@ -10,6 +10,7 @@ # information regarding copyright ownership. rm -f good.conf.in good.conf.out badzero.conf *.out +rm -f good-kasp.conf.in rm -rf test.keydir rm -f checkconf.out* rm -f diff.out* diff --git a/bin/tests/system/checkconf/good-kasp.conf b/bin/tests/system/checkconf/good-kasp.conf new file mode 100644 index 0000000000..804637a345 --- /dev/null +++ b/bin/tests/system/checkconf/good-kasp.conf @@ -0,0 +1,47 @@ +/* + * 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. + */ + +/* + * This is just a random selection of DNSSEC configuration options. + */ + +/* cut here */ +dnssec-policy "test" { + dnskey-ttl 3600; + keys { + ksk key-directory P1Y 13 256; + zsk key-directory P30D 13; + csk key-directory P30D 8 2048; + }; + publish-safety PT3600S; + retire-safety PT3600S; + signatures-refresh P3D; + signatures-validity P2W; + signatures-validity-dnskey P14D; + zone-max-ttl 86400; + zone-propagation-delay PT5M; + parent-ds-ttl 7200; + parent-propagation-delay PT1H; + parent-registration-delay P1D; +}; +options { + dnssec-policy "default"; +}; +zone "example1" { + type master; + dnssec-policy "test"; + file "example1.db"; +}; +zone "example2" { + type master; + dnssec-policy "default"; + file "example2.db"; +}; diff --git a/bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf b/bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf new file mode 100644 index 0000000000..d7c1bf8123 --- /dev/null +++ b/bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf @@ -0,0 +1,28 @@ +/* + * 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. + */ + +include "good-kasp.conf"; + +zone "nsec3.net" { + type master; + file "nsec3.db"; + dnssec-policy "test"; + auto-dnssec maintain; + dnskey-sig-validity 3600; + dnssec-dnskey-kskonly yes; + dnssec-secure-to-insecure yes; + dnssec-update-mode maintain; + inline-signing yes; + sig-validity-interval 3600; + update-check-ksk yes; + allow-update { any; }; +}; + diff --git a/bin/tests/system/checkconf/tests.sh b/bin/tests/system/checkconf/tests.sh index 6146754320..45e6cefb42 100644 --- a/bin/tests/system/checkconf/tests.sh +++ b/bin/tests/system/checkconf/tests.sh @@ -466,5 +466,38 @@ grep "'geoip-use-ecs' is obsolete" < checkconf.out$n > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; ret=1; fi status=`expr $status + $ret` +n=`expr $n + 1` +echo_i "checking named-checkconf kasp warnings ($n)" +ret=0 +$CHECKCONF kasp-and-other-dnssec-options.conf > checkconf.out$n 2>&1 +grep "'auto-dnssec maintain;' cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 +grep "dnskey-sig-validity: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 +grep "dnssec-dnskey-kskonly: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 +grep "dnssec-secure-to-insecure: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 +grep "dnssec-update-mode: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 +grep "inline-signing: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 +grep "sig-validity-interval: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 +grep "update-check-ksk: cannot be configured if dnssec-policy is also set" < checkconf.out$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "check that a good 'kasp' configuration is accepted ($n)" +ret=0 +$CHECKCONF good-kasp.conf > checkconf.out$n 2>/dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "checking that named-checkconf prints a known good kasp config ($n)" +ret=0 +awk 'BEGIN { ok = 0; } /cut here/ { ok = 1; getline } ok == 1 { print }' good-kasp.conf > good-kasp.conf.in +[ -s good-kasp.conf.in ] || ret=1 +$CHECKCONF -p good-kasp.conf.in | grep -v '^good-kasp.conf.in:' > good-kasp.conf.out 2>&1 || ret=1 +cmp good-kasp.conf.in good-kasp.conf.out || ret=1 + +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 96fcb0fb24..47b08440ef 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -3120,6 +3120,16 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. + + + dnssec-policy + + + + describes a DNSSEC key and signing policy for zones. + + + include @@ -11004,6 +11014,147 @@ example.com CNAME rpz-tcp-only. +
<command>dnssec-policy</command> Statement Grammar + +
+ +
<command>dnssec-policy</command> Statement Definition + and Usage + + + The dnssec-policy statement defines a key and + signing policy (KASP) for zones. + + + KASP is used to determine how one or more zones need to be signed + with DNSSEC. For example, how often RRSIG records need to be + refreshed, or what cryptographic algorithms to use. + + + You can configure multiple policies. To attach a policy to a zone + simply add dnssec-policy "policy_name" + option to the zone statement with a matching + policy name. + + + + + + dnskey-ttl + + + The TTL of the DNSKEY resource records. + Default is 3600 seconds. + + + + + + keys + + + A list of keys to use. Each line represents one key. Here is + an example (for illustration purposes only) of some possible + keys in a dnssec-policy: + + +keys { + ksk key-directory P5Y 8 2048; + zsk key-directory P30D 8; + csk key-directory P6MT12H3M15S 13; +}; + + + + This example lists three keys. The first token determines + what RRsets the key will sign. If set to + ksk the key will sign the DNSKEY, CDS, + and CDNSKEY RRsets, if set to zsk the + key will sign the other RRsets, and if set to + csk the key will sign all RRsets. + + + The following part determines where the key will be stored. + Currently keys can only be stored in the configured + key-directory. + + + The third token tells how long the key may be used. In the + example the first key has a lifetime of 5 years, the second + key may be used for 30 days and the third key has a rather + peculiar lifetime of 6 months, 12 hours, 3 minutes and 15 + seconds. + + + The last token(s) are the key's algorithm and algorithm length. + The length may be omitted as shown in the example for the + second and third key. + + + + + + publish-safety + + + A margin that is added to the publish interval in key timing + equations to give some extra time to cover unforeseen events. + Default is PT5M (5 minutes). + + + + + + retire-safety + + + A margin that is added to the retire interval in key timing + equations to give some extra time to cover unforeseen events. + Default is PT5M (5 minutes). + + + + + + signatures-refresh + + + This determines when a RRSIG record needs to be refreshed. + The signatures is renewed when the time until the expiration + time is closer than signatures-refresh. + signatures-resign interval. + Default is P5D (5 days), meaning a + signature that will expire in 5 days or sooner will be + refreshed. + + + + + + signatures-validity + + + The validity period of an RRSIG record (minus the inception + offset and jitter). Default is P2W + (2 weeks). + + + + + + signatures-validity-dnskey + + + Like signatures-validity but for DNSKEY + records. Default is P2W (2 weeks). + + + + + + +
+
<command>managed-keys</command> Statement Grammar
@@ -11878,6 +12029,17 @@ view "external" { + + dnssec-policy + + + The key and signing policy for this zone. Set to + "default" if you want to make use + of the default policy. + + + + dnssec-update-mode diff --git a/doc/arm/dnssec-policy.grammar.xml b/doc/arm/dnssec-policy.grammar.xml new file mode 100644 index 0000000000..68e27d964c --- /dev/null +++ b/doc/arm/dnssec-policy.grammar.xml @@ -0,0 +1,25 @@ + + + + + +dnssec-policy string { + dnskey-ttl ttlval; + keys { ( csk | ksk | zsk ) key-directory duration integer [ integer ] ; ... }; + publish-safety duration; + retire-safety duration; + signatures-refresh duration; + signatures-validity duration; + signatures-validity-dnskey duration; +}; + + diff --git a/doc/arm/master.zoneopt.xml b/doc/arm/master.zoneopt.xml index 0ed86ecc75..054b440492 100644 --- a/doc/arm/master.zoneopt.xml +++ b/doc/arm/master.zoneopt.xml @@ -36,6 +36,7 @@ dnskey-sig-validity integer; dnssec-dnskey-kskonly boolean; dnssec-loadkeys-interval integer; + dnssec-policy string; dnssec-secure-to-insecure boolean; dnssec-update-mode ( maintain | no-resign ); file quoted_string; diff --git a/doc/arm/slave.zoneopt.xml b/doc/arm/slave.zoneopt.xml index 63c0a4acf1..e78f296119 100644 --- a/doc/arm/slave.zoneopt.xml +++ b/doc/arm/slave.zoneopt.xml @@ -29,6 +29,7 @@ dnskey-sig-validity integer; dnssec-dnskey-kskonly boolean; dnssec-loadkeys-interval integer; + dnssec-policy string; dnssec-update-mode ( maintain | no-resign ); file quoted_string; forward ( first | only ); diff --git a/doc/design/dnssec-policy b/doc/design/dnssec-policy index 2079c8edc2..3e695a2c39 100644 --- a/doc/design/dnssec-policy +++ b/doc/design/dnssec-policy @@ -122,7 +122,6 @@ dnssec-policy "nsec3" { description "policy for zones that require zone walking mitigation"; // Signatures - signatures-resign PT2H; signatures-refresh P3D; signatures-validity P14D; signatures-validity-dnskey P14D; diff --git a/doc/misc/master.zoneopt b/doc/misc/master.zoneopt index aa55ed33e8..694d84eb69 100644 --- a/doc/misc/master.zoneopt +++ b/doc/misc/master.zoneopt @@ -23,6 +23,7 @@ zone [ ] { dnskey-sig-validity ; dnssec-dnskey-kskonly ; dnssec-loadkeys-interval ; + dnssec-policy ; dnssec-secure-to-insecure ; dnssec-update-mode ( maintain | no-resign ); file ; diff --git a/doc/misc/options b/doc/misc/options index 509cc38cf9..6f5674692c 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -25,6 +25,17 @@ dnssec-keys { ( static-key | initial-key ) ; ... }; // may occur multiple times +dnssec-policy { + dnskey-ttl ; + keys { ( csk | ksk | zsk ) key-directory + [ ]; ... }; + publish-safety ; + retire-safety ; + signatures-refresh ; + signatures-validity ; + signatures-validity-dnskey ; +}; // may occur multiple times + dyndb { }; // may occur multiple times diff --git a/doc/misc/slave.zoneopt b/doc/misc/slave.zoneopt index 750392f254..2dc3fd535c 100644 --- a/doc/misc/slave.zoneopt +++ b/doc/misc/slave.zoneopt @@ -16,6 +16,7 @@ zone [ ] { dnskey-sig-validity ; dnssec-dnskey-kskonly ; dnssec-loadkeys-interval ; + dnssec-policy ; dnssec-update-mode ( maintain | no-resign ); file ; forward ( first | only ); diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 86d7482157..73fb59bf82 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -856,6 +856,7 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, const char *str; isc_buffer_t b; uint32_t lifetime = 3600; + bool has_dnssecpolicy = false; const char *ccalg = "siphash24"; /* @@ -948,6 +949,44 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, } } + /* + * Check dnssec-policy at the view/options level + */ + obj = NULL; + (void)cfg_map_get(options, "dnssec-policy", &obj); + if (obj != NULL) { + bool bad_kasp = true; + if (optlevel == optlevel_zone && cfg_obj_isstring(obj)) { + bad_kasp = false; + } else if (optlevel == optlevel_config) { + if (cfg_obj_islist(obj)) { + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + if (!cfg_obj_istuple( + cfg_listelt_value(element))) + { + break; + } + } + bad_kasp = false; + } + } + + if (bad_kasp) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-policy may only be activated at " + "the top level and referenced to at the " + "zone level"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + has_dnssecpolicy = true; + } + obj = NULL; cfg_map_get(options, "max-rsa-exponent-size", &obj); if (obj != NULL) { @@ -996,6 +1035,13 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, result = ISC_R_RANGE; } } + + if (has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "sig-validity-interval: cannot be " + "configured if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } } obj = NULL; @@ -1012,6 +1058,12 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, result = ISC_R_RANGE; } + if (has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnskey-sig-validity: cannot be " + "configured if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } } obj = NULL; @@ -1117,8 +1169,9 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) result = tresult; } - if (symtab != NULL) + if (symtab != NULL) { isc_symtab_destroy(&symtab); + } } /* @@ -1858,6 +1911,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, bool dlz; dns_masterformat_t masterformat; bool ddns = false; + bool has_dnssecpolicy = false; const void *clauses = NULL; const char *option = NULL; static const char *acls[] = { @@ -2070,6 +2124,42 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, if (check_nonzero(zoptions, logctx) != ISC_R_SUCCESS) result = ISC_R_FAILURE; + /* + * Check if a dnssec-policy is set. + */ + obj = NULL; + (void)cfg_map_get(zoptions, "dnssec-policy", &obj); + if (obj != NULL) { + const cfg_obj_t *kasps = NULL; + const char* kaspname = cfg_obj_asstring(obj); + + if (strcmp(kaspname, "default") == 0) { + has_dnssecpolicy = true; + } else { + (void)cfg_map_get(config, "dnssec-policy", &kasps); + for (element = cfg_list_first(kasps); element != NULL; + element = cfg_list_next(element)) + { + const char* kn = cfg_obj_asstring( + cfg_tuple_get(cfg_listelt_value(element), + "name")); + if (strcmp(kaspname, kn) == 0) { + has_dnssecpolicy = true; + } + } + } + + if (!has_dnssecpolicy) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': option 'dnssec-policy %s' " + "has no matching dnssec-policy config", + znamestr, kaspname); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + } + /* * Check validity of the zone options. */ @@ -2256,19 +2346,36 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, if (res1 == ISC_R_SUCCESS) signing = cfg_obj_asboolean(obj); + if (signing && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "inline-signing: cannot be configured if " + "dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + obj = NULL; arg = "off"; res3 = cfg_map_get(zoptions, "auto-dnssec", &obj); - if (res3 == ISC_R_SUCCESS) + if (res3 == ISC_R_SUCCESS) { arg = cfg_obj_asstring(obj); - if (strcasecmp(arg, "off") != 0 && !ddns && !signing) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "'auto-dnssec %s;' requires%s " - "inline-signing to be configured for " - "the zone", arg, - (ztype == CFG_ZONE_MASTER) ? - " dynamic DNS or" : ""); - result = ISC_R_FAILURE; + } + if (strcasecmp(arg, "off") != 0) { + if (!ddns && !signing) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'auto-dnssec %s;' requires%s " + "inline-signing to be configured " + "for the zone", arg, + (ztype == CFG_ZONE_MASTER) ? + " dynamic DNS or" : ""); + result = ISC_R_FAILURE; + } + if (has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'auto-dnssec %s;' cannot be " + "configured if dnssec-policy is " + "also set", arg); + result = ISC_R_FAILURE; + } } obj = NULL; @@ -2293,6 +2400,21 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, "inline-signing when used in slave zone"); result = ISC_R_FAILURE; } + if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-dnskey-kskonly: cannot be " + "configured if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "dnssec-secure-to-insecure", &obj); + if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-secure-to-insecure: cannot be " + "configured if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } obj = NULL; res1 = cfg_map_get(zoptions, "dnssec-loadkeys-interval", &obj); @@ -2315,6 +2437,21 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, "inline-signing when used in slave zone"); result = ISC_R_FAILURE; } + if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "update-check-ksk: cannot be configured " + "if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "dnssec-update-mode", &obj); + if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-update-mode: cannot be configured " + "if dnssec-policy is also set"); + result = ISC_R_FAILURE; + } } /* diff --git a/lib/isccfg/include/isccfg/namedconf.h b/lib/isccfg/include/isccfg/namedconf.h index f75e56191f..f4f2c39ba1 100644 --- a/lib/isccfg/include/isccfg/namedconf.h +++ b/lib/isccfg/include/isccfg/namedconf.h @@ -49,4 +49,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_keyref; /*%< Zone options */ LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_zoneopts; +/*%< DNSSEC Key and Signing Policy options */ +LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_dnssecpolicyopts; + #endif /* ISCCFG_NAMEDCONF_H */ diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 3282a8b01f..100cb0979d 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -82,6 +83,7 @@ static cfg_type_t cfg_type_controls_sockaddr; static cfg_type_t cfg_type_destinationlist; static cfg_type_t cfg_type_dialuptype; static cfg_type_t cfg_type_dlz; +static cfg_type_t cfg_type_dnssecpolicy; static cfg_type_t cfg_type_dnstap; static cfg_type_t cfg_type_dnstapoutput; static cfg_type_t cfg_type_dyndb; @@ -410,6 +412,20 @@ static cfg_type_t cfg_type_zone = { &cfg_rep_tuple, zone_fields }; +/*% + * A dnssec-policy statement. + */ +static cfg_tuplefielddef_t dnssecpolicy_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "options", &cfg_type_dnssecpolicyopts, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_dnssecpolicy = { + "dnssec-policy", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, dnssecpolicy_fields +}; + /*% * A "category" clause in the "logging" statement. */ @@ -465,6 +481,40 @@ static cfg_type_t cfg_type_managedkey = { &cfg_rep_tuple, managedkey_fields }; +/*% + * DNSSEC key roles. + */ +static const char *dnsseckeyrole_enums[] = { "csk", "ksk", "zsk", NULL }; +static cfg_type_t cfg_type_dnsseckeyrole = { + "dnssec-key-role", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, + &cfg_rep_string, &dnsseckeyrole_enums +}; + +/*% + * DNSSEC key storage types. + */ +static const char *dnsseckeystore_enums[] = { "key-directory", NULL }; +static cfg_type_t cfg_type_dnsseckeystore = { + "dnssec-key-storage", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, + &cfg_rep_string, &dnsseckeystore_enums +}; + +/*% + * A dnssec key, as used in the "keys" statement in a "dnssec-policy". + */ +static cfg_tuplefielddef_t kaspkey_fields[] = { + { "role", &cfg_type_dnsseckeyrole, 0 }, + { "keystore-type", &cfg_type_dnsseckeystore, 0 }, + { "lifetime", &cfg_type_duration, 0 }, + { "algorithm", &cfg_type_uint32, 0 }, + { "length", &cfg_type_optional_uint32, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_kaspkey = { + "kaspkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, kaspkey_fields +}; + static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring }; static cfg_type_t cfg_type_optional_wild_class = { @@ -637,6 +687,14 @@ static cfg_type_t cfg_type_dnsseckeys = { cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_managedkey }; +/*% + * A list of key entries, used in a DNSSEC Key and Signing Policy. + */ +static cfg_type_t cfg_type_kaspkeys = { + "kaspkeys", cfg_parse_bracketed_list, cfg_print_bracketed_list, + cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_kaspkey +}; + static const char *forwardtype_enums[] = { "first", "only", NULL }; static cfg_type_t cfg_type_forwardtype = { "forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, @@ -962,6 +1020,7 @@ static cfg_clausedef_t namedconf_clauses[] = { { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, + { "dnssec-policy", &cfg_type_dnssecpolicy, CFG_CLAUSEFLAG_MULTI }, { "logging", &cfg_type_logging, 0 }, { "lwres", &cfg_type_bracketed_text, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE }, @@ -1997,6 +2056,21 @@ static cfg_type_t cfg_type_validityinterval = { &cfg_rep_tuple, validityinterval_fields }; +/*% + * Clauses that can be found in a 'dnssec-policy' statement. + */ +static cfg_clausedef_t +dnssecpolicy_clauses[] = { + { "dnskey-ttl", &cfg_type_duration, 0 }, + { "keys", &cfg_type_kaspkeys, 0 }, + { "publish-safety", &cfg_type_duration, 0 }, + { "retire-safety", &cfg_type_duration, 0 }, + { "signatures-refresh", &cfg_type_duration, 0 }, + { "signatures-validity", &cfg_type_duration, 0 }, + { "signatures-validity-dnskey", &cfg_type_duration, 0 }, + { NULL, NULL, 0 } +}; + /*% * Clauses that can be found in a 'zone' statement, * with defaults in the 'view' or 'options' statement. @@ -2241,6 +2315,9 @@ zone_only_clauses[] = { { "dlz", &cfg_type_astring, CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_REDIRECT }, + { "dnssec-policy", &cfg_type_astring, + CFG_ZONE_MASTER | CFG_ZONE_SLAVE + }, { "file", &cfg_type_qstring, CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB | CFG_ZONE_HINT | CFG_ZONE_REDIRECT @@ -2345,6 +2422,16 @@ LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_zoneopts = { "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets }; +/*% The "dnssec-policy" statement syntax. */ +static cfg_clausedef_t * +dnssecpolicy_clausesets[] = { + dnssecpolicy_clauses, + NULL +}; +LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_dnssecpolicyopts = { + "dnssecpolicyopts", cfg_parse_map, cfg_print_map, + cfg_doc_map, &cfg_rep_map, dnssecpolicy_clausesets }; + /*% The "dynamically loadable zones" statement syntax. */ static cfg_clausedef_t diff --git a/util/copyrights b/util/copyrights index 31f059b23b..1f7fef1d1d 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1410,6 +1410,7 @@ ./doc/arm/delegation-only.zoneopt.xml SGML 2018,2019 ./doc/arm/dlz.xml SGML 2012,2013,2014,2015,2016,2018,2019 ./doc/arm/dnssec-keys.grammar.xml SGML 2019 +./doc/arm/dnssec-policy.grammar.xml SGML 2019 ./doc/arm/dnssec.xml SGML 2010,2011,2015,2016,2017,2018,2019 ./doc/arm/dyndb.xml SGML 2015,2016,2018,2019 ./doc/arm/forward.zoneopt.xml SGML 2018,2019