/* 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 .
*
* Authors:
* Alexander Larsson
*/
#include "config.h"
#include "flatpak-run-dbus-private.h"
#include
#include
#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;
}