~ubuntu-branches/ubuntu/trusty/telepathy-idle/trusty

« back to all changes in this revision

Viewing changes to src/idle-server-connection.c

  • Committer: Package Import Robot
  • Author(s): Simon McVittie
  • Date: 2013-05-01 15:52:26 UTC
  • mfrom: (1.3.6)
  • Revision ID: package-import@ubuntu.com-20130501155226-ttpmql3jetet34iu
Tags: 0.1.16-1
* New upstream release
  - adds support for interactive TLS certificate verification
    (Closes: #706270)
* Add a patch to avoid use of a telepathy-glib 0.20 header, to make
  this easy to backport to wheezy

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
 
32
32
#define IDLE_DEBUG_FLAG IDLE_DEBUG_NETWORK
33
33
#include "idle-connection.h"
 
34
#include "server-tls-manager.h"
34
35
#include "idle-debug.h"
35
36
 
36
37
typedef struct _IdleServerConnectionPrivate IdleServerConnectionPrivate;
40
41
G_DEFINE_TYPE(IdleServerConnection, idle_server_connection, G_TYPE_OBJECT)
41
42
 
42
43
enum {
43
 
        STATUS_CHANGED,
 
44
        DISCONNECTED,
44
45
        RECEIVED,
45
46
        LAST_SIGNAL
46
47
};
47
48
 
48
49
enum {
49
50
        PROP_HOST = 1,
50
 
        PROP_PORT
 
51
        PROP_PORT,
 
52
        PROP_TLS_MANAGER
51
53
};
52
54
 
 
55
typedef enum {
 
56
        SERVER_CONNECTION_STATE_NOT_CONNECTED,
 
57
        SERVER_CONNECTION_STATE_CONNECTING,
 
58
        SERVER_CONNECTION_STATE_CONNECTED
 
59
} IdleServerConnectionState;
 
60
 
53
61
struct _IdleServerConnectionPrivate {
54
62
        gchar *host;
55
63
        guint16 port;
66
74
        GCancellable *cancellable;
67
75
 
68
76
        IdleServerConnectionState state;
 
77
        IdleServerTLSManager *tls_manager;
 
78
        GAsyncQueue *certificate_queue;
69
79
};
70
80
 
71
81
static GObject *idle_server_connection_constructor(GType type, guint n_props, GObjectConstructParam *props);
81
91
        priv->socket_client = g_socket_client_new();
82
92
 
83
93
        priv->state = SERVER_CONNECTION_STATE_NOT_CONNECTED;
 
94
        priv->certificate_queue = g_async_queue_new ();
84
95
}
85
96
 
86
97
static GObject *idle_server_connection_constructor(GType type, guint n_props, GObjectConstructParam *props) {
105
116
        IdleServerConnection *conn = IDLE_SERVER_CONNECTION(obj);
106
117
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
107
118
 
 
119
        g_async_queue_unref (priv->certificate_queue);
108
120
        g_free(priv->host);
109
121
}
110
122
 
121
133
                        g_value_set_uint(value, priv->port);
122
134
                        break;
123
135
 
 
136
                case PROP_TLS_MANAGER:
 
137
                        g_value_set_object(value, priv->tls_manager);
 
138
                        break;
 
139
 
124
140
                default:
125
141
                        G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
126
142
                        break;
144
160
                        priv->port = (guint16) g_value_get_uint(value);
145
161
                        break;
146
162
 
 
163
                case PROP_TLS_MANAGER:
 
164
                        priv->tls_manager = g_value_dup_object(value);
 
165
                        break;
 
166
 
147
167
                default:
148
168
                        G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
149
169
                        break;
183
203
 
184
204
        g_object_class_install_property(object_class, PROP_PORT, pspec);
185
205
 
