source: lib/ssl_openssl.c @ 193dc74

Last change on this file since 193dc74 was 50b8978, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-13T09:12:54Z

OpenSSL fixes + debugging.

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