Home | History | Annotate | Line # | Download | only in dns
keystore.c revision 1.1.1.1
      1 /*	$NetBSD: keystore.c,v 1.1.1.1 2025/01/26 16:12:33 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <string.h>
     19 
     20 #include <isc/assertions.h>
     21 #include <isc/buffer.h>
     22 #include <isc/mem.h>
     23 #include <isc/time.h>
     24 #include <isc/util.h>
     25 
     26 #include <dns/fixedname.h>
     27 #include <dns/keystore.h>
     28 #include <dns/keyvalues.h>
     29 
     30 isc_result_t
     31 dns_keystore_create(isc_mem_t *mctx, const char *name, const char *engine,
     32 		    dns_keystore_t **kspp) {
     33 	dns_keystore_t *keystore;
     34 
     35 	REQUIRE(name != NULL);
     36 	REQUIRE(kspp != NULL && *kspp == NULL);
     37 
     38 	keystore = isc_mem_get(mctx, sizeof(*keystore));
     39 	keystore->engine = engine;
     40 	keystore->mctx = NULL;
     41 	isc_mem_attach(mctx, &keystore->mctx);
     42 
     43 	keystore->name = isc_mem_strdup(mctx, name);
     44 	isc_mutex_init(&keystore->lock);
     45 
     46 	isc_refcount_init(&keystore->references, 1);
     47 
     48 	ISC_LINK_INIT(keystore, link);
     49 
     50 	keystore->directory = NULL;
     51 	keystore->pkcs11uri = NULL;
     52 
     53 	keystore->magic = DNS_KEYSTORE_MAGIC;
     54 	*kspp = keystore;
     55 
     56 	return ISC_R_SUCCESS;
     57 }
     58 
     59 static inline void
     60 dns__keystore_destroy(dns_keystore_t *keystore) {
     61 	char *name;
     62 
     63 	REQUIRE(!ISC_LINK_LINKED(keystore, link));
     64 
     65 	isc_mutex_destroy(&keystore->lock);
     66 	name = UNCONST(keystore->name);
     67 	isc_mem_free(keystore->mctx, name);
     68 	if (keystore->directory != NULL) {
     69 		isc_mem_free(keystore->mctx, keystore->directory);
     70 	}
     71 	if (keystore->pkcs11uri != NULL) {
     72 		isc_mem_free(keystore->mctx, keystore->pkcs11uri);
     73 	}
     74 	isc_mem_putanddetach(&keystore->mctx, keystore, sizeof(*keystore));
     75 }
     76 
     77 #ifdef DNS_KEYSTORE_TRACE
     78 ISC_REFCOUNT_TRACE_IMPL(dns_keystore, dns__keystore_destroy);
     79 #else
     80 ISC_REFCOUNT_IMPL(dns_keystore, dns__keystore_destroy);
     81 #endif
     82 
     83 const char *
     84 dns_keystore_name(dns_keystore_t *keystore) {
     85 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
     86 
     87 	return keystore->name;
     88 }
     89 
     90 const char *
     91 dns_keystore_engine(dns_keystore_t *keystore) {
     92 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
     93 
     94 	return keystore->engine;
     95 }
     96 
     97 const char *
     98 dns_keystore_directory(dns_keystore_t *keystore, const char *keydir) {
     99 	if (keystore == NULL) {
    100 		return keydir;
    101 	}
    102 
    103 	INSIST(DNS_KEYSTORE_VALID(keystore));
    104 
    105 	if (keystore->directory == NULL) {
    106 		return keydir;
    107 	}
    108 
    109 	return keystore->directory;
    110 }
    111 
    112 void
    113 dns_keystore_setdirectory(dns_keystore_t *keystore, const char *dir) {
    114 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
    115 
    116 	if (keystore->directory != NULL) {
    117 		isc_mem_free(keystore->mctx, keystore->directory);
    118 	}
    119 	keystore->directory = (dir == NULL)
    120 				      ? NULL
    121 				      : isc_mem_strdup(keystore->mctx, dir);
    122 }
    123 
    124 const char *
    125 dns_keystore_pkcs11uri(dns_keystore_t *keystore) {
    126 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
    127 
    128 	return keystore->pkcs11uri;
    129 }
    130 
    131 void
    132 dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri) {
    133 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
    134 
    135 	if (keystore->pkcs11uri != NULL) {
    136 		isc_mem_free(keystore->mctx, keystore->pkcs11uri);
    137 	}
    138 	keystore->pkcs11uri = (uri == NULL)
    139 				      ? NULL
    140 				      : isc_mem_strdup(keystore->mctx, uri);
    141 }
    142 
    143 static isc_result_t
    144 buildpkcs11label(const char *uri, const dns_name_t *zname, const char *policy,
    145 		 int flags, isc_buffer_t *buf) {
    146 	bool ksk = ((flags & DNS_KEYFLAG_KSK) != 0);
    147 	char timebuf[18];
    148 	isc_time_t now = isc_time_now();
    149 	isc_result_t result;
    150 	dns_fixedname_t fname;
    151 	dns_name_t *pname = dns_fixedname_initname(&fname);
    152 
    153 	/* uri + object */
    154 	if (isc_buffer_availablelength(buf) < strlen(uri) + strlen(";object="))
    155 	{
    156 		return ISC_R_NOSPACE;
    157 	}
    158 	isc_buffer_putstr(buf, uri);
    159 	isc_buffer_putstr(buf, ";object=");
    160 	/* zone name */
    161 	result = dns_name_tofilenametext(zname, false, buf);
    162 	if (result != ISC_R_SUCCESS) {
    163 		return result;
    164 	}
    165 	/*
    166 	 * policy name
    167 	 *
    168 	 * Note that strlen(policy) is not the actual length, but if this
    169 	 * already does not fit, the escaped version returned from
    170 	 * dns_name_tofilenametext() certainly won't fit.
    171 	 */
    172 	if (isc_buffer_availablelength(buf) < (strlen(policy) + 1)) {
    173 		return ISC_R_NOSPACE;
    174 	}
    175 	isc_buffer_putstr(buf, "-");
    176 	result = dns_name_fromstring(pname, policy, dns_rootname, 0, NULL);
    177 	if (result != ISC_R_SUCCESS) {
    178 		return result;
    179 	}
    180 	result = dns_name_tofilenametext(pname, false, buf);
    181 	if (result != ISC_R_SUCCESS) {
    182 		return result;
    183 	}
    184 	/* key type + current time */
    185 	isc_time_formatshorttimestamp(&now, timebuf, sizeof(timebuf));
    186 	return isc_buffer_printf(buf, "-%s-%s", ksk ? "ksk" : "zsk", timebuf);
    187 }
    188 
    189 isc_result_t
    190 dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin,
    191 		    const char *policy, dns_rdataclass_t rdclass,
    192 		    isc_mem_t *mctx, uint32_t alg, int size, int flags,
    193 		    dst_key_t **dstkey) {
    194 	isc_result_t result;
    195 	dst_key_t *newkey = NULL;
    196 	const char *uri = NULL;
    197 
    198 	REQUIRE(DNS_KEYSTORE_VALID(keystore));
    199 	REQUIRE(dns_name_isvalid(origin));
    200 	REQUIRE(policy != NULL);
    201 	REQUIRE(mctx != NULL);
    202 	REQUIRE(dstkey != NULL && *dstkey == NULL);
    203 
    204 	uri = dns_keystore_pkcs11uri(keystore);
    205 	if (uri != NULL) {
    206 		/*
    207 		 * Create the PKCS#11 label.
    208 		 * The label consists of the configured URI, and the object
    209 		 * parameter.  The object parameter needs to be unique.  We
    210 		 * know that for a given point in time, there will be at most
    211 		 * one key per type created for each zone in a given DNSSEC
    212 		 * policy.  Hence the object is constructed out of the following
    213 		 * parts: the zone name, policy name, key type, and the
    214 		 * current time.
    215 		 *
    216 		 * The object may not contain any characters that conflict with
    217 		 * special characters in the PKCS#11 URI scheme syntax (see
    218 		 * RFC 7512, Section 2.3). Therefore, we mangle the zone name
    219 		 * and policy name through 'dns_name_tofilenametext()'. We
    220 		 * could create a new function to convert a name to PKCS#11
    221 		 * text, but this existing function will suffice.
    222 		 */
    223 		char label[NAME_MAX];
    224 		isc_buffer_t buf;
    225 		isc_buffer_init(&buf, label, sizeof(label));
    226 		result = buildpkcs11label(uri, origin, policy, flags, &buf);
    227 		if (result != ISC_R_SUCCESS) {
    228 			char namebuf[DNS_NAME_FORMATSIZE];
    229 			dns_name_format(origin, namebuf, sizeof(namebuf));
    230 			isc_log_write(
    231 				dns_lctx, DNS_LOGCATEGORY_DNSSEC,
    232 				DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR,
    233 				"keystore: failed to create PKCS#11 object "
    234 				"for zone %s, policy %s: %s",
    235 				namebuf, policy, isc_result_totext(result));
    236 			return result;
    237 		}
    238 
    239 		/* Generate the key */
    240 		result = dst_key_generate(origin, alg, size, 0, flags,
    241 					  DNS_KEYPROTO_DNSSEC, rdclass, label,
    242 					  mctx, &newkey, NULL);
    243 
    244 		if (result != ISC_R_SUCCESS) {
    245 			isc_log_write(
    246 				dns_lctx, DNS_LOGCATEGORY_DNSSEC,
    247 				DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR,
    248 				"keystore: failed to generate PKCS#11 object "
    249 				"%s: %s",
    250 				label, isc_result_totext(result));
    251 			return result;
    252 		}
    253 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
    254 			      DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR,
    255 			      "keystore: generated PKCS#11 object %s", label);
    256 	} else {
    257 		result = dst_key_generate(origin, alg, size, 0, flags,
    258 					  DNS_KEYPROTO_DNSSEC, rdclass, NULL,
    259 					  mctx, &newkey, NULL);
    260 	}
    261 
    262 	if (result == ISC_R_SUCCESS) {
    263 		*dstkey = newkey;
    264 	}
    265 	return result;
    266 }
    267 
    268 isc_result_t
    269 dns_keystorelist_find(dns_keystorelist_t *list, const char *name,
    270 		      dns_keystore_t **kspp) {
    271 	dns_keystore_t *keystore = NULL;
    272 
    273 	REQUIRE(kspp != NULL && *kspp == NULL);
    274 
    275 	if (list == NULL) {
    276 		return ISC_R_NOTFOUND;
    277 	}
    278 
    279 	for (keystore = ISC_LIST_HEAD(*list); keystore != NULL;
    280 	     keystore = ISC_LIST_NEXT(keystore, link))
    281 	{
    282 		if (strcmp(keystore->name, name) == 0) {
    283 			break;
    284 		}
    285 	}
    286 
    287 	if (keystore == NULL) {
    288 		return ISC_R_NOTFOUND;
    289 	}
    290 
    291 	dns_keystore_attach(keystore, kspp);
    292 	return ISC_R_SUCCESS;
    293 }
    294