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