1*bcda20f6Schristos /* $NetBSD: keystore.c,v 1.2 2025/01/26 16:25:23 christos Exp $ */ 29689912eSchristos 39689912eSchristos /* 49689912eSchristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 59689912eSchristos * 69689912eSchristos * SPDX-License-Identifier: MPL-2.0 79689912eSchristos * 89689912eSchristos * This Source Code Form is subject to the terms of the Mozilla Public 99689912eSchristos * License, v. 2.0. If a copy of the MPL was not distributed with this 109689912eSchristos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 119689912eSchristos * 129689912eSchristos * See the COPYRIGHT file distributed with this work for additional 139689912eSchristos * information regarding copyright ownership. 149689912eSchristos */ 159689912eSchristos 169689912eSchristos /*! \file */ 179689912eSchristos 189689912eSchristos #include <string.h> 199689912eSchristos 209689912eSchristos #include <isc/assertions.h> 219689912eSchristos #include <isc/buffer.h> 229689912eSchristos #include <isc/mem.h> 239689912eSchristos #include <isc/time.h> 249689912eSchristos #include <isc/util.h> 259689912eSchristos 269689912eSchristos #include <dns/fixedname.h> 279689912eSchristos #include <dns/keystore.h> 289689912eSchristos #include <dns/keyvalues.h> 299689912eSchristos 309689912eSchristos isc_result_t 319689912eSchristos dns_keystore_create(isc_mem_t *mctx, const char *name, const char *engine, 329689912eSchristos dns_keystore_t **kspp) { 339689912eSchristos dns_keystore_t *keystore; 349689912eSchristos 359689912eSchristos REQUIRE(name != NULL); 369689912eSchristos REQUIRE(kspp != NULL && *kspp == NULL); 379689912eSchristos 389689912eSchristos keystore = isc_mem_get(mctx, sizeof(*keystore)); 399689912eSchristos keystore->engine = engine; 409689912eSchristos keystore->mctx = NULL; 419689912eSchristos isc_mem_attach(mctx, &keystore->mctx); 429689912eSchristos 439689912eSchristos keystore->name = isc_mem_strdup(mctx, name); 449689912eSchristos isc_mutex_init(&keystore->lock); 459689912eSchristos 469689912eSchristos isc_refcount_init(&keystore->references, 1); 479689912eSchristos 489689912eSchristos ISC_LINK_INIT(keystore, link); 499689912eSchristos 509689912eSchristos keystore->directory = NULL; 519689912eSchristos keystore->pkcs11uri = NULL; 529689912eSchristos 539689912eSchristos keystore->magic = DNS_KEYSTORE_MAGIC; 549689912eSchristos *kspp = keystore; 559689912eSchristos 569689912eSchristos return ISC_R_SUCCESS; 579689912eSchristos } 589689912eSchristos 599689912eSchristos static inline void 609689912eSchristos dns__keystore_destroy(dns_keystore_t *keystore) { 619689912eSchristos char *name; 629689912eSchristos 639689912eSchristos REQUIRE(!ISC_LINK_LINKED(keystore, link)); 649689912eSchristos 659689912eSchristos isc_mutex_destroy(&keystore->lock); 669689912eSchristos name = UNCONST(keystore->name); 679689912eSchristos isc_mem_free(keystore->mctx, name); 689689912eSchristos if (keystore->directory != NULL) { 699689912eSchristos isc_mem_free(keystore->mctx, keystore->directory); 709689912eSchristos } 719689912eSchristos if (keystore->pkcs11uri != NULL) { 729689912eSchristos isc_mem_free(keystore->mctx, keystore->pkcs11uri); 739689912eSchristos } 749689912eSchristos isc_mem_putanddetach(&keystore->mctx, keystore, sizeof(*keystore)); 759689912eSchristos } 769689912eSchristos 779689912eSchristos #ifdef DNS_KEYSTORE_TRACE 789689912eSchristos ISC_REFCOUNT_TRACE_IMPL(dns_keystore, dns__keystore_destroy); 799689912eSchristos #else 809689912eSchristos ISC_REFCOUNT_IMPL(dns_keystore, dns__keystore_destroy); 819689912eSchristos #endif 829689912eSchristos 839689912eSchristos const char * 849689912eSchristos dns_keystore_name(dns_keystore_t *keystore) { 859689912eSchristos REQUIRE(DNS_KEYSTORE_VALID(keystore)); 869689912eSchristos 879689912eSchristos return keystore->name; 889689912eSchristos } 899689912eSchristos 909689912eSchristos const char * 919689912eSchristos dns_keystore_engine(dns_keystore_t *keystore) { 929689912eSchristos REQUIRE(DNS_KEYSTORE_VALID(keystore)); 939689912eSchristos 949689912eSchristos return keystore->engine; 959689912eSchristos } 969689912eSchristos 979689912eSchristos const char * 989689912eSchristos dns_keystore_directory(dns_keystore_t *keystore, const char *keydir) { 999689912eSchristos if (keystore == NULL) { 1009689912eSchristos return keydir; 1019689912eSchristos } 1029689912eSchristos 1039689912eSchristos INSIST(DNS_KEYSTORE_VALID(keystore)); 1049689912eSchristos 1059689912eSchristos if (keystore->directory == NULL) { 1069689912eSchristos return keydir; 1079689912eSchristos } 1089689912eSchristos 1099689912eSchristos return keystore->directory; 1109689912eSchristos } 1119689912eSchristos 1129689912eSchristos void 1139689912eSchristos dns_keystore_setdirectory(dns_keystore_t *keystore, const char *dir) { 1149689912eSchristos REQUIRE(DNS_KEYSTORE_VALID(keystore)); 1159689912eSchristos 1169689912eSchristos if (keystore->directory != NULL) { 1179689912eSchristos isc_mem_free(keystore->mctx, keystore->directory); 1189689912eSchristos } 1199689912eSchristos keystore->directory = (dir == NULL) 1209689912eSchristos ? NULL 1219689912eSchristos : isc_mem_strdup(keystore->mctx, dir); 1229689912eSchristos } 1239689912eSchristos 1249689912eSchristos const char * 1259689912eSchristos dns_keystore_pkcs11uri(dns_keystore_t *keystore) { 1269689912eSchristos REQUIRE(DNS_KEYSTORE_VALID(keystore)); 1279689912eSchristos 1289689912eSchristos return keystore->pkcs11uri; 1299689912eSchristos } 1309689912eSchristos 1319689912eSchristos void 1329689912eSchristos dns_keystore_setpkcs11uri(dns_keystore_t *keystore, const char *uri) { 1339689912eSchristos REQUIRE(DNS_KEYSTORE_VALID(keystore)); 1349689912eSchristos 1359689912eSchristos if (keystore->pkcs11uri != NULL) { 1369689912eSchristos isc_mem_free(keystore->mctx, keystore->pkcs11uri); 1379689912eSchristos } 1389689912eSchristos keystore->pkcs11uri = (uri == NULL) 1399689912eSchristos ? NULL 1409689912eSchristos : isc_mem_strdup(keystore->mctx, uri); 1419689912eSchristos } 1429689912eSchristos 1439689912eSchristos static isc_result_t 1449689912eSchristos buildpkcs11label(const char *uri, const dns_name_t *zname, const char *policy, 1459689912eSchristos int flags, isc_buffer_t *buf) { 1469689912eSchristos bool ksk = ((flags & DNS_KEYFLAG_KSK) != 0); 1479689912eSchristos char timebuf[18]; 1489689912eSchristos isc_time_t now = isc_time_now(); 1499689912eSchristos isc_result_t result; 1509689912eSchristos dns_fixedname_t fname; 1519689912eSchristos dns_name_t *pname = dns_fixedname_initname(&fname); 1529689912eSchristos 1539689912eSchristos /* uri + object */ 1549689912eSchristos if (isc_buffer_availablelength(buf) < strlen(uri) + strlen(";object=")) 1559689912eSchristos { 1569689912eSchristos return ISC_R_NOSPACE; 1579689912eSchristos } 1589689912eSchristos isc_buffer_putstr(buf, uri); 1599689912eSchristos isc_buffer_putstr(buf, ";object="); 1609689912eSchristos /* zone name */ 1619689912eSchristos result = dns_name_tofilenametext(zname, false, buf); 1629689912eSchristos if (result != ISC_R_SUCCESS) { 1639689912eSchristos return result; 1649689912eSchristos } 1659689912eSchristos /* 1669689912eSchristos * policy name 1679689912eSchristos * 1689689912eSchristos * Note that strlen(policy) is not the actual length, but if this 1699689912eSchristos * already does not fit, the escaped version returned from 1709689912eSchristos * dns_name_tofilenametext() certainly won't fit. 1719689912eSchristos */ 1729689912eSchristos if (isc_buffer_availablelength(buf) < (strlen(policy) + 1)) { 1739689912eSchristos return ISC_R_NOSPACE; 1749689912eSchristos } 1759689912eSchristos isc_buffer_putstr(buf, "-"); 1769689912eSchristos result = dns_name_fromstring(pname, policy, dns_rootname, 0, NULL); 1779689912eSchristos if (result != ISC_R_SUCCESS) { 1789689912eSchristos return result; 1799689912eSchristos } 1809689912eSchristos result = dns_name_tofilenametext(pname, false, buf); 1819689912eSchristos if (result != ISC_R_SUCCESS) { 1829689912eSchristos return result; 1839689912eSchristos } 1849689912eSchristos /* key type + current time */ 1859689912eSchristos isc_time_formatshorttimestamp(&now, timebuf, sizeof(timebuf)); 1869689912eSchristos return isc_buffer_printf(buf, "-%s-%s", ksk ? "ksk" : "zsk", timebuf); 1879689912eSchristos } 1889689912eSchristos 1899689912eSchristos isc_result_t 1909689912eSchristos dns_keystore_keygen(dns_keystore_t *keystore, const dns_name_t *origin, 1919689912eSchristos const char *policy, dns_rdataclass_t rdclass, 1929689912eSchristos isc_mem_t *mctx, uint32_t alg, int size, int flags, 1939689912eSchristos dst_key_t **dstkey) { 1949689912eSchristos isc_result_t result; 1959689912eSchristos dst_key_t *newkey = NULL; 1969689912eSchristos const char *uri = NULL; 1979689912eSchristos 1989689912eSchristos REQUIRE(DNS_KEYSTORE_VALID(keystore)); 1999689912eSchristos REQUIRE(dns_name_isvalid(origin)); 2009689912eSchristos REQUIRE(policy != NULL); 2019689912eSchristos REQUIRE(mctx != NULL); 2029689912eSchristos REQUIRE(dstkey != NULL && *dstkey == NULL); 2039689912eSchristos 2049689912eSchristos uri = dns_keystore_pkcs11uri(keystore); 2059689912eSchristos if (uri != NULL) { 2069689912eSchristos /* 2079689912eSchristos * Create the PKCS#11 label. 2089689912eSchristos * The label consists of the configured URI, and the object 2099689912eSchristos * parameter. The object parameter needs to be unique. We 2109689912eSchristos * know that for a given point in time, there will be at most 2119689912eSchristos * one key per type created for each zone in a given DNSSEC 2129689912eSchristos * policy. Hence the object is constructed out of the following 2139689912eSchristos * parts: the zone name, policy name, key type, and the 2149689912eSchristos * current time. 2159689912eSchristos * 2169689912eSchristos * The object may not contain any characters that conflict with 2179689912eSchristos * special characters in the PKCS#11 URI scheme syntax (see 2189689912eSchristos * RFC 7512, Section 2.3). Therefore, we mangle the zone name 2199689912eSchristos * and policy name through 'dns_name_tofilenametext()'. We 2209689912eSchristos * could create a new function to convert a name to PKCS#11 2219689912eSchristos * text, but this existing function will suffice. 2229689912eSchristos */ 2239689912eSchristos char label[NAME_MAX]; 2249689912eSchristos isc_buffer_t buf; 2259689912eSchristos isc_buffer_init(&buf, label, sizeof(label)); 2269689912eSchristos result = buildpkcs11label(uri, origin, policy, flags, &buf); 2279689912eSchristos if (result != ISC_R_SUCCESS) { 2289689912eSchristos char namebuf[DNS_NAME_FORMATSIZE]; 2299689912eSchristos dns_name_format(origin, namebuf, sizeof(namebuf)); 2309689912eSchristos isc_log_write( 2319689912eSchristos dns_lctx, DNS_LOGCATEGORY_DNSSEC, 2329689912eSchristos DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, 2339689912eSchristos "keystore: failed to create PKCS#11 object " 2349689912eSchristos "for zone %s, policy %s: %s", 2359689912eSchristos namebuf, policy, isc_result_totext(result)); 2369689912eSchristos return result; 2379689912eSchristos } 2389689912eSchristos 2399689912eSchristos /* Generate the key */ 2409689912eSchristos result = dst_key_generate(origin, alg, size, 0, flags, 2419689912eSchristos DNS_KEYPROTO_DNSSEC, rdclass, label, 2429689912eSchristos mctx, &newkey, NULL); 2439689912eSchristos 2449689912eSchristos if (result != ISC_R_SUCCESS) { 2459689912eSchristos isc_log_write( 2469689912eSchristos dns_lctx, DNS_LOGCATEGORY_DNSSEC, 2479689912eSchristos DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, 2489689912eSchristos "keystore: failed to generate PKCS#11 object " 2499689912eSchristos "%s: %s", 2509689912eSchristos label, isc_result_totext(result)); 2519689912eSchristos return result; 2529689912eSchristos } 2539689912eSchristos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, 2549689912eSchristos DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, 2559689912eSchristos "keystore: generated PKCS#11 object %s", label); 2569689912eSchristos } else { 2579689912eSchristos result = dst_key_generate(origin, alg, size, 0, flags, 2589689912eSchristos DNS_KEYPROTO_DNSSEC, rdclass, NULL, 2599689912eSchristos mctx, &newkey, NULL); 2609689912eSchristos } 2619689912eSchristos 2629689912eSchristos if (result == ISC_R_SUCCESS) { 2639689912eSchristos *dstkey = newkey; 2649689912eSchristos } 2659689912eSchristos return result; 2669689912eSchristos } 2679689912eSchristos 2689689912eSchristos isc_result_t 2699689912eSchristos dns_keystorelist_find(dns_keystorelist_t *list, const char *name, 2709689912eSchristos dns_keystore_t **kspp) { 2719689912eSchristos dns_keystore_t *keystore = NULL; 2729689912eSchristos 2739689912eSchristos REQUIRE(kspp != NULL && *kspp == NULL); 2749689912eSchristos 2759689912eSchristos if (list == NULL) { 2769689912eSchristos return ISC_R_NOTFOUND; 2779689912eSchristos } 2789689912eSchristos 2799689912eSchristos for (keystore = ISC_LIST_HEAD(*list); keystore != NULL; 2809689912eSchristos keystore = ISC_LIST_NEXT(keystore, link)) 2819689912eSchristos { 2829689912eSchristos if (strcmp(keystore->name, name) == 0) { 2839689912eSchristos break; 2849689912eSchristos } 2859689912eSchristos } 2869689912eSchristos 2879689912eSchristos if (keystore == NULL) { 2889689912eSchristos return ISC_R_NOTFOUND; 2899689912eSchristos } 2909689912eSchristos 2919689912eSchristos dns_keystore_attach(keystore, kspp); 2929689912eSchristos return ISC_R_SUCCESS; 2939689912eSchristos } 294