source: lib/ssl_openssl.c @ cc6fdf8

Last change on this file since cc6fdf8 was 59c03bd, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-01-03T23:53:28Z

A few more SSL fixes merged from AopicieR. This also fixes OpenSSL compile
issues (bug #881).

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