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