source: lib/ssl_openssl.c @ ac46218

Last change on this file since ac46218 was 8a2221a7, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-03-23T14:29:19Z

Fixed stalling issue with OpenSSL and Jabber (#368).

  • Property mode set to 100644
File size: 6.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, GAIM_INPUT_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                SSLeay_add_ssl_algorithms();
119        }
120       
121        meth = TLSv1_client_method();
122        conn->ssl_ctx = SSL_CTX_new( meth );
123        if( conn->ssl_ctx == NULL )
124                goto ssl_connected_failure;
125       
126        conn->ssl = SSL_new( conn->ssl_ctx );
127        if( conn->ssl == NULL )
128                goto ssl_connected_failure;
129       
130        /* We can do at least the handshake with non-blocking I/O */
131        sock_make_nonblocking( conn->fd );
132        SSL_set_fd( conn->ssl, conn->fd );
133       
134        return ssl_handshake( data, source, cond );
135
136ssl_connected_failure:
137        conn->func( conn->data, NULL, cond );
138       
139        if( conn->ssl )
140        {
141                SSL_shutdown( conn->ssl );
142                SSL_free( conn->ssl );
143        }
144        if( conn->ssl_ctx )
145        {
146                SSL_CTX_free( conn->ssl_ctx );
147        }
148        if( source >= 0 ) closesocket( source );
149        g_free( conn );
150       
151        return FALSE;
152
153}       
154
155static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond )
156{
157        struct scd *conn = data;
158        int st;
159       
160        if( ( st = SSL_connect( conn->ssl ) ) < 0 )
161        {
162                conn->lasterr = SSL_get_error( conn->ssl, st );
163                if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE )
164                {
165                        conn->func( conn->data, NULL, cond );
166                       
167                        SSL_shutdown( conn->ssl );
168                        SSL_free( conn->ssl );
169                        SSL_CTX_free( conn->ssl_ctx );
170                       
171                        if( source >= 0 ) closesocket( source );
172                        g_free( conn );
173                       
174                        return FALSE;
175                }
176               
177                conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data );
178                return FALSE;
179        }
180       
181        conn->established = TRUE;
182        sock_make_blocking( conn->fd );         /* For now... */
183        conn->func( conn->data, conn, cond );
184        return FALSE;
185}
186
187int ssl_read( void *conn, char *buf, int len )
188{
189        int st;
190       
191        if( !((struct scd*)conn)->established )
192        {
193                ssl_errno = SSL_NOHANDSHAKE;
194                return -1;
195        }
196       
197        st = SSL_read( ((struct scd*)conn)->ssl, buf, len );
198       
199        ssl_errno = SSL_OK;
200        if( st <= 0 )
201        {
202                ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st );
203                if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE )
204                        ssl_errno = SSL_AGAIN;
205        }
206       
207        return st;
208}
209
210int ssl_write( void *conn, const char *buf, int len )
211{
212        int st;
213       
214        if( !((struct scd*)conn)->established )
215        {
216                ssl_errno = SSL_NOHANDSHAKE;
217                return -1;
218        }
219       
220        st = SSL_write( ((struct scd*)conn)->ssl, buf, len );
221       
222        ssl_errno = SSL_OK;
223        if( st <= 0 )
224        {
225                ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st );
226                if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE )
227                        ssl_errno = SSL_AGAIN;
228        }
229       
230        return st;
231}
232
233/* Only OpenSSL *really* needs this (and well, maybe NSS). See for more info:
234   http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209
235   http://www.openssl.org/docs/ssl/SSL_pending.html
236   
237   Required because OpenSSL empties the TCP buffer completely but doesn't
238   necessarily give us all the unencrypted data.
239   
240   Returns 0 if there's nothing left or if we don't have to care (GnuTLS),
241   1 if there's more data. */
242int ssl_pending( void *conn )
243{
244        return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ?
245               SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0;
246}
247
248void ssl_disconnect( void *conn_ )
249{
250        struct scd *conn = conn_;
251       
252        if( conn->inpa != -1 )
253                b_event_remove( conn->inpa );
254       
255        if( conn->established )
256                SSL_shutdown( conn->ssl );
257       
258        closesocket( conn->fd );
259       
260        SSL_free( conn->ssl );
261        SSL_CTX_free( conn->ssl_ctx );
262        g_free( conn );
263}
264
265int ssl_getfd( void *conn )
266{
267        return( ((struct scd*)conn)->fd );
268}
269
270b_input_condition ssl_getdirection( void *conn )
271{
272        return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ );
273}
Note: See TracBrowser for help on using the repository browser.