From 3cfd08a16c3d9424d37ef71756e4471930c32598 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 31 Oct 2022 12:07:52 +0100 Subject: [PATCH] gio: Make portal support aware of snaps --- docs/reference/gio/meson.build | 1 + gio/gportalsupport.c | 155 ++++++++++++------ gio/gsandbox.c | 126 ++++++++++++++ gio/gsandbox.h | 47 ++++++ gio/meson.build | 3 +- gio/tests/meson.build | 22 +++ gio/tests/portal-support-env-var.c | 45 +++++ gio/tests/portal-support-none.c | 43 +++++ gio/tests/portal-support-snap-classic.c | 119 ++++++++++++++ gio/tests/portal-support-snap.c | 208 ++++++++++++++++++++++++ gio/tests/portal-support-utils.c | 87 ++++++++++ gio/tests/portal-support-utils.h | 30 ++++ gio/tests/sandbox.c | 80 +++++++++ 13 files changed, 919 insertions(+), 47 deletions(-) create mode 100644 gio/gsandbox.c create mode 100644 gio/gsandbox.h create mode 100644 gio/tests/portal-support-env-var.c create mode 100644 gio/tests/portal-support-none.c create mode 100644 gio/tests/portal-support-snap-classic.c create mode 100644 gio/tests/portal-support-snap.c create mode 100644 gio/tests/portal-support-utils.c create mode 100644 gio/tests/portal-support-utils.h create mode 100644 gio/tests/sandbox.c diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build index bb14e6923..fe32a2ad3 100644 --- a/docs/reference/gio/meson.build +++ b/docs/reference/gio/meson.build @@ -71,6 +71,7 @@ if get_option('gtk_doc') 'gproxyresolverportal.h', 'gregistrysettingsbackend.h', 'gresourcefile.h', + 'gsandbox.h', 'gsettingsbackendinternal.h', 'gsettings-mapping.h', 'gsettingsschema-internal.h', diff --git a/gio/gportalsupport.c b/gio/gportalsupport.c index 233f6af45..0742b9beb 100644 --- a/gio/gportalsupport.c +++ b/gio/gportalsupport.c @@ -18,84 +18,147 @@ #include "config.h" +#include "glib-private.h" #include "gportalsupport.h" +#include "gsandbox.h" +static GSandboxType sandbox_type = G_SANDBOX_TYPE_UNKNOWN; static gboolean use_portal; static gboolean network_available; static gboolean dconf_access; +#ifdef G_PORTAL_SUPPORT_TEST +static const char *snapctl = "snapctl"; +#else +static const char *snapctl = "/usr/bin/snapctl"; +#endif + +static gboolean +snap_plug_is_connected (const gchar *plug_name) +{ + gint wait_status; + const gchar *argv[] = { snapctl, "is-connected", plug_name, NULL }; + + /* Bail out if our process is privileged - we don't want to pass those + * privileges to snapctl. It could be overridden and this would + * allow arbitrary code execution. + */ + if (GLIB_PRIVATE_CALL (g_check_setuid) ()) + return FALSE; + + if (!g_spawn_sync (NULL, (gchar **) argv, NULL, +#ifdef G_PORTAL_SUPPORT_TEST + G_SPAWN_SEARCH_PATH | +#endif + G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, NULL, NULL, &wait_status, + NULL)) + return FALSE; + + return g_spawn_check_wait_status (wait_status, NULL); +} + static void -read_flatpak_info (void) +sandbox_info_read (void) { - static gsize flatpak_info_read = 0; - const gchar *path = "/.flatpak-info"; + static gsize sandbox_info_is_read = 0; - if (!g_once_init_enter (&flatpak_info_read)) + /* Sandbox type and Flatpak info is static, so only read once */ + if (!g_once_init_enter (&sandbox_info_is_read)) return; - if (g_file_test (path, G_FILE_TEST_EXISTS)) - { - GKeyFile *keyfile; - - use_portal = TRUE; - network_available = FALSE; - dconf_access = FALSE; - - keyfile = g_key_file_new (); - if (g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) - { - char **shared = NULL; - char *dconf_policy = NULL; - - shared = g_key_file_get_string_list (keyfile, "Context", "shared", NULL, NULL); - if (shared) - { - network_available = g_strv_contains ((const char * const *)shared, "network"); - g_strfreev (shared); - } - - dconf_policy = g_key_file_get_string (keyfile, "Session Bus Policy", "ca.desrt.dconf", NULL); - if (dconf_policy) - { - if (strcmp (dconf_policy, "talk") == 0) - dconf_access = TRUE; - g_free (dconf_policy); - } - } - - g_key_file_unref (keyfile); - } - else + sandbox_type = glib_get_sandbox_type (); + + switch (sandbox_type) { - const char *var; + case G_SANDBOX_TYPE_FLATPAK: + { + GKeyFile *keyfile; - var = g_getenv ("GTK_USE_PORTAL"); - if (var && var[0] == '1') use_portal = TRUE; - network_available = TRUE; - dconf_access = TRUE; + network_available = FALSE; + dconf_access = FALSE; + + keyfile = g_key_file_new (); + if (g_key_file_load_from_file (keyfile, "/.flatpak-info", G_KEY_FILE_NONE, NULL)) + { + char **shared = NULL; + char *dconf_policy = NULL; + + shared = g_key_file_get_string_list (keyfile, "Context", "shared", NULL, NULL); + if (shared) + { + network_available = g_strv_contains ((const char * const *)shared, "network"); + g_strfreev (shared); + } + + dconf_policy = g_key_file_get_string (keyfile, "Session Bus Policy", "ca.desrt.dconf", NULL); + if (dconf_policy) + { + if (strcmp (dconf_policy, "talk") == 0) + dconf_access = TRUE; + g_free (dconf_policy); + } + } + + g_key_file_unref (keyfile); + } + break; + case G_SANDBOX_TYPE_SNAP: + break; + case G_SANDBOX_TYPE_UNKNOWN: + { + const char *var; + + var = g_getenv ("GTK_USE_PORTAL"); + if (var && var[0] == '1') + use_portal = TRUE; + network_available = TRUE; + dconf_access = TRUE; + } + break; } - g_once_init_leave (&flatpak_info_read, 1); + g_once_init_leave (&sandbox_info_is_read, 1); } gboolean glib_should_use_portal (void) { - read_flatpak_info (); + sandbox_info_read (); + + if (sandbox_type == G_SANDBOX_TYPE_SNAP) + return snap_plug_is_connected ("desktop"); + return use_portal; } gboolean glib_network_available_in_sandbox (void) { - read_flatpak_info (); + sandbox_info_read (); + + if (sandbox_type == G_SANDBOX_TYPE_SNAP) + { + /* FIXME: This is inefficient doing multiple calls to check connections. + * See https://github.com/snapcore/snapd/pull/12301 for a proposed + * improvement to snapd for this. + */ + return snap_plug_is_connected ("desktop") || + snap_plug_is_connected ("network-status"); + } + return network_available; } gboolean glib_has_dconf_access_in_sandbox (void) { - read_flatpak_info (); + sandbox_info_read (); + + if (sandbox_type == G_SANDBOX_TYPE_SNAP) + return snap_plug_is_connected ("gsettings"); + return dconf_access; } diff --git a/gio/gsandbox.c b/gio/gsandbox.c new file mode 100644 index 000000000..34f680b5a --- /dev/null +++ b/gio/gsandbox.c @@ -0,0 +1,126 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright 2022 Canonical Ltd + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + */ + +#include "config.h" + +#include "gsandbox.h" + +#include + +#define SNAP_CONFINEMENT_PREFIX "confinement:" + +static gboolean +is_flatpak (void) +{ + return g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS); +} + +static gchar * +get_snap_confinement (const char *snap_yaml, + GError **error) +{ + char *confinement = NULL; + char *yaml_contents; + + if (g_file_get_contents (snap_yaml, &yaml_contents, NULL, error)) + { + const char *line = yaml_contents; + + do + { + if (g_str_has_prefix (line, SNAP_CONFINEMENT_PREFIX)) + break; + + line = strchr (line, '\n'); + if (line) + line += 1; + } + while (line != NULL); + + if (line) + { + const char *start = line + strlen (SNAP_CONFINEMENT_PREFIX); + const char *end = strchr (start, '\n'); + + confinement = + g_strstrip (end ? g_strndup (start, end-start) : g_strdup (start)); + } + + g_free (yaml_contents); + } + + return g_steal_pointer (&confinement); +} + +static gboolean +is_snap (void) +{ + GError *error = NULL; + const gchar *snap_path; + gchar *yaml_path; + char *confinement; + gboolean result; + + snap_path = g_getenv ("SNAP"); + if (snap_path == NULL) + return FALSE; + + result = FALSE; + yaml_path = g_build_filename (snap_path, "meta", "snap.yaml", NULL); + confinement = get_snap_confinement (yaml_path, &error); + g_free (yaml_path); + + /* Classic snaps are de-facto no sandboxed apps, so we can ignore them */ + if (!error && g_strcmp0 (confinement, "classic") != 0) + result = TRUE; + + g_clear_error (&error); + g_free (confinement); + + return result; +} + +/* + * glib_get_sandbox_type: + * + * Gets the type of sandbox this process is running inside. + * + * Checking for sandboxes may involve doing blocking I/O calls, but should not take + * any significant time. + * + * The sandbox will not change over the lifetime of the process, so calling this + * function once and reusing the result is valid. + * + * If this process is not sandboxed then @G_SANDBOX_TYPE_UNKNOWN will be returned. + * This is because this function only detects known sandbox types in #GSandboxType. + * It may be updated in the future if new sandboxes come into use. + * + * Returns: a #GSandboxType. + */ +GSandboxType +glib_get_sandbox_type (void) +{ + if (is_flatpak ()) + return G_SANDBOX_TYPE_FLATPAK; + else if (is_snap ()) + return G_SANDBOX_TYPE_SNAP; + else + return G_SANDBOX_TYPE_UNKNOWN; +} diff --git a/gio/gsandbox.h b/gio/gsandbox.h new file mode 100644 index 000000000..7861b2756 --- /dev/null +++ b/gio/gsandbox.h @@ -0,0 +1,47 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright 2022 Canonical Ltd + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + */ + +#ifndef __G_SANDBOX_H__ +#define __G_SANDBOX_H__ + +#include + +G_BEGIN_DECLS + +/* + * GSandboxType: + * @G_SANDBOX_TYPE_UNKNOWN: process is running inside an unknown or no sandbox. + * @G_SANDBOX_TYPE_FLATPAK: process is running inside a flatpak sandbox. + * @G_SANDBOX_TYPE_SNAP: process is running inside a snap sandbox. + * + * The type of sandbox that processes can be running inside. + */ +typedef enum +{ + G_SANDBOX_TYPE_UNKNOWN, + G_SANDBOX_TYPE_FLATPAK, + G_SANDBOX_TYPE_SNAP +} GSandboxType; + +GSandboxType glib_get_sandbox_type (void); + +G_END_DECLS + +#endif diff --git a/gio/meson.build b/gio/meson.build index 9019104a4..ad4bc47ee 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -375,7 +375,8 @@ if host_system != 'windows' 'gproxyresolverportal.c', 'gtrashportal.c', 'gportalsupport.c', - 'gportalnotificationbackend.c'), + 'gportalnotificationbackend.c', + 'gsandbox.c'), xdp_dbus_generated ] diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 383d84319..f1345189a 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -94,6 +94,9 @@ gio_tests = { 'power-profile-monitor' : {}, 'proxy-test' : {}, 'readwrite' : {}, + 'sandbox' : { + 'extra_sources': ['../gsandbox.c', 'portal-support-utils.c'], + }, 'simple-async-result' : {}, 'simple-proxy' : {}, 'sleepy-stream' : {}, @@ -198,6 +201,25 @@ if host_machine.system() != 'windows' }, 'gdbus-peer-object-manager' : {}, 'live-g-file' : {}, + 'portal-support-none' : { + 'extra_sources': ['../gportalsupport.c', '../gsandbox.c'], + 'c_args': ['-DG_PORTAL_SUPPORT_TEST'], + 'suite': ['portal-support'], + }, + 'portal-support-env-var' : { + 'extra_sources': ['../gportalsupport.c', '../gsandbox.c'], + 'suite': ['portal-support'], + }, + 'portal-support-snap' : { + 'extra_sources': ['../gportalsupport.c', '../gsandbox.c', 'portal-support-utils.c'], + 'c_args': ['-DG_PORTAL_SUPPORT_TEST'], + 'suite': ['portal-support'], + }, + 'portal-support-snap-classic' : { + 'extra_sources': ['../gportalsupport.c', '../gsandbox.c', 'portal-support-utils.c'], + 'c_args': ['-DG_PORTAL_SUPPORT_TEST'], + 'suite': ['portal-support'], + }, 'resolver-parsing' : {'dependencies' : [network_libs]}, 'socket-address' : {}, 'stream-rw_all' : {}, diff --git a/gio/tests/portal-support-env-var.c b/gio/tests/portal-support-env-var.c new file mode 100644 index 000000000..b1d3fd3c3 --- /dev/null +++ b/gio/tests/portal-support-env-var.c @@ -0,0 +1,45 @@ +/* + * GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2022 Canonical Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Author: Marco Trevisan + */ + +#include "../gportalsupport.h" +#include + +static void +test_portal_support_env_var (void) +{ + g_assert_true (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_setenv ("GTK_USE_PORTAL", "1", TRUE); + + g_test_add_func ("/portal-support/env-var", test_portal_support_env_var); + + return g_test_run (); +} diff --git a/gio/tests/portal-support-none.c b/gio/tests/portal-support-none.c new file mode 100644 index 000000000..1bc0a9391 --- /dev/null +++ b/gio/tests/portal-support-none.c @@ -0,0 +1,43 @@ +/* + * GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2022 Canonical Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Author: Marco Trevisan + */ + +#include "../gportalsupport.h" +#include + +static void +test_portal_support_none (void) +{ + g_assert_false (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/portal-support/none", test_portal_support_none); + + return g_test_run (); +} diff --git a/gio/tests/portal-support-snap-classic.c b/gio/tests/portal-support-snap-classic.c new file mode 100644 index 000000000..8c0ed90c2 --- /dev/null +++ b/gio/tests/portal-support-snap-classic.c @@ -0,0 +1,119 @@ +/* + * GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2022 Canonical Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Author: Marco Trevisan + */ + +#include "portal-support-utils.h" + +#include "../gportalsupport.h" +#include + +typedef struct +{ + char *old_path; + char *old_snap; + + const char *bin_path; + const char *snap_path; +} SetupData; + +static void +tests_setup (SetupData *setup_data, + gconstpointer data) +{ + setup_data->old_path = g_strdup (g_getenv ("PATH")); + setup_data->old_snap = g_strdup (g_getenv ("SNAP")); + + setup_data->bin_path = g_get_user_runtime_dir (); + setup_data->snap_path = g_getenv ("G_TEST_TMPDIR"); + + g_assert_nonnull (setup_data->bin_path); + g_assert_nonnull (setup_data->snap_path); + + g_setenv ("PATH", setup_data->bin_path, TRUE); + g_setenv ("SNAP", setup_data->snap_path, TRUE); +} + +static void +tests_teardown (SetupData *setup_data, + gconstpointer data) +{ + if (setup_data->old_path) + g_setenv ("PATH", setup_data->old_path, TRUE); + else + g_unsetenv ("PATH"); + + if (setup_data->old_snap) + g_setenv ("SNAP", setup_data->old_snap, TRUE); + else + g_unsetenv ("SNAP"); + + g_clear_pointer (&setup_data->old_path, g_free); + g_clear_pointer (&setup_data->old_snap, g_free); +} + +static void +test_portal_support_snap_no_snapctl (SetupData *setup, + gconstpointer data) +{ + g_assert_false (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); +} + +static void +test_portal_support_snap_none (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, TRUE); + create_fake_snapctl (setup->bin_path, NULL); + + g_assert_false (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); +} + +static void +test_portal_support_snap_all (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, TRUE); + create_fake_snapctl (setup->bin_path, "desktop|network-status|gsettings"); + + g_assert_false (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); + + g_test_add ("/portal-support/snap-classic/no-snapctl", SetupData, NULL, + tests_setup, test_portal_support_snap_no_snapctl, tests_teardown); + g_test_add ("/portal-support/snap-classic/none", SetupData, NULL, + tests_setup, test_portal_support_snap_none, tests_teardown); + g_test_add ("/portal-support/snap-classic/all", SetupData, NULL, + tests_setup, test_portal_support_snap_all, tests_teardown); + + return g_test_run (); +} diff --git a/gio/tests/portal-support-snap.c b/gio/tests/portal-support-snap.c new file mode 100644 index 000000000..7dd14d82f --- /dev/null +++ b/gio/tests/portal-support-snap.c @@ -0,0 +1,208 @@ +/* + * GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2022 Canonical Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Author: Marco Trevisan + */ + +#include "portal-support-utils.h" + +#include "../gportalsupport.h" +#include +#include + +typedef struct +{ + char *old_path; + char *old_snap; + + const char *bin_path; + const char *snap_path; +} SetupData; + +static void +tests_setup (SetupData *setup_data, + gconstpointer data) +{ + setup_data->old_path = g_strdup (g_getenv ("PATH")); + setup_data->old_snap = g_strdup (g_getenv ("SNAP")); + + setup_data->bin_path = g_get_user_runtime_dir (); + setup_data->snap_path = g_getenv ("G_TEST_TMPDIR"); + + g_assert_nonnull (setup_data->bin_path); + g_assert_nonnull (setup_data->snap_path); + + g_setenv ("PATH", setup_data->bin_path, TRUE); + g_setenv ("SNAP", setup_data->snap_path, TRUE); +} + +static void +tests_teardown (SetupData *setup_data, + gconstpointer data) +{ + if (setup_data->old_path) + g_setenv ("PATH", setup_data->old_path, TRUE); + else + g_unsetenv ("PATH"); + + if (setup_data->old_snap) + g_setenv ("SNAP", setup_data->old_snap, TRUE); + else + g_unsetenv ("SNAP"); + + g_clear_pointer (&setup_data->old_path, g_free); + g_clear_pointer (&setup_data->old_snap, g_free); +} + +static void +test_portal_support_snap_no_snapctl (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, FALSE); + + g_assert_false (glib_should_use_portal ()); + g_assert_false (glib_network_available_in_sandbox ()); + g_assert_false (glib_has_dconf_access_in_sandbox ()); +} + +static void +test_portal_support_snap_none (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, FALSE); + create_fake_snapctl (setup->bin_path, NULL); + + g_assert_false (glib_should_use_portal ()); + g_assert_false (glib_network_available_in_sandbox ()); + g_assert_false (glib_has_dconf_access_in_sandbox ()); +} + +static void +test_portal_support_snap_all (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, FALSE); + create_fake_snapctl (setup->bin_path, "desktop|network-status|gsettings"); + + g_assert_true (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); +} + +static void +test_portal_support_snap_desktop_only (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, FALSE); + create_fake_snapctl (setup->bin_path, "desktop"); + + g_assert_true (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_false (glib_has_dconf_access_in_sandbox ()); +} + +static void +test_portal_support_snap_network_only (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, FALSE); + create_fake_snapctl (setup->bin_path, "network-status"); + + g_assert_false (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_false (glib_has_dconf_access_in_sandbox ()); +} + +static void +test_portal_support_snap_gsettings_only (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, FALSE); + create_fake_snapctl (setup->bin_path, "gsettings"); + + g_assert_false (glib_should_use_portal ()); + g_assert_false (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); +} + +static void +test_portal_support_snap_updates_dynamically (SetupData *setup, + gconstpointer data) +{ + create_fake_snap_yaml (setup->snap_path, FALSE); + create_fake_snapctl (setup->bin_path, NULL); + + g_assert_false (glib_should_use_portal ()); + g_assert_false (glib_network_available_in_sandbox ()); + g_assert_false (glib_has_dconf_access_in_sandbox ()); + + create_fake_snapctl (setup->bin_path, "desktop"); + g_assert_true (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_false (glib_has_dconf_access_in_sandbox ()); + + create_fake_snapctl (setup->bin_path, "network-status|gsettings"); + g_assert_false (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); + + create_fake_snapctl (setup->bin_path, "desktop|network-status|gsettings"); + g_assert_true (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); + + create_fake_snapctl (setup->bin_path, "desktop|gsettings"); + g_assert_true (glib_should_use_portal ()); + g_assert_true (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); + + create_fake_snapctl (setup->bin_path, "gsettings"); + g_assert_false (glib_should_use_portal ()); + g_assert_false (glib_network_available_in_sandbox ()); + g_assert_true (glib_has_dconf_access_in_sandbox ()); + + create_fake_snapctl (setup->bin_path, NULL); + g_assert_false (glib_should_use_portal ()); + g_assert_false (glib_network_available_in_sandbox ()); + g_assert_false (glib_has_dconf_access_in_sandbox ()); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); + + g_test_add ("/portal-support/snap/no-snapctl", SetupData, NULL, + tests_setup, test_portal_support_snap_no_snapctl, tests_teardown); + g_test_add ("/portal-support/snap/none", SetupData, NULL, + tests_setup, test_portal_support_snap_none, tests_teardown); + g_test_add ("/portal-support/snap/all", SetupData, NULL, + tests_setup, test_portal_support_snap_all, tests_teardown); + g_test_add ("/portal-support/snap/desktop-only", SetupData, NULL, + tests_setup, test_portal_support_snap_desktop_only, tests_teardown); + g_test_add ("/portal-support/snap/network-only", SetupData, NULL, + tests_setup, test_portal_support_snap_network_only, tests_teardown); + g_test_add ("/portal-support/snap/gsettings-only", SetupData, NULL, + tests_setup, test_portal_support_snap_gsettings_only, tests_teardown); + g_test_add ("/portal-support/snap/updates-dynamically", SetupData, NULL, + tests_setup, test_portal_support_snap_updates_dynamically, tests_teardown); + + return g_test_run (); +} diff --git a/gio/tests/portal-support-utils.c b/gio/tests/portal-support-utils.c new file mode 100644 index 000000000..9fb69287a --- /dev/null +++ b/gio/tests/portal-support-utils.c @@ -0,0 +1,87 @@ +/* + * GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2022 Canonical Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Author: Marco Trevisan + */ + +#include "portal-support-utils.h" + +#include +#include + +void +create_fake_snapctl (const char *path, + const char *supported_op) +{ + GError *error = NULL; + char *snapctl_content; + char *snapctl; + + snapctl = g_build_filename (path, "snapctl", NULL); + snapctl_content = g_strdup_printf ("#!/bin/sh\n" \ + "[ \"$1\" != 'is-connected' ] && exit 2\n" + "[ -z \"$2\" ] && exit 3\n" + "[ -n \"$3\" ] && exit 4\n" + "case \"$2\" in\n" + " %s) exit 0;;\n" + " *) exit 1;;\n" + "esac\n", + supported_op ? supported_op : ""); + + g_file_set_contents (snapctl, snapctl_content, -1, &error); + g_assert_no_error (error); + g_assert_cmpint (g_chmod (snapctl, 0500), ==, 0); + + g_test_message ("Created snapctl in %s", snapctl); + + g_clear_error (&error); + g_free (snapctl_content); + g_free (snapctl); +} + +void +create_fake_snap_yaml (const char *snap_path, + gboolean is_classic) +{ + char *meta_path; + char *yaml_path; + char *yaml_contents; + + g_assert_nonnull (snap_path); + + yaml_contents = g_strconcat ("name: glib-test-portal-support\n" + "title: GLib Portal Support Test\n" + "version: 2.76\n" + "summary: Test it works\n", + is_classic ? + "confinement: classic\n" : NULL, NULL); + + meta_path = g_build_filename (snap_path, "meta", NULL); + g_assert_cmpint (g_mkdir_with_parents (meta_path, 0700), ==, 0); + + yaml_path = g_build_filename (meta_path, "snap.yaml", NULL); + g_file_set_contents (yaml_path, yaml_contents, -1, NULL); + + g_test_message ("Created snap.yaml in %s", yaml_path); + + g_free (meta_path); + g_free (yaml_path); + g_free (yaml_contents); +} diff --git a/gio/tests/portal-support-utils.h b/gio/tests/portal-support-utils.h new file mode 100644 index 000000000..bb5a31c6e --- /dev/null +++ b/gio/tests/portal-support-utils.h @@ -0,0 +1,30 @@ +/* + * GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2022 Canonical Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Author: Marco Trevisan + */ + +#include + +void create_fake_snap_yaml (const char *snap_path, + gboolean is_classic); + +void create_fake_snapctl (const char *path, + const char *supported_op); diff --git a/gio/tests/sandbox.c b/gio/tests/sandbox.c new file mode 100644 index 000000000..a432556dd --- /dev/null +++ b/gio/tests/sandbox.c @@ -0,0 +1,80 @@ +/* + * Copyright 2022 Canonical Ltd + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + */ + +#include "portal-support-utils.h" + +#include "../gsandbox.h" +#include +#include + +static void +test_sandbox_none (void) +{ + g_assert_cmpint (glib_get_sandbox_type (), ==, G_SANDBOX_TYPE_UNKNOWN); +} + +static void +test_sandbox_snap (void) +{ + const char *temp_dir; + gchar *snap_path; + + temp_dir = g_getenv ("G_TEST_TMPDIR"); + g_assert_nonnull (temp_dir); + + snap_path = g_build_filename (temp_dir, "snap", "current", NULL); + create_fake_snap_yaml (snap_path, FALSE); + g_setenv ("SNAP", snap_path, TRUE); + + g_assert_cmpint (glib_get_sandbox_type (), ==, G_SANDBOX_TYPE_SNAP); + + g_unsetenv ("SNAP"); + g_free (snap_path); +} + +static void +test_sandbox_snap_classic (void) +{ + const char *temp_dir; + char *snap_path; + + temp_dir = g_getenv ("G_TEST_TMPDIR"); + g_assert_nonnull (temp_dir); + + snap_path = g_build_filename (temp_dir, "snap", "current", NULL); + create_fake_snap_yaml (snap_path, TRUE); + g_setenv ("SNAP", snap_path, TRUE); + + g_assert_cmpint (glib_get_sandbox_type (), ==, G_SANDBOX_TYPE_UNKNOWN); + + g_unsetenv ("SNAP"); + g_free (snap_path); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); + + g_test_add_func ("/sandbox/none", test_sandbox_none); + g_test_add_func ("/sandbox/snap", test_sandbox_snap); + g_test_add_func ("/sandbox/classic-snap", test_sandbox_snap_classic); + + return g_test_run (); +} -- 2.39.2