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