source: lib/ssl_openssl.c @ 3f81999

Last change on this file since 3f81999 was 6738a67, checked in by Sven Moritz Hallberg <pesco@…>, at 2008-07-16T23:22:52Z

merge in latest trunk

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