xref: /netbsd-src/external/mpl/bind/dist/lib/dns/keystore.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1*bcda20f6Schristos /*	$NetBSD: keystore.c,v 1.2 2025/01/26 16:25:23 christos Exp $	*/
29689912eSchristos 
39689912eSchristos /*
49689912eSchristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
59689912eSchristos  *
69689912eSchristos  * SPDX-License-Identifier: MPL-2.0
79689912eSchristos  *
89689912eSchristos  * This Source Code Form is subject to the terms of the Mozilla Public
99689912eSchristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
109689912eSchristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
119689912eSchristos  *
129689912eSchristos  * See the COPYRIGHT file distributed with this work for additional
139689912eSchristos  * information regarding copyright ownership.
149689912eSchristos  */
159689912eSchristos 
169689912eSchristos /*! \file */
179689912eSchristos 
189689912eSchristos #include <string.h>
199689912eSchristos 
209689912eSchristos #include <isc/assertions.h>
219689912eSchristos #include <isc/buffer.h>
229689912eSchristos #include <isc/mem.h>
239689912eSchristos #include <isc/time.h>
249689912eSchristos #include <isc/util.h>
259689912eSchristos 
269689912eSchristos #include <dns/fixedname.h>
279689912eSchristos #include <dns/keystore.h>
289689912eSchristos #include <dns/keyvalues.h>
299689912eSchristos 
309689912eSchristos isc_result_t
319689912eSchristos dns_keystore_create(isc_mem_t *mctx, const char *name, const char *engine,
329689912eSchristos 		    dns_keystore_t **kspp) {
339689912eSchristos 	dns_keystore_t *keystore;
349689912eSchristos 
359689912eSchristos 	REQUIRE(name != NULL);
369689912eSchristos 	REQUIRE(kspp != NULL && *kspp == NULL);
379689912eSchristos 
389689912eSchristos 	keystore = isc_mem_get(mctx, sizeof(*keystore));
399689912eSchristos 	keystore->engine = engine;
409689912eSchristos 	keystore->mctx = NULL;
419689912eSchristos 	isc_mem_attach(mctx, &keystore->mctx);
429689912eSchristos 
439689912eSchristos 	keystore->name = isc_mem_strdup(mctx, name);
449689912eSchristos 	isc_mutex_init(&keystore->lock);
459689912eSchristos 
469689912eSchristos 	isc_refcount_init(&keystore->references, 1);
479689912eSchristos 
489689912eSchristos 	ISC_LINK_INIT(keystore, link);
499689912eSchristos 
509689912eSchristos 	keystore->directory = NULL;
519689912eSchristos 	keystore->pkcs11uri = NULL;
529689912eSchristos 
539689912eSchristos 	keystore->magic = DNS_KEYSTORE_MAGIC;
549689912eSchristos 	*kspp = keystore;
559689912eSchristos 
569689912eSchristos 	return ISC_R_SUCCESS;
579689912eSchristos }
589689912eSchristos 
599689912eSchristos static inline void
609689912eSchristos dns__keystore_destroy(dns_keystore_t *keystore) {
619689912eSchristos 	char *name;
629689912eSchristos 
639689912eSchristos 	REQUIRE(!ISC_LINK_LINKED(keystore, link));
649689912eSchristos 
659689912eSchristos 	isc_mutex_destroy(&keystore->lock);
669689912eSchristos 	name = UNCONST(keystore->name);
679689912eSchristos 	isc_mem_free(keystore->mctx, name);
689689912eSchristos 	if (keystore->directory != NULL) {
699689912eSchristos 		isc_mem_free(keystore->mctx, keystore->directory);
709689912eSchristos 	}
719689912eSchristos 	if (keystore->pkcs11uri != NULL) {
729689912eSchristos 		isc_mem_free(keystore->mctx, keystore->pkcs11uri);
739689912eSchristos 	}
749689912eSchristos 	isc_mem_putanddetach(&keystore->mctx, keystore, sizeof(*keystore));
759689912eSchristos }
769689912eSchristos 
779689912eSchristos #ifdef DNS_KEYSTORE_TRACE
789689912eSchristos ISC_REFCOUNT_TRACE_IMPL(dns_keystore, dns__keystore_destroy);
799689912eSchristos #else
809689912eSchristos ISC_REFCOUNT_IMPL(dns_keystore, dns__keystore_destroy);
819689912eSchristos #endif
829689912eSchristos 
839689912eSchristos const char *
849689912eSchristos dns_keystore_name(dns_keystore_t *keystore) {
859689912eSchristos 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
869689912eSchristos 
879689912eSchristos 	return keystore->name;
889689912eSchristos }
899689912eSchristos 
909689912eSchristos const char *
919689912eSchristos dns_keystore_engine(dns_keystore_t *keystore) {
929689912eSchristos 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
939689912eSchristos 
949689912eSchristos 	return keystore->engine;
959689912eSchristos }
969689912eSchristos 
979689912eSchristos const char *
989689912eSchristos dns_keystore_directory(dns_keystore_t *keystore, const char *keydir) {
999689912eSchristos 	if (keystore == NULL) {
1009689912eSchristos 		return keydir;
1019689912eSchristos 	}
1029689912eSchristos 
1039689912eSchristos 	INSIST(DNS_KEYSTORE_VALID(keystore));
1049689912eSchristos 
1059689912eSchristos 	if (keystore->directory == NULL) {
1069689912eSchristos 		return keydir;
1079689912eSchristos 	}
1089689912eSchristos 
1099689912eSchristos 	return keystore->directory;
1109689912eSchristos }
1119689912eSchristos 
1129689912eSchristos void
1139689912eSchristos dns_keystore_setdirectory(dns_keystore_t *keystore, const char *dir) {
1149689912eSchristos 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
1159689912eSchristos 
1169689912eSchristos 	if (keystore->directory != NULL) {
1179689912eSchristos 		isc_mem_free(keystore->mctx, keystore->directory);
1189689912eSchristos 	}
1199689912eSchristos 	keystore->directory = (dir == NULL)
1209689912eSchristos 				      ? NULL
1219689912eSchristos 				      : isc_mem_strdup(keystore->mctx, dir);
1229689912eSchristos }
1239689912eSchristos 
1249689912eSchristos const char *
1259689912eSchristos dns_keystore_pkcs11uri(dns_keystore_t *keystore) {
1269689912eSchristos 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
1279689912eSchristos 
1289689912eSchristos 	return keystore->pkcs11uri;
1299689912eSchristos }
1309689912eSchristos 
1319689912eSchristos void
1329689912eSchristos dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri) {
1339689912eSchristos 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
1349689912eSchristos 
1359689912eSchristos 	if (keystore->pkcs11uri != NULL) {
1369689912eSchristos 		isc_mem_free(keystore->mctx, keystore->pkcs11uri);
1379689912eSchristos 	}
1389689912eSchristos 	keystore->pkcs11uri = (uri == NULL)
1399689912eSchristos 				      ? NULL
1409689912eSchristos 				      : isc_mem_strdup(keystore->mctx, uri);
1419689912eSchristos }
1429689912eSchristos 
1439689912eSchristos static isc_result_t
1449689912eSchristos buildpkcs11label(const char *uri, const dns_name_t *zname, const char *policy,
1459689912eSchristos 		 int flags, isc_buffer_t *buf) {
1469689912eSchristos 	bool ksk = ((flags & DNS_KEYFLAG_KSK) != 0);
1479689912eSchristos 	char timebuf[18];
1489689912eSchristos 	isc_time_t now = isc_time_now();
1499689912eSchristos 	isc_result_t result;
1509689912eSchristos 	dns_fixedname_t fname;
1519689912eSchristos 	dns_name_t *pname = dns_fixedname_initname(&fname);
1529689912eSchristos 
1539689912eSchristos 	/* uri + object */
1549689912eSchristos 	if (isc_buffer_availablelength(buf) < strlen(uri) + strlen(";object="))
1559689912eSchristos 	{
1569689912eSchristos 		return ISC_R_NOSPACE;
1579689912eSchristos 	}
1589689912eSchristos 	isc_buffer_putstr(buf, uri);
1599689912eSchristos 	isc_buffer_putstr(buf, ";object=");
1609689912eSchristos 	/* zone name */
1619689912eSchristos 	result = dns_name_tofilenametext(zname, false, buf);
1629689912eSchristos 	if (result != ISC_R_SUCCESS) {
1639689912eSchristos 		return result;
1649689912eSchristos 	}
1659689912eSchristos 	/*
1669689912eSchristos 	 * policy name
1679689912eSchristos 	 *
1689689912eSchristos 	 * Note that strlen(policy) is not the actual length, but if this
1699689912eSchristos 	 * already does not fit, the escaped version returned from
1709689912eSchristos 	 * dns_name_tofilenametext() certainly won't fit.
1719689912eSchristos 	 */
1729689912eSchristos 	if (isc_buffer_availablelength(buf) < (strlen(policy) + 1)) {
1739689912eSchristos 		return ISC_R_NOSPACE;
1749689912eSchristos 	}
1759689912eSchristos 	isc_buffer_putstr(buf, "-");
1769689912eSchristos 	result = dns_name_fromstring(pname, policy, dns_rootname, 0, NULL);
1779689912eSchristos 	if (result != ISC_R_SUCCESS) {
1789689912eSchristos 		return result;
1799689912eSchristos 	}
1809689912eSchristos 	result = dns_name_tofilenametext(pname, false, buf);
1819689912eSchristos 	if (result != ISC_R_SUCCESS) {
1829689912eSchristos 		return result;
1839689912eSchristos 	}
1849689912eSchristos 	/* key type + current time */
1859689912eSchristos 	isc_time_formatshorttimestamp(&now, timebuf, sizeof(timebuf));
1869689912eSchristos 	return isc_buffer_printf(buf, "-%s-%s", ksk ? "ksk" : "zsk", timebuf);
1879689912eSchristos }
1889689912eSchristos 
1899689912eSchristos isc_result_t
1909689912eSchristos dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin,
1919689912eSchristos 		    const char *policy, dns_rdataclass_t rdclass,
1929689912eSchristos 		    isc_mem_t *mctx, uint32_t alg, int size, int flags,
1939689912eSchristos 		    dst_key_t **dstkey) {
1949689912eSchristos 	isc_result_t result;
1959689912eSchristos 	dst_key_t *newkey = NULL;
1969689912eSchristos 	const char *uri = NULL;
1979689912eSchristos 
1989689912eSchristos 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
1999689912eSchristos 	REQUIRE(dns_name_isvalid(origin));
2009689912eSchristos 	REQUIRE(policy != NULL);
2019689912eSchristos 	REQUIRE(mctx != NULL);
2029689912eSchristos 	REQUIRE(dstkey != NULL && *dstkey == NULL);
2039689912eSchristos 
2049689912eSchristos 	uri = dns_keystore_pkcs11uri(keystore);
2059689912eSchristos 	if (uri != NULL) {
2069689912eSchristos 		/*
2079689912eSchristos 		 * Create the PKCS#11 label.
2089689912eSchristos 		 * The label consists of the configured URI, and the object
2099689912eSchristos 		 * parameter.  The object parameter needs to be unique.  We
2109689912eSchristos 		 * know that for a given point in time, there will be at most
2119689912eSchristos 		 * one key per type created for each zone in a given DNSSEC
2129689912eSchristos 		 * policy.  Hence the object is constructed out of the following
2139689912eSchristos 		 * parts: the zone name, policy name, key type, and the
2149689912eSchristos 		 * current time.
2159689912eSchristos 		 *
2169689912eSchristos 		 * The object may not contain any characters that conflict with
2179689912eSchristos 		 * special characters in the PKCS#11 URI scheme syntax (see
2189689912eSchristos 		 * RFC 7512, Section 2.3). Therefore, we mangle the zone name
2199689912eSchristos 		 * and policy name through 'dns_name_tofilenametext()'. We
2209689912eSchristos 		 * could create a new function to convert a name to PKCS#11
2219689912eSchristos 		 * text, but this existing function will suffice.
2229689912eSchristos 		 */
2239689912eSchristos 		char label[NAME_MAX];
2249689912eSchristos 		isc_buffer_t buf;
2259689912eSchristos 		isc_buffer_init(&buf, label, sizeof(label));
2269689912eSchristos 		result = buildpkcs11label(uri, origin, policy, flags, &buf);
2279689912eSchristos 		if (result != ISC_R_SUCCESS) {
2289689912eSchristos 			char namebuf[DNS_NAME_FORMATSIZE];
2299689912eSchristos 			dns_name_format(origin, namebuf, sizeof(namebuf));
2309689912eSchristos 			isc_log_write(
2319689912eSchristos 				dns_lctx, DNS_LOGCATEGORY_DNSSEC,
2329689912eSchristos 				DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR,
2339689912eSchristos 				"keystore: failed to create PKCS#11 object "
2349689912eSchristos 				"for zone %s, policy %s: %s",
2359689912eSchristos 				namebuf, policy, isc_result_totext(result));
2369689912eSchristos 			return result;
2379689912eSchristos 		}
2389689912eSchristos 
2399689912eSchristos 		/* Generate the key */
2409689912eSchristos 		result = dst_key_generate(origin, alg, size, 0, flags,
2419689912eSchristos 					  DNS_KEYPROTO_DNSSEC, rdclass, label,
2429689912eSchristos 					  mctx, &newkey, NULL);
2439689912eSchristos 
2449689912eSchristos 		if (result != ISC_R_SUCCESS) {
2459689912eSchristos 			isc_log_write(
2469689912eSchristos 				dns_lctx, DNS_LOGCATEGORY_DNSSEC,
2479689912eSchristos 				DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR,
2489689912eSchristos 				"keystore: failed to generate PKCS#11 object "
2499689912eSchristos 				"%s: %s",
2509689912eSchristos 				label, isc_result_totext(result));
2519689912eSchristos 			return result;
2529689912eSchristos 		}
2539689912eSchristos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
2549689912eSchristos 			      DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR,
2559689912eSchristos 			      "keystore: generated PKCS#11 object %s", label);
2569689912eSchristos 	} else {
2579689912eSchristos 		result = dst_key_generate(origin, alg, size, 0, flags,
2589689912eSchristos 					  DNS_KEYPROTO_DNSSEC, rdclass, NULL,
2599689912eSchristos 					  mctx, &newkey, NULL);
2609689912eSchristos 	}
2619689912eSchristos 
2629689912eSchristos 	if (result == ISC_R_SUCCESS) {
2639689912eSchristos 		*dstkey = newkey;
2649689912eSchristos 	}
2659689912eSchristos 	return result;
2669689912eSchristos }
2679689912eSchristos 
2689689912eSchristos isc_result_t
2699689912eSchristos dns_keystorelist_find(dns_keystorelist_t *list, const char *name,
2709689912eSchristos 		      dns_keystore_t **kspp) {
2719689912eSchristos 	dns_keystore_t *keystore = NULL;
2729689912eSchristos 
2739689912eSchristos 	REQUIRE(kspp != NULL && *kspp == NULL);
2749689912eSchristos 
2759689912eSchristos 	if (list == NULL) {
2769689912eSchristos 		return ISC_R_NOTFOUND;
2779689912eSchristos 	}
2789689912eSchristos 
2799689912eSchristos 	for (keystore = ISC_LIST_HEAD(*list); keystore != NULL;
2809689912eSchristos 	     keystore = ISC_LIST_NEXT(keystore, link))
2819689912eSchristos 	{
2829689912eSchristos 		if (strcmp(keystore->name, name) == 0) {
2839689912eSchristos 			break;
2849689912eSchristos 		}
2859689912eSchristos 	}
2869689912eSchristos 
2879689912eSchristos 	if (keystore == NULL) {
2889689912eSchristos 		return ISC_R_NOTFOUND;
2899689912eSchristos 	}
2909689912eSchristos 
2919689912eSchristos 	dns_keystore_attach(keystore, kspp);
2929689912eSchristos 	return ISC_R_SUCCESS;
2939689912eSchristos }
294