source: lib/ssl_gnutls.c @ 9c9a29c

Last change on this file since 9c9a29c was 6197702, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-09T18:41:19Z

Merging OTR branch. It's more or less a plugin if you enable it, and
otherwise a no-op. DO NOT INSTALL THIS ON PUBLIC SERVERS.

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