From adb98f54dfe035c8a58726bdfe706f07c0ae0169 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Sun, 28 Apr 2013 20:21:12 +0200 Subject: Add support for interactive TLS certificate checking With the pieces now in place, hook up the TLS channnel manager and pass it to the server connection so it can request interactive certificate checking. --- src/idle-connection.c | 10 +++++- src/idle-server-connection.c | 85 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/idle-connection.c b/src/idle-connection.c index 6a655b1..0e7e05c 100644 --- a/src/idle-connection.c +++ b/src/idle-connection.c @@ -48,6 +48,7 @@ #include "idle-roomlist-manager.h" #include "idle-parser.h" #include "idle-server-connection.h" +#include "server-tls-manager.h" #include "extensions/extensions.h" /* Renaming */ @@ -199,6 +200,9 @@ struct _IdleConnectionPrivate { /* so we can pop up a SASL channel asking for the password */ TpSimplePasswordManager *password_manager; + + /* TLS channel */ + IdleServerTLSManager *tls_manager; }; static void _iface_create_handle_repos(TpBaseConnection *self, TpHandleRepoIface **repos); @@ -533,6 +537,10 @@ static GPtrArray *_iface_create_channel_managers(TpBaseConnection *base) { manager = g_object_new(IDLE_TYPE_ROOMLIST_MANAGER, "connection", self, NULL); g_ptr_array_add(managers, manager); + priv->tls_manager = g_object_new (IDLE_TYPE_SERVER_TLS_MANAGER, + "connection", self, NULL); + g_ptr_array_add(managers, priv->tls_manager); + return managers; } @@ -742,7 +750,7 @@ static void _start_connecting_continue(IdleConnection *conn) { priv->username = g_strdup(g_get_user_name()); } - sconn = g_object_new(IDLE_TYPE_SERVER_CONNECTION, "host", priv->server, "port", priv->port, NULL); + sconn = g_object_new(IDLE_TYPE_SERVER_CONNECTION, "host", priv->server, "port", priv->port, "tls-manager", priv->tls_manager, NULL); if (priv->use_ssl) idle_server_connection_set_tls(sconn, TRUE); diff --git a/src/idle-server-connection.c b/src/idle-server-connection.c index 1605b61..d6b8250 100644 --- a/src/idle-server-connection.c +++ b/src/idle-server-connection.c @@ -31,6 +31,7 @@ #define IDLE_DEBUG_FLAG IDLE_DEBUG_NETWORK #include "idle-connection.h" +#include "server-tls-manager.h" #include "idle-debug.h" typedef struct _IdleServerConnectionPrivate IdleServerConnectionPrivate; @@ -47,7 +48,8 @@ enum { enum { PROP_HOST = 1, - PROP_PORT + PROP_PORT, + PROP_TLS_MANAGER }; struct _IdleServerConnectionPrivate { @@ -66,6 +68,8 @@ struct _IdleServerConnectionPrivate { GCancellable *cancellable; IdleServerConnectionState state; + IdleServerTLSManager *tls_manager; + GAsyncQueue *certificate_queue; }; static GObject *idle_server_connection_constructor(GType type, guint n_props, GObjectConstructParam *props); @@ -81,6 +85,7 @@ static void idle_server_connection_init(IdleServerConnection *conn) { priv->socket_client = g_socket_client_new(); priv->state = SERVER_CONNECTION_STATE_NOT_CONNECTED; + priv->certificate_queue = g_async_queue_new (); } static GObject *idle_server_connection_constructor(GType type, guint n_props, GObjectConstructParam *props) { @@ -105,6 +110,7 @@ static void idle_server_connection_finalize(GObject *obj) { IdleServerConnection *conn = IDLE_SERVER_CONNECTION(obj); IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn); + g_async_queue_unref (priv->certificate_queue); g_free(priv->host); } @@ -121,6 +127,10 @@ static void idle_server_connection_get_property(GObject *obj, guint prop_id, GV g_value_set_uint(value, priv->port); break; + case PROP_TLS_MANAGER: + g_value_set_object(value, priv->tls_manager); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); break; @@ -144,6 +154,10 @@ static void idle_server_connection_set_property(GObject *obj, priv->port = (guint16) g_value_get_uint(value); break; + case PROP_TLS_MANAGER: + priv->tls_manager = g_value_dup_object(value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); break; @@ -183,6 +197,16 @@ static void idle_server_connection_class_init(IdleServerConnectionClass *klass) g_object_class_install_property(object_class, PROP_PORT, pspec); + pspec = g_param_spec_object("tls-manager", "TLS Manager", + "TLS manager for interactive certificate checking", + IDLE_TYPE_SERVER_TLS_MANAGER, + G_PARAM_READABLE| + G_PARAM_WRITABLE| + G_PARAM_STATIC_NICK| + G_PARAM_STATIC_BLURB); + + g_object_class_install_property(object_class, PROP_TLS_MANAGER, pspec); + signals[STATUS_CHANGED] = g_signal_new("status-changed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, @@ -293,14 +317,73 @@ cleanup: g_object_unref(result); } + +#define CERT_ACCEPTED 1 +#define CERT_REJECTED 2 +struct CertData { + IdleServerConnection *self; + GTlsCertificate *certificate; +}; + +static void _certificate_verified (GObject *source, GAsyncResult *res, gpointer user_data) +{ + IdleServerConnection *self = user_data; + IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(self); + gboolean ret; + + ret = idle_server_tls_manager_verify_finish (IDLE_SERVER_TLS_MANAGER (source), res, NULL); + g_async_queue_push (priv->certificate_queue, GINT_TO_POINTER (ret ? CERT_ACCEPTED : CERT_REJECTED)); +} + +static gboolean +_check_certificate_interactively (gpointer data) { + struct CertData *d = data; + IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(d->self); + + idle_server_tls_manager_verify_async (priv->tls_manager, d->certificate, priv->host, _certificate_verified, d->self); + return FALSE; +} + +static gboolean _accept_certificate_request (GTlsConnection *tls_connection, GTlsCertificate *peer_cert, GTlsCertificateFlags errors, IdleServerConnection *conn) +{ + IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn); + struct CertData d; + gpointer result; + + /* Requested to check the extra errors from an ssl certificate, + * Need to bounce this back into the main thread so we can ask the UI + * on dbus */ + IDLE_DEBUG ("Requested to validate certificate"); + + d.self = conn; + d.certificate = peer_cert; + + g_idle_add (_check_certificate_interactively, &d); + result = g_async_queue_pop (priv->certificate_queue); + + return GPOINTER_TO_INT (result) == CERT_ACCEPTED; +} + +static void _connect_event_cb (GSocketClient *client, GSocketClientEvent event, GSocketConnectable *connectable, GIOStream *connection, gpointer user_data) +{ + if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING) + return; + + g_signal_connect (connection, "accept-certificate", G_CALLBACK (_accept_certificate_request), user_data); +} + static void _connect_in_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { IdleServerConnection *conn = source_object; IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn); GError *error = NULL; GSocketConnection *socket_connection; + gulong event_id; + event_id = g_signal_connect (priv->socket_client, "event", + G_CALLBACK (_connect_event_cb), conn); socket_connection = g_socket_client_connect_to_host (priv->socket_client, priv->host, priv->port, cancellable, &error); + g_signal_handler_disconnect (priv->socket_client, event_id); if (socket_connection != NULL) g_task_return_pointer (task, socket_connection, NULL); else -- cgit v1.2.3