diff options
| -rw-r--r-- | lib/ssl_client.h | 4 | ||||
| -rw-r--r-- | lib/ssl_gnutls.c | 45 | ||||
| -rw-r--r-- | protocols/jabber/io.c | 107 | ||||
| -rw-r--r-- | protocols/jabber/jabber.c | 4 | ||||
| -rw-r--r-- | protocols/jabber/jabber.h | 1 | 
5 files changed, 135 insertions, 26 deletions
| 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 ); diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index 333e3123..f1f62a48 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -22,6 +22,7 @@  \***************************************************************************/  #include "jabber.h" +#include "ssl_client.h"  static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond ); @@ -75,7 +76,12 @@ static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition  	if( jd->fd == -1 )  		return FALSE; -	st = write( jd->fd, jd->txq, jd->tx_len ); +	if( jd->ssl ) +		st = ssl_write( jd->ssl, jd->txq, jd->tx_len ); +	else +		st = write( jd->fd, jd->txq, jd->tx_len ); +	 +//	if( st > 0 ) write( 1, jd->txq, st );  	if( st == jd->tx_len )  	{ @@ -125,7 +131,12 @@ static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition  	if( jd->fd == -1 )  		return FALSE; -	st = read( fd, buf, sizeof( buf ) ); +	if( jd->ssl ) +		st = ssl_read( jd->ssl, buf, sizeof( buf ) ); +	else +		st = read( jd->fd, buf, sizeof( buf ) ); +	 +//	if( st > 0 ) write( 1, buf, st );  	if( st > 0 )  	{ @@ -209,6 +220,22 @@ gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition c  	return jabber_start_stream( gc );  } +gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond ) +{ +	struct gaim_connection *gc = data; +	 +	if( source == NULL ) +	{ +		hide_login_progress( gc, "Could not connect to server" ); +		signoff( gc ); +		return FALSE; +	} +	 +	set_login_progress( gc, 1, "Connected to server, logging in" ); +	 +	return jabber_start_stream( gc ); +} +  static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )  {  	return XT_ABORT; @@ -221,15 +248,43 @@ static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )  	struct xt_node *c, *reply;  	c = xt_find_node( node->children, "starttls" ); -	if( c ) +	if( c && !jd->ssl )  	{ -		/* -		jd->flags |= JFLAG_SUPPORTS_TLS; -		if( xt_find_node( c->children, "required" ) ) -			jd->flags |= JFLAG_REQUIRES_TLS; -		*/ +		/* If the server advertises the STARTTLS feature and if we're +		   not in a secure connection already: */ +		 +		int try; +		 +		try = g_strcasecmp( set_getstr( &gc->acc->set, "tls" ), "try" ) == 0; +		c = xt_find_node( c->children, "required" ); +		 +		/* Only run this if the tls setting is set to true or try: */ +		if( ( try | set_getbool( &gc->acc->set, "tls" ) ) ) +		{ +			reply = xt_new_node( "starttls", NULL, NULL ); +			xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-tls" ); +			if( !jabber_write_packet( gc, reply ) ) +			{ +				xt_free_node( reply ); +				return XT_ABORT; +			} +			xt_free_node( reply ); +			 +			return XT_HANDLED; +		} +	} +	else +	{ +		/* TODO: Abort if TLS is required by the user. */  	} +	/* This one used to be in jabber_handlers[], but it has to be done +	   from here to make sure the TLS session will be initialized +	   properly before we attempt SASL authentication. */ +	if( ( c = xt_find_node( node->children, "mechanisms" ) ) ) +		if( sasl_pkt_mechanisms( c, data ) == XT_ABORT ) +			return XT_ABORT; +	  	if( ( c = xt_find_node( node->children, "bind" ) ) )  	{  		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) ); @@ -268,6 +323,40 @@ static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )  	return XT_HANDLED;  } +static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data ) +{ +	struct gaim_connection *gc = data; +	struct jabber_data *jd = gc->proto_data; +	char *xmlns; +	 +	xmlns = xt_find_attr( node, "xmlns" ); +	 +	/* Just ignore it when it doesn't seem to be TLS-related (is that at +	   all possible??). */ +	if( !xmlns || strcmp( xmlns, "urn:ietf:params:xml:ns:xmpp-tls" ) != 0 ) +		return XT_HANDLED; +	 +	/* We don't want event handlers to touch our TLS session while it's +	   still initializing! */ +	b_event_remove( jd->r_inpa ); +	if( jd->tx_len > 0 ) +	{ +		/* Actually the write queue should be empty here, but just +		   to be sure... */ +		b_event_remove( jd->w_inpa ); +		g_free( jd->txq ); +		jd->txq = NULL; +		jd->tx_len = 0; +	} +	jd->w_inpa = jd->r_inpa = 0; +	 +	set_login_progress( gc, 1, "Converting stream to TLS" ); +	 +	jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, gc ); +	 +	return XT_HANDLED; +} +  static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )  {  	printf( "Received unknown packet:\n" ); @@ -282,7 +371,7 @@ static const struct xt_handler_entry jabber_handlers[] = {  	{ "presence",           "stream:stream",        jabber_pkt_presence },  	{ "iq",                 "stream:stream",        jabber_pkt_iq },  	{ "stream:features",    "stream:stream",        jabber_pkt_features }, -	{ "mechanisms",         "stream:features",      sasl_pkt_mechanisms }, +	{ "proceed",            "stream:stream",        jabber_pkt_proceed_tls },  	{ "challenge",          "stream:stream",        sasl_pkt_challenge },  	{ "success",            "stream:stream",        sasl_pkt_result },  	{ "failure",            "stream:stream",        sasl_pkt_result }, diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index ee26a007..a337dd68 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -79,8 +79,8 @@ static void jabber_login( account_t *acc )  	if( set_getbool( &acc->set, "ssl" ) )  	{ -		signoff( gc ); -		/* TODO! */ +		jd->ssl = ssl_connect( jd->server, set_getint( &acc->set, "port" ), jabber_connected_ssl, gc ); +		jd->fd = ssl_getfd( jd->ssl );  	}  	else  	{ diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 4a8292ea..cf84a6e8 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -94,6 +94,7 @@ extern const struct jabber_away_state jabber_away_state_list[];  int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node );  int jabber_write( struct gaim_connection *gc, char *buf, int len );  gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond ); +gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond );  gboolean jabber_start_stream( struct gaim_connection *gc );  void jabber_end_stream( struct gaim_connection *gc ); | 
