From 42127dcd26be4f6746004237eac5333ffbb94f8e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 24 Sep 2006 13:57:45 +0200 Subject: Added support for SSL- and TLS-connections. Checking of the "tls" user setting has to be finished, plus an ssl_starttls() function for the other SSL libraries (this code will only compile with GnuTLS for now). --- lib/ssl_client.h | 4 ++++ lib/ssl_gnutls.c | 45 ++++++++++++++++++++++++++++++--------------- 2 files changed, 34 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/ssl_client.h b/lib/ssl_client.h index 964caee4..dcbf9a01 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -51,6 +51,10 @@ typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition); blocking I/O! (Except for the DNS lookups, for now...) */ G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ); +/* Start an SSL session on an existing fd. Useful for STARTTLS functionality, + for example in Jabber. */ +G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data ); + /* Obviously you need special read/write functions to read data. */ G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len ); G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len ); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index 3ebe1756..fc848bb1 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -62,22 +62,28 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data if( conn->fd < 0 ) { g_free( conn ); - return( NULL ); + return NULL; } - if( !initialized ) - { - gnutls_global_init(); - initialized = TRUE; - atexit( gnutls_global_deinit ); - } + return conn; +} + +/* FIXME: It can happen that the handshake fails even before ssl_connected() + returns already. This function will then return an invalid pointer because + these failures can't be detected properly yet. Maybe ssl_connected() + shouldn't be called directly, but via a short timeout? */ +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); - gnutls_certificate_allocate_credentials( &conn->xcred ); - gnutls_init( &conn->session, GNUTLS_CLIENT ); - gnutls_set_default_priority( conn->session ); - gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); + conn->fd = fd; + conn->func = func; + conn->data = data; + conn->inpa = -1; + + ssl_connected( conn, fd, GAIM_INPUT_WRITE ); - return( conn ); + return conn; } static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); @@ -90,14 +96,23 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con { conn->func( conn->data, NULL, cond ); - gnutls_deinit( conn->session ); - gnutls_certificate_free_credentials( conn->xcred ); - g_free( conn ); return FALSE; } + if( !initialized ) + { + gnutls_global_init(); + initialized = TRUE; + atexit( gnutls_global_deinit ); + } + + gnutls_certificate_allocate_credentials( &conn->xcred ); + gnutls_init( &conn->session, GNUTLS_CLIENT ); + gnutls_set_default_priority( conn->session ); + gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); + sock_make_nonblocking( conn->fd ); gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) conn->fd ); -- cgit v1.2.3 From c1ed6527576ac4c6ee1241f662e7db8e59327fd8 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 2 Oct 2006 15:19:13 +0200 Subject: No more double free()/crashes when trying to set up an SSL connection to a non-SSL server, and better handling of TLS connection setup by initializing the TLS session from a callback function (which guarantees a valid return value from ssl_starttls() before any error callback could be called). --- lib/ssl_gnutls.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index fc848bb1..7481127a 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -48,6 +48,8 @@ struct scd }; 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 ); void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) @@ -68,10 +70,6 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data return conn; } -/* FIXME: It can happen that the handshake fails even before ssl_connected() - returns already. This function will then return an invalid pointer because - these failures can't be detected properly yet. Maybe ssl_connected() - shouldn't be called directly, but via a short timeout? */ void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -81,12 +79,26 @@ void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) conn->data = data; conn->inpa = -1; - ssl_connected( conn, fd, GAIM_INPUT_WRITE ); + /* This function should be called via a (short) timeout instead of + directly from here, because these SSL calls are *supposed* to be + *completely* asynchronous and not ready yet when this function + (or *_connect, for examle) returns. Also, errors are reported via + the callback function, not via this function's return value. + + In short, doing things like this makes the rest of the code a lot + simpler. */ + + b_timeout_add( 1, ssl_starttls_real, conn ); return conn; } -static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + + return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); +} static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) { -- cgit v1.2.3 From 36e9f62a6e6fdb1217b3b819320ac5a94025c448 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 7 Oct 2006 19:46:28 +0200 Subject: Added SRV lookups to automatically find out the correct server for a domain. --- lib/misc.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/misc.h | 10 ++++++++++ 2 files changed, 67 insertions(+) (limited to 'lib') diff --git a/lib/misc.c b/lib/misc.c index 64666492..9061af39 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -39,6 +39,11 @@ #include #include +#ifdef HAVE_RESOLV_A +#include +#include +#endif + void strip_linefeed(gchar *text) { int i, j; @@ -487,3 +492,55 @@ int bool2int( char *value ) return 0; } + +struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ) +{ + struct ns_srv_reply *reply = NULL; +#ifdef HAVE_RESOLV_A + char name[1024]; + unsigned char querybuf[1024]; + const unsigned char *buf; + ns_msg nsh; + ns_rr rr; + int i, len, size; + + g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain ); + + if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 ) + return NULL; + + if( ns_initparse( querybuf, size, &nsh ) != 0 ) + return NULL; + + if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 ) + return NULL; + + size = ns_rr_rdlen( rr ); + buf = ns_rr_rdata( rr ); + + len = 0; + for( i = 6; i < size && buf[i]; i += buf[i] + 1 ) + len += buf[i] + 1; + + if( i > size ) + return NULL; + + reply = g_malloc( sizeof( struct ns_srv_reply ) + len ); + memcpy( reply->name, buf + 7, len ); + + for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 ) + reply->name[i] = '.'; + + if( i > len ) + { + g_free( reply ); + return NULL; + } + + reply->prio = ( buf[0] << 8 ) | buf[1]; + reply->weight = ( buf[2] << 8 ) | buf[3]; + reply->port = ( buf[4] << 8 ) | buf[5]; +#endif + + return reply; +} diff --git a/lib/misc.h b/lib/misc.h index b021e642..55dabfc4 100644 --- a/lib/misc.h +++ b/lib/misc.h @@ -29,6 +29,14 @@ #include #include +struct ns_srv_reply +{ + int prio; + int weight; + int port; + char name[]; +}; + G_MODULE_EXPORT void strip_linefeed( gchar *text ); G_MODULE_EXPORT char *add_cr( char *text ); G_MODULE_EXPORT char *strip_newlines(char *source); @@ -53,4 +61,6 @@ G_MODULE_EXPORT void random_bytes( unsigned char *buf, int count ); G_MODULE_EXPORT int is_bool( char *value ); G_MODULE_EXPORT int bool2int( char *value ); +G_MODULE_EXPORT struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ); + #endif -- cgit v1.2.3 From f920d9eb003541245e0fc32a381447cbba8fbea5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 19 Oct 2006 09:51:35 +0200 Subject: Added starttls code to ssl_openssl.c so GnuTLS isn't the only supported SSL module in this branch anymore. --- lib/ssl_gnutls.c | 2 - lib/ssl_openssl.c | 117 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 77 insertions(+), 42 deletions(-) (limited to 'lib') diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index 7481127a..fbd1d0c0 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -107,9 +107,7 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con if( source == -1 ) { conn->func( conn->data, NULL, cond ); - g_free( conn ); - return FALSE; } diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index b6f6c520..b1ba1db9 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -52,23 +52,66 @@ struct scd }; 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 ); void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); - SSL_METHOD *meth; conn->fd = proxy_connect( host, port, ssl_connected, conn ); conn->func = func; conn->data = data; + conn->inpa = -1; if( conn->fd < 0 ) { g_free( conn ); - return( NULL ); + return NULL; } + return conn; +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = fd; + conn->func = func; + conn->data = data; + conn->inpa = -1; + + /* This function should be called via a (short) timeout instead of + directly from here, because these SSL calls are *supposed* to be + *completely* asynchronous and not ready yet when this function + (or *_connect, for examle) returns. Also, errors are reported via + the callback function, not via this function's return value. + + In short, doing things like this makes the rest of the code a lot + simpler. */ + + b_timeout_add( 1, ssl_starttls_real, conn ); + + return conn; +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + + return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + SSL_METHOD *meth; + + if( source == -1 ) + goto ssl_connected_failure; + if( !initialized ) { initialized = TRUE; @@ -78,35 +121,35 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data meth = TLSv1_client_method(); conn->ssl_ctx = SSL_CTX_new( meth ); if( conn->ssl_ctx == NULL ) - { - conn->fd = -1; - return( NULL ); - } + goto ssl_connected_failure; conn->ssl = SSL_new( conn->ssl_ctx ); if( conn->ssl == NULL ) - { - conn->fd = -1; - return( NULL ); - } - - return( conn ); -} - -static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); - -static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) -{ - struct scd *conn = data; - - if( source == -1 ) - return ssl_handshake( data, -1, cond ); + goto ssl_connected_failure; /* We can do at least the handshake with non-blocking I/O */ sock_make_nonblocking( conn->fd ); SSL_set_fd( conn->ssl, conn->fd ); return ssl_handshake( data, source, cond ); + +ssl_connected_failure: + conn->func( conn->data, NULL, cond ); + + if( conn->ssl ) + { + SSL_shutdown( conn->ssl ); + SSL_free( conn->ssl ); + } + if( conn->ssl_ctx ) + { + SSL_CTX_free( conn->ssl_ctx ); + } + if( source >= 0 ) closesocket( source ); + g_free( conn ); + + return FALSE; + } static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ) @@ -118,7 +161,18 @@ static gboolean ssl_handshake( gpointer data, gint source, b_input_condition con { conn->lasterr = SSL_get_error( conn->ssl, st ); if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE ) - goto ssl_connected_failure; + { + conn->func( conn->data, NULL, cond ); + + SSL_shutdown( conn->ssl ); + SSL_free( conn->ssl ); + SSL_CTX_free( conn->ssl_ctx ); + + if( source >= 0 ) closesocket( source ); + g_free( conn ); + + return FALSE; + } conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data ); return FALSE; @@ -128,23 +182,6 @@ static gboolean ssl_handshake( gpointer data, gint source, b_input_condition con sock_make_blocking( conn->fd ); /* For now... */ conn->func( conn->data, conn, cond ); return FALSE; - -ssl_connected_failure: - conn->func( conn->data, NULL, cond ); - - if( conn->ssl ) - { - SSL_shutdown( conn->ssl ); - SSL_free( conn->ssl ); - } - if( conn->ssl_ctx ) - { - SSL_CTX_free( conn->ssl_ctx ); - } - if( source >= 0 ) closesocket( source ); - g_free( conn ); - - return FALSE; } int ssl_read( void *conn, char *buf, int len ) -- cgit v1.2.3