]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Implement named-checkconf -k (check keys)
authorMatthijs Mekking <matthijs@isc.org>
Thu, 28 Aug 2025 08:28:02 +0000 (10:28 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Wed, 24 Sep 2025 15:03:06 +0000 (17:03 +0200)
With named-checkconf -k you can check your configuration including
checking the dnssec-policy keys against the configured keystores. If
there is a mismatch in the key files versus the policy, named-checkconf
will fail. This is useful for running before migrating to dnssec-policy.

For logging purposes, introduce a function that writes the identifying
information about a policy key into a string.

Allow a dnssec key to be initialized outside the keymgr code.

Add 'log_errors' to 'cfg_kasp_fromconfig' to avoid duplicate error
logs.

12 files changed:
bin/check/named-checkconf.c
bin/check/named-checkconf.rst
bin/dnssec/dnssectool.c
bin/named/server.c
lib/dns/include/dns/kasp.h
lib/dns/include/dns/keymgr.h
lib/dns/kasp.c
lib/dns/keymgr.c
lib/isccfg/check.c
lib/isccfg/include/isccfg/check.h
lib/isccfg/include/isccfg/kaspconf.h
lib/isccfg/kaspconf.c

index f9c801371902969a54edfc8357c8e213f0562580..a4da14d8e3b6bb7ef6840673e4356c9b42808edd 100644 (file)
@@ -57,7 +57,7 @@ usage(void);
 static void
 usage(void) {
        fprintf(stderr,
-               "usage: %s [-achijlvz] [-p [-x]] [-t directory] "
+               "usage: %s [-achijklvz] [-p [-x]] [-t directory] "
                "[named.conf]\n",
                isc_commandline_progname);
        exit(EXIT_SUCCESS);
@@ -593,7 +593,7 @@ main(int argc, char **argv) {
        /*
         * Process memory debugging argument first.
         */
-#define CMDLINE_FLAGS "acdhijlm:nt:pvxz"
+#define CMDLINE_FLAGS "acdhijklm:nt:pvxz"
        while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
                switch (c) {
                case 'm':
@@ -638,6 +638,10 @@ main(int argc, char **argv) {
                        nomerge = false;
                        break;
 
+               case 'k':
+                       checkflags |= BIND_CHECK_KEYS;
+                       break;
+
                case 'l':
                        list_zones = true;
                        break;
index bdc7933a60530ba9762abfb887935451e67b68e7..516756e9a6d1b033c80b9fca91671e5a10add91f 100644 (file)
@@ -21,7 +21,7 @@ named-checkconf - named configuration file syntax checking tool
 Synopsis
 ~~~~~~~~
 
-:program:`named-checkconf` [**-achjlnvz**] [**-p** [**-x** ]] [**-t** directory] {filename}
+:program:`named-checkconf` [**-achjklnvz**] [**-p** [**-x** ]] [**-t** directory] {filename}
 
 Description
 ~~~~~~~~~~~
@@ -56,6 +56,12 @@ Options
 
    When loading a zonefile, this option instructs :iscman:`named` to read the journal if it exists.
 
+.. option:: -k
+
+   Check the `dnssec-policy`'s DNSSEC keys against the key files in
+   the `key-directory`.  This is useful when checking a `named.conf`
+   to ensure a DNSSEC policy matches the existing keys.
+
 .. option:: -l
 
    This option lists all the configured zones. Each line of output contains the zone
index ed8a244e599ab0ae03b3d469538e0545fb2b83dc..5a1693fe6afc01839ee17b068f615ac40524281c 100644 (file)
@@ -601,6 +601,9 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name,
        const cfg_obj_t *keystores = NULL;
        dns_keystore_t *keystore = NULL;
        dns_keystorelist_t kslist;
+       unsigned int options = (ISCCFG_KASPCONF_CHECK_ALGORITHMS |
+                               ISCCFG_KASPCONF_CHECK_KEYLIST |
+                               ISCCFG_KASPCONF_LOG_ERRORS);
 
        ISC_LIST_INIT(kasplist);
        ISC_LIST_INIT(kslist);
@@ -635,8 +638,8 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name,
                        continue;
                }
 
-               result = cfg_kasp_fromconfig(kconfig, NULL, true, mctx, &kslist,
-                                            &kasplist, &kasp);
+               result = cfg_kasp_fromconfig(kconfig, NULL, options, mctx,
+                                            &kslist, &kasplist, &kasp);
                if (result != ISC_R_SUCCESS) {
                        fatal("failed to configure dnssec-policy '%s': %s",
                              cfg_obj_asstring(cfg_tuple_get(kconfig, "name")),
index bb46d6e48ce4018bfb4bfd785cf75998ecf53a0c..b82ffe82cb02dcd8b8a64808ffe20f9d98997917 100644 (file)
@@ -8052,6 +8052,9 @@ configure_kasplist(const cfg_obj_t *config, dns_kasplist_t *kasplist,
        isc_result_t result = ISC_R_SUCCESS;
        dns_kasp_t *default_kasp = NULL;
        const cfg_obj_t *kasps = NULL;
+       unsigned int kaspopts = (ISCCFG_KASPCONF_CHECK_ALGORITHMS |
+                                ISCCFG_KASPCONF_CHECK_KEYLIST |
+                                ISCCFG_KASPCONF_LOG_ERRORS);
 
        APPLY_CONFIGURATION_SUBROUTINE_LOG;
 
@@ -8063,7 +8066,7 @@ configure_kasplist(const cfg_obj_t *config, dns_kasplist_t *kasplist,
                cfg_obj_t *kconfig = cfg_listelt_value(element);
                dns_kasp_t *kasp = NULL;
 
-               result = cfg_kasp_fromconfig(kconfig, default_kasp, true,
+               result = cfg_kasp_fromconfig(kconfig, default_kasp, kaspopts,
                                             isc_g_mctx, keystorelist, kasplist,
                                             &kasp);
                if (result != ISC_R_SUCCESS) {
@@ -8091,7 +8094,7 @@ configure_kasplist(const cfg_obj_t *config, dns_kasplist_t *kasplist,
                cfg_obj_t *kconfig = cfg_listelt_value(element);
                dns_kasp_t *kasp = NULL;
 
-               result = cfg_kasp_fromconfig(kconfig, default_kasp, true,
+               result = cfg_kasp_fromconfig(kconfig, default_kasp, kaspopts,
                                             isc_g_mctx, keystorelist, kasplist,
                                             &kasp);
                if (result != ISC_R_SUCCESS) {
index 999e08ddcbac6d69af8117c2ca6d424674488694..eeb97b95ef088183213e6444dabc1c7d380e7c1a 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <dns/dnssec.h>
 #include <dns/keystore.h>
+#include <dns/name.h>
 #include <dns/types.h>
 
 /* For storing a list of digest types */
@@ -137,6 +138,8 @@ struct dns_kasp {
 #define DNS_KASP_KEY_ROLE_KSK 0x01
 #define DNS_KASP_KEY_ROLE_ZSK 0x02
 
+#define DNS_KASP_KEY_FORMATSIZE (DNS_NAME_FORMATSIZE + 64)
+
 void
 dns_kasp_create(isc_mem_t *mctx, const char *name, dns_kasp_t **kaspp);
 /*%<
@@ -764,6 +767,17 @@ dns_kasp_key_match(dns_kasp_key_t *key, dns_dnsseckey_t *dkey);
  *\li  False, otherwise.
  */
 
+void
+dns_kasp_key_format(dns_kasp_key_t *key, char *cp, unsigned int size);
+/*%<
+ * Write the identifying information about the policy key (role,
+ * algorithm, tag range) into a string 'cp' of size 'size'.
+ * Requires:
+ *
+ *\li  key != NULL
+ *\li  cp != NULL
+ */
+
 bool
 dns_kasp_nsec3(dns_kasp_t *kasp);
 /*%<
index b8cbf2d3552718908964e889eed1c749c0b6a482..8fcc6dbfbbb1cb92daadd57daf633ae48ae3547c 100644 (file)
@@ -37,6 +37,21 @@ dns_keymgr_settime_syncpublish(dst_key_t *key, dns_kasp_t *kasp, bool first);
  *\li           'kasp' is a valid DNSSEC policy.
  */
 
+void
+dns_keymgr_key_init(dns_dnsseckey_t *key, dns_kasp_t *kasp, isc_stdtime_t now,
+                   bool csk);
+/*
+ * Initialize this key's properties if not already present.  A key created
+ * and derived from a dnssec-policy will have the required metadata available,
+ * otherwise these may be missing and need to be initialized.  The key states
+ * will be initialized according to existing timing metadata. If 'csk' is
+ * set to true, the key is considered a combined signing key (CSK).
+ *
+ *      Requires:
+ *\li           'key' is a valid DNSSEC key.
+ *\li           'kasp' is a valid DNSSEC policy.
+ */
+
 isc_result_t
 dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
               isc_mem_t *mctx, dns_dnsseckeylist_t *keyring,
index 884e3e33ec858d1ebd6090bb9546fd76fdf1c1ce..76a59c225d0e88eafd617f79f33914f1fec6dfcd 100644 (file)
@@ -559,6 +559,21 @@ dns_kasp_key_match(dns_kasp_key_t *key, dns_dnsseckey_t *dkey) {
        return true;
 }
 
+void
+dns_kasp_key_format(dns_kasp_key_t *key, char *cp, unsigned int size) {
+       REQUIRE(key != NULL);
+       REQUIRE(cp != NULL);
+
+       char algstr[DNS_NAME_FORMATSIZE];
+       bool csk = dns_kasp_key_ksk(key) && dns_kasp_key_zsk(key);
+       const char *rolestr = (csk ? "csk"
+                                  : (dns_kasp_key_ksk(key) ? "ksk" : "zsk"));
+
+       dst_algorithm_format(key->algorithm, algstr, sizeof(algstr));
+       snprintf(cp, size, "%s algorithm:%s length:%u tag-range:%u-%u", rolestr,
+                algstr, dns_kasp_key_size(key), key->tag_min, key->tag_max);
+}
+
 uint8_t
 dns_kasp_nsec3iter(dns_kasp_t *kasp) {
        REQUIRE(kasp != NULL);
index 79ac71f83530d4123bb10e1486e5145e9ca59448..bb1f831673a260dc1374f36a63e04c56144bf1c3 100644 (file)
@@ -1635,16 +1635,9 @@ transition:
        return result;
 }
 
-/*
- * See if this key needs to be initialized with properties.  A key created
- * and derived from a dnssec-policy will have the required metadata available,
- * otherwise these may be missing and need to be initialized.  The key states
- * will be initialized according to existing timing metadata.
- *
- */
-static void
-keymgr_key_init(dns_dnsseckey_t *key, dns_kasp_t *kasp, isc_stdtime_t now,
-               bool csk) {
+void
+dns_keymgr_key_init(dns_dnsseckey_t *key, dns_kasp_t *kasp, isc_stdtime_t now,
+                   bool csk) {
        bool ksk, zsk;
        isc_result_t ret;
        isc_stdtime_t active = 0, pub = 0, syncpub = 0, retire = 0, remove = 0;
@@ -1926,7 +1919,7 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key,
                dst_key_setttl(dst_key, dns_kasp_dnskeyttl(kasp));
                dst_key_settime(dst_key, DST_TIME_CREATED, now);
                dns_dnsseckey_create(mctx, &dst_key, &new_key);
-               keymgr_key_init(new_key, kasp, now, csk);
+               dns_keymgr_key_init(new_key, kasp, now, csk);
                keycreated = true;
        }
        dst_key_setnum(new_key->key, DST_NUM_LIFETIME, lifetime);
@@ -2174,7 +2167,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
        ISC_LIST_FOREACH(*keyring, dkey, link) {
                bool found_match = false;
 
-               keymgr_key_init(dkey, kasp, now, numkeys == 1);
+               dns_keymgr_key_init(dkey, kasp, now, numkeys == 1);
 
                ISC_LIST_FOREACH(dns_kasp_keys(kasp), kkey, link) {
                        if (dns_kasp_key_match(kkey, dkey)) {
@@ -2785,7 +2778,7 @@ dns_keymgr_offline(const dns_name_t *origin, dns_dnsseckeylist_t *keyring,
                        continue;
                }
 
-               keymgr_key_init(dkey, kasp, now, false);
+               dns_keymgr_key_init(dkey, kasp, now, false);
 
                /* Get current metadata */
                RETERR(dst_key_getstate(dkey->key, DST_KEY_DNSKEY,
index 3d03d1e10cc7d2606ccf9ce3597303e2bff49b27..385a85e6cc58113099d64bd5f3ef9cf2366d272b 100644 (file)
@@ -47,6 +47,7 @@
 #include <dns/fixedname.h>
 #include <dns/journal.h>
 #include <dns/kasp.h>
+#include <dns/keymgr.h>
 #include <dns/keystore.h>
 #include <dns/keyvalues.h>
 #include <dns/peer.h>
@@ -1397,6 +1398,11 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config,
        if (obj != NULL) {
                bool bad_kasp = false;
                bool bad_name = false;
+               unsigned int kaspopts = (ISCCFG_KASPCONF_CHECK_KEYLIST |
+                                        ISCCFG_KASPCONF_LOG_ERRORS);
+               if (check_algorithms) {
+                       kaspopts |= ISCCFG_KASPCONF_CHECK_ALGORITHMS;
+               }
 
                if (optlevel != optlevel_config && !cfg_obj_isstring(obj)) {
                        bad_kasp = true;
@@ -1422,8 +1428,8 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config,
                                        }
 
                                        ret = cfg_kasp_fromconfig(
-                                               kconfig, NULL, check_algorithms,
-                                               mctx, &kslist, &list, &kasp);
+                                               kconfig, NULL, kaspopts, mctx,
+                                               &kslist, &list, &kasp);
                                        if (ret != ISC_R_SUCCESS) {
                                                if (result == ISC_R_SUCCESS) {
                                                        result = ret;
@@ -2815,29 +2821,29 @@ cleanup:
 
 static isc_result_t
 check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig,
-            dns_name_t *zname, const char *name, const char *keydir,
-            isc_symtab_t *keydirs, isc_mem_t *mctx) {
+            dns_name_t *origin, const char *zname, const char *name,
+            const char *keydir, isc_symtab_t *keydirs, isc_mem_t *mctx,
+            bool check_keys) {
        const char *dir = keydir;
        isc_result_t ret, result = ISC_R_SUCCESS;
-       bool do_cleanup = false;
        bool done = false;
        bool keystore = false;
        const cfg_obj_t *kasps = NULL;
+       const cfg_obj_t *kaspobj = NULL;
        dns_kasp_t *kasp = NULL;
        dns_kasplist_t kasplist;
        const cfg_obj_t *keystores = NULL;
        dns_keystorelist_t kslist;
+       isc_time_t timenow;
+       isc_stdtime_t now;
+
+       timenow = isc_time_now();
+       now = isc_time_seconds(&timenow);
 
-       /* If no dnssec-policy or key-store, use the dir (key-directory) */
        (void)cfg_map_get(config, "dnssec-policy", &kasps);
        (void)cfg_map_get(config, "key-store", &keystores);
-       if (kasps == NULL || keystores == NULL) {
-               goto check;
-       }
-
        ISC_LIST_INIT(kasplist);
        ISC_LIST_INIT(kslist);
-       do_cleanup = true;
 
        /*
         * Build the keystore list.
@@ -2854,7 +2860,7 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig,
         */
        CFG_LIST_FOREACH(kasps, element) {
                cfg_obj_t *kconfig = cfg_listelt_value(element);
-               const cfg_obj_t *kaspobj = NULL;
+               kaspobj = NULL;
 
                if (!cfg_obj_istuple(kconfig)) {
                        continue;
@@ -2865,7 +2871,7 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                        continue;
                }
 
-               ret = cfg_kasp_fromconfig(kconfig, NULL, false, mctx, &kslist,
+               ret = cfg_kasp_fromconfig(kconfig, NULL, 0, mctx, &kslist,
                                          &kasplist, &kasp);
                if (ret != ISC_R_SUCCESS) {
                        kasp = NULL;
@@ -2876,6 +2882,7 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig,
        if (kasp == NULL) {
                goto check;
        }
+       INSIST(kaspobj != NULL);
 
        /* Check key-stores of keys */
        dns_kasp_freeze(kasp);
@@ -2888,35 +2895,116 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                ret = keydirexist(zconfig,
                                  keystore ? "key-store directory"
                                           : "key-directory",
-                                 zname, dir, name, keydirs, mctx);
+                                 origin, dir, name, keydirs, mctx);
+               if (ret != ISC_R_SUCCESS) {
+                       result = ret;
+               }
+       }
+
+       if (check_keys) {
+               /* Find matching key files. */
+               dns_dnsseckeylist_t keys;
+               int numkaspkeys = 0;
+               int numkeyfiles = 0;
+
+               ISC_LIST_INIT(keys);
+               ret = dns_dnssec_findmatchingkeys(origin, kasp, keydir, &kslist,
+                                                 now, mctx, &keys);
                if (ret != ISC_R_SUCCESS) {
                        result = ret;
                }
+
+               ISC_LIST_FOREACH(keys, dkey, link) {
+                       numkeyfiles++;
+               }
+
+               ISC_LIST_FOREACH(keys, dkey, link) {
+                       bool found_match = false;
+
+                       dns_keymgr_key_init(dkey, kasp, now, numkeyfiles == 1);
+
+                       ISC_LIST_FOREACH(dns_kasp_keys(kasp), kkey, link) {
+                               if (dns_kasp_key_match(kkey, dkey)) {
+                                       found_match = true;
+                                       break;
+                               }
+                       }
+
+                       if (!found_match) {
+                               char keystr[DST_KEY_FORMATSIZE];
+                               dst_key_format(dkey->key, keystr,
+                                              sizeof(keystr));
+                               cfg_obj_log(kaspobj, ISC_LOG_ERROR,
+                                           "zone '%s': key file '%s' does not "
+                                           "match dnssec-policy %s",
+                                           zname, keystr,
+                                           dns_kasp_getname(kasp));
+                               result = ISC_R_NOTFOUND;
+                       }
+               }
+
+               ISC_LIST_FOREACH(dns_kasp_keys(kasp), kkey, link) {
+                       bool found_match = false;
+
+                       numkaspkeys++;
+
+                       ISC_LIST_FOREACH(keys, dkey, link) {
+                               if (dns_kasp_key_match(kkey, dkey)) {
+                                       found_match = true;
+                                       break;
+                               }
+                       }
+
+                       if (!found_match) {
+                               char keystr[DNS_KASP_KEY_FORMATSIZE];
+                               dns_kasp_key_format(kkey, keystr,
+                                                   sizeof(keystr));
+
+                               cfg_obj_log(
+                                       kaspobj, ISC_LOG_ERROR,
+                                       "zone '%s': no key file found matching "
+                                       "dnssec-policy %s key:'%s'",
+                                       zname, dns_kasp_getname(kasp), keystr);
+                               result = ISC_R_NOTFOUND;
+                       }
+               }
+
+               if (numkaspkeys != numkeyfiles) {
+                       cfg_obj_log(kaspobj, ISC_LOG_ERROR,
+                                   "zone '%s': wrong number of key files (%d, "
+                                   "expected %d)",
+                                   zname, numkeyfiles, numkaspkeys);
+                       result = ISC_R_FAILURE;
+               }
+
+               ISC_LIST_FOREACH(keys, key, link) {
+                       ISC_LIST_UNLINK(keys, key, link);
+                       dns_dnsseckey_destroy(mctx, &key);
+               }
        }
+
        dns_kasp_thaw(kasp);
        done = true;
 
 check:
        if (!done) {
-               ret = keydirexist(zconfig, "key-directory", zname, dir, name,
+               ret = keydirexist(zconfig, "key-directory", origin, dir, name,
                                  keydirs, mctx);
                if (ret != ISC_R_SUCCESS) {
                        result = ret;
                }
        }
 
-       if (do_cleanup) {
-               if (kasp != NULL) {
-                       dns_kasp_detach(&kasp);
-               }
-               ISC_LIST_FOREACH(kasplist, k, link) {
-                       ISC_LIST_UNLINK(kasplist, k, link);
-                       dns_kasp_detach(&k);
-               }
-               ISC_LIST_FOREACH(kslist, ks, link) {
-                       ISC_LIST_UNLINK(kslist, ks, link);
-                       dns_keystore_detach(&ks);
-               }
+       if (kasp != NULL) {
+               dns_kasp_detach(&kasp);
+       }
+       ISC_LIST_FOREACH(kasplist, k, link) {
+               ISC_LIST_UNLINK(kasplist, k, link);
+               dns_kasp_detach(&k);
+       }
+       ISC_LIST_FOREACH(kslist, ks, link) {
+               ISC_LIST_UNLINK(kslist, ks, link);
+               dns_keystore_detach(&ks);
        }
 
        return result;
@@ -3046,6 +3134,7 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
        bool has_dnssecpolicy = false;
        bool kasp_inlinesigning = false;
        bool inline_signing = false;
+       bool check_keys = (flags & BIND_CHECK_KEYS) != 0;
        const void *clauses = NULL;
        const char *option = NULL;
        const char *kaspname = NULL;
@@ -3821,8 +3910,9 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
         */
        if (zname != NULL && keydirs != NULL) {
                if (has_dnssecpolicy) {
-                       tresult = check_keydir(config, zconfig, zname, kaspname,
-                                              dir, keydirs, mctx);
+                       tresult = check_keydir(config, zconfig, zname, znamestr,
+                                              kaspname, dir, keydirs, mctx,
+                                              check_keys);
                } else {
                        tresult = keydirexist(zconfig, "key-directory", zname,
                                              dir, kaspname, keydirs, mctx);
index f3980204d2079aadb73e9ba33cbd4fe780519214..622308a012ada631e0f2e9cf6a8f5f230d4762ff 100644 (file)
  * Check the dnssec-policy DNSSEC algorithms against those
  * supported by the crypto provider.
  */
+#define BIND_CHECK_KEYS 0x00000004
+/*%<
+ * Check the dnssec-policy DNSSEC keys against the key files
+ * in the key stores.
+ */
 
 isc_result_t
 isccfg_check_namedconf(const cfg_obj_t *config, unsigned int flags,
index 0a2e501bc760512655e223b6f7ff51e30d117de9..14434f3e3a92aca1e8511202ca7df4b6c910398d 100644 (file)
 
 #include <isccfg/cfg.h>
 
+#define ISCCFG_KASPCONF_CHECK_ALGORITHMS 0x01
+#define ISCCFG_KASPCONF_CHECK_KEYLIST   0x02
+#define ISCCFG_KASPCONF_LOG_ERRORS      0x04
+
 /***
  *** Functions
  ***/
 
 isc_result_t
 cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
-                   bool check_algorithms, isc_mem_t *mctx,
+                   unsigned int options, isc_mem_t *mctx,
                    dns_keystorelist_t *keystorelist, dns_kasplist_t *kasplist,
                    dns_kasp_t **kaspp);
 /*%<
@@ -34,8 +38,16 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
  *
  * The 'keystorelist' is where to lookup key stores if KASP keys are using them.
  *
- * If 'check_algorithms' is true then the dnssec-policy DNSSEC key
- * algorithms are checked against those supported by the crypto provider.
+ * If 'options' has ISCCFG_KASPCONF_CHECK_ALGORITHMS set, then the dnssec-policy
+ * DNSSEC key algorithms are checked against those supported by the crypto
+ * provider.
+ *
+ * If 'options' has ISCCFG_KASPCONF_CHECK_KEYLIST set, then this function
+ * insists that the key list is not empty, unless the policy is "insecure"
+ * (then the key list must be empty).
+ *
+ * If 'options' has ISCCFG_KASPCONF_LOG_ERRORS set, then configuration errors
+ * and warnings are logged to the global logging context.
  *
  * Requires:
  *
index d83e0af46b51019055a9cd315f02fb25de2ca5c5..bee50de81e12b39c0ccca2c8a8edb47b953fc801 100644 (file)
@@ -114,7 +114,7 @@ get_string(const cfg_obj_t **maps, const char *option) {
  */
 static isc_result_t
 cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
-                      bool check_algorithms, bool offline_ksk,
+                      bool check_algorithms, bool log_errors, bool offline_ksk,
                       dns_keystorelist_t *keystorelist,
                       uint32_t ksk_min_lifetime, uint32_t zsk_min_lifetime) {
        isc_result_t result;
@@ -151,10 +151,13 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                        key->role |= DNS_KASP_KEY_ROLE_ZSK;
                } else if (strcmp(rolestr, "csk") == 0) {
                        if (offline_ksk) {
-                               cfg_obj_log(
-                                       config, ISC_LOG_ERROR,
-                                       "dnssec-policy: csk keys are not "
-                                       "allowed when offline-ksk is enabled");
+                               if (log_errors) {
+                                       cfg_obj_log(config, ISC_LOG_ERROR,
+                                                   "dnssec-policy: csk keys "
+                                                   "are not "
+                                                   "allowed when offline-ksk "
+                                                   "is enabled");
+                               }
                                result = ISC_R_FAILURE;
                                goto cleanup;
                        }
@@ -172,14 +175,20 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                result = dns_keystorelist_find(keystorelist, keydir,
                                               &key->keystore);
                if (result == ISC_R_NOTFOUND) {
-                       cfg_obj_log(obj, ISC_LOG_ERROR,
-                                   "dnssec-policy: keystore %s does not exist",
-                                   keydir);
+                       if (log_errors) {
+                               cfg_obj_log(obj, ISC_LOG_ERROR,
+                                           "dnssec-policy: keystore %s does "
+                                           "not exist",
+                                           keydir);
+                       }
                        result = ISC_R_FAILURE;
                        goto cleanup;
                } else if (result != ISC_R_SUCCESS) {
-                       cfg_obj_log(obj, ISC_LOG_ERROR,
-                                   "dnssec-policy: bad keystore %s", keydir);
+                       if (log_errors) {
+                               cfg_obj_log(obj, ISC_LOG_ERROR,
+                                           "dnssec-policy: bad keystore %s",
+                                           keydir);
+                       }
                        result = ISC_R_FAILURE;
                        goto cleanup;
                }
@@ -192,9 +201,12 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                }
                if (key->lifetime > 0) {
                        if (key->lifetime < 30 * (24 * 3600)) {
-                               cfg_obj_log(obj, ISC_LOG_WARNING,
-                                           "dnssec-policy: key lifetime is "
-                                           "shorter than 30 days");
+                               if (log_errors) {
+                                       cfg_obj_log(obj, ISC_LOG_WARNING,
+                                                   "dnssec-policy: key "
+                                                   "lifetime is "
+                                                   "shorter than 30 days");
+                               }
                        }
                        if ((key->role & DNS_KASP_KEY_ROLE_KSK) != 0 &&
                            key->lifetime <= ksk_min_lifetime)
@@ -207,10 +219,14 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                                error = true;
                        }
                        if (error) {
-                               cfg_obj_log(obj, ISC_LOG_ERROR,
-                                           "dnssec-policy: key lifetime is "
-                                           "shorter than the time it takes to "
-                                           "do a rollover");
+                               if (log_errors) {
+                                       cfg_obj_log(obj, ISC_LOG_ERROR,
+                                                   "dnssec-policy: key "
+                                                   "lifetime is "
+                                                   "shorter than the time it "
+                                                   "takes to "
+                                                   "do a rollover");
+                               }
                                result = ISC_R_FAILURE;
                                goto cleanup;
                        }
@@ -222,9 +238,11 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                result = dst_algorithm_fromtext(&key->algorithm,
                                                (isc_textregion_t *)&alg);
                if (result != ISC_R_SUCCESS) {
-                       cfg_obj_log(obj, ISC_LOG_ERROR,
-                                   "dnssec-policy: bad algorithm %s",
-                                   alg.base);
+                       if (log_errors) {
+                               cfg_obj_log(obj, ISC_LOG_ERROR,
+                                           "dnssec-policy: bad algorithm %s",
+                                           alg.base);
+                       }
                        result = DNS_R_BADALG;
                        goto cleanup;
                }
@@ -232,10 +250,13 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                    (key->algorithm == DST_ALG_RSASHA1 ||
                     key->algorithm == DST_ALG_NSEC3RSASHA1))
                {
-                       cfg_obj_log(obj, ISC_LOG_ERROR,
-                                   "dnssec-policy: algorithm %s not supported "
-                                   "in FIPS mode",
-                                   alg.base);
+                       if (log_errors) {
+                               cfg_obj_log(obj, ISC_LOG_ERROR,
+                                           "dnssec-policy: algorithm %s not "
+                                           "supported "
+                                           "in FIPS mode",
+                                           alg.base);
+                       }
                        result = DNS_R_BADALG;
                        goto cleanup;
                }
@@ -243,9 +264,12 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                if (check_algorithms &&
                    !dst_algorithm_supported(key->algorithm))
                {
-                       cfg_obj_log(obj, ISC_LOG_ERROR,
-                                   "dnssec-policy: algorithm %s not supported",
-                                   alg.base);
+                       if (log_errors) {
+                               cfg_obj_log(obj, ISC_LOG_ERROR,
+                                           "dnssec-policy: algorithm %s not "
+                                           "supported",
+                                           alg.base);
+                       }
                        result = DNS_R_BADALG;
                        goto cleanup;
                }
@@ -253,10 +277,13 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                switch (key->algorithm) {
                case DST_ALG_RSASHA1:
                case DST_ALG_NSEC3RSASHA1:
-                       cfg_obj_log(obj, ISC_LOG_WARNING,
-                                   "dnssec-policy: DNSSEC algorithm %s is "
-                                   "deprecated",
-                                   alg.base);
+                       if (log_errors) {
+                               cfg_obj_log(
+                                       obj, ISC_LOG_WARNING,
+                                       "dnssec-policy: DNSSEC algorithm %s is "
+                                       "deprecated",
+                                       alg.base);
+                       }
                        break;
                default:
                        break;
@@ -280,11 +307,15 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                                        min = DST_ALG_RSASHA512 ? 1024 : 512;
                                }
                                if (size < min || size > 4096) {
-                                       cfg_obj_log(obj, ISC_LOG_ERROR,
-                                                   "dnssec-policy: key with "
-                                                   "algorithm %s has invalid "
-                                                   "key length %u",
-                                                   alg.base, size);
+                                       if (log_errors) {
+                                               cfg_obj_log(obj, ISC_LOG_ERROR,
+                                                           "dnssec-policy: "
+                                                           "key with "
+                                                           "algorithm %s has "
+                                                           "invalid "
+                                                           "key length %u",
+                                                           alg.base, size);
+                                       }
                                        result = ISC_R_RANGE;
                                        goto cleanup;
                                }
@@ -293,11 +324,15 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                        case DST_ALG_ECDSA384:
                        case DST_ALG_ED25519:
                        case DST_ALG_ED448:
-                               cfg_obj_log(obj, ISC_LOG_WARNING,
-                                           "dnssec-policy: key algorithm %s "
-                                           "has predefined length; ignoring "
-                                           "length value %u",
-                                           alg.base, size);
+                               if (log_errors) {
+                                       cfg_obj_log(obj, ISC_LOG_WARNING,
+                                                   "dnssec-policy: key "
+                                                   "algorithm %s "
+                                                   "has predefined length; "
+                                                   "ignoring "
+                                                   "length value %u",
+                                                   alg.base, size);
+                               }
                        default:
                                break;
                        }
@@ -311,25 +346,31 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
                        obj = cfg_tuple_get(tagrange, "tag-min");
                        tag_min = cfg_obj_asuint32(obj);
                        if (tag_min > 0xffff) {
-                               cfg_obj_log(obj, ISC_LOG_ERROR,
-                                           "dnssec-policy: tag-min "
-                                           "too big");
+                               if (log_errors) {
+                                       cfg_obj_log(obj, ISC_LOG_ERROR,
+                                                   "dnssec-policy: tag-min "
+                                                   "too big");
+                               }
                                result = ISC_R_RANGE;
                                goto cleanup;
                        }
                        obj = cfg_tuple_get(tagrange, "tag-max");
                        tag_max = cfg_obj_asuint32(obj);
                        if (tag_max > 0xffff) {
-                               cfg_obj_log(obj, ISC_LOG_ERROR,
-                                           "dnssec-policy: tag-max "
-                                           "too big");
+                               if (log_errors) {
+                                       cfg_obj_log(obj, ISC_LOG_ERROR,
+                                                   "dnssec-policy: tag-max "
+                                                   "too big");
+                               }
                                result = ISC_R_RANGE;
                                goto cleanup;
                        }
                        if (tag_min >= tag_max) {
-                               cfg_obj_log(
-                                       obj, ISC_LOG_ERROR,
-                                       "dnssec-policy: tag-min >= tag_max");
+                               if (log_errors) {
+                                       cfg_obj_log(obj, ISC_LOG_ERROR,
+                                                   "dnssec-policy: tag-min >= "
+                                                   "tag_max");
+                               }
                                result = ISC_R_RANGE;
                                goto cleanup;
                        }
@@ -348,7 +389,8 @@ cleanup:
 }
 
 static isc_result_t
-cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp) {
+cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
+                         bool log_errors) {
        unsigned int min_keysize = 4096;
        const cfg_obj_t *obj = NULL;
        uint32_t iter = DEFAULT_NSEC3PARAM_ITER;
@@ -382,18 +424,22 @@ cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp) {
        if (badalg > 0) {
                char algstr[DNS_SECALG_FORMATSIZE];
                dns_secalg_format((dns_secalg_t)badalg, algstr, sizeof(algstr));
-               cfg_obj_log(
-                       obj, ISC_LOG_ERROR,
-                       "dnssec-policy: cannot use nsec3 with algorithm '%s'",
-                       algstr);
+               if (log_errors) {
+                       cfg_obj_log(obj, ISC_LOG_ERROR,
+                                   "dnssec-policy: cannot use nsec3 with "
+                                   "algorithm '%s'",
+                                   algstr);
+               }
                return DNS_R_NSEC3BADALG;
        }
 
        if (iter != DEFAULT_NSEC3PARAM_ITER) {
-               cfg_obj_log(obj, ISC_LOG_ERROR,
-                           "dnssec-policy: nsec3 iterations value %u "
-                           "not allowed, must be zero",
-                           iter);
+               if (log_errors) {
+                       cfg_obj_log(obj, ISC_LOG_ERROR,
+                                   "dnssec-policy: nsec3 iterations value %u "
+                                   "not allowed, must be zero",
+                                   iter);
+               }
                return DNS_R_NSEC3ITERRANGE;
        }
 
@@ -409,9 +455,12 @@ cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp) {
                saltlen = cfg_obj_asuint32(obj);
        }
        if (saltlen > 0xff) {
-               cfg_obj_log(obj, ISC_LOG_ERROR,
-                           "dnssec-policy: nsec3 salt length %u too high",
-                           saltlen);
+               if (log_errors) {
+                       cfg_obj_log(
+                               obj, ISC_LOG_ERROR,
+                               "dnssec-policy: nsec3 salt length %u too high",
+                               saltlen);
+               }
                return DNS_R_NSEC3SALTRANGE;
        }
 
@@ -420,7 +469,7 @@ cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp) {
 }
 
 static isc_result_t
-add_digest(dns_kasp_t *kasp, const cfg_obj_t *digest) {
+add_digest(dns_kasp_t *kasp, const cfg_obj_t *digest, bool log_errors) {
        isc_result_t result = ISC_R_SUCCESS;
        isc_textregion_t r;
        dns_dsdigest_t alg;
@@ -430,21 +479,28 @@ add_digest(dns_kasp_t *kasp, const cfg_obj_t *digest) {
        r.length = strlen(str);
        result = dns_dsdigest_fromtext(&alg, &r);
        if (result != ISC_R_SUCCESS) {
-               cfg_obj_log(digest, ISC_LOG_ERROR,
-                           "dnssec-policy: bad cds digest-type %s", str);
+               if (log_errors) {
+                       cfg_obj_log(digest, ISC_LOG_ERROR,
+                                   "dnssec-policy: bad cds digest-type %s",
+                                   str);
+               }
                result = DNS_R_BADALG;
        } else if (!dst_ds_digest_supported(alg)) {
-               cfg_obj_log(digest, ISC_LOG_ERROR,
-                           "dnssec-policy: unsupported cds "
-                           "digest-type %s",
-                           str);
+               if (log_errors) {
+                       cfg_obj_log(digest, ISC_LOG_ERROR,
+                                   "dnssec-policy: unsupported cds "
+                                   "digest-type %s",
+                                   str);
+               }
                result = DST_R_UNSUPPORTEDALG;
        } else {
                if (alg == DNS_DSDIGEST_SHA1) {
-                       cfg_obj_log(
-                               digest, ISC_LOG_WARNING,
-                               "dnssec-policy: deprecated CDS digest-type %s",
-                               str);
+                       if (log_errors) {
+                               cfg_obj_log(digest, ISC_LOG_WARNING,
+                                           "dnssec-policy: deprecated CDS "
+                                           "digest-type %s",
+                                           str);
+                       }
                }
                dns_kasp_adddigest(kasp, alg);
        }
@@ -453,7 +509,7 @@ add_digest(dns_kasp_t *kasp, const cfg_obj_t *digest) {
 
 isc_result_t
 cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
-                   bool check_algorithms, isc_mem_t *mctx,
+                   unsigned int options, isc_mem_t *mctx,
                    dns_keystorelist_t *keystorelist, dns_kasplist_t *kasplist,
                    dns_kasp_t **kaspp) {
        isc_result_t result;
@@ -474,6 +530,10 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
        uint32_t ipub = 0, iret = 0;
        uint32_t ksk_min_lifetime = 0, zsk_min_lifetime = 0;
        bool offline_ksk = false, manual_mode = false;
+       bool check_algorithms = (options & ISCCFG_KASPCONF_CHECK_ALGORITHMS) !=
+                               0;
+       bool check_keylist = (options & ISCCFG_KASPCONF_CHECK_KEYLIST) != 0;
+       bool log_errors = (options & ISCCFG_KASPCONF_LOG_ERRORS) != 0;
 
        REQUIRE(config != NULL);
        REQUIRE(kaspp != NULL && *kaspp == NULL);
@@ -487,10 +547,12 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
        result = dns_kasplist_find(kasplist, kaspname, &kasp);
 
        if (result == ISC_R_SUCCESS) {
-               cfg_obj_log(
-                       config, ISC_LOG_ERROR,
-                       "dnssec-policy: duplicately named policy found '%s'",
-                       kaspname);
+               if (log_errors) {
+                       cfg_obj_log(config, ISC_LOG_ERROR,
+                                   "dnssec-policy: duplicately named policy "
+                                   "found '%s'",
+                                   kaspname);
+               }
                dns_kasp_detach(&kasp);
                return ISC_R_EXISTS;
        }
@@ -526,42 +588,51 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
        sigvalidity = get_duration(maps, "signatures-validity-dnskey",
                                   DNS_KASP_SIG_VALIDITY_DNSKEY);
        if (sigrefresh >= (sigvalidity * 0.9)) {
-               cfg_obj_log(
-                       config, ISC_LOG_ERROR,
-                       "dnssec-policy: policy '%s' signatures-refresh must be "
-                       "at most 90%% of the signatures-validity-dnskey",
-                       kaspname);
+               if (log_errors) {
+                       cfg_obj_log(config, ISC_LOG_ERROR,
+                                   "dnssec-policy: policy '%s' "
+                                   "signatures-refresh must be "
+                                   "at most 90%% of the "
+                                   "signatures-validity-dnskey",
+                                   kaspname);
+               }
                result = ISC_R_FAILURE;
        }
        dns_kasp_setsigvalidity_dnskey(kasp, sigvalidity);
 
        if (sigjitter > sigvalidity) {
-               cfg_obj_log(
-                       config, ISC_LOG_ERROR,
-                       "dnssec-policy: policy '%s' signatures-jitter cannot "
-                       "be larger than signatures-validity-dnskey",
-                       kaspname);
+               if (log_errors) {
+                       cfg_obj_log(config, ISC_LOG_ERROR,
+                                   "dnssec-policy: policy '%s' "
+                                   "signatures-jitter cannot "
+                                   "be larger than signatures-validity-dnskey",
+                                   kaspname);
+               }
                result = ISC_R_FAILURE;
        }
 
        sigvalidity = get_duration(maps, "signatures-validity",
                                   DNS_KASP_SIG_VALIDITY);
        if (sigrefresh >= (sigvalidity * 0.9)) {
-               cfg_obj_log(
-                       config, ISC_LOG_ERROR,
-                       "dnssec-policy: policy '%s' signatures-refresh must be "
-                       "at most 90%% of the signatures-validity",
-                       kaspname);
+               if (log_errors) {
+                       cfg_obj_log(config, ISC_LOG_ERROR,
+                                   "dnssec-policy: policy '%s' "
+                                   "signatures-refresh must be "
+                                   "at most 90%% of the signatures-validity",
+                                   kaspname);
+               }
                result = ISC_R_FAILURE;
        }
        dns_kasp_setsigvalidity(kasp, sigvalidity);
 
        if (sigjitter > sigvalidity) {
-               cfg_obj_log(
-                       config, ISC_LOG_ERROR,
-                       "dnssec-policy: policy '%s' signatures-jitter cannot "
-                       "be larger than signatures-validity",
-                       kaspname);
+               if (log_errors) {
+                       cfg_obj_log(config, ISC_LOG_ERROR,
+                                   "dnssec-policy: policy '%s' "
+                                   "signatures-jitter cannot "
+                                   "be larger than signatures-validity",
+                                   kaspname);
+               }
                result = ISC_R_FAILURE;
        }
 
@@ -619,7 +690,8 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
        (void)confget(maps, "cds-digest-types", &cds);
        if (cds != NULL) {
                CFG_LIST_FOREACH(cds, element) {
-                       result = add_digest(kasp, cfg_listelt_value(element));
+                       result = add_digest(kasp, cfg_listelt_value(element),
+                                           log_errors);
                        if (result != ISC_R_SUCCESS) {
                                goto cleanup;
                        }
@@ -658,14 +730,16 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
                CFG_LIST_FOREACH(keys, element) {
                        cfg_obj_t *kobj = cfg_listelt_value(element);
                        result = cfg_kaspkey_fromconfig(
-                               kobj, kasp, check_algorithms, offline_ksk,
-                               keystorelist, ksk_min_lifetime,
+                               kobj, kasp, check_algorithms, log_errors,
+                               offline_ksk, keystorelist, ksk_min_lifetime,
                                zsk_min_lifetime);
                        if (result != ISC_R_SUCCESS) {
-                               cfg_obj_log(kobj, ISC_LOG_ERROR,
-                                           "dnssec-policy: failed to "
-                                           "configure keys (%s)",
-                                           isc_result_totext(result));
+                               if (log_errors) {
+                                       cfg_obj_log(kobj, ISC_LOG_ERROR,
+                                                   "dnssec-policy: failed to "
+                                                   "configure keys (%s)",
+                                                   isc_result_totext(result));
+                               }
                                goto cleanup;
                        }
                }
@@ -698,19 +772,22 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
                        if (role[i] !=
                            (DNS_KASP_KEY_ROLE_ZSK | DNS_KASP_KEY_ROLE_KSK))
                        {
-                               cfg_obj_log(keys, ISC_LOG_ERROR,
-                                           "dnssec-policy: algorithm %zu "
-                                           "requires both KSK and ZSK roles",
-                                           i);
+                               if (log_errors) {
+                                       cfg_obj_log(keys, ISC_LOG_ERROR,
+                                                   "dnssec-policy: algorithm "
+                                                   "%zu requires both KSK and "
+                                                   "ZSK roles",
+                                                   i);
+                               }
                                result = ISC_R_FAILURE;
                        }
-                       if (warn[i][0]) {
+                       if (warn[i][0] && log_errors) {
                                cfg_obj_log(keys, ISC_LOG_WARNING,
                                            "dnssec-policy: algorithm %zu has "
                                            "multiple keys with ZSK role",
                                            i);
                        }
-                       if (warn[i][1]) {
+                       if (warn[i][1] && log_errors) {
                                cfg_obj_log(keys, ISC_LOG_WARNING,
                                            "dnssec-policy: algorithm %zu has "
                                            "multiple keys with KSK role",
@@ -744,20 +821,22 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
                                keystorelist, DNS_KEYSTORE_KEYDIRECTORY,
                                &new_key->keystore);
                        if (result != ISC_R_SUCCESS) {
-                               cfg_obj_log(config, ISC_LOG_ERROR,
-                                           "dnssec-policy: failed to "
-                                           "find keystore (%s)",
-                                           isc_result_totext(result));
+                               if (log_errors) {
+                                       cfg_obj_log(config, ISC_LOG_ERROR,
+                                                   "dnssec-policy: failed to "
+                                                   "find keystore (%s)",
+                                                   isc_result_totext(result));
+                               }
                                goto cleanup;
                        }
                        dns_kasp_addkey(kasp, new_key);
                }
        }
 
-       if (strcmp(kaspname, "insecure") == 0) {
+       if (strcmp(kaspname, "insecure") == 0 && check_keylist) {
                /* "dnssec-policy insecure": key list must be empty */
                INSIST(dns_kasp_keylist_empty(kasp));
-       } else if (default_kasp != NULL) {
+       } else if (default_kasp != NULL && check_keylist) {
                /* There must be keys configured. */
                INSIST(!(dns_kasp_keylist_empty(kasp)));
        }
@@ -775,7 +854,7 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
                }
        } else {
                dns_kasp_setnsec3(kasp, true);
-               result = cfg_nsec3param_fromconfig(nsec3, kasp);
+               result = cfg_nsec3param_fromconfig(nsec3, kasp, log_errors);
                if (result != ISC_R_SUCCESS) {
                        goto cleanup;
                }