1*bcda20f6Schristos /* $NetBSD: openssl_link.c,v 1.12 2025/01/26 16:25:23 christos Exp $ */ 2d68c78b8Schristos 3d68c78b8Schristos /* 48596601aSchristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 58596601aSchristos * 68596601aSchristos * SPDX-License-Identifier: MPL-2.0 AND ISC 7d68c78b8Schristos * 8d68c78b8Schristos * This Source Code Form is subject to the terms of the Mozilla Public 9d68c78b8Schristos * License, v. 2.0. If a copy of the MPL was not distributed with this 10fce770bdSchristos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11d68c78b8Schristos * 12d68c78b8Schristos * See the COPYRIGHT file distributed with this work for additional 13d68c78b8Schristos * information regarding copyright ownership. 148596601aSchristos */ 158596601aSchristos 168596601aSchristos /* 178596601aSchristos * Copyright (C) Network Associates, Inc. 18d68c78b8Schristos * 19d68c78b8Schristos * Permission to use, copy, modify, and/or distribute this software for any 20d68c78b8Schristos * purpose with or without fee is hereby granted, provided that the above 21d68c78b8Schristos * copyright notice and this permission notice appear in all copies. 22d68c78b8Schristos * 23d68c78b8Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS 24d68c78b8Schristos * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 25d68c78b8Schristos * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE 26d68c78b8Schristos * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 27d68c78b8Schristos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 28d68c78b8Schristos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 29d68c78b8Schristos * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30d68c78b8Schristos */ 31d68c78b8Schristos 32*bcda20f6Schristos #include <isc/fips.h> 33d68c78b8Schristos #include <isc/mem.h> 34d68c78b8Schristos #include <isc/mutex.h> 35d68c78b8Schristos #include <isc/mutexblock.h> 36bb5aa156Schristos #include <isc/result.h> 37d68c78b8Schristos #include <isc/string.h> 38d68c78b8Schristos #include <isc/thread.h> 39bb5aa156Schristos #include <isc/tls.h> 40d68c78b8Schristos #include <isc/util.h> 41d68c78b8Schristos 42d68c78b8Schristos #include <dns/log.h> 43d68c78b8Schristos 44d68c78b8Schristos #include "dst_internal.h" 45d68c78b8Schristos #include "dst_openssl.h" 46d68c78b8Schristos 47bb5aa156Schristos #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 48d68c78b8Schristos #include <openssl/engine.h> 49bb5aa156Schristos #endif /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ 50*bcda20f6Schristos #if OPENSSL_VERSION_NUMBER >= 0x30000000L 51*bcda20f6Schristos #include <openssl/core_names.h> 52*bcda20f6Schristos #include <openssl/store.h> 53*bcda20f6Schristos #endif 54d68c78b8Schristos 55bb5aa156Schristos #include "openssl_shim.h" 56bb5aa156Schristos 57*bcda20f6Schristos #define DST_RET(a) \ 58*bcda20f6Schristos { \ 59*bcda20f6Schristos ret = a; \ 60*bcda20f6Schristos goto err; \ 61*bcda20f6Schristos } 62*bcda20f6Schristos 63bb5aa156Schristos #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 64*bcda20f6Schristos static ENGINE *global_engine = NULL; 65bb5aa156Schristos #endif /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ 66d68c78b8Schristos 67d4a20c3eSchristos static void 68d4a20c3eSchristos enable_fips_mode(void) { 69*bcda20f6Schristos #if defined(ENABLE_FIPS_MODE) 70*bcda20f6Schristos if (isc_fips_mode()) { 71d4a20c3eSchristos /* 72d4a20c3eSchristos * FIPS mode is already enabled. 73d4a20c3eSchristos */ 74d4a20c3eSchristos return; 75d68c78b8Schristos } 76d68c78b8Schristos 77*bcda20f6Schristos if (isc_fips_set_mode(1) != ISC_R_SUCCESS) { 78d4a20c3eSchristos dst__openssl_toresult2("FIPS_mode_set", DST_R_OPENSSLFAILURE); 79dff692fcSchristos exit(EXIT_FAILURE); 80d68c78b8Schristos } 81*bcda20f6Schristos #endif 82d68c78b8Schristos } 83d68c78b8Schristos 84d68c78b8Schristos isc_result_t 8599a1c298Schristos dst__openssl_init(const char *engine) { 86d4a20c3eSchristos enable_fips_mode(); 87d4a20c3eSchristos 885606745fSchristos if (engine != NULL && *engine == '\0') { 89d68c78b8Schristos engine = NULL; 905606745fSchristos } 91d68c78b8Schristos 92*bcda20f6Schristos if (engine == NULL) { 93*bcda20f6Schristos return ISC_R_SUCCESS; 94*bcda20f6Schristos } 95*bcda20f6Schristos 96*bcda20f6Schristos #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 97*bcda20f6Schristos global_engine = ENGINE_by_id(engine); 98*bcda20f6Schristos if (global_engine == NULL) { 99d68c78b8Schristos goto cleanup_rm; 100d68c78b8Schristos } 101*bcda20f6Schristos if (!ENGINE_init(global_engine)) { 102bb5aa156Schristos goto cleanup_rm; 103bb5aa156Schristos } 104d68c78b8Schristos /* This will init the engine. */ 105*bcda20f6Schristos if (!ENGINE_set_default(global_engine, ENGINE_METHOD_ALL)) { 106bb5aa156Schristos goto cleanup_init; 107d68c78b8Schristos } 108*bcda20f6Schristos return ISC_R_SUCCESS; 109bb5aa156Schristos cleanup_init: 110*bcda20f6Schristos ENGINE_finish(global_engine); 111d68c78b8Schristos cleanup_rm: 112*bcda20f6Schristos if (global_engine != NULL) { 113*bcda20f6Schristos ENGINE_free(global_engine); 1145606745fSchristos } 115bb5aa156Schristos ERR_clear_error(); 116*bcda20f6Schristos global_engine = NULL; 117bb5aa156Schristos #endif /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ 118*bcda20f6Schristos return DST_R_NOENGINE; 119d68c78b8Schristos } 120d68c78b8Schristos 121d68c78b8Schristos void 122d68c78b8Schristos dst__openssl_destroy(void) { 123bb5aa156Schristos #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 124*bcda20f6Schristos if (global_engine != NULL) { 125*bcda20f6Schristos ENGINE_finish(global_engine); 126*bcda20f6Schristos ENGINE_free(global_engine); 1275606745fSchristos } 128*bcda20f6Schristos global_engine = NULL; 129bb5aa156Schristos #endif /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ 130d68c78b8Schristos } 131d68c78b8Schristos 132d68c78b8Schristos static isc_result_t 133d68c78b8Schristos toresult(isc_result_t fallback) { 134d68c78b8Schristos isc_result_t result = fallback; 135d4a20c3eSchristos unsigned long err = ERR_peek_error(); 136d4a20c3eSchristos #if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) 137d68c78b8Schristos int lib = ERR_GET_LIB(err); 1385606745fSchristos #endif /* if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) */ 139d68c78b8Schristos int reason = ERR_GET_REASON(err); 140d68c78b8Schristos 141d68c78b8Schristos switch (reason) { 142d68c78b8Schristos /* 143d68c78b8Schristos * ERR_* errors are globally unique; others 144d68c78b8Schristos * are unique per sublibrary 145d68c78b8Schristos */ 146d68c78b8Schristos case ERR_R_MALLOC_FAILURE: 147d68c78b8Schristos result = ISC_R_NOMEMORY; 148d68c78b8Schristos break; 149d68c78b8Schristos default: 150d4a20c3eSchristos #if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) 151d68c78b8Schristos if (lib == ERR_R_ECDSA_LIB && 152903adeddSchristos reason == ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) 153903adeddSchristos { 154d68c78b8Schristos result = ISC_R_NOENTROPY; 155d68c78b8Schristos break; 156d68c78b8Schristos } 1575606745fSchristos #endif /* if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) */ 158d68c78b8Schristos break; 159d68c78b8Schristos } 160d68c78b8Schristos 161*bcda20f6Schristos return result; 162d68c78b8Schristos } 163d68c78b8Schristos 164d68c78b8Schristos isc_result_t 165d68c78b8Schristos dst__openssl_toresult(isc_result_t fallback) { 166d68c78b8Schristos isc_result_t result; 167d68c78b8Schristos 168d68c78b8Schristos result = toresult(fallback); 169d68c78b8Schristos 170d68c78b8Schristos ERR_clear_error(); 171*bcda20f6Schristos return result; 172d68c78b8Schristos } 173d68c78b8Schristos 174d68c78b8Schristos isc_result_t 175*bcda20f6Schristos dst___openssl_toresult2(const char *funcname, isc_result_t fallback, 176*bcda20f6Schristos const char *file, int line) { 177*bcda20f6Schristos return dst___openssl_toresult3(DNS_LOGCATEGORY_GENERAL, funcname, 178*bcda20f6Schristos fallback, file, line); 179d68c78b8Schristos } 180d68c78b8Schristos 181d68c78b8Schristos isc_result_t 182*bcda20f6Schristos dst___openssl_toresult3(isc_logcategory_t *category, const char *funcname, 183*bcda20f6Schristos isc_result_t fallback, const char *file, int line) { 184d68c78b8Schristos isc_result_t result; 185d68c78b8Schristos unsigned long err; 186*bcda20f6Schristos const char *func, *data; 187*bcda20f6Schristos int flags; 188d68c78b8Schristos char buf[256]; 189d68c78b8Schristos 190d68c78b8Schristos result = toresult(fallback); 191d68c78b8Schristos 1925606745fSchristos isc_log_write(dns_lctx, category, DNS_LOGMODULE_CRYPTO, ISC_LOG_WARNING, 193*bcda20f6Schristos "%s (%s:%d) failed (%s)", funcname, file, line, 194*bcda20f6Schristos isc_result_totext(result)); 195d68c78b8Schristos 1965606745fSchristos if (result == ISC_R_NOMEMORY) { 197d68c78b8Schristos goto done; 1985606745fSchristos } 199d68c78b8Schristos 200d68c78b8Schristos for (;;) { 201bb5aa156Schristos err = ERR_get_error_all(&file, &line, &func, &data, &flags); 2025606745fSchristos if (err == 0U) { 203d68c78b8Schristos goto done; 2045606745fSchristos } 205d68c78b8Schristos ERR_error_string_n(err, buf, sizeof(buf)); 2065606745fSchristos isc_log_write(dns_lctx, category, DNS_LOGMODULE_CRYPTO, 2075606745fSchristos ISC_LOG_INFO, "%s:%s:%d:%s", buf, file, line, 208d4a20c3eSchristos ((flags & ERR_TXT_STRING) != 0) ? data : ""); 209d68c78b8Schristos } 210d68c78b8Schristos 211d68c78b8Schristos done: 212d68c78b8Schristos ERR_clear_error(); 213*bcda20f6Schristos return result; 214d68c78b8Schristos } 215d68c78b8Schristos 216bb5aa156Schristos #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 217d68c78b8Schristos ENGINE * 218d68c78b8Schristos dst__openssl_getengine(const char *engine) { 2195606745fSchristos if (engine == NULL) { 220*bcda20f6Schristos return NULL; 221d68c78b8Schristos } 222*bcda20f6Schristos if (global_engine == NULL) { 223*bcda20f6Schristos return NULL; 2245606745fSchristos } 225*bcda20f6Schristos if (strcmp(engine, ENGINE_get_id(global_engine)) == 0) { 226*bcda20f6Schristos return global_engine; 2275606745fSchristos } 228*bcda20f6Schristos return NULL; 2295606745fSchristos } 230bb5aa156Schristos #endif /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ 231d68c78b8Schristos 232*bcda20f6Schristos static isc_result_t 233*bcda20f6Schristos dst__openssl_fromlabel_engine(int key_base_id, const char *engine, 234*bcda20f6Schristos const char *label, const char *pin, 235*bcda20f6Schristos EVP_PKEY **ppub, EVP_PKEY **ppriv) { 236*bcda20f6Schristos #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 237*bcda20f6Schristos isc_result_t ret = ISC_R_SUCCESS; 238*bcda20f6Schristos ENGINE *e = NULL; 239*bcda20f6Schristos 240*bcda20f6Schristos UNUSED(pin); 241*bcda20f6Schristos 242*bcda20f6Schristos e = dst__openssl_getengine(engine); 243*bcda20f6Schristos if (e == NULL) { 244*bcda20f6Schristos DST_RET(dst__openssl_toresult(DST_R_NOENGINE)); 245*bcda20f6Schristos } 246*bcda20f6Schristos 247*bcda20f6Schristos *ppub = ENGINE_load_public_key(e, label, NULL, NULL); 248*bcda20f6Schristos if (*ppub == NULL) { 249*bcda20f6Schristos DST_RET(dst__openssl_toresult2("ENGINE_load_public_key", 250*bcda20f6Schristos DST_R_OPENSSLFAILURE)); 251*bcda20f6Schristos } 252*bcda20f6Schristos if (EVP_PKEY_base_id(*ppub) != key_base_id) { 253*bcda20f6Schristos DST_RET(DST_R_BADKEYTYPE); 254*bcda20f6Schristos } 255*bcda20f6Schristos 256*bcda20f6Schristos *ppriv = ENGINE_load_private_key(e, label, NULL, NULL); 257*bcda20f6Schristos if (*ppriv == NULL) { 258*bcda20f6Schristos DST_RET(dst__openssl_toresult2("ENGINE_load_private_key", 259*bcda20f6Schristos DST_R_OPENSSLFAILURE)); 260*bcda20f6Schristos } 261*bcda20f6Schristos if (EVP_PKEY_base_id(*ppriv) != key_base_id) { 262*bcda20f6Schristos DST_RET(DST_R_BADKEYTYPE); 263*bcda20f6Schristos } 264*bcda20f6Schristos err: 265*bcda20f6Schristos return ret; 266*bcda20f6Schristos #else /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ 267*bcda20f6Schristos UNUSED(key_base_id); 268*bcda20f6Schristos UNUSED(engine); 269*bcda20f6Schristos UNUSED(label); 270*bcda20f6Schristos UNUSED(pin); 271*bcda20f6Schristos UNUSED(ppub); 272*bcda20f6Schristos UNUSED(ppriv); 273*bcda20f6Schristos return DST_R_NOENGINE; 274*bcda20f6Schristos #endif /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ 275*bcda20f6Schristos } 276*bcda20f6Schristos 277*bcda20f6Schristos static isc_result_t 278*bcda20f6Schristos dst__openssl_fromlabel_provider(int key_base_id, const char *label, 279*bcda20f6Schristos const char *pin, EVP_PKEY **ppub, 280*bcda20f6Schristos EVP_PKEY **ppriv) { 281*bcda20f6Schristos #if OPENSSL_VERSION_NUMBER >= 0x30000000L 282*bcda20f6Schristos isc_result_t ret = DST_R_OPENSSLFAILURE; 283*bcda20f6Schristos OSSL_STORE_CTX *ctx = NULL; 284*bcda20f6Schristos 285*bcda20f6Schristos UNUSED(pin); 286*bcda20f6Schristos 287*bcda20f6Schristos ctx = OSSL_STORE_open(label, NULL, NULL, NULL, NULL); 288*bcda20f6Schristos if (!ctx) { 289*bcda20f6Schristos DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); 290*bcda20f6Schristos } 291*bcda20f6Schristos 292*bcda20f6Schristos while (!OSSL_STORE_eof(ctx)) { 293*bcda20f6Schristos OSSL_STORE_INFO *info = OSSL_STORE_load(ctx); 294*bcda20f6Schristos if (info == NULL) { 295*bcda20f6Schristos continue; 296*bcda20f6Schristos } 297*bcda20f6Schristos switch (OSSL_STORE_INFO_get_type(info)) { 298*bcda20f6Schristos case OSSL_STORE_INFO_PKEY: 299*bcda20f6Schristos if (*ppriv != NULL) { 300*bcda20f6Schristos OSSL_STORE_INFO_free(info); 301*bcda20f6Schristos DST_RET(DST_R_INVALIDPRIVATEKEY); 302*bcda20f6Schristos } 303*bcda20f6Schristos *ppriv = OSSL_STORE_INFO_get1_PKEY(info); 304*bcda20f6Schristos if (EVP_PKEY_get_base_id(*ppriv) != key_base_id) { 305*bcda20f6Schristos OSSL_STORE_INFO_free(info); 306*bcda20f6Schristos DST_RET(DST_R_BADKEYTYPE); 307*bcda20f6Schristos } 308*bcda20f6Schristos break; 309*bcda20f6Schristos case OSSL_STORE_INFO_PUBKEY: 310*bcda20f6Schristos if (*ppub != NULL) { 311*bcda20f6Schristos OSSL_STORE_INFO_free(info); 312*bcda20f6Schristos DST_RET(DST_R_INVALIDPUBLICKEY); 313*bcda20f6Schristos } 314*bcda20f6Schristos *ppub = OSSL_STORE_INFO_get1_PUBKEY(info); 315*bcda20f6Schristos if (EVP_PKEY_get_base_id(*ppub) != key_base_id) { 316*bcda20f6Schristos OSSL_STORE_INFO_free(info); 317*bcda20f6Schristos DST_RET(DST_R_BADKEYTYPE); 318*bcda20f6Schristos } 319*bcda20f6Schristos break; 320*bcda20f6Schristos } 321*bcda20f6Schristos OSSL_STORE_INFO_free(info); 322*bcda20f6Schristos } 323*bcda20f6Schristos if (*ppriv != NULL && *ppub != NULL) { 324*bcda20f6Schristos ret = ISC_R_SUCCESS; 325*bcda20f6Schristos } 326*bcda20f6Schristos err: 327*bcda20f6Schristos OSSL_STORE_close(ctx); 328*bcda20f6Schristos return ret; 329*bcda20f6Schristos #else 330*bcda20f6Schristos UNUSED(key_base_id); 331*bcda20f6Schristos UNUSED(label); 332*bcda20f6Schristos UNUSED(pin); 333*bcda20f6Schristos UNUSED(ppub); 334*bcda20f6Schristos UNUSED(ppriv); 335*bcda20f6Schristos return DST_R_OPENSSLFAILURE; 336*bcda20f6Schristos #endif 337*bcda20f6Schristos } 338*bcda20f6Schristos 339*bcda20f6Schristos isc_result_t 340*bcda20f6Schristos dst__openssl_fromlabel(int key_base_id, const char *engine, const char *label, 341*bcda20f6Schristos const char *pin, EVP_PKEY **ppub, EVP_PKEY **ppriv) { 342*bcda20f6Schristos if (engine == NULL) { 343*bcda20f6Schristos return dst__openssl_fromlabel_provider(key_base_id, label, pin, 344*bcda20f6Schristos ppub, ppriv); 345*bcda20f6Schristos } 346*bcda20f6Schristos 347*bcda20f6Schristos if (*ppub != NULL) { 348*bcda20f6Schristos EVP_PKEY_free(*ppub); 349*bcda20f6Schristos *ppub = NULL; 350*bcda20f6Schristos } 351*bcda20f6Schristos 352*bcda20f6Schristos if (*ppriv != NULL) { 353*bcda20f6Schristos EVP_PKEY_free(*ppriv); 354*bcda20f6Schristos *ppriv = NULL; 355*bcda20f6Schristos } 356*bcda20f6Schristos 357*bcda20f6Schristos return dst__openssl_fromlabel_engine(key_base_id, engine, label, pin, 358*bcda20f6Schristos ppub, ppriv); 359*bcda20f6Schristos } 360*bcda20f6Schristos 361*bcda20f6Schristos bool 362*bcda20f6Schristos dst__openssl_keypair_compare(const dst_key_t *key1, const dst_key_t *key2) { 363*bcda20f6Schristos EVP_PKEY *pkey1 = key1->keydata.pkeypair.pub; 364*bcda20f6Schristos EVP_PKEY *pkey2 = key2->keydata.pkeypair.pub; 365*bcda20f6Schristos 366*bcda20f6Schristos if (pkey1 == pkey2) { 367*bcda20f6Schristos return true; 368*bcda20f6Schristos } else if (pkey1 == NULL || pkey2 == NULL) { 369*bcda20f6Schristos return false; 370*bcda20f6Schristos } 371*bcda20f6Schristos 372*bcda20f6Schristos /* `EVP_PKEY_eq` checks only the public components and parameters. */ 373*bcda20f6Schristos if (EVP_PKEY_eq(pkey1, pkey2) != 1) { 374*bcda20f6Schristos return false; 375*bcda20f6Schristos } 376*bcda20f6Schristos /* The private key presence must be same for keys to match. */ 377*bcda20f6Schristos if ((key1->keydata.pkeypair.priv != NULL) != 378*bcda20f6Schristos (key2->keydata.pkeypair.priv != NULL)) 379*bcda20f6Schristos { 380*bcda20f6Schristos return false; 381*bcda20f6Schristos } 382*bcda20f6Schristos return true; 383*bcda20f6Schristos } 384*bcda20f6Schristos 385*bcda20f6Schristos bool 386*bcda20f6Schristos dst__openssl_keypair_isprivate(const dst_key_t *key) { 387*bcda20f6Schristos return key->keydata.pkeypair.priv != NULL; 388*bcda20f6Schristos } 389*bcda20f6Schristos 390*bcda20f6Schristos void 391*bcda20f6Schristos dst__openssl_keypair_destroy(dst_key_t *key) { 392*bcda20f6Schristos if (key->keydata.pkeypair.priv != key->keydata.pkeypair.pub) { 393*bcda20f6Schristos EVP_PKEY_free(key->keydata.pkeypair.priv); 394*bcda20f6Schristos } 395*bcda20f6Schristos EVP_PKEY_free(key->keydata.pkeypair.pub); 396*bcda20f6Schristos key->keydata.pkeypair.pub = NULL; 397*bcda20f6Schristos key->keydata.pkeypair.priv = NULL; 398*bcda20f6Schristos } 399*bcda20f6Schristos 400d68c78b8Schristos /*! \file */ 401