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