1 /* GDBus - GLib D-Bus Library
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: David Zeuthen <davidz@redhat.com>
21 */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <errno.h>
29
30 #include "gioerror.h"
31 #include "gdbusutils.h"
32 #include "gdbusaddress.h"
33 #include "gdbuserror.h"
34 #include "gioenumtypes.h"
35 #include "glib-private.h"
36 #include "gnetworkaddress.h"
37 #include "gsocketclient.h"
38 #include "giostream.h"
39 #include "gasyncresult.h"
40 #include "gtask.h"
41 #include "glib-private.h"
42 #include "gdbusprivate.h"
43 #include "gstdio.h"
44
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <gio/gunixsocketaddress.h>
51
52 #ifdef G_OS_WIN32
53 #include <windows.h>
54 #endif
55
56 #ifdef G_OS_WIN32
57 #define FO_CLOEXEC ""
58 #else
59 #define FO_CLOEXEC "e"
60 #endif
61
62 #include "glibintl.h"
63
64 /**
65 * GDBusAddress:
66 *
67 * Routines for working with D-Bus addresses. A D-Bus address is a string
68 * like `unix:tmpdir=/tmp/my-app-name`. The exact format of addresses
69 * is explained in detail in the
70 * [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
71 *
72 * TCP D-Bus connections are supported, but accessing them via a proxy is
73 * currently not supported.
74 *
75 * Since GLib 2.72, `unix:` addresses are supported on Windows with `AF_UNIX`
76 * support (Windows 10).
77 */
78
79 static gchar *get_session_address_platform_specific (GError **error);
80 static gchar *get_session_address_dbus_launch (GError **error);
81
82 /* ---------------------------------------------------------------------------------------------------- */
83
84 /**
85 * g_dbus_is_address:
86 * @string: A string.
87 *
88 * Checks if @string is a
89 * [D-Bus address](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
90 *
91 * This doesn't check if @string is actually supported by #GDBusServer
92 * or #GDBusConnection - use g_dbus_is_supported_address() to do more
93 * checks.
94 *
95 * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
96 *
97 * Since: 2.26
98 */
99 gboolean
100 g_dbus_is_address (const gchar *string)
101 {
102 guint n;
103 gchar **a;
104 gboolean ret;
105
106 ret = FALSE;
107
108 g_return_val_if_fail (string != NULL, FALSE);
109
110 a = g_strsplit (string, ";", 0);
111 if (a[0] == NULL)
112 goto out;
113
114 for (n = 0; a[n] != NULL; n++)
115 {
116 if (!_g_dbus_address_parse_entry (a[n],
117 NULL,
118 NULL,
119 NULL))
120 goto out;
121 }
122
123 ret = TRUE;
124
125 out:
126 g_strfreev (a);
127 return ret;
128 }
129
130 static gboolean
131 is_valid_unix (const gchar *address_entry,
132 GHashTable *key_value_pairs,
133 GError **error)
134 {
135 gboolean ret;
136 GPtrArray *keys;
137 const gchar *path;
138 const gchar *dir;
139 const gchar *tmpdir;
140 const gchar *abstract;
141
142 ret = FALSE;
143 path = NULL;
144 dir = NULL;
145 tmpdir = NULL;
146 abstract = NULL;
147
148 keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
149 for (guint i = 0; i < keys->len; ++i)
150 {
151 const gchar *key = g_ptr_array_index (keys, i);
152 if (g_strcmp0 (key, "path") == 0)
153 path = g_hash_table_lookup (key_value_pairs, key);
154 else if (g_strcmp0 (key, "dir") == 0)
155 dir = g_hash_table_lookup (key_value_pairs, key);
156 else if (g_strcmp0 (key, "tmpdir") == 0)
157 tmpdir = g_hash_table_lookup (key_value_pairs, key);
158 else if (g_strcmp0 (key, "abstract") == 0)
159 abstract = g_hash_table_lookup (key_value_pairs, key);
160 else if (g_strcmp0 (key, "guid") != 0)
161 {
162 g_set_error (error,
163 G_IO_ERROR,
164 G_IO_ERROR_INVALID_ARGUMENT,
165 _("Unsupported key “%s” in address entry “%s”"),
166 key,
167 address_entry);
168 goto out;
169 }
170 }
171
172 /* Exactly one key must be set */
173 if ((path != NULL) + (dir != NULL) + (tmpdir != NULL) + (abstract != NULL) > 1)
174 {
175 g_set_error (error,
176 G_IO_ERROR,
177 G_IO_ERROR_INVALID_ARGUMENT,
178 _("Meaningless key/value pair combination in address entry “%s”"),
179 address_entry);
180 goto out;
181 }
182 else if (path == NULL && dir == NULL && tmpdir == NULL && abstract == NULL)
183 {
184 g_set_error (error,
185 G_IO_ERROR,
186 G_IO_ERROR_INVALID_ARGUMENT,
187 _("Address “%s” is invalid (need exactly one of path, dir, tmpdir, or abstract keys)"),
188 address_entry);
189 goto out;
190 }
191
192 ret = TRUE;
193
194 out:
195 g_ptr_array_unref (keys);
196
197 return ret;
198 }
199
200 static gboolean
201 is_valid_nonce_tcp (const gchar *address_entry,
202 GHashTable *key_value_pairs,
203 GError **error)
204 {
205 gboolean ret;
206 GPtrArray *keys;
207 const gchar *host;
208 const gchar *port;
209 const gchar *family;
210 const gchar *nonce_file;
211 gint port_num;
212 gchar *endp;
213
214 ret = FALSE;
215 host = NULL;
216 port = NULL;
217 family = NULL;
218 nonce_file = NULL;
219
220 keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
221 for (guint i = 0; i < keys->len; ++i)
222 {
223 const gchar *key = g_ptr_array_index (keys, i);
224 if (g_strcmp0 (key, "host") == 0)
225 host = g_hash_table_lookup (key_value_pairs, key);
226 else if (g_strcmp0 (key, "port") == 0)
227 port = g_hash_table_lookup (key_value_pairs, key);
228 else if (g_strcmp0 (key, "family") == 0)
229 family = g_hash_table_lookup (key_value_pairs, key);
230 else if (g_strcmp0 (key, "noncefile") == 0)
231 nonce_file = g_hash_table_lookup (key_value_pairs, key);
232 else if (g_strcmp0 (key, "guid") != 0)
233 {
234 g_set_error (error,
235 G_IO_ERROR,
236 G_IO_ERROR_INVALID_ARGUMENT,
237 _("Unsupported key “%s” in address entry “%s”"),
238 key,
239 address_entry);
240 goto out;
241 }
242 }
243
244 if (port != NULL)
245 {
246 port_num = strtol (port, &endp, 10);
247 if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
248 {
249 g_set_error (error,
250 G_IO_ERROR,
251 G_IO_ERROR_INVALID_ARGUMENT,
252 _("Error in address “%s” — the “%s” attribute is malformed"),
253 address_entry, "port");
254 goto out;
255 }
256 }
257
258 if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
259 {
260 g_set_error (error,
261 G_IO_ERROR,
262 G_IO_ERROR_INVALID_ARGUMENT,
263 _("Error in address “%s” — the “%s” attribute is malformed"),
264 address_entry, "family");
265 goto out;
266 }
267
268 if (host != NULL)
269 {
270 /* TODO: validate host */
271 }
272
273 if (nonce_file != NULL && *nonce_file == '\0')
274 {
275 g_set_error (error,
276 G_IO_ERROR,
277 G_IO_ERROR_INVALID_ARGUMENT,
278 _("Error in address “%s” — the “%s” attribute is malformed"),
279 address_entry, "noncefile");
280 goto out;
281 }
282
283 ret = TRUE;
284
285 out:
286 g_ptr_array_unref (keys);
287
288 return ret;
289 }
290
291 static gboolean
292 is_valid_tcp (const gchar *address_entry,
293 GHashTable *key_value_pairs,
294 GError **error)
295 {
296 gboolean ret;
297 GPtrArray *keys;
298 const gchar *host;
299 const gchar *port;
300 const gchar *family;
301 gint port_num;
302 gchar *endp;
303
304 ret = FALSE;
305 host = NULL;
306 port = NULL;
307 family = NULL;
308
309 keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
310 for (guint i = 0; i < keys->len; ++i)
311 {
312 const gchar *key = g_ptr_array_index (keys, i);
313 if (g_strcmp0 (key, "host") == 0)
314 host = g_hash_table_lookup (key_value_pairs, key);
315 else if (g_strcmp0 (key, "port") == 0)
316 port = g_hash_table_lookup (key_value_pairs, key);
317 else if (g_strcmp0 (key, "family") == 0)
318 family = g_hash_table_lookup (key_value_pairs, key);
319 else if (g_strcmp0 (key, "guid") != 0)
320 {
321 g_set_error (error,
322 G_IO_ERROR,
323 G_IO_ERROR_INVALID_ARGUMENT,
324 _("Unsupported key “%s” in address entry “%s”"),
325 key,
326 address_entry);
327 goto out;
328 }
329 }
330
331 if (port != NULL)
332 {
333 port_num = strtol (port, &endp, 10);
334 if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
335 {
336 g_set_error (error,
337 G_IO_ERROR,
338 G_IO_ERROR_INVALID_ARGUMENT,
339 _("Error in address “%s” — the “%s” attribute is malformed"),
340 address_entry, "port");
341 goto out;
342 }
343 }
344
345 if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
346 {
347 g_set_error (error,
348 G_IO_ERROR,
349 G_IO_ERROR_INVALID_ARGUMENT,
350 _("Error in address “%s” — the “%s” attribute is malformed"),
351 address_entry, "family");
352 goto out;
353 }
354
355 if (host != NULL)
356 {
357 /* TODO: validate host */
358 }
359
360 ret= TRUE;
361
362 out:
363 g_ptr_array_unref (keys);
364
365 return ret;
366 }
367
368 /**
369 * g_dbus_is_supported_address:
370 * @string: A string.
371 * @error: Return location for error or %NULL.
372 *
373 * Like g_dbus_is_address() but also checks if the library supports the
374 * transports in @string and that key/value pairs for each transport
375 * are valid. See the specification of the
376 * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
377 *
378 * Returns: %TRUE if @string is a valid D-Bus address that is
379 * supported by this library, %FALSE if @error is set.
380 *
381 * Since: 2.26
382 */
383 gboolean
384 g_dbus_is_supported_address (const gchar *string,
385 GError **error)
386 {
387 guint n;
388 gchar **a;
389 gboolean ret;
390
391 ret = FALSE;
392
393 g_return_val_if_fail (string != NULL, FALSE);
394 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
395
396 a = g_strsplit (string, ";", 0);
397 for (n = 0; a[n] != NULL; n++)
398 {
399 gchar *transport_name;
400 GHashTable *key_value_pairs;
401 gboolean supported;
402
403 if (!_g_dbus_address_parse_entry (a[n],
404 &transport_name,
405 &key_value_pairs,
406 error))
407 goto out;
408
409 supported = FALSE;
410 if (g_strcmp0 (transport_name, "unix") == 0)
411 supported = is_valid_unix (a[n], key_value_pairs, error);
412 else if (g_strcmp0 (transport_name, "tcp") == 0)
413 supported = is_valid_tcp (a[n], key_value_pairs, error);
414 else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
415 supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
416 else if (g_strcmp0 (a[n], "autolaunch:") == 0)
417 supported = TRUE;
418 else
419 g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
420 _("Unknown or unsupported transport “%s” for address “%s”"),
421 transport_name, a[n]);
422
423 g_free (transport_name);
424 g_hash_table_unref (key_value_pairs);
425
426 if (!supported)
427 goto out;
428 }
429
430 ret = TRUE;
431
432 out:
433 g_strfreev (a);
434
435 g_assert (ret || (!ret && (error == NULL || *error != NULL)));
436
437 return ret;
438 }
439
440 gboolean
441 _g_dbus_address_parse_entry (const gchar *address_entry,
442 gchar **out_transport_name,
443 GHashTable **out_key_value_pairs,
444 GError **error)
445 {
446 gboolean ret;
447 GHashTable *key_value_pairs;
448 gchar *transport_name;
449 gchar **kv_pairs;
450 const gchar *s;
451 guint n;
452
453 ret = FALSE;
454 kv_pairs = NULL;
455 transport_name = NULL;
456 key_value_pairs = NULL;
457
458 s = strchr (address_entry, ':');
459 if (s == NULL)
460 {
461 g_set_error (error,
462 G_IO_ERROR,
463 G_IO_ERROR_INVALID_ARGUMENT,
464 _("Address element “%s” does not contain a colon (:)"),
465 address_entry);
466 goto out;
467 }
468 else if (s == address_entry)
469 {
470 g_set_error (error,
471 G_IO_ERROR,
472 G_IO_ERROR_INVALID_ARGUMENT,
473 _("Transport name in address element “%s” must not be empty"),
474 address_entry);
475 goto out;
476 }
477
478 transport_name = g_strndup (address_entry, s - address_entry);
479 key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
480
481 kv_pairs = g_strsplit (s + 1, ",", 0);
482 for (n = 0; kv_pairs[n] != NULL; n++)
483 {
484 const gchar *kv_pair = kv_pairs[n];
485 gchar *key;
486 gchar *value;
487
488 s = strchr (kv_pair, '=');
489 if (s == NULL)
490 {
491 g_set_error (error,
492 G_IO_ERROR,
493 G_IO_ERROR_INVALID_ARGUMENT,
494 _("Key/Value pair %d, “%s”, in address element “%s” does not contain an equal sign"),
495 n,
496 kv_pair,
497 address_entry);
498 goto out;
499 }
500 else if (s == kv_pair)
501 {
502 g_set_error (error,
503 G_IO_ERROR,
504 G_IO_ERROR_INVALID_ARGUMENT,
505 _("Key/Value pair %d, “%s”, in address element “%s” must not have an empty key"),
506 n,
507 kv_pair,
508 address_entry);
509 goto out;
510 }
511
512 key = g_uri_unescape_segment (kv_pair, s, NULL);
513 value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
514 if (key == NULL || value == NULL)
515 {
516 g_set_error (error,
517 G_IO_ERROR,
518 G_IO_ERROR_INVALID_ARGUMENT,
519 _("Error unescaping key or value in Key/Value pair %d, “%s”, in address element “%s”"),
520 n,
521 kv_pair,
522 address_entry);
523 g_free (key);
524 g_free (value);
525 goto out;
526 }
527 g_hash_table_insert (key_value_pairs, key, value);
528 }
529
530 ret = TRUE;
531
532 out:
533 if (ret)
534 {
535 if (out_transport_name != NULL)
536 *out_transport_name = g_steal_pointer (&transport_name);
537 if (out_key_value_pairs != NULL)
538 *out_key_value_pairs = g_steal_pointer (&key_value_pairs);
539 }
540
541 g_clear_pointer (&key_value_pairs, g_hash_table_unref);
542 g_free (transport_name);
543 g_strfreev (kv_pairs);
544
545 return ret;
546 }
547
548 /* ---------------------------------------------------------------------------------------------------- */
549
550 static GIOStream *
551 g_dbus_address_try_connect_one (const gchar *address_entry,
552 gchar **out_guid,
553 GCancellable *cancellable,
554 GError **error);
555
556 /* TODO: Declare an extension point called GDBusTransport (or similar)
557 * and move code below to extensions implementing said extension
558 * point. That way we can implement a D-Bus transport over X11 without
559 * making libgio link to libX11...
560 */
561 static GIOStream *
562 g_dbus_address_connect (const gchar *address_entry,
563 const gchar *transport_name,
564 GHashTable *key_value_pairs,
565 GCancellable *cancellable,
566 GError **error)
567 {
568 GIOStream *ret;
569 GSocketConnectable *connectable;
570 const gchar *nonce_file;
571
572 connectable = NULL;
573 ret = NULL;
574 nonce_file = NULL;
575
576 if (g_strcmp0 (transport_name, "unix") == 0)
577 {
578 const gchar *path;
579 const gchar *abstract;
580 path = g_hash_table_lookup (key_value_pairs, "path");
581 abstract = g_hash_table_lookup (key_value_pairs, "abstract");
582 if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
583 {
584 g_set_error (error,
585 G_IO_ERROR,
586 G_IO_ERROR_INVALID_ARGUMENT,
587 _("Error in address “%s” — the unix transport requires exactly one of the "
588 "keys “path” or “abstract” to be set"),
589 address_entry);
590 }
591 else if (path != NULL)
592 {
593 connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
594 }
595 else if (abstract != NULL)
596 {
597 connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
598 -1,
599 G_UNIX_SOCKET_ADDRESS_ABSTRACT));
600 }
601 else
602 {
603 g_assert_not_reached ();
604 }
605 }
606 else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
607 {
608 const gchar *s;
609 const gchar *host;
610 glong port;
611 gchar *endp;
612 gboolean is_nonce;
613
614 is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
615
616 host = g_hash_table_lookup (key_value_pairs, "host");
617 if (host == NULL)
618 {
619 g_set_error (error,
620 G_IO_ERROR,
621 G_IO_ERROR_INVALID_ARGUMENT,
622 _("Error in address “%s” — the host attribute is missing or malformed"),
623 address_entry);
624 goto out;
625 }
626
627 s = g_hash_table_lookup (key_value_pairs, "port");
628 if (s == NULL)
629 s = "0";
630 port = strtol (s, &endp, 10);
631 if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
632 {
633 g_set_error (error,
634 G_IO_ERROR,
635 G_IO_ERROR_INVALID_ARGUMENT,
636 _("Error in address “%s” — the port attribute is missing or malformed"),
637 address_entry);
638 goto out;
639 }
640
641
642 if (is_nonce)
643 {
644 nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
645 if (nonce_file == NULL)
646 {
647 g_set_error (error,
648 G_IO_ERROR,
649 G_IO_ERROR_INVALID_ARGUMENT,
650 _("Error in address “%s” — the noncefile attribute is missing or malformed"),
651 address_entry);
652 goto out;
653 }
654 }
655
656 /* TODO: deal with family key/value-pair */
657 connectable = g_network_address_new (host, port);
658 }
659 else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
660 {
661 gchar *autolaunch_address;
662 autolaunch_address = get_session_address_dbus_launch (error);
663 if (autolaunch_address != NULL)
664 {
665 ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
666 g_free (autolaunch_address);
667 goto out;
668 }
669 else
670 {
671 g_prefix_error (error, _("Error auto-launching: "));
672 }
673 }
674 else
675 {
676 g_set_error (error,
677 G_IO_ERROR,
678 G_IO_ERROR_INVALID_ARGUMENT,
679 _("Unknown or unsupported transport “%s” for address “%s”"),
680 transport_name,
681 address_entry);
682 }
683
684 if (connectable != NULL)
685 {
686 GSocketClient *client;
687 GSocketConnection *connection;
688
689 g_assert (ret == NULL);
690 client = g_socket_client_new ();
691
692 /* Disable proxy support to prevent a deadlock on startup, since loading a
693 * proxy resolver causes the GIO modules to be loaded, and there will
694 * almost certainly be one of them which then tries to use GDBus.
695 * See: https://bugzilla.gnome.org/show_bug.cgi?id=792499 */
696 g_socket_client_set_enable_proxy (client, FALSE);
697
698 connection = g_socket_client_connect (client,
699 connectable,
700 cancellable,
701 error);
702 g_object_unref (connectable);
703 g_object_unref (client);
704 if (connection == NULL)
705 goto out;
706
707 ret = G_IO_STREAM (connection);
708
709 if (nonce_file != NULL)
710 {
711 gchar nonce_contents[16 + 1];
712 size_t num_bytes_read;
713 FILE *f;
714 int errsv;
715
716 /* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
717 f = fopen (nonce_file, "rb" FO_CLOEXEC);
718 errsv = errno;
719 if (f == NULL)
720 {
721 g_set_error (error,
722 G_IO_ERROR,
723 G_IO_ERROR_INVALID_ARGUMENT,
724 _("Error opening nonce file “%s”: %s"),
725 nonce_file,
726 g_strerror (errsv));
727 g_object_unref (ret);
728 ret = NULL;
729 goto out;
730 }
731 num_bytes_read = fread (nonce_contents,
732 sizeof (gchar),
733 16 + 1,
734 f);
735 errsv = errno;
736 if (num_bytes_read != 16)
737 {
738 if (num_bytes_read == 0)
739 {
740 g_set_error (error,
741 G_IO_ERROR,
742 G_IO_ERROR_INVALID_ARGUMENT,
743 _("Error reading from nonce file “%s”: %s"),
744 nonce_file,
745 g_strerror (errsv));
746 }
747 else
748 {
749 g_set_error (error,
750 G_IO_ERROR,
751 G_IO_ERROR_INVALID_ARGUMENT,
752 _("Error reading from nonce file “%s”, expected 16 bytes, got %d"),
753 nonce_file,
754 (gint) num_bytes_read);
755 }
756 g_object_unref (ret);
757 ret = NULL;
758 fclose (f);
759 goto out;
760 }
761 fclose (f);
762
763 if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
764 nonce_contents,
765 16,
766 NULL,
767 cancellable,
768 error))
769 {
770 g_prefix_error (error, _("Error writing contents of nonce file “%s” to stream:"), nonce_file);
771 g_object_unref (ret);
772 ret = NULL;
773 goto out;
774 }
775 }
776 }
777
778 out:
779
780 return ret;
781 }
782
783 static GIOStream *
784 g_dbus_address_try_connect_one (const gchar *address_entry,
785 gchar **out_guid,
786 GCancellable *cancellable,
787 GError **error)
788 {
789 GIOStream *ret;
790 GHashTable *key_value_pairs;
791 gchar *transport_name;
792 const gchar *guid;
793
794 ret = NULL;
795 transport_name = NULL;
796 key_value_pairs = NULL;
797
798 if (!_g_dbus_address_parse_entry (address_entry,
799 &transport_name,
800 &key_value_pairs,
801 error))
802 goto out;
803
804 ret = g_dbus_address_connect (address_entry,
805 transport_name,
806 key_value_pairs,
807 cancellable,
808 error);
809 if (ret == NULL)
810 goto out;
811
812 guid = g_hash_table_lookup (key_value_pairs, "guid");
813 if (guid != NULL && out_guid != NULL)
814 *out_guid = g_strdup (guid);
815
816 out:
817 g_free (transport_name);
818 if (key_value_pairs != NULL)
819 g_hash_table_unref (key_value_pairs);
820 return ret;
821 }
822
823
824 /* ---------------------------------------------------------------------------------------------------- */
825
826 typedef struct {
827 gchar *address;
828 gchar *guid;
829 } GetStreamData;
830
831 static void
832 get_stream_data_free (GetStreamData *data)
833 {
834 g_free (data->address);
835 g_free (data->guid);
836 g_free (data);
837 }
838
839 static void
840 get_stream_thread_func (GTask *task,
841 gpointer source_object,
842 gpointer task_data,
843 GCancellable *cancellable)
844 {
845 GetStreamData *data = task_data;
846 GIOStream *stream;
847 GError *error = NULL;
848
849 stream = g_dbus_address_get_stream_sync (data->address,
850 &data->guid,
851 cancellable,
852 &error);
853 if (stream)
854 g_task_return_pointer (task, stream, g_object_unref);
855 else
856 g_task_return_error (task, error);
857 }
858
859 /**
860 * g_dbus_address_get_stream:
861 * @address: A valid D-Bus address.
862 * @cancellable: (nullable): A #GCancellable or %NULL.
863 * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
864 * @user_data: Data to pass to @callback.
865 *
866 * Asynchronously connects to an endpoint specified by @address and
867 * sets up the connection so it is in a state to run the client-side
868 * of the D-Bus authentication conversation. @address must be in the
869 * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
870 *
871 * When the operation is finished, @callback will be invoked. You can
872 * then call g_dbus_address_get_stream_finish() to get the result of
873 * the operation.
874 *
875 * This is an asynchronous failable function. See
876 * g_dbus_address_get_stream_sync() for the synchronous version.
877 *
878 * Since: 2.26
879 */
880 void
881 g_dbus_address_get_stream (const gchar *address,
882 GCancellable *cancellable,
883 GAsyncReadyCallback callback,
884 gpointer user_data)
885 {
886 GTask *task;
887 GetStreamData *data;
888
889 g_return_if_fail (address != NULL);
890
891 data = g_new0 (GetStreamData, 1);
892 data->address = g_strdup (address);
893
894 task = g_task_new (NULL, cancellable, callback, user_data);
895 g_task_set_source_tag (task, g_dbus_address_get_stream);
896 g_task_set_task_data (task, data, (GDestroyNotify) get_stream_data_free);
897 g_task_run_in_thread (task, get_stream_thread_func);
898 g_object_unref (task);
899 }
900
901 /**
902 * g_dbus_address_get_stream_finish:
903 * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
904 * @out_guid: (optional) (out) (nullable): %NULL or return location to store the GUID extracted from @address, if any.
905 * @error: Return location for error or %NULL.
906 *
907 * Finishes an operation started with g_dbus_address_get_stream().
908 *
909 * A server is not required to set a GUID, so @out_guid may be set to %NULL
910 * even on success.
911 *
912 * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
913 *
914 * Since: 2.26
915 */
916 GIOStream *
917 g_dbus_address_get_stream_finish (GAsyncResult *res,
918 gchar **out_guid,
919 GError **error)
920 {
921 GTask *task;
922 GetStreamData *data;
923 GIOStream *ret;
924
925 g_return_val_if_fail (g_task_is_valid (res, NULL), NULL);
926 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
927
928 task = G_TASK (res);
929 ret = g_task_propagate_pointer (task, error);
930
931 if (ret != NULL && out_guid != NULL)
932 {
933 data = g_task_get_task_data (task);
934 *out_guid = data->guid;
935 data->guid = NULL;
936 }
937
938 return ret;
939 }
940
941 /**
942 * g_dbus_address_get_stream_sync:
943 * @address: A valid D-Bus address.
944 * @out_guid: (optional) (out) (nullable): %NULL or return location to store the GUID extracted from @address, if any.
945 * @cancellable: (nullable): A #GCancellable or %NULL.
946 * @error: Return location for error or %NULL.
947 *
948 * Synchronously connects to an endpoint specified by @address and
949 * sets up the connection so it is in a state to run the client-side
950 * of the D-Bus authentication conversation. @address must be in the
951 * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
952 *
953 * A server is not required to set a GUID, so @out_guid may be set to %NULL
954 * even on success.
955 *
956 * This is a synchronous failable function. See
957 * g_dbus_address_get_stream() for the asynchronous version.
958 *
959 * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
960 *
961 * Since: 2.26
962 */
963 GIOStream *
964 g_dbus_address_get_stream_sync (const gchar *address,
965 gchar **out_guid,
966 GCancellable *cancellable,
967 GError **error)
968 {
969 GIOStream *ret;
970 gchar **addr_array;
971 guint n;
972 GError *last_error;
973
974 g_return_val_if_fail (address != NULL, NULL);
975 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
976
977 ret = NULL;
978 last_error = NULL;
979
980 addr_array = g_strsplit (address, ";", 0);
981 if (addr_array[0] == NULL)
982 {
983 last_error = g_error_new_literal (G_IO_ERROR,
984 G_IO_ERROR_INVALID_ARGUMENT,
985 _("The given address is empty"));
986 goto out;
987 }
988
989 for (n = 0; addr_array[n] != NULL; n++)
990 {
991 const gchar *addr = addr_array[n];
992 GError *this_error;
993
994 this_error = NULL;
995 ret = g_dbus_address_try_connect_one (addr,
996 out_guid,
997 cancellable,
998 &this_error);
999 if (ret != NULL)
1000 {
1001 goto out;
1002 }
1003 else
1004 {
1005 g_assert (this_error != NULL);
1006 if (last_error != NULL)
1007 g_error_free (last_error);
1008 last_error = this_error;
1009 }
1010 }
1011
1012 out:
1013 if (ret != NULL)
1014 {
1015 if (last_error != NULL)
1016 g_error_free (last_error);
1017 }
1018 else
1019 {
1020 g_assert (last_error != NULL);
1021 g_propagate_error (error, last_error);
1022 }
1023
1024 g_strfreev (addr_array);
1025 return ret;
1026 }
1027
1028 /* ---------------------------------------------------------------------------------------------------- */
1029
1030 /*
1031 * Return the address of XDG_RUNTIME_DIR/bus if it exists, belongs to
1032 * us, and is a socket, and we are on Unix.
1033 */
1034 static gchar *
1035 get_session_address_xdg (void)
1036 {
1037 #ifdef G_OS_UNIX
1038 gchar *ret = NULL;
1039 gchar *bus;
1040 gchar *tmp;
1041 GStatBuf buf;
1042
1043 bus = g_build_filename (g_get_user_runtime_dir (), "bus", NULL);
1044
1045 /* if ENOENT, EPERM, etc., quietly don't use it */
1046 if (g_stat (bus, &buf) < 0)
1047 goto out;
1048
1049 /* if it isn't ours, we have incorrectly inherited someone else's
1050 * XDG_RUNTIME_DIR; silently don't use it
1051 */
1052 if (buf.st_uid != geteuid ())
1053 goto out;
1054
1055 /* if it isn't a socket, silently don't use it */
1056 if ((buf.st_mode & S_IFMT) != S_IFSOCK)
1057 goto out;
1058
1059 tmp = g_dbus_address_escape_value (bus);
1060 ret = g_strconcat ("unix:path=", tmp, NULL);
1061 g_free (tmp);
1062
1063 out:
1064 g_free (bus);
1065 return ret;
1066 #else
1067 return NULL;
1068 #endif
1069 }
1070
1071 /* ---------------------------------------------------------------------------------------------------- */
1072
1073 #ifdef G_OS_UNIX
1074 static gchar *
1075 get_session_address_dbus_launch (GError **error)
1076 {
1077 gchar *ret;
1078 gchar *machine_id;
1079 gchar *command_line;
1080 gchar *launch_stdout;
1081 gchar *launch_stderr;
1082 gint wait_status;
1083 gchar *old_dbus_verbose;
1084 gboolean restore_dbus_verbose;
1085
1086 ret = NULL;
1087 machine_id = NULL;
1088 command_line = NULL;
1089 launch_stdout = NULL;
1090 launch_stderr = NULL;
1091 restore_dbus_verbose = FALSE;
1092 old_dbus_verbose = NULL;
1093
1094 /* Don't run binaries as root if we're setuid. */
1095 if (GLIB_PRIVATE_CALL (g_check_setuid) ())
1096 {
1097 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1098 _("Cannot spawn a message bus when AT_SECURE is set"));
1099 goto out;
1100 }
1101
1102 machine_id = _g_dbus_get_machine_id (error);
1103 if (machine_id == NULL)
1104 {
1105 g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
1106 goto out;
1107 }
1108
1109 if (g_getenv ("DISPLAY") == NULL)
1110 {
1111 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1112 _("Cannot autolaunch D-Bus without X11 $DISPLAY"));
1113 goto out;
1114 }
1115
1116 /* We're using private libdbus facilities here. When everything
1117 * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
1118 * X11 property is correctly documented right now) we should
1119 * consider using the spec instead of dbus-launch.
1120 *
1121 * --autolaunch=MACHINEID
1122 * This option implies that dbus-launch should scan for a previ‐
1123 * ously-started session and reuse the values found there. If no
1124 * session is found, it will start a new session. The --exit-with-
1125 * session option is implied if --autolaunch is given. This option
1126 * is for the exclusive use of libdbus, you do not want to use it
1127 * manually. It may change in the future.
1128 */
1129
1130 /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
1131 command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
1132
1133 if (G_UNLIKELY (_g_dbus_debug_address ()))
1134 {
1135 _g_dbus_debug_print_lock ();
1136 g_print ("GDBus-debug:Address: Running '%s' to get bus address (possibly autolaunching)\n", command_line);
1137 old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
1138 restore_dbus_verbose = TRUE;
1139 g_setenv ("DBUS_VERBOSE", "1", TRUE);
1140 _g_dbus_debug_print_unlock ();
1141 }
1142
1143 if (!g_spawn_command_line_sync (command_line,
1144 &launch_stdout,
1145 &launch_stderr,
1146 &wait_status,
1147 error))
1148 {
1149 goto out;
1150 }
1151
1152 if (!g_spawn_check_wait_status (wait_status, error))
1153 {
1154 g_prefix_error (error, _("Error spawning command line “%s”: "), command_line);
1155 goto out;
1156 }
1157
1158 /* From the dbus-launch(1) man page:
1159 *
1160 * --binary-syntax Write to stdout a nul-terminated bus address,
1161 * then the bus PID as a binary integer of size sizeof(pid_t),
1162 * then the bus X window ID as a binary integer of size
1163 * sizeof(long). Integers are in the machine's byte order, not
1164 * network byte order or any other canonical byte order.
1165 */
1166 ret = g_strdup (launch_stdout);
1167
1168 out:
1169 if (G_UNLIKELY (_g_dbus_debug_address ()))
1170 {
1171 gchar *s;
1172 _g_dbus_debug_print_lock ();
1173 g_print ("GDBus-debug:Address: dbus-launch output:");
1174 if (launch_stdout != NULL)
1175 {
1176 s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
1177 g_print ("\n%s", s);
1178 g_free (s);
1179 }
1180 else
1181 {
1182 g_print (" (none)\n");
1183 }
1184 g_print ("GDBus-debug:Address: dbus-launch stderr output:");
1185 if (launch_stderr != NULL)
1186 g_print ("\n%s", launch_stderr);
1187 else
1188 g_print (" (none)\n");
1189 _g_dbus_debug_print_unlock ();
1190 }
1191
1192 g_free (machine_id);
1193 g_free (command_line);
1194 g_free (launch_stdout);
1195 g_free (launch_stderr);
1196 if (G_UNLIKELY (restore_dbus_verbose))
1197 {
1198 if (old_dbus_verbose != NULL)
1199 g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
1200 else
1201 g_unsetenv ("DBUS_VERBOSE");
1202 }
1203 g_free (old_dbus_verbose);
1204 return ret;
1205 }
1206
1207 /* end of G_OS_UNIX case */
1208 #elif defined(G_OS_WIN32)
1209
1210 static gchar *
1211 get_session_address_dbus_launch (GError **error)
1212 {
1213 return _g_dbus_win32_get_session_address_dbus_launch (error);
1214 }
1215
1216 #else /* neither G_OS_UNIX nor G_OS_WIN32 */
1217 static gchar *
1218 get_session_address_dbus_launch (GError **error)
1219 {
1220 g_set_error (error,
1221 G_IO_ERROR,
1222 G_IO_ERROR_FAILED,
1223 _("Cannot determine session bus address (not implemented for this OS)"));
1224 return NULL;
1225 }
1226 #endif /* neither G_OS_UNIX nor G_OS_WIN32 */
1227
1228 /* ---------------------------------------------------------------------------------------------------- */
1229
1230 static gchar *
1231 get_session_address_platform_specific (GError **error)
1232 {
1233 gchar *ret;
1234
1235 /* Use XDG_RUNTIME_DIR/bus if it exists and is suitable. This is appropriate
1236 * for systems using the "a session is a user-session" model described in
1237 * <http://lists.freedesktop.org/archives/dbus/2015-January/016522.html>,
1238 * and implemented in dbus >= 1.9.14 and sd-bus.
1239 *
1240 * On systems following the more traditional "a session is a login-session"
1241 * model, this will fail and we'll fall through to X11 autolaunching
1242 * (dbus-launch) below.
1243 */
1244 ret = get_session_address_xdg ();
1245
1246 if (ret != NULL)
1247 return ret;
1248
1249 /* TODO (#694472): try launchd on OS X, like
1250 * _dbus_lookup_session_address_launchd() does, since
1251 * 'dbus-launch --autolaunch' probably won't work there
1252 */
1253
1254 /* As a last resort, try the "autolaunch:" transport. On Unix this means
1255 * X11 autolaunching; on Windows this means a different autolaunching
1256 * mechanism based on shared memory.
1257 */
1258 return get_session_address_dbus_launch (error);
1259 }
1260
1261 /* ---------------------------------------------------------------------------------------------------- */
1262
1263 /**
1264 * g_dbus_address_get_for_bus_sync:
1265 * @bus_type: a #GBusType
1266 * @cancellable: (nullable): a #GCancellable or %NULL
1267 * @error: return location for error or %NULL
1268 *
1269 * Synchronously looks up the D-Bus address for the well-known message
1270 * bus instance specified by @bus_type. This may involve using various
1271 * platform specific mechanisms.
1272 *
1273 * The returned address will be in the
1274 * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
1275 *
1276 * Returns: (transfer full): a valid D-Bus address string for @bus_type or
1277 * %NULL if @error is set
1278 *
1279 * Since: 2.26
1280 */
1281 gchar *
1282 g_dbus_address_get_for_bus_sync (GBusType bus_type,
1283 GCancellable *cancellable,
1284 GError **error)
1285 {
1286 gboolean has_elevated_privileges = GLIB_PRIVATE_CALL (g_check_setuid) ();
1287 gchar *ret, *s = NULL;
1288 const gchar *starter_bus;
1289 GError *local_error;
1290
1291 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1292
1293 ret = NULL;
1294 local_error = NULL;
1295
1296 if (G_UNLIKELY (_g_dbus_debug_address ()))
1297 {
1298 guint n;
1299 _g_dbus_debug_print_lock ();
1300 s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
1301 g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
1302 s);
1303 g_free (s);
1304 for (n = 0; n < 3; n++)
1305 {
1306 const gchar *k;
1307 const gchar *v;
1308 switch (n)
1309 {
1310 case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
1311 case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
1312 case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
1313 default: g_assert_not_reached ();
1314 }
1315 v = g_getenv (k);
1316 g_print ("GDBus-debug:Address: env var %s", k);
1317 if (v != NULL)
1318 g_print ("='%s'\n", v);
1319 else
1320 g_print (" is not set\n");
1321 }
1322 _g_dbus_debug_print_unlock ();
1323 }
1324
1325 /* Don’t load the addresses from the environment if running as setuid, as they
1326 * come from an unprivileged caller. */
1327 switch (bus_type)
1328 {
1329 case G_BUS_TYPE_SYSTEM:
1330 if (has_elevated_privileges)
1331 ret = NULL;
1332 else
1333 ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
1334
1335 if (ret == NULL)
1336 {
1337 /* While the D-Bus specification says this must be `/var/run/dbus/system_bus_socket`,
1338 * a footnote allows it to use localstatedir:
1339 * https://dbus.freedesktop.org/doc/dbus-specification.html#ftn.id-1.13.6.4.3.3
1340 * or, on systems where /run is the same as /var/run, runstatedir:
1341 * https://gitlab.freedesktop.org/dbus/dbus/-/merge_requests/209 */
1342 ret = g_strdup ("unix:path=" GLIB_RUNSTATEDIR "/dbus/system_bus_socket");
1343 }
1344 break;
1345
1346 case G_BUS_TYPE_SESSION:
1347 if (has_elevated_privileges)
1348 ret = NULL;
1349 else
1350 ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
1351
1352 if (ret == NULL)
1353 {
1354 ret = get_session_address_platform_specific (&local_error);
1355 }
1356 break;
1357
1358 case G_BUS_TYPE_STARTER:
1359 starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
1360 if (g_strcmp0 (starter_bus, "session") == 0)
1361 {
1362 ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
1363 goto out;
1364 }
1365 else if (g_strcmp0 (starter_bus, "system") == 0)
1366 {
1367 ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
1368 goto out;
1369 }
1370 else
1371 {
1372 if (starter_bus != NULL)
1373 {
1374 g_set_error (&local_error,
1375 G_IO_ERROR,
1376 G_IO_ERROR_FAILED,
1377 _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
1378 " — unknown value “%s”"),
1379 starter_bus);
1380 }
1381 else
1382 {
1383 g_set_error_literal (&local_error,
1384 G_IO_ERROR,
1385 G_IO_ERROR_FAILED,
1386 _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1387 "variable is not set"));
1388 }
1389 }
1390 break;
1391
1392 default:
1393 g_set_error (&local_error,
1394 G_IO_ERROR,
1395 G_IO_ERROR_FAILED,
1396 _("Unknown bus type %d"),
1397 bus_type);
1398 break;
1399 }
1400
1401 out:
1402 if (G_UNLIKELY (_g_dbus_debug_address ()))
1403 {
1404 _g_dbus_debug_print_lock ();
1405 s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
1406 if (ret != NULL)
1407 {
1408 g_print ("GDBus-debug:Address: Returning address '%s' for bus type '%s'\n",
1409 ret, s);
1410 }
1411 else
1412 {
1413 g_print ("GDBus-debug:Address: Cannot look-up address bus type '%s': %s\n",
1414 s, local_error ? local_error->message : "");
1415 }
1416 g_free (s);
1417 _g_dbus_debug_print_unlock ();
1418 }
1419
1420 if (local_error != NULL)
1421 g_propagate_error (error, local_error);
1422
1423 return ret;
1424 }
1425
1426 /**
1427 * g_dbus_address_escape_value:
1428 * @string: an unescaped string to be included in a D-Bus address
1429 * as the value in a key-value pair
1430 *
1431 * Escape @string so it can appear in a D-Bus address as the value
1432 * part of a key-value pair.
1433 *
1434 * For instance, if @string is `/run/bus-for-:0`,
1435 * this function would return `/run/bus-for-%3A0`,
1436 * which could be used in a D-Bus address like
1437 * `unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0`.
1438 *
1439 * Returns: (transfer full): a copy of @string with all
1440 * non-optionally-escaped bytes escaped
1441 *
1442 * Since: 2.36
1443 */
1444 gchar *
1445 g_dbus_address_escape_value (const gchar *string)
1446 {
1447 GString *s;
1448 gsize i;
1449
1450 g_return_val_if_fail (string != NULL, NULL);
1451
1452 /* There will often not be anything needing escaping at all. */
1453 s = g_string_sized_new (strlen (string));
1454
1455 /* D-Bus address escaping is mostly the same as URI escaping... */
1456 g_string_append_uri_escaped (s, string, "\\/", FALSE);
1457
1458 /* ... but '~' is an unreserved character in URIs, but a
1459 * non-optionally-escaped character in D-Bus addresses. */
1460 for (i = 0; i < s->len; i++)
1461 {
1462 if (G_UNLIKELY (s->str[i] == '~'))
1463 {
1464 s->str[i] = '%';
1465 g_string_insert (s, i + 1, "7E");
1466 i += 2;
1467 }
1468 }
1469
1470 return g_string_free (s, FALSE);
1471 }