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
RevLine 
[b7d3cc34]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
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;
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
[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
42struct scd
43{
[26fdfc5]44        ssl_input_function func;
[b7d3cc34]45        gpointer data;
46        int fd;
47        gboolean established;
[486ddb5]48        gboolean verify;
[3f661849]49        char *hostname;
[b7d3cc34]50       
[26fdfc5]51        int inpa;
52        int lasterr;            /* Necessary for SSL_get_error */
[b7d3cc34]53        SSL *ssl;
54        SSL_CTX *ssl_ctx;
55};
56
[3f661849]57static void ssl_conn_free( struct scd *conn );
[309cb9e]58static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
[f920d9e]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
[9730d72]63void ssl_init( void )
[ba5add7]64{
65        initialized = TRUE;
[8e9e2b7]66        SSL_library_init();
67        // SSLeay_add_ssl_algorithms();
[ba5add7]68}
69
[a72dc2b]70void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data )
[b7d3cc34]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        {
[3f661849]77                ssl_conn_free( conn );
[f920d9e]78                return NULL;
[b7d3cc34]79        }
80       
[8a2221a7]81        conn->func = func;
82        conn->data = data;
83        conn->inpa = -1;
[3f661849]84        conn->hostname = g_strdup( host );
[8a2221a7]85       
[f920d9e]86        return conn;
87}
88
[486ddb5]89void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data )
[f920d9e]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;
[200e151]97        conn->verify = verify && global.conf->cafile;
[3f661849]98        conn->hostname = g_strdup( hostname );
[f920d9e]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       
[e046390]118        return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
[f920d9e]119}
120
121static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
122{
123        struct scd *conn = data;
[59c03bd]124        const SSL_METHOD *meth;
[f920d9e]125       
[486ddb5]126        if( conn->verify )
127        {
[3f661849]128                /* Right now we don't have any verification functionality for OpenSSL. */
[200e151]129                conn->func( conn->data, 1, NULL, cond );
[486ddb5]130                if( source >= 0 ) closesocket( source );
[3f661849]131                ssl_conn_free( conn );
[486ddb5]132
133                return FALSE;
134        }
135
[f920d9e]136        if( source == -1 )
137                goto ssl_connected_failure;
138       
[b7d3cc34]139        if( !initialized )
140        {
[ba5add7]141                ssl_init();
[b7d3cc34]142        }
143       
144        meth = TLSv1_client_method();
145        conn->ssl_ctx = SSL_CTX_new( meth );
146        if( conn->ssl_ctx == NULL )
[f920d9e]147                goto ssl_connected_failure;
[b7d3cc34]148       
149        conn->ssl = SSL_new( conn->ssl_ctx );
150        if( conn->ssl == NULL )
[f920d9e]151                goto ssl_connected_failure;
[b7d3cc34]152       
[309cb9e]153        /* We can do at least the handshake with non-blocking I/O */
[26fdfc5]154        sock_make_nonblocking( conn->fd );
[b7d3cc34]155        SSL_set_fd( conn->ssl, conn->fd );
156       
[3f661849]157        if( conn->hostname && !isdigit( conn->hostname[0] ) )
158                SSL_set_tlsext_host_name( conn->ssl, conn->hostname );
159       
[26fdfc5]160        return ssl_handshake( data, source, cond );
[f920d9e]161
162ssl_connected_failure:
[486ddb5]163        conn->func( conn->data, 0, NULL, cond );
[3f661849]164        ssl_disconnect( conn );
[f920d9e]165        return FALSE;
166
[26fdfc5]167}       
168
[309cb9e]169static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond )
[26fdfc5]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 )
[f920d9e]178                {
[486ddb5]179                        conn->func( conn->data, 0, NULL, cond );
[3f661849]180                        ssl_disconnect( conn );
[f920d9e]181                        return FALSE;
182                }
[26fdfc5]183               
[309cb9e]184                conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data );
185                return FALSE;
[26fdfc5]186        }
[b7d3cc34]187       
188        conn->established = TRUE;
[26fdfc5]189        sock_make_blocking( conn->fd );         /* For now... */
[486ddb5]190        conn->func( conn->data, 0, conn, cond );
[309cb9e]191        return FALSE;
[b7d3cc34]192}
193
194int ssl_read( void *conn, char *buf, int len )
195{
[26fdfc5]196        int st;
197       
[b7d3cc34]198        if( !((struct scd*)conn)->established )
[26fdfc5]199        {
200                ssl_errno = SSL_NOHANDSHAKE;
201                return -1;
202        }
203       
204        st = SSL_read( ((struct scd*)conn)->ssl, buf, len );
[b7d3cc34]205       
[26fdfc5]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       
[327af51]214        if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
[50b8978]215       
[26fdfc5]216        return st;
[b7d3cc34]217}
218
219int ssl_write( void *conn, const char *buf, int len )
220{
[26fdfc5]221        int st;
222       
[b7d3cc34]223        if( !((struct scd*)conn)->established )
[26fdfc5]224        {
225                ssl_errno = SSL_NOHANDSHAKE;
226                return -1;
227        }
228       
229        st = SSL_write( ((struct scd*)conn)->ssl, buf, len );
[b7d3cc34]230       
[327af51]231        if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
[50b8978]232       
[26fdfc5]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;
[b7d3cc34]242}
243
[8a2221a7]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
[3f661849]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
[b7d3cc34]259void ssl_disconnect( void *conn_ )
260{
261        struct scd *conn = conn_;
262       
[26fdfc5]263        if( conn->inpa != -1 )
[309cb9e]264                b_event_remove( conn->inpa );
[26fdfc5]265       
[b7d3cc34]266        if( conn->established )
267                SSL_shutdown( conn->ssl );
268       
269        closesocket( conn->fd );
270       
[3f661849]271        ssl_conn_free( conn );
[b7d3cc34]272}
273
274int ssl_getfd( void *conn )
275{
276        return( ((struct scd*)conn)->fd );
277}
[26fdfc5]278
[ba9edaa]279b_input_condition ssl_getdirection( void *conn )
[26fdfc5]280{
[e046390]281        return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ );
[26fdfc5]282}
[523fb23]283
[78b8401]284char *ssl_verify_strerror( int code )
285{
286        return g_strdup( "SSL certificate verification not supported by BitlBee OpenSSL code." );
287}
288
[523fb23]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;   
[50b8978]292        EVP_CIPHER_CTX ctx;
[523fb23]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);   
[50b8978]306        //EVP_cleanup();
[523fb23]307       
308        return output_length;
309}
Note: See TracBrowser for help on using the repository browser.