1 /*
2  * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  *
11  * Portions Copyright (C) Network Associates, Inc.
12  *
13  * Permission to use, copy, modify, and/or distribute this software for any
14  * purpose with or without fee is hereby granted, provided that the above
15  * copyright notice and this permission notice appear in all copies.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
18  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
20  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
23  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  */
25 
26 #include <isc/mem.h>
27 #include <isc/mutex.h>
28 #include <isc/mutexblock.h>
29 #include <isc/platform.h>
30 #include <isc/string.h>
31 #include <isc/thread.h>
32 #include <isc/util.h>
33 
34 #include <dns/log.h>
35 
36 #include <dst/result.h>
37 
38 #include "dst_internal.h"
39 #include "dst_openssl.h"
40 
41 static isc_mem_t *dst__mctx = NULL;
42 
43 #if !defined(OPENSSL_NO_ENGINE)
44 #include <openssl/engine.h>
45 #endif /* if !defined(OPENSSL_NO_ENGINE) */
46 
47 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
48 static isc_mutex_t *locks = NULL;
49 static int nlocks;
50 #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
51 	* defined(LIBRESSL_VERSION_NUMBER) */
52 
53 #if !defined(OPENSSL_NO_ENGINE)
54 static ENGINE *e = NULL;
55 #endif /* if !defined(OPENSSL_NO_ENGINE) */
56 
57 static void
enable_fips_mode(void)58 enable_fips_mode(void) {
59 #ifdef HAVE_FIPS_MODE
60 	if (FIPS_mode() != 0) {
61 		/*
62 		 * FIPS mode is already enabled.
63 		 */
64 		return;
65 	}
66 
67 	if (FIPS_mode_set(1) == 0) {
68 		dst__openssl_toresult2("FIPS_mode_set", DST_R_OPENSSLFAILURE);
69 		exit(1);
70 	}
71 #endif /* HAVE_FIPS_MODE */
72 }
73 
74 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
75 static void
lock_callback(int mode,int type,const char * file,int line)76 lock_callback(int mode, int type, const char *file, int line) {
77 	UNUSED(file);
78 	UNUSED(line);
79 	if ((mode & CRYPTO_LOCK) != 0) {
80 		LOCK(&locks[type]);
81 	} else {
82 		UNLOCK(&locks[type]);
83 	}
84 }
85 #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
86 	* defined(LIBRESSL_VERSION_NUMBER) */
87 
88 #if defined(LIBRESSL_VERSION_NUMBER)
89 static unsigned long
id_callback(void)90 id_callback(void) {
91 	return ((unsigned long)isc_thread_self());
92 }
93 #endif /* if defined(LIBRESSL_VERSION_NUMBER) */
94 
95 #if OPENSSL_VERSION_NUMBER < 0x10100000L
96 static void
_set_thread_id(CRYPTO_THREADID * id)97 _set_thread_id(CRYPTO_THREADID *id) {
98 	CRYPTO_THREADID_set_numeric(id, (unsigned long)isc_thread_self());
99 }
100 #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */
101 
102 isc_result_t
dst__openssl_init(isc_mem_t * mctx,const char * engine)103 dst__openssl_init(isc_mem_t *mctx, const char *engine) {
104 	isc_result_t result;
105 
106 	REQUIRE(dst__mctx == NULL);
107 	isc_mem_attach(mctx, &dst__mctx);
108 
109 #if defined(OPENSSL_NO_ENGINE)
110 	UNUSED(engine);
111 #endif /* if defined(OPENSSL_NO_ENGINE) */
112 
113 	enable_fips_mode();
114 
115 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
116 	nlocks = CRYPTO_num_locks();
117 	locks = isc_mem_allocate(dst__mctx, sizeof(isc_mutex_t) * nlocks);
118 	isc_mutexblock_init(locks, nlocks);
119 	CRYPTO_set_locking_callback(lock_callback);
120 #if defined(LIBRESSL_VERSION_NUMBER)
121 	CRYPTO_set_id_callback(id_callback);
122 #elif OPENSSL_VERSION_NUMBER < 0x10100000L
123 	CRYPTO_THREADID_set_callback(_set_thread_id);
124 #endif /* if defined(LIBRESSL_VERSION_NUMBER) */
125 	ERR_load_crypto_strings();
126 #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
127 	* defined(LIBRESSL_VERSION_NUMBER) */
128 
129 #if !defined(OPENSSL_NO_ENGINE)
130 #if !defined(CONF_MFLAGS_DEFAULT_SECTION)
131 	OPENSSL_config(NULL);
132 #else  /* if !defined(CONF_MFLAGS_DEFAULT_SECTION) */
133 	/*
134 	 * OPENSSL_config() can only be called a single time as of
135 	 * 1.0.2e so do the steps individually.
136 	 */
137 	OPENSSL_load_builtin_modules();
138 	ENGINE_load_builtin_engines();
139 	ERR_clear_error();
140 	CONF_modules_load_file(NULL, NULL,
141 			       CONF_MFLAGS_DEFAULT_SECTION |
142 				       CONF_MFLAGS_IGNORE_MISSING_FILE);
143 #endif /* if !defined(CONF_MFLAGS_DEFAULT_SECTION) */
144 
145 	if (engine != NULL && *engine == '\0') {
146 		engine = NULL;
147 	}
148 
149 	if (engine != NULL) {
150 		e = ENGINE_by_id(engine);
151 		if (e == NULL) {
152 			result = DST_R_NOENGINE;
153 			goto cleanup_rm;
154 		}
155 		/* This will init the engine. */
156 		if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
157 			result = DST_R_NOENGINE;
158 			goto cleanup_rm;
159 		}
160 	}
161 
162 #endif /* !defined(OPENSSL_NO_ENGINE) */
163 
164 	/* Protect ourselves against unseeded PRNG */
165 	if (RAND_status() != 1) {
166 		FATAL_ERROR(__FILE__, __LINE__,
167 			    "OpenSSL pseudorandom number generator "
168 			    "cannot be initialized (see the `PRNG not "
169 			    "seeded' message in the OpenSSL FAQ)");
170 	}
171 
172 	return (ISC_R_SUCCESS);
173 
174 #if !defined(OPENSSL_NO_ENGINE)
175 cleanup_rm:
176 	if (e != NULL) {
177 		ENGINE_free(e);
178 	}
179 	e = NULL;
180 #endif /* if !defined(OPENSSL_NO_ENGINE) */
181 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
182 	CRYPTO_set_locking_callback(NULL);
183 	isc_mutexblock_destroy(locks, nlocks);
184 	isc_mem_free(dst__mctx, locks);
185 	locks = NULL;
186 #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
187 	* defined(LIBRESSL_VERSION_NUMBER) */
188 	return (result);
189 }
190 
191 void
dst__openssl_destroy(void)192 dst__openssl_destroy(void) {
193 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
194 	/*
195 	 * Sequence taken from apps_shutdown() in <apps/apps.h>.
196 	 */
197 	CONF_modules_free();
198 	OBJ_cleanup();
199 	EVP_cleanup();
200 #if !defined(OPENSSL_NO_ENGINE)
201 	if (e != NULL) {
202 		ENGINE_free(e);
203 	}
204 	e = NULL;
205 	ENGINE_cleanup();
206 #endif /* if !defined(OPENSSL_NO_ENGINE) */
207 	CRYPTO_cleanup_all_ex_data();
208 	ERR_clear_error();
209 #if OPENSSL_VERSION_NUMBER < 0x10100000L
210 	ERR_remove_thread_state(NULL);
211 #elif defined(LIBRESSL_VERSION_NUMBER)
212 	ERR_remove_state(0);
213 #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */
214 	ERR_free_strings();
215 
216 #ifdef DNS_CRYPTO_LEAKS
217 	CRYPTO_mem_leaks_fp(stderr);
218 #endif /* ifdef DNS_CRYPTO_LEAKS */
219 
220 	if (locks != NULL) {
221 		CRYPTO_set_locking_callback(NULL);
222 		isc_mutexblock_destroy(locks, nlocks);
223 		isc_mem_free(dst__mctx, locks);
224 		locks = NULL;
225 	}
226 #endif /* if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
227 	* defined(LIBRESSL_VERSION_NUMBER) */
228 	isc_mem_detach(&dst__mctx);
229 }
230 
231 static isc_result_t
toresult(isc_result_t fallback)232 toresult(isc_result_t fallback) {
233 	isc_result_t result = fallback;
234 	unsigned long err = ERR_peek_error();
235 #if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
236 	int lib = ERR_GET_LIB(err);
237 #endif /* if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) */
238 	int reason = ERR_GET_REASON(err);
239 
240 	switch (reason) {
241 	/*
242 	 * ERR_* errors are globally unique; others
243 	 * are unique per sublibrary
244 	 */
245 	case ERR_R_MALLOC_FAILURE:
246 		result = ISC_R_NOMEMORY;
247 		break;
248 	default:
249 #if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
250 		if (lib == ERR_R_ECDSA_LIB &&
251 		    reason == ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) {
252 			result = ISC_R_NOENTROPY;
253 			break;
254 		}
255 #endif /* if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) */
256 		break;
257 	}
258 
259 	return (result);
260 }
261 
262 isc_result_t
dst__openssl_toresult(isc_result_t fallback)263 dst__openssl_toresult(isc_result_t fallback) {
264 	isc_result_t result;
265 
266 	result = toresult(fallback);
267 
268 	ERR_clear_error();
269 	return (result);
270 }
271 
272 isc_result_t
dst__openssl_toresult2(const char * funcname,isc_result_t fallback)273 dst__openssl_toresult2(const char *funcname, isc_result_t fallback) {
274 	return (dst__openssl_toresult3(DNS_LOGCATEGORY_GENERAL, funcname,
275 				       fallback));
276 }
277 
278 isc_result_t
dst__openssl_toresult3(isc_logcategory_t * category,const char * funcname,isc_result_t fallback)279 dst__openssl_toresult3(isc_logcategory_t *category, const char *funcname,
280 		       isc_result_t fallback) {
281 	isc_result_t result;
282 	unsigned long err;
283 	const char *file, *data;
284 	int line, flags;
285 	char buf[256];
286 
287 	result = toresult(fallback);
288 
289 	isc_log_write(dns_lctx, category, DNS_LOGMODULE_CRYPTO, ISC_LOG_WARNING,
290 		      "%s failed (%s)", funcname, isc_result_totext(result));
291 
292 	if (result == ISC_R_NOMEMORY) {
293 		goto done;
294 	}
295 
296 	for (;;) {
297 		err = ERR_get_error_line_data(&file, &line, &data, &flags);
298 		if (err == 0U) {
299 			goto done;
300 		}
301 		ERR_error_string_n(err, buf, sizeof(buf));
302 		isc_log_write(dns_lctx, category, DNS_LOGMODULE_CRYPTO,
303 			      ISC_LOG_INFO, "%s:%s:%d:%s", buf, file, line,
304 			      ((flags & ERR_TXT_STRING) != 0) ? data : "");
305 	}
306 
307 done:
308 	ERR_clear_error();
309 	return (result);
310 }
311 
312 #if !defined(OPENSSL_NO_ENGINE)
313 ENGINE *
dst__openssl_getengine(const char * engine)314 dst__openssl_getengine(const char *engine) {
315 	if (engine == NULL) {
316 		return (NULL);
317 	}
318 	if (e == NULL) {
319 		return (NULL);
320 	}
321 	if (strcmp(engine, ENGINE_get_id(e)) == 0) {
322 		return (e);
323 	}
324 	return (NULL);
325 }
326 #endif /* if !defined(OPENSSL_NO_ENGINE) */
327 
328 /*! \file */
329