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