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