diff options
author | Simon McVittie <smcv@collabora.com> | 2023-05-15 17:33:01 +0100 |
---|---|---|
committer | Simon McVittie <smcv@collabora.com> | 2023-05-15 19:54:51 +0100 |
commit | db7a8bb598d34b65cecc32d7af9056f9e75ac611 (patch) | |
tree | e02ee06705695dd377d74cd39a9b229e84a6d5c6 | |
parent | 1cbff35386f4e6584646199a26fdfe82e72d732b (diff) | |
download | flatpak-db7a8bb598d34b65cecc32d7af9056f9e75ac611.tar.gz |
common: Split up socket setup from flatpak-run into multiple files
flatpak-run is large enough to be getting unwieldy, so separate it out
into various smaller modules.
A side benefit of these is that they'll be easier to reuse in other
projects, like Steam's pressure-vessel tool.
Signed-off-by: Simon McVittie <smcv@collabora.com>
-rw-r--r-- | common/Makefile.am.inc | 10 | ||||
-rw-r--r-- | common/flatpak-run-cups-private.h | 32 | ||||
-rw-r--r-- | common/flatpak-run-cups.c | 127 | ||||
-rw-r--r-- | common/flatpak-run-dbus-private.h | 52 | ||||
-rw-r--r-- | common/flatpak-run-dbus.c | 449 | ||||
-rw-r--r-- | common/flatpak-run-private.h | 7 | ||||
-rw-r--r-- | common/flatpak-run-pulseaudio-private.h | 34 | ||||
-rw-r--r-- | common/flatpak-run-pulseaudio.c | 338 | ||||
-rw-r--r-- | common/flatpak-run-sockets-private.h | 37 | ||||
-rw-r--r-- | common/flatpak-run-sockets.c | 278 | ||||
-rw-r--r-- | common/flatpak-run-x11-private.h | 42 | ||||
-rw-r--r-- | common/flatpak-run-x11.c | 333 | ||||
-rw-r--r-- | common/flatpak-run.c | 1371 | ||||
-rw-r--r-- | common/meson.build | 5 | ||||
-rw-r--r-- | tests/testcommon.c | 1 |
15 files changed, 1744 insertions, 1372 deletions
diff --git a/common/Makefile.am.inc b/common/Makefile.am.inc index aa4dc6e2..a6050289 100644 --- a/common/Makefile.am.inc +++ b/common/Makefile.am.inc @@ -161,6 +161,16 @@ libflatpak_common_la_SOURCES = \ common/flatpak-remote.c \ common/flatpak-run-private.h \ common/flatpak-run.c \ + common/flatpak-run-cups-private.h \ + common/flatpak-run-cups.c \ + common/flatpak-run-dbus-private.h \ + common/flatpak-run-dbus.c \ + common/flatpak-run-pulseaudio-private.h \ + common/flatpak-run-pulseaudio.c \ + common/flatpak-run-sockets-private.h \ + common/flatpak-run-sockets.c \ + common/flatpak-run-x11-private.h \ + common/flatpak-run-x11.c \ common/flatpak-syscalls-private.h \ common/flatpak-transaction-private.h \ common/flatpak-transaction.c \ diff --git a/common/flatpak-run-cups-private.h b/common/flatpak-run-cups-private.h new file mode 100644 index 00000000..13c0b6d8 --- /dev/null +++ b/common/flatpak-run-cups-private.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2014 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#pragma once + +#include "libglnx.h" + +#include "flatpak-bwrap-private.h" +#include "flatpak-common-types-private.h" + +G_BEGIN_DECLS + +void flatpak_run_add_cups_args (FlatpakBwrap *bwrap); + +G_END_DECLS diff --git a/common/flatpak-run-cups.c b/common/flatpak-run-cups.c new file mode 100644 index 00000000..c355e0f8 --- /dev/null +++ b/common/flatpak-run-cups.c @@ -0,0 +1,127 @@ +/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: + * Copyright © 2014-2019 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#include "config.h" +#include "flatpak-run-cups-private.h" + +#include "flatpak-utils-private.h" + +static gboolean +flatpak_run_cups_check_server_is_socket (const char *server) +{ + if (g_str_has_prefix (server, "/") && strstr (server, ":") == NULL) + return TRUE; + + return FALSE; +} + +/* Try to find a default server from a cups confguration file */ +static char * +flatpak_run_get_cups_server_name_config (const char *path) +{ + g_autoptr(GFile) file = g_file_new_for_path (path); + g_autoptr(GError) my_error = NULL; + g_autoptr(GFileInputStream) input_stream = NULL; + g_autoptr(GDataInputStream) data_stream = NULL; + size_t len; + + input_stream = g_file_read (file, NULL, &my_error); + if (my_error) + { + g_info ("CUPS configuration file '%s': %s", path, my_error->message); + return NULL; + } + + data_stream = g_data_input_stream_new (G_INPUT_STREAM (input_stream)); + + while (TRUE) + { + g_autofree char *line = g_data_input_stream_read_line (data_stream, &len, NULL, NULL); + if (line == NULL) + break; + + g_strchug (line); + + if ((*line == '\0') || (*line == '#')) + continue; + + g_auto(GStrv) tokens = g_strsplit (line, " ", 2); + + if ((tokens[0] != NULL) && (tokens[1] != NULL)) + { + if (strcmp ("ServerName", tokens[0]) == 0) + { + g_strchug (tokens[1]); + + if (flatpak_run_cups_check_server_is_socket (tokens[1])) + return g_strdup (tokens[1]); + } + } + } + + return NULL; +} + +static char * +flatpak_run_get_cups_server_name (void) +{ + g_autofree char * cups_server = NULL; + g_autofree char * cups_config_path = NULL; + + /* TODO + * we don't currently support cups servers located on the network, if such + * server is detected, we simply ignore it and in the worst case we fallback + * to the default socket + */ + cups_server = g_strdup (g_getenv ("CUPS_SERVER")); + if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server)) + return g_steal_pointer (&cups_server); + g_clear_pointer (&cups_server, g_free); + + cups_config_path = g_build_filename (g_get_home_dir (), ".cups/client.conf", NULL); + cups_server = flatpak_run_get_cups_server_name_config (cups_config_path); + if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server)) + return g_steal_pointer (&cups_server); + g_clear_pointer (&cups_server, g_free); + + cups_server = flatpak_run_get_cups_server_name_config ("/etc/cups/client.conf"); + if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server)) + return g_steal_pointer (&cups_server); + + // Fallback to default socket + return g_strdup ("/var/run/cups/cups.sock"); +} + +void +flatpak_run_add_cups_args (FlatpakBwrap *bwrap) +{ + g_autofree char * sandbox_server_name = g_strdup ("/var/run/cups/cups.sock"); + g_autofree char * cups_server_name = flatpak_run_get_cups_server_name (); + + if (!g_file_test (cups_server_name, G_FILE_TEST_EXISTS)) + { + g_info ("Could not find CUPS server"); + return; + } + + flatpak_bwrap_add_args (bwrap, + "--ro-bind", cups_server_name, sandbox_server_name, + NULL); +} diff --git a/common/flatpak-run-dbus-private.h b/common/flatpak-run-dbus-private.h new file mode 100644 index 00000000..3b56264f --- /dev/null +++ b/common/flatpak-run-dbus-private.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2014 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#pragma once + +#include "libglnx.h" + +#include "flatpak-bwrap-private.h" +#include "flatpak-common-types-private.h" +#include "flatpak-context-private.h" + +G_BEGIN_DECLS + +gboolean flatpak_run_add_session_dbus_args (FlatpakBwrap *app_bwrap, + FlatpakBwrap *proxy_arg_bwrap, + FlatpakContext *context, + FlatpakRunFlags flags, + const char *app_id); + +gboolean flatpak_run_add_system_dbus_args (FlatpakBwrap *app_bwrap, + FlatpakBwrap *proxy_arg_bwrap, + FlatpakContext *context, + FlatpakRunFlags flags); + +gboolean flatpak_run_add_a11y_dbus_args (FlatpakBwrap *app_bwrap, + FlatpakBwrap *proxy_arg_bwrap, + FlatpakContext *context, + FlatpakRunFlags flags); + +gboolean flatpak_run_maybe_start_dbus_proxy (FlatpakBwrap *app_bwrap, + FlatpakBwrap *proxy_arg_bwrap, + const char *app_info_path, + GError **error); + +G_END_DECLS diff --git a/common/flatpak-run-dbus.c b/common/flatpak-run-dbus.c new file mode 100644 index 00000000..1706067b --- /dev/null +++ b/common/flatpak-run-dbus.c @@ -0,0 +1,449 @@ +/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: + * Copyright © 2014-2019 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#include "config.h" +#include "flatpak-run-dbus-private.h" + +#include <glib/gi18n-lib.h> + +#include <sys/vfs.h> + +#include "flatpak-utils-private.h" + +/* This wraps the argv in a bwrap call, primary to allow the + command to be run with a proper /.flatpak-info with data + taken from app_info_path */ +static gboolean +add_bwrap_wrapper (FlatpakBwrap *bwrap, + const char *app_info_path, + GError **error) +{ + glnx_autofd int app_info_fd = -1; + g_auto(GLnxDirFdIterator) dir_iter = { 0 }; + struct dirent *dent; + g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir (); + g_autofree char *proxy_socket_dir = g_build_filename (user_runtime_dir, ".dbus-proxy/", NULL); + + app_info_fd = open (app_info_path, O_RDONLY | O_CLOEXEC); + if (app_info_fd == -1) + return glnx_throw_errno_prefix (error, _("Failed to open app info file")); + + if (!glnx_dirfd_iterator_init_at (AT_FDCWD, "/", FALSE, &dir_iter, error)) + return FALSE; + + flatpak_bwrap_add_arg (bwrap, flatpak_get_bwrap ()); + + while (TRUE) + { + glnx_autofd int o_path_fd = -1; + struct statfs stfs; + + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dir_iter, &dent, NULL, error)) + return FALSE; + + if (dent == NULL) + break; + + if (strcmp (dent->d_name, ".flatpak-info") == 0) + continue; + + /* O_PATH + fstatfs is the magic that we need to statfs without automounting the target */ + o_path_fd = openat (dir_iter.fd, dent->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC); + if (o_path_fd == -1 || fstatfs (o_path_fd, &stfs) != 0 || stfs.f_type == AUTOFS_SUPER_MAGIC) + continue; /* AUTOFS mounts are risky and can cause us to block (see issue #1633), so ignore it. Its unlikely the proxy needs such a directory. */ + + if (dent->d_type == DT_DIR) + { + if (strcmp (dent->d_name, "tmp") == 0 || + strcmp (dent->d_name, "var") == 0 || + strcmp (dent->d_name, "run") == 0) + flatpak_bwrap_add_arg (bwrap, "--bind"); + else + flatpak_bwrap_add_arg (bwrap, "--ro-bind"); + + flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name); + flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name); + } + else if (dent->d_type == DT_LNK) + { + g_autofree gchar *target = NULL; + + target = glnx_readlinkat_malloc (dir_iter.fd, dent->d_name, + NULL, error); + if (target == NULL) + return FALSE; + flatpak_bwrap_add_args (bwrap, "--symlink", target, NULL); + flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name); + } + } + + flatpak_bwrap_add_args (bwrap, "--bind", proxy_socket_dir, proxy_socket_dir, NULL); + + /* This is a file rather than a bind mount, because it will then + not be unmounted from the namespace when the namespace dies. */ + flatpak_bwrap_add_args (bwrap, "--perms", "0600", NULL); + flatpak_bwrap_add_args_data_fd (bwrap, "--file", glnx_steal_fd (&app_info_fd), "/.flatpak-info"); + + if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error)) + return FALSE; + + return TRUE; +} + +gboolean +flatpak_run_maybe_start_dbus_proxy (FlatpakBwrap *app_bwrap, + FlatpakBwrap *proxy_arg_bwrap, + const char *app_info_path, + GError **error) +{ + char x = 'x'; + const char *proxy; + g_autofree char *commandline = NULL; + g_autoptr(FlatpakBwrap) proxy_bwrap = NULL; + int sync_fds[2] = {-1, -1}; + int proxy_start_index; + + if (flatpak_bwrap_is_empty (proxy_arg_bwrap)) + { + g_debug ("D-Bus proxy not needed"); + return TRUE; + } + + proxy_bwrap = flatpak_bwrap_new (NULL); + + if (!add_bwrap_wrapper (proxy_bwrap, app_info_path, error)) + return FALSE; + + proxy = g_getenv ("FLATPAK_DBUSPROXY"); + if (proxy == NULL) + proxy = DBUSPROXY; + + flatpak_bwrap_add_arg (proxy_bwrap, proxy); + + proxy_start_index = proxy_bwrap->argv->len; + + if (pipe2 (sync_fds, O_CLOEXEC) < 0) + { + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), + _("Unable to create sync pipe")); + return FALSE; + } + + /* read end goes to app */ + flatpak_bwrap_add_args_data_fd (app_bwrap, "--sync-fd", sync_fds[0], NULL); + + /* write end goes to proxy */ + flatpak_bwrap_add_fd (proxy_bwrap, sync_fds[1]); + flatpak_bwrap_add_arg_printf (proxy_bwrap, "--fd=%d", sync_fds[1]); + + /* Note: This steals the fds from proxy_arg_bwrap */ + flatpak_bwrap_append_bwrap (proxy_bwrap, proxy_arg_bwrap); + + if (!flatpak_bwrap_bundle_args (proxy_bwrap, proxy_start_index, -1, TRUE, error)) + return FALSE; + + flatpak_bwrap_finish (proxy_bwrap); + + commandline = flatpak_quote_argv ((const char **) proxy_bwrap->argv->pdata, -1); + g_info ("Running '%s'", commandline); + + /* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */ + if (!g_spawn_async (NULL, + (char **) proxy_bwrap->argv->pdata, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, + flatpak_bwrap_child_setup_cb, proxy_bwrap->fds, + NULL, error)) + return FALSE; + + /* The write end can be closed now, otherwise the read below will hang of xdg-dbus-proxy + fails to start. */ + g_clear_pointer (&proxy_bwrap, flatpak_bwrap_free); + + /* Sync with proxy, i.e. wait until its listening on the sockets */ + if (read (sync_fds[0], &x, 1) != 1) + { + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), + _("Failed to sync with dbus proxy")); + return FALSE; + } + + return TRUE; +} + +static char * +extract_unix_path_from_dbus_address (const char *address) +{ + const char *path, *path_end; + + if (address == NULL) + return NULL; + + if (!g_str_has_prefix (address, "unix:")) + return NULL; + + path = strstr (address, "path="); + if (path == NULL) + return NULL; + path += strlen ("path="); + path_end = path; + while (*path_end != 0 && *path_end != ',') + path_end++; + + return g_strndup (path, path_end - path); +} + +static char * +create_proxy_socket (char *template) +{ + g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir (); + g_autofree char *proxy_socket_dir = g_build_filename (user_runtime_dir, ".dbus-proxy", NULL); + g_autofree char *proxy_socket = g_build_filename (proxy_socket_dir, template, NULL); + int fd; + + if (!glnx_shutil_mkdir_p_at (AT_FDCWD, proxy_socket_dir, 0755, NULL, NULL)) + return NULL; + + fd = g_mkstemp (proxy_socket); + if (fd == -1) + return NULL; + + close (fd); + + return g_steal_pointer (&proxy_socket); +} + +gboolean +flatpak_run_add_session_dbus_args (FlatpakBwrap *app_bwrap, + FlatpakBwrap *proxy_arg_bwrap, + FlatpakContext *context, + FlatpakRunFlags flags, + const char *app_id) +{ + static const char sandbox_socket_path[] = "/run/flatpak/bus"; + static const char sandbox_dbus_address[] = "unix:path=/run/flatpak/bus"; + gboolean unrestricted, no_proxy; + const char *dbus_address = g_getenv ("DBUS_SESSION_BUS_ADDRESS"); + g_autofree char *dbus_session_socket = NULL; + + unrestricted = (context->sockets & FLATPAK_CONTEXT_SOCKET_SESSION_BUS) != 0; + + if (dbus_address != NULL) + { + dbus_session_socket = extract_unix_path_from_dbus_address (dbus_address); + } + else + { + g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir (); + struct stat statbuf; + + dbus_session_socket = g_build_filename (user_runtime_dir, "bus", NULL); + + if (stat (dbus_session_socket, &statbuf) < 0 + || (statbuf.st_mode & S_IFMT) != S_IFSOCK + || statbuf.st_uid != getuid ()) + return FALSE; + } + + if (unrestricted) + g_info ("Allowing session-dbus access"); + + no_proxy = (flags & FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY) != 0; + + if (dbus_session_socket != NULL && unrestricted) + { + flatpak_bwrap_add_args (app_bwrap, + "--ro-bind", dbus_session_socket, sandbox_socket_path, + NULL); + flatpak_bwrap_set_env (app_bwrap, "DBUS_SESSION_BUS_ADDRESS", sandbox_dbus_address, TRUE); + flatpak_bwrap_add_runtime_dir_member (app_bwrap, "bus"); + + return TRUE; + } + else if (!no_proxy && dbus_address != NULL) + { + g_autofree char *proxy_socket = create_proxy_socket ("session-bus-proxy-XXXXXX"); + + if (proxy_socket == NULL) + return FALSE; + + flatpak_bwrap_add_args (proxy_arg_bwrap, dbus_address, proxy_socket, NULL); + + if (!unrestricted) + { + flatpak_context_add_bus_filters (context, app_id, TRUE, flags & FLATPAK_RUN_FLAG_SANDBOX, proxy_arg_bwrap); + + /* Allow calling any interface+method on all portals, but only receive broadcasts under /org/desktop/portal */ + flatpak_bwrap_add_arg (proxy_arg_bwrap, + "--call=org.freedesktop.portal.*=*"); + flatpak_bwrap_add_arg (proxy_arg_bwrap, + "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*"); + } + + if ((flags & FLATPAK_RUN_FLAG_LOG_SESSION_BUS) != 0) + flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL); + + flatpak_bwrap_add_args (app_bwrap, + "--ro-bind", proxy_socket, sandbox_socket_path, + NULL); + flatpak_bwrap_set_env (app_bwrap, "DBUS_SESSION_BUS_ADDRESS", sandbox_dbus_address, TRUE); + flatpak_bwrap_add_runtime_dir_member (app_bwrap, "bus"); + + return TRUE; + } + + return FALSE; +} + +gboolean +flatpak_run_add_system_dbus_args (FlatpakBwrap *app_bwrap, + FlatpakBwrap *proxy_arg_bwrap, + FlatpakContext *context, + FlatpakRunFlags flags) +{ + gboolean unrestricted, no_proxy; + const char *dbus_address = g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"); + g_autofree char *real_dbus_address = NULL; + g_autofree char *dbus_system_socket = NULL; + + unrestricted = (context->sockets & FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS) != 0; + if (unrestricted) + g_info ("Allowing system-dbus access"); + + no_proxy = (flags & FLATPAK_RUN_FLAG_NO_SYSTEM_BUS_PROXY) != 0; + + if (dbus_address != NULL) + dbus_system_socket = extract_unix_path_from_dbus_address (dbus_address); + else if (g_file_test ("/var/run/dbus/system_bus_socket", G_FILE_TEST_EXISTS)) + dbus_system_socket = g_strdup ("/var/run/dbus/system_bus_socket"); + + if (dbus_system_socket != NULL && unrestricted) + { + flatpak_bwrap_add_args (app_bwrap, + "--ro-bind", dbus_system_socket, "/run/dbus/system_bus_socket", + NULL); + flatpak_bwrap_set_env (app_bwrap, "DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket", TRUE); + + return TRUE; + } + else if (!no_proxy && flatpak_context_get_needs_system_bus_proxy (context)) + { + g_autofree char *proxy_socket = create_proxy_socket ("system-bus-proxy-XXXXXX"); + + if (proxy_socket == NULL) + return FALSE; + + if (dbus_address) + real_dbus_address = g_strdup (dbus_address); + else + real_dbus_address = g_strdup_printf ("unix:path=%s", dbus_system_socket); + + flatpak_bwrap_add_args (proxy_arg_bwrap, real_dbus_address, proxy_socket, NULL); + + if (!unrestricted) + flatpak_context_add_bus_filters (context, NULL, FALSE, flags & FLATPAK_RUN_FLAG_SANDBOX, proxy_arg_bwrap); + + if ((flags & FLATPAK_RUN_FLAG_LOG_SYSTEM_BUS) != 0) + flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL); + + flatpak_bwrap_add_args (app_bwrap, + "--ro-bind", proxy_socket, "/run/dbus/system_bus_socket", + NULL); + flatpak_bwrap_set_env (app_bwrap, "DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket", TRUE); + + return TRUE; + } + return FALSE; +} + +gboolean +flatpak_run_add_a11y_dbus_args (FlatpakBwrap *app_bwrap, + FlatpakBwrap *proxy_arg_bwrap, + FlatpakContext *context, + FlatpakRunFlags flags) +{ + static const char sandbox_socket_path[] = "/run/flatpak/at-spi-bus"; + static const char sandbox_dbus_address[] = "unix:path=/run/flatpak/at-spi-bus"; + g_autoptr(GDBusConnection) session_bus = NULL; + g_autofree char *a11y_address = NULL; + g_autoptr(GError) local_error = NULL; + g_autoptr(GDBusMessage) reply = NULL; + g_autoptr(GDBusMessage) msg = NULL; + g_autofree char *proxy_socket = NULL; + + if ((flags & FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY) != 0) + return FALSE; + + session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + if (session_bus == NULL) + return FALSE; + + msg = g_dbus_message_new_method_call ("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus", "GetAddress"); + g_dbus_message_set_body (msg, g_variant_new ("()")); + reply = + g_dbus_connection_send_message_with_reply_sync (session_bus, msg, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + 30000, + NULL, + NULL, + NULL); + if (reply) + { + if (g_dbus_message_to_gerror (reply, &local_error)) + { + if (!g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) + g_message ("Can't find a11y bus: %s", local_error->message); + } + else + { + g_variant_get (g_dbus_message_get_body (reply), + "(s)", &a11y_address); + } + } + + if (!a11y_address) + return FALSE; + + proxy_socket = create_proxy_socket ("a11y-bus-proxy-XXXXXX"); + if (proxy_socket == NULL) + return FALSE; + + flatpak_bwrap_add_args (proxy_arg_bwrap, + a11y_address, + proxy_socket, "--filter", "--sloppy-names", + "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Embed@/org/a11y/atspi/accessible/root", + "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Unembed@/org/a11y/atspi/accessible/root", + "--call=org.a11y.atspi.Registry=org.a11y.atspi.Registry.GetRegisteredEvents@/org/a11y/atspi/registry", + "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetKeystrokeListeners@/org/a11y/atspi/registry/deviceeventcontroller", + "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetDeviceEventListeners@/org/a11y/atspi/registry/deviceeventcontroller", + "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersSync@/org/a11y/atspi/registry/deviceeventcontroller", + "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersAsync@/org/a11y/atspi/registry/deviceeventcontroller", + NULL); + + if ((flags & FLATPAK_RUN_FLAG_LOG_A11Y_BUS) != 0) + flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL); + + flatpak_bwrap_add_args (app_bwrap, + "--ro-bind", proxy_socket, sandbox_socket_path, + NULL); + flatpak_bwrap_set_env (app_bwrap, "AT_SPI_BUS_ADDRESS", sandbox_dbus_address, TRUE); + + return TRUE; +} diff --git a/common/flatpak-run-private.h b/common/flatpak-run-private.h index 72b45c8d..f0f2dce1 100644 --- a/common/flatpak-run-private.h +++ b/common/flatpak-run-private.h @@ -200,11 +200,4 @@ gboolean flatpak_run_app (FlatpakDecomposed *app_ref, extern const char * const *flatpak_abs_usrmerged_dirs; -gboolean flatpak_run_parse_x11_display (const char *display, - int *family, - char **x11_socket, - char **remote_host, - char **original_display_nr, - GError **error); - #endif /* __FLATPAK_RUN_H__ */ diff --git a/common/flatpak-run-pulseaudio-private.h b/common/flatpak-run-pulseaudio-private.h new file mode 100644 index 00000000..8fa4eb5a --- /dev/null +++ b/common/flatpak-run-pulseaudio-private.h @@ -0,0 +1,34 @@ +/* + * Copyright © 2014 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#pragma once + +#include "libglnx.h" + +#include "flatpak-bwrap-private.h" +#include "flatpak-common-types-private.h" +#include "flatpak-context-private.h" + +G_BEGIN_DECLS + +void flatpak_run_add_pulseaudio_args (FlatpakBwrap *bwrap, + FlatpakContextShares shares); + +G_END_DECLS diff --git a/common/flatpak-run-pulseaudio.c b/common/flatpak-run-pulseaudio.c new file mode 100644 index 00000000..9495f8a2 --- /dev/null +++ b/common/flatpak-run-pulseaudio.c @@ -0,0 +1,338 @@ +/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: + * Copyright © 2014-2019 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#include "config.h" +#include "flatpak-run-pulseaudio-private.h" + +#include "flatpak-utils-private.h" + +/* Try to find a default server from a pulseaudio confguration file */ +static char * +flatpak_run_get_pulseaudio_server_user_config (const char *path) +{ + g_autoptr(GFile) file = g_file_new_for_path (path); + g_autoptr(GError) my_error = NULL; + g_autoptr(GFileInputStream) input_stream = NULL; + g_autoptr(GDataInputStream) data_stream = NULL; + size_t len; + + input_stream = g_file_read (file, NULL, &my_error); + if (my_error) + { + g_info ("Pulseaudio user configuration file '%s': %s", path, my_error->message); + return NULL; + } + + data_stream = g_data_input_stream_new (G_INPUT_STREAM (input_stream)); + + while (TRUE) + { + g_autofree char *line = g_data_input_stream_read_line (data_stream, &len, NULL, NULL); + if (line == NULL) + break; + + g_strchug (line); + + if ((*line == '\0') || (*line == ';') || (*line == '#')) + continue; + + if (g_str_has_prefix (line, ".include ")) + { + g_autofree char *rec_path = g_strdup (line + 9); + g_strstrip (rec_path); + char *found = flatpak_run_get_pulseaudio_server_user_config (rec_path); + if (found) + return found; + } + else if (g_str_has_prefix (line, "[")) + { + return NULL; + } + else + { + g_auto(GStrv) tokens = g_strsplit (line, "=", 2); + + if ((tokens[0] != NULL) && (tokens[1] != NULL)) + { + g_strchomp (tokens[0]); + if (strcmp ("default-server", tokens[0]) == 0) + { + g_strstrip (tokens[1]); + g_info ("Found pulseaudio socket from configuration file '%s': %s", path, tokens[1]); + return g_strdup (tokens[1]); + } + } + } + } + + return NULL; +} + +static char * +flatpak_run_get_pulseaudio_server (void) +{ + const char * pulse_clientconfig; + char *pulse_server; + g_autofree char *pulse_user_config = NULL; + + pulse_server = g_strdup (g_getenv ("PULSE_SERVER")); + if (pulse_server) + return pulse_server; + + pulse_clientconfig = g_getenv ("PULSE_CLIENTCONFIG"); + if (pulse_clientconfig) + return flatpak_run_get_pulseaudio_server_user_config (pulse_clientconfig); + + pulse_user_config = g_build_filename (g_get_user_config_dir (), "pulse/client.conf", NULL); + pulse_server = flatpak_run_get_pulseaudio_server_user_config (pulse_user_config); + if (pulse_server) + return pulse_server; + + pulse_server = flatpak_run_get_pulseaudio_server_user_config ("/etc/pulse/client.conf"); + if (pulse_server) + return pulse_server; + + return NULL; +} + +/* + * Parse a PulseAudio server string, as documented on + * https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/ServerStrings/. + * Returns the first supported server address, or NULL if none are supported, + * or NULL with @remote set if @value points to a remote server. + */ +static char * +flatpak_run_parse_pulse_server (const char *value, + gboolean *remote) +{ + g_auto(GStrv) servers = g_strsplit (value, " ", 0); + gsize i; + + for (i = 0; servers[i] != NULL; i++) + { + const char *server = servers[i]; + if (g_str_has_prefix (server, "{")) + { + /* + * TODO: compare the value within {} to the local hostname and D-Bus machine ID, + * and skip if it matches neither. + */ + const char * closing = strstr (server, "}"); + if (closing == NULL) + continue; + server = closing + 1; + } + + if (g_str_has_prefix (server, "unix:")) + return g_strdup (server + 5); + if (server[0] == '/') + return g_strdup (server); + + if (g_str_has_prefix (server, "tcp:")) + { + *remote = TRUE; + return NULL; + } + } + + return NULL; +} + +/* + * Get the machine ID as used by PulseAudio. This is the systemd/D-Bus + * machine ID, or failing that, the hostname. + */ +static char * +flatpak_run_get_pulse_machine_id (void) +{ + static const char * const machine_ids[] = + { + "/etc/machine-id", + "/var/lib/dbus/machine-id", + }; + gsize i; + + for (i = 0; i < G_N_ELEMENTS (machine_ids); i++) + { + g_autofree char *ret = NULL; + + if (g_file_get_contents (machine_ids[i], &ret, NULL, NULL)) + { + gsize j; + + g_strstrip (ret); + + for (j = 0; ret[j] != '\0'; j++) + { + if (!g_ascii_isxdigit (ret[j])) + break; + } + + if (ret[0] != '\0' && ret[j] == '\0') + return g_steal_pointer (&ret); + } + } + + return g_strdup (g_get_host_name ()); +} + +/* + * Get the directory used by PulseAudio for its configuration. + */ +static char * +flatpak_run_get_pulse_home (void) +{ + /* Legacy path ~/.pulse is tried first, for compatibility */ + { + const char *parent = g_get_home_dir (); + g_autofree char *ret = g_build_filename (parent, ".pulse", NULL); + + if (g_file_test (ret, G_FILE_TEST_IS_DIR)) + return g_steal_pointer (&ret); + } + + /* The more modern path, usually ~/.config/pulse */ + { + const char *parent = g_get_user_config_dir (); + /* Usually ~/.config/pulse */ + g_autofree char *ret = g_build_filename (parent, "pulse", NULL); + + if (g_file_test (ret, G_FILE_TEST_IS_DIR)) + return g_steal_pointer (&ret); + } + + return NULL; +} + +/* + * Get the runtime directory used by PulseAudio for its socket. + */ +static char * +flatpak_run_get_pulse_runtime_dir (void) +{ + const char *val = NULL; + + val = g_getenv ("PULSE_RUNTIME_PATH"); + + if (val != NULL) + return realpath (val, NULL); + + { + const char *user_runtime_dir = g_get_user_runtime_dir (); + + if (user_runtime_dir != NULL) + { + g_autofree char *dir = g_build_filename (user_runtime_dir, "pulse", NULL); + + if (g_file_test (dir, G_FILE_TEST_IS_DIR)) + return realpath (dir, NULL); + } + } + + { + g_autofree char *pulse_home = flatpak_run_get_pulse_home (); + g_autofree char *machine_id = flatpak_run_get_pulse_machine_id (); + + if (pulse_home != NULL && machine_id != NULL) + { + /* This is usually a symlink, but we take its realpath() anyway */ + g_autofree char *dir = g_strdup_printf ("%s/%s-runtime", pulse_home, machine_id); + + if (g_file_test (dir, G_FILE_TEST_IS_DIR)) + return realpath (dir, NULL); + } + } + + return NULL; +} + +void +flatpak_run_add_pulseaudio_args (FlatpakBwrap *bwrap, + FlatpakContextShares shares) +{ + g_autofree char *pulseaudio_server = flatpak_run_get_pulseaudio_server (); + g_autofree char *pulseaudio_socket = NULL; + g_autofree char *pulse_runtime_dir = flatpak_run_get_pulse_runtime_dir (); + gboolean remote = FALSE; + + if (pulseaudio_server) + pulseaudio_socket = flatpak_run_parse_pulse_server (pulseaudio_server, + &remote); + + if (pulseaudio_socket == NULL && !remote) + { + pulseaudio_socket = g_build_filename (pulse_runtime_dir, "native", NULL); + + if (!g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS)) + g_clear_pointer (&pulseaudio_socket, g_free); + } + + if (pulseaudio_socket == NULL && !remote) + { + pulseaudio_socket = realpath ("/var/run/pulse/native", NULL); + + if (pulseaudio_socket && !g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS)) + g_clear_pointer (&pulseaudio_socket, g_free); + } + + flatpak_bwrap_unset_env (bwrap, "PULSE_SERVER"); + + if (remote) + { + if ((shares & FLATPAK_CONTEXT_SHARED_NETWORK) == 0) + { + g_warning ("Remote PulseAudio server configured."); + g_warning ("PulseAudio access will require --share=network permission."); + } + + g_info ("Using remote PulseAudio server \"%s\"", pulseaudio_server); + flatpak_bwrap_set_env (bwrap, "PULSE_SERVER", pulseaudio_server, TRUE); + } + else if (pulseaudio_socket && g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS)) + { + static const char sandbox_socket_path[] = "/run/flatpak/pulse/native"; + static const char pulse_server[] = "unix:/run/flatpak/pulse/native"; + static const char config_path[] = "/run/flatpak/pulse/config"; + gboolean share_shm = FALSE; /* TODO: When do we add this? */ + g_autofree char *client_config = g_strdup_printf ("enable-shm=%s\n", share_shm ? "yes" : "no"); + + /* FIXME - error handling */ + if (!flatpak_bwrap_add_args_data (bwrap, "pulseaudio", client_config, -1, config_path, NULL)) + return; + + flatpak_bwrap_add_args (bwrap, + "--ro-bind", pulseaudio_socket, sandbox_socket_path, + NULL); + + flatpak_bwrap_set_env (bwrap, "PULSE_SERVER", pulse_server, TRUE); + flatpak_bwrap_set_env (bwrap, "PULSE_CLIENTCONFIG", config_path, TRUE); + flatpak_bwrap_add_runtime_dir_member (bwrap, "pulse"); + } + else + g_info ("Could not find pulseaudio socket"); + + /* Also allow ALSA access. This was added in 1.8, and is not ideally named. However, + * since the practical permission of ALSA and PulseAudio are essentially the same, and + * since we don't want to add more permissions for something we plan to replace with + * portals/pipewire going forward we reinterpret pulseaudio to also mean ALSA. + */ + if (!remote && g_file_test ("/dev/snd", G_FILE_TEST_IS_DIR)) + flatpak_bwrap_add_args (bwrap, "--dev-bind", "/dev/snd", "/dev/snd", NULL); +} diff --git a/common/flatpak-run-sockets-private.h b/common/flatpak-run-sockets-private.h new file mode 100644 index 00000000..cd4a60e2 --- /dev/null +++ b/common/flatpak-run-sockets-private.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2014 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#pragma once + +#include "libglnx.h" + +#include "flatpak-bwrap-private.h" +#include "flatpak-common-types-private.h" +#include "flatpak-context-private.h" + +G_BEGIN_DECLS + +void flatpak_run_add_socket_args_environment (FlatpakBwrap *bwrap, + FlatpakContextShares shares, + FlatpakContextSockets sockets); +void flatpak_run_add_socket_args_late (FlatpakBwrap *bwrap, + FlatpakContextShares shares); + +G_END_DECLS diff --git a/common/flatpak-run-sockets.c b/common/flatpak-run-sockets.c new file mode 100644 index 00000000..e7767d26 --- /dev/null +++ b/common/flatpak-run-sockets.c @@ -0,0 +1,278 @@ +/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: + * Copyright © 2014-2019 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#include "config.h" +#include "flatpak-run-sockets-private.h" + +/* Setup for simple sockets that only need one function goes in this file. + * Setup for more complicated sockets should go in its own file. */ + +#include "flatpak-run-cups-private.h" +#include "flatpak-run-pulseaudio-private.h" +#include "flatpak-run-x11-private.h" +#include "flatpak-utils-private.h" + +/** + * flatpak_run_add_wayland_args: + * + * Returns: %TRUE if a Wayland socket was found. + */ +static gboolean +flatpak_run_add_wayland_args (FlatpakBwrap *bwrap) +{ + const char *wayland_display; + g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir (); + g_autofree char *wayland_socket = NULL; + g_autofree char *sandbox_wayland_socket = NULL; + gboolean res = FALSE; + struct stat statbuf; + + wayland_display = g_getenv ("WAYLAND_DISPLAY"); + if (!wayland_display) + wayland_display = "wayland-0"; + + if (wayland_display[0] == '/') + wayland_socket = g_strdup (wayland_display); + else + wayland_socket = g_build_filename (user_runtime_dir, wayland_display, NULL); + + if (!g_str_has_prefix (wayland_display, "wayland-") || + strchr (wayland_display, '/') != NULL) + { + wayland_display = "wayland-0"; + flatpak_bwrap_set_env (bwrap, "WAYLAND_DISPLAY", wayland_display, TRUE); + } + + sandbox_wayland_socket = g_strdup_printf ("/run/flatpak/%s", wayland_display); + + if (stat (wayland_socket, &statbuf) == 0 && + (statbuf.st_mode & S_IFMT) == S_IFSOCK) + { + res = TRUE; + flatpak_bwrap_add_args (bwrap, + "--ro-bind", wayland_socket, sandbox_wayland_socket, + NULL); + flatpak_bwrap_add_runtime_dir_member (bwrap, wayland_display); + } + return res; +} + +static void +flatpak_run_add_gssproxy_args (FlatpakBwrap *bwrap) +{ + /* We only expose the gssproxy user service. The gssproxy system service is + * not intended to be exposed to sandboxed environments. + */ + g_autofree char *gssproxy_host_dir = g_build_filename (g_get_user_runtime_dir (), "gssproxy", NULL); + const char *gssproxy_sandboxed_dir = "/run/flatpak/gssproxy/"; + + if (g_file_test (gssproxy_host_dir, G_FILE_TEST_EXISTS)) + flatpak_bwrap_add_args (bwrap, "--ro-bind", gssproxy_host_dir, gssproxy_sandboxed_dir, NULL); +} + +static void +flatpak_run_add_resolved_args (FlatpakBwrap *bwrap) +{ + const char *resolved_socket = "/run/systemd/resolve/io.systemd.Resolve"; + + if (g_file_test (resolved_socket, G_FILE_TEST_EXISTS)) + flatpak_bwrap_add_args (bwrap, "--bind", resolved_socket, resolved_socket, NULL); +} + +static void +flatpak_run_add_journal_args (FlatpakBwrap *bwrap) +{ + g_autofree char *journal_socket_socket = g_strdup ("/run/systemd/journal/socket"); + g_autofree char *journal_stdout_socket = g_strdup ("/run/systemd/journal/stdout"); + + if (g_file_test (journal_socket_socket, G_FILE_TEST_EXISTS)) + { + flatpak_bwrap_add_args (bwrap, + "--ro-bind", journal_socket_socket, journal_socket_socket, + NULL); + } + if (g_file_test (journal_stdout_socket, G_FILE_TEST_EXISTS)) + { + flatpak_bwrap_add_args (bwrap, + "--ro-bind", journal_stdout_socket, journal_stdout_socket, + NULL); + } +} + +static void +flatpak_run_add_pcsc_args (FlatpakBwrap *bwrap) +{ + const char * pcsc_socket; + const char * sandbox_pcsc_socket = "/run/pcscd/pcscd.comm"; + + pcsc_socket = g_getenv ("PCSCLITE_CSOCK_NAME"); + if (pcsc_socket) + { + if (!g_file_test (pcsc_socket, G_FILE_TEST_EXISTS)) + { + flatpak_bwrap_unset_env (bwrap, "PCSCLITE_CSOCK_NAME"); + return; + } + } + else + { + pcsc_socket = "/run/pcscd/pcscd.comm"; + if (!g_file_test (pcsc_socket, G_FILE_TEST_EXISTS)) + return; + } + + flatpak_bwrap_add_args (bwrap, + "--ro-bind", pcsc_socket, sandbox_pcsc_socket, + NULL); + flatpak_bwrap_set_env (bwrap, "PCSCLITE_CSOCK_NAME", sandbox_pcsc_socket, TRUE); +} + +static void +flatpak_run_add_gpg_agent_args (FlatpakBwrap *bwrap) +{ + const char * agent_socket; + g_autofree char * sandbox_agent_socket = NULL; + g_autoptr(GError) gpgconf_error = NULL; + g_autoptr(GSubprocess) process = NULL; + GInputStream *base_stream = NULL; + g_autoptr(GDataInputStream) data_stream = NULL; + + process = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE, + &gpgconf_error, + "gpgconf", "--list-dir", "agent-socket", NULL); + + if (gpgconf_error) + { + g_info ("GPG-Agent directories: %s", gpgconf_error->message); + return; + } + + base_stream = g_subprocess_get_stdout_pipe (process); + data_stream = g_data_input_stream_new (base_stream); + + agent_socket = g_data_input_stream_read_line (data_stream, + NULL, NULL, + &gpgconf_error); + + if (!agent_socket || gpgconf_error) + { + g_info ("GPG-Agent directories: %s", gpgconf_error->message); + return; + } + + sandbox_agent_socket = g_strdup_printf ("/run/user/%d/gnupg/S.gpg-agent", getuid ()); + + flatpak_bwrap_add_args (bwrap, + "--ro-bind-try", agent_socket, sandbox_agent_socket, + NULL); +} + +static void +flatpak_run_add_ssh_args (FlatpakBwrap *bwrap) +{ + static const char sandbox_auth_socket[] = "/run/flatpak/ssh-auth"; + const char * auth_socket; + + auth_socket = g_getenv ("SSH_AUTH_SOCK"); + + if (!auth_socket) + return; /* ssh agent not present */ + + if (!g_file_test (auth_socket, G_FILE_TEST_EXISTS)) + { + /* Let's clean it up, so that the application will not try to connect */ + flatpak_bwrap_unset_env (bwrap, "SSH_AUTH_SOCK"); + return; + } + + flatpak_bwrap_add_args (bwrap, + "--ro-bind", auth_socket, sandbox_auth_socket, + NULL); + flatpak_bwrap_set_env (bwrap, "SSH_AUTH_SOCK", sandbox_auth_socket, TRUE); +} + +/* + * Expose sockets that are available for `flatpak build`, apply_extra, and + * `flatpak run`, except for D-Bus which is handled separately due to its + * use of a proxy. + */ +void +flatpak_run_add_socket_args_environment (FlatpakBwrap *bwrap, + FlatpakContextShares shares, + FlatpakContextSockets sockets) +{ + gboolean has_wayland; + gboolean allow_x11; + + if (sockets & FLATPAK_CONTEXT_SOCKET_WAYLAND) + { + g_info ("Allowing wayland access"); + has_wayland = flatpak_run_add_wayland_args (bwrap); + } + + if ((sockets & FLATPAK_CONTEXT_SOCKET_FALLBACK_X11) != 0) + allow_x11 = !has_wayland; + else + allow_x11 = (sockets & FLATPAK_CONTEXT_SOCKET_X11) != 0; + + flatpak_run_add_x11_args (bwrap, allow_x11, shares); + + if (sockets & FLATPAK_CONTEXT_SOCKET_SSH_AUTH) + { + flatpak_run_add_ssh_args (bwrap); + } + + if (sockets & FLATPAK_CONTEXT_SOCKET_PULSEAUDIO) + { + g_info ("Allowing pulseaudio access"); + flatpak_run_add_pulseaudio_args (bwrap, shares); + } + + if (sockets & FLATPAK_CONTEXT_SOCKET_PCSC) + { + flatpak_run_add_pcsc_args (bwrap); + } + + if (sockets & FLATPAK_CONTEXT_SOCKET_CUPS) + { + flatpak_run_add_cups_args (bwrap); + } + + if (sockets & FLATPAK_CONTEXT_SOCKET_GPG_AGENT) + { + flatpak_run_add_gpg_agent_args (bwrap); + } +} + +/* + * Expose sockets that are available for `flatpak run` only. + */ +void +flatpak_run_add_socket_args_late (FlatpakBwrap *bwrap, + FlatpakContextShares shares) +{ + if ((shares & FLATPAK_CONTEXT_SHARED_NETWORK) != 0) + { + flatpak_run_add_gssproxy_args (bwrap); + flatpak_run_add_resolved_args (bwrap); + } + + flatpak_run_add_journal_args (bwrap); +} diff --git a/common/flatpak-run-x11-private.h b/common/flatpak-run-x11-private.h new file mode 100644 index 00000000..96da2e6b --- /dev/null +++ b/common/flatpak-run-x11-private.h @@ -0,0 +1,42 @@ +/* + * Copyright © 2014 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#pragma once + +#include "libglnx.h" + +#include "flatpak-bwrap-private.h" +#include "flatpak-common-types-private.h" +#include "flatpak-context-private.h" + +G_BEGIN_DECLS + +void flatpak_run_add_x11_args (FlatpakBwrap *bwrap, + gboolean allowed, + FlatpakContextShares shares); + +gboolean flatpak_run_parse_x11_display (const char *display, + int *family, + char **x11_socket, + char **remote_host, + char **original_display_nr, + GError **error); + +G_END_DECLS diff --git a/common/flatpak-run-x11.c b/common/flatpak-run-x11.c new file mode 100644 index 00000000..af8f0fa5 --- /dev/null +++ b/common/flatpak-run-x11.c @@ -0,0 +1,333 @@ +/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: + * Copyright © 2014-2019 Red Hat, Inc + * + * This program 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 <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#include "config.h" +#include "flatpak-run-x11-private.h" + +#include <sys/utsname.h> + +#ifdef ENABLE_XAUTH +#include <X11/Xauth.h> +#endif + +/* This is part of the X11 protocol, so we can safely hard-code it here */ +#define FamilyInternet6 (6) + +#ifdef ENABLE_XAUTH +static gboolean +auth_streq (const char *str, + const char *au_str, + size_t au_len) +{ + return au_len == strlen (str) && memcmp (str, au_str, au_len) == 0; +} + +static gboolean +xauth_entry_should_propagate (const Xauth *xa, + int family, + const char *remote_hostname, + const char *local_hostname, + const char *number) +{ + /* ensure entry isn't for a different type of access */ + if (family != FamilyWild && xa->family != family && xa->family != FamilyWild) + return FALSE; + + /* ensure entry isn't for remote access, except that if remote_hostname + * is specified, then remote access to that hostname is OK */ + if (xa->family != FamilyWild && xa->family != FamilyLocal && + (remote_hostname == NULL || + !auth_streq (remote_hostname, xa->address, xa->address_length))) + return FALSE; + + /* ensure entry is for this machine */ + if (xa->family == FamilyLocal && !auth_streq (local_hostname, xa->address, xa->address_length)) + { + /* OpenSUSE inherits the hostname value from DHCP without updating + * its X11 authentication cookie. The old hostname value can still + * be found in the environment variable XAUTHLOCALHOSTNAME. + * For reference: + * https://bugzilla.opensuse.org/show_bug.cgi?id=262309 + * For this reason if we have a cookie whose address is equal to the + * variable XAUTHLOCALHOSTNAME, we still need to propagate it, but + * we also need to change its address to `unames.nodename`. + */ + const char *xauth_local_hostname; + xauth_local_hostname = g_getenv ("XAUTHLOCALHOSTNAME"); + if (xauth_local_hostname == NULL) + return FALSE; + + if (!auth_streq ((char *) xauth_local_hostname, xa->address, xa->address_length)) + return FALSE; + } + + /* ensure entry is for this session */ + if (xa->number != NULL && !auth_streq (number, xa->number, xa->number_length)) + return FALSE; + + return TRUE; +} + +static void +write_xauth (int family, + const char *remote_host, + const char *number, + FILE *output) +{ + Xauth *xa, local_xa; + char *filename; + FILE *f; + struct utsname unames; + + if (uname (&unames)) + { + g_warning ("uname failed"); + return; + } + + filename = XauFileName (); + f = fopen (filename, "rb"); + if (f == NULL) + return; + + while (TRUE) + { + xa = XauReadAuth (f); + if (xa == NULL) + break; + if (xauth_entry_should_propagate (xa, family, remote_host, + unames.nodename, number)) + { + local_xa = *xa; + + if (local_xa.family == FamilyLocal && + !auth_streq (unames.nodename, local_xa.address, local_xa.address_length)) + { + /* If we decided to propagate this cookie, but its address + * doesn't match `unames.nodename`, we need to change it or + * inside the container it will not work. + */ + local_xa.address = unames.nodename; + local_xa.address_length = strlen (local_xa.address); + } + + if (!XauWriteAuth (output, &local_xa)) + g_warning ("xauth write error"); + } + + XauDisposeAuth (xa); + } + + fclose (f); +} +#else /* !ENABLE_XAUTH */ + +/* When not doing Xauth, any distinct values will do, but use the same + * ones Xauth does so that we can refer to them in our unit test. */ +#define FamilyLocal (256) +#define FamilyWild (65535) + +#endif /* !ENABLE_XAUTH */ + +/* + * @family: (out) (not optional): + * @x11_socket: (out) (not optional): + * @display_nr_out: (out) (not optional): + */ +gboolean +flatpak_run_parse_x11_display (const char *display, + int *family, + char **x11_socket, + char **remote_host, + char **display_nr_out, + GError **error) +{ + const char *colon; + const char *display_nr; + const char *display_nr_end; + + /* Use the last ':', not the first, to cope with [::1]:0 */ + colon = strrchr (display, ':'); + + if (colon == NULL) + return glnx_throw (error, "No colon found in DISPLAY=%s", display); + + if (!g_ascii_isdigit (colon[1])) + return glnx_throw (error, "Colon not followed by a digit in DISPLAY=%s", display); + + display_nr = &colon[1]; + display_nr_end = display_nr; + + while (g_ascii_isdigit (*display_nr_end)) + display_nr_end++; + + *display_nr_out = g_strndup (display_nr, display_nr_end - display_nr); + + if (display == colon || g_str_has_prefix (display, "unix:")) + { + *family = FamilyLocal; + *x11_socket = g_strdup_printf ("/tmp/.X11-unix/X%s", *display_nr_out); + } + else if (display[0] == '[' && display[colon - display - 1] == ']') + { + *family = FamilyInternet6; + *remote_host = g_strndup (display + 1, colon - display - 2); + } + else + { + *family = FamilyWild; + *remote_host = g_strndup (display, colon - display); + } + + return TRUE; +} + +void +flatpak_run_add_x11_args (FlatpakBwrap *bwrap, + gboolean allowed, + FlatpakContextShares shares) +{ + g_autofree char *x11_socket = NULL; + const char *display; + g_autoptr(GError) local_error = NULL; + + /* Always cover /tmp/.X11-unix, that way we never see the host one in case + * we have access to the host /tmp. If you request X access we'll put the right + * thing in this anyway. + * + * We need to be a bit careful here, because there are two situations in + * which potentially hostile processes have access to /tmp and could + * create symlinks, which in principle could cause us to create the + * directory and mount the tmpfs at the target of the symlink instead + * of in the intended place: + * + * - With --filesystem=/tmp, it's the host /tmp - but because of the + * special historical status of /tmp/.X11-unix, we can assume that + * it is pre-created by the host system before user code gets to run. + * + * - When /tmp is shared between all instances of the same app ID, + * in principle the app has control over what's in /tmp, but in + * practice it can't interfere with /tmp/.X11-unix, because we do + * this unconditionally - therefore by the time app code runs, + * /tmp/.X11-unix is already a mount point, meaning the app cannot + * rename or delete it. + */ + flatpak_bwrap_add_args (bwrap, + "--tmpfs", "/tmp/.X11-unix", + NULL); + + if (!allowed) + { + flatpak_bwrap_unset_env (bwrap, "DISPLAY"); + return; + } + + g_info ("Allowing x11 access"); + + display = g_getenv ("DISPLAY"); + + if (display != NULL) + { + g_autofree char *remote_host = NULL; + g_autofree char *display_nr = NULL; + int family = -1; + + if (!flatpak_run_parse_x11_display (display, &family, &x11_socket, + &remote_host, &display_nr, + &local_error)) + { + g_warning ("%s", local_error->message); + flatpak_bwrap_unset_env (bwrap, "DISPLAY"); + return; + } + + g_assert (display_nr != NULL); + + if (x11_socket != NULL + && g_file_test (x11_socket, G_FILE_TEST_EXISTS)) + { + g_assert (g_str_has_prefix (x11_socket, "/tmp/.X11-unix/X")); + flatpak_bwrap_add_args (bwrap, + "--ro-bind", x11_socket, x11_socket, + NULL); + flatpak_bwrap_set_env (bwrap, "DISPLAY", display, TRUE); + } + else if ((shares & FLATPAK_CONTEXT_SHARED_NETWORK) == 0) + { + /* If DISPLAY is for example :42 but /tmp/.X11-unix/X42 + * doesn't exist, then the only way this is going to work + * is if the app can connect to abstract socket + * @/tmp/.X11-unix/X42 or to TCP port localhost:6042, + * either of which requires a shared network namespace. + * + * Alternatively, if DISPLAY is othermachine:23, then we + * definitely need access to TCP port othermachine:6023. */ + if (x11_socket != NULL) + g_warning ("X11 socket %s does not exist in filesystem.", + x11_socket); + else + g_warning ("Remote X11 display detected."); + + g_warning ("X11 access will require --share=network permission."); + } + else if (x11_socket != NULL) + { + g_warning ("X11 socket %s does not exist in filesystem, " + "trying to use abstract socket instead.", + x11_socket); + } + else + { + g_debug ("Assuming --share=network gives access to remote X11"); + } + +#ifdef ENABLE_XAUTH + g_auto(GLnxTmpfile) xauth_tmpf = { 0, }; + + if (glnx_open_anonymous_tmpfile_full (O_RDWR | O_CLOEXEC, "/tmp", &xauth_tmpf, NULL)) + { + FILE *output = fdopen (xauth_tmpf.fd, "wb"); + if (output != NULL) + { + /* fd is now owned by output, steal it from the tmpfile */ + int tmp_fd = dup (glnx_steal_fd (&xauth_tmpf.fd)); + if (tmp_fd != -1) + { + static const char dest[] = "/run/flatpak/Xauthority"; + + write_xauth (family, remote_host, display_nr, output); + flatpak_bwrap_add_args_data_fd (bwrap, "--ro-bind-data", tmp_fd, dest); + + flatpak_bwrap_set_env (bwrap, "XAUTHORITY", dest, TRUE); + } + + fclose (output); + + if (tmp_fd != -1) + lseek (tmp_fd, 0, SEEK_SET); + } + } +#endif + } + else + { + flatpak_bwrap_unset_env (bwrap, "DISPLAY"); + } +} diff --git a/common/flatpak-run.c b/common/flatpak-run.c index fa549c19..3f970bbb 100644 --- a/common/flatpak-run.c +++ b/common/flatpak-run.c @@ -48,16 +48,14 @@ #include <seccomp.h> #endif -#ifdef ENABLE_XAUTH -#include <X11/Xauth.h> -#endif - #include <glib/gi18n-lib.h> #include <gio/gio.h> #include "libglnx.h" +#include "flatpak-run-dbus-private.h" #include "flatpak-run-private.h" +#include "flatpak-run-sockets-private.h" #include "flatpak-utils-base-private.h" #include "flatpak-dir-private.h" #include "flatpak-instance-private.h" @@ -79,1317 +77,6 @@ const char * const abs_usrmerged_dirs[] = }; const char * const *flatpak_abs_usrmerged_dirs = abs_usrmerged_dirs; -static char * -extract_unix_path_from_dbus_address (const char *address) -{ - const char *path, *path_end; - - if (address == NULL) - return NULL; - - if (!g_str_has_prefix (address, "unix:")) - return NULL; - - path = strstr (address, "path="); - if (path == NULL) - return NULL; - path += strlen ("path="); - path_end = path; - while (*path_end != 0 && *path_end != ',') - path_end++; - - return g_strndup (path, path_end - path); -} - -/* This is part of the X11 protocol, so we can safely hard-code it here */ -#define FamilyInternet6 (6) - -#ifdef ENABLE_XAUTH -static gboolean -auth_streq (const char *str, - const char *au_str, - size_t au_len) -{ - return au_len == strlen (str) && memcmp (str, au_str, au_len) == 0; -} - -static gboolean -xauth_entry_should_propagate (const Xauth *xa, - int family, - const char *remote_hostname, - const char *local_hostname, - const char *number) -{ - /* ensure entry isn't for a different type of access */ - if (family != FamilyWild && xa->family != family && xa->family != FamilyWild) - return FALSE; - - /* ensure entry isn't for remote access, except that if remote_hostname - * is specified, then remote access to that hostname is OK */ - if (xa->family != FamilyWild && xa->family != FamilyLocal && - (remote_hostname == NULL || - !auth_streq (remote_hostname, xa->address, xa->address_length))) - return FALSE; - - /* ensure entry is for this machine */ - if (xa->family == FamilyLocal && !auth_streq (local_hostname, xa->address, xa->address_length)) - { - /* OpenSUSE inherits the hostname value from DHCP without updating - * its X11 authentication cookie. The old hostname value can still - * be found in the environment variable XAUTHLOCALHOSTNAME. - * For reference: - * https://bugzilla.opensuse.org/show_bug.cgi?id=262309 - * For this reason if we have a cookie whose address is equal to the - * variable XAUTHLOCALHOSTNAME, we still need to propagate it, but - * we also need to change its address to `unames.nodename`. - */ - const char *xauth_local_hostname; - xauth_local_hostname = g_getenv ("XAUTHLOCALHOSTNAME"); - if (xauth_local_hostname == NULL) - return FALSE; - - if (!auth_streq ((char *) xauth_local_hostname, xa->address, xa->address_length)) - return FALSE; - } - - /* ensure entry is for this session */ - if (xa->number != NULL && !auth_streq (number, xa->number, xa->number_length)) - return FALSE; - - return TRUE; -} - -static void -write_xauth (int family, - const char *remote_host, - const char *number, - FILE *output) -{ - Xauth *xa, local_xa; - char *filename; - FILE *f; - struct utsname unames; - - if (uname (&unames)) - { - g_warning ("uname failed"); - return; - } - - filename = XauFileName (); - f = fopen (filename, "rb"); - if (f == NULL) - return; - - while (TRUE) - { - xa = XauReadAuth (f); - if (xa == NULL) - break; - if (xauth_entry_should_propagate (xa, family, remote_host, - unames.nodename, number)) - { - local_xa = *xa; - - if (local_xa.family == FamilyLocal && - !auth_streq (unames.nodename, local_xa.address, local_xa.address_length)) - { - /* If we decided to propagate this cookie, but its address - * doesn't match `unames.nodename`, we need to change it or - * inside the container it will not work. - */ - local_xa.address = unames.nodename; - local_xa.address_length = strlen (local_xa.address); - } - - if (!XauWriteAuth (output, &local_xa)) - g_warning ("xauth write error"); - } - - XauDisposeAuth (xa); - } - - fclose (f); -} -#else /* !ENABLE_XAUTH */ - -/* When not doing Xauth, any distinct values will do, but use the same - * ones Xauth does so that we can refer to them in our unit test. */ -#define FamilyLocal (256) -#define FamilyWild (65535) - -#endif /* !ENABLE_XAUTH */ - -/* - * @family: (out) (not optional): - * @x11_socket: (out) (not optional): - * @display_nr_out: (out) (not optional): - */ -gboolean -flatpak_run_parse_x11_display (const char *display, - int *family, - char **x11_socket, - char **remote_host, - char **display_nr_out, - GError **error) -{ - const char *colon; - const char *display_nr; - const char *display_nr_end; - - /* Use the last ':', not the first, to cope with [::1]:0 */ - colon = strrchr (display, ':'); - - if (colon == NULL) - return glnx_throw (error, "No colon found in DISPLAY=%s", display); - - if (!g_ascii_isdigit (colon[1])) - return glnx_throw (error, "Colon not followed by a digit in DISPLAY=%s", display); - - display_nr = &colon[1]; - display_nr_end = display_nr; - - while (g_ascii_isdigit (*display_nr_end)) - display_nr_end++; - - *display_nr_out = g_strndup (display_nr, display_nr_end - display_nr); - - if (display == colon || g_str_has_prefix (display, "unix:")) - { - *family = FamilyLocal; - *x11_socket = g_strdup_printf ("/tmp/.X11-unix/X%s", *display_nr_out); - } - else if (display[0] == '[' && display[colon - display - 1] == ']') - { - *family = FamilyInternet6; - *remote_host = g_strndup (display + 1, colon - display - 2); - } - else - { - *family = FamilyWild; - *remote_host = g_strndup (display, colon - display); - } - - return TRUE; -} - -static void -flatpak_run_add_x11_args (FlatpakBwrap *bwrap, - gboolean allowed, - FlatpakContextShares shares) -{ - g_autofree char *x11_socket = NULL; - const char *display; - g_autoptr(GError) local_error = NULL; - - /* Always cover /tmp/.X11-unix, that way we never see the host one in case - * we have access to the host /tmp. If you request X access we'll put the right - * thing in this anyway. - * - * We need to be a bit careful here, because there are two situations in - * which potentially hostile processes have access to /tmp and could - * create symlinks, which in principle could cause us to create the - * directory and mount the tmpfs at the target of the symlink instead - * of in the intended place: - * - * - With --filesystem=/tmp, it's the host /tmp - but because of the - * special historical status of /tmp/.X11-unix, we can assume that - * it is pre-created by the host system before user code gets to run. - * - * - When /tmp is shared between all instances of the same app ID, - * in principle the app has control over what's in /tmp, but in - * practice it can't interfere with /tmp/.X11-unix, because we do - * this unconditionally - therefore by the time app code runs, - * /tmp/.X11-unix is already a mount point, meaning the app cannot - * rename or delete it. - */ - flatpak_bwrap_add_args (bwrap, - "--tmpfs", "/tmp/.X11-unix", - NULL); - - if (!allowed) - { - flatpak_bwrap_unset_env (bwrap, "DISPLAY"); - return; - } - - g_info ("Allowing x11 access"); - - display = g_getenv ("DISPLAY"); - - if (display != NULL) - { - g_autofree char *remote_host = NULL; - g_autofree char *display_nr = NULL; - int family = -1; - - if (!flatpak_run_parse_x11_display (display, &family, &x11_socket, - &remote_host, &display_nr, - &local_error)) - { - g_warning ("%s", local_error->message); - flatpak_bwrap_unset_env (bwrap, "DISPLAY"); - return; - } - - g_assert (display_nr != NULL); - - if (x11_socket != NULL - && g_file_test (x11_socket, G_FILE_TEST_EXISTS)) - { - g_assert (g_str_has_prefix (x11_socket, "/tmp/.X11-unix/X")); - flatpak_bwrap_add_args (bwrap, - "--ro-bind", x11_socket, x11_socket, - NULL); - flatpak_bwrap_set_env (bwrap, "DISPLAY", display, TRUE); - } - else if ((shares & FLATPAK_CONTEXT_SHARED_NETWORK) == 0) - { - /* If DISPLAY is for example :42 but /tmp/.X11-unix/X42 - * doesn't exist, then the only way this is going to work - * is if the app can connect to abstract socket - * @/tmp/.X11-unix/X42 or to TCP port localhost:6042, - * either of which requires a shared network namespace. - * - * Alternatively, if DISPLAY is othermachine:23, then we - * definitely need access to TCP port othermachine:6023. */ - if (x11_socket != NULL) - g_warning ("X11 socket %s does not exist in filesystem.", - x11_socket); - else - g_warning ("Remote X11 display detected."); - - g_warning ("X11 access will require --share=network permission."); - } - else if (x11_socket != NULL) - { - g_warning ("X11 socket %s does not exist in filesystem, " - "trying to use abstract socket instead.", - x11_socket); - } - else - { - g_debug ("Assuming --share=network gives access to remote X11"); - } - -#ifdef ENABLE_XAUTH - g_auto(GLnxTmpfile) xauth_tmpf = { 0, }; - - if (glnx_open_anonymous_tmpfile_full (O_RDWR | O_CLOEXEC, "/tmp", &xauth_tmpf, NULL)) - { - FILE *output = fdopen (xauth_tmpf.fd, "wb"); - if (output != NULL) - { - /* fd is now owned by output, steal it from the tmpfile */ - int tmp_fd = dup (glnx_steal_fd (&xauth_tmpf.fd)); - if (tmp_fd != -1) - { - static const char dest[] = "/run/flatpak/Xauthority"; - - write_xauth (family, remote_host, display_nr, output); - flatpak_bwrap_add_args_data_fd (bwrap, "--ro-bind-data", tmp_fd, dest); - - flatpak_bwrap_set_env (bwrap, "XAUTHORITY", dest, TRUE); - } - - fclose (output); - - if (tmp_fd != -1) - lseek (tmp_fd, 0, SEEK_SET); - } - } -#endif - } - else - { - flatpak_bwrap_unset_env (bwrap, "DISPLAY"); - } -} - -static gboolean -flatpak_run_add_wayland_args (FlatpakBwrap *bwrap) -{ - const char *wayland_display; - g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir (); - g_autofree char *wayland_socket = NULL; - g_autofree char *sandbox_wayland_socket = NULL; - gboolean res = FALSE; - struct stat statbuf; - - wayland_display = g_getenv ("WAYLAND_DISPLAY"); - if (!wayland_display) - wayland_display = "wayland-0"; - - if (wayland_display[0] == '/') - wayland_socket = g_strdup (wayland_display); - else - wayland_socket = g_build_filename (user_runtime_dir, wayland_display, NULL); - - if (!g_str_has_prefix (wayland_display, "wayland-") || - strchr (wayland_display, '/') != NULL) - { - wayland_display = "wayland-0"; - flatpak_bwrap_set_env (bwrap, "WAYLAND_DISPLAY", wayland_display, TRUE); - } - - sandbox_wayland_socket = g_strdup_printf ("/run/flatpak/%s", wayland_display); - - if (stat (wayland_socket, &statbuf) == 0 && - (statbuf.st_mode & S_IFMT) == S_IFSOCK) - { - res = TRUE; - flatpak_bwrap_add_args (bwrap, - "--ro-bind", wayland_socket, sandbox_wayland_socket, - NULL); - flatpak_bwrap_add_runtime_dir_member (bwrap, wayland_display); - } - return res; -} - -static void -flatpak_run_add_ssh_args (FlatpakBwrap *bwrap) -{ - static const char sandbox_auth_socket[] = "/run/flatpak/ssh-auth"; - const char * auth_socket; - - auth_socket = g_getenv ("SSH_AUTH_SOCK"); - - if (!auth_socket) - return; /* ssh agent not present */ - - if (!g_file_test (auth_socket, G_FILE_TEST_EXISTS)) - { - /* Let's clean it up, so that the application will not try to connect */ - flatpak_bwrap_unset_env (bwrap, "SSH_AUTH_SOCK"); - return; - } - - flatpak_bwrap_add_args (bwrap, - "--ro-bind", auth_socket, sandbox_auth_socket, - NULL); - flatpak_bwrap_set_env (bwrap, "SSH_AUTH_SOCK", sandbox_auth_socket, TRUE); -} - -static void -flatpak_run_add_pcsc_args (FlatpakBwrap *bwrap) -{ - const char * pcsc_socket; - const char * sandbox_pcsc_socket = "/run/pcscd/pcscd.comm"; - - pcsc_socket = g_getenv ("PCSCLITE_CSOCK_NAME"); - if (pcsc_socket) - { - if (!g_file_test (pcsc_socket, G_FILE_TEST_EXISTS)) - { - flatpak_bwrap_unset_env (bwrap, "PCSCLITE_CSOCK_NAME"); - return; - } - } - else - { - pcsc_socket = "/run/pcscd/pcscd.comm"; - if (!g_file_test (pcsc_socket, G_FILE_TEST_EXISTS)) - return; - } - - flatpak_bwrap_add_args (bwrap, - "--ro-bind", pcsc_socket, sandbox_pcsc_socket, - NULL); - flatpak_bwrap_set_env (bwrap, "PCSCLITE_CSOCK_NAME", sandbox_pcsc_socket, TRUE); -} - -static gboolean -flatpak_run_cups_check_server_is_socket (const char *server) -{ - if (g_str_has_prefix (server, "/") && strstr (server, ":") == NULL) - return TRUE; - - return FALSE; -} - -/* Try to find a default server from a cups confguration file */ -static char * -flatpak_run_get_cups_server_name_config (const char *path) -{ - g_autoptr(GFile) file = g_file_new_for_path (path); - g_autoptr(GError) my_error = NULL; - g_autoptr(GFileInputStream) input_stream = NULL; - g_autoptr(GDataInputStream) data_stream = NULL; - size_t len; - - input_stream = g_file_read (file, NULL, &my_error); - if (my_error) - { - g_info ("CUPS configuration file '%s': %s", path, my_error->message); - return NULL; - } - - data_stream = g_data_input_stream_new (G_INPUT_STREAM (input_stream)); - - while (TRUE) - { - g_autofree char *line = g_data_input_stream_read_line (data_stream, &len, NULL, NULL); - if (line == NULL) - break; - - g_strchug (line); - - if ((*line == '\0') || (*line == '#')) - continue; - - g_auto(GStrv) tokens = g_strsplit (line, " ", 2); - - if ((tokens[0] != NULL) && (tokens[1] != NULL)) - { - if (strcmp ("ServerName", tokens[0]) == 0) - { - g_strchug (tokens[1]); - - if (flatpak_run_cups_check_server_is_socket (tokens[1])) - return g_strdup (tokens[1]); - } - } - } - - return NULL; -} - -static char * -flatpak_run_get_cups_server_name (void) -{ - g_autofree char * cups_server = NULL; - g_autofree char * cups_config_path = NULL; - - /* TODO - * we don't currently support cups servers located on the network, if such - * server is detected, we simply ignore it and in the worst case we fallback - * to the default socket - */ - cups_server = g_strdup (g_getenv ("CUPS_SERVER")); - if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server)) - return g_steal_pointer (&cups_server); - g_clear_pointer (&cups_server, g_free); - - cups_config_path = g_build_filename (g_get_home_dir (), ".cups/client.conf", NULL); - cups_server = flatpak_run_get_cups_server_name_config (cups_config_path); - if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server)) - return g_steal_pointer (&cups_server); - g_clear_pointer (&cups_server, g_free); - - cups_server = flatpak_run_get_cups_server_name_config ("/etc/cups/client.conf"); - if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server)) - return g_steal_pointer (&cups_server); - - // Fallback to default socket - return g_strdup ("/var/run/cups/cups.sock"); -} - -static void -flatpak_run_add_cups_args (FlatpakBwrap *bwrap) -{ - g_autofree char * sandbox_server_name = g_strdup ("/var/run/cups/cups.sock"); - g_autofree char * cups_server_name = flatpak_run_get_cups_server_name (); - - if (!g_file_test (cups_server_name, G_FILE_TEST_EXISTS)) - { - g_info ("Could not find CUPS server"); - return; - } - - flatpak_bwrap_add_args (bwrap, - "--ro-bind", cups_server_name, sandbox_server_name, - NULL); -} - -static void -flatpak_run_add_gpg_agent_args (FlatpakBwrap *bwrap) -{ - const char * agent_socket; - g_autofree char * sandbox_agent_socket = NULL; - g_autoptr(GError) gpgconf_error = NULL; - g_autoptr(GSubprocess) process = NULL; - GInputStream *base_stream = NULL; - g_autoptr(GDataInputStream) data_stream = NULL; - - process = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE, - &gpgconf_error, - "gpgconf", "--list-dir", "agent-socket", NULL); - - if (gpgconf_error) - { - g_info ("GPG-Agent directories: %s", gpgconf_error->message); - return; - } - - base_stream = g_subprocess_get_stdout_pipe (process); - data_stream = g_data_input_stream_new (base_stream); - - agent_socket = g_data_input_stream_read_line (data_stream, - NULL, NULL, - &gpgconf_error); - - if (!agent_socket || gpgconf_error) - { - g_info ("GPG-Agent directories: %s", gpgconf_error->message); - return; - } - - sandbox_agent_socket = g_strdup_printf ("/run/user/%d/gnupg/S.gpg-agent", getuid ()); - - flatpak_bwrap_add_args (bwrap, - "--ro-bind-try", agent_socket, sandbox_agent_socket, - NULL); -} - -/* Try to find a default server from a pulseaudio confguration file */ -static char * -flatpak_run_get_pulseaudio_server_user_config (const char *path) -{ - g_autoptr(GFile) file = g_file_new_for_path (path); - g_autoptr(GError) my_error = NULL; - g_autoptr(GFileInputStream) input_stream = NULL; - g_autoptr(GDataInputStream) data_stream = NULL; - size_t len; - - input_stream = g_file_read (file, NULL, &my_error); - if (my_error) - { - g_info ("Pulseaudio user configuration file '%s': %s", path, my_error->message); - return NULL; - } - - data_stream = g_data_input_stream_new (G_INPUT_STREAM (input_stream)); - - while (TRUE) - { - g_autofree char *line = g_data_input_stream_read_line (data_stream, &len, NULL, NULL); - if (line == NULL) - break; - - g_strchug (line); - - if ((*line == '\0') || (*line == ';') || (*line == '#')) - continue; - - if (g_str_has_prefix (line, ".include ")) - { - g_autofree char *rec_path = g_strdup (line + 9); - g_strstrip (rec_path); - char *found = flatpak_run_get_pulseaudio_server_user_config (rec_path); - if (found) - return found; - } - else if (g_str_has_prefix (line, "[")) - { - return NULL; - } - else - { - g_auto(GStrv) tokens = g_strsplit (line, "=", 2); - - if ((tokens[0] != NULL) && (tokens[1] != NULL)) - { - g_strchomp (tokens[0]); - if (strcmp ("default-server", tokens[0]) == 0) - { - g_strstrip (tokens[1]); - g_info ("Found pulseaudio socket from configuration file '%s': %s", path, tokens[1]); - return g_strdup (tokens[1]); - } - } - } - } - - return NULL; -} - -static char * -flatpak_run_get_pulseaudio_server (void) -{ - const char * pulse_clientconfig; - char *pulse_server; - g_autofree char *pulse_user_config = NULL; - - pulse_server = g_strdup (g_getenv ("PULSE_SERVER")); - if (pulse_server) - return pulse_server; - - pulse_clientconfig = g_getenv ("PULSE_CLIENTCONFIG"); - if (pulse_clientconfig) - return flatpak_run_get_pulseaudio_server_user_config (pulse_clientconfig); - - pulse_user_config = g_build_filename (g_get_user_config_dir (), "pulse/client.conf", NULL); - pulse_server = flatpak_run_get_pulseaudio_server_user_config (pulse_user_config); - if (pulse_server) - return pulse_server; - - pulse_server = flatpak_run_get_pulseaudio_server_user_config ("/etc/pulse/client.conf"); - if (pulse_server) - return pulse_server; - - return NULL; -} - -/* - * Parse a PulseAudio server string, as documented on - * https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/ServerStrings/. - * Returns the first supported server address, or NULL if none are supported, - * or NULL with @remote set if @value points to a remote server. - */ -static char * -flatpak_run_parse_pulse_server (const char *value, - gboolean *remote) -{ - g_auto(GStrv) servers = g_strsplit (value, " ", 0); - gsize i; - - for (i = 0; servers[i] != NULL; i++) - { - const char *server = servers[i]; - if (g_str_has_prefix (server, "{")) - { - /* - * TODO: compare the value within {} to the local hostname and D-Bus machine ID, - * and skip if it matches neither. - */ - const char * closing = strstr (server, "}"); - if (closing == NULL) - continue; - server = closing + 1; - } - - if (g_str_has_prefix (server, "unix:")) - return g_strdup (server + 5); - if (server[0] == '/') - return g_strdup (server); - - if (g_str_has_prefix (server, "tcp:")) - { - *remote = TRUE; - return NULL; - } - } - - return NULL; -} - -/* - * Get the machine ID as used by PulseAudio. This is the systemd/D-Bus - * machine ID, or failing that, the hostname. - */ -static char * -flatpak_run_get_pulse_machine_id (void) -{ - static const char * const machine_ids[] = - { - "/etc/machine-id", - "/var/lib/dbus/machine-id", - }; - gsize i; - - for (i = 0; i < G_N_ELEMENTS (machine_ids); i++) - { - g_autofree char *ret = NULL; - - if (g_file_get_contents (machine_ids[i], &ret, NULL, NULL)) - { - gsize j; - - g_strstrip (ret); - - for (j = 0; ret[j] != '\0'; j++) - { - if (!g_ascii_isxdigit (ret[j])) - break; - } - - if (ret[0] != '\0' && ret[j] == '\0') - return g_steal_pointer (&ret); - } - } - - return g_strdup (g_get_host_name ()); -} - -/* - * Get the directory used by PulseAudio for its configuration. - */ -static char * -flatpak_run_get_pulse_home (void) -{ - /* Legacy path ~/.pulse is tried first, for compatibility */ - { - const char *parent = g_get_home_dir (); - g_autofree char *ret = g_build_filename (parent, ".pulse", NULL); - - if (g_file_test (ret, G_FILE_TEST_IS_DIR)) - return g_steal_pointer (&ret); - } - - /* The more modern path, usually ~/.config/pulse */ - { - const char *parent = g_get_user_config_dir (); - /* Usually ~/.config/pulse */ - g_autofree char *ret = g_build_filename (parent, "pulse", NULL); - - if (g_file_test (ret, G_FILE_TEST_IS_DIR)) - return g_steal_pointer (&ret); - } - - return NULL; -} - -/* - * Get the runtime directory used by PulseAudio for its socket. - */ -static char * -flatpak_run_get_pulse_runtime_dir (void) -{ - const char *val = NULL; - - val = g_getenv ("PULSE_RUNTIME_PATH"); - - if (val != NULL) - return realpath (val, NULL); - - { - const char *user_runtime_dir = g_get_user_runtime_dir (); - - if (user_runtime_dir != NULL) - { - g_autofree char *dir = g_build_filename (user_runtime_dir, "pulse", NULL); - - if (g_file_test (dir, G_FILE_TEST_IS_DIR)) - return realpath (dir, NULL); - } - } - - { - g_autofree char *pulse_home = flatpak_run_get_pulse_home (); - g_autofree char *machine_id = flatpak_run_get_pulse_machine_id (); - - if (pulse_home != NULL && machine_id != NULL) - { - /* This is usually a symlink, but we take its realpath() anyway */ - g_autofree char *dir = g_strdup_printf ("%s/%s-runtime", pulse_home, machine_id); - - if (g_file_test (dir, G_FILE_TEST_IS_DIR)) - return realpath (dir, NULL); - } - } - - return NULL; -} - -static void -flatpak_run_add_pulseaudio_args (FlatpakBwrap *bwrap, - FlatpakContextShares shares) -{ - g_autofree char *pulseaudio_server = flatpak_run_get_pulseaudio_server (); - g_autofree char *pulseaudio_socket = NULL; - g_autofree char *pulse_runtime_dir = flatpak_run_get_pulse_runtime_dir (); - gboolean remote = FALSE; - - if (pulseaudio_server) - pulseaudio_socket = flatpak_run_parse_pulse_server (pulseaudio_server, - &remote); - - if (pulseaudio_socket == NULL && !remote) - { - pulseaudio_socket = g_build_filename (pulse_runtime_dir, "native", NULL); - - if (!g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS)) - g_clear_pointer (&pulseaudio_socket, g_free); - } - - if (pulseaudio_socket == NULL && !remote) - { - pulseaudio_socket = realpath ("/var/run/pulse/native", NULL); - - if (pulseaudio_socket && !g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS)) - g_clear_pointer (&pulseaudio_socket, g_free); - } - - flatpak_bwrap_unset_env (bwrap, "PULSE_SERVER"); - - if (remote) - { - if ((shares & FLATPAK_CONTEXT_SHARED_NETWORK) == 0) - { - g_warning ("Remote PulseAudio server configured."); - g_warning ("PulseAudio access will require --share=network permission."); - } - - g_info ("Using remote PulseAudio server \"%s\"", pulseaudio_server); - flatpak_bwrap_set_env (bwrap, "PULSE_SERVER", pulseaudio_server, TRUE); - } - else if (pulseaudio_socket && g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS)) - { - static const char sandbox_socket_path[] = "/run/flatpak/pulse/native"; - static const char pulse_server[] = "unix:/run/flatpak/pulse/native"; - static const char config_path[] = "/run/flatpak/pulse/config"; - gboolean share_shm = FALSE; /* TODO: When do we add this? */ - g_autofree char *client_config = g_strdup_printf ("enable-shm=%s\n", share_shm ? "yes" : "no"); - - /* FIXME - error handling */ - if (!flatpak_bwrap_add_args_data (bwrap, "pulseaudio", client_config, -1, config_path, NULL)) - return; - - flatpak_bwrap_add_args (bwrap, - "--ro-bind", pulseaudio_socket, sandbox_socket_path, - NULL); - - flatpak_bwrap_set_env (bwrap, "PULSE_SERVER", pulse_server, TRUE); - flatpak_bwrap_set_env (bwrap, "PULSE_CLIENTCONFIG", config_path, TRUE); - flatpak_bwrap_add_runtime_dir_member (bwrap, "pulse"); - } - else - g_info ("Could not find pulseaudio socket"); - - /* Also allow ALSA access. This was added in 1.8, and is not ideally named. However, - * since the practical permission of ALSA and PulseAudio are essentially the same, and - * since we don't want to add more permissions for something we plan to replace with - * portals/pipewire going forward we reinterpret pulseaudio to also mean ALSA. - */ - if (!remote && g_file_test ("/dev/snd", G_FILE_TEST_IS_DIR)) - flatpak_bwrap_add_args (bwrap, "--dev-bind", "/dev/snd", "/dev/snd", NULL); -} - -static void -flatpak_run_add_gssproxy_args (FlatpakBwrap *bwrap) -{ - /* We only expose the gssproxy user service. The gssproxy system service is - * not intended to be exposed to sandboxed environments. - */ - g_autofree char *gssproxy_host_dir = g_build_filename (g_get_user_runtime_dir (), "gssproxy", NULL); - const char *gssproxy_sandboxed_dir = "/run/flatpak/gssproxy/"; - - if (g_file_test (gssproxy_host_dir, G_FILE_TEST_EXISTS)) - flatpak_bwrap_add_args (bwrap, "--ro-bind", gssproxy_host_dir, gssproxy_sandboxed_dir, NULL); -} - -static void -flatpak_run_add_resolved_args (FlatpakBwrap *bwrap) -{ - const char *resolved_socket = "/run/systemd/resolve/io.systemd.Resolve"; - - if (g_file_test (resolved_socket, G_FILE_TEST_EXISTS)) - flatpak_bwrap_add_args (bwrap, "--bind", resolved_socket, resolved_socket, NULL); -} - -static void -flatpak_run_add_journal_args (FlatpakBwrap *bwrap) -{ - g_autofree char *journal_socket_socket = g_strdup ("/run/systemd/journal/socket"); - g_autofree char *journal_stdout_socket = g_strdup ("/run/systemd/journal/stdout"); - - if (g_file_test (journal_socket_socket, G_FILE_TEST_EXISTS)) - { - flatpak_bwrap_add_args (bwrap, - "--ro-bind", journal_socket_socket, journal_socket_socket, - NULL); - } - if (g_file_test (journal_stdout_socket, G_FILE_TEST_EXISTS)) - { - flatpak_bwrap_add_args (bwrap, - "--ro-bind", journal_stdout_socket, journal_stdout_socket, - NULL); - } -} - -static char * -create_proxy_socket (char *template) -{ - g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir (); - g_autofree char *proxy_socket_dir = g_build_filename (user_runtime_dir, ".dbus-proxy", NULL); - g_autofree char *proxy_socket = g_build_filename (proxy_socket_dir, template, NULL); - int fd; - - if (!glnx_shutil_mkdir_p_at (AT_FDCWD, proxy_socket_dir, 0755, NULL, NULL)) - return NULL; - - fd = g_mkstemp (proxy_socket); - if (fd == -1) - return NULL; - - close (fd); - - return g_steal_pointer (&proxy_socket); -} - -static gboolean -flatpak_run_add_system_dbus_args (FlatpakBwrap *app_bwrap, - FlatpakBwrap *proxy_arg_bwrap, - FlatpakContext *context, - FlatpakRunFlags flags) -{ - gboolean unrestricted, no_proxy; - const char *dbus_address = g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"); - g_autofree char *real_dbus_address = NULL; - g_autofree char *dbus_system_socket = NULL; - - unrestricted = (context->sockets & FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS) != 0; - if (unrestricted) - g_info ("Allowing system-dbus access"); - - no_proxy = (flags & FLATPAK_RUN_FLAG_NO_SYSTEM_BUS_PROXY) != 0; - - if (dbus_address != NULL) - dbus_system_socket = extract_unix_path_from_dbus_address (dbus_address); - else if (g_file_test ("/var/run/dbus/system_bus_socket", G_FILE_TEST_EXISTS)) - dbus_system_socket = g_strdup ("/var/run/dbus/system_bus_socket"); - - if (dbus_system_socket != NULL && unrestricted) - { - flatpak_bwrap_add_args (app_bwrap, - "--ro-bind", dbus_system_socket, "/run/dbus/system_bus_socket", - NULL); - flatpak_bwrap_set_env (app_bwrap, "DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket", TRUE); - - return TRUE; - } - else if (!no_proxy && flatpak_context_get_needs_system_bus_proxy (context)) - { - g_autofree char *proxy_socket = create_proxy_socket ("system-bus-proxy-XXXXXX"); - - if (proxy_socket == NULL) - return FALSE; - - if (dbus_address) - real_dbus_address = g_strdup (dbus_address); - else - real_dbus_address = g_strdup_printf ("unix:path=%s", dbus_system_socket); - - flatpak_bwrap_add_args (proxy_arg_bwrap, real_dbus_address, proxy_socket, NULL); - - if (!unrestricted) - flatpak_context_add_bus_filters (context, NULL, FALSE, flags & FLATPAK_RUN_FLAG_SANDBOX, proxy_arg_bwrap); - - if ((flags & FLATPAK_RUN_FLAG_LOG_SYSTEM_BUS) != 0) - flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL); - - flatpak_bwrap_add_args (app_bwrap, - "--ro-bind", proxy_socket, "/run/dbus/system_bus_socket", - NULL); - flatpak_bwrap_set_env (app_bwrap, "DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket", TRUE); - - return TRUE; - } - return FALSE; -} - -static gboolean -flatpak_run_add_session_dbus_args (FlatpakBwrap *app_bwrap, - FlatpakBwrap *proxy_arg_bwrap, - FlatpakContext *context, - FlatpakRunFlags flags, - const char *app_id) -{ - static const char sandbox_socket_path[] = "/run/flatpak/bus"; - static const char sandbox_dbus_address[] = "unix:path=/run/flatpak/bus"; - gboolean unrestricted, no_proxy; - const char *dbus_address = g_getenv ("DBUS_SESSION_BUS_ADDRESS"); - g_autofree char *dbus_session_socket = NULL; - - unrestricted = (context->sockets & FLATPAK_CONTEXT_SOCKET_SESSION_BUS) != 0; - - if (dbus_address != NULL) - { - dbus_session_socket = extract_unix_path_from_dbus_address (dbus_address); - } - else - { - g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir (); - struct stat statbuf; - - dbus_session_socket = g_build_filename (user_runtime_dir, "bus", NULL); - - if (stat (dbus_session_socket, &statbuf) < 0 - || (statbuf.st_mode & S_IFMT) != S_IFSOCK - || statbuf.st_uid != getuid ()) - return FALSE; - } - - if (unrestricted) - g_info ("Allowing session-dbus access"); - - no_proxy = (flags & FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY) != 0; - - if (dbus_session_socket != NULL && unrestricted) - { - flatpak_bwrap_add_args (app_bwrap, - "--ro-bind", dbus_session_socket, sandbox_socket_path, - NULL); - flatpak_bwrap_set_env (app_bwrap, "DBUS_SESSION_BUS_ADDRESS", sandbox_dbus_address, TRUE); - flatpak_bwrap_add_runtime_dir_member (app_bwrap, "bus"); - - return TRUE; - } - else if (!no_proxy && dbus_address != NULL) - { - g_autofree char *proxy_socket = create_proxy_socket ("session-bus-proxy-XXXXXX"); - - if (proxy_socket == NULL) - return FALSE; - - flatpak_bwrap_add_args (proxy_arg_bwrap, dbus_address, proxy_socket, NULL); - - if (!unrestricted) - { - flatpak_context_add_bus_filters (context, app_id, TRUE, flags & FLATPAK_RUN_FLAG_SANDBOX, proxy_arg_bwrap); - - /* Allow calling any interface+method on all portals, but only receive broadcasts under /org/desktop/portal */ - flatpak_bwrap_add_arg (proxy_arg_bwrap, - "--call=org.freedesktop.portal.*=*"); - flatpak_bwrap_add_arg (proxy_arg_bwrap, - "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*"); - } - - if ((flags & FLATPAK_RUN_FLAG_LOG_SESSION_BUS) != 0) - flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL); - - flatpak_bwrap_add_args (app_bwrap, - "--ro-bind", proxy_socket, sandbox_socket_path, - NULL); - flatpak_bwrap_set_env (app_bwrap, "DBUS_SESSION_BUS_ADDRESS", sandbox_dbus_address, TRUE); - flatpak_bwrap_add_runtime_dir_member (app_bwrap, "bus"); - - return TRUE; - } - - return FALSE; -} - -static gboolean -flatpak_run_add_a11y_dbus_args (FlatpakBwrap *app_bwrap, - FlatpakBwrap *proxy_arg_bwrap, - FlatpakContext *context, - FlatpakRunFlags flags) -{ - static const char sandbox_socket_path[] = "/run/flatpak/at-spi-bus"; - static const char sandbox_dbus_address[] = "unix:path=/run/flatpak/at-spi-bus"; - g_autoptr(GDBusConnection) session_bus = NULL; - g_autofree char *a11y_address = NULL; - g_autoptr(GError) local_error = NULL; - g_autoptr(GDBusMessage) reply = NULL; - g_autoptr(GDBusMessage) msg = NULL; - g_autofree char *proxy_socket = NULL; - - if ((flags & FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY) != 0) - return FALSE; - - session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - if (session_bus == NULL) - return FALSE; - - msg = g_dbus_message_new_method_call ("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus", "GetAddress"); - g_dbus_message_set_body (msg, g_variant_new ("()")); - reply = - g_dbus_connection_send_message_with_reply_sync (session_bus, msg, - G_DBUS_SEND_MESSAGE_FLAGS_NONE, - 30000, - NULL, - NULL, - NULL); - if (reply) - { - if (g_dbus_message_to_gerror (reply, &local_error)) - { - if (!g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) - g_message ("Can't find a11y bus: %s", local_error->message); - } - else - { - g_variant_get (g_dbus_message_get_body (reply), - "(s)", &a11y_address); - } - } - - if (!a11y_address) - return FALSE; - - proxy_socket = create_proxy_socket ("a11y-bus-proxy-XXXXXX"); - if (proxy_socket == NULL) - return FALSE; - - flatpak_bwrap_add_args (proxy_arg_bwrap, - a11y_address, - proxy_socket, "--filter", "--sloppy-names", - "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Embed@/org/a11y/atspi/accessible/root", - "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Unembed@/org/a11y/atspi/accessible/root", - "--call=org.a11y.atspi.Registry=org.a11y.atspi.Registry.GetRegisteredEvents@/org/a11y/atspi/registry", - "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetKeystrokeListeners@/org/a11y/atspi/registry/deviceeventcontroller", - "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetDeviceEventListeners@/org/a11y/atspi/registry/deviceeventcontroller", - "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersSync@/org/a11y/atspi/registry/deviceeventcontroller", - "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersAsync@/org/a11y/atspi/registry/deviceeventcontroller", - NULL); - - if ((flags & FLATPAK_RUN_FLAG_LOG_A11Y_BUS) != 0) - flatpak_bwrap_add_args (proxy_arg_bwrap, "--log", NULL); - - flatpak_bwrap_add_args (app_bwrap, - "--ro-bind", proxy_socket, sandbox_socket_path, - NULL); - flatpak_bwrap_set_env (app_bwrap, "AT_SPI_BUS_ADDRESS", sandbox_dbus_address, TRUE); - - return TRUE; -} - -/* This wraps the argv in a bwrap call, primary to allow the - command to be run with a proper /.flatpak-info with data - taken from app_info_path */ -static gboolean -add_bwrap_wrapper (FlatpakBwrap *bwrap, - const char *app_info_path, - GError **error) -{ - glnx_autofd int app_info_fd = -1; - g_auto(GLnxDirFdIterator) dir_iter = { 0 }; - struct dirent *dent; - g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir (); - g_autofree char *proxy_socket_dir = g_build_filename (user_runtime_dir, ".dbus-proxy/", NULL); - - app_info_fd = open (app_info_path, O_RDONLY | O_CLOEXEC); - if (app_info_fd == -1) - return glnx_throw_errno_prefix (error, _("Failed to open app info file")); - - if (!glnx_dirfd_iterator_init_at (AT_FDCWD, "/", FALSE, &dir_iter, error)) - return FALSE; - - flatpak_bwrap_add_arg (bwrap, flatpak_get_bwrap ()); - - while (TRUE) - { - glnx_autofd int o_path_fd = -1; - struct statfs stfs; - - if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dir_iter, &dent, NULL, error)) - return FALSE; - - if (dent == NULL) - break; - - if (strcmp (dent->d_name, ".flatpak-info") == 0) - continue; - - /* O_PATH + fstatfs is the magic that we need to statfs without automounting the target */ - o_path_fd = openat (dir_iter.fd, dent->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC); - if (o_path_fd == -1 || fstatfs (o_path_fd, &stfs) != 0 || stfs.f_type == AUTOFS_SUPER_MAGIC) - continue; /* AUTOFS mounts are risky and can cause us to block (see issue #1633), so ignore it. Its unlikely the proxy needs such a directory. */ - - if (dent->d_type == DT_DIR) - { - if (strcmp (dent->d_name, "tmp") == 0 || - strcmp (dent->d_name, "var") == 0 || - strcmp (dent->d_name, "run") == 0) - flatpak_bwrap_add_arg (bwrap, "--bind"); - else - flatpak_bwrap_add_arg (bwrap, "--ro-bind"); - - flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name); - flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name); - } - else if (dent->d_type == DT_LNK) - { - g_autofree gchar *target = NULL; - - target = glnx_readlinkat_malloc (dir_iter.fd, dent->d_name, - NULL, error); - if (target == NULL) - return FALSE; - flatpak_bwrap_add_args (bwrap, "--symlink", target, NULL); - flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name); - } - } - - flatpak_bwrap_add_args (bwrap, "--bind", proxy_socket_dir, proxy_socket_dir, NULL); - - /* This is a file rather than a bind mount, because it will then - not be unmounted from the namespace when the namespace dies. */ - flatpak_bwrap_add_args (bwrap, "--perms", "0600", NULL); - flatpak_bwrap_add_args_data_fd (bwrap, "--file", glnx_steal_fd (&app_info_fd), "/.flatpak-info"); - - if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error)) - return FALSE; - - return TRUE; -} - -static gboolean -start_dbus_proxy (FlatpakBwrap *app_bwrap, - FlatpakBwrap *proxy_arg_bwrap, - const char *app_info_path, - GError **error) -{ - char x = 'x'; - const char *proxy; - g_autofree char *commandline = NULL; - g_autoptr(FlatpakBwrap) proxy_bwrap = NULL; - int sync_fds[2] = {-1, -1}; - int proxy_start_index; - - proxy_bwrap = flatpak_bwrap_new (NULL); - - if (!add_bwrap_wrapper (proxy_bwrap, app_info_path, error)) - return FALSE; - - proxy = g_getenv ("FLATPAK_DBUSPROXY"); - if (proxy == NULL) - proxy = DBUSPROXY; - - flatpak_bwrap_add_arg (proxy_bwrap, proxy); - - proxy_start_index = proxy_bwrap->argv->len; - - if (pipe2 (sync_fds, O_CLOEXEC) < 0) - { - g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), - _("Unable to create sync pipe")); - return FALSE; - } - - /* read end goes to app */ - flatpak_bwrap_add_args_data_fd (app_bwrap, "--sync-fd", sync_fds[0], NULL); - - /* write end goes to proxy */ - flatpak_bwrap_add_fd (proxy_bwrap, sync_fds[1]); - flatpak_bwrap_add_arg_printf (proxy_bwrap, "--fd=%d", sync_fds[1]); - - /* Note: This steals the fds from proxy_arg_bwrap */ - flatpak_bwrap_append_bwrap (proxy_bwrap, proxy_arg_bwrap); - - if (!flatpak_bwrap_bundle_args (proxy_bwrap, proxy_start_index, -1, TRUE, error)) - return FALSE; - - flatpak_bwrap_finish (proxy_bwrap); - - commandline = flatpak_quote_argv ((const char **) proxy_bwrap->argv->pdata, -1); - g_info ("Running '%s'", commandline); - - /* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */ - if (!g_spawn_async (NULL, - (char **) proxy_bwrap->argv->pdata, - NULL, - G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, - flatpak_bwrap_child_setup_cb, proxy_bwrap->fds, - NULL, error)) - return FALSE; - - /* The write end can be closed now, otherwise the read below will hang of xdg-dbus-proxy - fails to start. */ - g_clear_pointer (&proxy_bwrap, flatpak_bwrap_free); - - /* Sync with proxy, i.e. wait until its listening on the sockets */ - if (read (sync_fds[0], &x, 1) != 1) - { - g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), - _("Failed to sync with dbus proxy")); - return FALSE; - } - - return TRUE; -} - static int flatpak_extension_compare_by_path (gconstpointer _a, gconstpointer _b) @@ -1590,8 +277,6 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap, g_autoptr(FlatpakExports) exports = NULL; g_autoptr(FlatpakBwrap) proxy_arg_bwrap = flatpak_bwrap_new (flatpak_bwrap_empty_env); g_autofree char *xdg_dirs_conf = NULL; - gboolean has_wayland = FALSE; - gboolean allow_x11 = FALSE; gboolean home_access = FALSE; gboolean sandboxed = (flags & FLATPAK_RUN_FLAG_SANDBOX) != 0; @@ -1802,45 +487,7 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap, flatpak_context_append_bwrap_filesystem (context, bwrap, app_id, app_id_dir, exports, xdg_dirs_conf, home_access); - if (context->sockets & FLATPAK_CONTEXT_SOCKET_WAYLAND) - { - g_info ("Allowing wayland access"); - has_wayland = flatpak_run_add_wayland_args (bwrap); - } - - if ((context->sockets & FLATPAK_CONTEXT_SOCKET_FALLBACK_X11) != 0) - allow_x11 = !has_wayland; - else - allow_x11 = (context->sockets & FLATPAK_CONTEXT_SOCKET_X11) != 0; - - flatpak_run_add_x11_args (bwrap, allow_x11, context->shares); - - if (context->sockets & FLATPAK_CONTEXT_SOCKET_SSH_AUTH) - { - flatpak_run_add_ssh_args (bwrap); - } - - if (context->sockets & FLATPAK_CONTEXT_SOCKET_PULSEAUDIO) - { - g_info ("Allowing pulseaudio access"); - flatpak_run_add_pulseaudio_args (bwrap, context->shares); - } - - if (context->sockets & FLATPAK_CONTEXT_SOCKET_PCSC) - { - flatpak_run_add_pcsc_args (bwrap); - } - - if (context->sockets & FLATPAK_CONTEXT_SOCKET_CUPS) - { - flatpak_run_add_cups_args (bwrap); - } - - if (context->sockets & FLATPAK_CONTEXT_SOCKET_GPG_AGENT) - { - flatpak_run_add_gpg_agent_args (bwrap); - } - + flatpak_run_add_socket_args_environment (bwrap, context->shares, context->sockets); flatpak_run_add_session_dbus_args (bwrap, proxy_arg_bwrap, context, flags, app_id); flatpak_run_add_system_dbus_args (bwrap, proxy_arg_bwrap, context, flags); flatpak_run_add_a11y_dbus_args (bwrap, proxy_arg_bwrap, context, flags); @@ -1855,8 +502,8 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap, g_clear_error (&my_error); } - if (!flatpak_bwrap_is_empty (proxy_arg_bwrap) && - !start_dbus_proxy (bwrap, proxy_arg_bwrap, app_info_path, error)) + if (!flatpak_run_maybe_start_dbus_proxy (bwrap, proxy_arg_bwrap, + app_info_path, error)) return FALSE; if (exports_out) @@ -4668,13 +3315,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref, NULL); } - if ((app_context->shares & FLATPAK_CONTEXT_SHARED_NETWORK) != 0) - { - flatpak_run_add_gssproxy_args (bwrap); - flatpak_run_add_resolved_args (bwrap); - } - - flatpak_run_add_journal_args (bwrap); + flatpak_run_add_socket_args_late (bwrap, app_context->shares); add_font_path_args (bwrap); add_icon_path_args (bwrap); diff --git a/common/meson.build b/common/meson.build index b87f22ac..86d0573c 100644 --- a/common/meson.build +++ b/common/meson.build @@ -154,6 +154,11 @@ sources = [ 'flatpak-remote-ref.c', 'flatpak-remote.c', 'flatpak-run.c', + 'flatpak-run-cups.c', + 'flatpak-run-dbus.c', + 'flatpak-run-pulseaudio.c', + 'flatpak-run-sockets.c', + 'flatpak-run-x11.c', 'flatpak-transaction.c', 'flatpak-utils-http.c', 'flatpak-utils.c', diff --git a/tests/testcommon.c b/tests/testcommon.c index 8ba3770d..7c96d8ed 100644 --- a/tests/testcommon.c +++ b/tests/testcommon.c @@ -10,6 +10,7 @@ #include "flatpak-appdata-private.h" #include "flatpak-builtins-utils.h" #include "flatpak-run-private.h" +#include "flatpak-run-x11-private.h" #include "flatpak-table-printer.h" #include "parse-datetime.h" |