source: lib/ssl_openssl.c @ 5a4f22e

Last change on this file since 5a4f22e was def3650, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-12-24T13:28:02Z

In the OpenSSL module, keep only one global SSL context instead of recreating
one for every connection.

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