source: lib/ssl_gnutls.c @ 193dc74

Last change on this file since 193dc74 was 50b8978, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-13T09:12:54Z

OpenSSL fixes + debugging.

  • Property mode set to 100644
File size: 6.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 <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, B_EV_IO_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        if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
192       
193        return st;
194}
195
196int ssl_write( void *conn, const char *buf, int len )
197{
198        int st;
199       
200        if( !((struct scd*)conn)->established )
201        {
202                ssl_errno = SSL_NOHANDSHAKE;
203                return( -1 );
204        }
205       
206        st = gnutls_record_send( ((struct scd*)conn)->session, buf, len );
207       
208        ssl_errno = SSL_OK;
209        if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
210                ssl_errno = SSL_AGAIN;
211       
212        if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
213       
214        return st;
215}
216
217/* See ssl_openssl.c for an explanation. */
218int ssl_pending( void *conn )
219{
220        return 0;
221}
222
223void ssl_disconnect( void *conn_ )
224{
225        struct scd *conn = conn_;
226       
227        if( conn->inpa != -1 )
228                b_event_remove( conn->inpa );
229       
230        if( conn->established )
231                gnutls_bye( conn->session, GNUTLS_SHUT_WR );
232       
233        closesocket( conn->fd );
234       
235        if( conn->session )
236                gnutls_deinit( conn->session );
237        if( conn->xcred )
238                gnutls_certificate_free_credentials( conn->xcred );
239        g_free( conn );
240}
241
242int ssl_getfd( void *conn )
243{
244        return( ((struct scd*)conn)->fd );
245}
246
247b_input_condition ssl_getdirection( void *conn )
248{
249        return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ?
250                B_EV_IO_WRITE : B_EV_IO_READ );
251}
Note: See TracBrowser for help on using the repository browser.