source: lib/ssl_gnutls.c @ 17f6079

Last change on this file since 17f6079 was 83e47ec, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-17T06:44:35Z

Use gcrypt for 3DES encryption (used for new MSN authentication) so we
mostly don't need lib/des.c anymore.

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