source: lib/ssl_openssl.c @ 37c9653

Last change on this file since 37c9653 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
RevLine 
[5ebff60]1/********************************************************************\
[b7d3cc34]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[def3650]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[b7d3cc34]5  \********************************************************************/
6
[26fdfc5]7/* SSL module - OpenSSL version                                         */
[b7d3cc34]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;
[6f10697]22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
[b7d3cc34]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
[59c03bd]33#include "bitlbee.h"
[b7d3cc34]34#include "proxy.h"
35#include "ssl_client.h"
36#include "sock.h"
37
[701acdd4]38int ssl_errno = 0;
39
[b7d3cc34]40static gboolean initialized = FALSE;
41
[5ebff60]42struct scd {
[26fdfc5]43        ssl_input_function func;
[b7d3cc34]44        gpointer data;
45        int fd;
46        gboolean established;
[486ddb5]47        gboolean verify;
[3f661849]48        char *hostname;
[5ebff60]49
[26fdfc5]50        int inpa;
[5ebff60]51        int lasterr;            /* Necessary for SSL_get_error */
[b7d3cc34]52        SSL *ssl;
53};
54
[def3650]55static SSL_CTX *ssl_ctx;
56
[5ebff60]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);
[b7d3cc34]61
62
[5ebff60]63void ssl_init(void)
[ba5add7]64{
[def3650]65        const SSL_METHOD *meth;
[5ebff60]66
[8e9e2b7]67        SSL_library_init();
[5ebff60]68
[def3650]69        meth = TLSv1_client_method();
[5ebff60]70        ssl_ctx = SSL_CTX_new(meth);
71
[def3650]72        initialized = TRUE;
[ba5add7]73}
74
[5ebff60]75void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data)
[b7d3cc34]76{
[5ebff60]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);
[f920d9e]82                return NULL;
[b7d3cc34]83        }
[5ebff60]84
[8a2221a7]85        conn->func = func;
86        conn->data = data;
87        conn->inpa = -1;
[5ebff60]88        conn->hostname = g_strdup(host);
89
[f920d9e]90        return conn;
91}
92
[5ebff60]93void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data)
[f920d9e]94{
[5ebff60]95        struct scd *conn = g_new0(struct scd, 1);
96
[f920d9e]97        conn->fd = fd;
98        conn->func = func;
99        conn->data = data;
100        conn->inpa = -1;
[200e151]101        conn->verify = verify && global.conf->cafile;
[5ebff60]102        conn->hostname = g_strdup(hostname);
103
[f920d9e]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.
[5ebff60]109
[f920d9e]110           In short, doing things like this makes the rest of the code a lot
111           simpler. */
[5ebff60]112
113        b_timeout_add(1, ssl_starttls_real, conn);
114
[f920d9e]115        return conn;
116}
117
[5ebff60]118static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond)
[f920d9e]119{
120        struct scd *conn = data;
[5ebff60]121
122        return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
[f920d9e]123}
124
[5ebff60]125static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond)
[f920d9e]126{
127        struct scd *conn = data;
[5ebff60]128
129        if (conn->verify) {
[3f661849]130                /* Right now we don't have any verification functionality for OpenSSL. */
[5ebff60]131                conn->func(conn->data, 1, NULL, cond);
132                if (source >= 0) {
[0db6618]133                        proxy_disconnect(source);
[5ebff60]134                }
135                ssl_conn_free(conn);
[486ddb5]136
137                return FALSE;
138        }
139
[5ebff60]140        if (source == -1) {
[f920d9e]141                goto ssl_connected_failure;
[5ebff60]142        }
143
144        if (!initialized) {
[ba5add7]145                ssl_init();
[b7d3cc34]146        }
[5ebff60]147
148
149        if (ssl_ctx == NULL) {
[f920d9e]150                goto ssl_connected_failure;
[5ebff60]151        }
152
153        conn->ssl = SSL_new(ssl_ctx);
154        if (conn->ssl == NULL) {
[f920d9e]155                goto ssl_connected_failure;
[5ebff60]156        }
157
[309cb9e]158        /* We can do at least the handshake with non-blocking I/O */
[5ebff60]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);
[f920d9e]167
168ssl_connected_failure:
[5ebff60]169        conn->func(conn->data, 0, NULL, cond);
170        ssl_disconnect(conn);
[f920d9e]171        return FALSE;
172
[5ebff60]173}
[26fdfc5]174
[5ebff60]175static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond)
[26fdfc5]176{
177        struct scd *conn = data;
178        int st;
[5ebff60]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);
[f920d9e]185                        return FALSE;
186                }
[5ebff60]187
188                conn->inpa = b_input_add(conn->fd, ssl_getdirection(conn), ssl_handshake, data);
[309cb9e]189                return FALSE;
[26fdfc5]190        }
[5ebff60]191
[b7d3cc34]192        conn->established = TRUE;
[5ebff60]193        sock_make_blocking(conn->fd);           /* For now... */
194        conn->func(conn->data, 0, conn, cond);
[309cb9e]195        return FALSE;
[b7d3cc34]196}
197
[5ebff60]198int ssl_read(void *conn, char *buf, int len)
[b7d3cc34]199{
[26fdfc5]200        int st;
[5ebff60]201
202        if (!((struct scd*) conn)->established) {
[26fdfc5]203                ssl_errno = SSL_NOHANDSHAKE;
204                return -1;
205        }
[5ebff60]206
207        st = SSL_read(((struct scd*) conn)->ssl, buf, len);
208
[26fdfc5]209        ssl_errno = SSL_OK;
[5ebff60]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) {
[26fdfc5]214                        ssl_errno = SSL_AGAIN;
[5ebff60]215                }
[26fdfc5]216        }
[5ebff60]217
218        if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
219                write(1, buf, st);
220        }
221
[26fdfc5]222        return st;
[b7d3cc34]223}
224
[5ebff60]225int ssl_write(void *conn, const char *buf, int len)
[b7d3cc34]226{
[26fdfc5]227        int st;
[5ebff60]228
229        if (!((struct scd*) conn)->established) {
[26fdfc5]230                ssl_errno = SSL_NOHANDSHAKE;
231                return -1;
232        }
[5ebff60]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
[26fdfc5]240        ssl_errno = SSL_OK;
[5ebff60]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) {
[26fdfc5]245                        ssl_errno = SSL_AGAIN;
[5ebff60]246                }
[26fdfc5]247        }
[5ebff60]248
[26fdfc5]249        return st;
[b7d3cc34]250}
251
[5ebff60]252int ssl_pending(void *conn)
[8a2221a7]253{
[5ebff60]254        return (((struct scd*) conn) && ((struct scd*) conn)->established) ?
255               SSL_pending(((struct scd*) conn)->ssl) > 0 : 0;
[8a2221a7]256}
257
[5ebff60]258static void ssl_conn_free(struct scd *conn)
[3f661849]259{
[5ebff60]260        SSL_free(conn->ssl);
261        g_free(conn->hostname);
262        g_free(conn);
263
[3f661849]264}
265
[5ebff60]266void ssl_disconnect(void *conn_)
[b7d3cc34]267{
268        struct scd *conn = conn_;
[5ebff60]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
[0db6618]278        proxy_disconnect(conn->fd);
[5ebff60]279
280        ssl_conn_free(conn);
[b7d3cc34]281}
282
[5ebff60]283int ssl_getfd(void *conn)
[b7d3cc34]284{
[5ebff60]285        return(((struct scd*) conn)->fd);
[b7d3cc34]286}
[26fdfc5]287
[5ebff60]288b_input_condition ssl_getdirection(void *conn)
[26fdfc5]289{
[5ebff60]290        return(((struct scd*) conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ);
[26fdfc5]291}
[523fb23]292
[5ebff60]293char *ssl_verify_strerror(int code)
[78b8401]294{
[5ebff60]295        return g_strdup("SSL certificate verification not supported by BitlBee OpenSSL code.");
[78b8401]296}
297
[5ebff60]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)
[523fb23]300{
[5ebff60]301        int output_length = 0;
[50b8978]302        EVP_CIPHER_CTX ctx;
[5ebff60]303
[523fb23]304        *res = g_new0(unsigned char, 72);
[5ebff60]305
[523fb23]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);
[5ebff60]315        EVP_CIPHER_CTX_cleanup(&ctx);
[50b8978]316        //EVP_cleanup();
[5ebff60]317
[523fb23]318        return output_length;
319}
Note: See TracBrowser for help on using the repository browser.