diff options
-rw-r--r-- | src/idle-connection.c | 10 | ||||
-rw-r--r-- | 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 |