summaryrefslogtreecommitdiff
path: root/src/idle-server-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/idle-server-connection.c')
-rw-r--r--src/idle-server-connection.c145
1 files changed, 122 insertions, 23 deletions
diff --git a/src/idle-server-connection.c b/src/idle-server-connection.c
index 6c5894f..b593fa3 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;
@@ -40,16 +41,23 @@ typedef struct _IdleServerConnectionPrivate IdleServerConnectionPrivate;
G_DEFINE_TYPE(IdleServerConnection, idle_server_connection, G_TYPE_OBJECT)
enum {
- STATUS_CHANGED,
+ DISCONNECTED,
RECEIVED,
LAST_SIGNAL
};
enum {
PROP_HOST = 1,
- PROP_PORT
+ PROP_PORT,
+ PROP_TLS_MANAGER
};
+typedef enum {
+ SERVER_CONNECTION_STATE_NOT_CONNECTED,
+ SERVER_CONNECTION_STATE_CONNECTING,
+ SERVER_CONNECTION_STATE_CONNECTED
+} IdleServerConnectionState;
+
struct _IdleServerConnectionPrivate {
gchar *host;
guint16 port;
@@ -66,6 +74,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 +91,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 +116,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 +133,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 +160,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,13 +203,21 @@ static void idle_server_connection_class_init(IdleServerConnectionClass *klass)
g_object_class_install_property(object_class, PROP_PORT, pspec);
- signals[STATUS_CHANGED] = g_signal_new("status-changed",
+ pspec = g_param_spec_object("tls-manager", "TLS Manager",
+ "TLS manager for interactive certificate checking",
+ IDLE_TYPE_SERVER_TLS_MANAGER,
+ G_PARAM_READWRITE|
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_property(object_class, PROP_TLS_MANAGER, pspec);
+
+ signals[DISCONNECTED] = g_signal_new("disconnected",
G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
g_cclosure_marshal_generic,
- G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+ G_TYPE_NONE, 1, G_TYPE_UINT);
signals[RECEIVED] = g_signal_new("received",
G_OBJECT_CLASS_TYPE(klass),
@@ -207,10 +235,11 @@ static void change_state(IdleServerConnection *conn, IdleServerConnectionState s
if (state == priv->state)
return;
- IDLE_DEBUG("emitting status-changed, state %u, reason %u", state, reason);
-
+ IDLE_DEBUG("moving to state %u, reason %u", state, reason);
priv->state = state;
- g_signal_emit(conn, signals[STATUS_CHANGED], 0, state, reason);
+
+ if (state == SERVER_CONNECTION_STATE_NOT_CONNECTED)
+ g_signal_emit(conn, signals[DISCONNECTED], 0, reason);
}
static void _input_stream_read(IdleServerConnection *conn, GInputStream *input_stream, GAsyncReadyCallback callback) {
@@ -254,7 +283,7 @@ cleanup:
}
static void _connect_to_host_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) {
- GSocketClient *socket_client = G_SOCKET_CLIENT(source_object);
+ GSimpleAsyncResult *task = G_SIMPLE_ASYNC_RESULT (res);
GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(user_data);
IdleServerConnection *conn = IDLE_SERVER_CONNECTION(g_async_result_get_source_object(G_ASYNC_RESULT(result)));
IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
@@ -265,8 +294,7 @@ static void _connect_to_host_ready(GObject *source_object, GAsyncResult *res, gp
gint socket_fd;
GError *error = NULL;
- socket_connection = g_socket_client_connect_to_host_finish(socket_client, res, &error);
- if (socket_connection == NULL) {
+ if (g_simple_async_result_propagate_error (task, &error)) {
IDLE_DEBUG("g_socket_client_connect_to_host failed: %s", error->message);
g_simple_async_result_set_error(result, TP_ERROR, TP_ERROR_NETWORK_ERROR, "%s", error->message);
g_error_free(error);
@@ -275,6 +303,7 @@ static void _connect_to_host_ready(GObject *source_object, GAsyncResult *res, gp
goto cleanup;
}
+ socket_connection = g_object_ref (g_simple_async_result_get_op_res_gpointer (task));
socket_ = g_socket_connection_get_socket(socket_connection);
g_socket_set_keepalive(socket_, TRUE);
@@ -292,11 +321,87 @@ static void _connect_to_host_ready(GObject *source_object, GAsyncResult *res, gp
cleanup:
g_simple_async_result_complete(result);
g_object_unref(result);
+ g_object_unref(task);
+}
+
+
+#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 (GSimpleAsyncResult *task, GObject *source_object, GCancellable *cancellable)
+{
+ IdleServerConnection *conn = IDLE_SERVER_CONNECTION (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_simple_async_result_set_op_res_gpointer (task, socket_connection, g_object_unref);
+ else
+ g_simple_async_result_take_error (task, error);
}
void idle_server_connection_connect_async(IdleServerConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) {
IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
GSimpleAsyncResult *result;
+ GSimpleAsyncResult *task;
if (priv->state != SERVER_CONNECTION_STATE_NOT_CONNECTED) {
IDLE_DEBUG("already connecting or connected!");
@@ -326,7 +431,10 @@ void idle_server_connection_connect_async(IdleServerConnection *conn, GCancellab
}
result = g_simple_async_result_new(G_OBJECT(conn), callback, user_data, idle_server_connection_connect_async);
- g_socket_client_connect_to_host_async(priv->socket_client, priv->host, priv->port, cancellable, _connect_to_host_ready, result);
+
+ task = g_simple_async_result_new (G_OBJECT (conn), _connect_to_host_ready, result, NULL);
+ g_simple_async_result_run_in_thread (task, _connect_in_thread, G_PRIORITY_DEFAULT, cancellable);
+
change_state(conn, SERVER_CONNECTION_STATE_CONNECTING, SERVER_CONNECTION_STATE_REASON_REQUESTED);
}
@@ -497,22 +605,13 @@ gboolean idle_server_connection_send_finish(IdleServerConnection *conn, GAsyncRe
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT(result), error);
}
-IdleServerConnectionState idle_server_connection_get_state(IdleServerConnection *conn) {
+gboolean idle_server_connection_is_connected(IdleServerConnection *conn) {
IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
- return priv->state;
+
+ return priv->state == SERVER_CONNECTION_STATE_CONNECTED;
}
void idle_server_connection_set_tls(IdleServerConnection *conn, gboolean tls) {
IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
g_socket_client_set_tls(priv->socket_client, tls);
-
- /* The regression tests don't have a CA-issued certificate,
- * oddly enough. */
- if (!tp_strdiff (g_getenv ("IDLE_TEST_BE_VULNERABLE_TO_MAN_IN_THE_MIDDLE_ATTACKS"), "vulnerable")) {
- g_socket_client_set_tls_validation_flags(priv->socket_client,
- G_TLS_CERTIFICATE_VALIDATE_ALL
- & ~G_TLS_CERTIFICATE_UNKNOWN_CA
- & ~G_TLS_CERTIFICATE_BAD_IDENTITY
- & ~G_TLS_CERTIFICATE_EXPIRED);
- }
}