source: lib/ssl_openssl.c @ 03a8f8e

Last change on this file since 03a8f8e was 8e9e2b7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-03T02:45:26Z

Merging mainline, which includes a huge msnp13 merge.

Not 100% sure about the OpenSSL merge, should double check that but I'm
currently offline.

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