Scan media entities as well, not just url entities. This should expand more
[bitlbee.git] / lib / ssl_gnutls.c
blob93601ba6fca119ffdcb1fe1256d76aaff3e3c66c
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2011 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* SSL module - GnuTLS version */
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.
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.
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
26 #include <gnutls/gnutls.h>
27 #include <gnutls/x509.h>
28 #include <gcrypt.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include "proxy.h"
32 #include "ssl_client.h"
33 #include "sock.h"
34 #include "stdlib.h"
35 #include "bitlbee.h"
37 int ssl_errno = 0;
39 static gboolean initialized = FALSE;
40 gnutls_certificate_credentials xcred;
42 #include <limits.h>
44 #if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL
45 #define GNUTLS_STUPID_CAST (long)
46 #else
47 #define GNUTLS_STUPID_CAST (int)
48 #endif
50 #define SSLDEBUG 0
52 struct scd
54 ssl_input_function func;
55 gpointer data;
56 int fd;
57 gboolean established;
58 int inpa;
59 char *hostname;
60 gboolean verify;
62 gnutls_session session;
65 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
66 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
67 static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
69 static void ssl_deinit( void );
71 static void ssl_log( int level, const char *line )
73 printf( "%d %s", level, line );
76 void ssl_init( void )
78 if( initialized )
79 return;
81 gnutls_global_init();
82 gnutls_certificate_allocate_credentials( &xcred );
83 if( global.conf->cafile )
85 gnutls_certificate_set_x509_trust_file( xcred, global.conf->cafile, GNUTLS_X509_FMT_PEM );
87 /* Not needed in GnuTLS 2.11+ but we support older versions for now. */
88 gnutls_certificate_set_verify_flags( xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT );
90 initialized = TRUE;
92 gnutls_global_set_log_function( ssl_log );
94 gnutls_global_set_log_level( 3 );
97 atexit( ssl_deinit );
100 static void ssl_deinit( void )
102 gnutls_global_deinit();
103 gnutls_certificate_free_credentials( xcred );
106 void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data )
108 struct scd *conn = g_new0( struct scd, 1 );
110 conn->fd = proxy_connect( host, port, ssl_connected, conn );
111 conn->func = func;
112 conn->data = data;
113 conn->inpa = -1;
114 conn->hostname = g_strdup( host );
115 conn->verify = verify && global.conf->cafile;
117 if( conn->fd < 0 )
119 g_free( conn );
120 return NULL;
123 return conn;
126 void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data )
128 struct scd *conn = g_new0( struct scd, 1 );
130 conn->fd = fd;
131 conn->func = func;
132 conn->data = data;
133 conn->inpa = -1;
134 conn->hostname = hostname;
136 /* For now, SSL verification is globally enabled by setting the cafile
137 setting in bitlbee.conf. Commented out by default because probably
138 not everyone has this file in the same place and plenty of folks
139 may not have the cert of their private Jabber server in it. */
140 conn->verify = verify && global.conf->cafile;
142 /* This function should be called via a (short) timeout instead of
143 directly from here, because these SSL calls are *supposed* to be
144 *completely* asynchronous and not ready yet when this function
145 (or *_connect, for examle) returns. Also, errors are reported via
146 the callback function, not via this function's return value.
148 In short, doing things like this makes the rest of the code a lot
149 simpler. */
151 b_timeout_add( 1, ssl_starttls_real, conn );
153 return conn;
156 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
158 struct scd *conn = data;
160 return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
163 static int verify_certificate_callback( gnutls_session_t session )
165 unsigned int status;
166 const gnutls_datum_t *cert_list;
167 unsigned int cert_list_size;
168 int gnutlsret;
169 int verifyret = 0;
170 gnutls_x509_crt_t cert;
171 const char *hostname;
173 hostname = gnutls_session_get_ptr( session );
175 gnutlsret = gnutls_certificate_verify_peers2( session, &status );
176 if( gnutlsret < 0 )
177 return VERIFY_CERT_ERROR;
179 if( status & GNUTLS_CERT_INVALID )
180 verifyret |= VERIFY_CERT_INVALID;
182 if( status & GNUTLS_CERT_REVOKED )
183 verifyret |= VERIFY_CERT_REVOKED;
185 if( status & GNUTLS_CERT_SIGNER_NOT_FOUND )
186 verifyret |= VERIFY_CERT_SIGNER_NOT_FOUND;
188 if( status & GNUTLS_CERT_SIGNER_NOT_CA )
189 verifyret |= VERIFY_CERT_SIGNER_NOT_CA;
191 if( status & GNUTLS_CERT_INSECURE_ALGORITHM )
192 verifyret |= VERIFY_CERT_INSECURE_ALGORITHM;
194 #ifdef GNUTLS_CERT_NOT_ACTIVATED
195 /* Amusingly, the GnuTLS function used above didn't check for expiry
196 until GnuTLS 2.8 or so. (See CVE-2009-1417) */
197 if( status & GNUTLS_CERT_NOT_ACTIVATED )
198 verifyret |= VERIFY_CERT_NOT_ACTIVATED;
200 if( status & GNUTLS_CERT_EXPIRED )
201 verifyret |= VERIFY_CERT_EXPIRED;
202 #endif
204 if( gnutls_certificate_type_get( session ) != GNUTLS_CRT_X509 || gnutls_x509_crt_init( &cert ) < 0 )
205 return VERIFY_CERT_ERROR;
207 cert_list = gnutls_certificate_get_peers( session, &cert_list_size );
208 if( cert_list == NULL || gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER ) < 0 )
209 return VERIFY_CERT_ERROR;
211 if( !gnutls_x509_crt_check_hostname( cert, hostname ) )
213 verifyret |= VERIFY_CERT_INVALID;
214 verifyret |= VERIFY_CERT_WRONG_HOSTNAME;
217 gnutls_x509_crt_deinit( cert );
219 return verifyret;
222 char *ssl_verify_strerror( int code )
224 GString *ret = g_string_new( "" );
226 if( code & VERIFY_CERT_REVOKED )
227 g_string_append( ret, "certificate has been revoked, " );
228 if( code & VERIFY_CERT_SIGNER_NOT_FOUND )
229 g_string_append( ret, "certificate hasn't got a known issuer, " );
230 if( code & VERIFY_CERT_SIGNER_NOT_CA )
231 g_string_append( ret, "certificate's issuer is not a CA, " );
232 if( code & VERIFY_CERT_INSECURE_ALGORITHM )
233 g_string_append( ret, "certificate uses an insecure algorithm, " );
234 if( code & VERIFY_CERT_NOT_ACTIVATED )
235 g_string_append( ret, "certificate has not been activated, " );
236 if( code & VERIFY_CERT_EXPIRED )
237 g_string_append( ret, "certificate has expired, " );
238 if( code & VERIFY_CERT_WRONG_HOSTNAME )
239 g_string_append( ret, "certificate hostname mismatch, " );
241 if( ret->len == 0 )
243 g_string_free( ret, TRUE );
244 return NULL;
246 else
248 g_string_truncate( ret, ret->len - 2 );
249 return g_string_free( ret, FALSE );
253 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
255 struct scd *conn = data;
257 if( source == -1 )
259 conn->func( conn->data, 0, NULL, cond );
260 g_free( conn );
261 return FALSE;
264 ssl_init();
266 gnutls_init( &conn->session, GNUTLS_CLIENT );
267 if( conn->verify )
268 gnutls_session_set_ptr( conn->session, (void *) conn->hostname );
269 #if GNUTLS_VERSION_NUMBER < 0x020c00
270 gnutls_transport_set_lowat( conn->session, 0 );
271 #endif
272 gnutls_set_default_priority( conn->session );
273 gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, xcred );
275 sock_make_nonblocking( conn->fd );
276 gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd );
278 return ssl_handshake( data, source, cond );
281 static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond )
283 struct scd *conn = data;
284 int st, stver;
286 if( ( st = gnutls_handshake( conn->session ) ) < 0 )
288 if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
290 conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ),
291 ssl_handshake, data );
293 else
295 conn->func( conn->data, 0, NULL, cond );
297 gnutls_deinit( conn->session );
298 closesocket( conn->fd );
300 g_free( conn );
303 else
305 if( conn->verify && ( stver = verify_certificate_callback( conn->session ) ) != 0 )
307 conn->func( conn->data, stver, NULL, cond );
309 gnutls_deinit( conn->session );
310 closesocket( conn->fd );
312 g_free( conn );
314 else
316 /* For now we can't handle non-blocking perfectly everywhere... */
317 sock_make_blocking( conn->fd );
319 conn->established = TRUE;
320 conn->func( conn->data, 0, conn, cond );
324 return FALSE;
327 int ssl_read( void *conn, char *buf, int len )
329 int st;
331 if( !((struct scd*)conn)->established )
333 ssl_errno = SSL_NOHANDSHAKE;
334 return -1;
337 st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len );
339 ssl_errno = SSL_OK;
340 if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
341 ssl_errno = SSL_AGAIN;
343 if( SSLDEBUG && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 2, buf, st );
345 return st;
348 int ssl_write( void *conn, const char *buf, int len )
350 int st;
352 if( !((struct scd*)conn)->established )
354 ssl_errno = SSL_NOHANDSHAKE;
355 return -1;
358 st = gnutls_record_send( ((struct scd*)conn)->session, buf, len );
360 ssl_errno = SSL_OK;
361 if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
362 ssl_errno = SSL_AGAIN;
364 if( SSLDEBUG && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 2, buf, st );
366 return st;
369 int ssl_pending( void *conn )
371 if( conn == NULL )
372 return 0;
374 if( !((struct scd*)conn)->established )
376 ssl_errno = SSL_NOHANDSHAKE;
377 return 0;
380 #if GNUTLS_VERSION_NUMBER >= 0x03000d && GNUTLS_VERSION_NUMBER <= 0x030012
381 if( ssl_errno == SSL_AGAIN )
382 return 0;
383 #endif
385 return gnutls_record_check_pending( ((struct scd*)conn)->session ) != 0;
388 void ssl_disconnect( void *conn_ )
390 struct scd *conn = conn_;
392 if( conn->inpa != -1 )
393 b_event_remove( conn->inpa );
395 if( conn->established )
396 gnutls_bye( conn->session, GNUTLS_SHUT_WR );
398 closesocket( conn->fd );
400 if( conn->session )
401 gnutls_deinit( conn->session );
402 g_free( conn );
405 int ssl_getfd( void *conn )
407 return( ((struct scd*)conn)->fd );
410 b_input_condition ssl_getdirection( void *conn )
412 return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ?
413 B_EV_IO_WRITE : B_EV_IO_READ );
416 size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input,
417 size_t input_len, const unsigned char *iv, unsigned char **res )
419 gcry_cipher_hd_t gcr;
420 gcry_error_t st;
422 ssl_init();
424 *res = g_malloc( input_len );
425 st = gcry_cipher_open( &gcr, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0 ) ||
426 gcry_cipher_setkey( gcr, key, key_len ) ||
427 gcry_cipher_setiv( gcr, iv, 8 ) ||
428 gcry_cipher_encrypt( gcr, *res, input_len, input, input_len );
430 gcry_cipher_close( gcr );
432 if( st == 0 )
433 return input_len;
435 g_free( *res );
436 return 0;