1/* $NetBSD: tls.c,v 1.1 2024/02/18 20:57:50 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#include <inttypes.h> 17 18#include <openssl/bn.h> 19#include <openssl/conf.h> 20#include <openssl/crypto.h> 21#include <openssl/err.h> 22#include <openssl/opensslv.h> 23#include <openssl/rand.h> 24#include <openssl/rsa.h> 25 26#include <isc/atomic.h> 27#include <isc/log.h> 28#include <isc/mutex.h> 29#include <isc/mutexblock.h> 30#include <isc/once.h> 31#include <isc/thread.h> 32#include <isc/util.h> 33 34#include "openssl_shim.h" 35#include "tls_p.h" 36 37static isc_once_t init_once = ISC_ONCE_INIT; 38static isc_once_t shut_once = ISC_ONCE_INIT; 39static atomic_bool init_done = false; 40static atomic_bool shut_done = false; 41 42#if OPENSSL_VERSION_NUMBER < 0x10100000L 43static isc_mutex_t *locks = NULL; 44static int nlocks; 45 46static void 47isc__tls_lock_callback(int mode, int type, const char *file, int line) { 48 UNUSED(file); 49 UNUSED(line); 50 if ((mode & CRYPTO_LOCK) != 0) { 51 LOCK(&locks[type]); 52 } else { 53 UNLOCK(&locks[type]); 54 } 55} 56 57static void 58isc__tls_set_thread_id(CRYPTO_THREADID *id) { 59 CRYPTO_THREADID_set_numeric(id, (unsigned long)isc_thread_self()); 60} 61#endif 62 63static void 64tls_initialize(void) { 65 REQUIRE(!atomic_load(&init_done)); 66 67#if OPENSSL_VERSION_NUMBER >= 0x10100000L 68 RUNTIME_CHECK(OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN | 69 OPENSSL_INIT_LOAD_CONFIG, 70 NULL) == 1); 71#else 72 nlocks = CRYPTO_num_locks(); 73 /* 74 * We can't use isc_mem API here, because it's called too 75 * early and when the isc_mem_debugging flags are changed 76 * later and ISC_MEM_DEBUGSIZE or ISC_MEM_DEBUGCTX flags are 77 * added, neither isc_mem_put() nor isc_mem_free() can be used 78 * to free up the memory allocated here because the flags were 79 * not set when calling isc_mem_get() or isc_mem_allocate() 80 * here. 81 * 82 * Actually, since this is a single allocation at library load 83 * and deallocation at library unload, using the standard 84 * allocator without the tracking is fine for this purpose. 85 */ 86 locks = calloc(nlocks, sizeof(locks[0])); 87 isc_mutexblock_init(locks, nlocks); 88 CRYPTO_set_locking_callback(isc__tls_lock_callback); 89 CRYPTO_THREADID_set_callback(isc__tls_set_thread_id); 90 91 CRYPTO_malloc_init(); 92 ERR_load_crypto_strings(); 93 SSL_load_error_strings(); 94 SSL_library_init(); 95 96#if !defined(OPENSSL_NO_ENGINE) 97 ENGINE_load_builtin_engines(); 98#endif 99 OpenSSL_add_all_algorithms(); 100 OPENSSL_load_builtin_modules(); 101 102 CONF_modules_load_file(NULL, NULL, 103 CONF_MFLAGS_DEFAULT_SECTION | 104 CONF_MFLAGS_IGNORE_MISSING_FILE); 105#endif 106 107 /* Protect ourselves against unseeded PRNG */ 108 if (RAND_status() != 1) { 109 FATAL_ERROR(__FILE__, __LINE__, 110 "OpenSSL pseudorandom number generator " 111 "cannot be initialized (see the `PRNG not " 112 "seeded' message in the OpenSSL FAQ)"); 113 } 114 115 REQUIRE(atomic_compare_exchange_strong(&init_done, &(bool){ false }, 116 true)); 117} 118 119void 120isc__tls_initialize(void) { 121 isc_result_t result = isc_once_do(&init_once, tls_initialize); 122 REQUIRE(result == ISC_R_SUCCESS); 123 REQUIRE(atomic_load(&init_done)); 124} 125 126static void 127tls_shutdown(void) { 128 REQUIRE(atomic_load(&init_done)); 129 REQUIRE(!atomic_load(&shut_done)); 130 131#if OPENSSL_VERSION_NUMBER >= 0x10100000L 132 OPENSSL_cleanup(); 133#else 134 CONF_modules_unload(1); 135 OBJ_cleanup(); 136 EVP_cleanup(); 137#if !defined(OPENSSL_NO_ENGINE) 138 ENGINE_cleanup(); 139#endif 140 CRYPTO_cleanup_all_ex_data(); 141 ERR_remove_thread_state(NULL); 142 RAND_cleanup(); 143 ERR_free_strings(); 144 145 CRYPTO_set_locking_callback(NULL); 146 147 if (locks != NULL) { 148 isc_mutexblock_destroy(locks, nlocks); 149 free(locks); 150 locks = NULL; 151 } 152#endif 153 154 REQUIRE(atomic_compare_exchange_strong(&shut_done, &(bool){ false }, 155 true)); 156} 157 158void 159isc__tls_shutdown(void) { 160 isc_result_t result = isc_once_do(&shut_once, tls_shutdown); 161 REQUIRE(result == ISC_R_SUCCESS); 162 REQUIRE(atomic_load(&shut_done)); 163} 164