1/*
2 * Portions Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 1999-2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
10 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
12 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * Portions Copyright (C) 1995-2000 by 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/*
33 * Principal Author: Brian Wellington
34 * $Id$
35 */
36#ifdef OPENSSL
37
38#include <config.h>
39
40#include <isc/entropy.h>
41#include <isc/mem.h>
42#include <isc/mutex.h>
43#include <isc/mutexblock.h>
44#include <isc/string.h>
45#include <isc/thread.h>
46#include <isc/util.h>
47
48#include <dst/result.h>
49
50#include "dst_internal.h"
51#include "dst_openssl.h"
52
53#ifdef USE_ENGINE
54#include <openssl/engine.h>
55#endif
56
57static RAND_METHOD *rm = NULL;
58
59static isc_mutex_t *locks = NULL;
60static int nlocks;
61
62#ifdef USE_ENGINE
63static ENGINE *e = NULL;
64#endif
65
66static int
67entropy_get(unsigned char *buf, int num) {
68	isc_result_t result;
69	if (num < 0)
70		return (-1);
71	result = dst__entropy_getdata(buf, (unsigned int) num, ISC_FALSE);
72	return (result == ISC_R_SUCCESS ? 1 : -1);
73}
74
75static int
76entropy_status(void) {
77	return (dst__entropy_status() > 32);
78}
79
80static int
81entropy_getpseudo(unsigned char *buf, int num) {
82	isc_result_t result;
83	if (num < 0)
84		return (-1);
85	result = dst__entropy_getdata(buf, (unsigned int) num, ISC_TRUE);
86	return (result == ISC_R_SUCCESS ? 1 : -1);
87}
88
89static void
90entropy_add(const void *buf, int num, double entropy) {
91	/*
92	 * Do nothing.  The only call to this provides no useful data anyway.
93	 */
94	UNUSED(buf);
95	UNUSED(num);
96	UNUSED(entropy);
97}
98
99static void
100lock_callback(int mode, int type, const char *file, int line) {
101	UNUSED(file);
102	UNUSED(line);
103	if ((mode & CRYPTO_LOCK) != 0)
104		LOCK(&locks[type]);
105	else
106		UNLOCK(&locks[type]);
107}
108
109static unsigned long
110id_callback(void) {
111	return ((unsigned long)isc_thread_self());
112}
113
114static void *
115mem_alloc(size_t size) {
116#ifdef OPENSSL_LEAKS
117	void *ptr;
118
119	INSIST(dst__memory_pool != NULL);
120	ptr = isc_mem_allocate(dst__memory_pool, size);
121	return (ptr);
122#else
123	INSIST(dst__memory_pool != NULL);
124	return (isc_mem_allocate(dst__memory_pool, size));
125#endif
126}
127
128static void
129mem_free(void *ptr) {
130	INSIST(dst__memory_pool != NULL);
131	if (ptr != NULL)
132		isc_mem_free(dst__memory_pool, ptr);
133}
134
135static void *
136mem_realloc(void *ptr, size_t size) {
137#ifdef OPENSSL_LEAKS
138	void *rptr;
139
140	INSIST(dst__memory_pool != NULL);
141	rptr = isc_mem_reallocate(dst__memory_pool, ptr, size);
142	return (rptr);
143#else
144	INSIST(dst__memory_pool != NULL);
145	return (isc_mem_reallocate(dst__memory_pool, ptr, size));
146#endif
147}
148
149isc_result_t
150dst__openssl_init(const char *engine) {
151	isc_result_t result;
152#ifdef USE_ENGINE
153	ENGINE *re;
154#else
155
156	UNUSED(engine);
157#endif
158
159#ifdef  DNS_CRYPTO_LEAKS
160	CRYPTO_malloc_debug_init();
161	CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
162	CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
163#endif
164	CRYPTO_set_mem_functions(mem_alloc, mem_realloc, mem_free);
165	nlocks = CRYPTO_num_locks();
166	locks = mem_alloc(sizeof(isc_mutex_t) * nlocks);
167	if (locks == NULL)
168		return (ISC_R_NOMEMORY);
169	result = isc_mutexblock_init(locks, nlocks);
170	if (result != ISC_R_SUCCESS)
171		goto cleanup_mutexalloc;
172	CRYPTO_set_locking_callback(lock_callback);
173	CRYPTO_set_id_callback(id_callback);
174
175	rm = mem_alloc(sizeof(RAND_METHOD));
176	if (rm == NULL) {
177		result = ISC_R_NOMEMORY;
178		goto cleanup_mutexinit;
179	}
180	rm->seed = NULL;
181	rm->bytes = entropy_get;
182	rm->cleanup = NULL;
183	rm->add = entropy_add;
184	rm->pseudorand = entropy_getpseudo;
185	rm->status = entropy_status;
186
187#ifdef USE_ENGINE
188	OPENSSL_config(NULL);
189
190	if (engine != NULL && *engine == '\0')
191		engine = NULL;
192
193	if (engine != NULL) {
194		e = ENGINE_by_id(engine);
195		if (e == NULL) {
196			result = DST_R_NOENGINE;
197			goto cleanup_rm;
198		}
199		/* This will init the engine. */
200		if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
201			result = DST_R_NOENGINE;
202			goto cleanup_rm;
203		}
204	}
205
206	re = ENGINE_get_default_RAND();
207	if (re == NULL) {
208		re = ENGINE_new();
209		if (re == NULL) {
210			result = ISC_R_NOMEMORY;
211			goto cleanup_rm;
212		}
213		ENGINE_set_RAND(re, rm);
214		ENGINE_set_default_RAND(re);
215		ENGINE_free(re);
216	} else
217		ENGINE_finish(re);
218#else
219	RAND_set_rand_method(rm);
220#endif /* USE_ENGINE */
221	return (ISC_R_SUCCESS);
222
223#ifdef USE_ENGINE
224 cleanup_rm:
225	if (e != NULL)
226		ENGINE_free(e);
227	e = NULL;
228	mem_free(rm);
229	rm = NULL;
230#endif
231 cleanup_mutexinit:
232	CRYPTO_set_locking_callback(NULL);
233	DESTROYMUTEXBLOCK(locks, nlocks);
234 cleanup_mutexalloc:
235	mem_free(locks);
236	locks = NULL;
237	return (result);
238}
239
240void
241dst__openssl_destroy() {
242
243	/*
244	 * Sequence taken from apps_shutdown() in <apps/apps.h>.
245	 */
246	if (rm != NULL) {
247#if OPENSSL_VERSION_NUMBER >= 0x00907000L
248		RAND_cleanup();
249#endif
250		mem_free(rm);
251		rm = NULL;
252	}
253#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
254	CONF_modules_free();
255#endif
256	OBJ_cleanup();
257	EVP_cleanup();
258#if defined(USE_ENGINE)
259	if (e != NULL)
260		ENGINE_free(e);
261	e = NULL;
262#if defined(USE_ENGINE) && OPENSSL_VERSION_NUMBER >= 0x00907000L
263	ENGINE_cleanup();
264#endif
265#endif
266#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
267	CRYPTO_cleanup_all_ex_data();
268#endif
269	ERR_clear_error();
270	ERR_remove_state(0);
271	ERR_free_strings();
272
273#ifdef  DNS_CRYPTO_LEAKS
274	CRYPTO_mem_leaks_fp(stderr);
275#endif
276
277	if (locks != NULL) {
278		CRYPTO_set_locking_callback(NULL);
279		DESTROYMUTEXBLOCK(locks, nlocks);
280		mem_free(locks);
281		locks = NULL;
282	}
283}
284
285isc_result_t
286dst__openssl_toresult(isc_result_t fallback) {
287	isc_result_t result = fallback;
288	int err = ERR_get_error();
289
290	switch (ERR_GET_REASON(err)) {
291	case ERR_R_MALLOC_FAILURE:
292		result = ISC_R_NOMEMORY;
293		break;
294	default:
295		break;
296	}
297	ERR_clear_error();
298	return (result);
299}
300
301#if defined(USE_ENGINE)
302ENGINE *
303dst__openssl_getengine(const char *engine) {
304
305	if (engine == NULL)
306		return (NULL);
307	if (e == NULL)
308		return (NULL);
309	if (strcmp(engine, ENGINE_get_id(e)) == 0)
310		return (e);
311	return (NULL);
312}
313#endif
314
315#else /* OPENSSL */
316
317#include <isc/util.h>
318
319EMPTY_TRANSLATION_UNIT
320
321#endif /* OPENSSL */
322/*! \file */
323