xref: /netbsd-src/external/mpl/bind/dist/lib/dns/keystore.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: keystore.c,v 1.2 2025/01/26 16:25:23 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