diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2013-05-01 15:46:49 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2013-05-01 15:46:49 +0100 |
commit | 7d43b91b99535b309298592e945ecc28c567e53e (patch) | |
tree | e20e4414abaeaec5bd566f0c634258aab4ea6feb | |
parent | 1ab28c1c339644304333203196c4d95a92a4e08c (diff) | |
parent | 92da2de2a91348121d85dad99d85e3657a9bf719 (diff) |
Merge branch 'interactive-tls'
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/idle-connection.c | 98 | ||||
-rw-r--r-- | src/idle-debug.c | 1 | ||||
-rw-r--r-- | src/idle-debug.h | 1 | ||||
-rw-r--r-- | src/idle-server-connection.c | 145 | ||||
-rw-r--r-- | src/idle-server-connection.h | 8 | ||||
-rw-r--r-- | src/server-tls-channel.c | 306 | ||||
-rw-r--r-- | src/server-tls-channel.h | 71 | ||||
-rw-r--r-- | src/server-tls-manager.c | 462 | ||||
-rw-r--r-- | src/server-tls-manager.h | 83 | ||||
-rw-r--r-- | src/tls-certificate.c | 344 | ||||
-rw-r--r-- | src/tls-certificate.h | 66 | ||||
-rw-r--r-- | tests/twisted/Makefile.am | 5 | ||||
-rw-r--r-- | tests/twisted/connect/connect-close-ssl.py | 28 | ||||
-rw-r--r-- | tests/twisted/connect/connect-fail-ssl.py | 1 | ||||
-rw-r--r-- | tests/twisted/connect/connect-reject-ssl.py | 29 | ||||
-rw-r--r-- | tests/twisted/connect/connect-success-ssl.py | 8 | ||||
-rw-r--r-- | tests/twisted/connect/disconnect-before-socket-connected.py | 31 | ||||
-rw-r--r-- | tests/twisted/connect/disconnect-during-cert-verification.py | 28 | ||||
-rw-r--r-- | tests/twisted/tools/tp-glib.supp | 390 |
20 files changed, 2036 insertions, 75 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 7326f8e..7ed3126 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,6 +38,12 @@ libidle_convenience_la_SOURCES = \ idle-server-connection.h \ idle-text.h \ idle-text.c \ + server-tls-channel.c \ + server-tls-channel.h \ + server-tls-manager.c \ + server-tls-manager.h \ + tls-certificate.c \ + tls-certificate.h \ $(NULL) nodist_libidle_convenience_la_SOURCES = \ diff --git a/src/idle-connection.c b/src/idle-connection.c index 6a655b1..24b93c2 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 */ @@ -145,7 +146,14 @@ struct _IdleConnectionPrivate { * network connection */ IdleServerConnection *conn; - guint sconn_status; + /* + * TRUE if 'conn' is connected to the server. (This just represents the TCP + * stream; not whether we are authenticated.) + */ + gboolean sconn_connected; + + /* Used for idle_server_connection_connect_async(). */ + GCancellable *connect_cancellable; /* When we sent a PING to the server which it hasn't PONGed for yet, or 0 if * there isn't a PING outstanding. @@ -199,6 +207,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); @@ -219,7 +230,7 @@ static IdleParserHandlerResult _version_privmsg_handler(IdleParser *parser, Idle static IdleParserHandlerResult _welcome_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); static IdleParserHandlerResult _whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); -static void sconn_status_changed_cb(IdleServerConnection *sconn, IdleServerConnectionState state, IdleServerConnectionStateReason reason, IdleConnection *conn); +static void sconn_disconnected_cb(IdleServerConnection *sconn, IdleServerConnectionStateReason reason, IdleConnection *conn); static void sconn_received_cb(IdleServerConnection *sconn, gchar *raw_msg, IdleConnection *conn); static void irc_handshakes(IdleConnection *conn); @@ -242,7 +253,7 @@ static void idle_connection_init(IdleConnection *obj) { IdleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, IDLE_TYPE_CONNECTION, IdleConnectionPrivate); obj->priv = priv; - priv->sconn_status = SERVER_CONNECTION_STATE_NOT_CONNECTED; + priv->sconn_connected = FALSE; priv->msg_queue = g_queue_new(); tp_contacts_mixin_init ((GObject *) obj, G_STRUCT_OFFSET (IdleConnection, contacts)); @@ -400,6 +411,8 @@ static void idle_connection_dispose (GObject *object) { priv->conn = NULL; } + g_clear_object (&priv->connect_cancellable); + if (priv->queued_aliases_owners) tp_handle_set_destroy(priv->queued_aliases_owners); @@ -492,7 +505,7 @@ static void idle_connection_class_init(IdleConnectionClass *klass) { param_spec = g_param_spec_string("username", "User name", "The username of the user connecting to IRC", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property(object_class, PROP_USERNAME, param_spec); - param_spec = g_param_spec_string("charset", "Character set", "The character set to use to communicate with the outside world", "NULL", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); + param_spec = g_param_spec_string("charset", "Character set", "The character set to use to communicate with the outside world", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property(object_class, PROP_CHARSET, param_spec); param_spec = g_param_spec_uint("keepalive-interval", "Keepalive interval", "Seconds between keepalive packets, or 0 to disable", 0, G_MAXUINT, DEFAULT_KEEPALIVE_INTERVAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); @@ -533,6 +546,11 @@ 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; } @@ -581,7 +599,7 @@ static void _iface_disconnected(TpBaseConnection *self) { /* we never got around to actually creating the connection * iface object because we were still trying to connect, so * don't try to send any traffic down it */ - if (priv->conn == NULL) { + if (!priv->sconn_connected) { return; } @@ -607,6 +625,9 @@ static void _iface_shut_down(TpBaseConnection *base) { * don't try to send any traffic down it */ if (priv->conn == NULL) { g_idle_add(_finish_shutdown_idle_func, self); + } else if (!priv->sconn_connected) { + IDLE_DEBUG("cancelling connection"); + g_cancellable_cancel (priv->connect_cancellable); } else { idle_server_connection_disconnect_async(priv->conn, NULL, NULL, NULL); } @@ -698,11 +719,10 @@ static void _connection_connect_ready(GObject *source_object, GAsyncResult *res, IDLE_DEBUG("idle_server_connection_connect failed: %s", error->message); _connection_disconnect_with_gerror(conn, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR, "debug-message", error); g_error_free(error); - g_object_unref(sconn); return; } - priv->conn = sconn; + priv->sconn_connected = TRUE; g_signal_connect(sconn, "received", (GCallback)(sconn_received_cb), conn); @@ -742,30 +762,34 @@ 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); - g_signal_connect(sconn, "status-changed", (GCallback)(sconn_status_changed_cb), conn); + g_signal_connect(sconn, "disconnected", (GCallback)(sconn_disconnected_cb), conn); - idle_server_connection_connect_async(sconn, NULL, _connection_connect_ready, conn); + priv->conn = sconn; + g_warn_if_fail (priv->connect_cancellable == NULL); + priv->connect_cancellable = g_cancellable_new (); + idle_server_connection_connect_async(sconn, priv->connect_cancellable, _connection_connect_ready, conn); } static gboolean keepalive_timeout_cb(gpointer user_data); -static void sconn_status_changed_cb(IdleServerConnection *sconn, IdleServerConnectionState state, IdleServerConnectionStateReason reason, IdleConnection *conn) { +static void sconn_disconnected_cb(IdleServerConnection *sconn, IdleServerConnectionStateReason reason, IdleConnection *conn) { IdleConnectionPrivate *priv = conn->priv; TpConnectionStatusReason tp_reason; /* cancel scheduled forced disconnect since we are now disconnected */ - if (state == SERVER_CONNECTION_STATE_NOT_CONNECTED && - priv->force_disconnect_id) { + if (priv->force_disconnect_id) { g_source_remove(priv->force_disconnect_id); priv->force_disconnect_id = 0; } - IDLE_DEBUG("called with state %u", state); - switch (reason) { case SERVER_CONNECTION_STATE_REASON_ERROR: tp_reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR; @@ -783,30 +807,8 @@ static void sconn_status_changed_cb(IdleServerConnection *sconn, IdleServerConne if (priv->quitting) tp_reason = TP_CONNECTION_STATUS_REASON_REQUESTED; - switch (state) { - case SERVER_CONNECTION_STATE_NOT_CONNECTED: - connection_disconnect_cb(conn, tp_reason); - break; - - case SERVER_CONNECTION_STATE_CONNECTING: - break; - - case SERVER_CONNECTION_STATE_CONNECTED: - if (priv->keepalive_interval != 0 && priv->keepalive_timeout == 0) - priv->keepalive_timeout = g_timeout_add_seconds(priv->keepalive_interval, keepalive_timeout_cb, conn); - - if (g_queue_get_length(priv->msg_queue) > 0) { - IDLE_DEBUG("we had messages in queue, start unloading them now"); - idle_connection_add_queue_timeout (conn); - } - break; - - default: - g_assert_not_reached(); - break; - } - - priv->sconn_status = state; + priv->sconn_connected = FALSE; + connection_disconnect_cb(conn, tp_reason); } static void sconn_received_cb(IdleServerConnection *sconn, gchar *raw_msg, IdleConnection *conn) { @@ -822,7 +824,7 @@ static gboolean keepalive_timeout_cb(gpointer user_data) { gchar cmd[IRC_MSG_MAXLEN + 1]; gint64 now; - if (priv->sconn_status != SERVER_CONNECTION_STATE_CONNECTED || + if (!priv->sconn_connected || priv->quitting) { priv->keepalive_timeout = 0; return FALSE; @@ -882,7 +884,7 @@ static gboolean msg_queue_timeout_cb(gpointer user_data) { IDLE_DEBUG("called"); - if (priv->sconn_status != SERVER_CONNECTION_STATE_CONNECTED) { + if (!priv->sconn_connected) { IDLE_DEBUG("connection was not connected!"); priv->msg_queue_timeout = 0; @@ -1219,11 +1221,21 @@ static void send_quit_request(IdleConnection *conn) { static void connection_connect_cb(IdleConnection *conn, gboolean success, TpConnectionStatusReason fail_reason) { TpBaseConnection *base = TP_BASE_CONNECTION(conn); + IdleConnectionPrivate *priv = conn->priv; - if (success) + if (success) { tp_base_connection_change_status(base, TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); - else + + if (priv->keepalive_interval != 0 && priv->keepalive_timeout == 0) + priv->keepalive_timeout = g_timeout_add_seconds(priv->keepalive_interval, keepalive_timeout_cb, conn); + + if (g_queue_get_length(priv->msg_queue) > 0) { + IDLE_DEBUG("we had messages in queue, start unloading them now"); + idle_connection_add_queue_timeout (conn); + } + } else { tp_base_connection_change_status(base, TP_CONNECTION_STATUS_DISCONNECTED, fail_reason); + } } static void connection_disconnect_cb(IdleConnection *conn, TpConnectionStatusReason reason) { diff --git a/src/idle-debug.c b/src/idle-debug.c index e8a2854..9b7ffcb 100644 --- a/src/idle-debug.c +++ b/src/idle-debug.c @@ -34,6 +34,7 @@ static GDebugKey _keys[] = { {"network", IDLE_DEBUG_NETWORK}, {"parser", IDLE_DEBUG_PARSER}, {"text", IDLE_DEBUG_TEXT}, + {"tls", IDLE_DEBUG_TLS}, {NULL, 0} }; diff --git a/src/idle-debug.h b/src/idle-debug.h index 9564bc0..ac933bf 100644 --- a/src/idle-debug.h +++ b/src/idle-debug.h @@ -31,6 +31,7 @@ typedef enum { IDLE_DEBUG_PARSER = (1 << 5), IDLE_DEBUG_TEXT = (1 << 6), IDLE_DEBUG_ROOMLIST = (1 << 7), + IDLE_DEBUG_TLS = (1 << 8), } IdleDebugFlags; void idle_debug_init (void); 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); - } } diff --git a/src/idle-server-connection.h b/src/idle-server-connection.h index 8ce36ca..1b595b7 100644 --- a/src/idle-server-connection.h +++ b/src/idle-server-connection.h @@ -31,12 +31,6 @@ typedef struct _IdleServerConnection IdleServerConnection; typedef struct _IdleServerConnectionClass IdleServerConnectionClass; typedef enum { - SERVER_CONNECTION_STATE_NOT_CONNECTED, - SERVER_CONNECTION_STATE_CONNECTING, - SERVER_CONNECTION_STATE_CONNECTED -} IdleServerConnectionState; - -typedef enum { SERVER_CONNECTION_STATE_REASON_ERROR, SERVER_CONNECTION_STATE_REASON_REQUESTED } IdleServerConnectionStateReason; @@ -77,7 +71,7 @@ void idle_server_connection_force_disconnect(IdleServerConnection *conn); gboolean idle_server_connection_disconnect_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error); void idle_server_connection_send_async(IdleServerConnection *conn, const gchar *cmd, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean idle_server_connection_send_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error); -IdleServerConnectionState idle_server_connection_get_state(IdleServerConnection *conn); +gboolean idle_server_connection_is_connected(IdleServerConnection *conn); void idle_server_connection_set_tls(IdleServerConnection *conn, gboolean tls); G_END_DECLS diff --git a/src/server-tls-channel.c b/src/server-tls-channel.c new file mode 100644 index 0000000..aa2ef62 --- /dev/null +++ b/src/server-tls-channel.c @@ -0,0 +1,306 @@ +/* + * server-tls-channel.c - Source for IdleServerTLSChannel + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "server-tls-channel.h" + +#include <gio/gio.h> +#include <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/telepathy-glib-dbus.h> + +#define IDLE_DEBUG_FLAG IDLE_DEBUG_TLS +#include "idle-debug.h" +#include "tls-certificate.h" + +G_DEFINE_TYPE_WITH_CODE (IdleServerTLSChannel, idle_server_tls_channel, + TP_TYPE_BASE_CHANNEL, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_SERVER_TLS_CONNECTION, + NULL)); + +static void idle_server_tls_channel_close (TpBaseChannel *base); + +enum { + /* server TLS channel iface */ + PROP_SERVER_CERTIFICATE = 1, + PROP_HOSTNAME, + PROP_REFERENCE_IDENTITIES, + + /* not exported */ + PROP_CERTIFICATE, + + NUM_PROPERTIES +}; + +struct _IdleServerTLSChannelPrivate { + GTlsCertificate *certificate; + + IdleTLSCertificate *server_cert; + gchar *server_cert_path; + gchar *hostname; + GStrv reference_identities; + + gboolean dispose_has_run; +}; + +static void +idle_server_tls_channel_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + IdleServerTLSChannel *self = IDLE_SERVER_TLS_CHANNEL (object); + + switch (property_id) + { + case PROP_SERVER_CERTIFICATE: + g_value_set_boxed (value, self->priv->server_cert_path); + break; + case PROP_HOSTNAME: + g_value_set_string (value, self->priv->hostname); + break; + case PROP_REFERENCE_IDENTITIES: + g_value_set_boxed (value, self->priv->reference_identities); + break; + case PROP_CERTIFICATE: + g_value_set_object (value, self->priv->certificate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +idle_server_tls_channel_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + IdleServerTLSChannel *self = IDLE_SERVER_TLS_CHANNEL (object); + + switch (property_id) + { + case PROP_CERTIFICATE: + self->priv->certificate = g_value_dup_object (value); + break; + case PROP_HOSTNAME: + self->priv->hostname = g_value_dup_string (value); + break; + case PROP_REFERENCE_IDENTITIES: + self->priv->reference_identities = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +idle_server_tls_channel_finalize (GObject *object) +{ + IdleServerTLSChannel *self = IDLE_SERVER_TLS_CHANNEL (object); + + IDLE_DEBUG ("Finalize TLS channel"); + + g_free (self->priv->server_cert_path); + g_free (self->priv->hostname); + g_strfreev (self->priv->reference_identities); + + G_OBJECT_CLASS (idle_server_tls_channel_parent_class)->finalize (object); +} + +static void +idle_server_tls_channel_dispose (GObject *object) +{ + IdleServerTLSChannel *self = IDLE_SERVER_TLS_CHANNEL (object); + + if (self->priv->dispose_has_run) + return; + + IDLE_DEBUG ("Dispose TLS channel"); + + self->priv->dispose_has_run = TRUE; + + tp_clear_object (&self->priv->server_cert); + tp_clear_object (&self->priv->certificate); + + G_OBJECT_CLASS (idle_server_tls_channel_parent_class)->dispose (object); +} + +static void +idle_server_tls_channel_constructed (GObject *object) +{ + IdleServerTLSChannel *self = IDLE_SERVER_TLS_CHANNEL (object); + TpBaseChannel *base = TP_BASE_CHANNEL (self); + void (*chain_up) (GObject *) = + G_OBJECT_CLASS (idle_server_tls_channel_parent_class)->constructed; + const gchar *path; + gchar *cert_object_path; + GPtrArray *certificates; + GTlsCertificate *cert; + + if (chain_up != NULL) + chain_up (object); + + tp_base_channel_register (base); + + /* create the TLS certificate object */ + path = tp_base_channel_get_object_path (base); + cert_object_path = g_strdup_printf ("%s/TLSCertificateObject", path); + certificates = g_ptr_array_new (); + + /* Setup the full chain */ + cert = self->priv->certificate; + while (cert != NULL) + { + GByteArray *content; + GArray *c; + + g_object_get (cert, + "certificate", &content, + NULL); + c = g_array_sized_new (TRUE, TRUE, sizeof (guchar), content->len); + g_array_append_vals (c, content->data, content->len); + g_ptr_array_add (certificates, c); + + g_byte_array_unref (content); + + cert = g_tls_certificate_get_issuer (cert); + } + + self->priv->server_cert = g_object_new (IDLE_TYPE_TLS_CERTIFICATE, + "object-path", cert_object_path, + "certificate-chain-data", certificates, + "certificate-type", "x509", + "dbus-daemon", + tp_base_connection_get_dbus_daemon ( + tp_base_channel_get_connection (TP_BASE_CHANNEL (self))), + NULL); + self->priv->server_cert_path = cert_object_path; + g_ptr_array_unref (certificates); + + IDLE_DEBUG ("Server TLS channel constructed at %s", path); +} + +static void +idle_server_tls_channel_fill_immutable_properties ( + TpBaseChannel *chan, + GHashTable *properties) +{ + TP_BASE_CHANNEL_CLASS (idle_server_tls_channel_parent_class) + ->fill_immutable_properties (chan, properties); + + tp_dbus_properties_mixin_fill_properties_hash ( + G_OBJECT (chan), properties, + TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "ServerCertificate", + TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "Hostname", + TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, "ReferenceIdentities", + NULL); +} + +static gchar * +idle_server_tls_channel_get_object_path_suffix (TpBaseChannel *base) +{ + static guint count = 0; + + return g_strdup_printf ("ServerTLSChannel%u", ++count); +} + +static void +idle_server_tls_channel_init (IdleServerTLSChannel *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + IDLE_TYPE_SERVER_TLS_CHANNEL, IdleServerTLSChannelPrivate); +} + +static void +idle_server_tls_channel_class_init (IdleServerTLSChannelClass *klass) +{ + static TpDBusPropertiesMixinPropImpl server_tls_props[] = { + { "ServerCertificate", "server-certificate", NULL }, + { "Hostname", "hostname", NULL }, + { "ReferenceIdentities", "reference-identities", NULL }, + { NULL } + }; + + GObjectClass *oclass = G_OBJECT_CLASS (klass); + TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (IdleServerTLSChannelPrivate)); + + oclass->get_property = idle_server_tls_channel_get_property; + oclass->set_property = idle_server_tls_channel_set_property; + oclass->dispose = idle_server_tls_channel_dispose; + oclass->finalize = idle_server_tls_channel_finalize; + oclass->constructed = idle_server_tls_channel_constructed; + + base_class->channel_type = TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION; + base_class->target_handle_type = TP_HANDLE_TYPE_NONE; + base_class->fill_immutable_properties = + idle_server_tls_channel_fill_immutable_properties; + base_class->get_object_path_suffix = + idle_server_tls_channel_get_object_path_suffix; + base_class->close = idle_server_tls_channel_close; + + pspec = g_param_spec_boxed ("server-certificate", "Server certificate path", + "The object path of the server certificate.", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_SERVER_CERTIFICATE, pspec); + + pspec = g_param_spec_string ("hostname", "The hostname to be verified", + "The hostname which should be certified by the server certificate.", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_HOSTNAME, pspec); + + pspec = g_param_spec_boxed ("reference-identities", + "The various identities to check the certificate against", + "The server certificate identity should match one of these identities.", + G_TYPE_STRV, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_REFERENCE_IDENTITIES, pspec); + + pspec = g_param_spec_object ("certificate", "The GTLSCertificate", + "The GTLSCertificate object containing the TLS information", + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CERTIFICATE, pspec); + + tp_dbus_properties_mixin_implement_interface (oclass, + TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION, + tp_dbus_properties_mixin_getter_gobject_properties, NULL, + server_tls_props); +} + +static void +idle_server_tls_channel_close (TpBaseChannel *base) +{ + IDLE_DEBUG ("Close() called on the TLS channel %p", base); + tp_base_channel_destroyed (base); +} + +IdleTLSCertificate * +idle_server_tls_channel_get_certificate (IdleServerTLSChannel *self) +{ + return self->priv->server_cert; +} diff --git a/src/server-tls-channel.h b/src/server-tls-channel.h new file mode 100644 index 0000000..47b8474 --- /dev/null +++ b/src/server-tls-channel.h @@ -0,0 +1,71 @@ +/* + * server-tls-channel.h - Header for IdleServerTLSChannel + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __IDLE_SERVER_TLS_CHANNEL_H__ +#define __IDLE_SERVER_TLS_CHANNEL_H__ + +#include <glib-object.h> + +#include <telepathy-glib/telepathy-glib.h> + +#include <extensions/extensions.h> + +#include "tls-certificate.h" + +G_BEGIN_DECLS + +typedef struct _IdleServerTLSChannelPrivate IdleServerTLSChannelPrivate; +typedef struct _IdleServerTLSChannelClass IdleServerTLSChannelClass; +typedef struct _IdleServerTLSChannel IdleServerTLSChannel; + +struct _IdleServerTLSChannelClass { + TpBaseChannelClass base_class; +}; + +struct _IdleServerTLSChannel { + TpBaseChannel parent; + + IdleServerTLSChannelPrivate *priv; +}; + +GType idle_server_tls_channel_get_type (void); + +#define IDLE_TYPE_SERVER_TLS_CHANNEL \ + (idle_server_tls_channel_get_type ()) +#define IDLE_SERVER_TLS_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_SERVER_TLS_CHANNEL, \ + IdleServerTLSChannel)) +#define IDLE_SERVER_TLS_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_SERVER_TLS_CHANNEL, \ + IdleServerTLSChannelClass)) +#define IDLE_IS_SERVER_TLS_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_SERVER_TLS_CHANNEL)) +#define IDLE_IS_SERVER_TLS_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_SERVER_TLS_CHANNEL)) +#define IDLE_SERVER_TLS_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_SERVER_TLS_CHANNEL,\ + IdleServerTLSChannelClass)) + +IdleTLSCertificate * idle_server_tls_channel_get_certificate ( + IdleServerTLSChannel *self); + +G_END_DECLS + +#endif /* #ifndef __IDLE_SERVER_TLS_CHANNEL_H__*/ diff --git a/src/server-tls-manager.c b/src/server-tls-manager.c new file mode 100644 index 0000000..b14cecd --- /dev/null +++ b/src/server-tls-manager.c @@ -0,0 +1,462 @@ +/* + * server-tls-manager.c - Source for IdleServerTLSManager + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "server-tls-manager.h" + +#include <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/telepathy-glib-dbus.h> + +#define IDLE_DEBUG_FLAG IDLE_DEBUG_TLS +#include "idle-debug.h" +#include "idle-connection.h" +#include "server-tls-channel.h" + +#include "extensions/extensions.h" + +static void channel_manager_iface_init (gpointer, gpointer); + +G_DEFINE_TYPE_WITH_CODE (IdleServerTLSManager, idle_server_tls_manager, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, + channel_manager_iface_init)); + +enum { + PROP_CONNECTION = 1, + NUM_PROPERTIES +}; + +struct _IdleServerTLSManagerPrivate { + /* Properties */ + IdleConnection *connection; + + /* Current operation data */ + IdleServerTLSChannel *channel; + GSimpleAsyncResult *async_result; + + /* List of owned TpBaseChannel not yet closed by the client */ + GList *completed_channels; + + gboolean dispose_has_run; +}; + +#define chainup ((WockyTLSHandlerClass *) \ + idle_server_tls_manager_parent_class) + +static void +idle_server_tls_manager_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + IdleServerTLSManager *self = IDLE_SERVER_TLS_MANAGER (object); + + switch (property_id) + { + case PROP_CONNECTION: + g_value_set_object (value, self->priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +idle_server_tls_manager_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + IdleServerTLSManager *self = IDLE_SERVER_TLS_MANAGER (object); + + switch (property_id) + { + case PROP_CONNECTION: + self->priv->connection = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +close_all (IdleServerTLSManager *self) +{ + GList *l; + + if (self->priv->channel != NULL) + tp_base_channel_close (TP_BASE_CHANNEL (self->priv->channel)); + + l = self->priv->completed_channels; + while (l != NULL) + { + /* use a temporary variable as the ::closed callback will delete + * the link from the list. */ + GList *next = l->next; + + tp_base_channel_close (l->data); + + l = next; + } +} + +static void +connection_status_changed_cb (IdleConnection *conn, + guint status, + guint reason, + gpointer user_data) +{ + IdleServerTLSManager *self = user_data; + + IDLE_DEBUG ("Connection status changed, now %d", status); + + if (status == TP_CONNECTION_STATUS_DISCONNECTED) + { + close_all (self); + tp_clear_object (&self->priv->connection); + } +} + +static void +complete_verify (IdleServerTLSManager *self) +{ + /* Move channel to a list until a client Close() it */ + if (self->priv->channel != NULL) + { + self->priv->completed_channels = g_list_prepend ( + self->priv->completed_channels, + g_object_ref (self->priv->channel)); + } + + g_simple_async_result_complete (self->priv->async_result); + + /* Reset to initial state */ + g_clear_object (&self->priv->channel); + g_clear_object (&self->priv->async_result); +} + +static void +server_tls_channel_closed_cb (IdleServerTLSChannel *channel, + gpointer user_data) +{ + IdleServerTLSManager *self = user_data; + + IDLE_DEBUG ("Server TLS channel closed."); + + if (channel == self->priv->channel) + { + IDLE_DEBUG ("Channel closed before being handled. Failing verification"); + + g_simple_async_result_set_error (self->priv->async_result, + IDLE_SERVER_TLS_ERROR, 0, "TLS verification channel closed"); + + self->priv->channel = NULL; + complete_verify (self); + } + else + { + GList *l; + + l = g_list_find (self->priv->completed_channels, channel); + g_assert (l != NULL); + + self->priv->completed_channels = g_list_delete_link ( + self->priv->completed_channels, l); + } + + tp_channel_manager_emit_channel_closed_for_object (self, + TP_EXPORTABLE_CHANNEL (channel)); + g_object_unref (channel); +} + +GQuark +idle_server_tls_error_quark (void) +{ + static GQuark quark = 0; + + if (!quark) + quark = g_quark_from_static_string ("server-tls-error"); + + return quark; +} + +static void +tls_certificate_accepted_cb (IdleTLSCertificate *certificate, + gpointer user_data) +{ + IdleServerTLSManager *self = user_data; + + IDLE_DEBUG ("TLS certificate accepted"); + + complete_verify (self); +} + +static void +tls_certificate_rejected_cb (IdleTLSCertificate *certificate, + GPtrArray *rejections, + gpointer user_data) +{ + IdleServerTLSManager *self = user_data; + + IDLE_DEBUG ("TLS certificate rejected with rejections %p, length %u.", + rejections, rejections->len); + + g_simple_async_result_set_error (self->priv->async_result, + IDLE_SERVER_TLS_ERROR, 0, "TLS certificate rejected"); + + complete_verify (self); +} + +void +idle_server_tls_manager_verify_async (IdleServerTLSManager *self, + GTlsCertificate *certificate, + const gchar *peername, + GAsyncReadyCallback callback, + gpointer user_data) +{ + IdleTLSCertificate *cert; + GSimpleAsyncResult *result; + const gchar *identities[] = { peername, NULL }; + + g_return_if_fail (self->priv->async_result == NULL); + + IDLE_DEBUG ("verify_async() called on the IdleServerTLSManager."); + + result = g_simple_async_result_new (G_OBJECT (self), + callback, user_data, idle_server_tls_manager_verify_async); + + if (self->priv->connection == NULL) + { + IDLE_DEBUG ("connection already went away; failing immediately"); + g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_CANCELLED, + "The Telepathy connection has already been disconnected"); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + return; + } + + self->priv->async_result = result; + + self->priv->channel = g_object_new (IDLE_TYPE_SERVER_TLS_CHANNEL, + "connection", self->priv->connection, + "certificate", certificate, + "hostname", peername, + "reference-identities", identities, + NULL); + + g_signal_connect (self->priv->channel, "closed", + G_CALLBACK (server_tls_channel_closed_cb), self); + + cert = idle_server_tls_channel_get_certificate (self->priv->channel); + + g_signal_connect (cert, "accepted", + G_CALLBACK (tls_certificate_accepted_cb), self); + g_signal_connect (cert, "rejected", + G_CALLBACK (tls_certificate_rejected_cb), self); + + /* emit NewChannel on the ChannelManager iface */ + tp_channel_manager_emit_new_channel (self, + (TpExportableChannel *) self->priv->channel, NULL); +} + +gboolean +idle_server_tls_manager_verify_finish (IdleServerTLSManager *self, + GAsyncResult *result, + GError **error) +{ + if (g_simple_async_result_propagate_error ( + G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT(self), idle_server_tls_manager_verify_async), FALSE); + return TRUE; +} + +static void +idle_server_tls_manager_init (IdleServerTLSManager *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + IDLE_TYPE_SERVER_TLS_MANAGER, IdleServerTLSManagerPrivate); +} + +static void +idle_server_tls_manager_dispose (GObject *object) +{ + IdleServerTLSManager *self = IDLE_SERVER_TLS_MANAGER (object); + + IDLE_DEBUG ("%p", self); + + if (self->priv->dispose_has_run) + return; + + self->priv->dispose_has_run = TRUE; + + tp_clear_object (&self->priv->connection); + + G_OBJECT_CLASS (idle_server_tls_manager_parent_class)->dispose (object); +} + +static void +idle_server_tls_manager_finalize (GObject *object) +{ + IdleServerTLSManager *self = IDLE_SERVER_TLS_MANAGER (object); + + IDLE_DEBUG ("%p", self); + + close_all (self); + + G_OBJECT_CLASS (idle_server_tls_manager_parent_class)->finalize (object); +} + +static void +idle_server_tls_manager_constructed (GObject *object) +{ + IdleServerTLSManager *self = IDLE_SERVER_TLS_MANAGER (object); + void (*chain_up) (GObject *) = + G_OBJECT_CLASS (idle_server_tls_manager_parent_class)->constructed; + + if (chain_up != NULL) + chain_up (object); + + IDLE_DEBUG ("Server TLS Manager constructed"); + + tp_g_signal_connect_object (self->priv->connection, "status-changed", + G_CALLBACK (connection_status_changed_cb), object, 0); +} + +static void +idle_server_tls_manager_class_init (IdleServerTLSManagerClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (IdleServerTLSManagerPrivate)); + + oclass->dispose = idle_server_tls_manager_dispose; + oclass->finalize = idle_server_tls_manager_finalize; + oclass->constructed = idle_server_tls_manager_constructed; + oclass->set_property = idle_server_tls_manager_set_property; + oclass->get_property = idle_server_tls_manager_get_property; + + pspec = g_param_spec_object ("connection", "Base connection object", + "base connection object that owns this manager.", + TP_TYPE_BASE_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CONNECTION, pspec); +} + +static void +idle_server_tls_manager_foreach_channel (TpChannelManager *manager, + TpExportableChannelFunc func, + gpointer user_data) +{ + IdleServerTLSManager *self = IDLE_SERVER_TLS_MANAGER (manager); + GList *l; + + if (self->priv->channel != NULL) + func (TP_EXPORTABLE_CHANNEL (self->priv->channel), user_data); + + for (l = self->priv->completed_channels; l != NULL; l = l->next) + { + func (l->data, user_data); + } +} + +static void +channel_manager_iface_init (gpointer g_iface, + gpointer iface_data) +{ + TpChannelManagerIface *iface = g_iface; + + iface->foreach_channel = idle_server_tls_manager_foreach_channel; + + /* these channels are not requestable. */ + iface->ensure_channel = NULL; + iface->create_channel = NULL; + iface->request_channel = NULL; + iface->foreach_channel_class = NULL; +} + +static TpConnectionStatusReason +cert_reject_reason_to_conn_reason (TpTLSCertificateRejectReason tls_reason) +{ + #define EASY_CASE(x) \ + case TP_TLS_CERTIFICATE_REJECT_REASON_ ## x: \ + return TP_CONNECTION_STATUS_REASON_CERT_ ## x; + + switch (tls_reason) + { + EASY_CASE (UNTRUSTED); + EASY_CASE (EXPIRED); + EASY_CASE (NOT_ACTIVATED); + EASY_CASE (FINGERPRINT_MISMATCH); + EASY_CASE (HOSTNAME_MISMATCH); + EASY_CASE (SELF_SIGNED); + EASY_CASE (REVOKED); + EASY_CASE (INSECURE); + EASY_CASE (LIMIT_EXCEEDED); + + case TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN: + default: + return TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR; + } + + #undef EASY_CASE +} + +void +idle_server_tls_manager_get_rejection_details (IdleServerTLSManager *self, + gchar **dbus_error, + GHashTable **details, + TpConnectionStatusReason *reason) +{ + IdleTLSCertificate *certificate; + GPtrArray *rejections; + GValueArray *rejection; + TpTLSCertificateRejectReason tls_reason; + + /* We probably want the rejection details of last completed operation */ + g_return_if_fail (self->priv->completed_channels != NULL); + + certificate = idle_server_tls_channel_get_certificate ( + self->priv->completed_channels->data); + g_object_get (certificate, + "rejections", &rejections, + NULL); + + /* we return 'Invalid_Argument' if Reject() is called with zero + * reasons, so if this fails something bad happened. + */ + g_assert (rejections->len >= 1); + + rejection = g_ptr_array_index (rejections, 0); + + tls_reason = g_value_get_uint (g_value_array_get_nth (rejection, 0)); + *dbus_error = g_value_dup_string (g_value_array_get_nth (rejection, 1)); + *details = g_value_dup_boxed (g_value_array_get_nth (rejection, 2)); + + *reason = cert_reject_reason_to_conn_reason (tls_reason); + + tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + &rejections); +} diff --git a/src/server-tls-manager.h b/src/server-tls-manager.h new file mode 100644 index 0000000..9e33753 --- /dev/null +++ b/src/server-tls-manager.h @@ -0,0 +1,83 @@ +/* + * server-tls-manager.h - Header for IdleServerTLSManager + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __IDLE_SERVER_TLS_MANAGER_H__ +#define __IDLE_SERVER_TLS_MANAGER_H__ + +#include <glib-object.h> +#include <telepathy-glib/telepathy-glib.h> + +#include "extensions/extensions.h" + +G_BEGIN_DECLS + +typedef struct _IdleServerTLSManager IdleServerTLSManager; +typedef struct _IdleServerTLSManagerClass IdleServerTLSManagerClass; +typedef struct _IdleServerTLSManagerPrivate IdleServerTLSManagerPrivate; + +struct _IdleServerTLSManagerClass { + GObjectClass parent_class; +}; + +struct _IdleServerTLSManager { + GObject parent; + IdleServerTLSManagerPrivate *priv; +}; + +GType idle_server_tls_manager_get_type (void); + +#define IDLE_TYPE_SERVER_TLS_MANAGER \ + (idle_server_tls_manager_get_type ()) +#define IDLE_SERVER_TLS_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_SERVER_TLS_MANAGER, \ + IdleServerTLSManager)) +#define IDLE_SERVER_TLS_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_SERVER_TLS_MANAGER, \ + IdleServerTLSManagerClass)) +#define IDLE_IS_SERVER_TLS_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_SERVER_TLS_MANAGER)) +#define IDLE_IS_SERVER_TLS_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_SERVER_TLS_MANAGER)) +#define IDLE_SERVER_TLS_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_SERVER_TLS_MANAGER, \ + IdleServerTLSManagerClass)) + +#define IDLE_SERVER_TLS_ERROR idle_server_tls_error_quark () +GQuark idle_server_tls_error_quark (void); + +void idle_server_tls_manager_get_rejection_details ( + IdleServerTLSManager *self, + gchar **dbus_error, + GHashTable **details, + TpConnectionStatusReason *reason); + +void idle_server_tls_manager_verify_async (IdleServerTLSManager *self, + GTlsCertificate *certificate, + const gchar *peername, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean idle_server_tls_manager_verify_finish (IdleServerTLSManager *self, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* #ifndef __IDLE_SERVER_TLS_MANAGER_H__ */ diff --git a/src/tls-certificate.c b/src/tls-certificate.c new file mode 100644 index 0000000..467d13e --- /dev/null +++ b/src/tls-certificate.c @@ -0,0 +1,344 @@ +/* + * tls-certificate.c - Source for IdleTLSCertificate + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "tls-certificate.h" + +#include <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/telepathy-glib-dbus.h> + +#define IDLE_DEBUG_FLAG IDLE_DEBUG_TLS +#include "idle-debug.h" + +static void +tls_certificate_iface_init (gpointer g_iface, gpointer iface_data); + +G_DEFINE_TYPE_WITH_CODE (IdleTLSCertificate, + idle_tls_certificate, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE, + tls_certificate_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, + tp_dbus_properties_mixin_iface_init);) + +struct _IdleTLSCertificatePrivate { + gchar *object_path; + + gchar *cert_type; + TpTLSCertificateState cert_state; + + GPtrArray *rejections; + GPtrArray *cert_data; + + TpDBusDaemon *daemon; + + gboolean dispose_has_run; +}; + +enum { + PROP_OBJECT_PATH = 1, + PROP_STATE, + PROP_REJECTIONS, + PROP_CERTIFICATE_TYPE, + PROP_CERTIFICATE_CHAIN_DATA, + + /* not exported */ + PROP_DBUS_DAEMON, + + NUM_PROPERTIES +}; + +static void +idle_tls_certificate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + IdleTLSCertificate *self = IDLE_TLS_CERTIFICATE (object); + + switch (property_id) + { + case PROP_OBJECT_PATH: + g_value_set_string (value, self->priv->object_path); + break; + case PROP_STATE: + g_value_set_uint (value, self->priv->cert_state); + break; + case PROP_REJECTIONS: + g_value_set_boxed (value, self->priv->rejections); + break; + case PROP_CERTIFICATE_TYPE: + g_value_set_string (value, self->priv->cert_type); + break; + case PROP_CERTIFICATE_CHAIN_DATA: + g_value_set_boxed (value, self->priv->cert_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +idle_tls_certificate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + IdleTLSCertificate *self = IDLE_TLS_CERTIFICATE (object); + + switch (property_id) + { + case PROP_OBJECT_PATH: + self->priv->object_path = g_value_dup_string (value); + break; + case PROP_CERTIFICATE_TYPE: + self->priv->cert_type = g_value_dup_string (value); + break; + case PROP_CERTIFICATE_CHAIN_DATA: + self->priv->cert_data = g_value_dup_boxed (value); + break; + case PROP_DBUS_DAEMON: + self->priv->daemon = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, value); + break; + } +} + +static void +idle_tls_certificate_finalize (GObject *object) +{ + IdleTLSCertificate *self = IDLE_TLS_CERTIFICATE (object); + + tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + &self->priv->rejections); + + g_free (self->priv->object_path); + g_free (self->priv->cert_type); + g_ptr_array_unref (self->priv->cert_data); + + G_OBJECT_CLASS (idle_tls_certificate_parent_class)->finalize (object); +} + +static void +idle_tls_certificate_dispose (GObject *object) +{ + IdleTLSCertificate *self = IDLE_TLS_CERTIFICATE (object); + + if (self->priv->dispose_has_run) + return; + + self->priv->dispose_has_run = TRUE; + + tp_clear_object (&self->priv->daemon); + + G_OBJECT_CLASS (idle_tls_certificate_parent_class)->dispose (object); +} + +static void +idle_tls_certificate_constructed (GObject *object) +{ + IdleTLSCertificate *self = IDLE_TLS_CERTIFICATE (object); + void (*chain_up) (GObject *) = + G_OBJECT_CLASS (idle_tls_certificate_parent_class)->constructed; + + if (chain_up != NULL) + chain_up (object); + + /* register the certificate on the bus */ + tp_dbus_daemon_register_object (self->priv->daemon, + self->priv->object_path, self); +} + +static void +idle_tls_certificate_init (IdleTLSCertificate *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + IDLE_TYPE_TLS_CERTIFICATE, IdleTLSCertificatePrivate); + self->priv->rejections = g_ptr_array_new (); +} + +static void +idle_tls_certificate_class_init (IdleTLSCertificateClass *klass) +{ + static TpDBusPropertiesMixinPropImpl object_props[] = { + { "State", "state", NULL }, + { "Rejections", "rejections", NULL }, + { "CertificateType", "certificate-type", NULL }, + { "CertificateChainData", "certificate-chain-data", NULL }, + { NULL } + }; + static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { + { TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE, + tp_dbus_properties_mixin_getter_gobject_properties, + NULL, + object_props, + }, + { NULL } + }; + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (IdleTLSCertificatePrivate)); + + oclass->finalize = idle_tls_certificate_finalize; + oclass->dispose = idle_tls_certificate_dispose; + oclass->set_property = idle_tls_certificate_set_property; + oclass->get_property = idle_tls_certificate_get_property; + oclass->constructed = idle_tls_certificate_constructed; + + pspec = g_param_spec_string ("object-path", + "D-Bus object path", + "The D-Bus object path used for this object on the bus.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_OBJECT_PATH, pspec); + + pspec = g_param_spec_uint ("state", + "State of this certificate", + "The state of this TLS certificate.", + 0, NUM_TP_TLS_CERTIFICATE_STATES - 1, + TP_TLS_CERTIFICATE_STATE_PENDING, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_STATE, pspec); + + pspec = g_param_spec_boxed ("rejections", + "The reject reasons", + "The reasons why this TLS certificate has been rejected", + TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_REJECTIONS, pspec); + + pspec = g_param_spec_string ("certificate-type", + "The certificate type", + "The type of this certificate.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CERTIFICATE_TYPE, pspec); + + pspec = g_param_spec_boxed ("certificate-chain-data", + "The certificate chain data", + "The raw PEM-encoded trust chain of this certificate.", + TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CERTIFICATE_CHAIN_DATA, pspec); + + pspec = g_param_spec_object ("dbus-daemon", + "The DBus daemon connection", + "The connection to the DBus daemon owning the CM", + TP_TYPE_DBUS_DAEMON, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_DBUS_DAEMON, pspec); + + klass->dbus_props_class.interfaces = prop_interfaces; + tp_dbus_properties_mixin_class_init (oclass, + G_STRUCT_OFFSET (IdleTLSCertificateClass, dbus_props_class)); +} + +static void +idle_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *cert, + DBusGMethodInvocation *context) +{ + IdleTLSCertificate *self = IDLE_TLS_CERTIFICATE (cert); + + IDLE_DEBUG ("Accept() called on the TLS certificate; current state %u", + self->priv->cert_state); + + if (self->priv->cert_state != TP_TLS_CERTIFICATE_STATE_PENDING) + { + GError error = + { TP_ERROR, + TP_ERROR_INVALID_ARGUMENT, + "Calling Accept() on a certificate with state != PENDING " + "doesn't make sense." + }; + + dbus_g_method_return_error (context, &error); + return; + } + + self->priv->cert_state = TP_TLS_CERTIFICATE_STATE_ACCEPTED; + tp_svc_authentication_tls_certificate_emit_accepted (self); + + tp_svc_authentication_tls_certificate_return_from_accept (context); +} + +static void +idle_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *cert, + const GPtrArray *rejections, + DBusGMethodInvocation *context) +{ + IdleTLSCertificate *self = IDLE_TLS_CERTIFICATE (cert); + + IDLE_DEBUG ("Reject() called on the TLS certificate with rejections %p, " + "length %u; current state %u", rejections, rejections->len, + self->priv->cert_state); + + if (rejections->len < 1) + { + GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Calling Reject() with a zero-length rejection list." }; + + dbus_g_method_return_error (context, &error); + return; + } + + if (self->priv->cert_state != TP_TLS_CERTIFICATE_STATE_PENDING) + { + GError error = + { TP_ERROR, + TP_ERROR_INVALID_ARGUMENT, + "Calling Reject() on a certificate with state != PENDING " + "doesn't make sense." + }; + + dbus_g_method_return_error (context, &error); + return; + } + + tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + &self->priv->rejections); + + self->priv->rejections = + g_boxed_copy (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST, + rejections); + self->priv->cert_state = TP_TLS_CERTIFICATE_STATE_REJECTED; + + tp_svc_authentication_tls_certificate_emit_rejected ( + self, self->priv->rejections); + + tp_svc_authentication_tls_certificate_return_from_reject (context); +} + +static void +tls_certificate_iface_init (gpointer g_iface, + gpointer iface_data) +{ + TpSvcAuthenticationTLSCertificateClass *klass = g_iface; + +#define IMPLEMENT(x) \ + tp_svc_authentication_tls_certificate_implement_##x ( \ + klass, idle_tls_certificate_##x) + IMPLEMENT (accept); + IMPLEMENT (reject); +#undef IMPLEMENT +} diff --git a/src/tls-certificate.h b/src/tls-certificate.h new file mode 100644 index 0000000..52ea684 --- /dev/null +++ b/src/tls-certificate.h @@ -0,0 +1,66 @@ +/* + * tls-certificate.h - Header for IdleTLSCertificate + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __IDLE_TLS_CERTIFICATE_H__ +#define __IDLE_TLS_CERTIFICATE_H__ + +#include <glib-object.h> + +#include <telepathy-glib/telepathy-glib.h> + +G_BEGIN_DECLS + +typedef struct _IdleTLSCertificate IdleTLSCertificate; +typedef struct _IdleTLSCertificateClass IdleTLSCertificateClass; +typedef struct _IdleTLSCertificatePrivate IdleTLSCertificatePrivate; + +struct _IdleTLSCertificateClass { + GObjectClass parent_class; + + TpDBusPropertiesMixinClass dbus_props_class; +}; + +struct _IdleTLSCertificate { + GObject parent; + + IdleTLSCertificatePrivate *priv; +}; + +GType idle_tls_certificate_get_type (void); + +#define IDLE_TYPE_TLS_CERTIFICATE \ + (idle_tls_certificate_get_type ()) +#define IDLE_TLS_CERTIFICATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_TLS_CERTIFICATE, \ + IdleTLSCertificate)) +#define IDLE_TLS_CERTIFICATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_TLS_CERTIFICATE, \ + IdleTLSCertificateClass)) +#define IDLE_IS_TLS_CERTIFICATE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_TLS_CERTIFICATE)) +#define IDLE_IS_TLS_CERTIFICATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_TLS_CERTIFICATE)) +#define IDLE_TLS_CERTIFICATE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_TLS_CERTIFICATE, \ + IdleTLSCertificateClass)) + +G_END_DECLS + +#endif /* #ifndef __IDLE_TLS_CERTIFICATE_H__*/ diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index aa749ef..764f074 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -1,9 +1,13 @@ TWISTED_TESTS = \ cm/protocol.py \ + connect/connect-close-ssl.py \ connect/connect-success.py \ connect/connect-success-ssl.py \ + connect/connect-reject-ssl.py \ connect/connect-fail.py \ connect/connect-fail-ssl.py \ + connect/disconnect-before-socket-connected.py \ + connect/disconnect-during-cert-verification.py \ connect/ping.py \ connect/server-quit-ignore.py \ connect/server-quit-noclose.py \ @@ -46,7 +50,6 @@ check-twisted: rm -f tools/core rm -f tools/idle-testing.log failed=0; \ - IDLE_TEST_BE_VULNERABLE_TO_MAN_IN_THE_MIDDLE_ATTACKS=vulnerable \ sh $(srcdir)/tools/with-session-bus.sh \ --config-file=tools/tmp-session-bus.conf \ -- $(MAKE) check-TESTS \ diff --git a/tests/twisted/connect/connect-close-ssl.py b/tests/twisted/connect/connect-close-ssl.py new file mode 100644 index 0000000..6fecd50 --- /dev/null +++ b/tests/twisted/connect/connect-close-ssl.py @@ -0,0 +1,28 @@ + +""" +Test connecting to a SSL server. +""" + +import dbus +import constants as cs +from idletest import exec_test, SSLIRCServer +from servicetest import EventPattern, wrap_channel + +def test(q, bus, conn, stream): + conn.Connect() + q.expect_many( + EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), + EventPattern('irc-connected')) + e = q.expect('dbus-signal', signal='NewChannels') + channels = e.args[0] + path, props = channels[0] + + channel = wrap_channel(bus.get_object(conn.bus_name, path), + cs.CHANNEL_TYPE_SERVER_TLS_CONNECTION) + channel.Close() + + q.expect('dbus-signal', signal='StatusChanged', args=[2, 2]) + +if __name__ == '__main__': + exec_test(test, {'use-ssl':dbus.Boolean(True)}, protocol=SSLIRCServer) + diff --git a/tests/twisted/connect/connect-fail-ssl.py b/tests/twisted/connect/connect-fail-ssl.py index dd9658f..283bc03 100644 --- a/tests/twisted/connect/connect-fail-ssl.py +++ b/tests/twisted/connect/connect-fail-ssl.py @@ -14,4 +14,3 @@ def test(q, bus, conn, stream): if __name__ == '__main__': # there is no ssl server listening at port 5600, so this should fail exec_test(test, {'port': dbus.UInt32(5600), 'use-ssl': dbus.Boolean(True)}) - diff --git a/tests/twisted/connect/connect-reject-ssl.py b/tests/twisted/connect/connect-reject-ssl.py new file mode 100644 index 0000000..8028428 --- /dev/null +++ b/tests/twisted/connect/connect-reject-ssl.py @@ -0,0 +1,29 @@ + +""" +Test connecting to a SSL server. +""" + +import dbus +import constants as cs +from idletest import exec_test, SSLIRCServer +from servicetest import EventPattern, call_async + +def test(q, bus, conn, stream): + conn.Connect() + q.expect_many( + EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), + EventPattern('irc-connected')) + e = q.expect('dbus-signal', signal='NewChannels') + channels = e.args[0] + path, props = channels[0] + + cert = bus.get_object (conn.bus_name, props[cs.TLS_CERT_PATH]) + cert.Reject([(cs.TLS_REJECT_REASON_UNTRUSTED, cs.CERT_UNTRUSTED, {})], + signature = 'a(usa{sv})') + + q.expect('dbus-signal', signal='StatusChanged', args=[2, 2]) + return True + +if __name__ == '__main__': + exec_test(test, {'use-ssl':dbus.Boolean(True)}, protocol=SSLIRCServer) + diff --git a/tests/twisted/connect/connect-success-ssl.py b/tests/twisted/connect/connect-success-ssl.py index 627eeb8..33062c5 100644 --- a/tests/twisted/connect/connect-success-ssl.py +++ b/tests/twisted/connect/connect-success-ssl.py @@ -4,6 +4,7 @@ Test connecting to a SSL server. """ import dbus +import constants as cs from idletest import exec_test, SSLIRCServer from servicetest import EventPattern, call_async @@ -12,6 +13,13 @@ def test(q, bus, conn, stream): q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), EventPattern('irc-connected')) + e = q.expect('dbus-signal', signal='NewChannels') + channels = e.args[0] + path, props = channels[0] + + cert = bus.get_object (conn.bus_name, props[cs.TLS_CERT_PATH]) + cert.Accept() + q.expect('dbus-signal', signal='SelfHandleChanged', args=[1L]) q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) diff --git a/tests/twisted/connect/disconnect-before-socket-connected.py b/tests/twisted/connect/disconnect-before-socket-connected.py new file mode 100644 index 0000000..8fc89e0 --- /dev/null +++ b/tests/twisted/connect/disconnect-before-socket-connected.py @@ -0,0 +1,31 @@ +""" +Test disconnecting before the TCP session is established. +""" + +from idletest import exec_test +from servicetest import call_async + +def test(q, bus, conn, stream): + conn.Connect() + q.expect('dbus-signal', signal='StatusChanged', args=[1, 1]) + + # We want the call to Disconnect to reach Idle before the call to + # g_socket_client_connect_to_host(), which connects to this Python process, + # completes. I tried making the Twisted infrastructure stop calling + # .accept() but that doesn't seem to have any effect. + # + # But! All is not lost! Making a blocking call to Disconnect() does the + # job, because we block in libdbus and Twisted doesn't get a chance to poll + # the listening socket until Disconnect() returns. + conn.Disconnect() + + # The bug was that Idle would not try to cancel the in-progress connection + # attempt. It worked when the connection was blocked on TLS verification + # (see disconnect-during-cert-verification.py) because closing that channel + # (as a side-effect of disconnecting) caused the TCP connection attempt to + # fail, but didn't work in this case. + q.expect('dbus-signal', signal='StatusChanged', args=[2, 1]) + +if __name__ == '__main__': + exec_test(test) + diff --git a/tests/twisted/connect/disconnect-during-cert-verification.py b/tests/twisted/connect/disconnect-during-cert-verification.py new file mode 100644 index 0000000..83fe004 --- /dev/null +++ b/tests/twisted/connect/disconnect-during-cert-verification.py @@ -0,0 +1,28 @@ +""" +Test disconnecting while a certificate verification channel is open. +""" + +import dbus +import constants as cs +from idletest import exec_test, SSLIRCServer +from servicetest import EventPattern, sync_dbus + +def test(q, bus, conn, stream): + conn.Connect() + q.expect_many( + EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), + EventPattern('irc-connected')) + e = q.expect('dbus-signal', signal='NewChannels') + + conn.Disconnect() + q.expect_many( + EventPattern('dbus-signal', signal='StatusChanged'), + EventPattern('irc-disconnected'), + ) + + # Idle would now crash in an idle callback; so let's see if it's alive. + sync_dbus(bus, q, conn) + +if __name__ == '__main__': + exec_test(test, {'use-ssl':dbus.Boolean(True)}, protocol=SSLIRCServer) + diff --git a/tests/twisted/tools/tp-glib.supp b/tests/twisted/tools/tp-glib.supp new file mode 100644 index 0000000..28bd5a0 --- /dev/null +++ b/tests/twisted/tools/tp-glib.supp @@ -0,0 +1,390 @@ +# Valgrind error suppression file + +# ============================= libc ================================== + +{ + ld.so initialization + selinux + Memcheck:Leak + ... + fun:_dl_init + obj:/lib/ld-*.so +} + +{ + dlopen initialization, triggered by handle-leak-debug code + Memcheck:Leak + ... + fun:__libc_dlopen_mode + fun:init + fun:backtrace + fun:handle_leak_debug_bt + fun:dynamic_ensure_handle + fun:tp_handle_ensure +} + +# default.supp has these for 2.10, but they're too specific +{ + Debian libc6 (2.10.x, 2.11.x) stripped dynamic linker + Memcheck:Cond + fun:index + fun:expand_dynamic_string_token + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start + obj:/lib/ld-*.so +} +{ + Debian libc6 (2.9.x - 2.11.x) stripped dynamic linker + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start + obj:/lib/ld-*.so +} + +{ + ld.so initialization on glibc 2.9 + Memcheck:Cond + fun:strlen + fun:_dl_init_paths + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start + obj:/lib/ld-2.9.so +} + +# ======================= libselinux on Debian amd64 ===================== + +{ + I have no idea what SELinux is doing but it's not my problem + Memcheck:Cond + ... + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 +} + +{ + I have no idea what SELinux is doing but it's not my problem + Memcheck:Value8 + ... + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 +} + +{ + I have no idea what SELinux is doing but it's not my problem + Memcheck:Leak + ... + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 +} + +# ============================= GLib ================================== + +{ + g_set_prgname copies its argument + Memcheck:Leak + ... + fun:g_set_prgname +} + +{ + one g_get_charset per child^Wprocess + Memcheck:Leak + ... + fun:g_get_charset +} + +{ + one g_get_home_dir per process + Memcheck:Leak + ... + fun:g_get_home_dir +} + +{ + GQuarks can't be freed + Memcheck:Leak + ... + fun:g_quark_from_static_string +} + +{ + GQuarks can't be freed + Memcheck:Leak + ... + fun:g_quark_from_string +} + +{ + interned strings can't be freed + Memcheck:Leak + ... + fun:g_intern_string +} + +{ + interned strings can't be freed + Memcheck:Leak + ... + fun:g_intern_static_string +} + +{ + shared global default g_main_context + Memcheck:Leak + ... + fun:g_main_context_new + fun:g_main_context_default +} + +{ + GTest initialization + Memcheck:Leak + ... + fun:g_test_init + fun:main +} + +{ + GTest admin + Memcheck:Leak + ... + fun:g_test_add_vtable +} + +{ + GTest pseudorandomness + Memcheck:Leak + ... + fun:g_rand_new_with_seed_array + fun:test_run_seed + ... + fun:g_test_run +} + +{ + GSLice initialization + Memcheck:Leak + ... + fun:g_malloc0 + fun:g_slice_init_nomessage + fun:g_slice_alloc +} + +# ============================= GObject =============================== + +{ + g_type_init + Memcheck:Leak + ... + fun:g_type_init +} + +{ + g_type_init_with_debug_flags + Memcheck:Leak + ... + fun:g_type_init_with_debug_flags +} + +{ + g_type_register_static + Memcheck:Leak + ... + fun:g_type_register_static +} + +{ + g_type_add_interface_static + Memcheck:Leak + ... + fun:g_type_add_interface_static +} + +{ + initialization of interfaces + Memcheck:Leak + ... + fun:type_iface_vtable_base_init_Wm + fun:g_type_class_ref +} + +# ============================= GIO =================================== + +{ + GIO init + Memcheck:Leak + ... + fun:g_inet_address_class_intern_init +} + +{ + g_simple_async_result class + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_simple_async_result_new +} + +# ============================= dbus-glib ============================= + +{ + registering marshallers is permanent + Memcheck:Leak + ... + fun:dbus_g_object_register_marshaller_array + fun:dbus_g_object_register_marshaller +} + +{ + dbus-glib specialized GTypes are permanent + Memcheck:Leak + ... + fun:dbus_g_type_specialized_init +} + +{ + libdbus shared connection + Memcheck:Leak + ... + fun:dbus_g_bus_get +} + +{ + dbus-gobject registrations aren't freed unless we fall off the bus + Memcheck:Leak + ... + fun:g_slist_append + fun:dbus_g_connection_register_g_object +} + +{ + DBusGProxy slots aren't freed unless we fall off the bus + Memcheck:Leak + ... + fun:dbus_connection_allocate_data_slot + ... + fun:dbus_g_proxy_constructor +} + +{ + error registrations are for life, not just for Christmas + Memcheck:Leak + ... + fun:dbus_g_error_domain_register +} + +{ + DBusGProxy class init + Memcheck:Leak + ... + fun:dbus_g_proxy_class_init +} + +# ============================= telepathy-glib ======================== + +{ + tp_dbus_daemon_constructor @daemons once per DBusConnection + Memcheck:Leak + ... + fun:g_slice_alloc + fun:tp_dbus_daemon_constructor +} + +{ + tp_proxy_subclass_add_error_mapping refs the enum + Memcheck:Leak + ... + fun:g_type_class_ref + fun:tp_proxy_subclass_add_error_mapping +} + +{ + tp_proxy_or_subclass_hook_on_interface_add never frees its list + Memcheck:Leak + ... + fun:tp_proxy_or_subclass_hook_on_interface_add +} + +{ + tp_dbus_daemon_constructor filter not freed til we fall off the bus + Memcheck:Leak + ... + fun:dbus_connection_add_filter + fun:tp_dbus_daemon_constructor +} + +{ + tp_g_socket_address_from_variant reffing GNIO types + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:tp_g_socket_address_from_variant +} + +{ + creating classes for DBusGProxy + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_object_new + ... + fun:tp_proxy_borrow_interface_by_id +} + +{ + creating classes for tp_dbus_daemon_new + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_object_new + ... + fun:tp_dbus_daemon_new +} + +{ + creating classes for TpCHannel + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_object_new + ... + fun:tp_channel_new +} + +{ + creating a boxed type to use in TpCapabilities + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_param_spec_boxed + fun:tp_capabilities_class_intern_init +} + +# ============================= questionable ========================== + +{ + creating classes for instances (this is a pretty big hammer) + Memcheck:Leak + ... + fun:g_type_class_ref + ... + fun:g_type_create_instance + ... + fun:g_param_spec_string +} |