| 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, |
| 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 * |
| 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 { |