close Warning: Failed to sync with repository "(default)": [Errno 12] Cannot allocate memory; repository information may be out of date. Look in the Trac log for more information including mitigation strategies.

source: lib/ssl_openssl.c @ df67b48

Last change on this file since df67b48 was 82e55d2, checked in by dequis <dx@…>, at 2016-09-29T17:53:41Z

ssl_openssl: Allow TLS versions newer than 1.0

Another victim of SSLv23_client_method's terrible name. At least we
weren't forcing ssl 3.0.

Thanks kl3 for pointing this out.

Despite what this commit might suggest, using openssl is not recommended
at all. Potential GPL incompatibility aside, that module doesn't have
certificate verification code. Don't use it unless you have a good
reason to do so.

  • Property mode set to 100644
File size: 7.8 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* SSL module - OpenSSL version                                         */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#include <openssl/crypto.h>
27#include <openssl/rand.h>
28#include <openssl/x509.h>
29#include <openssl/pem.h>
30#include <openssl/ssl.h>
31#include <openssl/err.h>
32
33#include "bitlbee.h"
34#include "proxy.h"
35#include "ssl_client.h"
36#include "sock.h"
37
38int ssl_errno = 0;
39
40static gboolean initialized = FALSE;
41
42struct scd {
43        ssl_input_function func;
44        gpointer data;
45        int fd;
46        gboolean established;
47        gboolean verify;
48        char *hostname;
49
50        int inpa;
51        int lasterr;            /* Necessary for SSL_get_error */
52        SSL *ssl;
53};
54
55static SSL_CTX *ssl_ctx;
56
57static void ssl_conn_free(struct scd *conn);
58static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond);
59static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond);
60static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond);
61
62
63void ssl_init(void)
64{
65        const SSL_METHOD *meth;
66
67        SSL_library_init();
68
69        meth = SSLv23_client_method();
70        ssl_ctx = SSL_CTX_new(meth);
71        SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
72
73        initialized = TRUE;
74}
75
76void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data)
77{
78        struct scd *conn = g_new0(struct scd, 1);
79
80        conn->fd = proxy_connect(host, port, ssl_connected, conn);
81        if (conn->fd < 0) {
82                ssl_conn_free(conn);
83                return NULL;
84        }
85
86        conn->func = func;
87        conn->data = data;
88        conn->inpa = -1;
89        conn->hostname = g_strdup(host);
90
91        return conn;
92}
93
94void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data)
95{
96        struct scd *conn = g_new0(struct scd, 1);
97
98        conn->fd = fd;
99        conn->func = func;
100        conn->data = data;
101        conn->inpa = -1;
102        conn->verify = verify && global.conf->cafile;
103        conn->hostname = g_strdup(hostname);
104
105        /* This function should be called via a (short) timeout instead of
106           directly from here, because these SSL calls are *supposed* to be
107           *completely* asynchronous and not ready yet when this function
108           (or *_connect, for examle) returns. Also, errors are reported via
109           the callback function, not via this function's return value.
110
111           In short, doing things like this makes the rest of the code a lot
112           simpler. */
113
114        b_timeout_add(1, ssl_starttls_real, conn);
115
116        return conn;
117}
118
119static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond)
120{
121        struct scd *conn = data;
122
123        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
124}
125
126static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond)
127{
128        struct scd *conn = data;
129
130        if (conn->verify) {
131                /* Right now we don't have any verification functionality for OpenSSL. */
132                conn->func(conn->data, 1, NULL, cond);
133                if (source >= 0) {
134                        proxy_disconnect(source);
135                }
136                ssl_conn_free(conn);
137
138                return FALSE;
139        }
140
141        if (source == -1) {
142                goto ssl_connected_failure;
143        }
144
145        if (!initialized) {
146                ssl_init();
147        }
148
149
150        if (ssl_ctx == NULL) {
151                goto ssl_connected_failure;
152        }
153
154        conn->ssl = SSL_new(ssl_ctx);
155        if (conn->ssl == NULL) {
156                goto ssl_connected_failure;
157        }
158
159        /* We can do at least the handshake with non-blocking I/O */
160        sock_make_nonblocking(conn->fd);
161        SSL_set_fd(conn->ssl, conn->fd);
162
163        if (conn->hostname && !g_ascii_isdigit(conn->hostname[0])) {
164                SSL_set_tlsext_host_name(conn->ssl, conn->hostname);
165        }
166
167        return ssl_handshake(data, source, cond);
168
169ssl_connected_failure:
170        conn->func(conn->data, 0, NULL, cond);
171        ssl_disconnect(conn);
172        return FALSE;
173
174}
175
176static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond)
177{
178        struct scd *conn = data;
179        int st;
180
181        if ((st = SSL_connect(conn->ssl)) < 0) {
182                conn->lasterr = SSL_get_error(conn->ssl, st);
183                if (conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE) {
184                        conn->func(conn->data, 0, NULL, cond);
185                        ssl_disconnect(conn);
186                        return FALSE;
187                }
188
189                conn->inpa = b_input_add(conn->fd, ssl_getdirection(conn), ssl_handshake, data);
190                return FALSE;
191        }
192
193        conn->established = TRUE;
194        sock_make_blocking(conn->fd);           /* For now... */
195        conn->func(conn->data, 0, conn, cond);
196        return FALSE;
197}
198
199int ssl_read(void *conn, char *buf, int len)
200{
201        int st;
202
203        if (!((struct scd*) conn)->established) {
204                ssl_errno = SSL_NOHANDSHAKE;
205                return -1;
206        }
207
208        st = SSL_read(((struct scd*) conn)->ssl, buf, len);
209
210        ssl_errno = SSL_OK;
211        if (st <= 0) {
212                ((struct scd*) conn)->lasterr = SSL_get_error(((struct scd*) conn)->ssl, st);
213                if (((struct scd*) conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*) conn)->lasterr ==
214                    SSL_ERROR_WANT_WRITE) {
215                        ssl_errno = SSL_AGAIN;
216                }
217        }
218
219        if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
220                write(1, buf, st);
221        }
222
223        return st;
224}
225
226int ssl_write(void *conn, const char *buf, int len)
227{
228        int st;
229
230        if (!((struct scd*) conn)->established) {
231                ssl_errno = SSL_NOHANDSHAKE;
232                return -1;
233        }
234
235        st = SSL_write(((struct scd*) conn)->ssl, buf, len);
236
237        if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
238                write(1, buf, st);
239        }
240
241        ssl_errno = SSL_OK;
242        if (st <= 0) {
243                ((struct scd*) conn)->lasterr = SSL_get_error(((struct scd*) conn)->ssl, st);
244                if (((struct scd*) conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*) conn)->lasterr ==
245                    SSL_ERROR_WANT_WRITE) {
246                        ssl_errno = SSL_AGAIN;
247                }
248        }
249
250        return st;
251}
252
253int ssl_pending(void *conn)
254{
255        return (((struct scd*) conn) && ((struct scd*) conn)->established) ?
256               SSL_pending(((struct scd*) conn)->ssl) > 0 : 0;
257}
258
259static void ssl_conn_free(struct scd *conn)
260{
261        SSL_free(conn->ssl);
262        g_free(conn->hostname);
263        g_free(conn);
264
265}
266
267void ssl_disconnect(void *conn_)
268{
269        struct scd *conn = conn_;
270
271        if (conn->inpa != -1) {
272                b_event_remove(conn->inpa);
273        }
274
275        if (conn->established) {
276                SSL_shutdown(conn->ssl);
277        }
278
279        proxy_disconnect(conn->fd);
280
281        ssl_conn_free(conn);
282}
283
284int ssl_getfd(void *conn)
285{
286        return(((struct scd*) conn)->fd);
287}
288
289b_input_condition ssl_getdirection(void *conn)
290{
291        return(((struct scd*) conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ);
292}
293
294char *ssl_verify_strerror(int code)
295{
296        return g_strdup("SSL certificate verification not supported by BitlBee OpenSSL code.");
297}
298
299size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len,
300                        const unsigned char *iv, unsigned char **res)
301{
302        int output_length = 0;
303        EVP_CIPHER_CTX ctx;
304
305        *res = g_new0(unsigned char, 72);
306
307        /* Don't set key or IV because we will modify the parameters */
308        EVP_CIPHER_CTX_init(&ctx);
309        EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1);
310        EVP_CIPHER_CTX_set_key_length(&ctx, key_len);
311        EVP_CIPHER_CTX_set_padding(&ctx, 0);
312        /* We finished modifying parameters so now we can set key and IV */
313        EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1);
314        EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len);
315        EVP_CipherFinal_ex(&ctx, *res, &output_length);
316        EVP_CIPHER_CTX_cleanup(&ctx);
317        //EVP_cleanup();
318
319        return output_length;
320}
Note: See TracBrowser for help on using the repository browser.