Address OpenSSL initialization issues when using third-party libraries.
[asterisk/asterisk.git] / main / libasteriskssl.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009-2012, Digium, Inc.
5  *
6  * Russell Bryant <russell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Common OpenSSL support code
22  *
23  * \author Russell Bryant <russell@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #ifdef HAVE_OPENSSL
31 #include <openssl/ssl.h>
32 #include <openssl/err.h>
33 #endif
34
35 #include <dlfcn.h>
36
37 #include "asterisk/_private.h" /* ast_ssl_init() */
38
39 #include "asterisk/utils.h"
40 #include "asterisk/lock.h"
41
42 #ifdef HAVE_OPENSSL
43
44 #define get_OpenSSL_function(func) do { real_##func = dlsym(RTLD_NEXT, __stringify(func)); } while(0)
45
46 static int startup_complete;
47
48 static ast_mutex_t *ssl_locks;
49
50 static int ssl_num_locks;
51
52 static unsigned long ssl_threadid(void)
53 {
54         return (unsigned long) pthread_self();
55 }
56
57 static void ssl_lock(int mode, int n, const char *file, int line)
58 {
59         if (n < 0 || n >= ssl_num_locks) {
60                 ast_log(LOG_ERROR, "OpenSSL is full of LIES!!! - "
61                                 "ssl_num_locks '%d' - n '%d'\n",
62                                 ssl_num_locks, n);
63                 return;
64         }
65
66         if (mode & CRYPTO_LOCK) {
67                 ast_mutex_lock(&ssl_locks[n]);
68         } else {
69                 ast_mutex_unlock(&ssl_locks[n]);
70         }
71 }
72
73 int SSL_library_init(void)
74 {
75 #if defined(AST_DEVMODE)
76         if (startup_complete) {
77                 ast_debug(1, "Called after startup... ignoring!\n");
78         }
79 #endif
80         return 0;
81 }
82
83 void SSL_load_error_strings(void)
84 {
85 #if defined(AST_DEVMODE)
86         if (startup_complete) {
87                 ast_debug(1, "Called after startup... ignoring!\n");
88         }
89 #endif
90 }
91
92 void ERR_load_SSL_strings(void)
93 {
94 #if defined(AST_DEVMODE)
95         if (startup_complete) {
96                 ast_debug(1, "Called after startup... ignoring!\n");
97         }
98 #endif
99 }
100
101 void ERR_load_crypto_strings(void)
102 {
103 #if defined(AST_DEVMODE)
104         if (startup_complete) {
105                 ast_debug(1, "Called after startup... ignoring!\n");
106         }
107 #endif
108 }
109
110 void ERR_load_BIO_strings(void)
111 {
112 #if defined(AST_DEVMODE)
113         if (startup_complete) {
114                 ast_debug(1, "Called after startup... ignoring!\n");
115         }
116 #endif
117 }
118
119 void CRYPTO_set_id_callback(unsigned long (*func)(void))
120 {
121 #if defined(AST_DEVMODE)
122         if (startup_complete) {
123                 ast_debug(1, "Called after startup... ignoring!\n");
124         }
125 #endif
126 }
127
128 void CRYPTO_set_locking_callback(void (*func)(int mode,int type, const char *file, int line))
129 {
130 #if defined(AST_DEVMODE)
131         if (startup_complete) {
132                 ast_debug(1, "Called after startup... ignoring!\n");
133         }
134 #endif
135 }
136
137 void ERR_free_strings(void)
138 {
139         /* we can't allow this to be called, ever */
140 }
141
142 #endif /* HAVE_OPENSSL */
143
144 /*!
145  * \internal
146  * \brief Common OpenSSL initialization for all of Asterisk.
147  */
148 int ast_ssl_init(void)
149 {
150 #ifdef HAVE_OPENSSL
151         unsigned int i;
152         int (*real_SSL_library_init)(void);
153         void (*real_CRYPTO_set_id_callback)(unsigned long (*)(void));
154         void (*real_CRYPTO_set_locking_callback)(void (*)(int, int, const char *, int));
155         void (*real_SSL_load_error_strings)(void);
156         void (*real_ERR_load_SSL_strings)(void);
157         void (*real_ERR_load_crypto_strings)(void);
158         void (*real_ERR_load_BIO_strings)(void);
159         const char *errstr;
160
161         /* clear any previous dynamic linker errors */
162         dlerror();
163         get_OpenSSL_function(SSL_library_init);
164         if ((errstr = dlerror()) != NULL) {
165                 ast_debug(1, "unable to get real address of SSL_library_init: %s\n", errstr);
166                 /* there is no way to continue in this situation... SSL will
167                  * likely be broken in this process
168                  */
169                 return -1;
170         } else {
171                 real_SSL_library_init();
172         }
173
174         /* Make OpenSSL usage thread-safe. */
175
176         dlerror();
177         get_OpenSSL_function(CRYPTO_set_id_callback);
178         if ((errstr = dlerror()) != NULL) {
179                 ast_debug(1, "unable to get real address of CRYPTO_set_id_callback: %s\n", errstr);
180                 /* there is no way to continue in this situation... SSL will
181                  * likely be broken in this process
182                  */
183                 return -1;
184         } else {
185                 real_CRYPTO_set_id_callback(ssl_threadid);
186         }
187
188         dlerror();
189         get_OpenSSL_function(CRYPTO_set_locking_callback);
190         if ((errstr = dlerror()) != NULL) {
191                 ast_debug(1, "unable to get real address of CRYPTO_set_locking_callback: %s\n", errstr);
192                 /* there is no way to continue in this situation... SSL will
193                  * likely be broken in this process
194                  */
195                 return -1;
196         } else {
197                 ssl_num_locks = CRYPTO_num_locks();
198                 if (!(ssl_locks = ast_calloc(ssl_num_locks, sizeof(ssl_locks[0])))) {
199                         return -1;
200                 }
201                 for (i = 0; i < ssl_num_locks; i++) {
202                         ast_mutex_init(&ssl_locks[i]);
203                 }
204                 real_CRYPTO_set_locking_callback(ssl_lock);
205         }
206
207         /* after this point, we don't check for errors from the dlsym() calls,
208          * under the assumption that if the ones above were successful, all
209          * the rest will be too. this assumption holds as long as OpenSSL still
210          * provides all of these functions.
211          */
212
213         get_OpenSSL_function(SSL_load_error_strings);
214         real_SSL_load_error_strings();
215
216         get_OpenSSL_function(ERR_load_SSL_strings);
217         real_ERR_load_SSL_strings();
218
219         get_OpenSSL_function(ERR_load_crypto_strings);
220         real_ERR_load_crypto_strings();
221
222         get_OpenSSL_function(ERR_load_BIO_strings);
223         real_ERR_load_BIO_strings();
224
225 #if 0
226         /* currently this is just another call to SSL_library_init, so we don't call it */
227         OpenSSL_add_all_algorithms();
228 #endif
229
230         startup_complete = 1;
231
232 #endif /* HAVE_OPENSSL */
233         return 0;
234 }
235