source: protocols/ssl_openssl.c @ 74c119d

Last change on this file since 74c119d was a03a9f3, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-18T01:02:29Z

Non-blocking I/O for OpenSSL, plus some extra (important) cleanup code for GnuTLS.

  • Property mode set to 100644
File size: 5.1 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 - GnuTLS 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 void ssl_connected( gpointer data, gint source, GaimInputCondition cond );
55
56
57
58void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
59{
60        struct scd *conn = g_new0( struct scd, 1 );
61        SSL_METHOD *meth;
62       
63        conn->fd = proxy_connect( host, port, ssl_connected, conn );
64        conn->func = func;
65        conn->data = data;
66       
67        if( conn->fd < 0 )
68        {
69                g_free( conn );
70                return( NULL );
71        }
72       
73        if( !initialized )
74        {
75                initialized = TRUE;
76                SSLeay_add_ssl_algorithms();
77        }
78       
79        meth = TLSv1_client_method();
80        conn->ssl_ctx = SSL_CTX_new( meth );
81        if( conn->ssl_ctx == NULL )
82        {
83                conn->fd = -1;
84                return( NULL );
85        }
86       
87        conn->ssl = SSL_new( conn->ssl_ctx );
88        if( conn->ssl == NULL )
89        {
90                conn->fd = -1;
91                return( NULL );
92        }
93       
94        return( conn );
95}
96
97static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond );
98
99static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
100{
101        struct scd *conn = data;
102       
103        if( source == -1 )
104                return ssl_handshake( data, -1, cond );
105       
106        /* Make it non-blocking at least during the handshake... */
107        sock_make_nonblocking( conn->fd );
108        SSL_set_fd( conn->ssl, conn->fd );
109       
110        return ssl_handshake( data, source, cond );
111}       
112
113static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond )
114{
115        struct scd *conn = data;
116        int st;
117       
118        if( conn->inpa != -1 )
119        {
120                gaim_input_remove( conn->inpa );
121                conn->inpa = -1;
122        }
123       
124        if( ( st = SSL_connect( conn->ssl ) ) < 0 )
125        {
126                conn->lasterr = SSL_get_error( conn->ssl, st );
127                if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE )
128                        goto ssl_connected_failure;
129               
130                conn->inpa = gaim_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data );
131                return;
132        }
133       
134        conn->established = TRUE;
135        sock_make_blocking( conn->fd );         /* For now... */
136        conn->func( conn->data, conn, cond );
137        return;
138       
139ssl_connected_failure:
140        conn->func( conn->data, NULL, cond );
141       
142        if( conn->ssl )
143        {
144                SSL_shutdown( conn->ssl );
145                SSL_free( conn->ssl );
146        }
147        if( conn->ssl_ctx )
148        {
149                SSL_CTX_free( conn->ssl_ctx );
150        }
151        if( source >= 0 ) closesocket( source );
152        g_free( conn );
153}
154
155int ssl_read( void *conn, char *buf, int len )
156{
157        int st;
158       
159        if( !((struct scd*)conn)->established )
160        {
161                ssl_errno = SSL_NOHANDSHAKE;
162                return -1;
163        }
164       
165        st = SSL_read( ((struct scd*)conn)->ssl, buf, len );
166       
167        ssl_errno = SSL_OK;
168        if( st <= 0 )
169        {
170                ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st );
171                if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE )
172                        ssl_errno = SSL_AGAIN;
173        }
174       
175        return st;
176}
177
178int ssl_write( void *conn, const char *buf, int len )
179{
180        int st;
181       
182        if( !((struct scd*)conn)->established )
183        {
184                ssl_errno = SSL_NOHANDSHAKE;
185                return -1;
186        }
187       
188        st = SSL_write( ((struct scd*)conn)->ssl, buf, len );
189       
190        ssl_errno = SSL_OK;
191        if( st <= 0 )
192        {
193                ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st );
194                if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE )
195                        ssl_errno = SSL_AGAIN;
196        }
197       
198        return st;
199}
200
201void ssl_disconnect( void *conn_ )
202{
203        struct scd *conn = conn_;
204       
205        if( conn->inpa != -1 )
206                gaim_input_remove( conn->inpa );
207       
208        if( conn->established )
209                SSL_shutdown( conn->ssl );
210       
211        closesocket( conn->fd );
212       
213        SSL_free( conn->ssl );
214        SSL_CTX_free( conn->ssl_ctx );
215        g_free( conn );
216}
217
218int ssl_getfd( void *conn )
219{
220        return( ((struct scd*)conn)->fd );
221}
222
223GaimInputCondition ssl_getdirection( void *conn )
224{
225        return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ );
226}
Note: See TracBrowser for help on using the repository browser.