source: lib/ssl_gnutls.c @ 27ad50b

Last change on this file since 27ad50b was d43d55a, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-04-18T13:47:36Z

Fix GnuTLS >2.12 or so compatibility.
Bug #779 and https://savannah.gnu.org/support/index.php?107660

  • Property mode set to 100644
File size: 6.8 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        gnutls_transport_set_lowat( conn->session, 1 ); 
138        gnutls_set_default_priority( conn->session );
139        gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
140       
141        sock_make_nonblocking( conn->fd );
142        gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd );
143       
144        return ssl_handshake( data, source, cond );
145}
146
147static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond )
148{
149        struct scd *conn = data;
150        int st;
151       
152        if( ( st = gnutls_handshake( conn->session ) ) < 0 )
153        {
154                if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
155                {
156                        conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ),
157                                                  ssl_handshake, data );
158                }
159                else
160                {
161                        conn->func( conn->data, NULL, cond );
162                       
163                        gnutls_deinit( conn->session );
164                        gnutls_certificate_free_credentials( conn->xcred );
165                        closesocket( conn->fd );
166                       
167                        g_free( conn );
168                }
169        }
170        else
171        {
172                /* For now we can't handle non-blocking perfectly everywhere... */
173                sock_make_blocking( conn->fd );
174               
175                conn->established = TRUE;
176                conn->func( conn->data, conn, cond );
177        }
178       
179        return FALSE;
180}
181
182int ssl_read( void *conn, char *buf, int len )
183{
184        int st;
185       
186        if( !((struct scd*)conn)->established )
187        {
188                ssl_errno = SSL_NOHANDSHAKE;
189                return( -1 );
190        }
191       
192        st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len );
193       
194        ssl_errno = SSL_OK;
195        if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
196                ssl_errno = SSL_AGAIN;
197       
198        if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st );
199       
200        return st;
201}
202
203int ssl_write( void *conn, const char *buf, int len )
204{
205        int st;
206       
207        if( !((struct scd*)conn)->established )
208        {
209                ssl_errno = SSL_NOHANDSHAKE;
210                return( -1 );
211        }
212       
213        st = gnutls_record_send( ((struct scd*)conn)->session, buf, len );
214       
215        ssl_errno = SSL_OK;
216        if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
217                ssl_errno = SSL_AGAIN;
218       
219        if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st );
220       
221        return st;
222}
223
224/* See ssl_openssl.c for an explanation. */
225int ssl_pending( void *conn )
226{
227        return 0;
228}
229
230void ssl_disconnect( void *conn_ )
231{
232        struct scd *conn = conn_;
233       
234        if( conn->inpa != -1 )
235                b_event_remove( conn->inpa );
236       
237        if( conn->established )
238                gnutls_bye( conn->session, GNUTLS_SHUT_WR );
239       
240        closesocket( conn->fd );
241       
242        if( conn->session )
243                gnutls_deinit( conn->session );
244        if( conn->xcred )
245                gnutls_certificate_free_credentials( conn->xcred );
246        g_free( conn );
247}
248
249int ssl_getfd( void *conn )
250{
251        return( ((struct scd*)conn)->fd );
252}
253
254b_input_condition ssl_getdirection( void *conn )
255{
256        return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ?
257                B_EV_IO_WRITE : B_EV_IO_READ );
258}
259
260size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input,
261                         size_t input_len, const unsigned char *iv, unsigned char **res )
262{
263        gcry_cipher_hd_t gcr;
264        gcry_error_t st;
265       
266        ssl_init();
267       
268        *res = g_malloc( input_len  );
269        st = gcry_cipher_open( &gcr, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0 ) ||
270             gcry_cipher_setkey( gcr, key, key_len ) ||
271             gcry_cipher_setiv( gcr, iv, 8 ) ||
272             gcry_cipher_encrypt( gcr, *res, input_len, input, input_len );
273       
274        gcry_cipher_close( gcr );
275       
276        if( st == 0 )
277                return input_len;
278       
279        g_free( *res );
280        return 0;
281}
Note: See TracBrowser for help on using the repository browser.