summaryrefslogtreecommitdiffstats
path: root/src/lib-ssl-iostream/dovecot-openssl-common.c
blob: 76f98bc9e70bc71a1e0308da9dfb656ada93186b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "randgen.h"
#include "dovecot-openssl-common.h"

#include <openssl/ssl.h>
#include <openssl/engine.h>
#include <openssl/rand.h>

static int openssl_init_refcount = 0;
static ENGINE *dovecot_openssl_engine;

#ifdef HAVE_SSL_NEW_MEM_FUNCS
static void *dovecot_openssl_malloc(size_t size, const char *u0 ATTR_UNUSED, int u1 ATTR_UNUSED)
#else
static void *dovecot_openssl_malloc(size_t size)
#endif
{
	/* this may be performance critical, so don't use
	   i_malloc() or calloc() */
	void *mem = malloc(size);
	if (mem == NULL) {
		i_fatal_status(FATAL_OUTOFMEM,
			"OpenSSL: malloc(%zu): Out of memory", size);
	}
	return mem;
}

#ifdef HAVE_SSL_NEW_MEM_FUNCS
static void *dovecot_openssl_realloc(void *ptr, size_t size, const char *u0 ATTR_UNUSED, int u1 ATTR_UNUSED)
#else
static void *dovecot_openssl_realloc(void *ptr, size_t size)
#endif
{
	void *mem = realloc(ptr, size);
	if (mem == NULL) {
		i_fatal_status(FATAL_OUTOFMEM,
			"OpenSSL: realloc(%zu): Out of memory", size);
	}
	return mem;
}

#ifdef HAVE_SSL_NEW_MEM_FUNCS
static void dovecot_openssl_free(void *ptr, const char *u0 ATTR_UNUSED, int u1 ATTR_UNUSED)
#else
static void dovecot_openssl_free(void *ptr)
#endif
{
	free(ptr);
}

void dovecot_openssl_common_global_ref(void)
{
	if (openssl_init_refcount++ > 0)
		return;

	/* use our own memory allocation functions that will die instead of
	   returning NULL. this avoids random failures on out-of-memory
	   conditions. */
	if (CRYPTO_set_mem_functions(dovecot_openssl_malloc,
				     dovecot_openssl_realloc, dovecot_openssl_free) == 0) {
		/*i_warning("CRYPTO_set_mem_functions() was called too late");*/
	}

	SSL_library_init();
	SSL_load_error_strings();
	OpenSSL_add_all_algorithms();
}

bool dovecot_openssl_common_global_unref(void)
{
	i_assert(openssl_init_refcount > 0);

	if (--openssl_init_refcount > 0)
		return TRUE;

	if (dovecot_openssl_engine != NULL) {
		ENGINE_finish(dovecot_openssl_engine);
		dovecot_openssl_engine = NULL;
	}
	/* OBJ_cleanup() is called automatically by EVP_cleanup() in
	   newer versions. Doesn't hurt to call it anyway. */
	OBJ_cleanup();
#ifdef HAVE_SSL_COMP_FREE_COMPRESSION_METHODS
	SSL_COMP_free_compression_methods();
#endif
	ENGINE_cleanup();
	EVP_cleanup();
	CRYPTO_cleanup_all_ex_data();
#ifdef HAVE_OPENSSL_AUTO_THREAD_DEINIT
	/* no cleanup needed */
#elif defined(HAVE_OPENSSL_ERR_REMOVE_THREAD_STATE)
	/* This was marked as deprecated in v1.1. */
	ERR_remove_thread_state(NULL);
#else
	/* This was deprecated by ERR_remove_thread_state(NULL) in v1.0.0. */
	ERR_remove_state(0);
#endif
	ERR_free_strings();
#ifdef HAVE_OPENSSL_CLEANUP
	OPENSSL_cleanup();
#endif
	return FALSE;
}

int dovecot_openssl_common_global_set_engine(const char *engine,
					     const char **error_r)
{
	if (dovecot_openssl_engine != NULL)
		return 1;

	ENGINE_load_builtin_engines();
	dovecot_openssl_engine = ENGINE_by_id(engine);
	if (dovecot_openssl_engine == NULL) {
		*error_r = t_strdup_printf("Unknown engine '%s'", engine);
		return 0;
	}
	if (ENGINE_init(dovecot_openssl_engine) == 0) {
		*error_r = t_strdup_printf("ENGINE_init(%s) failed", engine);
		ENGINE_free(dovecot_openssl_engine);
		dovecot_openssl_engine = NULL;
		return -1;
	}
	if (ENGINE_set_default(dovecot_openssl_engine, ENGINE_METHOD_ALL) == 0) {
		*error_r = t_strdup_printf("ENGINE_set_default(%s) failed", engine);
		ENGINE_free(dovecot_openssl_engine);
		dovecot_openssl_engine = NULL;
		return -1;
	}
	return 1;
}