aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2006-09-24 13:57:45 +0200
committerWilmer van der Gaast <wilmer@gaast.net>2006-09-24 13:57:45 +0200
commit42127dcd26be4f6746004237eac5333ffbb94f8e (patch)
tree61b1334e1d963cbb6383a9f5e5452605e6710ac4
parent172a73f1a4b37fa20d1d50496a3faccb8fe6c769 (diff)
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).
-rw-r--r--lib/ssl_client.h4
-rw-r--r--lib/ssl_gnutls.c45
-rw-r--r--protocols/jabber/io.c107
-rw-r--r--protocols/jabber/jabber.c4
-rw-r--r--protocols/jabber/jabber.h1
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 );