source: lib/ssl_gnutls.c @ d717020

Last change on this file since d717020 was ca974d7, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-04T19:14:29Z

Debug output tweaks: Try to send everything to stderr, and add ifdef to
enable printing of all SSL traffic.

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