source: lib/ssl_gnutls.c @ 7adc657

Last change on this file since 7adc657 was 3e79889, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-12-12T23:57:49Z

Checking conn->xcred before trying to clean it up since GnuTLS doesn't
seem to check for NULL pointers here. (Closes #257)

  • Property mode set to 100644
File size: 5.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 - 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 <gnutls/gnutls.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include "proxy.h"
30#include "ssl_client.h"
31#include "sock.h"
32#include "stdlib.h"
33
34int ssl_errno = 0;
35
36static gboolean initialized = FALSE;
37
38#include <limits.h>
39
40#if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL
41#define GNUTLS_STUPID_CAST (long)
42#else
43#define GNUTLS_STUPID_CAST (int)
44#endif
45
46struct scd
47{
48        ssl_input_function func;
49        gpointer data;
50        int fd;
51        gboolean established;
52        int inpa;
53       
54        gnutls_session session;
55        gnutls_certificate_credentials xcred;
56};
57
58static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
59static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
60static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
61
62
63void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
64{
65        struct scd *conn = g_new0( struct scd, 1 );
66       
67        conn->fd = proxy_connect( host, port, ssl_connected, conn );
68        conn->func = func;
69        conn->data = data;
70        conn->inpa = -1;
71       
72        if( conn->fd < 0 )
73        {
74                g_free( conn );
75                return NULL;
76        }
77       
78        return conn;
79}
80
81void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
82{
83        struct scd *conn = g_new0( struct scd, 1 );
84       
85        conn->fd = fd;
86        conn->func = func;
87        conn->data = data;
88        conn->inpa = -1;
89       
90        /* This function should be called via a (short) timeout instead of
91           directly from here, because these SSL calls are *supposed* to be
92           *completely* asynchronous and not ready yet when this function
93           (or *_connect, for examle) returns. Also, errors are reported via
94           the callback function, not via this function's return value.
95           
96           In short, doing things like this makes the rest of the code a lot
97           simpler. */
98       
99        b_timeout_add( 1, ssl_starttls_real, conn );
100       
101        return conn;
102}
103
104static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
105{
106        struct scd *conn = data;
107       
108        return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE );
109}
110
111static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
112{
113        struct scd *conn = data;
114       
115        if( source == -1 )
116        {
117                conn->func( conn->data, NULL, cond );
118                g_free( conn );
119                return FALSE;
120        }
121       
122        if( !initialized )
123        {
124                gnutls_global_init();
125                initialized = TRUE;
126                atexit( gnutls_global_deinit );
127        }
128       
129        gnutls_certificate_allocate_credentials( &conn->xcred );
130        gnutls_init( &conn->session, GNUTLS_CLIENT );
131        gnutls_set_default_priority( conn->session );
132        gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
133       
134        sock_make_nonblocking( conn->fd );
135        gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd );
136       
137        return ssl_handshake( data, source, cond );
138}
139
140static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond )
141{
142        struct scd *conn = data;
143        int st;
144       
145        if( ( st = gnutls_handshake( conn->session ) ) < 0 )
146        {
147                if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
148                {
149                        conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ),
150                                                  ssl_handshake, data );
151                }
152                else
153                {
154                        conn->func( conn->data, NULL, cond );
155                       
156                        gnutls_deinit( conn->session );
157                        gnutls_certificate_free_credentials( conn->xcred );
158                        closesocket( conn->fd );
159                       
160                        g_free( conn );
161                }
162        }
163        else
164        {
165                /* For now we can't handle non-blocking perfectly everywhere... */
166                sock_make_blocking( conn->fd );
167               
168                conn->established = TRUE;
169                conn->func( conn->data, conn, cond );
170        }
171       
172        return FALSE;
173}
174
175int ssl_read( void *conn, char *buf, int len )
176{
177        int st;
178       
179        if( !((struct scd*)conn)->established )
180        {
181                ssl_errno = SSL_NOHANDSHAKE;
182                return( -1 );
183        }
184       
185        st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len );
186       
187        ssl_errno = SSL_OK;
188        if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
189                ssl_errno = SSL_AGAIN;
190       
191        return st;
192}
193
194int ssl_write( void *conn, const char *buf, int len )
195{
196        int st;
197       
198        if( !((struct scd*)conn)->established )
199        {
200                ssl_errno = SSL_NOHANDSHAKE;
201                return( -1 );
202        }
203       
204        st = gnutls_record_send( ((struct scd*)conn)->session, buf, len );
205       
206        ssl_errno = SSL_OK;
207        if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
208                ssl_errno = SSL_AGAIN;
209       
210        return st;
211}
212
213void ssl_disconnect( void *conn_ )
214{
215        struct scd *conn = conn_;
216       
217        if( conn->inpa != -1 )
218                b_event_remove( conn->inpa );
219       
220        if( conn->established )
221                gnutls_bye( conn->session, GNUTLS_SHUT_WR );
222       
223        closesocket( conn->fd );
224       
225        if( conn->session )
226                gnutls_deinit( conn->session );
227        if( conn->xcred )
228                gnutls_certificate_free_credentials( conn->xcred );
229        g_free( conn );
230}
231
232int ssl_getfd( void *conn )
233{
234        return( ((struct scd*)conn)->fd );
235}
236
237b_input_condition ssl_getdirection( void *conn )
238{
239        return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ?
240                GAIM_INPUT_WRITE : GAIM_INPUT_READ );
241}
Note: See TracBrowser for help on using the repository browser.