source: lib/ssl_openssl.c @ ad54149

Last change on this file since ad54149 was 0db6618, checked in by dequis <dx@…>, at 2015-10-26T08:28:10Z

Use proxy_disconnect() in http, ssl, jabber, oscar

Twitter and MSN are all HTTP/SSL, so they don't need it either.

The out of tree facebook and steam plugins are also covered by the
HTTP/SSL changes.

Yahoo is written in a weird way and doesn't seem to need it (it seems it
doesn't immediately stop connections when you tell it to logout)

  • 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 = TLSv1_client_method();
70        ssl_ctx = SSL_CTX_new(meth);
71
72        initialized = TRUE;
73}
74
75void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data)
76{
77        struct scd *conn = g_new0(struct scd, 1);
78
79        conn->fd = proxy_connect(host, port, ssl_connected, conn);
80        if (conn->fd < 0) {
81                ssl_conn_free(conn);
82                return NULL;
83        }
84
85        conn->func = func;
86        conn->data = data;
87        conn->inpa = -1;
88        conn->hostname = g_strdup(host);
89
90        return conn;
91}
92
93void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data)
94{
95        struct scd *conn = g_new0(struct scd, 1);
96
97        conn->fd = fd;
98        conn->func = func;
99        conn->data = data;
100        conn->inpa = -1;
101        conn->verify = verify && global.conf->cafile;
102        conn->hostname = g_strdup(hostname);
103
104        /* This function should be called via a (short) timeout instead of
105           directly from here, because these SSL calls are *supposed* to be
106           *completely* asynchronous and not ready yet when this function
107           (or *_connect, for examle) returns. Also, errors are reported via
108           the callback function, not via this function's return value.
109
110           In short, doing things like this makes the rest of the code a lot
111           simpler. */
112
113        b_timeout_add(1, ssl_starttls_real, conn);
114
115        return conn;
116}
117
118static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond)
119{
120        struct scd *conn = data;
121
122        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
123}
124
125static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond)
126{
127        struct scd *conn = data;
128
129        if (conn->verify) {
130                /* Right now we don't have any verification functionality for OpenSSL. */
131                conn->func(conn->data, 1, NULL, cond);
132                if (source >= 0) {
133                        proxy_disconnect(source);
134                }
135                ssl_conn_free(conn);
136
137                return FALSE;
138        }
139
140        if (source == -1) {
141                goto ssl_connected_failure;
142        }
143
144        if (!initialized) {
145                ssl_init();
146        }
147
148
149        if (ssl_ctx == NULL) {
150                goto ssl_connected_failure;
151        }
152
153        conn->ssl = SSL_new(ssl_ctx);
154        if (conn->ssl == NULL) {
155                goto ssl_connected_failure;
156        }
157
158        /* We can do at least the handshake with non-blocking I/O */
159        sock_make_nonblocking(conn->fd);
160        SSL_set_fd(conn->ssl, conn->fd);
161
162        if (conn->hostname && !g_ascii_isdigit(conn->hostname[0])) {
163                SSL_set_tlsext_host_name(conn->ssl, conn->hostname);
164        }
165
166        return ssl_handshake(data, source, cond);
167
168ssl_connected_failure:
169        conn->func(conn->data, 0, NULL, cond);
170        ssl_disconnect(conn);
171        return FALSE;
172
173}
174
175static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond)
176{
177        struct scd *conn = data;
178        int st;
179
180        if ((st = SSL_connect(conn->ssl)) < 0) {
181                conn->lasterr = SSL_get_error(conn->ssl, st);
182                if (conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE) {
183                        conn->func(conn->data, 0, NULL, cond);
184                        ssl_disconnect(conn);
185                        return FALSE;
186                }
187
188                conn->inpa = b_input_add(conn->fd, ssl_getdirection(conn), ssl_handshake, data);
189                return FALSE;
190        }
191
192        conn->established = TRUE;
193        sock_make_blocking(conn->fd);           /* For now... */
194        conn->func(conn->data, 0, conn, cond);
195        return FALSE;
196}
197
198int ssl_read(void *conn, char *buf, int len)
199{
200        int st;
201
202        if (!((struct scd*) conn)->established) {
203                ssl_errno = SSL_NOHANDSHAKE;
204                return -1;
205        }
206
207        st = SSL_read(((struct scd*) conn)->ssl, buf, len);
208
209        ssl_errno = SSL_OK;
210        if (st <= 0) {
211                ((struct scd*) conn)->lasterr = SSL_get_error(((struct scd*) conn)->ssl, st);
212                if (((struct scd*) conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*) conn)->lasterr ==
213                    SSL_ERROR_WANT_WRITE) {
214                        ssl_errno = SSL_AGAIN;
215                }
216        }
217
218        if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
219                write(1, buf, st);
220        }
221
222        return st;
223}
224
225int ssl_write(void *conn, const char *buf, int len)
226{
227        int st;
228
229        if (!((struct scd*) conn)->established) {
230                ssl_errno = SSL_NOHANDSHAKE;
231                return -1;
232        }
233
234        st = SSL_write(((struct scd*) conn)->ssl, buf, len);
235
236        if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
237                write(1, buf, st);
238        }
239
240        ssl_errno = SSL_OK;
241        if (st <= 0) {
242                ((struct scd*) conn)->lasterr = SSL_get_error(((struct scd*) conn)->ssl, st);
243                if (((struct scd*) conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*) conn)->lasterr ==
244                    SSL_ERROR_WANT_WRITE) {
245                        ssl_errno = SSL_AGAIN;
246                }
247        }
248
249        return st;
250}
251
252int ssl_pending(void *conn)
253{
254        return (((struct scd*) conn) && ((struct scd*) conn)->established) ?
255               SSL_pending(((struct scd*) conn)->ssl) > 0 : 0;
256}
257
258static void ssl_conn_free(struct scd *conn)
259{
260        SSL_free(conn->ssl);
261        g_free(conn->hostname);
262        g_free(conn);
263
264}
265
266void ssl_disconnect(void *conn_)
267{
268        struct scd *conn = conn_;
269
270        if (conn->inpa != -1) {
271                b_event_remove(conn->inpa);
272        }
273
274        if (conn->established) {
275                SSL_shutdown(conn->ssl);
276        }
277
278        proxy_disconnect(conn->fd);
279
280        ssl_conn_free(conn);
281}
282
283int ssl_getfd(void *conn)
284{
285        return(((struct scd*) conn)->fd);
286}
287
288b_input_condition ssl_getdirection(void *conn)
289{
290        return(((struct scd*) conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ);
291}
292
293char *ssl_verify_strerror(int code)
294{
295        return g_strdup("SSL certificate verification not supported by BitlBee OpenSSL code.");
296}
297
298size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len,
299                        const unsigned char *iv, unsigned char **res)
300{
301        int output_length = 0;
302        EVP_CIPHER_CTX ctx;
303
304        *res = g_new0(unsigned char, 72);
305
306        /* Don't set key or IV because we will modify the parameters */
307        EVP_CIPHER_CTX_init(&ctx);
308        EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1);
309        EVP_CIPHER_CTX_set_key_length(&ctx, key_len);
310        EVP_CIPHER_CTX_set_padding(&ctx, 0);
311        /* We finished modifying parameters so now we can set key and IV */
312        EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1);
313        EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len);
314        EVP_CipherFinal_ex(&ctx, *res, &output_length);
315        EVP_CIPHER_CTX_cleanup(&ctx);
316        //EVP_cleanup();
317
318        return output_length;
319}
Note: See TracBrowser for help on using the repository browser.