source: lib/ssl_openssl.c @ 80acb6d

Last change on this file since 80acb6d was 80acb6d, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-11-14T10:43:03Z

GnuTLS now also needs ssl_pending() implemented. Bug #860.

  • Property mode set to 100644
File size: 7.3 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
243int ssl_pending( void *conn )
244{
245        return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ?
246               SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0;
247}
248
249void ssl_disconnect( void *conn_ )
250{
251        struct scd *conn = conn_;
252       
253        if( conn->inpa != -1 )
254                b_event_remove( conn->inpa );
255       
256        if( conn->established )
257                SSL_shutdown( conn->ssl );
258       
259        closesocket( conn->fd );
260       
261        SSL_free( conn->ssl );
262        SSL_CTX_free( conn->ssl_ctx );
263        g_free( conn );
264}
265
266int ssl_getfd( void *conn )
267{
268        return( ((struct scd*)conn)->fd );
269}
270
271b_input_condition ssl_getdirection( void *conn )
272{
273        return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ );
274}
275
276size_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)
277{
278        int output_length = 0;   
279        EVP_CIPHER_CTX ctx;
280       
281        *res = g_new0(unsigned char, 72);
282       
283        /* Don't set key or IV because we will modify the parameters */
284        EVP_CIPHER_CTX_init(&ctx);
285        EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1);
286        EVP_CIPHER_CTX_set_key_length(&ctx, key_len);
287        EVP_CIPHER_CTX_set_padding(&ctx, 0);
288        /* We finished modifying parameters so now we can set key and IV */
289        EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1);
290        EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len);
291        EVP_CipherFinal_ex(&ctx, *res, &output_length);
292        EVP_CIPHER_CTX_cleanup(&ctx);   
293        //EVP_cleanup();
294       
295        return output_length;
296}
Note: See TracBrowser for help on using the repository browser.