source: lib/ssl_openssl.c @ 3f661849

Last change on this file since 3f661849 was 3f661849, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-12-24T12:51:26Z

SNI client support in GnuTLS+OpenSSL modules.

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