186
 
        signals[STATUS_CHANGED] = g_signal_new("status-changed",
 
206
        pspec = g_param_spec_object("tls-manager", "TLS Manager",
 
207
                                                          "TLS manager for interactive certificate checking",
 
208
                                                          IDLE_TYPE_SERVER_TLS_MANAGER,
 
209
                                                          G_PARAM_READWRITE|
 
210
                                                          G_PARAM_STATIC_STRINGS);
 
211
 
 
212
        g_object_class_install_property(object_class, PROP_TLS_MANAGER, pspec);
 
213
 
 
214
        signals[DISCONNECTED] = g_signal_new("disconnected",
187
215
                                                G_OBJECT_CLASS_TYPE(klass),
188
216
                                                G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
189
217
                                                0,
190
218
                                                NULL, NULL,
191
219
                                                g_cclosure_marshal_generic,
192
 
                                                G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
 
220
                                                G_TYPE_NONE, 1, G_TYPE_UINT);
193
221
 
194
222
        signals[RECEIVED] = g_signal_new("received",
195
223
                                                G_OBJECT_CLASS_TYPE(klass),
207
235
        if (state == priv->state)
208
236
                return;
209
237
 
210
 
        IDLE_DEBUG("emitting status-changed, state %u, reason %u", state, reason);
211
 
 
 
238
        IDLE_DEBUG("moving to state %u, reason %u", state, reason);
212
239
        priv->state = state;
213
 
        g_signal_emit(conn, signals[STATUS_CHANGED], 0, state, reason);
 
240
 
 
241
        if (state == SERVER_CONNECTION_STATE_NOT_CONNECTED)
 
242
                g_signal_emit(conn, signals[DISCONNECTED], 0, reason);
214
243
}
215
244
 
216
245
static void _input_stream_read(IdleServerConnection *conn, GInputStream *input_stream, GAsyncReadyCallback callback) {
254
283
}
255
284
 
256
285
static void _connect_to_host_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) {
257
 
        GSocketClient *socket_client = G_SOCKET_CLIENT(source_object);
 
286
        GSimpleAsyncResult *task = G_SIMPLE_ASYNC_RESULT (res);
258
287
        GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(user_data);
259
288
        IdleServerConnection *conn = IDLE_SERVER_CONNECTION(g_async_result_get_source_object(G_ASYNC_RESULT(result)));
260
289
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
265
294
        gint socket_fd;
266
295
        GError *error = NULL;
267
296
 
268
 
        socket_connection = g_socket_client_connect_to_host_finish(socket_client, res, &error);
269
 
        if (socket_connection == NULL) {
 
297
        if (g_simple_async_result_propagate_error (task, &error)) {
270
298
                IDLE_DEBUG("g_socket_client_connect_to_host failed: %s", error->message);
271
299
                g_simple_async_result_set_error(result, TP_ERROR, TP_ERROR_NETWORK_ERROR, "%s", error->message);
272
300
                g_error_free(error);
275
303
                goto cleanup;
276
304
        }
277
305
 
 
306
        socket_connection = g_object_ref (g_simple_async_result_get_op_res_gpointer (task));
278
307
        socket_ = g_socket_connection_get_socket(socket_connection);
279
308
        g_socket_set_keepalive(socket_, TRUE);
280
309
 
292
321
cleanup:
293
322
        g_simple_async_result_complete(result);
294
323
        g_object_unref(result);
 
324
        g_object_unref(task);
 
325
}
 
326
 
 
327
 
 
328
#define CERT_ACCEPTED 1
 
329
#define CERT_REJECTED 2
 
330
struct CertData {
 
331
        IdleServerConnection *self;
 
332
        GTlsCertificate *certificate;
 
333
};
 
334
 
 
335
static void _certificate_verified (GObject *source, GAsyncResult *res, gpointer user_data)
 
336
{
 
337
        IdleServerConnection *self = user_data;
 
338
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(self);
 
339
        gboolean ret;
 
340
 
 
341
        ret = idle_server_tls_manager_verify_finish (IDLE_SERVER_TLS_MANAGER (source), res, NULL);
 
342
        g_async_queue_push (priv->certificate_queue, GINT_TO_POINTER (ret ?  CERT_ACCEPTED : CERT_REJECTED));
 
343
}
 
344
 
 
345
static gboolean
 
346
_check_certificate_interactively (gpointer data) {
 
347
        struct CertData *d = data;
 
348
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(d->self);
 
349
 
 
350
        idle_server_tls_manager_verify_async (priv->tls_manager, d->certificate, priv->host, _certificate_verified, d->self);
 
351
        return FALSE;
 
352
}
 
353
 
 
354
static gboolean _accept_certificate_request (GTlsConnection *tls_connection, GTlsCertificate *peer_cert, GTlsCertificateFlags errors, IdleServerConnection *conn)
 
355
{
 
356
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
 
357
        struct CertData d;
 
358
        gpointer result;
 
359
 
 
360
        /* Requested to check the extra errors from an ssl certificate,
 
361
         * Need to bounce this back into the main thread so we can ask the UI
 
362
         * on dbus */
 
363
        IDLE_DEBUG ("Requested to validate certificate");
 
364
 
 
365
        d.self = conn;
 
366
        d.certificate = peer_cert;
 
367
 
 
368
        g_idle_add (_check_certificate_interactively, &d);
 
369
        result = g_async_queue_pop (priv->certificate_queue);
 
370
 
 
371
        return GPOINTER_TO_INT (result) == CERT_ACCEPTED;
 
372
}
 
373
 
 
374
static void _connect_event_cb (GSocketClient *client, GSocketClientEvent event, GSocketConnectable *connectable, GIOStream *connection, gpointer user_data)
 
375
{
 
376
        if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING)
 
377
                return;
 
378
 
 
379
        g_signal_connect (connection, "accept-certificate", G_CALLBACK (_accept_certificate_request), user_data);
 
380
}
 
