summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2013-05-01 15:46:49 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2013-05-01 15:46:49 +0100
commit7d43b91b99535b309298592e945ecc28c567e53e (patch)
treee20e4414abaeaec5bd566f0c634258aab4ea6feb
parent1ab28c1c339644304333203196c4d95a92a4e08c (diff)
parent92da2de2a91348121d85dad99d85e3657a9bf719 (diff)
Merge branch 'interactive-tls'
-rw-r--r--src/Makefile.am6
-rw-r--r--src/idle-connection.c98
-rw-r--r--src/idle-debug.c1
-rw-r--r--src/idle-debug.h1
-rw-r--r--src/idle-server-connection.c145
-rw-r--r--src/idle-server-connection.h8
-rw-r--r--src/server-tls-channel.c306
-rw-r--r--src/server-tls-channel.h71
-rw-r--r--src/server-tls-manager.c462
-rw-r--r--src/server-tls-manager.h83
-rw-r--r--src/tls-certificate.c344
-rw-r--r--src/tls-certificate.h66
-rw-r--r--tests/twisted/Makefile.am5
-rw-r--r--tests/twisted/connect/connect-close-ssl.py28
-rw-r--r--tests/twisted/connect/connect-fail-ssl.py1
-rw-r--r--tests/twisted/connect/connect-reject-ssl.py29
-rw-r--r--tests/twisted/connect/connect-success-ssl.py8
-rw-r--r--tests/twisted/connect/disconnect-before-socket-connected.py31
-rw-r--r--tests/twisted/connect/disconnect-during-cert-verification.py28
-rw-r--r--tests/twisted/tools/tp-glib.supp390
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
+}