/* $NetBSD: keystore.c,v 1.2 2025/01/26 16:25:23 christos Exp $ */ /* * 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 */ #include #include #include #include #include #include #include #include #include isc_result_t 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); 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; } static inline void dns__keystore_destroy(dns_keystore_t *keystore) { char *name; 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)); } #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) { REQUIRE(DNS_KEYSTORE_VALID(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, const char *keydir) { if (keystore == NULL) { return keydir; } INSIST(DNS_KEYSTORE_VALID(keystore)); if (keystore->directory == NULL) { return keydir; } 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); } 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, 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) { /* * 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); if (result != ISC_R_SUCCESS) { 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, 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) { 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; }