libpurple/sslconn.c

branch
purple-ssl-to-gio
changeset 37623
53718d3c53f0
parent 37417
b29ee022017f
child 37630
95771d0cf853
equal deleted inserted replaced
37622:14d1273cae74 37623:53718d3c53f0
20 */ 20 */
21 #define _PURPLE_SSLCONN_C_ 21 #define _PURPLE_SSLCONN_C_
22 22
23 #include "internal.h" 23 #include "internal.h"
24 24
25 #include "certificate.h"
26 #include "debug.h" 25 #include "debug.h"
27 #include "plugins.h" 26 #include "plugins.h"
28 #include "request.h" 27 #include "request.h"
29 #include "sslconn.h" 28 #include "sslconn.h"
29 #include "tls-certificate.h"
30 30
31 static gboolean _ssl_initialized = FALSE; 31 static gboolean _ssl_initialized = FALSE;
32 static PurpleSslOps *_ssl_ops = NULL; 32 static PurpleSslOps *_ssl_ops = NULL;
33 33
34 static gboolean 34 static gboolean
35 ssl_init(void) 35 ssl_init(void)
36 { 36 {
37 PurplePlugin *plugin; 37 return g_tls_backend_supports_tls(g_tls_backend_get_default());
38 PurpleSslOps *ops; 38 }
39 39
40 if (_ssl_initialized) 40 static void
41 return FALSE; 41 emit_error(PurpleSslConnection *gsc, int error_code)
42 42 {
43 plugin = purple_plugins_find_plugin("core-ssl"); 43 if (gsc->error_cb != NULL)
44 44 gsc->error_cb(gsc, error_code, gsc->connect_cb_data);
45 if (plugin && !purple_plugin_is_loaded(plugin)) 45 }
46 purple_plugin_load(plugin, NULL); 46
47 47 static void
48 ops = purple_ssl_get_ops(); 48 tls_handshake_cb(GObject *source, GAsyncResult *res, gpointer user_data)
49 if ((ops == NULL) || (ops->init == NULL) || (ops->uninit == NULL) || 49 {
50 (ops->connectfunc == NULL) || (ops->close == NULL) || 50 PurpleSslConnection *gsc = user_data;
51 (ops->read == NULL) || (ops->write == NULL)) 51 GError *error = NULL;
52 { 52
53 return FALSE; 53 if (!g_tls_connection_handshake_finish(G_TLS_CONNECTION(source), res,
54 } 54 &error)) {
55 55 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
56 return (_ssl_initialized = ops->init()); 56 /* Connection already closed/freed. Escape. */
57 return;
58 } else if (g_error_matches(error, G_TLS_ERROR,
59 G_TLS_ERROR_HANDSHAKE)) {
60 /* In Gio, a handshake error is because of the cert */
61 emit_error(gsc, PURPLE_SSL_CERTIFICATE_INVALID);
62 } else {
63 /* Report any other errors as handshake failing */
64 emit_error(gsc, PURPLE_SSL_HANDSHAKE_FAILED);
65 }
66
67 purple_ssl_close(gsc);
68 return;
69 }
70
71 gsc->connect_cb(gsc->connect_cb_data, gsc, PURPLE_INPUT_READ);
72 }
73
74 static void
75 tls_connect(PurpleSslConnection *gsc)
76 {
77 GSocket *socket;
78 GSocketConnection *conn;
79 GSocketConnectable *identity;
80 GIOStream *tls_conn;
81 GError *error = NULL;
82
83 g_return_if_fail(gsc->conn == NULL);
84
85 socket = g_socket_new_from_fd(gsc->fd, &error);
86 if (socket == NULL) {
87 emit_error(gsc, PURPLE_SSL_CONNECT_FAILED);
88 purple_ssl_close(gsc);
89 return;
90 }
91
92 conn = g_socket_connection_factory_create_connection(socket);
93 g_object_unref(socket);
94
95 identity = g_network_address_new(gsc->host, gsc->port);
96 tls_conn = g_tls_client_connection_new(G_IO_STREAM(conn), identity,
97 &error);
98 g_object_unref(identity);
99 g_object_unref(conn);
100
101 if (tls_conn == NULL) {
102 emit_error(gsc, PURPLE_SSL_CONNECT_FAILED);
103 purple_ssl_close(gsc);
104 return;
105 }
106
107 gsc->conn = G_TLS_CONNECTION(tls_conn);
108 gsc->cancellable = g_cancellable_new();
109
110 purple_tls_certificate_attach_to_tls_connection(gsc->conn);
111
112 g_tls_connection_handshake_async(gsc->conn, G_PRIORITY_DEFAULT,
113 gsc->cancellable, tls_handshake_cb, gsc);
57 } 114 }
58 115
59 static void 116 static void
60 purple_ssl_connect_cb(gpointer data, gint source, const gchar *error_message) 117 purple_ssl_connect_cb(gpointer data, gint source, const gchar *error_message)
61 { 118 {
62 PurpleSslConnection *gsc; 119 PurpleSslConnection *gsc;
63 PurpleSslOps *ops;
64 120
65 gsc = data; 121 gsc = data;
66 gsc->connect_data = NULL; 122 gsc->connect_data = NULL;
67 123
68 if (source < 0) 124 if (source < 0)
69 { 125 {
70 if (gsc->error_cb != NULL) 126 emit_error(gsc, PURPLE_SSL_CONNECT_FAILED);
71 gsc->error_cb(gsc, PURPLE_SSL_CONNECT_FAILED, gsc->connect_cb_data);
72
73 purple_ssl_close(gsc); 127 purple_ssl_close(gsc);
74 return; 128 return;
75 } 129 }
76 130
77 gsc->fd = source; 131 gsc->fd = source;
78 132
79 ops = purple_ssl_get_ops(); 133 tls_connect(gsc);
80 ops->connectfunc(gsc);
81 } 134 }
82 135
83 PurpleSslConnection * 136 PurpleSslConnection *
84 purple_ssl_connect(PurpleAccount *account, const char *host, int port, 137 purple_ssl_connect(PurpleAccount *account, const char *host, int port,
85 PurpleSslInputFunction func, PurpleSslErrorFunction error_func, 138 PurpleSslInputFunction func, PurpleSslErrorFunction error_func,
113 gsc->port = port; 166 gsc->port = port;
114 gsc->connect_cb_data = data; 167 gsc->connect_cb_data = data;
115 gsc->connect_cb = func; 168 gsc->connect_cb = func;
116 gsc->error_cb = error_func; 169 gsc->error_cb = error_func;
117 170
118 /* TODO: Move this elsewhere */
119 gsc->verifier = purple_certificate_find_verifier("x509","tls_cached");
120
121 gsc->connect_data = purple_proxy_connect(NULL, account, host, port, purple_ssl_connect_cb, gsc); 171 gsc->connect_data = purple_proxy_connect(NULL, account, host, port, purple_ssl_connect_cb, gsc);
122 172
123 if (gsc->connect_data == NULL) 173 if (gsc->connect_data == NULL)
124 { 174 {
125 g_free(gsc->host); 175 g_free(gsc->host);
129 } 179 }
130 180
131 return (PurpleSslConnection *)gsc; 181 return (PurpleSslConnection *)gsc;
132 } 182 }
133 183
134 static void 184 static gboolean
135 recv_cb(gpointer data, gint source, PurpleInputCondition cond) 185 recv_cb(GObject *source, gpointer data)
136 { 186 {
137 PurpleSslConnection *gsc = data; 187 PurpleSslConnection *gsc = data;
138 188
139 gsc->recv_cb(gsc->recv_cb_data, gsc, cond); 189 gsc->recv_cb(gsc->recv_cb_data, gsc, PURPLE_INPUT_READ);
190
191 return TRUE;
140 } 192 }
141 193
142 void 194 void
143 purple_ssl_input_add(PurpleSslConnection *gsc, PurpleSslInputFunction func, 195 purple_ssl_input_add(PurpleSslConnection *gsc, PurpleSslInputFunction func,
144 void *data) 196 void *data)
145 { 197 {
198 GInputStream *input;
199 GSource *source;
200
146 g_return_if_fail(func != NULL); 201 g_return_if_fail(func != NULL);
202 g_return_if_fail(gsc->conn != NULL);
147 203
148 purple_ssl_input_remove(gsc); 204 purple_ssl_input_remove(gsc);
149 205
150 gsc->recv_cb_data = data; 206 gsc->recv_cb_data = data;
151 gsc->recv_cb = func; 207 gsc->recv_cb = func;
152 208
153 gsc->inpa = purple_input_add(gsc->fd, PURPLE_INPUT_READ, recv_cb, gsc); 209 input = g_io_stream_get_input_stream(G_IO_STREAM(gsc->conn));
210 /* Pass NULL for cancellable as we don't want it notified on cancel */
211 source = g_pollable_input_stream_create_source(
212 G_POLLABLE_INPUT_STREAM(input), NULL);
213 g_source_set_callback(source, (GSourceFunc)recv_cb, gsc, NULL);
214 gsc->inpa = g_source_attach(source, NULL);
215 g_source_unref(source);
154 } 216 }
155 217
156 void 218 void
157 purple_ssl_input_remove(PurpleSslConnection *gsc) 219 purple_ssl_input_remove(PurpleSslConnection *gsc)
158 { 220 {
159 if (gsc->inpa > 0) { 221 if (gsc->inpa > 0) {
160 purple_input_remove(gsc->inpa); 222 g_source_remove(gsc->inpa);
161 gsc->inpa = 0; 223 gsc->inpa = 0;
162 } 224 }
163 } 225 }
164 226
165 const gchar * 227 const gchar *
184 PurpleSslErrorFunction error_func, 246 PurpleSslErrorFunction error_func,
185 const char *host, 247 const char *host,
186 void *data) 248 void *data)
187 { 249 {
188 PurpleSslConnection *gsc; 250 PurpleSslConnection *gsc;
189 PurpleSslOps *ops;
190 251
191 g_return_val_if_fail(fd != -1, NULL); 252 g_return_val_if_fail(fd != -1, NULL);
192 g_return_val_if_fail(func != NULL, NULL); 253 g_return_val_if_fail(func != NULL, NULL);
193 254
194 if (!_ssl_initialized) 255 if (!_ssl_initialized)
203 gsc->connect_cb = func; 264 gsc->connect_cb = func;
204 gsc->error_cb = error_func; 265 gsc->error_cb = error_func;
205 gsc->fd = fd; 266 gsc->fd = fd;
206 if(host) 267 if(host)
207 gsc->host = g_strdup(host); 268 gsc->host = g_strdup(host);
208 269 gsc->cancellable = g_cancellable_new();
209 /* TODO: Move this elsewhere */ 270
210 gsc->verifier = purple_certificate_find_verifier("x509","tls_cached"); 271 tls_connect(gsc);
211
212
213 ops = purple_ssl_get_ops();
214 ops->connectfunc(gsc);
215 272
216 return (PurpleSslConnection *)gsc; 273 return (PurpleSslConnection *)gsc;
217 } 274 }
218 275
219 void 276 void
220 purple_ssl_close(PurpleSslConnection *gsc) 277 purple_ssl_close(PurpleSslConnection *gsc)
221 { 278 {
222 PurpleSslOps *ops;
223
224 g_return_if_fail(gsc != NULL); 279 g_return_if_fail(gsc != NULL);
225 280
226 purple_request_close_with_handle(gsc); 281 purple_request_close_with_handle(gsc);
227 purple_notify_close_with_handle(gsc); 282 purple_notify_close_with_handle(gsc);
228 283
229 ops = purple_ssl_get_ops();
230 (ops->close)(gsc);
231
232 if (gsc->connect_data != NULL) 284 if (gsc->connect_data != NULL)
233 purple_proxy_connect_cancel(gsc->connect_data); 285 purple_proxy_connect_cancel(gsc->connect_data);
234 286
235 if (gsc->inpa > 0) 287 if (gsc->inpa > 0)
236 purple_input_remove(gsc->inpa); 288 purple_input_remove(gsc->inpa);
237 289
238 if (gsc->fd >= 0) 290 /* Stop any pending operations */
239 close(gsc->fd); 291 if (G_IS_CANCELLABLE(gsc->cancellable)) {
292 g_cancellable_cancel(gsc->cancellable);
293 g_clear_object(&gsc->cancellable);
294 }
295
296 if (gsc->conn != NULL) {
297 /* Close the stream. Shouldn't take long and it can't
298 * be further cancelled so don't pass a cancellable
299 */
300 g_io_stream_close(G_IO_STREAM(gsc->conn), NULL, NULL);
301 g_clear_object(&gsc->conn);
302 }
240 303
241 g_free(gsc->host); 304 g_free(gsc->host);
242 g_free(gsc); 305 g_free(gsc);
243 } 306 }
244 307
245 size_t 308 size_t
246 purple_ssl_read(PurpleSslConnection *gsc, void *data, size_t len) 309 purple_ssl_read(PurpleSslConnection *gsc, void *data, size_t len)
247 { 310 {
248 PurpleSslOps *ops; 311 GInputStream *input;
312 gssize outlen;
313 GError *error = NULL;
249 314
250 g_return_val_if_fail(gsc != NULL, 0); 315 g_return_val_if_fail(gsc != NULL, 0);
251 g_return_val_if_fail(data != NULL, 0); 316 g_return_val_if_fail(data != NULL, 0);
252 g_return_val_if_fail(len > 0, 0); 317 g_return_val_if_fail(len > 0, 0);
253 318 g_return_val_if_fail(gsc->conn != NULL, 0);
254 ops = purple_ssl_get_ops(); 319
255 return (ops->read)(gsc, data, len); 320 input = g_io_stream_get_input_stream(G_IO_STREAM(gsc->conn));
321 outlen = g_pollable_input_stream_read_nonblocking(
322 G_POLLABLE_INPUT_STREAM(input), data, len,
323 gsc->cancellable, &error);
324
325 if (outlen < 0) {
326 if (outlen == G_IO_ERROR_WOULD_BLOCK) {
327 errno = EAGAIN;
328 }
329
330 g_clear_error(&error);
331 }
332
333 return outlen;
256 } 334 }
257 335
258 size_t 336 size_t
259 purple_ssl_write(PurpleSslConnection *gsc, const void *data, size_t len) 337 purple_ssl_write(PurpleSslConnection *gsc, const void *data, size_t len)
260 { 338 {
261 PurpleSslOps *ops; 339 GOutputStream *output;
340 gssize outlen;
341 GError *error = NULL;
262 342
263 g_return_val_if_fail(gsc != NULL, 0); 343 g_return_val_if_fail(gsc != NULL, 0);
264 g_return_val_if_fail(data != NULL, 0); 344 g_return_val_if_fail(data != NULL, 0);
265 g_return_val_if_fail(len > 0, 0); 345 g_return_val_if_fail(len > 0, 0);
266 346 g_return_val_if_fail(gsc->conn != NULL, 0);
267 ops = purple_ssl_get_ops(); 347
268 return (ops->write)(gsc, data, len); 348 output = g_io_stream_get_output_stream(G_IO_STREAM(gsc->conn));
349 outlen = g_pollable_output_stream_write_nonblocking(
350 G_POLLABLE_OUTPUT_STREAM(output), data, len,
351 gsc->cancellable, &error);
352
353 if (outlen < 0) {
354 if (g_error_matches(error, G_IO_ERROR,
355 G_IO_ERROR_WOULD_BLOCK)) {
356 errno = EAGAIN;
357 }
358
359 g_clear_error(&error);
360 }
361
362 return outlen;
269 } 363 }
270 364
271 GList * 365 GList *
272 purple_ssl_get_peer_certificates(PurpleSslConnection *gsc) 366 purple_ssl_get_peer_certificates(PurpleSslConnection *gsc)
273 { 367 {
274 PurpleSslOps *ops; 368 GTlsCertificate *certificate;
275 369
276 g_return_val_if_fail(gsc != NULL, NULL); 370 g_return_val_if_fail(gsc != NULL, NULL);
277 371 g_return_val_if_fail(gsc->conn != NULL, NULL);
278 ops = purple_ssl_get_ops(); 372
279 return (ops->get_peer_certificates)(gsc); 373 certificate = g_tls_connection_get_peer_certificate(gsc->conn);
374
375 return certificate != NULL ? g_list_append(NULL, certificate) : NULL;
280 } 376 }
281 377
282 void 378 void
283 purple_ssl_set_ops(PurpleSslOps *ops) 379 purple_ssl_set_ops(PurpleSslOps *ops)
284 { 380 {
303 } 399 }
304 400
305 void 401 void
306 purple_ssl_uninit(void) 402 purple_ssl_uninit(void)
307 { 403 {
308 PurpleSslOps *ops;
309
310 if (!_ssl_initialized) 404 if (!_ssl_initialized)
311 return; 405 return;
312 406
313 ops = purple_ssl_get_ops();
314 ops->uninit();
315
316 _ssl_initialized = FALSE; 407 _ssl_initialized = FALSE;
317 } 408 }

mercurial