From a035f3b10ee0818dec2db773bb0b1f0955d844c0 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 20 Jan 2022 14:00:27 +0100 Subject: [PATCH 01/37] Add configuration for key-store Add new configuration for setting key stores. The new 'key-store' statement allows users to configure key store backends. These can be of type 'file' (that works the same as 'key-directory') or of type 'pkcs11'. In the latter case, keys should be stored in a HSM that is accessible through a PKCS#11 interface. Keys configured within 'dnssec-policy' can now also use the 'key-store' option to set a specific key store. Update the checkconf test to accomodate for the new configuration. --- bin/tests/system/checkconf/good-kasp.conf | 6 +- bin/tests/system/checkconf/good.conf.in | 8 ++- doc/misc/options | 7 +- lib/isccfg/namedconf.c | 84 +++++++++++++++++++++-- 4 files changed, 95 insertions(+), 10 deletions(-) diff --git a/bin/tests/system/checkconf/good-kasp.conf b/bin/tests/system/checkconf/good-kasp.conf index d5da98fc6d..40b5793ad0 100644 --- a/bin/tests/system/checkconf/good-kasp.conf +++ b/bin/tests/system/checkconf/good-kasp.conf @@ -26,7 +26,7 @@ dnssec-policy "test" { keys { ksk key-directory lifetime P1Y algorithm ecdsa256; zsk lifetime P30D algorithm 13; - csk key-directory lifetime unlimited algorithm rsasha256 2048; + csk key-store "hsm" lifetime unlimited algorithm rsasha256 2048; }; max-zone-ttl 86400; nsec3param iterations 0 optout no salt-length 8; @@ -39,6 +39,10 @@ dnssec-policy "test" { signatures-validity-dnskey P14D; zone-propagation-delay PT5M; }; +key-store "hsm" { + directory "."; + uri "pkcs11:token=bind9;pin-value=1234"; +}; options { dnssec-policy "default"; }; diff --git a/bin/tests/system/checkconf/good.conf.in b/bin/tests/system/checkconf/good.conf.in index 7d1f6b8576..b39b20941a 100644 --- a/bin/tests/system/checkconf/good.conf.in +++ b/bin/tests/system/checkconf/good.conf.in @@ -24,8 +24,8 @@ dnssec-policy "test" { dnskey-ttl 3600; keys { ksk key-directory lifetime P1Y algorithm 13 256; - zsk key-directory lifetime P30D algorithm 13; - csk key-directory lifetime P30D algorithm 8 2048; + zsk lifetime P30D algorithm 13; + csk key-store "hsm" lifetime P30D algorithm 8 2048; }; max-zone-ttl 86400; nsec3param ; @@ -39,6 +39,10 @@ dnssec-policy "test" { signatures-validity-dnskey P14D; zone-propagation-delay PT5M; }; +key-store "hsm" { + directory "."; + uri "pkcs11:token=bind9;pin-value=1234"; +}; options { avoid-v4-udp-ports { 100; diff --git a/doc/misc/options b/doc/misc/options index edf6fb04af..bde27bd36d 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -15,7 +15,7 @@ dnssec-policy { cds-digest-types { ; ... }; dnskey-ttl ; inline-signing ; - keys { ( csk | ksk | zsk ) [ ( key-directory ) ] lifetime algorithm [ ]; ... }; + keys { ( csk | ksk | zsk ) [ key-directory | key-store ] lifetime algorithm [ ]; ... }; max-zone-ttl ; nsec3param [ iterations ] [ optout ] [ salt-length ]; parent-ds-ttl ; @@ -42,6 +42,11 @@ key { secret ; }; // may occur multiple times +key-store { + directory ; + uri ; +}; // may occur multiple times + logging { category { ; ... }; // may occur multiple times channel { diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 625edc7f4f..216eebce30 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -105,6 +105,7 @@ static cfg_type_t cfg_type_http_description; static cfg_type_t cfg_type_ixfrdifftype; static cfg_type_t cfg_type_ixfrratio; static cfg_type_t cfg_type_key; +static cfg_type_t cfg_type_keystore; static cfg_type_t cfg_type_logfile; static cfg_type_t cfg_type_logging; static cfg_type_t cfg_type_logseverity; @@ -477,7 +478,6 @@ static cfg_tuplefielddef_t dnssecpolicy_fields[] = { { "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 @@ -582,10 +582,58 @@ static cfg_type_t cfg_type_dnsseckeyrole = { /*% * DNSSEC key storage types. */ -static const char *dnsseckeystore_enums[] = { "key-directory", NULL }; -static cfg_type_t cfg_type_dnsseckeystore = { - "dnssec-key-storage", parse_optional_enum, cfg_print_ustring, - doc_optional_enum, &cfg_rep_string, dnsseckeystore_enums +static keyword_type_t keystore_kw = { "key-store", &cfg_type_astring }; +static cfg_type_t cfg_type_keystorage = { "keystorage", parse_keyvalue, + print_keyvalue, doc_keyvalue, + &cfg_rep_string, &keystore_kw }; + +static isc_result_t +parse_keystore(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + isc_result_t result; + cfg_obj_t *obj = NULL; + + UNUSED(type); + + CHECK(cfg_peektoken(pctx, 0)); + if (pctx->token.type == isc_tokentype_string && + strcasecmp(TOKEN_STRING(pctx), "key-directory") == 0) + { + CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, &obj)); + } else if (pctx->token.type == isc_tokentype_string && + strcasecmp(TOKEN_STRING(pctx), "key-store") == 0) + { + CHECK(cfg_parse_obj(pctx, &cfg_type_keystorage, &obj)); + } else { + CHECK(cfg_parse_void(pctx, NULL, &obj)); + } + + *ret = obj; +cleanup: + return (result); +} + +static void +doc_keystore(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + + cfg_print_cstr(pctx, "[ key-directory | key-store ]"); +} + +static void +print_keystore(cfg_printer_t *pctx, const cfg_obj_t *obj) { + REQUIRE(pctx != NULL); + REQUIRE(obj != NULL); + REQUIRE(obj->type->rep == &cfg_rep_string); + + if (strcasecmp(cfg_obj_asstring(obj), "key-directory") != 0) { + cfg_print_cstr(pctx, "key-store "); + } + cfg_print_ustring(pctx, obj); +} + +static cfg_type_t cfg_type_optional_keystore = { + "optionalkeystorage", parse_keystore, print_keystore, + doc_keystore, &cfg_rep_string, &keystore_kw }; /*% @@ -604,7 +652,7 @@ static cfg_type_t cfg_type_lifetime = { "lifetime", parse_keyvalue, static cfg_tuplefielddef_t kaspkey_fields[] = { { "role", &cfg_type_dnsseckeyrole, 0 }, - { "keystore-type", &cfg_type_dnsseckeystore, 0 }, + { "keystorage", &cfg_type_optional_keystore, 0 }, { "lifetime", &cfg_type_lifetime, 0 }, { "algorithm", &cfg_type_algorithm, 0 }, { "length", &cfg_type_optional_uint32, 0 }, @@ -1143,6 +1191,7 @@ static cfg_clausedef_t namedconf_clauses[] = { { "http", &cfg_type_http_description, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NOTCONFIGURED }, #endif + { "key-store", &cfg_type_keystore, CFG_CLAUSEFLAG_MULTI }, { "logging", &cfg_type_logging, 0 }, { "lwres", NULL, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_ANCIENT }, { "masters", &cfg_type_remoteservers, @@ -2549,6 +2598,29 @@ static cfg_type_t cfg_type_key = { "key", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, key_clausesets }; +/*% + * A key-store statement. + */ +static cfg_clausedef_t keystore_clauses[] = { { "directory", &cfg_type_astring, + 0 }, + { "uri", &cfg_type_qstring, 0 }, + { NULL, NULL, 0 } }; + +static cfg_clausedef_t *keystore_clausesets[] = { keystore_clauses, NULL }; +static cfg_type_t cfg_type_keystoreopts = { + "keystoreopts", cfg_parse_map, cfg_print_map, + cfg_doc_map, &cfg_rep_map, keystore_clausesets +}; + +static cfg_tuplefielddef_t keystore_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "options", &cfg_type_keystoreopts, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_keystore = { "key-store", cfg_parse_tuple, + cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, keystore_fields }; + /*% * Clauses that can be found in a 'server' statement. * From 0284482687f02fd7e0e8af87389aa991c4eee7aa Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 25 Jan 2022 09:25:03 +0100 Subject: [PATCH 02/37] Add code to store key-stores New files to define a structure and functions for dealing with key-stores. --- lib/dns/Makefile.am | 2 + lib/dns/include/dns/keystore.h | 194 +++++++++++++++++++++++++++++++++ lib/dns/keystore.c | 164 ++++++++++++++++++++++++++++ 3 files changed, 360 insertions(+) create mode 100644 lib/dns/include/dns/keystore.h create mode 100644 lib/dns/keystore.c diff --git a/lib/dns/Makefile.am b/lib/dns/Makefile.am index 27a5f14ddb..e18a393609 100644 --- a/lib/dns/Makefile.am +++ b/lib/dns/Makefile.am @@ -83,6 +83,7 @@ libdns_la_HEADERS = \ include/dns/keydata.h \ include/dns/keyflags.h \ include/dns/keymgr.h \ + include/dns/keystore.h \ include/dns/keytable.h \ include/dns/keyvalues.h \ include/dns/librpz.h \ @@ -190,6 +191,7 @@ libdns_la_SOURCES = \ key.c \ keydata.c \ keymgr.c \ + keystore.c \ keytable.c \ log.c \ master.c \ diff --git a/lib/dns/include/dns/keystore.h b/lib/dns/include/dns/keystore.h new file mode 100644 index 0000000000..f898817f57 --- /dev/null +++ b/lib/dns/include/dns/keystore.h @@ -0,0 +1,194 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/***** +***** Module Info +*****/ + +/*! \file dns/keystore.h + * \brief + * DNSSEC Key Store + * + * A key store defines where to store DNSSEC keys. + */ + +#include +#include +#include +#include + +#include + +ISC_LANG_BEGINDECLS + +/* Key store */ +struct dns_keystore { + unsigned int magic; + isc_mem_t *mctx; + const char *name; + const char *engine; + + /* Internals. */ + isc_mutex_t lock; + + /* Locked by themselves. */ + isc_refcount_t references; + + /* Under owner's locking control. */ + ISC_LINK(struct dns_keystore) link; + + /* Configuration values */ + char *directory; + char *pkcs11uri; +}; + +#define DNS_KEYSTORE_MAGIC ISC_MAGIC('K', 'E', 'Y', 'S') +#define DNS_KEYSTORE_VALID(ks) ISC_MAGIC_VALID(ks, DNS_KEYSTORE_MAGIC) + +isc_result_t +dns_keystore_create(isc_mem_t *mctx, const char *name, dns_keystore_t **kspp); +/*%< + * Create a key store. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li 'name' is a valid C string. + * + *\li kspp != NULL && *kspp == NULL + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + * + *\li Other errors are possible. + */ + +void +dns_keystore_attach(dns_keystore_t *source, dns_keystore_t **targetp); +/*%< + * Attach '*targetp' to 'source'. + * + * Requires: + * + *\li 'source' is a valid keystore. + * + *\li 'targetp' points to a NULL dns_keystore_t *. + * + * Ensures: + * + *\li *targetp is attached to source. + * + *\li While *targetp is attached, the keystore will not shut down. + */ + +void +dns_keystore_detach(dns_keystore_t **kspp); +/*%< + * Detach keystore. + * + * Requires: + * + *\li 'kspp' points to a valid dns_keystore_t * + * + * Ensures: + * + *\li *kspp is NULL. + */ + +const char * +dns_keystore_name(dns_keystore_t *keystore); +/*%< + * Get keystore name. + * + * Requires: + * + *\li 'keystore' is a valid keystore. + * + * Returns: + * + *\li name of 'keystore'. + */ + +const char * +dns_keystore_directory(dns_keystore_t *keystore); +/*%< + * Get keystore directory. + * + * Requires: + * + *\li 'keystore' is a valid keystore. + * + * Returns: + * + *\li directory of 'keystore'. + */ + +void +dns_keystore_setdirectory(dns_keystore_t *keystore, const char *dir); +/*%< + * Set keystore directory. + * + * Requires: + * + *\li 'keystore' is a valid keystore. + * + */ + +const char * +dns_keystore_pkcs11uri(dns_keystore_t *keystore); +/*%< + * Get keystore PKCS#11 URI. + * + * Requires: + * + *\li 'keystore' is a valid keystore. + * + * Returns: + * + *\li PKCS#11 URI of 'keystore'. + */ + +void +dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri); +/*%< + * Set keystore PKCS#11 URI. + * + * Requires: + * + *\li 'keystore' is a valid keystore. + * + */ + +isc_result_t +dns_keystorelist_find(dns_keystorelist_t *list, const char *name, + dns_keystore_t **kspp); +/*%< + * Search for a keystore with name 'name' in 'list'. + * If found, '*kspp' is (strongly) attached to it. + * + * Requires: + * + *\li 'kspp' points to a NULL dns_keystore_t *. + * + * Returns: + * + *\li #ISC_R_SUCCESS A matching keystore was found. + *\li #ISC_R_NOTFOUND No matching keystore was found. + */ + +ISC_LANG_ENDDECLS diff --git a/lib/dns/keystore.c b/lib/dns/keystore.c new file mode 100644 index 0000000000..e7fac0ab62 --- /dev/null +++ b/lib/dns/keystore.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#ifdef HAVE_GNUTLS +#include +#include +#endif + +#include + +#include +#include +#include +#include + +#include + +isc_result_t +dns_keystore_create(isc_mem_t *mctx, const char *name, dns_keystore_t **kspp) { + dns_keystore_t *keystore; + + REQUIRE(name != NULL); + REQUIRE(kspp != NULL && *kspp == NULL); + + keystore = isc_mem_get(mctx, sizeof(*keystore)); + keystore->mctx = NULL; + isc_mem_attach(mctx, &keystore->mctx); + + keystore->name = isc_mem_strdup(mctx, name); + isc_mutex_init(&keystore->lock); + + isc_refcount_init(&keystore->references, 1); + + ISC_LINK_INIT(keystore, link); + + keystore->directory = NULL; + keystore->pkcs11uri = NULL; + + keystore->magic = DNS_KEYSTORE_MAGIC; + *kspp = keystore; + + return (ISC_R_SUCCESS); +} + +void +dns_keystore_attach(dns_keystore_t *source, dns_keystore_t **targetp) { + REQUIRE(DNS_KEYSTORE_VALID(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + isc_refcount_increment(&source->references); + *targetp = source; +} + +static inline void +destroy(dns_keystore_t *keystore) { + REQUIRE(!ISC_LINK_LINKED(keystore, link)); + + isc_mutex_destroy(&keystore->lock); + name = UNCONST(keystore->name); + isc_mem_free(keystore->mctx, name); + if (keystore->directory != NULL) { + isc_mem_free(keystore->mctx, keystore->directory); + } + if (keystore->pkcs11uri != NULL) { + isc_mem_free(keystore->mctx, keystore->pkcs11uri); + } + isc_mem_putanddetach(&keystore->mctx, keystore, sizeof(*keystore)); +} + +void +dns_keystore_detach(dns_keystore_t **kspp) { + REQUIRE(kspp != NULL && DNS_KEYSTORE_VALID(*kspp)); + + dns_keystore_t *ks = *kspp; + *kspp = NULL; + + if (isc_refcount_decrement(&ks->references) == 1) { + destroy(ks); + } +} + +const char * +dns_keystore_name(dns_keystore_t *keystore) { + REQUIRE(DNS_KEYSTORE_VALID(keystore)); + + return (keystore->name); +} + +const char * +dns_keystore_directory(dns_keystore_t *keystore) { + REQUIRE(DNS_KEYSTORE_VALID(keystore)); + + return (keystore->directory); +} + +void +dns_keystore_setdirectory(dns_keystore_t *keystore, const char *dir) { + REQUIRE(DNS_KEYSTORE_VALID(keystore)); + + if (keystore->directory != NULL) { + isc_mem_free(keystore->mctx, keystore->directory); + } + keystore->directory = (dir == NULL) + ? NULL + : isc_mem_strdup(keystore->mctx, dir); +} + +const char * +dns_keystore_pkcs11uri(dns_keystore_t *keystore) { + REQUIRE(DNS_KEYSTORE_VALID(keystore)); + + return (keystore->pkcs11uri); +} + +void +dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri) { + REQUIRE(DNS_KEYSTORE_VALID(keystore)); + + if (keystore->pkcs11uri != NULL) { + isc_mem_free(keystore->mctx, keystore->pkcs11uri); + } + keystore->pkcs11uri = (uri == NULL) + ? NULL + : isc_mem_strdup(keystore->mctx, uri); +} + +isc_result_t +dns_keystorelist_find(dns_keystorelist_t *list, const char *name, + dns_keystore_t **kspp) { + dns_keystore_t *keystore = NULL; + + REQUIRE(kspp != NULL && *kspp == NULL); + + if (list == NULL) { + return (ISC_R_NOTFOUND); + } + + for (keystore = ISC_LIST_HEAD(*list); keystore != NULL; + keystore = ISC_LIST_NEXT(keystore, link)) + { + if (strcmp(keystore->name, name) == 0) { + break; + } + } + + if (keystore == NULL) { + return (ISC_R_NOTFOUND); + } + + dns_keystore_attach(keystore, kspp); + return (ISC_R_SUCCESS); +} From 3a86c0742224eb3d4caee4f1645e5ebebd55aa50 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 25 Jan 2022 10:08:43 +0100 Subject: [PATCH 03/37] Add code for creating keystore from config Add code for configuring keystore objects. Add this to the "kaspconf" code, as it is related to 'dnssec-policy' and it is too small to create a separate file for it. --- lib/dns/include/dns/types.h | 6 +- lib/isccfg/include/isccfg/kaspconf.h | 28 ++++++++++ lib/isccfg/kaspconf.c | 84 ++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 89e6b15f78..462e36f2af 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -106,8 +106,10 @@ typedef struct dns_kasp_nsec3param dns_kasp_nsec3param_t; typedef uint16_t dns_keyflags_t; typedef struct dns_keynode dns_keynode_t; typedef ISC_LIST(dns_keynode_t) dns_keynodelist_t; -typedef struct dns_keytable dns_keytable_t; -typedef uint16_t dns_keytag_t; +typedef struct dns_keytable dns_keytable_t; +typedef uint16_t dns_keytag_t; +typedef struct dns_keystore dns_keystore_t; +typedef ISC_LIST(dns_keystore_t) dns_keystorelist_t; typedef struct dns_loadctx dns_loadctx_t; typedef struct dns_loadmgr dns_loadmgr_t; typedef struct dns_masterrawheader dns_masterrawheader_t; diff --git a/lib/isccfg/include/isccfg/kaspconf.h b/lib/isccfg/include/isccfg/kaspconf.h index 744a327695..a005a39165 100644 --- a/lib/isccfg/include/isccfg/kaspconf.h +++ b/lib/isccfg/include/isccfg/kaspconf.h @@ -56,4 +56,32 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, *\li Other errors are possible. */ +isc_result_t +cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, + isc_log_t *logctx, dns_keystorelist_t *keystorelist, + dns_keystore_t **kspp); +/*%< + * Create and configure a key store. If a 'keystorelist' is provided, a lookup + * happens and if a keystore already exists with the same name, no new one is + * created, and no attach to 'kspp' happens. + * + * Requires: + * + *\li config != NULL + + *\li 'mctx' is a valid memory context. + * + *\li 'logctx' is a valid logging context. + * + *\li kspp != NULL && *kspp == NULL + * + * Returns: + * + *\li #ISC_R_SUCCESS If creating and configuring the keystore succeeds. + *\li #ISC_R_EXISTS If 'keystorelist' already has a keystore with 'name'. + *\li #ISC_R_NOMEMORY + * + *\li Other errors are possible. + */ + ISC_LANG_ENDDECLS diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 1a2b0da47c..2757209cdc 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -89,6 +90,23 @@ get_duration(const cfg_obj_t **maps, const char *option, const char *dfl) { return (cfg_obj_asduration(obj)); } +/* + * Utility function for configuring strings. + */ +static const char * +get_string(const cfg_obj_t **maps, const char *option) { + const cfg_obj_t *obj; + isc_result_t result; + obj = NULL; + + result = confget(maps, option, &obj); + if (result == ISC_R_NOTFOUND) { + return (NULL); + } + INSIST(result == ISC_R_SUCCESS); + return (cfg_obj_asstring(obj)); +} + /* * Create a new kasp key derived from configuration. */ @@ -655,3 +673,69 @@ cleanup: dns_kasp_detach(&kasp); return (result); } + +isc_result_t +cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, + isc_log_t *logctx, dns_keystorelist_t *keystorelist, + dns_keystore_t **kspp) { + isc_result_t result; + const cfg_obj_t *maps[2]; + const cfg_obj_t *koptions = NULL; + const char *name = NULL; + dns_keystore_t *keystore = NULL; + int i = 0; + + REQUIRE(config != NULL); + REQUIRE(kspp != NULL && *kspp == NULL); + + name = cfg_obj_asstring(cfg_tuple_get(config, "name")); + INSIST(name != NULL); + + result = dns_keystorelist_find(keystorelist, name, &keystore); + + if (result == ISC_R_SUCCESS) { + cfg_obj_log(config, logctx, ISC_LOG_ERROR, + "key-store: duplicate key-store found '%s'", name); + dns_keystore_detach(&keystore); + return (ISC_R_EXISTS); + } + if (result != ISC_R_NOTFOUND) { + cfg_obj_log(config, logctx, ISC_LOG_ERROR, + "key-store: lookup '%s' failed: %s", name, + isc_result_totext(result)); + return (result); + } + + /* + * No key-store with configured name was found in list, create new one. + */ + INSIST(keystore == NULL); + result = dns_keystore_create(mctx, name, &keystore); + if (result != ISC_R_SUCCESS) { + return (result); + } + INSIST(keystore != NULL); + + /* Now configure. */ + INSIST(DNS_KEYSTORE_VALID(keystore)); + + if (config != NULL) { + koptions = cfg_tuple_get(config, "options"); + maps[i++] = koptions; + } + maps[i] = NULL; + + /* Configuration */ + dns_keystore_setdirectory(keystore, get_string(maps, "directory")); + dns_keystore_setpkcs11uri(keystore, get_string(maps, "uri")); + + /* Append it to the list for future lookups. */ + ISC_LIST_APPEND(*keystorelist, keystore, link); + INSIST(!(ISC_LIST_EMPTY(*keystorelist))); + + /* Success: Attach the keystore to the pointer and return. */ + dns_keystore_attach(keystore, kspp); + + /* Don't detach as keystore is on '*keystorelist' */ + return (ISC_R_SUCCESS); +} From f837bb2af82723c00a9653c3db9e4f8ab90b4e9a Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 25 Jan 2022 10:30:04 +0100 Subject: [PATCH 04/37] Parse key-store config Add the code that actually stores the key-store configuration into structures, also store the reference into the kasp key. --- bin/named/include/named/server.h | 1 + bin/named/server.c | 49 ++++++++++++++++++++++++++++++++ lib/dns/include/dns/kasp.h | 1 + lib/dns/kasp.c | 13 +++++---- lib/isccfg/kaspconf.c | 6 ++++ 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index b0f531e41d..52a13d5658 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -65,6 +65,7 @@ struct named_server { dns_zonemgr_t *zonemgr; dns_viewlist_t viewlist; dns_kasplist_t kasplist; + dns_keystorelist_t keystorelist; ns_interfacemgr_t *interfacemgr; dns_db_t *in_roothints; diff --git a/bin/named/server.c b/bin/named/server.c index 062f021c57..d506c56e47 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -8129,10 +8130,14 @@ load_configuration(const char *filename, named_server_t *server, const cfg_obj_t *options; const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports; const cfg_obj_t *kasps; + const cfg_obj_t *keystores; dns_kasp_t *kasp = NULL; dns_kasp_t *kasp_next = NULL; dns_kasp_t *default_kasp = NULL; dns_kasplist_t tmpkasplist, kasplist; + dns_keystore_t *keystore = NULL; + dns_keystore_t *keystore_next = NULL; + dns_keystorelist_t tmpkeystorelist, keystorelist; const cfg_obj_t *views; dns_view_t *view_next = NULL; @@ -8171,6 +8176,7 @@ load_configuration(const char *filename, named_server_t *server, REQUIRE(isc_loop_current(named_g_loopmgr) == named_g_mainloop); ISC_LIST_INIT(kasplist); + ISC_LIST_INIT(keystorelist); ISC_LIST_INIT(viewlist); ISC_LIST_INIT(builtin_viewlist); ISC_LIST_INIT(cachelist); @@ -8882,6 +8888,29 @@ load_configuration(const char *filename, named_server_t *server, */ (void)configure_session_key(maps, server, named_g_mctx, first_time); + /* + * Create the DNSSEC key stores. + */ + keystores = NULL; + (void)cfg_map_get(config, "key-store", &keystores); + for (element = cfg_list_first(keystores); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + keystore = NULL; + result = cfg_keystore_fromconfig(kconfig, named_g_mctx, + named_g_lctx, &keystorelist, + &keystore)); + if (result != ISC_R_SUCCESS) { + goto cleanup_keystorelist; + } + INSIST(keystore != NULL); + dns_keystore_detach(&keystore); + } + tmpkeystorelist = server->keystorelist; + server->keystorelist = keystorelist; + keystorelist = tmpkeystorelist; + /* * Create the built-in kasp policies ("default", "insecure"). */ @@ -9585,6 +9614,15 @@ cleanup_kasplist: dns_kasp_detach(&kasp); } +cleanup_keystorelist: + for (keystore = ISC_LIST_HEAD(keystorelist); keystore != NULL; + keystore = keystore_next) + { + keystore_next = ISC_LIST_NEXT(keystore, link); + ISC_LIST_UNLINK(keystorelist, keystore, link); + dns_keystore_detach(&keystore); + } + cleanup_v6portset: isc_portset_destroy(named_g_mctx, &v6portset); @@ -9849,6 +9887,7 @@ shutdown_server(void *arg) { named_server_t *server = (named_server_t *)arg; dns_view_t *view = NULL, *view_next = NULL; dns_kasp_t *kasp = NULL, *kasp_next = NULL; + dns_keystore_t *keystore = NULL, *keystore_next = NULL; bool flush = server->flushonshutdown; named_cache_t *nsc = NULL; @@ -9895,6 +9934,14 @@ shutdown_server(void *arg) { dns_kasp_detach(&kasp); } + for (keystore = ISC_LIST_HEAD(server->keystorelist); keystore != NULL; + keystore = keystore_next) + { + keystore_next = ISC_LIST_NEXT(keystore, link); + ISC_LIST_UNLINK(server->keystorelist, keystore, link); + dns_keystore_detach(&keystore); + } + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = view_next) { @@ -10001,6 +10048,7 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { /* Initialize server data structures. */ ISC_LIST_INIT(server->kasplist); + ISC_LIST_INIT(server->keystorelist); ISC_LIST_INIT(server->viewlist); /* Must be first. */ @@ -10109,6 +10157,7 @@ named_server_destroy(named_server_t **serverp) { dst_lib_destroy(); INSIST(ISC_LIST_EMPTY(server->kasplist)); + INSIST(ISC_LIST_EMPTY(server->keystorelist)); INSIST(ISC_LIST_EMPTY(server->viewlist)); INSIST(ISC_LIST_EMPTY(server->cachelist)); diff --git a/lib/dns/include/dns/kasp.h b/lib/dns/include/dns/kasp.h index 9a32f586b2..92bf406c98 100644 --- a/lib/dns/include/dns/kasp.h +++ b/lib/dns/include/dns/kasp.h @@ -51,6 +51,7 @@ struct dns_kasp_key { ISC_LINK(struct dns_kasp_key) link; /* Configuration */ + char *keystore; uint32_t lifetime; uint8_t algorithm; int length; diff --git a/lib/dns/kasp.c b/lib/dns/kasp.c index 639811bf4e..aa6637f594 100644 --- a/lib/dns/kasp.c +++ b/lib/dns/kasp.c @@ -385,21 +385,20 @@ dns_kasp_addkey(dns_kasp_t *kasp, dns_kasp_key_t *key) { isc_result_t dns_kasp_key_create(dns_kasp_t *kasp, dns_kasp_key_t **keyp) { - dns_kasp_key_t *key; + dns_kasp_key_t *key = NULL; + dns_kasp_key_t k = { .length = -1 }; REQUIRE(DNS_KASP_VALID(kasp)); REQUIRE(keyp != NULL && *keyp == NULL); key = isc_mem_get(kasp->mctx, sizeof(*key)); + *key = k; + key->mctx = NULL; isc_mem_attach(kasp->mctx, &key->mctx); ISC_LINK_INIT(key, link); - key->lifetime = 0; - key->algorithm = 0; - key->length = -1; - key->role = 0; *keyp = key; return (ISC_R_SUCCESS); } @@ -408,6 +407,10 @@ void dns_kasp_key_destroy(dns_kasp_key_t *key) { REQUIRE(key != NULL); + if (key->keystore != NULL) { + isc_mem_free(key->mctx, key->keystore); + key->keystore = NULL; + } isc_mem_putanddetach(&key->mctx, key, sizeof(*key)); } diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 2757209cdc..bb0aa46f0e 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -145,6 +145,12 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, key->role |= DNS_KASP_KEY_ROLE_ZSK; } + obj = cfg_tuple_get(config, "keystorage"); + if (cfg_obj_isstring(obj)) { + key->keystore = isc_mem_strdup(key->mctx, + cfg_obj_asstring(obj)); + } + key->lifetime = 0; /* unlimited */ obj = cfg_tuple_get(config, "lifetime"); if (cfg_obj_isduration(obj)) { From 594d4a81f1a10c55756f19efa655a80ee407d399 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 9 Feb 2022 11:19:21 +0100 Subject: [PATCH 05/37] Check if key-store exists Add checkconf check to ensure that the used key-store in the keys section exists. Error if that is not the case. We also don't allow the special keyword 'key-directory' as that is internally used to signal that the zone's key-directory should be used. --- .../system/checkconf/bad-kasp-keystore.conf | 19 ++++ .../checkconf/bad-keystore-key-directory.conf | 24 ++++ lib/dns/include/dns/kasp.h | 25 ++++- lib/dns/include/dns/keystore.h | 2 + lib/dns/kasp.c | 11 +- lib/isccfg/check.c | 103 ++++++++++++++++++ 6 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 bin/tests/system/checkconf/bad-kasp-keystore.conf create mode 100644 bin/tests/system/checkconf/bad-keystore-key-directory.conf diff --git a/bin/tests/system/checkconf/bad-kasp-keystore.conf b/bin/tests/system/checkconf/bad-kasp-keystore.conf new file mode 100644 index 0000000000..8bbe9a38e2 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp-keystore.conf @@ -0,0 +1,19 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// Bad dnssec-policy configuration because there is no key-store with this name. +dnssec-policy "bad" { + keys { + csk key-store "ks404" lifetime unlimited algorithm 13; + }; +}; diff --git a/bin/tests/system/checkconf/bad-keystore-key-directory.conf b/bin/tests/system/checkconf/bad-keystore-key-directory.conf new file mode 100644 index 0000000000..7007cf80f4 --- /dev/null +++ b/bin/tests/system/checkconf/bad-keystore-key-directory.conf @@ -0,0 +1,24 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// Bad key-store configuration because the keyword 'key-directory' may not +// be used. +key-store "key-directory" { + directory "."; +}; + +dnssec-policy "bad" { + keys { + csk key-store "key-directory" lifetime unlimited algorithm 13; + }; +}; diff --git a/lib/dns/include/dns/kasp.h b/lib/dns/include/dns/kasp.h index 92bf406c98..288c754c9f 100644 --- a/lib/dns/include/dns/kasp.h +++ b/lib/dns/include/dns/kasp.h @@ -51,11 +51,11 @@ struct dns_kasp_key { ISC_LINK(struct dns_kasp_key) link; /* Configuration */ - char *keystore; - uint32_t lifetime; - uint8_t algorithm; - int length; - uint8_t role; + const char *keystore; + uint32_t lifetime; + uint8_t algorithm; + int length; + uint8_t role; }; struct dns_kasp_nsec3param { @@ -644,6 +644,21 @@ dns_kasp_key_lifetime(dns_kasp_key_t *key); * */ +const char * +dns_kasp_key_keystore(dns_kasp_key_t *key); +/*%< + * The keystore reference of this key. + * + * Requires: + * + *\li key != NULL + * + * Returns: + * + *\li Keystore of key, or NULL if zone's key-directory is used. + * + */ + bool dns_kasp_key_ksk(dns_kasp_key_t *key); /*%< diff --git a/lib/dns/include/dns/keystore.h b/lib/dns/include/dns/keystore.h index f898817f57..a5d5840aa1 100644 --- a/lib/dns/include/dns/keystore.h +++ b/lib/dns/include/dns/keystore.h @@ -57,6 +57,8 @@ struct dns_keystore { #define DNS_KEYSTORE_MAGIC ISC_MAGIC('K', 'E', 'Y', 'S') #define DNS_KEYSTORE_VALID(ks) ISC_MAGIC_VALID(ks, DNS_KEYSTORE_MAGIC) +#define DNS_KEYSTORE_KEYDIRECTORY "key-directory" + isc_result_t dns_keystore_create(isc_mem_t *mctx, const char *name, dns_keystore_t **kspp); /*%< diff --git a/lib/dns/kasp.c b/lib/dns/kasp.c index aa6637f594..de8d757fd4 100644 --- a/lib/dns/kasp.c +++ b/lib/dns/kasp.c @@ -408,7 +408,9 @@ dns_kasp_key_destroy(dns_kasp_key_t *key) { REQUIRE(key != NULL); if (key->keystore != NULL) { - isc_mem_free(key->mctx, key->keystore); + char *ks; + DE_CONST(key->keystore, ks); + isc_mem_free(key->mctx, ks); key->keystore = NULL; } isc_mem_putanddetach(&key->mctx, key, sizeof(*key)); @@ -472,6 +474,13 @@ dns_kasp_key_lifetime(dns_kasp_key_t *key) { return (key->lifetime); } +const char * +dns_kasp_key_keystore(dns_kasp_key_t *key) { + REQUIRE(key != NULL); + + return (key->keystore); +} + bool dns_kasp_key_ksk(dns_kasp_key_t *key) { REQUIRE(key != NULL); diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index c4e09f1c88..5929b27888 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -1187,6 +1188,39 @@ check_port(const cfg_obj_t *options, isc_log_t *logctx, const char *type, return (ISC_R_SUCCESS); } +static isc_result_t +check_keystore(const cfg_obj_t *obj, isc_log_t *logctx, dns_kasp_t *kasp, + dns_keystorelist_t *kslist) { + isc_result_t result = ISC_R_SUCCESS; + dns_kasp_key_t *kkey; + dns_keystore_t *ks = NULL; + + REQUIRE(kasp != NULL); + + dns_kasp_freeze(kasp); + + for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL; + kkey = ISC_LIST_NEXT(kkey, link)) + { + const char *keystore = dns_kasp_key_keystore(kkey); + if (keystore != NULL && strcmp("key-directory", keystore) != 0) + { + if (dns_keystorelist_find(kslist, keystore, &ks) == + ISC_R_SUCCESS) { + dns_keystore_detach(&ks); + } else { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "key-store '%s' not found", + keystore); + result = ISC_R_FAILURE; + } + } + } + + dns_kasp_thaw(kasp); + return (result); +} + static isc_result_t check_options(const cfg_obj_t *options, const cfg_obj_t *config, bool check_algorithms, isc_log_t *logctx, isc_mem_t *mctx, @@ -1200,6 +1234,8 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, const char *str; isc_buffer_t b; uint32_t lifetime = 3600; + dns_keystorelist_t kslist; + dns_keystore_t *ks = NULL, *ks_next = NULL; const char *ccalg = "siphash24"; cfg_aclconfctx_t *actx = NULL; static const char *sources[] = { @@ -1329,6 +1365,55 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, } } + /* + * Check key-store. + */ + ISC_LIST_INIT(kslist); + + obj = NULL; + (void)cfg_map_get(options, "key-store", &obj); + if (obj != NULL) { + if (optlevel != optlevel_config) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "may only be configured at the top level"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } else if (cfg_obj_islist(obj)) { + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + isc_result_t ret; + const char *name; + cfg_obj_t *kconfig = cfg_listelt_value(element); + if (!cfg_obj_istuple(kconfig)) { + continue; + } + name = cfg_obj_asstring(cfg_tuple_get( + cfg_listelt_value(element), "name")); + if (strcmp(DNS_KEYSTORE_KEYDIRECTORY, name) == 0) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "name '%s' not allowed", + DNS_KEYSTORE_KEYDIRECTORY); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + } + + ret = cfg_keystore_fromconfig( + kconfig, mctx, logctx, &kslist, &ks); + if (ret != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + result = ret; + } + } + if (ks != NULL) { + dns_keystore_detach(&ks); + } + } + } + } + /* * Check dnssec-policy. */ @@ -1367,6 +1452,12 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, ret = cfg_kasp_fromconfig( kconfig, NULL, check_algorithms, mctx, logctx, &list, &kasp); + if (ret == ISC_R_SUCCESS) { + /* Check key-stores of keys */ + ret = check_keystore( + obj, logctx, kasp, + &kslist); + } if (ret != ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) { result = ret; @@ -1407,6 +1498,18 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, } } + /* + * Cleanup key-store. + */ + for (ks = ISC_LIST_HEAD(kslist); ks != NULL; ks = ks_next) { + ks_next = ISC_LIST_NEXT(ks, link); + ISC_LIST_UNLINK(kslist, ks, link); + dns_keystore_detach(&ks); + } + + /* + * Other checks. + */ obj = NULL; cfg_map_get(options, "max-rsa-exponent-size", &obj); if (obj != NULL) { From 22d1fde1a5aca5db95bf269371f89cd4db2ec5d7 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 9 Feb 2022 12:19:06 +0100 Subject: [PATCH 06/37] Check if key-store directory exists Similar to key-directory, check if the key-store directory exists and if it is an actual directory. This commit fixes an accidental test bug in checkconf where if the "warn key-dir" test failed, the result was ignored. --- bin/tests/system/checkconf/tests.sh | 12 +++++ bin/tests/system/checkconf/warn-keydir.conf | 4 ++ lib/isccfg/check.c | 50 +++++++++++++++++++-- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/bin/tests/system/checkconf/tests.sh b/bin/tests/system/checkconf/tests.sh index d3289015d2..74361fa9cb 100644 --- a/bin/tests/system/checkconf/tests.sh +++ b/bin/tests/system/checkconf/tests.sh @@ -300,20 +300,32 @@ n=$((n + 1)) echo_i "checking for missing key directory warning ($n)" ret=0 rm -rf test.keydir +rm -rf test.keystoredir $CHECKCONF warn-keydir.conf >checkconf.out$n.1 2>&1 l=$(grep "'test.keydir' does not exist" checkconf.out$n.2 2>&1 l=$(grep "'test.keydir' is not a directory" checkconf.out$n.3 2>&1 l=$(grep "key-directory" Date: Thu, 10 Feb 2022 16:51:32 +0100 Subject: [PATCH 07/37] Check if key-store directory is not reused Similar to key-directory, check for zones in different views and different key and signing policies. Zones must be using different key directories to store key files on disk. Now that a key directory can be linked with a dnssec-policy key, the 'keydirexist' checking needs to be reshuffled. Add tests for bad configuration examples, named-checkconf should catch those. Also add test cases for a mix of key-directory and key-store directory. --- .../bad-kasp-keydir-vs-keystore1.conf | 59 ++++++ .../bad-kasp-keydir-vs-keystore2.conf | 59 ++++++ .../bad-kasp-keydir-vs-keystore3.conf | 64 ++++++ .../bad-kasp-keydir-vs-keystore4.conf | 60 ++++++ .../system/checkconf/bad-kasp-keystore1.conf | 68 ++++++ .../system/checkconf/bad-kasp-keystore2.conf | 64 ++++++ lib/isccfg/check.c | 194 ++++++++++++++++-- 7 files changed, 549 insertions(+), 19 deletions(-) create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore1.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore2.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore3.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore4.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-keystore1.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-keystore2.conf diff --git a/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore1.conf b/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore1.conf new file mode 100644 index 0000000000..f01ded49f3 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore1.conf @@ -0,0 +1,59 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * The same zone in different views is using different DNSSEC policies, so it + * may not use the same directory for storing keys. + */ + + +key "keyforview1" { + algorithm "hmac-sha1"; + secret "YPfMoAk6h+3iN8MDRQC004iSNHY="; +}; + +key "keyforview2" { + algorithm "hmac-sha1"; + secret "4xILSZQnuO1UKubXHkYUsvBRPu8="; +}; + +key-store "store2" { + directory "."; +}; + +dnssec-policy "policy2" { + keys { + csk key-store "store2" lifetime unlimited algorithm 13; + }; +}; + +view "example1" { + match-clients { key "keyforview1"; }; + + zone "example.net" { + type primary; + dnssec-policy "default"; + key-directory "."; + file "example1.db"; + }; +}; + +view "example2" { + match-clients { key "keyforview2"; }; + + zone "example.net" { + type primary; + dnssec-policy "policy2"; + file "example2.db"; + }; +}; diff --git a/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore2.conf b/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore2.conf new file mode 100644 index 0000000000..efe09e37fb --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore2.conf @@ -0,0 +1,59 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * In view "example1" no key-directory is set, so the default is used. + * In view "example2" the key-store directory is set to "." which is the + * default. This should fail because the same zone in different views is using + * different DNSSEC policies. + */ + +key "keyforview1" { + algorithm "hmac-sha1"; + secret "YPfMoAk6h+3iN8MDRQC004iSNHY="; +}; + +key "keyforview2" { + algorithm "hmac-sha1"; + secret "4xILSZQnuO1UKubXHkYUsvBRPu8="; +}; + +key-store "store2" { + directory "."; +}; + +dnssec-policy "policy2" { + keys { + csk key-store "store2" lifetime unlimited algorithm 13; + }; +}; + +view "example1" { + match-clients { key "keyforview1"; }; + + zone "example.net" { + type primary; + dnssec-policy "default"; + file "example1.db"; + }; +}; + +view "example2" { + match-clients { key "keyforview2"; }; + + zone "example.net" { + type primary; + dnssec-policy "policy2"; + file "example2.db"; + }; +}; diff --git a/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore3.conf b/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore3.conf new file mode 100644 index 0000000000..fdc8577d44 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore3.conf @@ -0,0 +1,64 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * The zone in view "example1" inherits the key directory value from "options", + * but in view "example2" sets the key-store directory to the same value. + * This should be detected as an error because the zone is using different + * DNSSEC policies and should thus use different key directories. + */ + +key "keyforview1" { + algorithm "hmac-sha1"; + secret "YPfMoAk6h+3iN8MDRQC004iSNHY="; +}; + +key "keyforview2" { + algorithm "hmac-sha1"; + secret "4xILSZQnuO1UKubXHkYUsvBRPu8="; +}; + +key-store "store2" { + directory "keys"; +}; + +dnssec-policy "policy2" { + keys { + csk key-store "store2" lifetime unlimited algorithm 13; + }; +}; + +options { + key-directory "keys"; +}; + +view "example1" { + match-clients { key "keyforview1"; }; + + zone "example.net" { + type primary; + /* key-directory inherited from options. */ + dnssec-policy "default"; + file "example1.db"; + }; +}; + +view "example2" { + match-clients { key "keyforview2"; }; + + zone "example.net" { + type primary; + dnssec-policy "policy2"; + file "example2.db"; + }; +}; diff --git a/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore4.conf b/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore4.conf new file mode 100644 index 0000000000..ddfbebc768 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp-keydir-vs-keystore4.conf @@ -0,0 +1,60 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * The zone inherits the key-directory from the "view" level. Both views use the + * same directory for storing keys, but the zone uses a different DNSSEC policy + * per view. This is a configuration error. + */ + +key "keyforview1" { + algorithm "hmac-sha1"; + secret "YPfMoAk6h+3iN8MDRQC004iSNHY="; +}; + +key "keyforview2" { + algorithm "hmac-sha1"; + secret "4xILSZQnuO1UKubXHkYUsvBRPu8="; +}; + +key-store "store2" { + directory "keys"; +}; + +dnssec-policy "policy2" { + keys { + csk key-store "store2" lifetime unlimited algorithm 13; + }; +}; + +view "example1" { + match-clients { key "keyforview1"; }; + + key-directory "keys"; + + zone "example.net" { + type primary; + dnssec-policy "default"; + file "example1.db"; + }; +}; + +view "example2" { + match-clients { key "keyforview2"; }; + + zone "example.net" { + type primary; + dnssec-policy "policy2"; + file "example2.db"; + }; +}; diff --git a/bin/tests/system/checkconf/bad-kasp-keystore1.conf b/bin/tests/system/checkconf/bad-kasp-keystore1.conf new file mode 100644 index 0000000000..68540fab38 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp-keystore1.conf @@ -0,0 +1,68 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * The same zone in different views is using different DNSSEC policies, so it + * may not use the same key-store directory. + */ + + +key "keyforview1" { + algorithm "hmac-sha1"; + secret "YPfMoAk6h+3iN8MDRQC004iSNHY="; +}; + +key "keyforview2" { + algorithm "hmac-sha1"; + secret "4xILSZQnuO1UKubXHkYUsvBRPu8="; +}; + +key-store "store1" { + directory "keys"; +}; + +key-store "store2" { + directory "keys"; +}; + +dnssec-policy "policy1" { + keys { + csk key-store "store1" lifetime unlimited algorithm 13; + }; +}; + +dnssec-policy "policy2" { + keys { + csk key-store "store2" lifetime unlimited algorithm 13; + }; +}; + +view "example1" { + match-clients { key "keyforview1"; }; + + zone "example.net" { + type primary; + dnssec-policy "policy1"; + file "example1.db"; + }; +}; + +view "example2" { + match-clients { key "keyforview2"; }; + + zone "example.net" { + type primary; + dnssec-policy "policy2"; + file "example2.db"; + }; +}; diff --git a/bin/tests/system/checkconf/bad-kasp-keystore2.conf b/bin/tests/system/checkconf/bad-kasp-keystore2.conf new file mode 100644 index 0000000000..19be1bd51e --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp-keystore2.conf @@ -0,0 +1,64 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Both policies use the same key-store. Should fail because the same zone in + * different views is using different DNSSEC policies. + */ + +key "keyforview1" { + algorithm "hmac-sha1"; + secret "YPfMoAk6h+3iN8MDRQC004iSNHY="; +}; + +key "keyforview2" { + algorithm "hmac-sha1"; + secret "4xILSZQnuO1UKubXHkYUsvBRPu8="; +}; + +key-store "store" { + directory "keys"; +}; + +dnssec-policy "policy1" { + keys { + csk key-store "store" lifetime unlimited algorithm 13; + }; +}; + +dnssec-policy "policy2" { + keys { + csk key-store "store" lifetime unlimited algorithm 13; + }; +}; + + +view "example1" { + match-clients { key "keyforview1"; }; + + zone "example.net" { + type primary; + dnssec-policy "policy1"; + file "example1.db"; + }; +}; + +view "example2" { + match-clients { key "keyforview2"; }; + + zone "example.net" { + type primary; + dnssec-policy "policy2"; + file "example2.db"; + }; +}; diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 78474c0581..fb042d095e 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -78,8 +78,9 @@ fileexist(const cfg_obj_t *obj, isc_symtab_t *symtab, bool writeable, isc_log_t *logctxlogc); static isc_result_t -keydirexist(const cfg_obj_t *zcgf, const char *dir, const char *kaspnamestr, - isc_symtab_t *symtab, isc_log_t *logctx, isc_mem_t *mctx); +keydirexist(const cfg_obj_t *zcgf, const char *optname, dns_name_t *zname, + const char *dirname, const char *kaspnamestr, isc_symtab_t *symtab, + isc_log_t *logctx, isc_mem_t *mctx); static const cfg_obj_t * find_maplist(const cfg_obj_t *config, const char *listname, const char *name); @@ -2945,6 +2946,147 @@ cleanup: return (retval); } +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_log_t *logctx, isc_mem_t *mctx) { + const char *dir = keydir; + const cfg_listelt_t *element; + isc_result_t ret, result = ISC_R_SUCCESS; + bool do_cleanup = false; + bool done = false; + bool keystore = false; + + const cfg_obj_t *kasps = NULL; + dns_kasp_t *kasp = NULL, *kasp_next = NULL; + dns_kasplist_t kasplist; + + const cfg_obj_t *keystores = NULL; + dns_keystore_t *ks = NULL, *ks_next = NULL; + dns_keystorelist_t kslist; + + /* 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; + + /* + * Look for the dnssec-policy by name, which is the dnssec-policy + * for the zone in question. + */ + for (element = cfg_list_first(kasps); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + const cfg_obj_t *kaspobj = NULL; + + if (!cfg_obj_istuple(kconfig)) { + continue; + } + + kaspobj = cfg_tuple_get(kconfig, "name"); + if (strcmp(name, cfg_obj_asstring(kaspobj)) != 0) { + continue; + } + + ret = cfg_kasp_fromconfig(kconfig, NULL, mctx, logctx, + &kasplist, &kasp); + if (ret != ISC_R_SUCCESS) { + kasp = NULL; + } + break; + } + if (kasp == NULL) { + goto check; + } + + /* Check key-stores of keys */ + dns_kasp_freeze(kasp); + for (dns_kasp_key_t *kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); + kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) + { + const char *ksname = dns_kasp_key_keystore(kkey); + if (ksname == NULL || strcmp("key-directory", ksname) == 0) { + dir = keydir; + keystore = false; + } else { + /* Look for the key-store by name */ + for (element = cfg_list_first(keystores); + element != NULL; element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + const cfg_obj_t *ksobj = NULL; + + if (!cfg_obj_istuple(kconfig)) { + continue; + } + + ksobj = cfg_tuple_get(kconfig, "name"); + if (strcmp(ksname, cfg_obj_asstring(ksobj)) != + 0) { + continue; + } + + /* Found the keystore */ + ksobj = NULL; + if (cfg_map_get(cfg_tuple_get(kconfig, "option" + "s"), + "directory", + &ksobj) == ISC_R_SUCCESS) + { + /* Check this directory in the symtable + */ + dir = cfg_obj_asstring(ksobj); + keystore = true; + } else { + dir = keydir; + keystore = false; + } + break; + } + } + } + dns_kasp_thaw(kasp); + done = true; + +check: + if (!done) { + ret = keydirexist(zconfig, "key-directory", zname, dir, name, + keydirs, logctx, mctx); + if (ret != ISC_R_SUCCESS) { + result = ret; + } + } + + if (do_cleanup) { + if (ks != NULL) { + dns_keystore_detach(&ks); + } + if (kasp != NULL) { + dns_kasp_detach(&kasp); + } + for (ks = ISC_LIST_HEAD(kslist); ks != NULL; ks = ks_next) { + ks_next = ISC_LIST_NEXT(ks, link); + ISC_LIST_UNLINK(kslist, ks, link); + dns_keystore_detach(&ks); + } + for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; + kasp = kasp_next) { + kasp_next = ISC_LIST_NEXT(kasp, link); + ISC_LIST_UNLINK(kasplist, kasp, link); + dns_kasp_detach(&kasp); + } + } + + return (result); +} + static isc_result_t check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, const cfg_obj_t *config, isc_symtab_t *symtab, @@ -3213,8 +3355,6 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, (void)cfg_map_get(goptions, "dnssec-policy", &obj); } if (obj != NULL) { - const cfg_obj_t *kasps = NULL; - kaspname = cfg_obj_asstring(obj); if (strcmp(kaspname, "default") == 0) { has_dnssecpolicy = true; @@ -3798,19 +3938,18 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } /* - * Make sure there is no other zone with the same - * key-directory and a different dnssec-policy. + * Make sure there is no other zone with the same key directory (from + * (key-directory or key-store/directory) and a different dnssec-policy. */ if (zname != NULL) { - char keydirbuf[DNS_NAME_FORMATSIZE + 128]; - char *tmp = keydirbuf; - size_t len = sizeof(keydirbuf); - dns_name_format(zname, keydirbuf, sizeof(keydirbuf)); - len -= strlen(tmp); - tmp += strlen(tmp); - (void)snprintf(tmp, len, "/%s", (dir == NULL) ? "(null)" : dir); - tresult = keydirexist(zconfig, (const char *)keydirbuf, - kaspname, keydirs, logctx, mctx); + if (has_dnssecpolicy) { + tresult = check_keydir(config, zconfig, zname, kaspname, + dir, keydirs, logctx, mctx); + } else { + tresult = keydirexist(zconfig, "key-directory", zname, + dir, kaspname, keydirs, logctx, + mctx); + } if (tresult != ISC_R_SUCCESS) { result = tresult; } @@ -4030,16 +4169,33 @@ fileexist(const cfg_obj_t *obj, isc_symtab_t *symtab, bool writeable, } static isc_result_t -keydirexist(const cfg_obj_t *zcfg, const char *keydir, const char *kaspnamestr, - isc_symtab_t *symtab, isc_log_t *logctx, isc_mem_t *mctx) { +keydirexist(const cfg_obj_t *zcfg, const char *optname, dns_name_t *zname, + const char *dirname, const char *kaspnamestr, isc_symtab_t *symtab, + isc_log_t *logctx, isc_mem_t *mctx) { isc_result_t result; isc_symvalue_t symvalue; char *symkey; + char keydirbuf[DNS_NAME_FORMATSIZE + 128]; + char *keydir = keydirbuf; + size_t len = sizeof(keydirbuf); + size_t n; if (kaspnamestr == NULL || strcmp(kaspnamestr, "none") == 0) { return (ISC_R_SUCCESS); } + dns_name_format(zname, keydirbuf, sizeof(keydirbuf)); + len -= strlen(keydir); + keydir += strlen(keydir); + n = snprintf(keydir, len, "/%s", (dirname == NULL) ? "." : dirname); + if (n > len) { + cfg_obj_log(zcfg, logctx, ISC_LOG_WARNING, + "%s '%s' truncated because too long, may cause " + "false positives in key directory in use checks", + optname, (dirname == NULL) ? "." : dirname); + } + keydir = keydirbuf; + result = isc_symtab_lookup(symtab, keydir, 0, &symvalue); if (result == ISC_R_SUCCESS) { const cfg_obj_t *kasp = NULL; @@ -4061,9 +4217,9 @@ keydirexist(const cfg_obj_t *zcfg, const char *keydir, const char *kaspnamestr, } cfg_obj_log(zcfg, logctx, ISC_LOG_ERROR, - "key-directory '%s' already in use by zone %s with " + "%s '%s' already in use by zone %s with " "policy %s: %s:%u", - keydir, + optname, keydir, cfg_obj_asstring(cfg_tuple_get(exist, "name")), cfg_obj_asstring(kasp), file, line); return (ISC_R_EXISTS); From 155aba5bd56b463473cb48e6a3d390ade4ba29dd Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 10 Feb 2022 16:56:00 +0100 Subject: [PATCH 08/37] Fix a checkconf bug The check for printing zone list failed because of these additional lines in the output: good.conf:22: dnssec-policy: key algorithm 13 has predefined length; \ ignoring length value 256 I am not sure why this failure hasn't happened before already. --- bin/tests/system/checkconf/good.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tests/system/checkconf/good.conf.in b/bin/tests/system/checkconf/good.conf.in index b39b20941a..59ecbcec44 100644 --- a/bin/tests/system/checkconf/good.conf.in +++ b/bin/tests/system/checkconf/good.conf.in @@ -23,7 +23,7 @@ dnssec-policy "test" { }; dnskey-ttl 3600; keys { - ksk key-directory lifetime P1Y algorithm 13 256; + ksk key-directory lifetime P1Y algorithm 13; zsk lifetime P30D algorithm 13; csk key-store "hsm" lifetime P30D algorithm 8 2048; }; From ffc41d1b140a8b859abf33f60ca46b5d38445d5d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 18 Feb 2022 15:03:49 +0100 Subject: [PATCH 09/37] Store key store reference instead of name When creating the kasp structure, instead of storing the name of the key store on keys, store a reference to the key store object instead. This requires to build the keystore list prior to creating the kasp structures, in the dnssec tools, the check code and the server code. We will create a builtin keystore called "key-directory" which means use the zone's key-directory as the key store. The check code changes, because now the keystore is looked up before creating the kasp structure (and if the keystore is not found, this is an error). Instead of looking up the keystore after all 'dnssec-policy' clauses have been read. --- bin/dnssec/dnssec-keygen.c | 39 +++++++- bin/named/server.c | 33 +++++-- lib/dns/include/dns/kasp.h | 13 +-- lib/dns/kasp.c | 7 +- lib/isccfg/check.c | 138 ++++++++++----------------- lib/isccfg/include/isccfg/kaspconf.h | 14 ++- lib/isccfg/kaspconf.c | 71 +++++++++++--- 7 files changed, 191 insertions(+), 124 deletions(-) diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index 6e5dd34769..9d32558ac2 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -256,13 +256,39 @@ progress(int p) { static void kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, dns_kasp_t **kaspp) { + isc_result_t result = ISC_R_NOTFOUND; const cfg_listelt_t *element; const cfg_obj_t *kasps = NULL; dns_kasp_t *kasp = NULL, *kasp_next; - isc_result_t result = ISC_R_NOTFOUND; dns_kasplist_t kasplist; + const cfg_obj_t *keystores = NULL; + dns_keystore_t *ks = NULL, *ks_next; + dns_keystorelist_t kslist; ISC_LIST_INIT(kasplist); + ISC_LIST_INIT(kslist); + + (void)cfg_map_get(config, "key-store", &keystores); + for (element = cfg_list_first(keystores); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + ks = NULL; + result = cfg_keystore_fromconfig(kconfig, mctx, lctx, &kslist, + &ks); + if (result != ISC_R_SUCCESS) { + fatal("failed to configure key-store '%s': %s", + cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), + isc_result_totext(result)); + } + INSIST(ks != NULL); + dns_keystore_detach(&ks); + } + /* Default key-directory key store. */ + ks = NULL; + (void)cfg_keystore_fromconfig(NULL, mctx, lctx, engine, &kslist, &ks); + INSIST(ks != NULL); + dns_keystore_detach(&ks); (void)cfg_map_get(config, "dnssec-policy", &kasps); for (element = cfg_list_first(kasps); element != NULL; @@ -277,7 +303,7 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, } result = cfg_kasp_fromconfig(kconfig, NULL, true, mctx, lctx, - &kasplist, &kasp); + &kslist, &kasplist, &kasp); if (result != ISC_R_SUCCESS) { fatal("failed to configure dnssec-policy '%s': %s", cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), @@ -298,6 +324,15 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, ISC_LIST_UNLINK(kasplist, kasp, link); dns_kasp_detach(&kasp); } + + /* + * Cleanup keystore list. + */ + for (ks = ISC_LIST_HEAD(kslist); ks != NULL; ks = ks_next) { + ks_next = ISC_LIST_NEXT(ks, link); + ISC_LIST_UNLINK(kslist, ks, link); + dns_keystore_detach(&ks); + } } static void diff --git a/bin/named/server.c b/bin/named/server.c index d506c56e47..1b6037d9b3 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -8888,6 +8888,19 @@ load_configuration(const char *filename, named_server_t *server, */ (void)configure_session_key(maps, server, named_g_mctx, first_time); + /* + * Create the built-in key store ("key-directory"). + */ + keystore = NULL; + result = cfg_keystore_fromconfig(NULL, named_g_mctx, named_g_lctx, + named_g_engine, &keystorelist, + &keystore); + if (result != ISC_R_SUCCESS) { + goto cleanup_keystorelist; + } + INSIST(keystore != NULL); + dns_keystore_detach(&keystore); + /* * Create the DNSSEC key stores. */ @@ -8899,17 +8912,14 @@ load_configuration(const char *filename, named_server_t *server, cfg_obj_t *kconfig = cfg_listelt_value(element); keystore = NULL; result = cfg_keystore_fromconfig(kconfig, named_g_mctx, - named_g_lctx, &keystorelist, - &keystore)); + named_g_lctx, named_g_engine, + &keystorelist, &keystore); if (result != ISC_R_SUCCESS) { goto cleanup_keystorelist; } INSIST(keystore != NULL); dns_keystore_detach(&keystore); } - tmpkeystorelist = server->keystorelist; - server->keystorelist = keystorelist; - keystorelist = tmpkeystorelist; /* * Create the built-in kasp policies ("default", "insecure"). @@ -8924,7 +8934,7 @@ load_configuration(const char *filename, named_server_t *server, kasp = NULL; result = cfg_kasp_fromconfig(kconfig, default_kasp, true, named_g_mctx, named_g_lctx, - &kasplist, &kasp); + &keystorelist, &kasplist, &kasp); if (result != ISC_R_SUCCESS) { goto cleanup_kasplist; } @@ -8953,7 +8963,7 @@ load_configuration(const char *filename, named_server_t *server, kasp = NULL; result = cfg_kasp_fromconfig(kconfig, default_kasp, true, named_g_mctx, named_g_lctx, - &kasplist, &kasp); + &keystorelist, &kasplist, &kasp); if (result != ISC_R_SUCCESS) { goto cleanup_kasplist; } @@ -8961,8 +8971,15 @@ load_configuration(const char *filename, named_server_t *server, dns_kasp_freeze(kasp); dns_kasp_detach(&kasp); } - dns_kasp_detach(&default_kasp); + + /* + * Save keystore list and kasp list. + */ + tmpkeystorelist = server->keystorelist; + server->keystorelist = keystorelist; + keystorelist = tmpkeystorelist; + tmpkasplist = server->kasplist; server->kasplist = kasplist; kasplist = tmpkasplist; diff --git a/lib/dns/include/dns/kasp.h b/lib/dns/include/dns/kasp.h index 288c754c9f..42fe126396 100644 --- a/lib/dns/include/dns/kasp.h +++ b/lib/dns/include/dns/kasp.h @@ -30,6 +30,7 @@ #include #include +#include #include ISC_LANG_BEGINDECLS @@ -51,11 +52,11 @@ struct dns_kasp_key { ISC_LINK(struct dns_kasp_key) link; /* Configuration */ - const char *keystore; - uint32_t lifetime; - uint8_t algorithm; - int length; - uint8_t role; + dns_keystore_t *keystore; + uint32_t lifetime; + uint8_t algorithm; + int length; + uint8_t role; }; struct dns_kasp_nsec3param { @@ -644,7 +645,7 @@ dns_kasp_key_lifetime(dns_kasp_key_t *key); * */ -const char * +dns_keystore_t * dns_kasp_key_keystore(dns_kasp_key_t *key); /*%< * The keystore reference of this key. diff --git a/lib/dns/kasp.c b/lib/dns/kasp.c index de8d757fd4..8658fd629c 100644 --- a/lib/dns/kasp.c +++ b/lib/dns/kasp.c @@ -408,10 +408,7 @@ dns_kasp_key_destroy(dns_kasp_key_t *key) { REQUIRE(key != NULL); if (key->keystore != NULL) { - char *ks; - DE_CONST(key->keystore, ks); - isc_mem_free(key->mctx, ks); - key->keystore = NULL; + dns_keystore_detach(&key->keystore); } isc_mem_putanddetach(&key->mctx, key, sizeof(*key)); } @@ -474,7 +471,7 @@ dns_kasp_key_lifetime(dns_kasp_key_t *key) { return (key->lifetime); } -const char * +dns_keystore_t * dns_kasp_key_keystore(dns_kasp_key_t *key) { REQUIRE(key != NULL); diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index fb042d095e..fb8012cf53 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -1189,39 +1189,6 @@ check_port(const cfg_obj_t *options, isc_log_t *logctx, const char *type, return (ISC_R_SUCCESS); } -static isc_result_t -check_keystore(const cfg_obj_t *obj, isc_log_t *logctx, dns_kasp_t *kasp, - dns_keystorelist_t *kslist) { - isc_result_t result = ISC_R_SUCCESS; - dns_kasp_key_t *kkey; - dns_keystore_t *ks = NULL; - - REQUIRE(kasp != NULL); - - dns_kasp_freeze(kasp); - - for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL; - kkey = ISC_LIST_NEXT(kkey, link)) - { - const char *keystore = dns_kasp_key_keystore(kkey); - if (keystore != NULL && strcmp("key-directory", keystore) != 0) - { - if (dns_keystorelist_find(kslist, keystore, &ks) == - ISC_R_SUCCESS) { - dns_keystore_detach(&ks); - } else { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "key-store '%s' not found", - keystore); - result = ISC_R_FAILURE; - } - } - } - - dns_kasp_thaw(kasp); - return (result); -} - static isc_result_t check_options(const cfg_obj_t *options, const cfg_obj_t *config, bool check_algorithms, isc_log_t *logctx, isc_mem_t *mctx, @@ -1401,12 +1368,14 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, DNS_KEYSTORE_KEYDIRECTORY); if (result == ISC_R_SUCCESS) { result = ISC_R_FAILURE; + continue; } } kopt = cfg_tuple_get(kconfig, "options"); if (cfg_map_get(kopt, "directory", &kobj) == - ISC_R_SUCCESS) { + ISC_R_SUCCESS) + { val = cfg_obj_asstring(kobj); ret = isc_file_isdirectory(val); switch (ret) { @@ -1457,6 +1426,19 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, } } + /* + * Add default key-store "key-directory". + */ + tresult = cfg_keystore_fromconfig(NULL, mctx, logctx, &kslist, &ks); + if (tresult != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + if (ks != NULL) { + dns_keystore_detach(&ks); + } + /* * Check dnssec-policy. */ @@ -1494,13 +1476,8 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, ret = cfg_kasp_fromconfig( kconfig, NULL, check_algorithms, - mctx, logctx, &list, &kasp); - if (ret == ISC_R_SUCCESS) { - /* Check key-stores of keys */ - ret = check_keystore( - obj, logctx, kasp, - &kslist); - } + mctx, logctx, &kslist, &list, + &kasp); if (ret != ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) { result = ret; @@ -2976,6 +2953,23 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig, ISC_LIST_INIT(kslist); do_cleanup = true; + /* + * Build the keystore list. + */ + for (element = cfg_list_first(keystores); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kcfg = cfg_listelt_value(element); + ks = NULL; + (void)cfg_keystore_fromconfig(kcfg, mctx, logctx, &kslist, &ks); + INSIST(ks != NULL); + dns_keystore_detach(&ks); + } + ks = NULL; + (void)cfg_keystore_fromconfig(NULL, mctx, logctx, &kslist, &ks); + INSIST(ks != NULL); + dns_keystore_detach(&ks); + /* * Look for the dnssec-policy by name, which is the dnssec-policy * for the zone in question. @@ -2995,8 +2989,8 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig, continue; } - ret = cfg_kasp_fromconfig(kconfig, NULL, mctx, logctx, - &kasplist, &kasp); + ret = cfg_kasp_fromconfig(kconfig, NULL, false, mctx, logctx, + &kslist, &kasplist, &kasp); if (ret != ISC_R_SUCCESS) { kasp = NULL; } @@ -3011,45 +3005,15 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig, for (dns_kasp_key_t *kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) { - const char *ksname = dns_kasp_key_keystore(kkey); - if (ksname == NULL || strcmp("key-directory", ksname) == 0) { + dns_keystore_t *kks = dns_kasp_key_keystore(kkey); + if (kks == NULL || strcmp(DNS_KEYSTORE_KEYDIRECTORY, + dns_keystore_name(kks)) == 0) + { dir = keydir; keystore = false; } else { - /* Look for the key-store by name */ - for (element = cfg_list_first(keystores); - element != NULL; element = cfg_list_next(element)) - { - cfg_obj_t *kconfig = cfg_listelt_value(element); - const cfg_obj_t *ksobj = NULL; - - if (!cfg_obj_istuple(kconfig)) { - continue; - } - - ksobj = cfg_tuple_get(kconfig, "name"); - if (strcmp(ksname, cfg_obj_asstring(ksobj)) != - 0) { - continue; - } - - /* Found the keystore */ - ksobj = NULL; - if (cfg_map_get(cfg_tuple_get(kconfig, "option" - "s"), - "directory", - &ksobj) == ISC_R_SUCCESS) - { - /* Check this directory in the symtable - */ - dir = cfg_obj_asstring(ksobj); - keystore = true; - } else { - dir = keydir; - keystore = false; - } - break; - } + dir = dns_keystore_directory(kks); + keystore = true; } } dns_kasp_thaw(kasp); @@ -3065,10 +3029,17 @@ check: } if (do_cleanup) { + if (kasp != NULL) { + dns_kasp_detach(&kasp); + } if (ks != NULL) { dns_keystore_detach(&ks); } - if (kasp != NULL) { + for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; + kasp = kasp_next) + { + kasp_next = ISC_LIST_NEXT(kasp, link); + ISC_LIST_UNLINK(kasplist, kasp, link); dns_kasp_detach(&kasp); } for (ks = ISC_LIST_HEAD(kslist); ks != NULL; ks = ks_next) { @@ -3076,12 +3047,6 @@ check: ISC_LIST_UNLINK(kslist, ks, link); dns_keystore_detach(&ks); } - for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; - kasp = kasp_next) { - kasp_next = ISC_LIST_NEXT(kasp, link); - ISC_LIST_UNLINK(kasplist, kasp, link); - dns_kasp_detach(&kasp); - } } return (result); @@ -3366,6 +3331,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, has_dnssecpolicy = false; kasp_inlinesigning = false; } else { + const cfg_obj_t *kasps = NULL; (void)cfg_map_get(config, "dnssec-policy", &kasps); for (element = cfg_list_first(kasps); element != NULL; element = cfg_list_next(element)) diff --git a/lib/isccfg/include/isccfg/kaspconf.h b/lib/isccfg/include/isccfg/kaspconf.h index a005a39165..6f40fab072 100644 --- a/lib/isccfg/include/isccfg/kaspconf.h +++ b/lib/isccfg/include/isccfg/kaspconf.h @@ -26,13 +26,17 @@ ISC_LANG_BEGINDECLS isc_result_t cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, bool check_algorithms, isc_mem_t *mctx, isc_log_t *logctx, - dns_kasplist_t *kasplist, dns_kasp_t **kaspp); + dns_keystorelist_t *keystorelist, dns_kasplist_t *kasplist, + dns_kasp_t **kaspp); /*%< * Create and configure a KASP. If 'default_kasp' is not NULL, the built-in * default configuration is used to set values that are not explicitly set in - * the policy. If a 'kasplist' is provided, a lookup happens and if a KASP - * already exists with the same name, no new KASP is created, and no attach to - * 'kaspp' happens. + * the policy. + * + * If a 'kasplist' is provided, a lookup happens and if a KASP already exists + * with the same name, no new KASP is created, and no attach to 'kaspp' happens. + * + * 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. @@ -59,7 +63,7 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, isc_result_t cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *logctx, dns_keystorelist_t *keystorelist, - dns_keystore_t **kspp); + dns_keystore_t **kspp); /*%< * Create and configure a key store. If a 'keystorelist' is provided, a lookup * happens and if a keystore already exists with the same name, no new one is diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index bb0aa46f0e..9598864ef2 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -113,6 +113,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, isc_log_t *logctx, + dns_keystorelist_t *keystorelist, uint32_t ksk_min_lifetime, uint32_t zsk_min_lifetime) { isc_result_t result; dns_kasp_key_t *key = NULL; @@ -129,8 +130,15 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, key->lifetime = 0; /* unlimited */ key->algorithm = DNS_KEYALG_ECDSA256; key->length = -1; + result = dns_keystorelist_find(keystorelist, + DNS_KEYSTORE_KEYDIRECTORY, + &key->keystore); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } } else { const char *rolestr = NULL; + const char *keydir = NULL; const cfg_obj_t *obj = NULL; isc_consttextregion_t alg; bool error = false; @@ -147,9 +155,26 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, obj = cfg_tuple_get(config, "keystorage"); if (cfg_obj_isstring(obj)) { - key->keystore = isc_mem_strdup(key->mctx, - cfg_obj_asstring(obj)); + keydir = cfg_obj_asstring(obj); } + if (keydir == NULL) { + keydir = DNS_KEYSTORE_KEYDIRECTORY; + } + result = dns_keystorelist_find(keystorelist, keydir, + &key->keystore); + if (result == ISC_R_NOTFOUND) { + cfg_obj_log(obj, logctx, 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, logctx, ISC_LOG_ERROR, + "dnssec-policy: bad keystore %s", keydir); + result = ISC_R_FAILURE; + goto cleanup; + } + INSIST(key->keystore != NULL); key->lifetime = 0; /* unlimited */ obj = cfg_tuple_get(config, "lifetime"); @@ -373,7 +398,8 @@ add_digest(dns_kasp_t *kasp, const cfg_obj_t *digest, isc_log_t *logctx) { isc_result_t cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, bool check_algorithms, isc_mem_t *mctx, isc_log_t *logctx, - dns_kasplist_t *kasplist, dns_kasp_t **kaspp) { + dns_keystorelist_t *keystorelist, dns_kasplist_t *kasplist, + dns_kasp_t **kaspp) { isc_result_t result; const cfg_obj_t *maps[2]; const cfg_obj_t *koptions = NULL; @@ -548,8 +574,13 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, cfg_obj_t *kobj = cfg_listelt_value(element); result = cfg_kaspkey_fromconfig( kobj, kasp, check_algorithms, logctx, - ksk_min_lifetime, zsk_min_lifetime); + keystorelist, ksk_min_lifetime, + zsk_min_lifetime); if (result != ISC_R_SUCCESS) { + cfg_obj_log(kobj, logctx, ISC_LOG_ERROR, + "dnssec-policy: failed to " + "configure keys (%s)", + isc_result_totext(result)); goto cleanup; } } @@ -620,9 +651,12 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, new_key = NULL; result = dns_kasp_key_create(kasp, &new_key); if (result != ISC_R_SUCCESS) { + cfg_obj_log(config, logctx, ISC_LOG_ERROR, + "dnssec-policy: failed to " + "configure keys (%s)", + isc_result_totext(result)); goto cleanup; } - if (dns_kasp_key_ksk(key)) { new_key->role |= DNS_KASP_KEY_ROLE_KSK; } @@ -632,6 +666,16 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, new_key->lifetime = dns_kasp_key_lifetime(key); new_key->algorithm = dns_kasp_key_algorithm(key); new_key->length = dns_kasp_key_size(key); + result = dns_keystorelist_find( + keystorelist, DNS_KEYSTORE_KEYDIRECTORY, + &new_key->keystore); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(config, logctx, ISC_LOG_ERROR, + "dnssec-policy: failed to " + "find keystore (%s)", + isc_result_totext(result)); + goto cleanup; + } dns_kasp_addkey(kasp, new_key); } } @@ -688,13 +732,17 @@ cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, const cfg_obj_t *maps[2]; const cfg_obj_t *koptions = NULL; const char *name = NULL; + const char *keydirectory = DNS_KEYSTORE_KEYDIRECTORY; dns_keystore_t *keystore = NULL; int i = 0; - REQUIRE(config != NULL); REQUIRE(kspp != NULL && *kspp == NULL); - name = cfg_obj_asstring(cfg_tuple_get(config, "name")); + if (config != NULL) { + name = cfg_obj_asstring(cfg_tuple_get(config, "name")); + } else { + name = keydirectory; + } INSIST(name != NULL); result = dns_keystorelist_find(keystorelist, name, &keystore); @@ -728,12 +776,11 @@ cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, if (config != NULL) { koptions = cfg_tuple_get(config, "options"); maps[i++] = koptions; + maps[i] = NULL; + dns_keystore_setdirectory(keystore, + get_string(maps, "directory")); + dns_keystore_setpkcs11uri(keystore, get_string(maps, "uri")); } - maps[i] = NULL; - - /* Configuration */ - dns_keystore_setdirectory(keystore, get_string(maps, "directory")); - dns_keystore_setpkcs11uri(keystore, get_string(maps, "uri")); /* Append it to the list for future lookups. */ ISC_LIST_APPEND(*keystorelist, keystore, link); From d79571054173879dd615a75dd9c39203f41f0c79 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 30 Aug 2023 14:59:44 +0200 Subject: [PATCH 10/37] Add object parameter to dst_key_generate() Add a parameter to store a possible PKCS#11 object that can later be used to identify a key with a PKCS#11 URI string (RFC 7512). --- bin/confgen/keygen.c | 2 +- bin/dnssec/dnssec-keygen.c | 5 +++-- bin/named/server.c | 2 +- lib/dns/dst_api.c | 11 +++++++++-- lib/dns/dst_internal.h | 1 + lib/dns/include/dst/dst.h | 4 ++-- lib/dns/keymgr.c | 4 ++-- 7 files changed, 19 insertions(+), 10 deletions(-) diff --git a/bin/confgen/keygen.c b/bin/confgen/keygen.c index 4698d1a078..348c05d874 100644 --- a/bin/confgen/keygen.c +++ b/bin/confgen/keygen.c @@ -124,7 +124,7 @@ generate_key(isc_mem_t *mctx, dns_secalg_t alg, int keysize, DO("generate key", dst_key_generate(dns_rootname, alg, keysize, 0, 0, DNS_KEYPROTO_ANY, - dns_rdataclass_in, mctx, &key, NULL)); + dns_rdataclass_in, NULL, mctx, &key, NULL)); isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret)); diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index 9d32558ac2..e3542df49f 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -692,14 +692,15 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { fprintf(stderr, "Generating key pair."); ret = dst_key_generate(name, ctx->alg, ctx->size, param, flags, ctx->protocol, - ctx->rdclass, mctx, &key, + ctx->rdclass, NULL, mctx, &key, &progress); putc('\n', stderr); fflush(stderr); } else { ret = dst_key_generate(name, ctx->alg, ctx->size, param, flags, ctx->protocol, - ctx->rdclass, mctx, &key, NULL); + ctx->rdclass, NULL, mctx, &key, + NULL); } if (ret != ISC_R_SUCCESS) { diff --git a/bin/named/server.c b/bin/named/server.c index 1b6037d9b3..d8bda0f3d2 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -7435,7 +7435,7 @@ generate_session_key(const char *filename, const char *keynamestr, /* generate key */ result = dst_key_generate(keyname, alg, bits, 1, 0, DNS_KEYPROTO_ANY, - dns_rdataclass_in, mctx, &key, NULL); + dns_rdataclass_in, NULL, mctx, &key, NULL); if (result != ISC_R_SUCCESS) { return (result); } diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index af53947ac6..ca81410089 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -1027,8 +1027,8 @@ dst_key_fromlabel(const dns_name_t *name, int alg, unsigned int flags, isc_result_t dst_key_generate(const dns_name_t *name, unsigned int alg, unsigned int bits, unsigned int param, unsigned int flags, unsigned int protocol, - dns_rdataclass_t rdclass, isc_mem_t *mctx, dst_key_t **keyp, - void (*callback)(int)) { + dns_rdataclass_t rdclass, const char *object, isc_mem_t *mctx, + dst_key_t **keyp, void (*callback)(int)) { dst_key_t *key; isc_result_t ret; @@ -1042,6 +1042,10 @@ dst_key_generate(const dns_name_t *name, unsigned int alg, unsigned int bits, key = get_key_struct(name, alg, flags, protocol, bits, rdclass, 0, mctx); + if (object != NULL) { + key->object = isc_mem_strdup(mctx, object); + } + if (bits == 0) { /*%< NULL KEY */ key->key_flags |= DNS_KEYTYPE_NOKEY; *keyp = key; @@ -1397,6 +1401,9 @@ dst_key_free(dst_key_t **keyp) { if (key->label != NULL) { isc_mem_free(mctx, key->label); } + if (key->object != NULL) { + isc_mem_free(mctx, key->object); + } dns_name_free(key->key_name, mctx); isc_mem_put(mctx, key->key_name, sizeof(dns_name_t)); if (key->key_tkeytoken) { diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h index ad9e097fd9..a4fcc05bd8 100644 --- a/lib/dns/dst_internal.h +++ b/lib/dns/dst_internal.h @@ -93,6 +93,7 @@ struct dst_key { isc_mem_t *mctx; /*%< memory context */ char *engine; /*%< engine name (HSM) */ char *label; /*%< engine label (HSM) */ + char *object; /*%< engine object (HSM) */ union { void *generic; dns_gss_ctx_id_t gssctx; diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index c0912f3e67..f83a2adc11 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -629,8 +629,8 @@ dst_key_fromlabel(const dns_name_t *name, int alg, unsigned int flags, isc_result_t dst_key_generate(const dns_name_t *name, unsigned int alg, unsigned int bits, unsigned int param, unsigned int flags, unsigned int protocol, - dns_rdataclass_t rdclass, isc_mem_t *mctx, dst_key_t **keyp, - void (*callback)(int)); + dns_rdataclass_t rdclass, const char *object, isc_mem_t *mctx, + dst_key_t **keyp, void (*callback)(int)); /*%< * Generate a DST key (or keypair) with the supplied parameters. The diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 56672a1198..2327f89064 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -459,8 +459,8 @@ keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin, keyflags |= DNS_KEYFLAG_KSK; } RETERR(dst_key_generate(origin, algo, size, 0, keyflags, - DNS_KEYPROTO_DNSSEC, rdclass, mctx, - &newkey, NULL)); + DNS_KEYPROTO_DNSSEC, rdclass, NULL, + mctx, &newkey, NULL)); /* Key collision? */ conflict = keymgr_keyid_conflict(newkey, keylist); From f096472eb4cce7578a23c5ebd6aeb9dd154bf6ac Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 28 Feb 2022 11:50:43 +0100 Subject: [PATCH 11/37] Create private keys with PKCS#11 object If there is a keystore configured with a PKCS#11 URI, zones that are using a dnssec-policy that uses such a keystore should create keys via the PKCS#11 interface. Those keys are generally stored inside an HSM. Some changes to the code are required, to store the engine reference into the keystore. --- bin/dnssec/dnssec-keygen.c | 8 +-- configure.ac | 1 - lib/dns/include/dns/keystore.h | 40 +++++++++++- lib/dns/keymgr.c | 32 ++++++---- lib/dns/keystore.c | 93 ++++++++++++++++++++++++++-- lib/dns/opensslecdsa_link.c | 85 +++++++++++++++++++++++-- lib/dns/opensslrsa_link.c | 75 ++++++++++++++++++++-- lib/isccfg/check.c | 13 ++-- lib/isccfg/include/isccfg/kaspconf.h | 3 +- lib/isccfg/kaspconf.c | 5 +- 10 files changed, 314 insertions(+), 41 deletions(-) diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index e3542df49f..55ecec9e87 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -255,7 +255,7 @@ progress(int p) { static void kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, - dns_kasp_t **kaspp) { + const char *engine, dns_kasp_t **kaspp) { isc_result_t result = ISC_R_NOTFOUND; const cfg_listelt_t *element; const cfg_obj_t *kasps = NULL; @@ -274,8 +274,8 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, { cfg_obj_t *kconfig = cfg_listelt_value(element); ks = NULL; - result = cfg_keystore_fromconfig(kconfig, mctx, lctx, &kslist, - &ks); + result = cfg_keystore_fromconfig(kconfig, mctx, lctx, engine, + &kslist, &ks); if (result != ISC_R_SUCCESS) { fatal("failed to configure key-store '%s': %s", cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), @@ -1305,7 +1305,7 @@ main(int argc, char **argv) { ctx.policy, ctx.configfile); } - kasp_from_conf(config, mctx, ctx.policy, &kasp); + kasp_from_conf(config, mctx, ctx.policy, engine, &kasp); if (kasp == NULL) { fatal("failed to load dnssec-policy '%s'", ctx.policy); diff --git a/configure.ac b/configure.ac index 92f40c17ac..3631772b14 100644 --- a/configure.ac +++ b/configure.ac @@ -644,7 +644,6 @@ AS_IF([test "$enable_doh" = "yes"], AM_CONDITIONAL([HAVE_LIBNGHTTP2], [test -n "$LIBNGHTTP2_LIBS"]) - # # flockfile is usually provided by pthreads # diff --git a/lib/dns/include/dns/keystore.h b/lib/dns/include/dns/keystore.h index a5d5840aa1..bd486e541e 100644 --- a/lib/dns/include/dns/keystore.h +++ b/lib/dns/include/dns/keystore.h @@ -31,6 +31,8 @@ #include +#include + ISC_LANG_BEGINDECLS /* Key store */ @@ -60,7 +62,8 @@ struct dns_keystore { #define DNS_KEYSTORE_KEYDIRECTORY "key-directory" isc_result_t -dns_keystore_create(isc_mem_t *mctx, const char *name, dns_keystore_t **kspp); +dns_keystore_create(isc_mem_t *mctx, const char *name, const char *engine, + dns_keystore_t **kspp); /*%< * Create a key store. * @@ -70,6 +73,8 @@ dns_keystore_create(isc_mem_t *mctx, const char *name, dns_keystore_t **kspp); * *\li 'name' is a valid C string. * + *\li 'engine' is the name of the OpenSSL engine to use, may be NULL. + * *\li kspp != NULL && *kspp == NULL * * Returns: @@ -126,6 +131,20 @@ dns_keystore_name(dns_keystore_t *keystore); *\li name of 'keystore'. */ +const char * +dns_keystore_engine(dns_keystore_t *keystore); +/*%< + * Get keystore engine. + * + * Requires: + * + *\li 'keystore' is a valid keystore. + * + * Returns: + * + *\li engine of 'keystore'. May be NULL. + */ + const char * dns_keystore_directory(dns_keystore_t *keystore); /*%< @@ -176,6 +195,25 @@ dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri); * */ +isc_result_t +dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, + dns_rdataclass_t rdclass, isc_mem_t *mctx, uint32_t alg, + int size, int flags, dst_key_t **dstkey); +/*%< + * Create a DNSSEC key pair. Set keystore PKCS#11 URI. + * + * Requires: + * + *\li 'keystore' is a valid keystore. + * + *\li 'origin' is a valid DNS owner name. + * + *\li 'mctx' is a valid memory context. + * + *\li 'dstkey' is not NULL and '*dstkey' is NULL. + * + */ + isc_result_t dns_keystorelist_find(dns_keystorelist_t *list, const char *name, dns_keystore_t **kspp); diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 2327f89064..027965fef4 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -446,21 +447,28 @@ keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin, dns_rdataclass_t rdclass, isc_mem_t *mctx, dns_dnsseckeylist_t *keylist, dns_dnsseckeylist_t *newkeys, dst_key_t **dst_key) { - bool conflict = false; - int keyflags = DNS_KEYOWNER_ZONE; isc_result_t result = ISC_R_SUCCESS; + bool conflict = false; + int flags = DNS_KEYOWNER_ZONE; dst_key_t *newkey = NULL; + uint32_t alg = dns_kasp_key_algorithm(kkey); + dns_keystore_t *keystore = dns_kasp_key_keystore(kkey); + int size = dns_kasp_key_size(kkey); + + if (dns_kasp_key_ksk(kkey)) { + flags |= DNS_KEYFLAG_KSK; + } do { - uint32_t algo = dns_kasp_key_algorithm(kkey); - int size = dns_kasp_key_size(kkey); - - if (dns_kasp_key_ksk(kkey)) { - keyflags |= DNS_KEYFLAG_KSK; + if (keystore == NULL) { + RETERR(dst_key_generate(origin, alg, size, 0, flags, + DNS_KEYPROTO_DNSSEC, rdclass, + NULL, mctx, &newkey, NULL)); + } else { + RETERR(dns_keystore_keygen(keystore, origin, rdclass, + mctx, alg, size, flags, + &newkey)); } - RETERR(dst_key_generate(origin, algo, size, 0, keyflags, - DNS_KEYPROTO_DNSSEC, rdclass, NULL, - mctx, &newkey, NULL)); /* Key collision? */ conflict = keymgr_keyid_conflict(newkey, keylist); @@ -1989,8 +1997,10 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); char keystr[DST_KEY_FORMATSIZE]; - REQUIRE(DNS_KASP_VALID(kasp)); + REQUIRE(dns_name_isvalid(origin)); + REQUIRE(mctx != NULL); REQUIRE(keyring != NULL); + REQUIRE(DNS_KASP_VALID(kasp)); ISC_LIST_INIT(newkeys); diff --git a/lib/dns/keystore.c b/lib/dns/keystore.c index e7fac0ab62..c57c712a7a 100644 --- a/lib/dns/keystore.c +++ b/lib/dns/keystore.c @@ -13,11 +13,6 @@ /*! \file */ -#ifdef HAVE_GNUTLS -#include -#include -#endif - #include #include @@ -26,15 +21,18 @@ #include #include +#include isc_result_t -dns_keystore_create(isc_mem_t *mctx, const char *name, dns_keystore_t **kspp) { +dns_keystore_create(isc_mem_t *mctx, const char *name, const char *engine, + dns_keystore_t **kspp) { dns_keystore_t *keystore; REQUIRE(name != NULL); REQUIRE(kspp != NULL && *kspp == NULL); keystore = isc_mem_get(mctx, sizeof(*keystore)); + keystore->engine = engine; keystore->mctx = NULL; isc_mem_attach(mctx, &keystore->mctx); @@ -65,6 +63,8 @@ dns_keystore_attach(dns_keystore_t *source, dns_keystore_t **targetp) { static inline void destroy(dns_keystore_t *keystore) { + char *name; + REQUIRE(!ISC_LINK_LINKED(keystore, link)); isc_mutex_destroy(&keystore->lock); @@ -98,6 +98,13 @@ dns_keystore_name(dns_keystore_t *keystore) { return (keystore->name); } +const char * +dns_keystore_engine(dns_keystore_t *keystore) { + REQUIRE(DNS_KEYSTORE_VALID(keystore)); + + return (keystore->engine); +} + const char * dns_keystore_directory(dns_keystore_t *keystore) { REQUIRE(DNS_KEYSTORE_VALID(keystore)); @@ -136,6 +143,80 @@ dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri) { : isc_mem_strdup(keystore->mctx, uri); } +isc_result_t +dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, + dns_rdataclass_t rdclass, isc_mem_t *mctx, uint32_t alg, + int size, int flags, dst_key_t **dstkey) { + isc_result_t result; + dst_key_t *newkey = NULL; + const char *uri = NULL; + + REQUIRE(DNS_KEYSTORE_VALID(keystore)); + REQUIRE(dns_name_isvalid(origin)); + REQUIRE(mctx != NULL); + REQUIRE(dstkey != NULL && *dstkey == NULL); + + uri = dns_keystore_pkcs11uri(keystore); + if (uri != NULL) { + dst_key_t *key = NULL; + char *label = NULL; + size_t len; + char timebuf[18]; + isc_time_t now = isc_time_now(); + bool ksk = ((flags & DNS_KEYFLAG_KSK) != 0); + char namebuf[DNS_NAME_FORMATSIZE]; + char object[DNS_NAME_FORMATSIZE + 26]; + + /* Generate the key */ + isc_time_formatshorttimestamp(&now, timebuf, sizeof(timebuf)); + dns_name_format(origin, namebuf, sizeof(namebuf)); + snprintf(object, sizeof(object), "%s-%s-%s", namebuf, + ksk ? "ksk" : "zsk", timebuf); + + result = dst_key_generate(origin, alg, size, 0, flags, + DNS_KEYPROTO_DNSSEC, rdclass, object, + mctx, &key, NULL); + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, + "keystore: failed to generate key " + "%s (ret=%d)", + object, result); + return (result); + } + dst_key_free(&key); + + /* Retrieve generated key from label */ + len = strlen(object) + strlen(uri) + 10; + label = isc_mem_get(mctx, len); + sprintf(label, "%s;object=%s;", uri, object); + result = dst_key_fromlabel( + origin, alg, flags, DNS_KEYPROTO_DNSSEC, + dns_rdataclass_in, dns_keystore_engine(keystore), label, + NULL, mctx, &newkey); + + isc_mem_put(mctx, label, len); + + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, + "keystore: failed to access key " + "%s (ret=%d)", + object, result); + return (result); + } + } else { + result = dst_key_generate(origin, alg, size, 0, flags, + DNS_KEYPROTO_DNSSEC, rdclass, NULL, + mctx, &newkey, NULL); + } + + if (result == ISC_R_SUCCESS) { + *dstkey = newkey; + } + return (result); +} + isc_result_t dns_keystorelist_find(dns_keystorelist_t *list, const char *name, dns_keystore_t **kspp) { diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c index b54e43ade3..4b8740b8d7 100644 --- a/lib/dns/opensslecdsa_link.c +++ b/lib/dns/opensslecdsa_link.c @@ -410,13 +410,85 @@ opensslecdsa_create_pkey(unsigned int key_alg, bool private, #if OPENSSL_VERSION_NUMBER >= 0x30000000L static isc_result_t -opensslecdsa_generate_pkey(unsigned int key_alg, EVP_PKEY **retkey) { +opensslecdsa_generate_pkey_with_object(int group_nid, const char *object, + EVP_PKEY **retkey) { + int status; + isc_result_t ret; + unsigned char id[16]; + char *label = UNCONST(object); + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM params[3]; + + /* Generate the key's parameters. */ + status = RAND_bytes(id, 16); + if (status != 1) { + DST_RET(dst__openssl_toresult2("RAND_bytes", + DST_R_OPENSSLFAILURE)); + } + + params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_key_label", label, + 0); + params[1] = OSSL_PARAM_construct_octet_string("pkcs11_key_id", id, 16); + params[2] = OSSL_PARAM_construct_end(); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "provider=pkcs11"); + if (ctx == NULL) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name", + DST_R_OPENSSLFAILURE)); + } + + status = EVP_PKEY_keygen_init(ctx); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init", + DST_R_OPENSSLFAILURE)); + } + + status = EVP_PKEY_CTX_set_params(ctx, params); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_set_params", + DST_R_OPENSSLFAILURE)); + } + /* + * Setting the P-384 curve doesn't work correctly when using: + * OSSL_PARAM_construct_utf8_string("ec_paramgen_curve", "P-384", 0); + * + * Instead use the OpenSSL function to set the curve nid param. + */ + status = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, group_nid); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_set_ec_paramgen_" + "curve_nid", + DST_R_OPENSSLFAILURE)); + } + + /* Generate the key. */ + status = EVP_PKEY_generate(ctx, retkey); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_generate", + DST_R_OPENSSLFAILURE)); + } + + ret = ISC_R_SUCCESS; + +err: + EVP_PKEY_CTX_free(ctx); + return (ret); +} + +static isc_result_t +opensslecdsa_generate_pkey(unsigned int key_alg, const char *object, + EVP_PKEY **retkey) { isc_result_t ret; EVP_PKEY_CTX *ctx = NULL; EVP_PKEY *params_pkey = NULL; int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); int status; + if (object != NULL) { + return (opensslecdsa_generate_pkey_with_object(group_nid, + object, retkey)); + } + /* Generate the key's parameters. */ ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); if (ctx == NULL) { @@ -498,11 +570,16 @@ opensslecdsa_extract_private_key(const dst_key_t *key, unsigned char *buf, #else static isc_result_t -opensslecdsa_generate_pkey(unsigned int key_alg, EVP_PKEY **retkey) { +opensslecdsa_generate_pkey(unsigned int key_alg, const char *object, + EVP_PKEY **retkey) { isc_result_t ret; EC_KEY *eckey = NULL; EVP_PKEY *pkey = NULL; - int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); + int group_nid; + + UNUSED(object); + + group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); eckey = EC_KEY_new_by_curve_name(group_nid); if (eckey == NULL) { @@ -815,7 +892,7 @@ opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { UNUSED(unused); UNUSED(callback); - ret = opensslecdsa_generate_pkey(key->key_alg, &pkey); + ret = opensslecdsa_generate_pkey(key->key_alg, key->object, &pkey); if (ret != ISC_R_SUCCESS) { return (ret); } diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index 4c264c183a..6d06c71f27 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -366,13 +366,17 @@ progress_cb(int p, int n, BN_GENCB *cb) { } static isc_result_t -opensslrsa_generate_pkey(unsigned int key_size, BIGNUM *e, +opensslrsa_generate_pkey(unsigned int key_size, const char *object, BIGNUM *e, void (*callback)(int), EVP_PKEY **retkey) { - RSA *rsa = RSA_new(); - EVP_PKEY *pkey = EVP_PKEY_new(); + RSA *rsa = NULL; + EVP_PKEY *pkey = NULL; BN_GENCB *cb = NULL; isc_result_t ret; + UNUSED(object); + + rsa = RSA_new(); + pkey = EVP_PKEY_new(); if (rsa == NULL || pkey == NULL) { DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); } @@ -493,11 +497,69 @@ progress_cb(EVP_PKEY_CTX *ctx) { } static isc_result_t -opensslrsa_generate_pkey(unsigned int key_size, BIGNUM *e, +opensslrsa_generate_pkey_with_object(size_t key_size, const char *object, + EVP_PKEY **retkey) { + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM params[4]; + unsigned char id[16]; + char *label = UNCONST(object); + isc_result_t ret; + int status; + + status = RAND_bytes(id, 16); + if (status != 1) { + DST_RET(dst__openssl_toresult2("RAND_bytes", + DST_R_OPENSSLFAILURE)); + } + + params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_key_label", label, + 0); + params[1] = OSSL_PARAM_construct_octet_string("pkcs11_key_id", id, 16); + params[2] = OSSL_PARAM_construct_size_t("rsa_keygen_bits", &key_size); + params[3] = OSSL_PARAM_construct_end(); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", "provider=pkcs11"); + if (ctx == NULL) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name", + DST_R_OPENSSLFAILURE)); + } + + status = EVP_PKEY_keygen_init(ctx); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init", + DST_R_OPENSSLFAILURE)); + } + + status = EVP_PKEY_CTX_set_params(ctx, params); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_set_params", + DST_R_OPENSSLFAILURE)); + } + + status = EVP_PKEY_generate(ctx, retkey); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_generate", + DST_R_OPENSSLFAILURE)); + } + + ret = ISC_R_SUCCESS; +err: + EVP_PKEY_CTX_free(ctx); + return (ret); +} + +static isc_result_t +opensslrsa_generate_pkey(unsigned int key_size, const char *object, BIGNUM *e, void (*callback)(int), EVP_PKEY **retkey) { - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + EVP_PKEY_CTX *ctx; isc_result_t ret; + if (object != NULL) { + return (opensslrsa_generate_pkey_with_object(key_size, object, + retkey)); + } + + ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); if (ctx == NULL) { DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); } @@ -669,7 +731,8 @@ opensslrsa_generate(dst_key_t *key, int exp, void (*callback)(int)) { BN_set_bit(e, 32); } - ret = opensslrsa_generate_pkey(key->key_size, e, callback, &pkey); + ret = opensslrsa_generate_pkey(key->key_size, key->object, e, callback, + &pkey); if (ret != ISC_R_SUCCESS) { goto err; } diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index fb8012cf53..c00c588845 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -1412,8 +1412,9 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, } } - ret = cfg_keystore_fromconfig( - kconfig, mctx, logctx, &kslist, &ks); + ret = cfg_keystore_fromconfig(kconfig, mctx, + logctx, NULL, + &kslist, &ks); if (ret != ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) { result = ret; @@ -1429,7 +1430,8 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, /* * Add default key-store "key-directory". */ - tresult = cfg_keystore_fromconfig(NULL, mctx, logctx, &kslist, &ks); + tresult = cfg_keystore_fromconfig(NULL, mctx, logctx, NULL, &kslist, + &ks); if (tresult != ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) { result = tresult; @@ -2961,12 +2963,13 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig, { cfg_obj_t *kcfg = cfg_listelt_value(element); ks = NULL; - (void)cfg_keystore_fromconfig(kcfg, mctx, logctx, &kslist, &ks); + (void)cfg_keystore_fromconfig(kcfg, mctx, logctx, NULL, &kslist, + &ks); INSIST(ks != NULL); dns_keystore_detach(&ks); } ks = NULL; - (void)cfg_keystore_fromconfig(NULL, mctx, logctx, &kslist, &ks); + (void)cfg_keystore_fromconfig(NULL, mctx, logctx, NULL, &kslist, &ks); INSIST(ks != NULL); dns_keystore_detach(&ks); diff --git a/lib/isccfg/include/isccfg/kaspconf.h b/lib/isccfg/include/isccfg/kaspconf.h index 6f40fab072..1bc727d89c 100644 --- a/lib/isccfg/include/isccfg/kaspconf.h +++ b/lib/isccfg/include/isccfg/kaspconf.h @@ -62,7 +62,8 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, isc_result_t cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, - isc_log_t *logctx, dns_keystorelist_t *keystorelist, + isc_log_t *logctx, const char *engine, + dns_keystorelist_t *keystorelist, dns_keystore_t **kspp); /*%< * Create and configure a key store. If a 'keystorelist' is provided, a lookup diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 9598864ef2..9bd4ebb3e9 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -726,7 +726,8 @@ cleanup: isc_result_t cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, - isc_log_t *logctx, dns_keystorelist_t *keystorelist, + isc_log_t *logctx, const char *engine, + dns_keystorelist_t *keystorelist, dns_keystore_t **kspp) { isc_result_t result; const cfg_obj_t *maps[2]; @@ -764,7 +765,7 @@ cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, * No key-store with configured name was found in list, create new one. */ INSIST(keystore == NULL); - result = dns_keystore_create(mctx, name, &keystore); + result = dns_keystore_create(mctx, name, engine, &keystore); if (result != ISC_R_SUCCESS) { return (result); } From 91f18c98b385f166c54175df5702c371b59c1d88 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 28 Feb 2022 13:51:47 +0100 Subject: [PATCH 12/37] Add tests for key-store with engine_pkcs11 Add cases for each algorithm to test the interaction between dnssec-policy and engine_pkcs11. Ensure that named creates keys on startup. Also test dnssec-keygen when using a dnssec-policy with a PKCS#11 based key-store. --- bin/tests/system/enginepkcs11/clean.sh | 6 +- .../system/enginepkcs11/ns1/named.conf.in | 14 ++++ bin/tests/system/enginepkcs11/setup.sh | 28 ++++++-- bin/tests/system/enginepkcs11/tests.sh | 70 ++++++++++++++++++- 4 files changed, 108 insertions(+), 10 deletions(-) diff --git a/bin/tests/system/enginepkcs11/clean.sh b/bin/tests/system/enginepkcs11/clean.sh index 087b434792..7233784a02 100644 --- a/bin/tests/system/enginepkcs11/clean.sh +++ b/bin/tests/system/enginepkcs11/clean.sh @@ -18,18 +18,20 @@ set -e rm -f dig.out.* rm -f dsset-* -rm -f pin rm -f keyfromlabel.err.* keyfromlabel.out.* rm -f pkcs11-tool.err.* pkcs11-tool.out.* rm -f signer.out.* rm -f ns1/*.example.db ns1/*.example.db.signed +rm -f ns1/*.kasp.db ns1/*.kasp.db.signed rm -f ns1/*.kskid1 ns1/*.kskid2 ns1/*.zskid1 ns1/*.zskid2 rm -f ns1/dig.out.* rm -f ns1/K* +rm -f ns1/keygen.out.* rm -f ns1/named.conf ns1/named.args ns1/named.run ns1/named.memstats +rm -f ns1/pin rm -f ns1/update.cmd.* rm -f ns1/update.log.* rm -f ns1/verify.out.* -rm -f ns1/zone.*.signed.jnl ns1/zone.*.signed.jbk +rm -f ns1/zone.*.jnl ns1/zone.*.jbk OPENSSL_CONF= softhsm2-util --delete-token --token "softhsm2-enginepkcs11" >/dev/null 2>&1 || echo_i "softhsm2-enginepkcs11 token not found for cleaning" diff --git a/bin/tests/system/enginepkcs11/ns1/named.conf.in b/bin/tests/system/enginepkcs11/ns1/named.conf.in index 985974db81..2e04e45b26 100644 --- a/bin/tests/system/enginepkcs11/ns1/named.conf.in +++ b/bin/tests/system/enginepkcs11/ns1/named.conf.in @@ -34,3 +34,17 @@ key rndc_key { controls { inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; }; + +key-store "hsm" { + directory "."; + uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; +}; + +key-store "pin" { + directory "."; + uri "pkcs11:token=softhsm2-enginepkcs11;pin-source=pin"; +}; + +key-store "disk" { + directory "keys"; +}; diff --git a/bin/tests/system/enginepkcs11/setup.sh b/bin/tests/system/enginepkcs11/setup.sh index 3cb216bdbf..59db124664 100644 --- a/bin/tests/system/enginepkcs11/setup.sh +++ b/bin/tests/system/enginepkcs11/setup.sh @@ -18,8 +18,8 @@ set -e OPENSSL_CONF= softhsm2-util --init-token --free --pin 1234 --so-pin 1234 --label "softhsm2-enginepkcs11" | awk '/^The token has been initialized and is reassigned to slot/ { print $NF }' -printf '%s' "${HSMPIN:-1234}" >pin parse_openssl_config +printf '%s' "${HSMPIN:-1234}" >ns1/pin PWD=$(pwd) copy_setports ns1/named.conf.in ns1/named.conf @@ -33,7 +33,7 @@ keygen() { label="${id}-${zone}" p11id=$(echo "${label}" | openssl sha1 -r | awk '{print $1}') - OPENSSL_CONF= pkcs11-tool --module $SOFTHSM2_MODULE --token-label "softhsm2-enginepkcs11" -l -k --key-type $type:$bits --label "${label}" --id "${p11id}" --pin $(cat $PWD/pin) >pkcs11-tool.out.$zone.$id 2>pkcs11-tool.err.$zone.$id || return 1 + OPENSSL_CONF= pkcs11-tool --module $SOFTHSM2_MODULE --token-label "softhsm2-enginepkcs11" -l -k --key-type $type:$bits --label "${label}" --id "${p11id}" --pin $(cat $PWD/ns1/pin) >pkcs11-tool.out.$zone.$id 2>pkcs11-tool.err.$zone.$id || return 1 } keyfromlabel() { @@ -43,7 +43,7 @@ keyfromlabel() { dir="$4" shift 4 - $KEYFRLAB $ENGINE_ARG -K $dir -a $alg -l "pkcs11:token=softhsm2-enginepkcs11;object=${id}-${zone};pin-source=$PWD/pin" "$@" $zone >>keyfromlabel.out.$zone.$id 2>keyfromlabel.err.$zone.$id || return 1 + $KEYFRLAB $ENGINE_ARG -K $dir -a $alg -l "pkcs11:token=softhsm2-enginepkcs11;object=${id}-${zone};pin-source=$PWD/ns1/pin" "$@" $zone >>keyfromlabel.out.$zone.$id 2>keyfromlabel.err.$zone.$id || return 1 cat keyfromlabel.out.$zone.$id } @@ -57,9 +57,10 @@ for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \ type=$(echo "$algtypebits" | cut -f 2 -d :) bits=$(echo "$algtypebits" | cut -f 3 -d :) + tld="example" if $SHELL ../testcrypto.sh $alg; then - zone="$alg.example" - zonefile="zone.$alg.example.db" + zone="$alg.$tld" + zonefile="zone.$alg.$tld.db" ret=0 echo_i "Generate keys $alg $type:$bits for zone $zone" @@ -111,6 +112,9 @@ for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \ cp "${ksk2}.key" "${ksk2}.ksk2" ) + echo_i "Add zone $alg.kasp to named.conf" + cp $infile ${dir}/zone.${alg}.kasp.db + echo_i "Add zone $zone to named.conf" cat >>"${dir}/named.conf" <dig.out.dnskey.$zone.$n || return 1 + count=$(awk 'BEGIN { count = 0 } $4 == "DNSKEY" { count++ } END {print count}' dig.out.dnskey.$zone.$n) + test $count -eq 2 + } + retry_quiet 2 _dig_policy_dnskey || ret=1 + test "$ret" -eq 0 || echo_i "failed (expected 2 DNSKEY records)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test SOA response for $zone ($n)" + _dig_policy_soa() { + dig_with_opts "$zone" @10.53.0.1 SOA >dig.out.soa.$zone.$n || return 1 + awk '$4 == "RRSIG" && $5 == "SOA" { print $11 }' dig.out.soa.$zone.$n >dig.out.keyids.$zone.$n || return 1 + numsigs=$(cat dig.out.keyids.$zone.$n | wc -l) + test $numsigs -eq 1 || return 1 + return 0 + } + retry_quiet 2 _dig_policy_soa || ret=1 + test "$ret" -eq 0 || echo_i "failed (expected a SOA RRSIG record)" + status=$((status + ret)) + + # Check dnssec-keygen with dnssec-policy and key-store. + zone="${alg}.keygen" + + n=$((n + 1)) + ret=0 + echo_i "Test dnssec-keygen for $zone ($n)" + $KEYGEN $ENGINE_ARG -k $alg -l named.conf $zone >keygen.out.$zone.$n 2>/dev/null || ret=1 + check_keys $zone 2 || ret=1 + status=$((status + ret)) + done # Go back to main test dir. From 9081426313c9a3e660d83fef90239551c63d36f9 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 9 Mar 2022 10:55:48 +0100 Subject: [PATCH 13/37] Refactor findmatchingkeys and keylistfromrdataset Refactor dns_dnssec_findmatchingkeys and dns_dnssec_keylistfromrdataset to take into account the key store directories in case the zone is using dnssec-policy (kasp). Add 'kasp' and 'keystores' parameters. This requires the keystorelist to be stored inside the zone structure. The calls to these functions in the DNSSEC tools can use NULL as the kasp value, as dnssec-signzone does not (yet) support dnssec-policy, and key collision is checked inside the directory where it is created. --- bin/dnssec/dnssec-signzone.c | 6 +- bin/dnssec/dnssectool.c | 3 +- bin/named/include/named/zoneconf.h | 4 +- bin/named/server.c | 51 +++++---- bin/named/zoneconf.c | 6 +- lib/dns/dnssec.c | 177 +++++++++++++++++++++-------- lib/dns/include/dns/dnssec.h | 28 +++-- lib/dns/include/dns/zone.h | 25 +++- lib/dns/update.c | 13 ++- lib/dns/zone.c | 56 +++++++-- 10 files changed, 266 insertions(+), 103 deletions(-) diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index 83e3b9ee6a..29ae7d578c 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -2639,7 +2639,7 @@ loadzonekeys(bool preserve_keys, bool load_public) { /* Load keys corresponding to the existing DNSKEY RRset. */ result = dns_dnssec_keylistfromrdataset( - gorigin, directory, mctx, &rdataset, &keysigs, &soasigs, + gorigin, NULL, directory, mctx, &rdataset, &keysigs, &soasigs, preserve_keys, load_public, &keylist); if (result != ISC_R_SUCCESS) { fatal("failed to load the zone keys: %s", @@ -2830,8 +2830,8 @@ findkeys: /* * Find keys that match this zone in the key repository. */ - result = dns_dnssec_findmatchingkeys(gorigin, directory, now, mctx, - &matchkeys); + result = dns_dnssec_findmatchingkeys(gorigin, NULL, directory, NULL, + now, mctx, &matchkeys); if (result == ISC_R_NOTFOUND) { result = ISC_R_SUCCESS; } diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c index 92980a8ca9..59fc80b3f6 100644 --- a/bin/dnssec/dnssectool.c +++ b/bin/dnssec/dnssectool.c @@ -498,7 +498,8 @@ key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir, alg = dst_key_alg(dstkey); ISC_LIST_INIT(matchkeys); - result = dns_dnssec_findmatchingkeys(name, dir, now, mctx, &matchkeys); + result = dns_dnssec_findmatchingkeys(name, NULL, dir, NULL, now, mctx, + &matchkeys); if (result == ISC_R_NOTFOUND) { return (false); } diff --git a/bin/named/include/named/zoneconf.h b/bin/named/include/named/zoneconf.h index dbecd4a79e..1eb059b25a 100644 --- a/bin/named/include/named/zoneconf.h +++ b/bin/named/include/named/zoneconf.h @@ -28,8 +28,8 @@ ISC_LANG_BEGINDECLS isc_result_t named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, - dns_kasplist_t *kasplist, dns_zone_t *zone, - dns_zone_t *raw); + dns_kasplist_t *kasplist, dns_keystorelist_t *keystores, + dns_zone_t *zone, dns_zone_t *raw); /*%< * Configure or reconfigure a zone according to the named.conf * data. diff --git a/bin/named/server.c b/bin/named/server.c index d8bda0f3d2..c371fb9c08 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -442,8 +442,8 @@ static isc_result_t configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, dns_view_t *view, dns_viewlist_t *viewlist, dns_kasplist_t *kasplist, - cfg_aclconfctx_t *aclconf, bool added, bool old_rpz_ok, - bool modify); + dns_keystorelist_t *keystores, cfg_aclconfctx_t *aclconf, + bool added, bool old_rpz_ok, bool modify); static void configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig, @@ -2788,13 +2788,13 @@ catz_addmodzone_cb(void *arg) { zoneobj = cfg_listelt_value(cfg_list_first(zlist)); /* Mark view unfrozen so that zone can be added */ - isc_loopmgr_pause(named_g_loopmgr); dns_view_thaw(cz->view); result = configure_zone(cfg->config, zoneobj, cfg->vconfig, cz->view, &cz->cbd->server->viewlist, - &cz->cbd->server->kasplist, cfg->actx, true, - false, cz->mod); + &cz->cbd->server->kasplist, + &cz->cbd->server->keystorelist, + cfg->actx, true, false, cz->mod); dns_view_freeze(cz->view); isc_loopmgr_resume(named_g_loopmgr); @@ -3976,8 +3976,9 @@ static const char *const response_synonyms[] = { "response", NULL }; static isc_result_t configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, cfg_obj_t *vconfig, named_cachelist_t *cachelist, - dns_kasplist_t *kasplist, const cfg_obj_t *bindkeys, - isc_mem_t *mctx, cfg_aclconfctx_t *actx, bool need_hints) { + dns_kasplist_t *kasplist, dns_keystorelist_t *keystores, + const cfg_obj_t *bindkeys, isc_mem_t *mctx, + cfg_aclconfctx_t *actx, bool need_hints) { const cfg_obj_t *maps[4]; const cfg_obj_t *cfgmaps[3]; const cfg_obj_t *optionmaps[3]; @@ -4122,7 +4123,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, { const cfg_obj_t *zconfig = cfg_listelt_value(element); CHECK(configure_zone(config, zconfig, vconfig, view, viewlist, - kasplist, actx, false, old_rpz_ok, false)); + kasplist, keystores, actx, false, + old_rpz_ok, false)); zone_element_latest = element; } @@ -6430,8 +6432,8 @@ static isc_result_t configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, dns_view_t *view, dns_viewlist_t *viewlist, dns_kasplist_t *kasplist, - cfg_aclconfctx_t *aclconf, bool added, bool old_rpz_ok, - bool modify) { + dns_keystorelist_t *keystores, cfg_aclconfctx_t *aclconf, + bool added, bool old_rpz_ok, bool modify) { dns_view_t *pview = NULL; /* Production view */ dns_zone_t *zone = NULL; /* New or reused zone */ dns_zone_t *raw = NULL; /* New or reused raw zone */ @@ -6625,7 +6627,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, dns_zone_setstats(zone, named_g_server->zonestats); } CHECK(named_zone_configure(config, vconfig, zconfig, aclconf, - kasplist, zone, NULL)); + kasplist, keystores, zone, NULL)); dns_zone_attach(zone, &view->redirect); goto cleanup; } @@ -6801,7 +6803,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, * Configure the zone. */ CHECK(named_zone_configure(config, vconfig, zconfig, aclconf, kasplist, - zone, raw)); + keystores, zone, raw)); /* * Add the zone to its view in the new view list. @@ -7801,7 +7803,8 @@ configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, const cfg_obj_t *zconfig = cfg_listelt_value(element); CHECK(configure_zone(config, zconfig, vconfig, view, &named_g_server->viewlist, - &named_g_server->kasplist, actx, true, + &named_g_server->kasplist, + &named_g_server->keystorelist, actx, true, false, false)); } @@ -7986,7 +7989,8 @@ configure_newzone(const cfg_obj_t *zconfig, cfg_obj_t *config, cfg_aclconfctx_t *actx) { return (configure_zone( config, zconfig, vconfig, view, &named_g_server->viewlist, - &named_g_server->kasplist, actx, true, false, false)); + &named_g_server->kasplist, &named_g_server->keystorelist, actx, + true, false, false)); } /*% @@ -9083,7 +9087,8 @@ load_configuration(const char *filename, named_server_t *server, } result = configure_view(view, &viewlist, config, vconfig, - &cachelist, &server->kasplist, bindkeys, + &cachelist, &server->kasplist, + &server->keystorelist, bindkeys, named_g_mctx, named_g_aclconfctx, true); if (result != ISC_R_SUCCESS) { dns_view_detach(&view); @@ -9104,7 +9109,8 @@ load_configuration(const char *filename, named_server_t *server, goto cleanup_cachelist; } result = configure_view(view, &viewlist, config, NULL, - &cachelist, &server->kasplist, bindkeys, + &cachelist, &server->kasplist, + &server->keystorelist, bindkeys, named_g_mctx, named_g_aclconfctx, true); if (result != ISC_R_SUCCESS) { dns_view_detach(&view); @@ -9132,7 +9138,8 @@ load_configuration(const char *filename, named_server_t *server, } result = configure_view(view, &viewlist, config, vconfig, - &cachelist, &server->kasplist, bindkeys, + &cachelist, &server->kasplist, + &server->keystorelist, bindkeys, named_g_mctx, named_g_aclconfctx, false); if (result != ISC_R_SUCCESS) { @@ -13417,8 +13424,9 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, /* Mark view unfrozen and configure zone */ dns_view_thaw(view); result = configure_zone(cfg->config, zoneobj, cfg->vconfig, view, - &server->viewlist, &server->kasplist, cfg->actx, - true, false, false); + &server->viewlist, &server->kasplist, + &server->keystorelist, cfg->actx, true, false, + false); dns_view_freeze(view); isc_loopmgr_resume(named_g_loopmgr); @@ -13602,8 +13610,9 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, /* Reconfigure the zone */ dns_view_thaw(view); result = configure_zone(cfg->config, zoneobj, cfg->vconfig, view, - &server->viewlist, &server->kasplist, cfg->actx, - true, false, true); + &server->viewlist, &server->kasplist, + &server->keystorelist, cfg->actx, true, false, + true); dns_view_freeze(view); isc_loopmgr_resume(named_g_loopmgr); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 06982f9aeb..1359507a04 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -866,8 +866,8 @@ process_notifytype(dns_notifytype_t ntype, dns_zonetype_t ztype, isc_result_t named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, - dns_kasplist_t *kasplist, dns_zone_t *zone, - dns_zone_t *raw) { + dns_kasplist_t *kasplist, dns_keystorelist_t *keystorelist, + dns_zone_t *zone, dns_zone_t *raw) { isc_result_t result; const char *zname; dns_rdataclass_t zclass; @@ -1576,6 +1576,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, filename = cfg_obj_asstring(obj); CHECK(dns_zone_setkeydirectory(zone, filename)); } + /* Also save a reference to the keystore list. */ + dns_zone_setkeystores(zone, keystorelist); obj = NULL; result = named_config_get(maps, "sig-signing-signatures", &obj); diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 6b45dfc117..90234daa27 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -1396,32 +1396,18 @@ dns_dnssec_get_hints(dns_dnsseckey_t *key, isc_stdtime_t now) { } } -/*% - * Get a list of DNSSEC keys from the key repository. - */ -isc_result_t -dns_dnssec_findmatchingkeys(const dns_name_t *origin, const char *directory, - isc_stdtime_t now, isc_mem_t *mctx, - dns_dnsseckeylist_t *keylist) { +static isc_result_t +findmatchingkeys(const char *directory, char *namebuf, unsigned int len, + isc_mem_t *mctx, isc_stdtime_t now, + dns_dnsseckeylist_t *list) { isc_result_t result = ISC_R_SUCCESS; - bool dir_open = false; - dns_dnsseckeylist_t list; isc_dir_t dir; + bool dir_open = false; + unsigned int i, alg; dns_dnsseckey_t *key = NULL; dst_key_t *dstkey = NULL; - char namebuf[DNS_NAME_FORMATSIZE]; - isc_buffer_t b; - unsigned int len, i, alg; - REQUIRE(keylist != NULL); - ISC_LIST_INIT(list); isc_dir_init(&dir); - - isc_buffer_init(&b, namebuf, sizeof(namebuf) - 1); - RETERR(dns_name_tofilenametext(origin, false, &b)); - len = isc_buffer_usedlength(&b); - namebuf[len] = '\0'; - if (directory == NULL) { directory = "."; } @@ -1508,11 +1494,77 @@ dns_dnssec_findmatchingkeys(const dns_name_t *origin, const char *directory, if (key->legacy) { dns_dnsseckey_destroy(mctx, &key); } else { - ISC_LIST_APPEND(list, key, link); + ISC_LIST_APPEND(*list, key, link); key = NULL; } } +failure: + if (dir_open) { + isc_dir_close(&dir); + } + if (dstkey != NULL) { + dst_key_free(&dstkey); + } + return (result); +} + +/*% + * Get a list of DNSSEC keys from the key repository. + */ +isc_result_t +dns_dnssec_findmatchingkeys(const dns_name_t *origin, dns_kasp_t *kasp, + const char *keydir, dns_keystorelist_t *keystores, + isc_stdtime_t now, isc_mem_t *mctx, + dns_dnsseckeylist_t *keylist) { + isc_result_t result = ISC_R_SUCCESS; + dns_dnsseckeylist_t list; + dns_dnsseckey_t *key = NULL; + char namebuf[DNS_NAME_FORMATSIZE]; + isc_buffer_t b; + unsigned int len; + + REQUIRE(keylist != NULL); + ISC_LIST_INIT(list); + + isc_buffer_init(&b, namebuf, sizeof(namebuf) - 1); + RETERR(dns_name_tofilenametext(origin, false, &b)); + len = isc_buffer_usedlength(&b); + namebuf[len] = '\0'; + + if (kasp == NULL || (strcmp(dns_kasp_getname(kasp), "none") == 0) || + (strcmp(dns_kasp_getname(kasp), "insecure") == 0)) + { + RETERR(findmatchingkeys(keydir, namebuf, len, mctx, now, + &list)); + } else if (keystores != NULL) { + for (dns_keystore_t *keystore = ISC_LIST_HEAD(*keystores); + keystore != NULL; keystore = ISC_LIST_NEXT(keystore, link)) + { + for (dns_kasp_key_t *kkey = + ISC_LIST_HEAD(dns_kasp_keys(kasp)); + kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) + { + if (dns_kasp_key_keystore(kkey) == keystore) { + const char *directory = + dns_keystore_directory( + keystore); + if (directory == NULL || + (strcmp(dns_keystore_name(keystore), + DNS_KEYSTORE_KEYDIRECTORY) == + 0)) + { + directory = keydir; + } + RETERR(findmatchingkeys( + directory, namebuf, len, mctx, + now, &list)); + break; + } + } + } + } + if (!ISC_LIST_EMPTY(list)) { result = ISC_R_SUCCESS; ISC_LIST_APPENDLIST(*keylist, list, link); @@ -1521,19 +1573,12 @@ dns_dnssec_findmatchingkeys(const dns_name_t *origin, const char *directory, } failure: - if (dir_open) { - isc_dir_close(&dir); - } - INSIST(key == NULL); while ((key = ISC_LIST_HEAD(list)) != NULL) { ISC_LIST_UNLINK(list, key, link); INSIST(key->key != NULL); dst_key_free(&key->key); dns_dnsseckey_destroy(mctx, &key); } - if (dstkey != NULL) { - dst_key_free(&dstkey); - } return (result); } @@ -1641,15 +1686,54 @@ mark_active_keys(dns_dnsseckeylist_t *keylist, dns_rdataset_t *rrsigs) { return (result); } +static isc_result_t +keyfromfile(dns_kasp_t *kasp, const char *keydir, dst_key_t *key, int type, + isc_mem_t *mctx, dst_key_t **savekey) { + const char *directory = keydir; + isc_result_t result = ISC_R_NOTFOUND; + + if (kasp == NULL || (strcmp(dns_kasp_getname(kasp), "none") == 0) || + (strcmp(dns_kasp_getname(kasp), "insecure") == 0)) + { + result = dst_key_fromfile(dst_key_name(key), dst_key_id(key), + dst_key_alg(key), type, directory, + mctx, savekey); + } else { + for (dns_kasp_key_t *kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); + kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) + { + dns_keystore_t *ks = dns_kasp_key_keystore(kkey); + if (ks == NULL || + strcmp(dns_keystore_name(ks), + DNS_KEYSTORE_KEYDIRECTORY) == 0) + { + directory = keydir; + } else { + directory = dns_keystore_directory(ks); + } + + result = dst_key_fromfile(dst_key_name(key), + dst_key_id(key), + dst_key_alg(key), type, + directory, mctx, savekey); + if (result == ISC_R_SUCCESS) { + break; + } + } + } + + return (result); +} + /*% * Add the contents of a DNSKEY rdataset 'keyset' to 'keylist'. */ isc_result_t -dns_dnssec_keylistfromrdataset(const dns_name_t *origin, const char *directory, - isc_mem_t *mctx, dns_rdataset_t *keyset, - dns_rdataset_t *keysigs, dns_rdataset_t *soasigs, - bool savekeys, bool publickey, - dns_dnsseckeylist_t *keylist) { +dns_dnssec_keylistfromrdataset(const dns_name_t *origin, dns_kasp_t *kasp, + const char *directory, isc_mem_t *mctx, + dns_rdataset_t *keyset, dns_rdataset_t *keysigs, + dns_rdataset_t *soasigs, bool savekeys, + bool publickey, dns_dnsseckeylist_t *keylist) { dns_rdataset_t keys; dns_rdata_t rdata = DNS_RDATA_INIT; dst_key_t *dnskey = NULL, *pubkey = NULL, *privkey = NULL; @@ -1695,21 +1779,19 @@ dns_dnssec_keylistfromrdataset(const dns_name_t *origin, const char *directory, } /* Try to read the public key. */ - result = dst_key_fromfile( - dst_key_name(dnskey), dst_key_id(dnskey), - dst_key_alg(dnskey), (DST_TYPE_PUBLIC | DST_TYPE_STATE), - directory, mctx, &pubkey); + result = keyfromfile(kasp, directory, dnskey, + (DST_TYPE_PUBLIC | DST_TYPE_STATE), mctx, + &pubkey); if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) { result = ISC_R_SUCCESS; } RETERR(result); /* Now read the private key. */ - result = dst_key_fromfile( - dst_key_name(dnskey), dst_key_id(dnskey), - dst_key_alg(dnskey), + result = keyfromfile( + kasp, directory, dnskey, (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE), - directory, mctx, &privkey); + mctx, &privkey); /* * If the key was revoked and the private file @@ -1722,12 +1804,11 @@ dns_dnssec_keylistfromrdataset(const dns_name_t *origin, const char *directory, if ((flags & DNS_KEYFLAG_REVOKE) != 0) { dst_key_setflags(dnskey, flags & ~DNS_KEYFLAG_REVOKE); - result = dst_key_fromfile( - dst_key_name(dnskey), - dst_key_id(dnskey), dst_key_alg(dnskey), - (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | - DST_TYPE_STATE), - directory, mctx, &privkey); + result = keyfromfile(kasp, directory, dnskey, + (DST_TYPE_PUBLIC | + DST_TYPE_PRIVATE | + DST_TYPE_STATE), + mctx, &privkey); if (result == ISC_R_SUCCESS && dst_key_pubcompare(dnskey, privkey, false)) { @@ -1750,7 +1831,7 @@ dns_dnssec_keylistfromrdataset(const dns_name_t *origin, const char *directory, dst_key_alg(dnskey), (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE), - directory, mctx, &buf); + NULL, mctx, &buf); if (result2 != ISC_R_SUCCESS) { char namebuf[DNS_NAME_FORMATSIZE]; char algbuf[DNS_SECALG_FORMATSIZE]; diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h index 903d40c4f7..b9bdffc681 100644 --- a/lib/dns/include/dns/dnssec.h +++ b/lib/dns/include/dns/dnssec.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -295,11 +296,15 @@ dns_dnssec_get_hints(dns_dnsseckey_t *key, isc_stdtime_t now); */ isc_result_t -dns_dnssec_findmatchingkeys(const dns_name_t *origin, const char *directory, +dns_dnssec_findmatchingkeys(const dns_name_t *origin, dns_kasp_t *kasp, + const char *keydir, dns_keystorelist_t *keystores, isc_stdtime_t now, isc_mem_t *mctx, dns_dnsseckeylist_t *keylist); /*%< - * Search 'directory' for K* key files matching the name in 'origin'. + * Search for K* key files matching the name in 'origin'. If 'kasp' is not + * NULL, search in the directories used in 'keystores'. Otherwise search in the + * key-directory 'keydir'. + * * Append all such keys, along with use hints gleaned from their * metadata, onto 'keylist'. Skip any unsupported algorithms. * @@ -318,17 +323,18 @@ dns_dnssec_findmatchingkeys(const dns_name_t *origin, const char *directory, */ isc_result_t -dns_dnssec_keylistfromrdataset(const dns_name_t *origin, const char *directory, - isc_mem_t *mctx, dns_rdataset_t *keyset, - dns_rdataset_t *keysigs, dns_rdataset_t *soasigs, - bool savekeys, bool publickey, - dns_dnsseckeylist_t *keylist); +dns_dnssec_keylistfromrdataset(const dns_name_t *origin, dns_kasp_t *kasp, + const char *directory, isc_mem_t *mctx, + dns_rdataset_t *keyset, dns_rdataset_t *keysigs, + dns_rdataset_t *soasigs, bool savekeys, + bool publickey, dns_dnsseckeylist_t *keylist); /*%< * Append the contents of a DNSKEY rdataset 'keyset' to 'keylist'. - * Omit duplicates. If 'publickey' is false, search 'directory' for - * matching key files, and load the private keys that go with - * the public ones. If 'savekeys' is true, mark the keys so - * they will not be deleted or inactivated regardless of metadata. + * Omit duplicates. If 'publickey' is false, search the key stores referenced + * in 'kasp', or 'directory' if 'kasp' is NULL, for matching key files, and + * load the private keys that go with the public ones. If 'savekeys' is true, + * mark the keys so they will not be deleted or inactivated regardless of + * metadata. * * 'keysigs' and 'soasigs', if not NULL and associated, contain the * RRSIGS for the DNSKEY and SOA records respectively and are used to mark diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index de48246d1d..a463fee162 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1595,7 +1595,7 @@ isc_result_t dns_zone_setkeydirectory(dns_zone_t *zone, const char *directory); /*%< * Sets the name of the directory where private keys used for - * online signing of dynamic zones are found. + * online signing or dynamic zones are found. * * Require: *\li 'zone' to be a valid zone. @@ -1618,6 +1618,29 @@ dns_zone_getkeydirectory(dns_zone_t *zone); * Pointer to null-terminated file name, or NULL. */ +void +dns_zone_setkeystores(dns_zone_t *zone, dns_keystorelist_t *keystores); +/*%< + * Sets the keystore list where private keys used for + * online signing or dynamic zones are found. + * + * Require: + *\li 'zone' to be a valid zone. + */ + +dns_keystorelist_t * +dns_zone_getkeystores(dns_zone_t *zone); +/*%< + * Gets the keystore list where private keys used for + * online signing or dynamic zones are found. + * + * Require: + *\li 'zone' to be a valid zone. + * + * Returns: + * Pointer to the keystore list, or NULL. + */ + isc_result_t dns_zone_getdnsseckeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_t now, dns_dnsseckeylist_t *keys); diff --git a/lib/dns/update.c b/lib/dns/update.c index 6bef476fd5..cbcbe1c139 100644 --- a/lib/dns/update.c +++ b/lib/dns/update.c @@ -1056,13 +1056,20 @@ find_zone_keys(dns_zone_t *zone, isc_mem_t *mctx, unsigned int maxkeys, unsigned int count = 0; isc_result_t result; isc_stdtime_t now = isc_stdtime_now(); + dns_kasp_t *kasp; + dns_keystorelist_t *keystores; + const char *keydir; ISC_LIST_INIT(keylist); + kasp = dns_zone_getkasp(zone); + keydir = dns_zone_getkeydirectory(zone); + keystores = dns_zone_getkeystores(zone); + dns_zone_lock_keyfiles(zone); - result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), - dns_zone_getkeydirectory(zone), - now, mctx, &keylist); + result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), kasp, + keydir, keystores, now, mctx, + &keylist); dns_zone_unlock_keyfiles(zone); if (result != ISC_R_SUCCESS) { diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 0e4be36948..7651dcb3e9 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -303,6 +303,7 @@ struct dns_zone { isc_stdtime_t log_key_expired_timer; char *keydirectory; dns_keyfileio_t *kfio; + dns_keystorelist_t *keystores; uint32_t maxrefresh; uint32_t minrefresh; @@ -6120,8 +6121,8 @@ dns_zone_getdnsseckeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, /* Get keys from private key files. */ dns_zone_lock_keyfiles(zone); - result = dns_dnssec_findmatchingkeys(origin, dir, now, - dns_zone_getmctx(zone), keys); + result = dns_dnssec_findmatchingkeys(origin, kasp, dir, zone->keystores, + now, dns_zone_getmctx(zone), keys); dns_zone_unlock_keyfiles(zone); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { @@ -6134,8 +6135,8 @@ dns_zone_getdnsseckeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_none, 0, &keyset, NULL); if (result == ISC_R_SUCCESS) { CHECK(dns_dnssec_keylistfromrdataset( - origin, dir, dns_zone_getmctx(zone), &keyset, NULL, - NULL, false, false, &dnskeys)); + origin, kasp, dir, dns_zone_getmctx(zone), &keyset, + NULL, NULL, false, false, &dnskeys)); } else if (result != ISC_R_NOTFOUND) { CHECK(result); } @@ -15920,6 +15921,9 @@ dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse) { isc_result_t result = ISC_R_SUCCESS; isc_stdtime_t now = isc_stdtime_now(); isc_mem_t *mctx; + dns_kasp_t *kasp; + dns_keystorelist_t *keystores; + const char *keydir; REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(dns_rdatatype_iskeymaterial(rdata->type)); @@ -15930,10 +15934,14 @@ dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse) { *inuse = false; + kasp = dns_zone_getkasp(zone); + keydir = dns_zone_getkeydirectory(zone); + keystores = dns_zone_getkeystores(zone); + dns_zone_lock_keyfiles(zone); - result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), - dns_zone_getkeydirectory(zone), - now, mctx, &keylist); + result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), kasp, + keydir, keystores, now, mctx, + &keylist); dns_zone_unlock_keyfiles(zone); if (result == ISC_R_NOTFOUND) { return (ISC_R_SUCCESS); @@ -19414,6 +19422,32 @@ dns_zone_getkeydirectory(dns_zone_t *zone) { return (zone->keydirectory); } +void +dns_zone_setkeystores(dns_zone_t *zone, dns_keystorelist_t *keystores) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->keystores = keystores; + UNLOCK_ZONE(zone); +} + +dns_keystorelist_t * +dns_zone_getkeystores(dns_zone_t *zone) { + dns_keystorelist_t *ks = NULL; + + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + if (inline_raw(zone) && zone->secure != NULL) { + ks = zone->secure->keystores; + } else { + ks = zone->keystores; + } + UNLOCK_ZONE(zone); + + return (ks); +} + unsigned int dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state) { dns_zone_t *zone; @@ -21634,8 +21668,8 @@ zone_rekey(dns_zone_t *zone) { dns_zone_lock_keyfiles(zone); result = dns_dnssec_keylistfromrdataset( - &zone->origin, dir, mctx, &keyset, &keysigs, &soasigs, - false, false, &dnskeys); + &zone->origin, kasp, dir, mctx, &keyset, &keysigs, + &soasigs, false, false, &dnskeys); dns_zone_unlock_keyfiles(zone); @@ -21696,8 +21730,8 @@ zone_rekey(dns_zone_t *zone) { KASP_LOCK(kasp); dns_zone_lock_keyfiles(zone); - result = dns_dnssec_findmatchingkeys(&zone->origin, dir, now, mctx, - &keys); + result = dns_dnssec_findmatchingkeys(&zone->origin, kasp, dir, + zone->keystores, now, mctx, &keys); dns_zone_unlock_keyfiles(zone); if (result != ISC_R_SUCCESS) { From 0701a140d382eb58649aad7f7fae9132587f6899 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 9 Mar 2022 11:27:03 +0100 Subject: [PATCH 14/37] Add directory to dst_key structure Store key directory when reading the key from file. This is the directory it was read from and can be used when saving the key back to disk. --- lib/dns/dst_api.c | 7 +++++++ lib/dns/dst_internal.h | 1 + 2 files changed, 8 insertions(+) diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index ca81410089..803214c096 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -691,6 +691,10 @@ dst_key_fromnamedfile(const char *filename, const char *dirname, int type, } key->modified = false; + + if (dirname != NULL) { + key->directory = isc_mem_strdup(mctx, dirname); + } *keyp = key; key = NULL; @@ -1395,6 +1399,9 @@ dst_key_free(dst_key_t **keyp) { INSIST(key->func->destroy != NULL); key->func->destroy(key); } + if (key->directory != NULL) { + isc_mem_free(mctx, key->directory); + } if (key->engine != NULL) { isc_mem_free(mctx, key->engine); } diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h index a4fcc05bd8..7026e7ffae 100644 --- a/lib/dns/dst_internal.h +++ b/lib/dns/dst_internal.h @@ -91,6 +91,7 @@ struct dst_key { dns_rdataclass_t key_class; /*%< class of the key record */ dns_ttl_t key_ttl; /*%< default/initial dnskey ttl */ isc_mem_t *mctx; /*%< memory context */ + char *directory; /*%< key directory */ char *engine; /*%< engine name (HSM) */ char *label; /*%< engine label (HSM) */ char *object; /*%< engine object (HSM) */ From 80387532cd1f714bfc9b7b5f93a0ea1754ea87e8 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 9 Mar 2022 11:33:03 +0100 Subject: [PATCH 15/37] Use dst_key's directory when writing key files When writing key files to disk, use the internally stored directory. Add an access function 'dst_key_directory()'. Most calls to keymgr functions no longer need to provide the key-directory value. Only 'dns_keymgr_run' still needs access to the zone's key-directory in case the key-store is set to the built-in key-directory. --- bin/named/server.c | 10 ++-- lib/dns/include/dns/keymgr.h | 19 +++--- lib/dns/include/dst/dst.h | 12 ++++ lib/dns/key.c | 15 +++++ lib/dns/keymgr.c | 109 ++++++++++++++++++++++------------- lib/dns/zone.c | 7 +-- 6 files changed, 110 insertions(+), 62 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index c371fb9c08..304e1f3e2e 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -14636,7 +14636,6 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, char output[4096]; isc_stdtime_t now, when; isc_time_t timenow, timewhen; - const char *dir; dns_db_t *db = NULL; dns_dbversion_t *version = NULL; @@ -14771,7 +14770,6 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, } /* Get DNSSEC keys. */ - dir = dns_zone_getkeydirectory(zone); CHECK(dns_zone_getdb(zone, &db)); dns_db_currentversion(db, &version); LOCK(&kasp->lock); @@ -14803,11 +14801,11 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, LOCK(&kasp->lock); if (use_keyid) { - result = dns_keymgr_checkds_id(kasp, &keys, dir, now, - when, dspublish, keyid, + result = dns_keymgr_checkds_id(kasp, &keys, now, when, + dspublish, keyid, (unsigned int)algorithm); } else { - result = dns_keymgr_checkds(kasp, &keys, dir, now, when, + result = dns_keymgr_checkds(kasp, &keys, now, when, dspublish); } UNLOCK(&kasp->lock); @@ -14858,7 +14856,7 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, isc_result_t ret; LOCK(&kasp->lock); - result = dns_keymgr_rollover(kasp, &keys, dir, now, when, keyid, + result = dns_keymgr_rollover(kasp, &keys, now, when, keyid, (unsigned int)algorithm); UNLOCK(&kasp->lock); diff --git a/lib/dns/include/dns/keymgr.h b/lib/dns/include/dns/keymgr.h index bf08fbb549..eadb0ea877 100644 --- a/lib/dns/include/dns/keymgr.h +++ b/lib/dns/include/dns/keymgr.h @@ -26,13 +26,13 @@ ISC_LANG_BEGINDECLS isc_result_t dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, - const char *directory, isc_mem_t *mctx, - dns_dnsseckeylist_t *keyring, dns_dnsseckeylist_t *dnskeys, + isc_mem_t *mctx, dns_dnsseckeylist_t *keyring, + dns_dnsseckeylist_t *dnskeys, const char *keydir, dns_kasp_t *kasp, isc_stdtime_t now, isc_stdtime_t *nexttime); /*%< * Manage keys in 'keyring' and update timing data according to 'kasp' policy. - * Create new keys for 'origin' if necessary in 'directory'. Append all such - * keys, along with use hints gleaned from their metadata, onto 'keyring'. + * Create new keys for 'origin' if necessary. Append all such keys, along + * with use hints gleaned from their metadata, onto 'keyring'. * * Update key states and store changes back to disk. Store when to run next * in 'nexttime'. @@ -54,13 +54,11 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, isc_result_t dns_keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, - const char *directory, isc_stdtime_t now, isc_stdtime_t when, - bool dspublish); + isc_stdtime_t now, isc_stdtime_t when, bool dspublish); isc_result_t dns_keymgr_checkds_id(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, - const char *directory, isc_stdtime_t now, - isc_stdtime_t when, bool dspublish, dns_keytag_t id, - unsigned int algorithm); + isc_stdtime_t now, isc_stdtime_t when, bool dspublish, + dns_keytag_t id, unsigned int algorithm); /*%< * Check DS for one key in 'keyring'. The key must have the KSK role. * If 'dspublish' is set to true, set the DS Publish time to 'now'. @@ -82,8 +80,7 @@ dns_keymgr_checkds_id(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, isc_result_t dns_keymgr_rollover(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, - const char *directory, isc_stdtime_t now, - isc_stdtime_t when, dns_keytag_t id, + isc_stdtime_t now, isc_stdtime_t when, dns_keytag_t id, unsigned int algorithm); /*%< * Rollover key with given 'id'. If the 'algorithm' is non-zero, it must diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index f83a2adc11..e4895e1932 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -765,6 +765,9 @@ dst_key_rid(const dst_key_t *key); dns_rdataclass_t dst_key_class(const dst_key_t *key); +const char * +dst_key_directory(const dst_key_t *key); + bool dst_key_isprivate(const dst_key_t *key); @@ -1233,6 +1236,15 @@ dst_key_copy_metadata(dst_key_t *to, dst_key_t *from); * 'to' and 'from' to be valid. */ +void +dst_key_setdirectory(dst_key_t *key, const char *dir); +/*%< + * Set the directory where to store key files for this key. + * + * Requires: + * 'key' to be valid. + */ + const char * dst_hmac_algorithm_totext(dst_algorithm_t alg); /*$< diff --git a/lib/dns/key.c b/lib/dns/key.c index 2449e8aac1..a0cde831a5 100644 --- a/lib/dns/key.c +++ b/lib/dns/key.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -123,6 +124,12 @@ dst_key_class(const dst_key_t *key) { return (key->key_class); } +const char * +dst_key_directory(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->directory); +} + bool dst_key_iszonekey(const dst_key_t *key) { REQUIRE(VALID_KEY(key)); @@ -247,4 +254,12 @@ dst_key_getttl(const dst_key_t *key) { return (key->key_ttl); } +void +dst_key_setdirectory(dst_key_t *key, const char *dir) { + if (key->directory != NULL) { + isc_mem_free(key->mctx, key->directory); + } + key->directory = isc_mem_strdup(key->mctx, dir); +} + /*! \file */ diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 027965fef4..d0059f59a3 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -444,7 +444,7 @@ keymgr_keyid_conflict(dst_key_t *newkey, dns_dnsseckeylist_t *keys) { */ static isc_result_t keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin, - dns_rdataclass_t rdclass, isc_mem_t *mctx, + dns_rdataclass_t rdclass, isc_mem_t *mctx, const char *keydir, dns_dnsseckeylist_t *keylist, dns_dnsseckeylist_t *newkeys, dst_key_t **dst_key) { isc_result_t result = ISC_R_SUCCESS; @@ -489,6 +489,20 @@ keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin, dst_key_setnum(newkey, DST_NUM_LIFETIME, dns_kasp_key_lifetime(kkey)); dst_key_setbool(newkey, DST_BOOL_KSK, dns_kasp_key_ksk(kkey)); dst_key_setbool(newkey, DST_BOOL_ZSK, dns_kasp_key_zsk(kkey)); + + if (keystore == NULL || + strcmp(dns_keystore_name(keystore), "key-directory") == 0) + { + if (keydir != NULL) { + dst_key_setdirectory(newkey, keydir); + } + } else { + if (dns_keystore_directory(keystore) != NULL) { + dst_key_setdirectory(newkey, + dns_keystore_directory(keystore)); + } + } + *dst_key = newkey; return (ISC_R_SUCCESS); @@ -1679,8 +1693,8 @@ static isc_result_t keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, dns_dnsseckeylist_t *keyring, dns_dnsseckeylist_t *newkeys, const dns_name_t *origin, dns_rdataclass_t rdclass, - dns_kasp_t *kasp, uint32_t lifetime, bool rollover, - isc_stdtime_t now, isc_stdtime_t *nexttime, + dns_kasp_t *kasp, const char *keydir, uint32_t lifetime, + bool rollover, isc_stdtime_t now, isc_stdtime_t *nexttime, isc_mem_t *mctx) { char keystr[DST_KEY_FORMATSIZE]; isc_stdtime_t retire = 0, active = 0, prepub = 0; @@ -1799,8 +1813,8 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, dns_kasp_key_zsk(kaspkey)); isc_result_t result = keymgr_createkey(kaspkey, origin, rdclass, - mctx, keyring, newkeys, - &dst_key); + mctx, keydir, keyring, + newkeys, &dst_key); if (result != ISC_R_SUCCESS) { return (result); } @@ -1944,7 +1958,7 @@ keymgr_key_may_be_purged(dst_key_t *key, uint32_t after, isc_stdtime_t now) { } static void -keymgr_purge_keyfile(dst_key_t *key, const char *dir, int type) { +keymgr_purge_keyfile(dst_key_t *key, int type) { isc_result_t ret; isc_buffer_t fileb; char filename[NAME_MAX]; @@ -1953,7 +1967,7 @@ keymgr_purge_keyfile(dst_key_t *key, const char *dir, int type) { * Make the filename. */ isc_buffer_init(&fileb, filename, sizeof(filename)); - ret = dst_key_buildfilename(key, type, dir, &fileb); + ret = dst_key_buildfilename(key, type, dst_key_directory(key), &fileb); if (ret != ISC_R_SUCCESS) { char keystr[DST_KEY_FORMATSIZE]; dst_key_format(key, keystr, sizeof(keystr)); @@ -1983,8 +1997,8 @@ keymgr_purge_keyfile(dst_key_t *key, const char *dir, int type) { */ isc_result_t dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, - const char *directory, isc_mem_t *mctx, - dns_dnsseckeylist_t *keyring, dns_dnsseckeylist_t *dnskeys, + isc_mem_t *mctx, dns_dnsseckeylist_t *keyring, + dns_dnsseckeylist_t *dnskeys, const char *keydir, dns_kasp_t *kasp, isc_stdtime_t now, isc_stdtime_t *nexttime) { isc_result_t result = ISC_R_SUCCESS; dns_dnsseckeylist_t newkeys; @@ -2004,14 +2018,6 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, ISC_LIST_INIT(newkeys); - isc_dir_init(&dir); - if (directory == NULL) { - directory = "."; - } - - RETERR(isc_dir_open(&dir, directory)); - dir_open = true; - *nexttime = 0; /* Debug logging: what keys are available in the keyring? */ @@ -2086,13 +2092,9 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, keystr, keymgr_keyrole(dkey->key), dns_kasp_getname(kasp)); - keymgr_purge_keyfile(dkey->key, directory, - DST_TYPE_PUBLIC); - keymgr_purge_keyfile(dkey->key, directory, - DST_TYPE_PRIVATE); - keymgr_purge_keyfile(dkey->key, directory, - DST_TYPE_STATE); - + keymgr_purge_keyfile(dkey->key, DST_TYPE_PUBLIC); + keymgr_purge_keyfile(dkey->key, DST_TYPE_PRIVATE); + keymgr_purge_keyfile(dkey->key, DST_TYPE_STATE); dkey->purge = true; } } @@ -2201,9 +2203,10 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, } /* See if this key requires a rollover. */ - RETERR(keymgr_key_rollover( - kkey, active_key, keyring, &newkeys, origin, rdclass, - kasp, lifetime, rollover_allowed, now, nexttime, mctx)); + RETERR(keymgr_key_rollover(kkey, active_key, keyring, &newkeys, + origin, rdclass, kasp, keydir, + lifetime, rollover_allowed, now, + nexttime, mctx)); } /* Walked all kasp key configurations. Append new keys. */ @@ -2221,6 +2224,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, keymgr_update(keyring, kasp, now, nexttime, secure_to_insecure); /* Store key states and update hints. */ + isc_dir_init(&dir); for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; dkey = ISC_LIST_NEXT(dkey, link)) { @@ -2230,8 +2234,31 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, modified = true; } if (modified && !dkey->purge) { + const char *directory = dst_key_directory(dkey->key); + if (directory == NULL) { + directory = "."; + } + + RETERR(isc_dir_open(&dir, directory)); + dir_open = true; + dns_dnssec_get_hints(dkey, now); RETERR(dst_key_tofile(dkey->key, options, directory)); + dst_key_setmodified(dkey->key, false); + + isc_dir_close(&dir); + dir_open = false; + + if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) { + continue; + } + dst_key_format(dkey->key, keystr, sizeof(keystr)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(3), + "keymgr: DNSKEY %s (%s) " + "saved to directory %s, policy %s", + keystr, keymgr_keyrole(dkey->key), + directory, dns_kasp_getname(kasp)); } dst_key_setmodified(dkey->key, false); } @@ -2264,10 +2291,10 @@ failure: static isc_result_t keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, - const char *directory, isc_stdtime_t now, isc_stdtime_t when, - bool dspublish, dns_keytag_t id, unsigned int alg, - bool check_id) { + isc_stdtime_t now, isc_stdtime_t when, bool dspublish, + dns_keytag_t id, unsigned int alg, bool check_id) { int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); + const char *directory = NULL; isc_dir_t dir; isc_result_t result; dns_dnsseckey_t *ksk_key = NULL; @@ -2336,6 +2363,7 @@ keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, /* Store key state and update hints. */ isc_dir_init(&dir); + directory = dst_key_directory(ksk_key->key); if (directory == NULL) { directory = "."; } @@ -2356,19 +2384,17 @@ keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, isc_result_t dns_keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, - const char *directory, isc_stdtime_t now, isc_stdtime_t when, - bool dspublish) { - return (keymgr_checkds(kasp, keyring, directory, now, when, dspublish, - 0, 0, false)); + isc_stdtime_t now, isc_stdtime_t when, bool dspublish) { + return (keymgr_checkds(kasp, keyring, now, when, dspublish, 0, 0, + false)); } isc_result_t dns_keymgr_checkds_id(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, - const char *directory, isc_stdtime_t now, - isc_stdtime_t when, bool dspublish, dns_keytag_t id, - unsigned int alg) { - return (keymgr_checkds(kasp, keyring, directory, now, when, dspublish, - id, alg, true)); + isc_stdtime_t now, isc_stdtime_t when, bool dspublish, + dns_keytag_t id, unsigned int alg) { + return (keymgr_checkds(kasp, keyring, now, when, dspublish, id, alg, + true)); } static void @@ -2575,10 +2601,10 @@ dns_keymgr_status(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, isc_result_t dns_keymgr_rollover(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, - const char *directory, isc_stdtime_t now, - isc_stdtime_t when, dns_keytag_t id, + isc_stdtime_t now, isc_stdtime_t when, dns_keytag_t id, unsigned int algorithm) { int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); + const char *directory = NULL; isc_dir_t dir; isc_result_t result; dns_dnsseckey_t *key = NULL; @@ -2639,6 +2665,7 @@ dns_keymgr_rollover(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, /* Store key state and update hints. */ isc_dir_init(&dir); + directory = dst_key_directory(key->key); if (directory == NULL) { directory = "."; } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 7651dcb3e9..428dfd94b9 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -20355,7 +20355,6 @@ static bool do_checkds(dns_zone_t *zone, dst_key_t *key, isc_stdtime_t now, bool dspublish) { dns_kasp_t *kasp = zone->kasp; - const char *dir = dns_zone_getkeydirectory(zone); isc_result_t result; uint32_t count = 0; uint32_t num; @@ -20406,7 +20405,7 @@ do_checkds(dns_zone_t *zone, dst_key_t *key, isc_stdtime_t now, dspublish ? "published" : "withdrawn", dst_key_id(key)); dns_zone_lock_keyfiles(zone); - result = dns_keymgr_checkds_id(kasp, &zone->checkds_ok, dir, now, now, + result = dns_keymgr_checkds_id(kasp, &zone->checkds_ok, now, now, dspublish, dst_key_id(key), dst_key_alg(key)); dns_zone_unlock_keyfiles(zone); @@ -21608,7 +21607,6 @@ zone_rekey(dns_zone_t *zone) { dns_rdataset_init(&keysigs); dns_rdataset_init(&cdsset); dns_rdataset_init(&cdnskeyset); - dir = dns_zone_getkeydirectory(zone); mctx = zone->mctx; dns_diff_init(mctx, &diff); dns_diff_init(mctx, &_sig_diff); @@ -21622,6 +21620,7 @@ zone_rekey(dns_zone_t *zone) { now = isc_time_seconds(&timenow); kasp = zone->kasp; + dir = dns_zone_getkeydirectory(zone); dnssec_log(zone, ISC_LOG_INFO, "reconfiguring zone keys"); @@ -21767,7 +21766,7 @@ zone_rekey(dns_zone_t *zone) { if (result == ISC_R_SUCCESS || result == ISC_R_NOTFOUND) { dns_zone_lock_keyfiles(zone); result = dns_keymgr_run(&zone->origin, zone->rdclass, - dir, mctx, &keys, &dnskeys, + mctx, &keys, &dnskeys, dir, kasp, now, &nexttime); dns_zone_unlock_keyfiles(zone); From 49b668171a5ea71c2df74dbf1a28b838dd8ff8d3 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 11 Mar 2022 15:33:08 +0100 Subject: [PATCH 16/37] Test key-store with non-default directory Add a test case where dnssec-policy uses key stores with a directory other than the zone's key-directory. This requires changing the kasp shell script to take into account that keys can be in different directories. When looking for keys, the 'find' command now takes a maxdepth of 3 to also look for keys in subdirectories. Note this maxdepth value is arbitrary, the added 'keystore.kasp' test only requires a maxdepth of 2. Because of this change, the dnssec-keygen tests no longer work because they are for the same zone (although different directories). Change the test to use a different zone ('kasp2' instead of 'kasp'). --- bin/tests/system/kasp.sh | 32 +++++++++--- bin/tests/system/kasp/clean.sh | 2 + bin/tests/system/kasp/ns3/named-fips.conf.in | 8 +++ .../kasp/ns3/policies/kasp-fips.conf.in | 17 +++++++ bin/tests/system/kasp/ns3/setup.sh | 6 ++- bin/tests/system/kasp/tests.sh | 51 +++++++++++++++++++ 6 files changed, 108 insertions(+), 8 deletions(-) diff --git a/bin/tests/system/kasp.sh b/bin/tests/system/kasp.sh index 5f879cbe71..bc8cc9e0f5 100644 --- a/bin/tests/system/kasp.sh +++ b/bin/tests/system/kasp.sh @@ -67,6 +67,8 @@ VIEW3="C1Azf+gGPMmxrUg/WQINP6eV9Y0=" # PRIVKEY_STAT # PUBKEY_STAT # STATE_STAT +# FLAGS +# KEYDIR key_key() { echo "${1}__${2}" @@ -132,6 +134,7 @@ key_clear() { key_set "$1" "PRIVKEY_STAT" '0' key_set "$1" "PUBKEY_STAT" '0' key_set "$1" "STATE_STAT" '0' + key_set "$1" "KEYDIR" 'none' } # Start clear. @@ -176,7 +179,7 @@ get_keyids() { _zone=$2 _regex="K${_zone}.+*+*.key" - find "${_dir}" -mindepth 1 -maxdepth 1 -name "${_regex}" | sed "s,$_dir/K${_zone}.+\([0-9]\{3\}\)+\([0-9]\{5\}\).key,\2," + find "${_dir}" -mindepth 1 -maxdepth 3 -name "${_regex}" | sed "s,.*/K${_zone}.+\([0-9]\{3\}\)+\([0-9]\{5\}\).key,\2," } # By default log errors and don't quit immediately. @@ -313,6 +316,13 @@ set_keystate() { key_set "$1" "$2" "$3" } +# Set key directory. +# $1: Key to update (KEY1, KEY2, ...) +# $2: Directory. +set_keydir() { + key_set "$1" "KEYDIR" "$2" +} + # Check the key $1 with id $2. # This requires environment variables to be set. # @@ -324,7 +334,10 @@ set_keystate() { # KEY_ID=$(echo $1 | sed 's/^0\{0,4\}//') # KEY_CREATED (from the KEY_FILE) check_key() { - _dir="$DIR" + _dir=$(key_get "$1" KEYDIR) + if [ "$_dir" = "none" ]; then + _dir="$DIR" + fi _zone="$ZONE" _role=$(key_get "$1" ROLE) _key_idpad="$2" @@ -465,7 +478,10 @@ check_key() { # Check the key timing metadata for key $1. check_timingmetadata() { - _dir="$DIR" + _dir=$(key_get "$1" KEYDIR) + if [ "$_dir" = "none" ]; then + _dir="$DIR" + fi _zone="$ZONE" _key_idpad=$(key_get "$1" ID) _key_id=$(echo "$_key_idpad" | sed 's/^0\{0,4\}//') @@ -644,11 +660,11 @@ check_keytimes() { # STATE_FILE="${BASE_FILE}.state" # KEY_ID=$(echo $1 | sed 's/^0\{0,4\}//') key_unused() { - _dir=$DIR - _zone=$ZONE - _key_idpad=$1 + _dir="$DIR" + _zone="$ZONE" + _key_idpad="$1" _key_id=$(echo "$_key_idpad" | sed 's/^0\{0,4\}//') - _alg_num=$2 + _alg_num="$2" _alg_numpad=$(printf "%03d" "$_alg_num") BASE_FILE="${_dir}/K${_zone}.+${_alg_numpad}+${_key_idpad}" @@ -788,6 +804,8 @@ _check_keys() { # # It is expected that KEY1, KEY2, KEY3, and KEY4 arrays are set correctly. # Found key identifiers are stored in the right key array. +# Keys are found if they are stored inside $DIR or in a subdirectory up to +# three levels deeper. check_keys() { n=$((n + 1)) echo_i "check keys are created for zone ${ZONE} ($n)" diff --git a/bin/tests/system/kasp/clean.sh b/bin/tests/system/kasp/clean.sh index d31b53a464..8102d50597 100644 --- a/bin/tests/system/kasp/clean.sh +++ b/bin/tests/system/kasp/clean.sh @@ -35,3 +35,5 @@ rm -f rndc.dnssec.*.out.* rndc.zonestatus.out.* rm -f python.out.* rm -f *-supported.file rm -f created.key-* unused.key-* +rm -f ns3/ksk/K* ns3/zsk/K* +rm -rf ./ns3/ksk/ ./ns3/zsk/ diff --git a/bin/tests/system/kasp/ns3/named-fips.conf.in b/bin/tests/system/kasp/ns3/named-fips.conf.in index 01d7592686..3ca1551c29 100644 --- a/bin/tests/system/kasp/ns3/named-fips.conf.in +++ b/bin/tests/system/kasp/ns3/named-fips.conf.in @@ -166,6 +166,14 @@ zone "inline-signing.kasp" { dnssec-policy "default"; }; +/* A zone that uses dnssec-policy with key stores. */ +zone "keystore.kasp" { + type primary; + file "keystore.kasp.db"; + inline-signing yes; + dnssec-policy "keystore"; +}; + /* * A configured dnssec-policy but some keys already created. */ diff --git a/bin/tests/system/kasp/ns3/policies/kasp-fips.conf.in b/bin/tests/system/kasp/ns3/policies/kasp-fips.conf.in index 6778bac4d3..7b775f14b8 100644 --- a/bin/tests/system/kasp/ns3/policies/kasp-fips.conf.in +++ b/bin/tests/system/kasp/ns3/policies/kasp-fips.conf.in @@ -121,3 +121,20 @@ dnssec-policy "checkds-csk" { dnssec-policy "ttl" { max-zone-ttl 299; }; + +key-store "ksk" { + directory "ksk"; +}; + +key-store "zsk" { + directory "zsk"; +}; + +dnssec-policy "keystore" { + dnskey-ttl 303; + + keys { + ksk key-store "ksk" lifetime unlimited algorithm @DEFAULT_ALGORITHM@; + zsk key-store "zsk" lifetime unlimited algorithm @DEFAULT_ALGORITHM@; + }; +}; diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index 4d76d250c6..dd9dc83d8f 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -16,6 +16,10 @@ echo_i "ns3/setup.sh" +# Create key store directories. +mkdir ksk +mkdir zsk + setup() { zone="$1" echo_i "setting up zone: $zone" @@ -46,7 +50,7 @@ for zn in default dnssec-keygen some-keys legacy-keys pregenerated \ rumoured rsasha256 rsasha512 ecdsa256 ecdsa384 \ dynamic dynamic-inline-signing inline-signing \ checkds-ksk checkds-doubleksk checkds-csk inherit unlimited \ - manual-rollover multisigner-model2; do + manual-rollover multisigner-model2 keystore; do setup "${zn}.kasp" cp template.db.in "$zonefile" done diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 59dd4d391a..e0eb143385 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -127,6 +127,7 @@ set_zone "kasp" set_policy "default" "1" "3600" set_server "." "10.53.0.1" # Key properties. +key_clear "KEY1" set_keyrole "KEY1" "csk" set_keylifetime "KEY1" "0" set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" @@ -538,6 +539,7 @@ key_clear "KEY4" set_zone "checkds-ksk.kasp" set_policy "checkds-ksk" "2" "303" set_server "ns3" "10.53.0.3" + # Key properties. set_keyrole "KEY1" "ksk" set_keylifetime "KEY1" "0" @@ -940,6 +942,55 @@ check_apex check_subdomain dnssec_verify +# +# Zone: keystore.kasp. +# +set_zone "keystore.kasp" +set_policy "keystore" "2" "303" +set_server "ns3" "10.53.0.3" +# Key properties. +key_clear "KEY1" +set_keyrole "KEY1" "ksk" +set_keylifetime "KEY1" "0" +set_keydir "KEY1" "ns3/ksk" +set_keyalgorithm "KEY1" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS" +set_keysigning "KEY1" "yes" +set_zonesigning "KEY1" "no" + +key_clear "KEY2" +set_keyrole "KEY2" "zsk" +set_keylifetime "KEY2" "0" +set_keydir "KEY2" "ns3/zsk" +set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS" +set_keysigning "KEY2" "no" +set_zonesigning "KEY2" "yes" + +# KSK: DNSKEY, RRSIG (ksk) published. DS needs to wait. +# ZSK: DNSKEY, RRSIG (zsk) published. +set_keystate "KEY1" "GOAL" "omnipresent" +set_keystate "KEY1" "STATE_DNSKEY" "rumoured" +set_keystate "KEY1" "STATE_KRRSIG" "rumoured" +set_keystate "KEY1" "STATE_DS" "hidden" + +set_keystate "KEY2" "GOAL" "omnipresent" +set_keystate "KEY2" "STATE_DNSKEY" "rumoured" +set_keystate "KEY2" "STATE_ZRRSIG" "rumoured" +# Two keys only. +key_clear "KEY3" +key_clear "KEY4" + +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +# Reuse set_keytimes_csk_policy to set the KEY1 keytimes. +set_keytimes_csk_policy +created=$(key_get KEY2 CREATED) +set_keytime "KEY2" "PUBLISHED" "${created}" +set_keytime "KEY2" "ACTIVE" "${created}" +check_keytimes +check_apex +check_subdomain +dnssec_verify + # # Zone: inherit.kasp. # From 118e54504560a4d1bd1449b0fc0f544f31ab9d24 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 15 Mar 2022 15:54:56 +0100 Subject: [PATCH 17/37] Add support for key-store to dnssec-keygen If the provided policy in the configuration file uses a key-store, use that to generate the key, instead of 'dst_key_generate()'. --- bin/dnssec/dnssec-keygen.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index 55ecec9e87..9f9c3b5b08 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -92,6 +92,7 @@ struct keygen_ctx { const char *policy; const char *configfile; const char *directory; + dns_keystore_t *keystore; char *algname; char *nametype; char *type; @@ -690,12 +691,17 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { if (!ctx->quiet && show_progress) { fprintf(stderr, "Generating key pair."); + } + + if (ctx->keystore != NULL) { + ret = dns_keystore_keygen(ctx->keystore, name, + ctx->rdclass, mctx, ctx->alg, + ctx->size, flags, &key); + } else if (!ctx->quiet && show_progress) { ret = dst_key_generate(name, ctx->alg, ctx->size, param, flags, ctx->protocol, ctx->rdclass, NULL, mctx, &key, &progress); - putc('\n', stderr); - fflush(stderr); } else { ret = dst_key_generate(name, ctx->alg, ctx->size, param, flags, ctx->protocol, @@ -703,6 +709,11 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { NULL); } + if (!ctx->quiet && show_progress) { + putc('\n', stderr); + fflush(stderr); + } + if (ret != ISC_R_SUCCESS) { char namestr[DNS_NAME_FORMATSIZE]; dns_name_format(name, namestr, sizeof(namestr)); @@ -896,6 +907,18 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { } } +static void +check_keystore_options(keygen_ctx_t *ctx) { + ctx->directory = dns_keystore_directory(ctx->keystore); + if (ctx->directory != NULL) { + isc_result_t ret = try_dir(ctx->directory); + if (ret != ISC_R_SUCCESS) { + fatal("cannot open directory %s: %s", ctx->directory, + isc_result_totext(ret)); + } + } +} + int main(int argc, char **argv) { char *algname = NULL, *freeit = NULL; @@ -1331,7 +1354,10 @@ main(int argc, char **argv) { ctx.ksk = dns_kasp_key_ksk(kaspkey); ctx.zsk = dns_kasp_key_zsk(kaspkey); ctx.lifetime = dns_kasp_key_lifetime(kaspkey); - + ctx.keystore = dns_kasp_key_keystore(kaspkey); + if (ctx.keystore != NULL) { + check_keystore_options(&ctx); + } keygen(&ctx, mctx, argc, argv); kaspkey = ISC_LIST_NEXT(kaspkey, link); From 4b5bba3a992b832aa5b59e9eee75903c95c306ed Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 15 Mar 2022 16:10:34 +0100 Subject: [PATCH 18/37] Test dnssec-policy with multiple key stores Make sure that if a dnssec-policy uses multiple key stores, the keys have the right attributes and are stored in their appropriate directory. --- bin/tests/system/enginepkcs11/clean.sh | 2 ++ bin/tests/system/enginepkcs11/setup.sh | 19 +++++++++++++++++++ bin/tests/system/enginepkcs11/tests.sh | 21 +++++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/bin/tests/system/enginepkcs11/clean.sh b/bin/tests/system/enginepkcs11/clean.sh index 7233784a02..c8b3c79d0d 100644 --- a/bin/tests/system/enginepkcs11/clean.sh +++ b/bin/tests/system/enginepkcs11/clean.sh @@ -23,6 +23,7 @@ rm -f pkcs11-tool.err.* pkcs11-tool.out.* rm -f signer.out.* rm -f ns1/*.example.db ns1/*.example.db.signed rm -f ns1/*.kasp.db ns1/*.kasp.db.signed +rm -f ns1/*.split.db ns1/*.split.db.signed rm -f ns1/*.kskid1 ns1/*.kskid2 ns1/*.zskid1 ns1/*.zskid2 rm -f ns1/dig.out.* rm -f ns1/K* @@ -33,5 +34,6 @@ rm -f ns1/update.cmd.* rm -f ns1/update.log.* rm -f ns1/verify.out.* rm -f ns1/zone.*.jnl ns1/zone.*.jbk +rm -rf ./ns1/keys/ OPENSSL_CONF= softhsm2-util --delete-token --token "softhsm2-enginepkcs11" >/dev/null 2>&1 || echo_i "softhsm2-enginepkcs11 token not found for cleaning" diff --git a/bin/tests/system/enginepkcs11/setup.sh b/bin/tests/system/enginepkcs11/setup.sh index 59db124664..54bd956820 100644 --- a/bin/tests/system/enginepkcs11/setup.sh +++ b/bin/tests/system/enginepkcs11/setup.sh @@ -25,6 +25,8 @@ PWD=$(pwd) copy_setports ns1/named.conf.in ns1/named.conf sed -e "s/@ENGINE_ARGS@/${ENGINE_ARG}/g" ns1/named.args +mkdir ns1/keys + keygen() { type="$1" bits="$2" @@ -115,6 +117,9 @@ for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \ echo_i "Add zone $alg.kasp to named.conf" cp $infile ${dir}/zone.${alg}.kasp.db + echo_i "Add zone $alg.split to named.conf" + cp $infile ${dir}/zone.${alg}.split.db + echo_i "Add zone $zone to named.conf" cat >>"${dir}/named.conf" < Date: Thu, 17 Mar 2022 10:03:02 +0100 Subject: [PATCH 19/37] Add CHANGES and release note for #1129 Newsworthy. --- CHANGES | 5 +++++ doc/notes/notes-current.rst | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/CHANGES b/CHANGES index d70f42d04c..b22184cf5e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +6329. [func] Add HSM support for dnssec-policy. You can now + configure keys with a key-store that allows you to + set the directory to store key files and to set a + PKCS #11 URI string. [GL #1129] + 6328. [doc] Update ZSK minimum lifetime documentation in ARM, also depends on signing delay. [GL #4510] diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 9867bbdc3a..9aa27c16ae 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -28,6 +28,11 @@ New Features - The statistics channel now includes counters that indicate the number of currently connected TCP IPv4/IPv6 clients. :gl:`#4425` +- Add HSM support to :any:`dnssec-policy`. You can now configure keys with a + ``key-store`` that allows you to set the directory to store the key files and + set a PKCS#11 URI string. The latter requires OpenSSL 3 and a valid PKCS#11 + provider to be configured for OpenSSL. :gl`#1129`. + Removed Features ~~~~~~~~~~~~~~~~ From 224a6a6cf845294042f66baf2978ae24f2cbdc34 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 17 Mar 2022 10:31:03 +0100 Subject: [PATCH 20/37] Add documentation for key-store Add grammar and statement sections to the ARM. Add a note about when changing dnssec-policy you should take into account the key directory paths. --- doc/arm/reference.rst | 65 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 69d632802d..6989c4fab7 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -407,6 +407,9 @@ The following blocks are supported: :namedconf:ref:`key` Specifies key information for use in authentication and authorization using TSIG. + :any:``key-store`` + Describes a DNSSEC key store. See :ref:`key-store Grammar ` for details. + :any:`logging` Specifies what information the server logs and where the log messages are sent. @@ -592,6 +595,42 @@ matching this name, algorithm, and secret. The ``secret_string`` is the secret to be used by the algorithm, and is treated as a Base64-encoded string. +.. _key_store_grammar: + +:any:`key-store` Block Grammar +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. namedconf:statement:: key-store + :tags: dnssec + :short: Configures a DNSSEC key store. + +.. _key_store_statement: + +``key-store`` Block Definition and Usage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``key-store`` statement defines how DNSSEC keys should be stored. + +There is one built-in key store named ``key-directory``. Configuring +keys to use ``key-store "key-directory"`` is identical to using +``key-directory``. + +The following options can be specified in a :any:`key-store` statement: + +.. directory + + The ``directory`` specifies where key files for this key should be stored. + This is similar to using the zone's ``key-directory``. + +.. namedconf:statement:: uri + :tags: dnssec, pkcs11 + + The ``uri`` is a string that specifies a PKCS#11 URI Scheme (defined in + :rfc:`7512`). When set, ``named`` will try to create keys inside the + corresponding PKCS#11 token. This requires BIND to be built with OpenSSL 3, + and have a PKCS#11 provider configured. + +.. _logging_grammar: + :any:`logging` Block Grammar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. namedconf:statement:: logging @@ -6282,7 +6321,10 @@ The following options can be specified in a :any:`dnssec-policy` statement: This behavior is enabled by default. -:any:`keys` +.. keys + :tags: dnssec + :short: Specifies the type of keys to be used for DNSSEC signing. + This is a list specifying the algorithms and roles to use when generating keys and signing the zone. Entries in this list do not represent specific DNSSEC keys, which may be changed on a regular @@ -6298,7 +6340,7 @@ The following options can be specified in a :any:`dnssec-policy` statement: keys { ksk key-directory lifetime unlimited algorithm rsasha256 2048; zsk lifetime 30d algorithm 8; - csk lifetime P6MT12H3M15S algorithm ecdsa256; + csk key-store "hsm" lifetime P6MT12H3M15S algorithm ecdsa256; }; This example specifies that three keys should be used in the zone. @@ -6311,9 +6353,15 @@ The following options can be specified in a :any:`dnssec-policy` statement: used to sign all RRsets. An optional second token determines where the key is stored. - Currently, keys can only be stored in the configured - :any:`key-directory`. This token may be used in the future to store - keys in hardware security modules or separate directories. + The two available options are ``key-store `` and + ``key-directory``. + + When using ``key-store``, the referenced :any:`key-store` describes + how the key should be be stored. This can be as a file, or it can be + inside a PKCS#11 token. + + When using ``key-directory``, the key is stored in the zone's + configured :any:`key-directory`. This is also the default. The ``lifetime`` parameter specifies how long a key may be used before rolling over. For convenience, TTL-style time-unit suffixes @@ -6346,6 +6394,13 @@ The following options can be specified in a :any:`dnssec-policy` statement: Each KSK/ZSK pair must have the same algorithm. A CSK combines the functionality of a ZSK and a KSK. +.. note:: When changing the ``key-directory`` or the ``key-store``, BIND will + be unable to find existing key files. Make sure you copy key files to the + new directory before changing the path used in the configuration file. + This is also true when changing to a built-in policy, for example to + ``insecure``. In this specific case you should move the existing key files + to the zone's ``key-directory`` from the new configuration. + .. namedconf:statement:: purge-keys :tags: dnssec :short: Specifies the amount of time after which DNSSEC keys that have been deleted from the zone can be removed from disk. From b0f14a604d4c5681b0227305b4e1357554591935 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 13 Apr 2023 11:17:43 +0200 Subject: [PATCH 21/37] dnssec-keygen: -K keydir takes priority When using dnssec-policy with dnssec-keygen in combination with setting the key-directory on the command line, the commandline argument takes priority over the key-directory from the default named.conf. --- bin/dnssec/dnssec-keygen.c | 9 +++++++-- bin/tests/system/kasp/tests.sh | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index 9f9c3b5b08..c3b98ce0a5 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -256,7 +256,7 @@ progress(int p) { static void kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, - const char *engine, dns_kasp_t **kaspp) { + const char *keydir, const char *engine, dns_kasp_t **kaspp) { isc_result_t result = ISC_R_NOTFOUND; const cfg_listelt_t *element; const cfg_obj_t *kasps = NULL; @@ -289,6 +289,10 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, ks = NULL; (void)cfg_keystore_fromconfig(NULL, mctx, lctx, engine, &kslist, &ks); INSIST(ks != NULL); + if (keydir != NULL) { + /* '-K keydir' takes priority */ + dns_keystore_setdirectory(ks, keydir); + } dns_keystore_detach(&ks); (void)cfg_map_get(config, "dnssec-policy", &kasps); @@ -1328,7 +1332,8 @@ main(int argc, char **argv) { ctx.policy, ctx.configfile); } - kasp_from_conf(config, mctx, ctx.policy, engine, &kasp); + kasp_from_conf(config, mctx, ctx.policy, ctx.directory, + engine, &kasp); if (kasp == NULL) { fatal("failed to load dnssec-policy '%s'", ctx.policy); diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index e0eb143385..b6c80b8311 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -97,6 +97,7 @@ set_zonesigning "KEY4" "yes" lines=$(get_keyids "$DIR" "$ZONE" | wc -l) test "$lines" -eq $NUM_KEYS || log_error "bad number of key ids" +status=$((status + ret)) ids=$(get_keyids "$DIR" "$ZONE") for id in $ids; do From 18b566cceaecc00966dde6cdf94b56e12c182686 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 20 Jun 2023 15:44:17 +0200 Subject: [PATCH 22/37] Refactor findzonekeys Move dns_dnssec_findzonekeys from the dnssec.{c,h} source code to zone.{c,h} (the header file already commented that this should be done inside dns_zone_t). Alter the function in such a way, that keys are searched for in the key stores if a 'dnssec-policy' (kasp) is attached to the zone, otherwise keep using the zone's key-directory. --- bin/named/server.c | 13 +- bin/tests/system/multisigner/tests.sh | 8 +- lib/dns/dnssec.c | 171 ----------------- lib/dns/include/dns/dnssec.h | 14 -- lib/dns/include/dns/zone.h | 22 ++- lib/dns/zone.c | 255 ++++++++++++++++++++++++-- lib/dns/zone_p.h | 5 - tests/dns/sigs_test.c | 4 +- 8 files changed, 268 insertions(+), 224 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 304e1f3e2e..64fbd5dc99 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -2793,8 +2793,8 @@ catz_addmodzone_cb(void *arg) { result = configure_zone(cfg->config, zoneobj, cfg->vconfig, cz->view, &cz->cbd->server->viewlist, &cz->cbd->server->kasplist, - &cz->cbd->server->keystorelist, - cfg->actx, true, false, cz->mod); + &cz->cbd->server->keystorelist, cfg->actx, true, + false, cz->mod); dns_view_freeze(cz->view); isc_loopmgr_resume(named_g_loopmgr); @@ -9137,11 +9137,10 @@ load_configuration(const char *filename, named_server_t *server, goto cleanup_cachelist; } - result = configure_view(view, &viewlist, config, vconfig, - &cachelist, &server->kasplist, - &server->keystorelist, bindkeys, - named_g_mctx, named_g_aclconfctx, - false); + result = configure_view( + view, &viewlist, config, vconfig, &cachelist, + &server->kasplist, &server->keystorelist, bindkeys, + named_g_mctx, named_g_aclconfctx, false); if (result != ISC_R_SUCCESS) { dns_view_detach(&view); goto cleanup_cachelist; diff --git a/bin/tests/system/multisigner/tests.sh b/bin/tests/system/multisigner/tests.sh index 36d6252902..abe19ff215 100644 --- a/bin/tests/system/multisigner/tests.sh +++ b/bin/tests/system/multisigner/tests.sh @@ -147,7 +147,7 @@ status=$((status + ret)) n=$((n + 1)) echo_i "make sure we did not try to sign with the keys added with nsupdate for zone ${ZONE} ($n)" ret=0 -grep "dns_dnssec_findzonekeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1 +grep "dns_zone_findkeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1 test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) # Verify again. @@ -176,7 +176,7 @@ status=$((status + ret)) n=$((n + 1)) echo_i "make sure we did not try to sign with the keys added with nsupdate for zone ${ZONE} ($n)" ret=0 -grep "dns_dnssec_findzonekeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1 +grep "dns_zone_findkeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1 test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) # Verify again. @@ -521,7 +521,7 @@ test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) dnssec_verify no_dnssec_in_journal -grep "dns_dnssec_findzonekeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1 +grep "dns_zone_findkeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1 test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) # NS4 @@ -534,7 +534,7 @@ test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) dnssec_verify no_dnssec_in_journal -grep "dns_dnssec_findzonekeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1 +grep "dns_zone_findkeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1 test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 90234daa27..c1b1beedfa 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -759,177 +759,6 @@ syncdelete(dst_key_t *key, isc_stdtime_t now) { #define is_zone_key(key) \ ((dst_key_flags(key) & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE) -isc_result_t -dns_dnssec_findzonekeys(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node, - const dns_name_t *name, const char *directory, - isc_stdtime_t now, isc_mem_t *mctx, - unsigned int maxkeys, dst_key_t **keys, - unsigned int *nkeys) { - dns_rdataset_t rdataset; - dns_rdata_t rdata = DNS_RDATA_INIT; - isc_result_t result; - dst_key_t *pubkey = NULL; - unsigned int count = 0; - - REQUIRE(nkeys != NULL); - REQUIRE(keys != NULL); - - *nkeys = 0; - memset(keys, 0, sizeof(*keys) * maxkeys); - dns_rdataset_init(&rdataset); - RETERR(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0, - &rdataset, NULL)); - RETERR(dns_rdataset_first(&rdataset)); - while (result == ISC_R_SUCCESS && count < maxkeys) { - pubkey = NULL; - dns_rdataset_current(&rdataset, &rdata); - RETERR(dns_dnssec_keyfromrdata(name, &rdata, mctx, &pubkey)); - dst_key_setttl(pubkey, rdataset.ttl); - - if (!is_zone_key(pubkey) || - (dst_key_flags(pubkey) & DNS_KEYTYPE_NOAUTH) != 0) - { - goto next; - } - /* Corrupted .key file? */ - if (!dns_name_equal(name, dst_key_name(pubkey))) { - goto next; - } - keys[count] = NULL; - result = dst_key_fromfile( - dst_key_name(pubkey), dst_key_id(pubkey), - dst_key_alg(pubkey), - DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE, - directory, mctx, &keys[count]); - - /* - * If the key was revoked and the private file - * doesn't exist, maybe it was revoked internally - * by named. Try loading the unrevoked version. - */ - if (result == ISC_R_FILENOTFOUND) { - uint32_t flags; - flags = dst_key_flags(pubkey); - if ((flags & DNS_KEYFLAG_REVOKE) != 0) { - dst_key_setflags(pubkey, - flags & ~DNS_KEYFLAG_REVOKE); - result = dst_key_fromfile( - dst_key_name(pubkey), - dst_key_id(pubkey), dst_key_alg(pubkey), - DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | - DST_TYPE_STATE, - directory, mctx, &keys[count]); - if (result == ISC_R_SUCCESS && - dst_key_pubcompare(pubkey, keys[count], - false)) - { - dst_key_setflags(keys[count], flags); - } - dst_key_setflags(pubkey, flags); - } - } - - if (result != ISC_R_SUCCESS) { - char filename[DNS_NAME_FORMATSIZE + - DNS_SECALG_FORMATSIZE + - sizeof("key file for //65535")]; - isc_result_t result2; - isc_buffer_t buf; - - isc_buffer_init(&buf, filename, NAME_MAX); - result2 = dst_key_getfilename( - dst_key_name(pubkey), dst_key_id(pubkey), - dst_key_alg(pubkey), - (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | - DST_TYPE_STATE), - directory, mctx, &buf); - if (result2 != ISC_R_SUCCESS) { - char namebuf[DNS_NAME_FORMATSIZE]; - char algbuf[DNS_SECALG_FORMATSIZE]; - - dns_name_format(dst_key_name(pubkey), namebuf, - sizeof(namebuf)); - dns_secalg_format(dst_key_alg(pubkey), algbuf, - sizeof(algbuf)); - snprintf(filename, sizeof(filename) - 1, - "key file for %s/%s/%d", namebuf, - algbuf, dst_key_id(pubkey)); - } - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, - "dns_dnssec_findzonekeys: error " - "reading %s: %s", - filename, isc_result_totext(result)); - } - - if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) { - keys[count] = pubkey; - pubkey = NULL; - count++; - goto next; - } - - if (result != ISC_R_SUCCESS) { - goto failure; - } - - /* - * If a key is marked inactive, skip it - */ - if (!dns_dnssec_keyactive(keys[count], now)) { - dst_key_setinactive(pubkey, true); - dst_key_free(&keys[count]); - keys[count] = pubkey; - pubkey = NULL; - count++; - goto next; - } - - /* - * Whatever the key's default TTL may have - * been, the rdataset TTL takes priority. - */ - dst_key_setttl(keys[count], rdataset.ttl); - - if ((dst_key_flags(keys[count]) & DNS_KEYTYPE_NOAUTH) != 0) { - /* We should never get here. */ - dst_key_free(&keys[count]); - goto next; - } - count++; - next: - if (pubkey != NULL) { - dst_key_free(&pubkey); - } - dns_rdata_reset(&rdata); - result = dns_rdataset_next(&rdataset); - } - if (result != ISC_R_NOMORE) { - goto failure; - } - if (count == 0) { - result = ISC_R_NOTFOUND; - } else { - result = ISC_R_SUCCESS; - } - -failure: - if (dns_rdataset_isassociated(&rdataset)) { - dns_rdataset_disassociate(&rdataset); - } - if (pubkey != NULL) { - dst_key_free(&pubkey); - } - if (result != ISC_R_SUCCESS) { - while (count > 0) { - dst_key_free(&keys[--count]); - } - } - *nkeys = count; - return (result); -} - isc_result_t dns_dnssec_signmessage(dns_message_t *msg, dst_key_t *key) { dns_rdata_sig_t sig; /* SIG(0) */ diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h index b9bdffc681..7a6c5b5bc9 100644 --- a/lib/dns/include/dns/dnssec.h +++ b/lib/dns/include/dns/dnssec.h @@ -177,20 +177,6 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, *\li DST_R_* */ -/*@{*/ -isc_result_t -dns_dnssec_findzonekeys(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node, - const dns_name_t *name, const char *directory, - isc_stdtime_t now, isc_mem_t *mctx, - unsigned int maxkeys, dst_key_t **keys, - unsigned int *nkeys); - -/*%< - * Finds a set of zone keys. - * XXX temporary - this should be handled in dns_zone_t. - */ -/*@}*/ - bool dns_dnssec_keyactive(dst_key_t *key, isc_stdtime_t now); /*%< diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index a463fee162..d1e589f445 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1644,7 +1644,7 @@ dns_zone_getkeystores(dns_zone_t *zone); isc_result_t dns_zone_getdnsseckeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_t now, dns_dnsseckeylist_t *keys); -/*% +/*%< * Find DNSSEC keys used for signing with dnssec-policy. Load these keys * into 'keys'. * @@ -1657,6 +1657,26 @@ dns_zone_getdnsseckeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, *\li Error */ +isc_result_t +dns_zone_findkeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + isc_stdtime_t now, isc_mem_t *mctx, unsigned int maxkeys, + dst_key_t **keys, unsigned int *nkeys); +/*%< + * Finds a set of zone keys. Searches in the applicable key stores for the + * given 'zone' if there is a dnssec-policy attached, otherwise it looks up + * the keys in the zone's key-directory. The found keys are loaded into 'keys'. + * + * Requires: + *\li 'zone' to be a valid initialised zone. + *\li 'mctx' is not NULL. + *\li 'keys' is not NULL and has enough space form 'nkeys' keys. + *\li 'nkeys' is not NULL. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li Error + */ + void dns_zonemgr_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_nm_t *netmgr, dns_zonemgr_t **zmgrp); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 428dfd94b9..4d9fa86574 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -218,6 +218,13 @@ typedef struct dns_include dns_include_t; #define ZONEDB_LOCK(l, t) RWLOCK((l), (t)) #define ZONEDB_UNLOCK(l, t) RWUNLOCK((l), (t)) +#define RETERR(x) \ + do { \ + result = (x); \ + if (result != ISC_R_SUCCESS) \ + goto failure; \ + } while (0) + #ifdef ENABLE_AFL extern bool dns_fuzzing_resolver; #endif /* ifdef ENABLE_AFL */ @@ -6057,6 +6064,212 @@ was_dumping(dns_zone_t *zone) { return (false); } +static isc_result_t +keyfromfile(dns_zone_t *zone, dst_key_t *pubkey, isc_mem_t *mctx, + dst_key_t **key) { + const char *directory = dns_zone_getkeydirectory(zone); + dns_kasp_t *kasp = dns_zone_getkasp(zone); + dst_key_t *foundkey = NULL; + isc_result_t result = ISC_R_NOTFOUND; + + if (kasp == NULL || (strcmp(dns_kasp_getname(kasp), "none") == 0) || + (strcmp(dns_kasp_getname(kasp), "insecure") == 0)) + { + result = dst_key_fromfile( + dst_key_name(pubkey), dst_key_id(pubkey), + dst_key_alg(pubkey), + (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE), + directory, mctx, &foundkey); + } else { + for (dns_kasp_key_t *kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); + kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) + { + dns_keystore_t *ks = dns_kasp_key_keystore(kkey); + if (ks == NULL || + strcmp(dns_keystore_name(ks), "key-directory") == 0) + { + directory = dns_zone_getkeydirectory(zone); + } else { + directory = dns_keystore_directory(ks); + } + + result = dst_key_fromfile( + dst_key_name(pubkey), dst_key_id(pubkey), + dst_key_alg(pubkey), + (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | + DST_TYPE_STATE), + directory, mctx, &foundkey); + if (result == ISC_R_SUCCESS) { + break; + } + } + } + + *key = foundkey; + return (result); +} + +#define is_zone_key(key) \ + ((dst_key_flags(key) & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE) + +static isc_result_t +findzonekeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + dns_dbnode_t *node, const dns_name_t *name, isc_stdtime_t now, + isc_mem_t *mctx, unsigned int maxkeys, dst_key_t **keys, + unsigned int *nkeys) { + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t result; + dst_key_t *pubkey = NULL; + unsigned int count = 0; + + *nkeys = 0; + memset(keys, 0, sizeof(*keys) * maxkeys); + dns_rdataset_init(&rdataset); + RETERR(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0, + &rdataset, NULL)); + RETERR(dns_rdataset_first(&rdataset)); + while (result == ISC_R_SUCCESS && count < maxkeys) { + pubkey = NULL; + dns_rdataset_current(&rdataset, &rdata); + RETERR(dns_dnssec_keyfromrdata(name, &rdata, mctx, &pubkey)); + dst_key_setttl(pubkey, rdataset.ttl); + + if (!is_zone_key(pubkey) || + (dst_key_flags(pubkey) & DNS_KEYTYPE_NOAUTH) != 0) + { + goto next; + } + /* Corrupted .key file? */ + if (!dns_name_equal(name, dst_key_name(pubkey))) { + goto next; + } + keys[count] = NULL; + result = keyfromfile(zone, pubkey, mctx, &keys[count]); + + /* + * If the key was revoked and the private file + * doesn't exist, maybe it was revoked internally + * by named. Try loading the unrevoked version. + */ + if (result == ISC_R_FILENOTFOUND) { + uint32_t flags; + flags = dst_key_flags(pubkey); + if ((flags & DNS_KEYFLAG_REVOKE) != 0) { + dst_key_setflags(pubkey, + flags & ~DNS_KEYFLAG_REVOKE); + result = keyfromfile(zone, pubkey, mctx, + &keys[count]); + if (result == ISC_R_SUCCESS && + dst_key_pubcompare(pubkey, keys[count], + false)) + { + dst_key_setflags(keys[count], flags); + } + dst_key_setflags(pubkey, flags); + } + } + + if (result != ISC_R_SUCCESS) { + char filename[DNS_NAME_FORMATSIZE + + DNS_SECALG_FORMATSIZE + + sizeof("key file for //65535")]; + isc_result_t result2; + isc_buffer_t buf; + + isc_buffer_init(&buf, filename, sizeof(filename)); + result2 = dst_key_getfilename( + dst_key_name(pubkey), dst_key_id(pubkey), + dst_key_alg(pubkey), + (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | + DST_TYPE_STATE), + NULL, mctx, &buf); + if (result2 != ISC_R_SUCCESS) { + char namebuf[DNS_NAME_FORMATSIZE]; + char algbuf[DNS_SECALG_FORMATSIZE]; + + dns_name_format(dst_key_name(pubkey), namebuf, + sizeof(namebuf)); + dns_secalg_format(dst_key_alg(pubkey), algbuf, + sizeof(algbuf)); + snprintf(filename, sizeof(filename) - 1, + "key file for %s/%s/%d", namebuf, + algbuf, dst_key_id(pubkey)); + } + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, + "dns_zone_findkeys: error reading %s: %s", + filename, isc_result_totext(result)); + } + + if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) { + keys[count] = pubkey; + pubkey = NULL; + count++; + goto next; + } + + if (result != ISC_R_SUCCESS) { + goto failure; + } + + /* + * If a key is marked inactive, skip it + */ + if (!dns_dnssec_keyactive(keys[count], now)) { + dst_key_setinactive(pubkey, true); + dst_key_free(&keys[count]); + keys[count] = pubkey; + pubkey = NULL; + count++; + goto next; + } + + /* + * Whatever the key's default TTL may have + * been, the rdataset TTL takes priority. + */ + dst_key_setttl(keys[count], rdataset.ttl); + + if ((dst_key_flags(keys[count]) & DNS_KEYTYPE_NOAUTH) != 0) { + /* We should never get here. */ + dst_key_free(&keys[count]); + goto next; + } + count++; + next: + if (pubkey != NULL) { + dst_key_free(&pubkey); + } + dns_rdata_reset(&rdata); + result = dns_rdataset_next(&rdataset); + } + if (result != ISC_R_NOMORE) { + goto failure; + } + if (count == 0) { + result = ISC_R_NOTFOUND; + } else { + result = ISC_R_SUCCESS; + } + +failure: + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + if (pubkey != NULL) { + dst_key_free(&pubkey); + } + if (result != ISC_R_SUCCESS) { + while (count > 0) { + dst_key_free(&keys[--count]); + } + } + *nkeys = count; + return (result); +} + /*% * Find up to 'maxkeys' DNSSEC keys used for signing version 'ver' of database * 'db' for zone 'zone' in its key directory, then load these keys into 'keys'. @@ -6064,21 +6277,23 @@ was_dumping(dns_zone_t *zone) { * 'now'. Store the number of keys found in 'nkeys'. */ isc_result_t -dns__zone_findkeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, - isc_stdtime_t now, isc_mem_t *mctx, unsigned int maxkeys, - dst_key_t **keys, unsigned int *nkeys) { +dns_zone_findkeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + isc_stdtime_t now, isc_mem_t *mctx, unsigned int maxkeys, + dst_key_t **keys, unsigned int *nkeys) { isc_result_t result; dns_dbnode_t *node = NULL; - const char *directory = dns_zone_getkeydirectory(zone); + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(mctx != NULL); + REQUIRE(nkeys != NULL); + REQUIRE(keys != NULL); CHECK(dns_db_findnode(db, dns_db_origin(db), false, &node)); - memset(keys, 0, sizeof(*keys) * maxkeys); dns_zone_lock_keyfiles(zone); - result = dns_dnssec_findzonekeys(db, ver, node, dns_db_origin(db), - directory, now, mctx, maxkeys, keys, - nkeys); + result = findzonekeys(zone, db, ver, node, dns_db_origin(db), now, mctx, + maxkeys, keys, nkeys); dns_zone_unlock_keyfiles(zone); @@ -6752,11 +6967,11 @@ zone_resigninc(dns_zone_t *zone) { now = isc_stdtime_now(); - result = dns__zone_findkeys(zone, db, version, now, zone->mctx, - DNS_MAXZONEKEYS, zone_keys, &nkeys); + result = dns_zone_findkeys(zone, db, version, now, zone->mctx, + DNS_MAXZONEKEYS, zone_keys, &nkeys); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, - "zone_resigninc:dns__zone_findkeys -> %s", + "zone_resigninc:dns_zone_findkeys -> %s", isc_result_totext(result)); goto failure; } @@ -7987,11 +8202,11 @@ zone_nsec3chain(dns_zone_t *zone) { now = isc_stdtime_now(); - result = dns__zone_findkeys(zone, db, version, now, zone->mctx, - DNS_MAXZONEKEYS, zone_keys, &nkeys); + result = dns_zone_findkeys(zone, db, version, now, zone->mctx, + DNS_MAXZONEKEYS, zone_keys, &nkeys); if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, - "zone_nsec3chain:dns__zone_findkeys -> %s", + "zone_nsec3chain:dns_zone_findkeys -> %s", isc_result_totext(result)); goto failure; } @@ -9072,11 +9287,11 @@ zone_sign(dns_zone_t *zone) { now = isc_stdtime_now(); - result = dns__zone_findkeys(zone, db, version, now, zone->mctx, - DNS_MAXZONEKEYS, zone_keys, &nkeys); + result = dns_zone_findkeys(zone, db, version, now, zone->mctx, + DNS_MAXZONEKEYS, zone_keys, &nkeys); if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, - "zone_sign:dns__zone_findkeys -> %s", + "zone_sign:dns_zone_findkeys -> %s", isc_result_totext(result)); goto cleanup; } @@ -20139,11 +20354,11 @@ sign_apex(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dst_key_t *zone_keys[DNS_MAXZONEKEYS]; unsigned int nkeys = 0, i; - result = dns__zone_findkeys(zone, db, ver, now, zone->mctx, - DNS_MAXZONEKEYS, zone_keys, &nkeys); + result = dns_zone_findkeys(zone, db, ver, now, zone->mctx, + DNS_MAXZONEKEYS, zone_keys, &nkeys); if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, - "sign_apex:dns__zone_findkeys -> %s", + "sign_apex:dns_zone_findkeys -> %s", isc_result_totext(result)); return (result); } diff --git a/lib/dns/zone_p.h b/lib/dns/zone_p.h index fcbd3711c2..16ee0e522a 100644 --- a/lib/dns/zone_p.h +++ b/lib/dns/zone_p.h @@ -29,11 +29,6 @@ typedef struct { bool offline; } dns__zonediff_t; -isc_result_t -dns__zone_findkeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, - isc_stdtime_t now, isc_mem_t *mctx, unsigned int maxkeys, - dst_key_t **keys, unsigned int *nkeys); - isc_result_t dns__zone_updatesigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version, dst_key_t *zone_keys[], unsigned int nkeys, diff --git a/tests/dns/sigs_test.c b/tests/dns/sigs_test.c index 94d94fbb39..0910504003 100644 --- a/tests/dns/sigs_test.c +++ b/tests/dns/sigs_test.c @@ -315,8 +315,8 @@ ISC_RUN_TEST_IMPL(updatesigs_next) { result = dns_zone_setkeydirectory(zone, TESTS_DIR "/testkeys"); assert_int_equal(result, ISC_R_SUCCESS); - result = dns__zone_findkeys(zone, db, NULL, now, mctx, DNS_MAXZONEKEYS, - zone_keys, &nkeys); + result = dns_zone_findkeys(zone, db, NULL, now, mctx, DNS_MAXZONEKEYS, + zone_keys, &nkeys); assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(nkeys, 2); From 3dff3eac0aec61ba11afd357220630a4d679b75b Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 29 Aug 2023 15:13:22 +0200 Subject: [PATCH 23/37] Fix tsan errors When working internally on the zone, we can access the zone's variables directly. --- lib/dns/zone.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 4d9fa86574..2b7bab8316 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -6067,8 +6067,8 @@ was_dumping(dns_zone_t *zone) { static isc_result_t keyfromfile(dns_zone_t *zone, dst_key_t *pubkey, isc_mem_t *mctx, dst_key_t **key) { - const char *directory = dns_zone_getkeydirectory(zone); - dns_kasp_t *kasp = dns_zone_getkasp(zone); + const char *directory = zone->keydirectory; + dns_kasp_t *kasp = zone->kasp; dst_key_t *foundkey = NULL; isc_result_t result = ISC_R_NOTFOUND; @@ -6088,7 +6088,7 @@ keyfromfile(dns_zone_t *zone, dst_key_t *pubkey, isc_mem_t *mctx, if (ks == NULL || strcmp(dns_keystore_name(ks), "key-directory") == 0) { - directory = dns_zone_getkeydirectory(zone); + directory = zone->keydirectory; } else { directory = dns_keystore_directory(ks); } From 1e88bb018604e3d3c70af27aa1c075f9f653e5e0 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 6 Sep 2023 14:09:46 +0200 Subject: [PATCH 24/37] Create keys with PKCS#11 URI instead of object The pkcs11-provider has changed to take a PKCS#11 URI instead of an object identifier. Change the BIND 9 code accordingly to pass through the label instead of just the object identifier. See: https://github.com/latchset/pkcs11-provider/pull/284 --- configure.ac | 1 + lib/dns/dst_api.c | 9 +++------ lib/dns/dst_internal.h | 1 - lib/dns/include/dst/dst.h | 2 +- lib/dns/keystore.c | 11 ++++++----- lib/dns/opensslecdsa_link.c | 35 +++++++++++++---------------------- lib/dns/opensslrsa_link.c | 37 ++++++++++++++----------------------- 7 files changed, 38 insertions(+), 58 deletions(-) diff --git a/configure.ac b/configure.ac index 3631772b14..92f40c17ac 100644 --- a/configure.ac +++ b/configure.ac @@ -644,6 +644,7 @@ AS_IF([test "$enable_doh" = "yes"], AM_CONDITIONAL([HAVE_LIBNGHTTP2], [test -n "$LIBNGHTTP2_LIBS"]) + # # flockfile is usually provided by pthreads # diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index 803214c096..90d0f8dfe1 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -1031,7 +1031,7 @@ dst_key_fromlabel(const dns_name_t *name, int alg, unsigned int flags, isc_result_t dst_key_generate(const dns_name_t *name, unsigned int alg, unsigned int bits, unsigned int param, unsigned int flags, unsigned int protocol, - dns_rdataclass_t rdclass, const char *object, isc_mem_t *mctx, + dns_rdataclass_t rdclass, const char *label, isc_mem_t *mctx, dst_key_t **keyp, void (*callback)(int)) { dst_key_t *key; isc_result_t ret; @@ -1046,8 +1046,8 @@ dst_key_generate(const dns_name_t *name, unsigned int alg, unsigned int bits, key = get_key_struct(name, alg, flags, protocol, bits, rdclass, 0, mctx); - if (object != NULL) { - key->object = isc_mem_strdup(mctx, object); + if (label != NULL) { + key->label = isc_mem_strdup(mctx, label); } if (bits == 0) { /*%< NULL KEY */ @@ -1408,9 +1408,6 @@ dst_key_free(dst_key_t **keyp) { if (key->label != NULL) { isc_mem_free(mctx, key->label); } - if (key->object != NULL) { - isc_mem_free(mctx, key->object); - } dns_name_free(key->key_name, mctx); isc_mem_put(mctx, key->key_name, sizeof(dns_name_t)); if (key->key_tkeytoken) { diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h index 7026e7ffae..a78b710738 100644 --- a/lib/dns/dst_internal.h +++ b/lib/dns/dst_internal.h @@ -94,7 +94,6 @@ struct dst_key { char *directory; /*%< key directory */ char *engine; /*%< engine name (HSM) */ char *label; /*%< engine label (HSM) */ - char *object; /*%< engine object (HSM) */ union { void *generic; dns_gss_ctx_id_t gssctx; diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index e4895e1932..3da59150ad 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -629,7 +629,7 @@ dst_key_fromlabel(const dns_name_t *name, int alg, unsigned int flags, isc_result_t dst_key_generate(const dns_name_t *name, unsigned int alg, unsigned int bits, unsigned int param, unsigned int flags, unsigned int protocol, - dns_rdataclass_t rdclass, const char *object, isc_mem_t *mctx, + dns_rdataclass_t rdclass, const char *label, isc_mem_t *mctx, dst_key_t **keyp, void (*callback)(int)); /*%< diff --git a/lib/dns/keystore.c b/lib/dns/keystore.c index c57c712a7a..d0637354b4 100644 --- a/lib/dns/keystore.c +++ b/lib/dns/keystore.c @@ -167,14 +167,18 @@ dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, char namebuf[DNS_NAME_FORMATSIZE]; char object[DNS_NAME_FORMATSIZE + 26]; - /* Generate the key */ + /* Create the PKCS11 URI */ isc_time_formatshorttimestamp(&now, timebuf, sizeof(timebuf)); dns_name_format(origin, namebuf, sizeof(namebuf)); snprintf(object, sizeof(object), "%s-%s-%s", namebuf, ksk ? "ksk" : "zsk", timebuf); + len = strlen(object) + strlen(uri) + 10; + label = isc_mem_get(mctx, len); + sprintf(label, "%s;object=%s;", uri, object); + /* Generate the key */ result = dst_key_generate(origin, alg, size, 0, flags, - DNS_KEYPROTO_DNSSEC, rdclass, object, + DNS_KEYPROTO_DNSSEC, rdclass, label, mctx, &key, NULL); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, @@ -187,9 +191,6 @@ dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, dst_key_free(&key); /* Retrieve generated key from label */ - len = strlen(object) + strlen(uri) + 10; - label = isc_mem_get(mctx, len); - sprintf(label, "%s;object=%s;", uri, object); result = dst_key_fromlabel( origin, alg, flags, DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, dns_keystore_engine(keystore), label, diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c index 4b8740b8d7..9ca9abad89 100644 --- a/lib/dns/opensslecdsa_link.c +++ b/lib/dns/opensslecdsa_link.c @@ -410,26 +410,17 @@ opensslecdsa_create_pkey(unsigned int key_alg, bool private, #if OPENSSL_VERSION_NUMBER >= 0x30000000L static isc_result_t -opensslecdsa_generate_pkey_with_object(int group_nid, const char *object, - EVP_PKEY **retkey) { +opensslecdsa_generate_pkey_with_uri(int group_nid, const char *label, + EVP_PKEY **retkey) { int status; isc_result_t ret; - unsigned char id[16]; - char *label = UNCONST(object); + char *uri = UNCONST(label); EVP_PKEY_CTX *ctx = NULL; - OSSL_PARAM params[3]; + OSSL_PARAM params[2]; /* Generate the key's parameters. */ - status = RAND_bytes(id, 16); - if (status != 1) { - DST_RET(dst__openssl_toresult2("RAND_bytes", - DST_R_OPENSSLFAILURE)); - } - - params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_key_label", label, - 0); - params[1] = OSSL_PARAM_construct_octet_string("pkcs11_key_id", id, 16); - params[2] = OSSL_PARAM_construct_end(); + params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_uri", uri, 0); + params[1] = OSSL_PARAM_construct_end(); ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "provider=pkcs11"); if (ctx == NULL) { @@ -476,7 +467,7 @@ err: } static isc_result_t -opensslecdsa_generate_pkey(unsigned int key_alg, const char *object, +opensslecdsa_generate_pkey(unsigned int key_alg, const char *label, EVP_PKEY **retkey) { isc_result_t ret; EVP_PKEY_CTX *ctx = NULL; @@ -484,9 +475,9 @@ opensslecdsa_generate_pkey(unsigned int key_alg, const char *object, int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); int status; - if (object != NULL) { - return (opensslecdsa_generate_pkey_with_object(group_nid, - object, retkey)); + if (label != NULL) { + return (opensslecdsa_generate_pkey_with_uri(group_nid, label, + retkey)); } /* Generate the key's parameters. */ @@ -570,14 +561,14 @@ opensslecdsa_extract_private_key(const dst_key_t *key, unsigned char *buf, #else static isc_result_t -opensslecdsa_generate_pkey(unsigned int key_alg, const char *object, +opensslecdsa_generate_pkey(unsigned int key_alg, const char *label, EVP_PKEY **retkey) { isc_result_t ret; EC_KEY *eckey = NULL; EVP_PKEY *pkey = NULL; int group_nid; - UNUSED(object); + UNUSED(label); group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); @@ -892,7 +883,7 @@ opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { UNUSED(unused); UNUSED(callback); - ret = opensslecdsa_generate_pkey(key->key_alg, key->object, &pkey); + ret = opensslecdsa_generate_pkey(key->key_alg, key->label, &pkey); if (ret != ISC_R_SUCCESS) { return (ret); } diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index 6d06c71f27..e1e804bbdc 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -366,14 +366,14 @@ progress_cb(int p, int n, BN_GENCB *cb) { } static isc_result_t -opensslrsa_generate_pkey(unsigned int key_size, const char *object, BIGNUM *e, +opensslrsa_generate_pkey(unsigned int key_size, const char *label, BIGNUM *e, void (*callback)(int), EVP_PKEY **retkey) { RSA *rsa = NULL; EVP_PKEY *pkey = NULL; BN_GENCB *cb = NULL; isc_result_t ret; - UNUSED(object); + UNUSED(label); rsa = RSA_new(); pkey = EVP_PKEY_new(); @@ -497,26 +497,17 @@ progress_cb(EVP_PKEY_CTX *ctx) { } static isc_result_t -opensslrsa_generate_pkey_with_object(size_t key_size, const char *object, - EVP_PKEY **retkey) { +opensslrsa_generate_pkey_with_uri(size_t key_size, const char *label, + EVP_PKEY **retkey) { EVP_PKEY_CTX *ctx = NULL; - OSSL_PARAM params[4]; - unsigned char id[16]; - char *label = UNCONST(object); + OSSL_PARAM params[3]; + char *uri = UNCONST(label); isc_result_t ret; int status; - status = RAND_bytes(id, 16); - if (status != 1) { - DST_RET(dst__openssl_toresult2("RAND_bytes", - DST_R_OPENSSLFAILURE)); - } - - params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_key_label", label, - 0); - params[1] = OSSL_PARAM_construct_octet_string("pkcs11_key_id", id, 16); - params[2] = OSSL_PARAM_construct_size_t("rsa_keygen_bits", &key_size); - params[3] = OSSL_PARAM_construct_end(); + params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_uri", uri, 0); + params[1] = OSSL_PARAM_construct_size_t("rsa_keygen_bits", &key_size); + params[2] = OSSL_PARAM_construct_end(); ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", "provider=pkcs11"); if (ctx == NULL) { @@ -549,14 +540,14 @@ err: } static isc_result_t -opensslrsa_generate_pkey(unsigned int key_size, const char *object, BIGNUM *e, +opensslrsa_generate_pkey(unsigned int key_size, const char *label, BIGNUM *e, void (*callback)(int), EVP_PKEY **retkey) { EVP_PKEY_CTX *ctx; isc_result_t ret; - if (object != NULL) { - return (opensslrsa_generate_pkey_with_object(key_size, object, - retkey)); + if (label != NULL) { + return (opensslrsa_generate_pkey_with_uri(key_size, label, + retkey)); } ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); @@ -731,7 +722,7 @@ opensslrsa_generate(dst_key_t *key, int exp, void (*callback)(int)) { BN_set_bit(e, 32); } - ret = opensslrsa_generate_pkey(key->key_size, key->object, e, callback, + ret = opensslrsa_generate_pkey(key->key_size, key->label, e, callback, &pkey); if (ret != ISC_R_SUCCESS) { goto err; From 62e7cc66d066bc2a7933f52dc08690f2db88be05 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 10 Oct 2023 15:18:11 +0200 Subject: [PATCH 25/37] Specify key usage to be digital signature If not set, the created keys allows signing plus decrypt which is bad practice. Setting the key usage explicitly will generate keys that allow only signing. --- lib/dns/opensslecdsa_link.c | 6 ++++-- lib/dns/opensslrsa_link.c | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c index 9ca9abad89..2c99650285 100644 --- a/lib/dns/opensslecdsa_link.c +++ b/lib/dns/opensslecdsa_link.c @@ -416,11 +416,13 @@ opensslecdsa_generate_pkey_with_uri(int group_nid, const char *label, isc_result_t ret; char *uri = UNCONST(label); EVP_PKEY_CTX *ctx = NULL; - OSSL_PARAM params[2]; + OSSL_PARAM params[3]; /* Generate the key's parameters. */ params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_uri", uri, 0); - params[1] = OSSL_PARAM_construct_end(); + params[1] = OSSL_PARAM_construct_utf8_string( + "pkcs11_key_usage", (char *)"digitalSignature", 0); + params[2] = OSSL_PARAM_construct_end(); ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "provider=pkcs11"); if (ctx == NULL) { diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index e1e804bbdc..6e26f8651b 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -500,14 +500,16 @@ static isc_result_t opensslrsa_generate_pkey_with_uri(size_t key_size, const char *label, EVP_PKEY **retkey) { EVP_PKEY_CTX *ctx = NULL; - OSSL_PARAM params[3]; + OSSL_PARAM params[4]; char *uri = UNCONST(label); isc_result_t ret; int status; params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_uri", uri, 0); - params[1] = OSSL_PARAM_construct_size_t("rsa_keygen_bits", &key_size); - params[2] = OSSL_PARAM_construct_end(); + params[1] = OSSL_PARAM_construct_utf8_string( + "pkcs11_key_usage", (char *)"digitalSignature", 0); + params[2] = OSSL_PARAM_construct_size_t("rsa_keygen_bits", &key_size); + params[3] = OSSL_PARAM_construct_end(); ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", "provider=pkcs11"); if (ctx == NULL) { From 2e9fd6d0c11d0648589da5779baeefb5b98e855e Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 10 Oct 2023 15:21:58 +0200 Subject: [PATCH 26/37] Only run pkcs11engine test if pkcs11-provider is available The bullseye and bookworm images are not set up with pkcs11-provider, so we need to add an additional prerequisite for running the pkcs11engine test. Check the path of OPENSSL_CONF. --- .gitlab-ci.yml | 2 +- bin/tests/system/enginepkcs11/prereq.sh | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 22e86f800e..99699f6a85 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -946,7 +946,7 @@ gcc:ossl3:sid:amd64: <<: *build_job system:gcc:ossl3:sid:amd64: - # Set up environment variables to run pkcs11-provider system tests + # Set up environment variables to run pkcs11-provider based system tests variables: OPENSSL_CONF: "/var/tmp/etc/openssl-provider.cnf" SOFTHSM2_CONF: "/var/tmp/softhsm2/softhsm2.conf" diff --git a/bin/tests/system/enginepkcs11/prereq.sh b/bin/tests/system/enginepkcs11/prereq.sh index c6caa0dc88..449d9f2058 100644 --- a/bin/tests/system/enginepkcs11/prereq.sh +++ b/bin/tests/system/enginepkcs11/prereq.sh @@ -13,6 +13,11 @@ . ../conf.sh +[ "prereq/var/tmp/etc/openssl-provider.cnf" -eq "prereq${OPENSSL_CONF}" ] || { + echo_i "skip: pkcs11-provider not enabled" + exit 255 +} + [ -n "${SOFTHSM2_CONF}" ] || { echo_i "skip: softhsm2 configuration not available" exit 255 From 750536f74d1d8e261c566b41c765dea5e420e610 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 16 Oct 2023 13:24:10 +0200 Subject: [PATCH 27/37] No longer need to get generated key from label The pkcs11-provider did not yet support getting X/Y coordinates on newly generated EC PKEY keys, thus we attempted to get the key from the label after it was generated in the keystore. This has been fixed in: https://github.com/latchset/pkcs11-provider/pull/293 Thus now we should be able to use the generated key structure immediately. --- lib/dns/keystore.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/lib/dns/keystore.c b/lib/dns/keystore.c index d0637354b4..f733310ab9 100644 --- a/lib/dns/keystore.c +++ b/lib/dns/keystore.c @@ -158,7 +158,6 @@ dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, uri = dns_keystore_pkcs11uri(keystore); if (uri != NULL) { - dst_key_t *key = NULL; char *label = NULL; size_t len; char timebuf[18]; @@ -179,29 +178,14 @@ dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, /* Generate the key */ result = dst_key_generate(origin, alg, size, 0, flags, DNS_KEYPROTO_DNSSEC, rdclass, label, - mctx, &key, NULL); - if (result != ISC_R_SUCCESS) { - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, - DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, - "keystore: failed to generate key " - "%s (ret=%d)", - object, result); - return (result); - } - dst_key_free(&key); - - /* Retrieve generated key from label */ - result = dst_key_fromlabel( - origin, alg, flags, DNS_KEYPROTO_DNSSEC, - dns_rdataclass_in, dns_keystore_engine(keystore), label, - NULL, mctx, &newkey); + mctx, &newkey, NULL); isc_mem_put(mctx, label, len); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, - "keystore: failed to access key " + "keystore: failed to generate key " "%s (ret=%d)", object, result); return (result); From 1ac02b0f1dc2a1d977670a4c61a44e88298ea76b Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 13 Nov 2023 14:40:59 +0100 Subject: [PATCH 28/37] The use of isc_dir_t in keymgr is not needed The internal keymgr used 'isc_dir_open(&dir)' and 'isc_dir_close(&dir)', but was not using the variable 'dir`, other than checking if the directory can be opened. Errors like these will be be caught already in the dst_api function calls. --- lib/dns/keymgr.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index d0059f59a3..32bbd5b9f6 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -2004,8 +2004,6 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, dns_dnsseckeylist_t newkeys; dns_kasp_key_t *kkey; dns_dnsseckey_t *newkey = NULL; - isc_dir_t dir; - bool dir_open = false; bool secure_to_insecure = false; int numkeys = 0; int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); @@ -2224,7 +2222,6 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, keymgr_update(keyring, kasp, now, nexttime, secure_to_insecure); /* Store key states and update hints. */ - isc_dir_init(&dir); for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; dkey = ISC_LIST_NEXT(dkey, link)) { @@ -2239,16 +2236,10 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, directory = "."; } - RETERR(isc_dir_open(&dir, directory)); - dir_open = true; - dns_dnssec_get_hints(dkey, now); RETERR(dst_key_tofile(dkey->key, options, directory)); dst_key_setmodified(dkey->key, false); - isc_dir_close(&dir); - dir_open = false; - if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) { continue; } @@ -2266,10 +2257,6 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, result = ISC_R_SUCCESS; failure: - if (dir_open) { - isc_dir_close(&dir); - } - if (result != ISC_R_SUCCESS) { while ((newkey = ISC_LIST_HEAD(newkeys)) != NULL) { ISC_LIST_UNLINK(newkeys, newkey, link); @@ -2295,7 +2282,6 @@ keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, dns_keytag_t id, unsigned int alg, bool check_id) { int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); const char *directory = NULL; - isc_dir_t dir; isc_result_t result; dns_dnsseckey_t *ksk_key = NULL; @@ -2362,22 +2348,16 @@ keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, } /* Store key state and update hints. */ - isc_dir_init(&dir); directory = dst_key_directory(ksk_key->key); if (directory == NULL) { directory = "."; } - result = isc_dir_open(&dir, directory); - if (result != ISC_R_SUCCESS) { - return (result); - } dns_dnssec_get_hints(ksk_key, now); result = dst_key_tofile(ksk_key->key, options, directory); if (result == ISC_R_SUCCESS) { dst_key_setmodified(ksk_key->key, false); } - isc_dir_close(&dir); return (result); } @@ -2605,7 +2585,6 @@ dns_keymgr_rollover(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, unsigned int algorithm) { int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); const char *directory = NULL; - isc_dir_t dir; isc_result_t result; dns_dnsseckey_t *key = NULL; isc_stdtime_t active, retire, prepub; @@ -2664,22 +2643,16 @@ dns_keymgr_rollover(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, dst_key_setnum(key->key, DST_NUM_LIFETIME, (retire - active)); /* Store key state and update hints. */ - isc_dir_init(&dir); directory = dst_key_directory(key->key); if (directory == NULL) { directory = "."; } - result = isc_dir_open(&dir, directory); - if (result != ISC_R_SUCCESS) { - return (result); - } dns_dnssec_get_hints(key, now); result = dst_key_tofile(key->key, options, directory); if (result == ISC_R_SUCCESS) { dst_key_setmodified(key->key, false); } - isc_dir_close(&dir); return (result); } From c59c2ac85fcf8a5da729798c7d54be6d8af64f78 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 16 Nov 2023 12:10:50 +0100 Subject: [PATCH 29/37] Minor fixes in enginepkcs11 system test - Shell function body should be in between curly braces. - Some erroneous '|| return 1' are replaced with '|| ret=1'. - Fix a variable name (was 'ret', should be '_ret'). - Clean up when setting up a new test. --- bin/tests/system/enginepkcs11/setup.sh | 2 ++ bin/tests/system/enginepkcs11/tests.sh | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/bin/tests/system/enginepkcs11/setup.sh b/bin/tests/system/enginepkcs11/setup.sh index 54bd956820..84d4d7edb0 100644 --- a/bin/tests/system/enginepkcs11/setup.sh +++ b/bin/tests/system/enginepkcs11/setup.sh @@ -16,6 +16,8 @@ set -e +$SHELL clean.sh + OPENSSL_CONF= softhsm2-util --init-token --free --pin 1234 --so-pin 1234 --label "softhsm2-enginepkcs11" | awk '/^The token has been initialized and is reassigned to slot/ { print $NF }' parse_openssl_config diff --git a/bin/tests/system/enginepkcs11/tests.sh b/bin/tests/system/enginepkcs11/tests.sh index 48b349e392..d9fe358475 100644 --- a/bin/tests/system/enginepkcs11/tests.sh +++ b/bin/tests/system/enginepkcs11/tests.sh @@ -23,9 +23,9 @@ status=0 ret=0 n=0 -dig_with_opts() ( +dig_with_opts() { $DIG +tcp +noadd +nosea +nostat +nocmd +dnssec -p "$PORT" "$@" -) +} check_keys() { _zone=$1 @@ -34,7 +34,7 @@ check_keys() { _status=0 _count=$(ls K*.key | grep "K${_zone}" | wc -l) - test "$_count" -eq "$_expect" || ret=1 + test "$_count" -eq "$_expect" || _ret=1 test "$_ret" -eq 0 || echo_i "failed (expected $_expect keys, got $_count)" _status=$((_status + _ret)) @@ -86,10 +86,10 @@ for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \ ret=0 echo_i "Test inline signing for $zone ($n)" dig_with_opts "$zone" @10.53.0.1 SOA >dig.out.soa.$zone.$n || ret=1 - awk '$4 == "RRSIG" { print $11 }' dig.out.soa.$zone.$n >dig.out.keyids.$zone.$n || return 1 + awk '$4 == "RRSIG" { print $11 }' dig.out.soa.$zone.$n >dig.out.keyids.$zone.$n || ret=1 numsigs=$(cat dig.out.keyids.$zone.$n | wc -l) - test $numsigs -eq 1 || return 1 - grep -w "$zskid1" dig.out.keyids.$zone.$n >/dev/null || return 1 + test $numsigs -eq 1 || ret=1 + grep -w "$zskid1" dig.out.keyids.$zone.$n >/dev/null || ret=1 test "$ret" -eq 0 || echo_i "failed (SOA RRset not signed with key $zskid1)" status=$((status + ret)) @@ -112,11 +112,11 @@ EOF n=$((n + 1)) ret=0 echo_i "Test DNSKEY response for $zone after inline signing ($n)" - _dig_dnskey() ( + _dig_dnskey() { dig_with_opts "$zone" @10.53.0.1 DNSKEY >dig.out.dnskey.$zone.$n || return 1 count=$(awk 'BEGIN { count = 0 } $4 == "DNSKEY" { count++ } END {print count}' dig.out.dnskey.$zone.$n) test $count -eq 3 - ) + } retry_quiet 10 _dig_dnskey || ret=1 test "$ret" -eq 0 || echo_i "failed (expected 3 DNSKEY records)" status=$((status + ret)) @@ -124,7 +124,7 @@ EOF n=$((n + 1)) ret=0 echo_i "Test SOA response for $zone after inline signing ($n)" - _dig_soa() ( + _dig_soa() { dig_with_opts "$zone" @10.53.0.1 SOA >dig.out.soa.$zone.$n || return 1 awk '$4 == "RRSIG" { print $11 }' dig.out.soa.$zone.$n >dig.out.keyids.$zone.$n || return 1 numsigs=$(cat dig.out.keyids.$zone.$n | wc -l) @@ -132,7 +132,7 @@ EOF grep -w "$zskid1" dig.out.keyids.$zone.$n >/dev/null || return 1 grep -w "$zskid2" dig.out.keyids.$zone.$n >/dev/null || return 1 return 0 - ) + } retry_quiet 10 _dig_soa || ret=1 test "$ret" -eq 0 || echo_i "failed (expected 2 SOA RRSIG records)" status=$((status + ret)) @@ -160,7 +160,7 @@ EOF n=$((n + 1)) ret=0 echo_i "Test DNSKEY response for $zone after inline signing (key signing) ($n)" - _dig_dnskey_ksk() ( + _dig_dnskey_ksk() { dig_with_opts "$zone" @10.53.0.1 DNSKEY >dig.out.dnskey.$zone.$n || return 1 count=$(awk 'BEGIN { count = 0 } $4 == "DNSKEY" { count++ } END {print count}' dig.out.dnskey.$zone.$n) test $count -eq 4 || return 1 @@ -170,7 +170,7 @@ EOF grep -w "$kskid1" dig.out.keyids.$zone.$n >/dev/null || return 1 grep -w "$kskid2" dig.out.keyids.$zone.$n >/dev/null || return 1 return 0 - ) + } retry_quiet 10 _dig_dnskey_ksk || ret=1 test "$ret" -eq 0 || echo_i "failed (expected 4 DNSKEY records, 2 KSK signatures)" status=$((status + ret)) From dd6cfb464f002ded4d1d05a52abccfad145c5b72 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 16 Nov 2023 15:24:19 +0100 Subject: [PATCH 30/37] Remove "error reading" grep from kasp system test This log may still occur if there is a DNSKEY in the unsigned zone. This may happen in a multi-signer setup for example. Ideally this should not log a warning, but that requires looking up keys a different way (by searching for key files only). However, that requires adapting a bunch of system tests, and is out of scope for now. --- bin/tests/system/kasp/tests.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index b6c80b8311..3c2484cc4d 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -510,7 +510,6 @@ n=$((n + 1)) echo_i "check if resigning the raw version of the zone is prevented for zone ${ZONE} ($n)" ret=0 grep "zone_resigninc: zone $ZONE/IN (unsigned): enter" $DIR/named.run && ret=1 -grep "error reading K$ZONE" $DIR/named.run && ret=1 test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) From 89cf3049d4923aba6fbc1183e22cf96740103c32 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 16 Nov 2023 15:37:34 +0100 Subject: [PATCH 31/37] Test dnssec-policy/hsm with multiple views Add test cases for zones in different views that are using PKCS#11 tokens to store its keys. If it is using the same DNSSEC policy, only one PKCS#11 token should be created and the same key should be used for the zone in both views. If it is using a different DNSSEC policy, multiple PKCS#11 token should be created and each view should use their respective key. --- bin/tests/system/enginepkcs11/clean.sh | 22 +-- .../system/enginepkcs11/ns2/named.args.in | 1 + .../system/enginepkcs11/ns2/named.conf.in | 57 +++++++ .../system/enginepkcs11/ns2/template.db.in | 23 +++ bin/tests/system/enginepkcs11/setup.sh | 156 +++++++++++++++++- bin/tests/system/enginepkcs11/tests.sh | 144 ++++++++++++++++ 6 files changed, 388 insertions(+), 15 deletions(-) create mode 100644 bin/tests/system/enginepkcs11/ns2/named.args.in create mode 100644 bin/tests/system/enginepkcs11/ns2/named.conf.in create mode 100644 bin/tests/system/enginepkcs11/ns2/template.db.in diff --git a/bin/tests/system/enginepkcs11/clean.sh b/bin/tests/system/enginepkcs11/clean.sh index c8b3c79d0d..5a24ebd3da 100644 --- a/bin/tests/system/enginepkcs11/clean.sh +++ b/bin/tests/system/enginepkcs11/clean.sh @@ -21,19 +21,21 @@ rm -f dsset-* rm -f keyfromlabel.err.* keyfromlabel.out.* rm -f pkcs11-tool.err.* pkcs11-tool.out.* rm -f signer.out.* +rm -f ns*/*.kskid1 ns*/*.kskid2 ns*/*.zskid1 ns/*.zskid2 +rm -f ns*/dig.out.* +rm -f ns*/K* +rm -f ns*/keygen.out.* +rm -f ns*/named.conf ns1/named.args ns1/named.run ns1/named.memstats +rm -f ns*/pin +rm -f ns*/update.cmd.* +rm -f ns*/update.log.* +rm -f ns*/verify.out.* +rm -f ns*/zone.*.jnl ns1/zone.*.jbk rm -f ns1/*.example.db ns1/*.example.db.signed rm -f ns1/*.kasp.db ns1/*.kasp.db.signed rm -f ns1/*.split.db ns1/*.split.db.signed -rm -f ns1/*.kskid1 ns1/*.kskid2 ns1/*.zskid1 ns1/*.zskid2 -rm -f ns1/dig.out.* -rm -f ns1/K* -rm -f ns1/keygen.out.* -rm -f ns1/named.conf ns1/named.args ns1/named.run ns1/named.memstats -rm -f ns1/pin -rm -f ns1/update.cmd.* -rm -f ns1/update.log.* -rm -f ns1/verify.out.* -rm -f ns1/zone.*.jnl ns1/zone.*.jbk +rm -f ns2/*.views.db ns1/*.views.db.signed rm -rf ./ns1/keys/ +rm -rf ./ns2/keys/ OPENSSL_CONF= softhsm2-util --delete-token --token "softhsm2-enginepkcs11" >/dev/null 2>&1 || echo_i "softhsm2-enginepkcs11 token not found for cleaning" diff --git a/bin/tests/system/enginepkcs11/ns2/named.args.in b/bin/tests/system/enginepkcs11/ns2/named.args.in new file mode 100644 index 0000000000..1d6beb9a9f --- /dev/null +++ b/bin/tests/system/enginepkcs11/ns2/named.args.in @@ -0,0 +1 @@ +@ENGINE_ARGS@ -D enginepkcs11-ns2 -m record -c named.conf -d 99 -U 4 -T maxcachesize=2097152 diff --git a/bin/tests/system/enginepkcs11/ns2/named.conf.in b/bin/tests/system/enginepkcs11/ns2/named.conf.in new file mode 100644 index 0000000000..262419e983 --- /dev/null +++ b/bin/tests/system/enginepkcs11/ns2/named.conf.in @@ -0,0 +1,57 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +controls { /* empty */ }; + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + recursion no; + dnssec-validation no; + notify no; +}; + +key "keyforview1" { + algorithm @DEFAULT_HMAC@; + secret "YPfMoAk6h+3iN8MDRQC004iSNHY="; +}; + +key "keyforview2" { + algorithm @DEFAULT_HMAC@; + secret "4xILSZQnuO1UKubXHkYUsvBRPu8="; +}; + +key-store "hsm" { + directory "."; + uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; +}; + +key-store "hsm2" { + directory "keys"; + uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; +}; + +key-store "pin" { + directory "."; + uri "pkcs11:token=softhsm2-enginepkcs11;pin-source=pin"; +}; + +key-store "disk" { + directory "keys"; +}; + diff --git a/bin/tests/system/enginepkcs11/ns2/template.db.in b/bin/tests/system/enginepkcs11/ns2/template.db.in new file mode 100644 index 0000000000..a140bff4ac --- /dev/null +++ b/bin/tests/system/enginepkcs11/ns2/template.db.in @@ -0,0 +1,23 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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. + +$TTL 300 ; 5 minutes +@ IN SOA ns root ( + 2000082401 ; serial + 1800 ; refresh (30 minutes) + 1800 ; retry (30 minutes) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.2 + +txt TXT "test" diff --git a/bin/tests/system/enginepkcs11/setup.sh b/bin/tests/system/enginepkcs11/setup.sh index 84d4d7edb0..59a2361e68 100644 --- a/bin/tests/system/enginepkcs11/setup.sh +++ b/bin/tests/system/enginepkcs11/setup.sh @@ -24,11 +24,6 @@ parse_openssl_config printf '%s' "${HSMPIN:-1234}" >ns1/pin PWD=$(pwd) -copy_setports ns1/named.conf.in ns1/named.conf -sed -e "s/@ENGINE_ARGS@/${ENGINE_ARG}/g" ns1/named.args - -mkdir ns1/keys - keygen() { type="$1" bits="$2" @@ -52,6 +47,11 @@ keyfromlabel() { } # Setup ns1. +copy_setports ns1/named.conf.in ns1/named.conf +sed -e "s/@ENGINE_ARGS@/${ENGINE_ARG}/g" ns1/named.args + +mkdir ns1/keys + dir="ns1" infile="${dir}/template.db.in" for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \ @@ -161,3 +161,149 @@ zone "${alg}.split" { EOF fi done + +# Setup ns2 (with views). +copy_setports ns2/named.conf.in ns2/named.conf +sed -e "s/@ENGINE_ARGS@/${ENGINE_ARG}/g" ns2/named.args + +mkdir ns2/keys + +dir="ns2" +infile="${dir}/template.db.in" +algtypebits="ecdsap256sha256:EC:prime256v1" +alg=$(echo "$algtypebits" | cut -f 1 -d :) +type=$(echo "$algtypebits" | cut -f 2 -d :) +bits=$(echo "$algtypebits" | cut -f 3 -d :) +tld="views" + +if $SHELL ../testcrypto.sh $alg; then + zone="$alg.$tld" + zonefile1="zone.$alg.$tld.view1.db" + zonefile2="zone.$alg.$tld.view2.db" + ret=0 + + echo_i "Generate keys $alg $type:$bits for zone $zone" + keygen $type $bits $zone enginepkcs11-zsk || ret=1 + keygen $type $bits $zone enginepkcs11-ksk || ret=1 + test "$ret" -eq 0 || exit 1 + + echo_i "Get ZSK $alg $zone $type:$bits" + zsk1=$(keyfromlabel $alg $zone enginepkcs11-zsk $dir) + test -z "$zsk1" && exit 1 + + echo_i "Get KSK $alg $zone $type:$bits" + ksk1=$(keyfromlabel $alg $zone enginepkcs11-ksk $dir -f KSK) + test -z "$ksk1" && exit 1 + + ( + cd $dir + zskid1=$(keyfile_to_key_id $zsk1) + kskid1=$(keyfile_to_key_id $ksk1) + echo "$zskid1" >$zone.zskid1 + echo "$kskid1" >$zone.kskid1 + ) + + echo_i "Sign zone with $ksk1 $zsk1" + cat "$infile" "${dir}/${ksk1}.key" "${dir}/${zsk1}.key" >"${dir}/${zonefile1}" + $SIGNER $ENGINE_ARG -K $dir -S -a -g -O full -o "$zone" "${dir}/${zonefile1}" >signer.out.view1.$zone || ret=1 + test "$ret" -eq 0 || exit 1 + + cat "$infile" "${dir}/${ksk1}.key" "${dir}/${zsk1}.key" >"${dir}/${zonefile2}" + $SIGNER $ENGINE_ARG -K $dir -S -a -g -O full -o "$zone" "${dir}/${zonefile2}" >signer.out.view2.$zone || ret=1 + test "$ret" -eq 0 || exit 1 + + echo_i "Generate successor keys $alg $type:$bits for zone $zone" + keygen $type $bits $zone enginepkcs11-zsk2 || ret=1 + keygen $type $bits $zone enginepkcs11-ksk2 || ret=1 + test "$ret" -eq 0 || exit 1 + + echo_i "Get ZSK $alg $id-$zone $type:$bits" + zsk2=$(keyfromlabel $alg $zone enginepkcs11-zsk2 $dir) + test -z "$zsk2" && exit 1 + + echo_i "Get KSK $alg $id-$zone $type:$bits" + ksk2=$(keyfromlabel $alg $zone enginepkcs11-ksk2 $dir -f KSK) + test -z "$ksk2" && exit 1 + + ( + cd $dir + zskid2=$(keyfile_to_key_id $zsk2) + kskid2=$(keyfile_to_key_id $ksk2) + echo "$zskid2" >$zone.zskid2 + echo "$kskid2" >$zone.kskid2 + cp "${zsk2}.key" "${zsk2}.zsk2" + cp "${ksk2}.key" "${ksk2}.ksk2" + ) + + echo_i "Add zone $alg.same-policy.$tld to named.conf" + cp $infile ${dir}/zone.${alg}.same-policy.view1.db + cp $infile ${dir}/zone.${alg}.same-policy.view2.db + + echo_i "Add zone zone-with.different-policy.$tld to named.conf" + cp $infile ${dir}/zone.zone-with.different-policy.view1.db + cp $infile ${dir}/zone.zone-with.different-policy.view2.db + + echo_i "Add zone $zone to named.conf" + cat >>"${dir}/named.conf" <verify.out.$zone.view1.$n 2>&1 || ret=1 + test "$ret" -eq 0 || echo_i "failed (dnssec-verify failed)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test zone signing was successful for $zone in view2 ($n)" + $VERIFY -z -o $zone "${zonefile2}" >verify.out.$zone.view2.$n 2>&1 || ret=1 + test "$ret" -eq 0 || echo_i "failed (dnssec-verify failed)" + status=$((status + ret)) + + # Test dnssec-policy signing with keys stored in engine. + zone="${alg}.same-policy.views" + + n=$((n + 1)) + ret=0 + echo_i "Test key generation was successful for $zone ($n)" + check_keys $zone 1 || ret=1 + status=$((status + ret)) + + _dig_inview() { + _qtype="$1" + _alg="$2" + _tsig="$DEFAULT_HMAC:$3:$4" + dig_with_opts "$zone" @10.53.0.2 $_qtype -y "$_tsig" >dig.out.$zone.$n || return 1 + awk -v cov="$_qtype" '$4 == "RRSIG" && $5 == cov { print $6 }' dig.out.$zone.$n >dig.out.alg.$zone.$n || return 1 + numsigs=$(cat dig.out.alg.$zone.$n | wc -l) + test $numsigs -eq 1 || return 1 + grep -w "$_alg" dig.out.alg.$zone.$n >/dev/null || return 1 + } + + n=$((n + 1)) + ret=0 + echo_i "Test SOA is signed for $zone in view1 ($n)" + VIEW1="YPfMoAk6h+3iN8MDRQC004iSNHY=" + retry_quiet 4 _dig_inview SOA 13 keyforview1 $VIEW1 || ret=1 + test "$ret" -eq 0 || echo_i "failed (SOA RRset not signed)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test DNSKEY is signed for $zone in view1 ($n)" + retry_quiet 4 _dig_inview DNSKEY 13 keyforview1 $VIEW1 || ret=1 + test "$ret" -eq 0 || echo_i "failed (DNSKEY RRset not signed)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test SOA is signed for $zone in view2 ($n)" + VIEW2="4xILSZQnuO1UKubXHkYUsvBRPu8=" + retry_quiet 4 _dig_inview SOA 13 keyforview2 $VIEW2 || ret=1 + test "$ret" -eq 0 || echo_i "failed (SOA RRset not signed)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test DNSKEY is signed for $zone in view2 ($n)" + retry_quiet 4 _dig_inview DNSKEY 13 keyforview2 $VIEW2 || ret=1 + test "$ret" -eq 0 || echo_i "failed (DNSKEY RRset not signed)" + status=$((status + ret)) + + # Now test zone in different views using a different dnssec-policy. + zone="zone-with.different-policy.views" + + n=$((n + 1)) + ret=0 + echo_i "Test key generation was successful for $zone in view1 ($n)" + # view1 + check_keys $zone 1 || ret=1 + status=$((status + ret)) + # view2 + echo_i "Test key generation was successful for $zone in view2 ($n)" + count=$(ls keys/K*.key | grep "K${zone}" | wc -l) + test "$count" -eq 1 || ret=1 + test "$ret" -eq 0 || echo_i "failed (expected 1 key, got $count)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test SOA is signed for $zone in view1 ($n)" + VIEW1="YPfMoAk6h+3iN8MDRQC004iSNHY=" + retry_quiet 4 _dig_inview SOA 13 keyforview1 $VIEW1 || ret=1 + test "$ret" -eq 0 || echo_i "failed (SOA RRset not signed)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test DNSKEY is signed for $zone in view1 ($n)" + retry_quiet 4 _dig_inview DNSKEY 13 keyforview1 $VIEW1 || ret=1 + test "$ret" -eq 0 || echo_i "failed (DNSKEY RRset not signed)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test SOA is signed for $zone in view2 ($n)" + VIEW2="4xILSZQnuO1UKubXHkYUsvBRPu8=" + retry_quiet 4 _dig_inview SOA 8 keyforview2 $VIEW2 || ret=1 + test "$ret" -eq 0 || echo_i "failed (SOA RRset not signed)" + status=$((status + ret)) + + n=$((n + 1)) + ret=0 + echo_i "Test DNSKEY is signed for $zone in view2 ($n)" + retry_quiet 4 _dig_inview DNSKEY 8 keyforview2 $VIEW2 || ret=1 + test "$ret" -eq 0 || echo_i "failed (DNSKEY RRset not signed)" + status=$((status + ret)) +fi + +# Go back to main test dir. +cd .. + n=$((n + 1)) ret=0 echo_i "Checking for assertion failure in pk11_numbits()" From 934d17255eeb412446a53ed54d288cd7bf9fa2d5 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 17 Nov 2023 10:45:05 +0100 Subject: [PATCH 32/37] Better PKCS#11 label creation When using the same PKCS#11 URI for a zone that uses different DNSSEC policies, the PKCS#11 label could collide, i.e. the same label could be used for different keys. Add the policy name to the label to make it more unique. Also, the zone name could contain characters that are interpreted as special characters when parsing the PKCS#11 URI string. Mangle the zone name through 'dns_name_tofilenametext()' to make it PKCS#11 safe. Move the creation to a separate function for clarity. Furthermore, add a log message whenever a PKCS#11 object has been successfully created. --- bin/dnssec/dnssec-keygen.c | 8 +-- lib/dns/include/dns/keystore.h | 7 +- lib/dns/keymgr.c | 18 ++--- lib/dns/keystore.c | 118 ++++++++++++++++++++++++++------- 4 files changed, 111 insertions(+), 40 deletions(-) diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index c3b98ce0a5..d770e41c94 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -697,10 +697,10 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { fprintf(stderr, "Generating key pair."); } - if (ctx->keystore != NULL) { - ret = dns_keystore_keygen(ctx->keystore, name, - ctx->rdclass, mctx, ctx->alg, - ctx->size, flags, &key); + if (ctx->keystore != NULL && ctx->policy != NULL) { + ret = dns_keystore_keygen( + ctx->keystore, name, ctx->policy, ctx->rdclass, + mctx, ctx->alg, ctx->size, flags, &key); } else if (!ctx->quiet && show_progress) { ret = dst_key_generate(name, ctx->alg, ctx->size, param, flags, ctx->protocol, diff --git a/lib/dns/include/dns/keystore.h b/lib/dns/include/dns/keystore.h index bd486e541e..6db1e9cbbb 100644 --- a/lib/dns/include/dns/keystore.h +++ b/lib/dns/include/dns/keystore.h @@ -197,8 +197,9 @@ dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri); isc_result_t dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, - dns_rdataclass_t rdclass, isc_mem_t *mctx, uint32_t alg, - int size, int flags, dst_key_t **dstkey); + const char *policy, dns_rdataclass_t rdclass, + isc_mem_t *mctx, uint32_t alg, int size, int flags, + dst_key_t **dstkey); /*%< * Create a DNSSEC key pair. Set keystore PKCS#11 URI. * @@ -208,6 +209,8 @@ dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, * *\li 'origin' is a valid DNS owner name. * + *\li 'policy' is the name of the DNSSEC policy. + * *\li 'mctx' is a valid memory context. * *\li 'dstkey' is not NULL and '*dstkey' is NULL. diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 32bbd5b9f6..05b26261c9 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -444,9 +444,9 @@ keymgr_keyid_conflict(dst_key_t *newkey, dns_dnsseckeylist_t *keys) { */ static isc_result_t keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin, - dns_rdataclass_t rdclass, isc_mem_t *mctx, const char *keydir, - dns_dnsseckeylist_t *keylist, dns_dnsseckeylist_t *newkeys, - dst_key_t **dst_key) { + dns_kasp_t *kasp, dns_rdataclass_t rdclass, isc_mem_t *mctx, + const char *keydir, dns_dnsseckeylist_t *keylist, + dns_dnsseckeylist_t *newkeys, dst_key_t **dst_key) { isc_result_t result = ISC_R_SUCCESS; bool conflict = false; int flags = DNS_KEYOWNER_ZONE; @@ -465,9 +465,9 @@ keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin, DNS_KEYPROTO_DNSSEC, rdclass, NULL, mctx, &newkey, NULL)); } else { - RETERR(dns_keystore_keygen(keystore, origin, rdclass, - mctx, alg, size, flags, - &newkey)); + RETERR(dns_keystore_keygen( + keystore, origin, dns_kasp_getname(kasp), + rdclass, mctx, alg, size, flags, &newkey)); } /* Key collision? */ @@ -1812,9 +1812,9 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, bool csk = (dns_kasp_key_ksk(kaspkey) && dns_kasp_key_zsk(kaspkey)); - isc_result_t result = keymgr_createkey(kaspkey, origin, rdclass, - mctx, keydir, keyring, - newkeys, &dst_key); + isc_result_t result = + keymgr_createkey(kaspkey, origin, kasp, rdclass, mctx, + keydir, keyring, newkeys, &dst_key); if (result != ISC_R_SUCCESS) { return (result); } diff --git a/lib/dns/keystore.c b/lib/dns/keystore.c index f733310ab9..8ca22873d8 100644 --- a/lib/dns/keystore.c +++ b/lib/dns/keystore.c @@ -16,10 +16,12 @@ #include #include +#include #include #include #include +#include #include #include @@ -143,53 +145,119 @@ dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri) { : isc_mem_strdup(keystore->mctx, uri); } +static isc_result_t +buildpkcs11label(const char *uri, const dns_name_t *zname, const char *policy, + int flags, isc_buffer_t *buf) { + bool ksk = ((flags & DNS_KEYFLAG_KSK) != 0); + char timebuf[18]; + isc_time_t now = isc_time_now(); + isc_result_t result; + dns_fixedname_t fname; + dns_name_t *pname = dns_fixedname_initname(&fname); + + /* uri + object */ + if (isc_buffer_availablelength(buf) < strlen(uri) + strlen(";object=")) + { + return (ISC_R_NOSPACE); + } + isc_buffer_putstr(buf, uri); + isc_buffer_putstr(buf, ";object="); + /* zone name */ + result = dns_name_tofilenametext(zname, false, buf); + if (result != ISC_R_SUCCESS) { + return (result); + } + /* + * policy name + * + * Note that strlen(policy) is not the actual length, but if this + * already does not fit, the escaped version returned from + * dns_name_tofilenametext() certainly won't fit. + */ + if (isc_buffer_availablelength(buf) < (strlen(policy) + 1)) { + return (ISC_R_NOSPACE); + } + isc_buffer_putstr(buf, "-"); + result = dns_name_fromstring(pname, policy, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + result = dns_name_tofilenametext(pname, false, buf); + if (result != ISC_R_SUCCESS) { + return (result); + } + /* key type + current time */ + isc_time_formatshorttimestamp(&now, timebuf, sizeof(timebuf)); + return isc_buffer_printf(buf, "-%s-%s", ksk ? "ksk" : "zsk", timebuf); +} + isc_result_t dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, - dns_rdataclass_t rdclass, isc_mem_t *mctx, uint32_t alg, - int size, int flags, dst_key_t **dstkey) { + const char *policy, dns_rdataclass_t rdclass, + isc_mem_t *mctx, uint32_t alg, int size, int flags, + dst_key_t **dstkey) { isc_result_t result; dst_key_t *newkey = NULL; const char *uri = NULL; REQUIRE(DNS_KEYSTORE_VALID(keystore)); REQUIRE(dns_name_isvalid(origin)); + REQUIRE(policy != NULL); REQUIRE(mctx != NULL); REQUIRE(dstkey != NULL && *dstkey == NULL); uri = dns_keystore_pkcs11uri(keystore); if (uri != NULL) { - char *label = NULL; - size_t len; - char timebuf[18]; - isc_time_t now = isc_time_now(); - bool ksk = ((flags & DNS_KEYFLAG_KSK) != 0); - char namebuf[DNS_NAME_FORMATSIZE]; - char object[DNS_NAME_FORMATSIZE + 26]; - - /* Create the PKCS11 URI */ - isc_time_formatshorttimestamp(&now, timebuf, sizeof(timebuf)); - dns_name_format(origin, namebuf, sizeof(namebuf)); - snprintf(object, sizeof(object), "%s-%s-%s", namebuf, - ksk ? "ksk" : "zsk", timebuf); - len = strlen(object) + strlen(uri) + 10; - label = isc_mem_get(mctx, len); - sprintf(label, "%s;object=%s;", uri, object); + /* + * Create the PKCS#11 label. + * The label consists of the configured URI, and the object + * parameter. The object parameter needs to be unique. We + * know that for a given point in time, there will be at most + * one key per type created for each zone in a given DNSSEC + * policy. Hence the object is constructed out of the following + * parts: the zone name, policy name, key type, and the + * current time. + * + * The object may not contain any characters that conflict with + * special characters in the PKCS#11 URI scheme syntax (see + * RFC 7512, Section 2.3). Therefore, we mangle the zone name + * and policy name through 'dns_name_tofilenametext()'. We + * could create a new function to convert a name to PKCS#11 + * text, but this existing function will suffice. + */ + char label[NAME_MAX]; + isc_buffer_t buf; + isc_buffer_init(&buf, label, sizeof(label)); + result = buildpkcs11label(uri, origin, policy, flags, &buf); + if (result != ISC_R_SUCCESS) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(origin, namebuf, sizeof(namebuf)); + isc_log_write( + dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, + "keystore: failed to create PKCS#11 object " + "for zone %s, policy %s: %s", + namebuf, policy, isc_result_totext(result)); + return (result); + } /* Generate the key */ result = dst_key_generate(origin, alg, size, 0, flags, DNS_KEYPROTO_DNSSEC, rdclass, label, mctx, &newkey, NULL); - isc_mem_put(mctx, label, len); - if (result != ISC_R_SUCCESS) { - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, - DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, - "keystore: failed to generate key " - "%s (ret=%d)", - object, result); + isc_log_write( + dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, + "keystore: failed to generate PKCS#11 object " + "%s: %s", + label, isc_result_totext(result)); return (result); } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, + "keystore: generated PKCS#11 object %s", label); } else { result = dst_key_generate(origin, alg, size, 0, flags, DNS_KEYPROTO_DNSSEC, rdclass, NULL, From 624034125f5236b401967c2e763f812c0eee102d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 17 Nov 2023 10:55:00 +0100 Subject: [PATCH 33/37] Add test case with special characters in zone name Add a zone to the system test that has special characters to ensure it does not mess up PKCS#11 labels when creating keys. --- bin/tests/system/enginepkcs11/setup.sh | 18 ++++++++++++++++++ bin/tests/system/enginepkcs11/tests.sh | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/bin/tests/system/enginepkcs11/setup.sh b/bin/tests/system/enginepkcs11/setup.sh index 59a2361e68..bf140f1895 100644 --- a/bin/tests/system/enginepkcs11/setup.sh +++ b/bin/tests/system/enginepkcs11/setup.sh @@ -122,6 +122,9 @@ for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \ echo_i "Add zone $alg.split to named.conf" cp $infile ${dir}/zone.${alg}.split.db + echo_i "Add weird zone to named.conf" + cp $infile ${dir}/zone.${alg}.weird.db + echo_i "Add zone $zone to named.conf" cat >>"${dir}/named.conf" < Date: Fri, 17 Nov 2023 16:28:01 +0100 Subject: [PATCH 34/37] Rename "uri" to "pkcs11-uri" The name "uri" was considered to be too generic and could potentially clash with a future URI configuration option. Renamed to "pkcs11-uri". Note that this option name was also preferred over "pkcs11uri", the dash is considered to be the more clearer form. --- bin/tests/system/checkconf/good-kasp.conf | 2 +- bin/tests/system/checkconf/good.conf.in | 2 +- bin/tests/system/enginepkcs11/ns1/named.conf.in | 4 ++-- bin/tests/system/enginepkcs11/ns2/named.conf.in | 6 +++--- doc/arm/reference.rst | 2 +- doc/misc/options | 2 +- lib/isccfg/kaspconf.c | 3 ++- lib/isccfg/namedconf.c | 9 +++++---- 8 files changed, 16 insertions(+), 14 deletions(-) diff --git a/bin/tests/system/checkconf/good-kasp.conf b/bin/tests/system/checkconf/good-kasp.conf index 40b5793ad0..95ba817b3b 100644 --- a/bin/tests/system/checkconf/good-kasp.conf +++ b/bin/tests/system/checkconf/good-kasp.conf @@ -41,7 +41,7 @@ dnssec-policy "test" { }; key-store "hsm" { directory "."; - uri "pkcs11:token=bind9;pin-value=1234"; + pkcs11-uri "pkcs11:token=bind9;pin-value=1234"; }; options { dnssec-policy "default"; diff --git a/bin/tests/system/checkconf/good.conf.in b/bin/tests/system/checkconf/good.conf.in index 59ecbcec44..2fde415a40 100644 --- a/bin/tests/system/checkconf/good.conf.in +++ b/bin/tests/system/checkconf/good.conf.in @@ -41,7 +41,7 @@ dnssec-policy "test" { }; key-store "hsm" { directory "."; - uri "pkcs11:token=bind9;pin-value=1234"; + pkcs11-uri "pkcs11:token=bind9;pin-value=1234"; }; options { avoid-v4-udp-ports { diff --git a/bin/tests/system/enginepkcs11/ns1/named.conf.in b/bin/tests/system/enginepkcs11/ns1/named.conf.in index 2e04e45b26..b6c2d2df9d 100644 --- a/bin/tests/system/enginepkcs11/ns1/named.conf.in +++ b/bin/tests/system/enginepkcs11/ns1/named.conf.in @@ -37,12 +37,12 @@ controls { key-store "hsm" { directory "."; - uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; + pkcs11-uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; }; key-store "pin" { directory "."; - uri "pkcs11:token=softhsm2-enginepkcs11;pin-source=pin"; + pkcs11-uri "pkcs11:token=softhsm2-enginepkcs11;pin-source=pin"; }; key-store "disk" { diff --git a/bin/tests/system/enginepkcs11/ns2/named.conf.in b/bin/tests/system/enginepkcs11/ns2/named.conf.in index 262419e983..0622a949ff 100644 --- a/bin/tests/system/enginepkcs11/ns2/named.conf.in +++ b/bin/tests/system/enginepkcs11/ns2/named.conf.in @@ -38,17 +38,17 @@ key "keyforview2" { key-store "hsm" { directory "."; - uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; + pkcs11-uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; }; key-store "hsm2" { directory "keys"; - uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; + pkcs11-uri "pkcs11:token=softhsm2-enginepkcs11;pin-value=1234"; }; key-store "pin" { directory "."; - uri "pkcs11:token=softhsm2-enginepkcs11;pin-source=pin"; + pkcs11-uri "pkcs11:token=softhsm2-enginepkcs11;pin-source=pin"; }; key-store "disk" { diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 6989c4fab7..3ebcfc82a5 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -621,7 +621,7 @@ The following options can be specified in a :any:`key-store` statement: The ``directory`` specifies where key files for this key should be stored. This is similar to using the zone's ``key-directory``. -.. namedconf:statement:: uri +.. namedconf:statement:: pkcs11-uri :tags: dnssec, pkcs11 The ``uri`` is a string that specifies a PKCS#11 URI Scheme (defined in diff --git a/doc/misc/options b/doc/misc/options index bde27bd36d..25c23e9e06 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -44,7 +44,7 @@ key { key-store { directory ; - uri ; + pkcs11-uri ; }; // may occur multiple times logging { diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 9bd4ebb3e9..583f482f10 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -780,7 +780,8 @@ cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, maps[i] = NULL; dns_keystore_setdirectory(keystore, get_string(maps, "directory")); - dns_keystore_setpkcs11uri(keystore, get_string(maps, "uri")); + dns_keystore_setpkcs11uri(keystore, + get_string(maps, "pkcs11-uri")); } /* Append it to the list for future lookups. */ diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 216eebce30..8465139e0b 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2601,10 +2601,11 @@ static cfg_type_t cfg_type_key = { "key", cfg_parse_named_map, /*% * A key-store statement. */ -static cfg_clausedef_t keystore_clauses[] = { { "directory", &cfg_type_astring, - 0 }, - { "uri", &cfg_type_qstring, 0 }, - { NULL, NULL, 0 } }; +static cfg_clausedef_t keystore_clauses[] = { + { "directory", &cfg_type_astring, 0 }, + { "pkcs11-uri", &cfg_type_qstring, 0 }, + { NULL, NULL, 0 } +}; static cfg_clausedef_t *keystore_clausesets[] = { keystore_clauses, NULL }; static cfg_type_t cfg_type_keystoreopts = { From daaa70f48b3217bdb97113ee8b2ef386ba34687b Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 17 Nov 2023 17:09:00 +0100 Subject: [PATCH 35/37] Refactor dns_keystore_directory() Add a default key-directory parameter to the function that can be returned if there is no keystore, or if the keystore directory is NULL (the latter is also true for the built-in keystore). --- bin/dnssec/dnssec-keygen.c | 2 +- lib/dns/dnssec.c | 21 +++------------------ lib/dns/include/dns/keystore.h | 9 +++------ lib/dns/keymgr.c | 16 ++++------------ lib/dns/keystore.c | 12 ++++++++++-- lib/dns/zone.c | 9 ++------- lib/isccfg/check.c | 18 ++++++++++-------- 7 files changed, 33 insertions(+), 54 deletions(-) diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index d770e41c94..b9e5fdb62b 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -913,7 +913,7 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { static void check_keystore_options(keygen_ctx_t *ctx) { - ctx->directory = dns_keystore_directory(ctx->keystore); + ctx->directory = dns_keystore_directory(ctx->keystore, NULL); if (ctx->directory != NULL) { isc_result_t ret = try_dir(ctx->directory); if (ret != ISC_R_SUCCESS) { diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index c1b1beedfa..eaf7ef6f11 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -1376,15 +1376,8 @@ dns_dnssec_findmatchingkeys(const dns_name_t *origin, dns_kasp_t *kasp, { if (dns_kasp_key_keystore(kkey) == keystore) { const char *directory = - dns_keystore_directory( - keystore); - if (directory == NULL || - (strcmp(dns_keystore_name(keystore), - DNS_KEYSTORE_KEYDIRECTORY) == - 0)) - { - directory = keydir; - } + dns_keystore_directory(keystore, + keydir); RETERR(findmatchingkeys( directory, namebuf, len, mctx, now, &list)); @@ -1532,15 +1525,7 @@ keyfromfile(dns_kasp_t *kasp, const char *keydir, dst_key_t *key, int type, kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) { dns_keystore_t *ks = dns_kasp_key_keystore(kkey); - if (ks == NULL || - strcmp(dns_keystore_name(ks), - DNS_KEYSTORE_KEYDIRECTORY) == 0) - { - directory = keydir; - } else { - directory = dns_keystore_directory(ks); - } - + directory = dns_keystore_directory(ks, keydir); result = dst_key_fromfile(dst_key_name(key), dst_key_id(key), dst_key_alg(key), type, diff --git a/lib/dns/include/dns/keystore.h b/lib/dns/include/dns/keystore.h index 6db1e9cbbb..8cf573cb31 100644 --- a/lib/dns/include/dns/keystore.h +++ b/lib/dns/include/dns/keystore.h @@ -146,13 +146,10 @@ dns_keystore_engine(dns_keystore_t *keystore); */ const char * -dns_keystore_directory(dns_keystore_t *keystore); +dns_keystore_directory(dns_keystore_t *keystore, const char *keydir); /*%< - * Get keystore directory. - * - * Requires: - * - *\li 'keystore' is a valid keystore. + * Get keystore directory. If 'keystore' is NULL or 'keystore->directory' is + *NULL, return 'keydir'. * * Returns: * diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 05b26261c9..cc59e42c0b 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -453,6 +453,7 @@ keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin, dst_key_t *newkey = NULL; uint32_t alg = dns_kasp_key_algorithm(kkey); dns_keystore_t *keystore = dns_kasp_key_keystore(kkey); + const char *dir = NULL; int size = dns_kasp_key_size(kkey); if (dns_kasp_key_ksk(kkey)) { @@ -490,19 +491,10 @@ keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin, dst_key_setbool(newkey, DST_BOOL_KSK, dns_kasp_key_ksk(kkey)); dst_key_setbool(newkey, DST_BOOL_ZSK, dns_kasp_key_zsk(kkey)); - if (keystore == NULL || - strcmp(dns_keystore_name(keystore), "key-directory") == 0) - { - if (keydir != NULL) { - dst_key_setdirectory(newkey, keydir); - } - } else { - if (dns_keystore_directory(keystore) != NULL) { - dst_key_setdirectory(newkey, - dns_keystore_directory(keystore)); - } + dir = dns_keystore_directory(keystore, keydir); + if (dir != NULL) { + dst_key_setdirectory(newkey, dir); } - *dst_key = newkey; return (ISC_R_SUCCESS); diff --git a/lib/dns/keystore.c b/lib/dns/keystore.c index 8ca22873d8..128bededc0 100644 --- a/lib/dns/keystore.c +++ b/lib/dns/keystore.c @@ -108,8 +108,16 @@ dns_keystore_engine(dns_keystore_t *keystore) { } const char * -dns_keystore_directory(dns_keystore_t *keystore) { - REQUIRE(DNS_KEYSTORE_VALID(keystore)); +dns_keystore_directory(dns_keystore_t *keystore, const char *keydir) { + if (keystore == NULL) { + return (keydir); + } + + INSIST(DNS_KEYSTORE_VALID(keystore)); + + if (keystore->directory == NULL) { + return (keydir); + } return (keystore->directory); } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 2b7bab8316..f0360e4d44 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -6085,13 +6085,8 @@ keyfromfile(dns_zone_t *zone, dst_key_t *pubkey, isc_mem_t *mctx, kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) { dns_keystore_t *ks = dns_kasp_key_keystore(kkey); - if (ks == NULL || - strcmp(dns_keystore_name(ks), "key-directory") == 0) - { - directory = zone->keydirectory; - } else { - directory = dns_keystore_directory(ks); - } + directory = dns_keystore_directory(ks, + zone->keydirectory); result = dst_key_fromfile( dst_key_name(pubkey), dst_key_id(pubkey), diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index c00c588845..ad4ae8441d 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -3009,14 +3009,16 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig, kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) { dns_keystore_t *kks = dns_kasp_key_keystore(kkey); - if (kks == NULL || strcmp(DNS_KEYSTORE_KEYDIRECTORY, - dns_keystore_name(kks)) == 0) - { - dir = keydir; - keystore = false; - } else { - dir = dns_keystore_directory(kks); - keystore = true; + dir = dns_keystore_directory(kks, keydir); + keystore = (kks != NULL && strcmp(DNS_KEYSTORE_KEYDIRECTORY, + dns_keystore_name(kks)) != 0); + + ret = keydirexist(zconfig, + keystore ? "key-store directory" + : "key-directory", + zname, dir, name, keydirs, logctx, mctx); + if (ret != ISC_R_SUCCESS) { + result = ret; } } dns_kasp_thaw(kasp); From 2615b8a8b58bf878c8ba6bd0ae1798ee44f344d2 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 27 Nov 2023 11:54:35 +0100 Subject: [PATCH 36/37] Update pkcs11 documentation Update the minimum required version of pkcs11-provider that contains the fixes needed in order to make it work with dnssec-policy. Update documentation to not recommend using engine_pkcs11 in conjunction with dnssec-policy. --- doc/arm/pkcs11.inc.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/arm/pkcs11.inc.rst b/doc/arm/pkcs11.inc.rst index 78de07bcf3..7a586802fb 100644 --- a/doc/arm/pkcs11.inc.rst +++ b/doc/arm/pkcs11.inc.rst @@ -91,6 +91,11 @@ When using engine_pkcs11, all BIND binaries potentially need the keys require Even though OpenSSL 3 has compatibility support for Engine API it is not recommended to be used due to bugs in OpenSSL and libp11. +It is not possible to generate new keys via the engine_pkcs11 and therefore it +is not recommended to use it in a ``dnssec-policy`` setup (although it is +possible to put previously generated keys in the ``key-directory`` and let the +key manager select those keys when a key rollover is started. + Configuring engine_pkcs11 ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -170,8 +175,8 @@ path to the PKCS#11 module which should be gatewayed to. This can be done by editing the OpenSSL configuration file, by engine specific controls, or by using the p11-kit proxy module. -It is recommended that pkcs11-provider git commit 8672b98d2558aecb49f173df97b1463c7697b540 -from August 15, 2023 or later is used. +It is required to use pkcs11-provider git commit +2e8c26b4157fd21422c66f0b4d7b26cf8c320570 from October 2, 2023 or later. BIND support for pkcs11-provider is built in and the -E command line option explained above should not be used. From 8602beecd11757e0607928895e596531ebd461a8 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 3 Jan 2024 16:24:53 +0100 Subject: [PATCH 37/37] Replace keystore attach/detach with ISC_REFCOUNT_IMPL/ISC_REFCOUNT_DECL This is now the default way to implement attaching to/detaching from a pointer. Also update cfg_keystore_fromconfig() to allow NULL value for the keystore pointer. In most cases we detach it immediately after the function call. --- bin/dnssec/dnssec-keygen.c | 4 +-- bin/named/server.c | 10 ++---- lib/dns/include/dns/keystore.h | 50 ++++++++++------------------ lib/dns/keystore.c | 27 ++++----------- lib/isccfg/check.c | 23 +++---------- lib/isccfg/include/isccfg/kaspconf.h | 2 +- lib/isccfg/kaspconf.c | 7 ++-- 7 files changed, 36 insertions(+), 87 deletions(-) diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index b9e5fdb62b..7876f3f731 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -276,14 +276,12 @@ kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, cfg_obj_t *kconfig = cfg_listelt_value(element); ks = NULL; result = cfg_keystore_fromconfig(kconfig, mctx, lctx, engine, - &kslist, &ks); + &kslist, NULL); if (result != ISC_R_SUCCESS) { fatal("failed to configure key-store '%s': %s", cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), isc_result_totext(result)); } - INSIST(ks != NULL); - dns_keystore_detach(&ks); } /* Default key-directory key store. */ ks = NULL; diff --git a/bin/named/server.c b/bin/named/server.c index 64fbd5dc99..d2da295f06 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -8895,15 +8895,11 @@ load_configuration(const char *filename, named_server_t *server, /* * Create the built-in key store ("key-directory"). */ - keystore = NULL; result = cfg_keystore_fromconfig(NULL, named_g_mctx, named_g_lctx, - named_g_engine, &keystorelist, - &keystore); + named_g_engine, &keystorelist, NULL); if (result != ISC_R_SUCCESS) { goto cleanup_keystorelist; } - INSIST(keystore != NULL); - dns_keystore_detach(&keystore); /* * Create the DNSSEC key stores. @@ -8917,12 +8913,10 @@ load_configuration(const char *filename, named_server_t *server, keystore = NULL; result = cfg_keystore_fromconfig(kconfig, named_g_mctx, named_g_lctx, named_g_engine, - &keystorelist, &keystore); + &keystorelist, NULL); if (result != ISC_R_SUCCESS) { goto cleanup_keystorelist; } - INSIST(keystore != NULL); - dns_keystore_detach(&keystore); } /* diff --git a/lib/dns/include/dns/keystore.h b/lib/dns/include/dns/keystore.h index 8cf573cb31..8888eb12d4 100644 --- a/lib/dns/include/dns/keystore.h +++ b/lib/dns/include/dns/keystore.h @@ -24,6 +24,8 @@ * A key store defines where to store DNSSEC keys. */ +/* Add -DDNS_KEYSTORE_TRACE=1 to CFLAGS for detailed reference tracing */ + #include #include #include @@ -85,38 +87,6 @@ dns_keystore_create(isc_mem_t *mctx, const char *name, const char *engine, *\li Other errors are possible. */ -void -dns_keystore_attach(dns_keystore_t *source, dns_keystore_t **targetp); -/*%< - * Attach '*targetp' to 'source'. - * - * Requires: - * - *\li 'source' is a valid keystore. - * - *\li 'targetp' points to a NULL dns_keystore_t *. - * - * Ensures: - * - *\li *targetp is attached to source. - * - *\li While *targetp is attached, the keystore will not shut down. - */ - -void -dns_keystore_detach(dns_keystore_t **kspp); -/*%< - * Detach keystore. - * - * Requires: - * - *\li 'kspp' points to a valid dns_keystore_t * - * - * Ensures: - * - *\li *kspp is NULL. - */ - const char * dns_keystore_name(dns_keystore_t *keystore); /*%< @@ -231,4 +201,20 @@ dns_keystorelist_find(dns_keystorelist_t *list, const char *name, *\li #ISC_R_NOTFOUND No matching keystore was found. */ +#ifdef DNS_KEYSTORE_TRACE +/* Compatibility macros */ +#define dns_keystore_attach(ks, ksp) \ + dns_keystore__attach(ks, ksp, __func__, __FILE__, __LINE__) +#define dns_keystore_detach(ksp) \ + dns_keystore__detach(ksp, __func__, __FILE__, __LINE__) +#define dns_keystore_ref(ptr) \ + dns_keystore__ref(ptr, __func__, __FILE__, __LINE__) +#define dns_keystore_unref(ptr) \ + dns_keystore__unref(ptr, __func__, __FILE__, __LINE__) + +ISC_REFCOUNT_TRACE_DECL(dns_keystore); +#else +ISC_REFCOUNT_DECL(dns_keystore); +#endif /* DNS_KEYSTORE_TRACE */ + ISC_LANG_ENDDECLS diff --git a/lib/dns/keystore.c b/lib/dns/keystore.c index 128bededc0..90b766d62e 100644 --- a/lib/dns/keystore.c +++ b/lib/dns/keystore.c @@ -54,17 +54,8 @@ dns_keystore_create(isc_mem_t *mctx, const char *name, const char *engine, return (ISC_R_SUCCESS); } -void -dns_keystore_attach(dns_keystore_t *source, dns_keystore_t **targetp) { - REQUIRE(DNS_KEYSTORE_VALID(source)); - REQUIRE(targetp != NULL && *targetp == NULL); - - isc_refcount_increment(&source->references); - *targetp = source; -} - static inline void -destroy(dns_keystore_t *keystore) { +dns__keystore_destroy(dns_keystore_t *keystore) { char *name; REQUIRE(!ISC_LINK_LINKED(keystore, link)); @@ -81,17 +72,11 @@ destroy(dns_keystore_t *keystore) { isc_mem_putanddetach(&keystore->mctx, keystore, sizeof(*keystore)); } -void -dns_keystore_detach(dns_keystore_t **kspp) { - REQUIRE(kspp != NULL && DNS_KEYSTORE_VALID(*kspp)); - - dns_keystore_t *ks = *kspp; - *kspp = NULL; - - if (isc_refcount_decrement(&ks->references) == 1) { - destroy(ks); - } -} +#ifdef DNS_KEYSTORE_TRACE +ISC_REFCOUNT_TRACE_IMPL(dns_keystore, dns__keystore_destroy); +#else +ISC_REFCOUNT_IMPL(dns_keystore, dns__keystore_destroy); +#endif const char * dns_keystore_name(dns_keystore_t *keystore) { diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index ad4ae8441d..c4ddf1940d 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -1414,15 +1414,12 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, ret = cfg_keystore_fromconfig(kconfig, mctx, logctx, NULL, - &kslist, &ks); + &kslist, NULL); if (ret != ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) { result = ret; } } - if (ks != NULL) { - dns_keystore_detach(&ks); - } } } } @@ -1431,15 +1428,12 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, * Add default key-store "key-directory". */ tresult = cfg_keystore_fromconfig(NULL, mctx, logctx, NULL, &kslist, - &ks); + NULL); if (tresult != ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) { result = tresult; } } - if (ks != NULL) { - dns_keystore_detach(&ks); - } /* * Check dnssec-policy. @@ -2962,16 +2956,10 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig, element = cfg_list_next(element)) { cfg_obj_t *kcfg = cfg_listelt_value(element); - ks = NULL; (void)cfg_keystore_fromconfig(kcfg, mctx, logctx, NULL, &kslist, - &ks); - INSIST(ks != NULL); - dns_keystore_detach(&ks); + NULL); } - ks = NULL; - (void)cfg_keystore_fromconfig(NULL, mctx, logctx, NULL, &kslist, &ks); - INSIST(ks != NULL); - dns_keystore_detach(&ks); + (void)cfg_keystore_fromconfig(NULL, mctx, logctx, NULL, &kslist, NULL); /* * Look for the dnssec-policy by name, which is the dnssec-policy @@ -3037,9 +3025,6 @@ check: if (kasp != NULL) { dns_kasp_detach(&kasp); } - if (ks != NULL) { - dns_keystore_detach(&ks); - } for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) { diff --git a/lib/isccfg/include/isccfg/kaspconf.h b/lib/isccfg/include/isccfg/kaspconf.h index 1bc727d89c..ccc1cecc5d 100644 --- a/lib/isccfg/include/isccfg/kaspconf.h +++ b/lib/isccfg/include/isccfg/kaspconf.h @@ -78,7 +78,7 @@ cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, * *\li 'logctx' is a valid logging context. * - *\li kspp != NULL && *kspp == NULL + *\li kspp == NULL || *kspp == NULL * * Returns: * diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 583f482f10..f756ed97da 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -737,8 +737,6 @@ cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, dns_keystore_t *keystore = NULL; int i = 0; - REQUIRE(kspp != NULL && *kspp == NULL); - if (config != NULL) { name = cfg_obj_asstring(cfg_tuple_get(config, "name")); } else { @@ -789,7 +787,10 @@ cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, INSIST(!(ISC_LIST_EMPTY(*keystorelist))); /* Success: Attach the keystore to the pointer and return. */ - dns_keystore_attach(keystore, kspp); + if (kspp != NULL) { + INSIST(*kspp == NULL); + dns_keystore_attach(keystore, kspp); + } /* Don't detach as keystore is on '*keystorelist' */ return (ISC_R_SUCCESS);