source: lib/ssl_gnutls.c @ 59ab2af

Last change on this file since 59ab2af was ba5add7, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-17T01:39:39Z

explicitly initialize ssl in order to avoid gnutls and libotr fighting over the global state of libgcrypt

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