aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl_gnutls.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl_gnutls.c')
-rw-r--r--lib/ssl_gnutls.c81
1 files changed, 67 insertions, 14 deletions
diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c
index 93601ba6..b698e630 100644
--- a/lib/ssl_gnutls.c
+++ b/lib/ssl_gnutls.c
@@ -1,7 +1,7 @@
/********************************************************************\
* BitlBee -- An IRC to other IM-networks gateway *
* *
- * Copyright 2002-2011 Wilmer van der Gaast and others *
+ * Copyright 2002-2012 Wilmer van der Gaast and others *
\********************************************************************/
/* SSL module - GnuTLS version */
@@ -37,7 +37,7 @@
int ssl_errno = 0;
static gboolean initialized = FALSE;
-gnutls_certificate_credentials xcred;
+gnutls_certificate_credentials_t xcred;
#include <limits.h>
@@ -59,9 +59,11 @@ struct scd
char *hostname;
gboolean verify;
- gnutls_session session;
+ gnutls_session_t session;
};
+static GHashTable *session_cache;
+
static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
@@ -84,8 +86,10 @@ void ssl_init( void )
{
gnutls_certificate_set_x509_trust_file( xcred, global.conf->cafile, GNUTLS_X509_FMT_PEM );
- /* Not needed in GnuTLS 2.11+ but we support older versions for now. */
- gnutls_certificate_set_verify_flags( xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT );
+ /* Not needed in GnuTLS 2.11+ (enabled by default there) so
+ don't do it (resets possible other defaults). */
+ if( !gnutls_check_version( "2.11" ) )
+ gnutls_certificate_set_verify_flags( xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT );
}
initialized = TRUE;
@@ -94,6 +98,8 @@ void ssl_init( void )
gnutls_global_set_log_level( 3 );
*/
+ session_cache = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free );
+
atexit( ssl_deinit );
}
@@ -101,18 +107,20 @@ static void ssl_deinit( void )
{
gnutls_global_deinit();
gnutls_certificate_free_credentials( xcred );
+ g_hash_table_destroy( session_cache );
+ session_cache = NULL;
}
void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data )
{
struct scd *conn = g_new0( struct scd, 1 );
- conn->fd = proxy_connect( host, port, ssl_connected, conn );
conn->func = func;
conn->data = data;
conn->inpa = -1;
conn->hostname = g_strdup( host );
conn->verify = verify && global.conf->cafile;
+ conn->fd = proxy_connect( host, port, ssl_connected, conn );
if( conn->fd < 0 )
{
@@ -131,7 +139,7 @@ void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function
conn->func = func;
conn->data = data;
conn->inpa = -1;
- conn->hostname = hostname;
+ conn->hostname = g_strdup( hostname );
/* For now, SSL verification is globally enabled by setting the cafile
setting in bitlbee.conf. Commented out by default because probably
@@ -168,9 +176,9 @@ static int verify_certificate_callback( gnutls_session_t session )
int gnutlsret;
int verifyret = 0;
gnutls_x509_crt_t cert;
- const char *hostname;
+ struct scd *conn;
- hostname = gnutls_session_get_ptr( session );
+ conn = gnutls_session_get_ptr( session );
gnutlsret = gnutls_certificate_verify_peers2( session, &status );
if( gnutlsret < 0 )
@@ -208,7 +216,7 @@ static int verify_certificate_callback( gnutls_session_t session )
if( cert_list == NULL || gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER ) < 0 )
return VERIFY_CERT_ERROR;
- if( !gnutls_x509_crt_check_hostname( cert, hostname ) )
+ if( !gnutls_x509_crt_check_hostname( cert, conn->hostname ) )
{
verifyret |= VERIFY_CERT_INVALID;
verifyret |= VERIFY_CERT_WRONG_HOSTNAME;
@@ -219,6 +227,45 @@ static int verify_certificate_callback( gnutls_session_t session )
return verifyret;
}
+struct ssl_session
+{
+ size_t size;
+ char data[];
+};
+
+static void ssl_cache_add( struct scd *conn )
+{
+ size_t data_size;
+ struct ssl_session *data;
+ char *hostname;
+
+ if( !conn->hostname ||
+ gnutls_session_get_data( conn->session, NULL, &data_size ) != 0 )
+ return;
+
+ data = g_malloc( sizeof( struct ssl_session ) + data_size );
+ if( gnutls_session_get_data( conn->session, data->data, &data->size ) != 0 )
+ {
+ g_free( data );
+ return;
+ }
+
+ hostname = g_strdup( conn->hostname );
+ g_hash_table_insert( session_cache, hostname, data );
+}
+
+static void ssl_cache_resume( struct scd *conn )
+{
+ struct ssl_session *data;
+
+ if( conn->hostname &&
+ ( data = g_hash_table_lookup( session_cache, conn->hostname ) ) )
+ {
+ gnutls_session_set_data( conn->session, data->data, data->size );
+ g_hash_table_remove( session_cache, conn->hostname );
+ }
+}
+
char *ssl_verify_strerror( int code )
{
GString *ret = g_string_new( "" );
@@ -264,16 +311,20 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con
ssl_init();
gnutls_init( &conn->session, GNUTLS_CLIENT );
- if( conn->verify )
- gnutls_session_set_ptr( conn->session, (void *) conn->hostname );
+ gnutls_session_set_ptr( conn->session, (void *) conn );
#if GNUTLS_VERSION_NUMBER < 0x020c00
gnutls_transport_set_lowat( conn->session, 0 );
#endif
gnutls_set_default_priority( conn->session );
gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, xcred );
+ if( conn->hostname && !isdigit( conn->hostname[0] ) )
+ gnutls_server_name_set( conn->session, GNUTLS_NAME_DNS,
+ conn->hostname, strlen( conn->hostname ) );
sock_make_nonblocking( conn->fd );
- gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd );
+ gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr_t) GNUTLS_STUPID_CAST conn->fd );
+
+ ssl_cache_resume( conn );
return ssl_handshake( data, source, cond );
}
@@ -315,7 +366,8 @@ static gboolean ssl_handshake( gpointer data, gint source, b_input_condition con
{
/* For now we can't handle non-blocking perfectly everywhere... */
sock_make_blocking( conn->fd );
-
+
+ ssl_cache_add( conn );
conn->established = TRUE;
conn->func( conn->data, 0, conn, cond );
}
@@ -399,6 +451,7 @@ void ssl_disconnect( void *conn_ )
if( conn->session )
gnutls_deinit( conn->session );
+ g_free( conn->hostname );
g_free( conn );
}