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 ); |