381
 
 
382
static void _connect_in_thread (GSimpleAsyncResult *task, GObject *source_object, GCancellable *cancellable)
 
383
{
 
384
        IdleServerConnection *conn = IDLE_SERVER_CONNECTION (source_object);
 
385
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
 
386
        GError *error = NULL;
 
387
        GSocketConnection *socket_connection;
 
388
        gulong event_id;
 
389
 
 
390
        event_id = g_signal_connect (priv->socket_client, "event",
 
391
                G_CALLBACK (_connect_event_cb), conn);
 
392
        socket_connection = g_socket_client_connect_to_host (priv->socket_client, priv->host, priv->port, cancellable, &error);
 
393
        g_signal_handler_disconnect (priv->socket_client, event_id);
 
394
 
 
395
        if (socket_connection != NULL)
 
396
                g_simple_async_result_set_op_res_gpointer (task, socket_connection, g_object_unref);
 
397
        else
 
398
                g_simple_async_result_take_error (task, error);
295
399
}
296
400
 
297
401
void idle_server_connection_connect_async(IdleServerConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) {
298
402
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
299
403
        GSimpleAsyncResult *result;
 
404
        GSimpleAsyncResult *task;
300
405
 
301
406
        if (priv->state != SERVER_CONNECTION_STATE_NOT_CONNECTED) {
302
407
                IDLE_DEBUG("already connecting or connected!");
326
431
        }
327
432
 
328
433
        result = g_simple_async_result_new(G_OBJECT(conn), callback, user_data, idle_server_connection_connect_async);
329
 
        g_socket_client_connect_to_host_async(priv->socket_client, priv->host, priv->port, cancellable, _connect_to_host_ready, result);
 
434
 
 
435
        task = g_simple_async_result_new (G_OBJECT (conn), _connect_to_host_ready, result, NULL);
 
436
        g_simple_async_result_run_in_thread (task, _connect_in_thread, G_PRIORITY_DEFAULT, cancellable);
 
437
 
330
438
        change_state(conn, SERVER_CONNECTION_STATE_CONNECTING, SERVER_CONNECTION_STATE_REASON_REQUESTED);
331
439
}
332
440
 
497
605
        return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT(result), error);
498
606
}
499
607
 
500
 
IdleServerConnectionState idle_server_connection_get_state(IdleServerConnection *conn) {
 
608
gboolean idle_server_connection_is_connected(IdleServerConnection *conn) {
501
609
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
502
 
        return priv->state;
 
610
 
 
611
        return priv->state == SERVER_CONNECTION_STATE_CONNECTED;
503
612
}
504
613
 
505
614
void idle_server_connection_set_tls(IdleServerConnection *conn, gboolean tls) {
506
615
        IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
507
616
        g_socket_client_set_tls(priv->socket_client, tls);
508
 
 
509
 
        /* The regression tests don't have a CA-issued certificate,
510
 
         * oddly enough. */
511
 
        if (!tp_strdiff (g_getenv ("IDLE_TEST_BE_VULNERABLE_TO_MAN_IN_THE_MIDDLE_ATTACKS"), "vulnerable")) {
512
 
                g_socket_client_set_tls_validation_flags(priv->socket_client,
513
 
                        G_TLS_CERTIFICATE_VALIDATE_ALL
514
 
                        & ~G_TLS_CERTIFICATE_UNKNOWN_CA
515
 
                        & ~G_TLS_CERTIFICATE_BAD_IDENTITY
516
 
                        & ~G_TLS_CERTIFICATE_EXPIRED);
517
 
        }
518
617
}