source: lib/ssl_gnutls.c @ 191ac9d

Last change on this file since 191ac9d was 80acb6d, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-11-14T10:43:03Z

GnuTLS now also needs ssl_pending() implemented. Bug #860.

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