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