1 /* $NetBSD: keystore.c,v 1.3 2025/07/17 19:01:45 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