Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-lan-channel"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <gio/gnetworking.h>
10 : : #include <json-glib/json-glib.h>
11 : : #include <valent.h>
12 : :
13 : : #include "valent-lan-channel.h"
14 : : #include "valent-lan-utils.h"
15 : :
16 : :
17 : : struct _ValentLanChannel
18 : : {
19 : : ValentChannel parent_instance;
20 : :
21 : : char *verification_key;
22 : : char *host;
23 : : uint16_t port;
24 : : };
25 : :
26 [ + + + - ]: 30 : G_DEFINE_FINAL_TYPE (ValentLanChannel, valent_lan_channel, VALENT_TYPE_CHANNEL)
27 : :
28 : : enum {
29 : : PROP_0,
30 : : PROP_CERTIFICATE,
31 : : PROP_HOST,
32 : : PROP_PEER_CERTIFICATE,
33 : : PROP_PORT,
34 : : N_PROPERTIES
35 : : };
36 : :
37 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
38 : :
39 : :
40 : : /*
41 : : * ValentChannel
42 : : */
43 : : static const char *
44 : 2 : valent_lan_channel_get_verification_key (ValentChannel *channel)
45 : : {
46 : 2 : ValentLanChannel *self = VALENT_LAN_CHANNEL (channel);
47 : 4 : g_autoptr (GChecksum) checksum = NULL;
48 [ + - ]: 2 : g_autoptr (GTlsCertificate) cert = NULL;
49 [ + - ]: 2 : g_autoptr (GTlsCertificate) peer_cert = NULL;
50 : 2 : GByteArray *pubkey;
51 : 2 : GByteArray *peer_pubkey;
52 : 2 : size_t cmplen;
53 : :
54 [ + - ]: 2 : if (self->verification_key == NULL)
55 : : {
56 [ + - ]: 2 : if ((cert = valent_lan_channel_ref_certificate (self)) == NULL ||
57 [ - + ]: 2 : (peer_cert = valent_lan_channel_ref_peer_certificate (self)) == NULL)
58 : 0 : g_return_val_if_reached (NULL);
59 : :
60 [ + - ]: 2 : if ((pubkey = valent_certificate_get_public_key (cert)) == NULL ||
61 [ - + ]: 2 : (peer_pubkey = valent_certificate_get_public_key (peer_cert)) == NULL)
62 : 0 : g_return_val_if_reached (NULL);
63 : :
64 : 2 : checksum = g_checksum_new (G_CHECKSUM_SHA256);
65 : 2 : cmplen = MIN (pubkey->len, peer_pubkey->len);
66 : :
67 [ + + ]: 2 : if (memcmp (pubkey->data, peer_pubkey->data, cmplen) > 0)
68 : : {
69 : 1 : g_checksum_update (checksum, pubkey->data, pubkey->len);
70 : 1 : g_checksum_update (checksum, peer_pubkey->data, peer_pubkey->len);
71 : : }
72 : : else
73 : : {
74 : 1 : g_checksum_update (checksum, peer_pubkey->data, peer_pubkey->len);
75 : 1 : g_checksum_update (checksum, pubkey->data, pubkey->len);
76 : : }
77 : :
78 : 2 : self->verification_key = g_strndup (g_checksum_get_string (checksum), 8);
79 : : }
80 : :
81 : 2 : return self->verification_key;
82 : : }
83 : :
84 : : static GIOStream *
85 : 1 : valent_lan_channel_download (ValentChannel *channel,
86 : : JsonNode *packet,
87 : : GCancellable *cancellable,
88 : : GError **error)
89 : : {
90 : 1 : ValentLanChannel *self = VALENT_LAN_CHANNEL (channel);
91 : 1 : JsonObject *info;
92 : 1 : int64_t port;
93 : 1 : goffset size;
94 : 2 : g_autoptr (GSocketClient) client = NULL;
95 [ + - ]: 1 : g_autoptr (GSocketConnection) connection = NULL;
96 [ + - ]: 1 : g_autoptr (GTlsCertificate) certificate = NULL;
97 [ + - ]: 1 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
98 [ + - ]: 1 : g_autofree char *host = NULL;
99 : 1 : g_autoptr (GIOStream) tls_stream = NULL;
100 : :
101 [ + - ]: 1 : g_assert (VALENT_IS_CHANNEL (channel));
102 [ - + ]: 1 : g_assert (VALENT_IS_PACKET (packet));
103 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
104 [ + - - + ]: 1 : g_assert (error == NULL || *error == NULL);
105 : :
106 : : /* Get the payload information */
107 [ + - ]: 1 : if ((info = valent_packet_get_payload_full (packet, &size, error)) == NULL)
108 : : return NULL;
109 : :
110 [ + - ]: 1 : if ((port = json_object_get_int_member (info, "port")) == 0 ||
111 [ - + ]: 1 : (port < VALENT_LAN_TRANSFER_PORT_MIN || port > VALENT_LAN_TRANSFER_PORT_MAX))
112 : : {
113 : 0 : g_set_error (error,
114 : : VALENT_PACKET_ERROR,
115 : : VALENT_PACKET_ERROR_INVALID_FIELD,
116 : : "expected \"port\" field holding a uint16 between %u-%u",
117 : : VALENT_LAN_TRANSFER_PORT_MIN,
118 : : VALENT_LAN_TRANSFER_PORT_MAX);
119 : 0 : return NULL;
120 : : }
121 : :
122 : : /* Open the outgoing connection */
123 : 1 : host = valent_lan_channel_dup_host (self);
124 : 1 : client = g_object_new (G_TYPE_SOCKET_CLIENT,
125 : : "enable-proxy", FALSE,
126 : : NULL);
127 : 1 : connection = g_socket_client_connect_to_host (client,
128 : : host,
129 : : (uint16_t)port,
130 : : cancellable,
131 : : error);
132 : :
133 [ + - ]: 1 : if (connection == NULL)
134 : : return NULL;
135 : :
136 : : /* We're the TLS client when downloading */
137 : 1 : certificate = valent_lan_channel_ref_certificate (self);
138 : 1 : peer_certificate = valent_lan_channel_ref_peer_certificate (self);
139 : 1 : tls_stream = valent_lan_encrypt_client (connection,
140 : : certificate,
141 : : peer_certificate,
142 : : cancellable,
143 : : error);
144 : :
145 [ - + ]: 1 : if (tls_stream == NULL)
146 : : {
147 : 0 : g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
148 : 0 : return NULL;
149 : : }
150 : :
151 : : return g_steal_pointer (&tls_stream);
152 : : }
153 : :
154 : : static GIOStream *
155 : 1 : valent_lan_channel_upload (ValentChannel *channel,
156 : : JsonNode *packet,
157 : : GCancellable *cancellable,
158 : : GError **error)
159 : : {
160 : 1 : ValentLanChannel *self = VALENT_LAN_CHANNEL (channel);
161 : 1 : JsonObject *info;
162 : 2 : g_autoptr (GSocketListener) listener = NULL;
163 [ + - ]: 1 : g_autoptr (GSocketConnection) connection = NULL;
164 : 1 : uint16_t port = VALENT_LAN_TRANSFER_PORT_MIN;
165 [ + - ]: 1 : g_autoptr (GTlsCertificate) certificate = NULL;
166 [ + - ]: 1 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
167 [ + - ]: 1 : g_autoptr (GIOStream) tls_stream = NULL;
168 : :
169 [ + - ]: 1 : g_assert (VALENT_IS_CHANNEL (channel));
170 [ - + ]: 1 : g_assert (VALENT_IS_PACKET (packet));
171 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
172 [ + - - + ]: 1 : g_assert (error == NULL || *error == NULL);
173 : :
174 : : /* Find an open port */
175 : 1 : listener = g_socket_listener_new ();
176 : :
177 [ - + ]: 1 : while (!g_socket_listener_add_inet_port (listener, port, NULL, error))
178 : : {
179 [ # # ]: 0 : if (port >= VALENT_LAN_TRANSFER_PORT_MAX)
180 : : return NULL;
181 : :
182 : 0 : port++;
183 : 0 : g_clear_error (error);
184 : : }
185 : :
186 : : /* Set the payload information */
187 : 1 : info = json_object_new();
188 : 1 : json_object_set_int_member (info, "port", (int64_t)port);
189 : 1 : valent_packet_set_payload_info (packet, info);
190 : :
191 : : /* Notify the device and accept the incoming connection */
192 : 1 : valent_channel_write_packet (channel, packet, cancellable, NULL, NULL);
193 : :
194 : 1 : connection = g_socket_listener_accept (listener, NULL, cancellable, error);
195 : 1 : g_socket_listener_close (listener);
196 : :
197 [ - + ]: 1 : if (connection == NULL)
198 : : return NULL;
199 : :
200 : : /* We're the TLS server when uploading */
201 : 1 : certificate = valent_lan_channel_ref_certificate (self);
202 : 1 : peer_certificate = valent_lan_channel_ref_peer_certificate (self);
203 : 1 : tls_stream = valent_lan_encrypt_server (connection,
204 : : certificate,
205 : : peer_certificate,
206 : : cancellable,
207 : : error);
208 : :
209 [ - + ]: 1 : if (tls_stream == NULL)
210 : : {
211 : 0 : g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
212 : 0 : return NULL;
213 : : }
214 : :
215 : : return g_steal_pointer (&tls_stream);
216 : : }
217 : :
218 : : static void
219 : 0 : valent_lan_channel_store_data (ValentChannel *channel,
220 : : ValentContext *context)
221 : : {
222 : 0 : g_autoptr (GTlsCertificate) certificate = NULL;
223 [ # # ]: 0 : g_autofree char *certificate_pem = NULL;
224 : 0 : g_autoptr (GFile) certificate_file = NULL;
225 [ # # ]: 0 : g_autoptr (GError) error = NULL;
226 : :
227 [ # # ]: 0 : g_assert (VALENT_IS_LAN_CHANNEL (channel));
228 [ # # ]: 0 : g_assert (VALENT_IS_CONTEXT (context));
229 : :
230 : : /* Chain-up first */
231 : 0 : VALENT_CHANNEL_CLASS (valent_lan_channel_parent_class)->store_data (channel,
232 : : context);
233 : :
234 : : /* Save the peer certificate */
235 : 0 : g_object_get (channel, "peer-certificate", &certificate, NULL);
236 : 0 : g_object_get (certificate, "certificate-pem", &certificate_pem, NULL);
237 : :
238 : 0 : certificate_file = valent_context_get_config_file (context, "certificate.pem");
239 : 0 : g_file_set_contents_full (g_file_peek_path (certificate_file),
240 : : certificate_pem,
241 : 0 : strlen (certificate_pem),
242 : : G_FILE_SET_CONTENTS_DURABLE,
243 : : 0600,
244 : : &error);
245 : :
246 [ # # ]: 0 : if (error != NULL)
247 : : {
248 : 0 : g_warning ("%s(): failed to write \"%s\": %s",
249 : : G_STRFUNC,
250 : : g_file_peek_path (certificate_file),
251 : : error->message);
252 : : }
253 : 0 : }
254 : :
255 : : /*
256 : : * GObject
257 : : */
258 : : static void
259 : 6 : valent_lan_channel_finalize (GObject *object)
260 : : {
261 : 6 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
262 : :
263 [ + - ]: 6 : g_clear_pointer (&self->host, g_free);
264 [ + + ]: 6 : g_clear_pointer (&self->verification_key, g_free);
265 : :
266 : 6 : G_OBJECT_CLASS (valent_lan_channel_parent_class)->finalize (object);
267 : 6 : }
268 : :
269 : : static void
270 : 4 : valent_lan_channel_get_property (GObject *object,
271 : : guint prop_id,
272 : : GValue *value,
273 : : GParamSpec *pspec)
274 : : {
275 : 4 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
276 : :
277 [ + + + + : 4 : switch (prop_id)
- ]
278 : : {
279 : 1 : case PROP_CERTIFICATE:
280 : 1 : g_value_take_object (value, valent_lan_channel_ref_certificate (self));
281 : 1 : break;
282 : :
283 : 1 : case PROP_HOST:
284 : 1 : g_value_take_string (value, valent_lan_channel_dup_host (self));
285 : 1 : break;
286 : :
287 : 1 : case PROP_PEER_CERTIFICATE:
288 : 1 : g_value_take_object (value, valent_lan_channel_ref_peer_certificate (self));
289 : 1 : break;
290 : :
291 : 1 : case PROP_PORT:
292 : 1 : g_value_set_uint (value, valent_lan_channel_get_port (self));
293 : 1 : break;
294 : :
295 : 0 : default:
296 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
297 : : }
298 : 4 : }
299 : :
300 : : static void
301 : 12 : valent_lan_channel_set_property (GObject *object,
302 : : guint prop_id,
303 : : const GValue *value,
304 : : GParamSpec *pspec)
305 : : {
306 : 12 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
307 : :
308 [ + + - ]: 12 : switch (prop_id)
309 : : {
310 : : case PROP_HOST:
311 : 6 : valent_object_lock (VALENT_OBJECT (self));
312 : 6 : self->host = g_value_dup_string (value);
313 : 6 : valent_object_unlock (VALENT_OBJECT (self));
314 : 6 : break;
315 : :
316 : : case PROP_PORT:
317 : 6 : valent_object_lock (VALENT_OBJECT (self));
318 : 6 : self->port = g_value_get_uint (value);
319 : 6 : valent_object_unlock (VALENT_OBJECT (self));
320 : 6 : break;
321 : :
322 : 0 : default:
323 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
324 : : }
325 : 12 : }
326 : :
327 : : static void
328 : 1 : valent_lan_channel_class_init (ValentLanChannelClass *klass)
329 : : {
330 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
331 : 1 : ValentChannelClass *channel_class = VALENT_CHANNEL_CLASS (klass);
332 : :
333 : 1 : object_class->finalize = valent_lan_channel_finalize;
334 : 1 : object_class->get_property = valent_lan_channel_get_property;
335 : 1 : object_class->set_property = valent_lan_channel_set_property;
336 : :
337 : 1 : channel_class->get_verification_key = valent_lan_channel_get_verification_key;
338 : 1 : channel_class->download = valent_lan_channel_download;
339 : 1 : channel_class->upload = valent_lan_channel_upload;
340 : 1 : channel_class->store_data = valent_lan_channel_store_data;
341 : :
342 : : /**
343 : : * ValentLanChannel:certificate:
344 : : *
345 : : * The TLS certificate.
346 : : */
347 : 2 : properties [PROP_CERTIFICATE] =
348 : 1 : g_param_spec_object ("certificate", NULL, NULL,
349 : : G_TYPE_TLS_CERTIFICATE,
350 : : (G_PARAM_READABLE |
351 : : G_PARAM_EXPLICIT_NOTIFY |
352 : : G_PARAM_STATIC_STRINGS));
353 : :
354 : : /**
355 : : * ValentLanChannel:host:
356 : : *
357 : : * The remote TCP/IP address for the channel.
358 : : */
359 : 2 : properties [PROP_HOST] =
360 : 1 : g_param_spec_string ("host", NULL, NULL,
361 : : NULL,
362 : : (G_PARAM_READWRITE |
363 : : G_PARAM_CONSTRUCT_ONLY |
364 : : G_PARAM_EXPLICIT_NOTIFY |
365 : : G_PARAM_STATIC_STRINGS));
366 : :
367 : : /**
368 : : * ValentLanChannel:peer-certificate:
369 : : *
370 : : * The peer TLS certificate.
371 : : */
372 : 2 : properties [PROP_PEER_CERTIFICATE] =
373 : 1 : g_param_spec_object ("peer-certificate", NULL, NULL,
374 : : G_TYPE_TLS_CERTIFICATE,
375 : : (G_PARAM_READABLE |
376 : : G_PARAM_EXPLICIT_NOTIFY |
377 : : G_PARAM_STATIC_STRINGS));
378 : :
379 : : /**
380 : : * ValentLanChannel:port:
381 : : *
382 : : * The remote TCP/IP port for the channel.
383 : : */
384 : 2 : properties [PROP_PORT] =
385 : 1 : g_param_spec_uint ("port", NULL, NULL,
386 : : 0, G_MAXUINT16,
387 : : VALENT_LAN_PROTOCOL_PORT,
388 : : (G_PARAM_READWRITE |
389 : : G_PARAM_CONSTRUCT_ONLY |
390 : : G_PARAM_EXPLICIT_NOTIFY |
391 : : G_PARAM_STATIC_STRINGS));
392 : :
393 : 1 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
394 : 1 : }
395 : :
396 : : static void
397 : 6 : valent_lan_channel_init (ValentLanChannel *self)
398 : : {
399 : 6 : }
400 : :
401 : : /**
402 : : * valent_lan_channel_ref_certificate:
403 : : * @self: a `ValentLanChannel`
404 : : *
405 : : * Get the TLS certificate.
406 : : *
407 : : * Returns: (transfer full) (nullable): a `GTlsCertificate`
408 : : */
409 : : GTlsCertificate *
410 : 7 : valent_lan_channel_ref_certificate (ValentLanChannel *self)
411 : : {
412 : 14 : g_autoptr (GIOStream) base_stream = NULL;
413 : 7 : GTlsCertificate *ret = NULL;
414 : :
415 [ + - ]: 7 : g_return_val_if_fail (VALENT_IS_LAN_CHANNEL (self), NULL);
416 : :
417 : 7 : base_stream = valent_channel_ref_base_stream (VALENT_CHANNEL (self));
418 : :
419 [ + - ]: 7 : if (base_stream != NULL)
420 : 7 : g_object_get (base_stream, "certificate", &ret, NULL);
421 : :
422 [ - + ]: 7 : return g_steal_pointer (&ret);
423 : : }
424 : :
425 : : /**
426 : : * valent_lan_channel_ref_peer_certificate:
427 : : * @self: a `ValentLanChannel`
428 : : *
429 : : * Get the peer TLS certificate.
430 : : *
431 : : * Returns: (transfer full) (nullable): a `GTlsCertificate`
432 : : */
433 : : GTlsCertificate *
434 : 10 : valent_lan_channel_ref_peer_certificate (ValentLanChannel *self)
435 : : {
436 : 20 : g_autoptr (GIOStream) base_stream = NULL;
437 : 10 : GTlsCertificate *ret = NULL;
438 : :
439 [ + - ]: 10 : g_return_val_if_fail (VALENT_IS_LAN_CHANNEL (self), NULL);
440 : :
441 : 10 : base_stream = valent_channel_ref_base_stream (VALENT_CHANNEL (self));
442 : :
443 [ + - ]: 10 : if (base_stream != NULL)
444 : 10 : g_object_get (base_stream, "peer-certificate", &ret, NULL);
445 : :
446 [ - + ]: 10 : return g_steal_pointer (&ret);
447 : : }
448 : :
449 : : /**
450 : : * valent_lan_channel_dup_host:
451 : : * @self: a `ValentLanChannel`
452 : : *
453 : : * Get the host address.
454 : : *
455 : : * Returns: (transfer full) (nullable): the remote host address
456 : : */
457 : : char *
458 : 2 : valent_lan_channel_dup_host (ValentLanChannel *self)
459 : : {
460 : 2 : char *ret = NULL;
461 : :
462 [ + - ]: 2 : g_return_val_if_fail (VALENT_LAN_CHANNEL (self), NULL);
463 : :
464 : 2 : valent_object_lock (VALENT_OBJECT (self));
465 [ - + ]: 2 : ret = g_strdup (self->host);
466 : 2 : valent_object_unlock (VALENT_OBJECT (self));
467 : :
468 : 2 : return g_steal_pointer (&ret);
469 : : }
470 : :
471 : : /**
472 : : * valent_lan_channel_get_port:
473 : : * @self: a `ValentLanChannel`
474 : : *
475 : : * Get the host port.
476 : : *
477 : : * Returns: the remote host port
478 : : */
479 : : uint16_t
480 : 1 : valent_lan_channel_get_port (ValentLanChannel *self)
481 : : {
482 : 1 : uint16_t ret;
483 : :
484 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_LAN_CHANNEL (self), 0);
485 : :
486 : 1 : valent_object_lock (VALENT_OBJECT (self));
487 : 1 : ret = self->port;
488 : 1 : valent_object_unlock (VALENT_OBJECT (self));
489 : :
490 : 1 : return ret;
491 : : }
492 : :
|