Add an option to share the pid namespace with the parent flatpak

As with flatpak run --parent-expose-pids, this will only work if we have
a working, non-setuid bwrap. Systems where user namespace creation is
restricted and bwrap needs to be setuid (Debian 10, RHEL/CentOS 7,
Arch Linux linux-hardened kernel) will have degraded functionality.

This option is similar to --expose-pids, except that instead of making
the subsandbox use a nested pid namespace inside the parent's, it makes
the subsandbox share the parent's pid namespace as-is, so that process
IDs in the parent and the subsandbox are interchangeable. This will
be useful if the parent and the subsandbox communicate via protocols
that assume a global view of the process ID namespace, for example
passing process IDs across an AF_UNIX socket or in shared memory.

In particular, this will be useful for Steam's pressure-vessel container
tool: the IPC between the Steam client and the "game overlay" loaded into
Steam games uses process IDs, and becomes confused if they don't match up.

This weakens the security boundary between a subsandbox and the parent,
but that's OK in some cases, especially if the subsandbox is being used
as a way to get a different runtime /usr (flatpak-spawn --latest-version
or #4018) rather than as a security boundary.

Signed-off-by: Simon McVittie <smcv@collabora.com>
This commit is contained in:
Simon McVittie
2021-01-07 13:16:55 +00:00
committed by Alexander Larsson
parent e5da98ff4b
commit bbf6debec2
7 changed files with 60 additions and 15 deletions

View File

@@ -57,6 +57,7 @@ static char *opt_commit;
static char *opt_runtime_commit;
static int opt_parent_pid;
static gboolean opt_parent_expose_pids;
static gboolean opt_parent_share_pids;
static int opt_instance_id_fd = -1;
static GOptionEntry options[] = {
@@ -82,6 +83,7 @@ static GOptionEntry options[] = {
{ "die-with-parent", 'p', 0, G_OPTION_ARG_NONE, &opt_die_with_parent, N_("Kill processes when the parent process dies"), NULL },
{ "parent-pid", 0, 0, G_OPTION_ARG_INT, &opt_parent_pid, N_("Use PID as parent pid for sharing namespaces"), N_("PID") },
{ "parent-expose-pids", 0, 0, G_OPTION_ARG_NONE, &opt_parent_expose_pids, N_("Make processes visible in parent namespace"), NULL },
{ "parent-share-pids", 0, 0, G_OPTION_ARG_NONE, &opt_parent_share_pids, N_("Share process ID namespace with parent"), NULL },
{ "instance-id-fd", 0, 0, G_OPTION_ARG_INT, &opt_instance_id_fd, N_("Write the instance ID to the given file descriptor"), NULL },
{ NULL }
};
@@ -286,6 +288,8 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **
flags |= FLATPAK_RUN_FLAG_NO_DOCUMENTS_PORTAL;
if (opt_parent_expose_pids)
flags |= FLATPAK_RUN_FLAG_PARENT_EXPOSE_PIDS;
if (opt_parent_share_pids)
flags |= FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS;
if (!opt_a11y_bus)
flags |= FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY;
if (!opt_session_bus)

View File

@@ -48,6 +48,7 @@ typedef enum {
FLATPAK_RUN_FLAG_DO_NOT_REAP = (1 << 18),
FLATPAK_RUN_FLAG_NO_PROC = (1 << 19),
FLATPAK_RUN_FLAG_PARENT_EXPOSE_PIDS = (1 << 20),
FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS = (1 << 21),
} FlatpakRunFlags;
typedef struct FlatpakDir FlatpakDir;

View File

@@ -2940,8 +2940,10 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
"--proc", "/proc",
NULL);
if (!(flags & FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS))
flatpak_bwrap_add_arg (bwrap, "--unshare-pid");
flatpak_bwrap_add_args (bwrap,
"--unshare-pid",
"--dir", "/tmp",
"--dir", "/var/tmp",
"--dir", "/run/host",
@@ -3596,7 +3598,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
gboolean use_ld_so_cache = TRUE;
gboolean sandboxed = (flags & FLATPAK_RUN_FLAG_SANDBOX) != 0;
gboolean parent_expose_pids = (flags & FLATPAK_RUN_FLAG_PARENT_EXPOSE_PIDS) != 0;
gboolean parent_share_pids = (flags & FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS) != 0;
struct stat s;
if (!check_sudo (error))
@@ -3911,7 +3913,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
if (cwd)
flatpak_bwrap_add_args (bwrap, "--chdir", cwd, NULL);
if (parent_expose_pids)
if (parent_expose_pids || parent_share_pids)
{
g_autofree char *userns_path = NULL;
g_autofree char *pidns_path = NULL;

View File

@@ -36,7 +36,7 @@
bus name org.freedesktop.portal.Flatpak and the object path
/org/freedesktop/portal/Flatpak.
This documentation describes version 4 of this interface.
This documentation describes version 5 of this interface.
-->
<interface name='org.freedesktop.portal.Flatpak'>
<property name="version" type="u" access="read"/>
@@ -49,7 +49,9 @@
<varlistentry>
<term>1 (FLATPAK_SPAWN_SUPPORT_FLAGS_EXPOSE_PIDS)</term>
<listitem><para>
Supports the expose sandbox pids flag of Spawn.
Supports the expose sandbox pids flag of Spawn.
If the version of this interface is 5 or later, this also
indicates that the share sandbox pids flag is available.
</para></listitem>
</varlistentry>
</variablelist>
@@ -120,6 +122,17 @@
This was added in version 4 of this interface (available from flatpak 1.8.0 and later).
</para></listitem>
</varlistentry>
<varlistentry>
<term>128 (FLATPAK_SPAWN_FLAGS_SHARE_PIDS)</term>
<listitem><para>
Expose the sandbox process IDs in the caller's sandbox and
the caller's process IDs in the new sandbox. Only supported
if using user namespaces for containers (not setuid), see the
support property.
</para><para>
This was added in version 5 of this interface (available from flatpak 1.10.0 and later).
</para></listitem>
</varlistentry>
</variablelist>
Unknown (unsupported) flags are an error and will cause Spawn()
@@ -246,7 +259,8 @@
SpawnStarted:
@pid: the PID of the process that has been started
@relpid: the PID of the process relative to the current namespace.
This is only non-zero if the expose PIDs flag (32) was passed to
This is only non-zero if the expose PIDs flag (32) or the share
PIDs flag (128) was passed to
org.freedesktop.portal.Flatpak.Spawn(), and it may still be zero if
the process exits before its relative PID could be read.

View File

@@ -563,7 +563,8 @@ key=v1;v2;
<term><option>--parent-pid=PID</option></term>
<listitem><para>
Specifies the pid of the "parent" flatpak, used by --parent-expose-pids.
Specifies the pid of the "parent" flatpak, used by
--parent-expose-pids and --parent-share-pids.
</para></listitem>
</varlistentry>
@@ -576,6 +577,16 @@ key=v1;v2;
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--parent-share-pids</option></term>
<listitem><para>
Use the same process ID namespace for the processes of
the new sandbox and the sandbox of the parent flatpak, as
defined by --parent-pid. Implies --parent-expose-pids.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--instance-id-fd</option></term>

View File

@@ -207,7 +207,7 @@ typedef struct
char *client;
guint child_watch;
gboolean watch_bus;
gboolean expose_pids;
gboolean expose_or_share_pids;
} PidData;
static void
@@ -408,7 +408,7 @@ check_child_pid_status (void *user_data)
}
/* Only send the child PID if it's exposed */
if (pid_data->expose_pids)
if (pid_data->expose_or_share_pids)
{
g_autoptr(GError) error = NULL;
relative_child_pid = get_child_pid_relative_to_parent_sandbox (child_pid, &error);
@@ -779,6 +779,7 @@ handle_spawn (PortalFlatpak *object,
guint sandbox_flags = 0;
gboolean sandboxed;
gboolean expose_pids;
gboolean share_pids;
gboolean notify_start;
gboolean devel;
@@ -1047,7 +1048,9 @@ handle_spawn (PortalFlatpak *object,
}
expose_pids = (arg_flags & FLATPAK_SPAWN_FLAGS_EXPOSE_PIDS) != 0;
if (expose_pids)
share_pids = (arg_flags & FLATPAK_SPAWN_FLAGS_SHARE_PIDS) != 0;
if (expose_pids || share_pids)
{
g_autofree char *instance_id = NULL;
int sender_pid1 = 0;
@@ -1056,7 +1059,7 @@ handle_spawn (PortalFlatpak *object,
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_NOT_SUPPORTED,
"Expose pids not supported");
"Expose pids not supported with setuid bwrap");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
@@ -1079,7 +1082,11 @@ handle_spawn (PortalFlatpak *object,
}
g_ptr_array_add (flatpak_argv, g_strdup_printf ("--parent-pid=%d", sender_pid1));
g_ptr_array_add (flatpak_argv, g_strdup ("--parent-expose-pids"));
if (share_pids)
g_ptr_array_add (flatpak_argv, g_strdup ("--parent-share-pids"));
else
g_ptr_array_add (flatpak_argv, g_strdup ("--parent-expose-pids"));
}
notify_start = (arg_flags & FLATPAK_SPAWN_FLAGS_NOTIFY_START) != 0;
@@ -1278,7 +1285,7 @@ handle_spawn (PortalFlatpak *object,
pid_data->pid = pid;
pid_data->client = g_strdup (g_dbus_method_invocation_get_sender (invocation));
pid_data->watch_bus = (arg_flags & FLATPAK_SPAWN_FLAGS_WATCH_BUS) != 0;
pid_data->expose_pids = expose_pids;
pid_data->expose_or_share_pids = (expose_pids || share_pids);
pid_data->child_watch = g_child_watch_add_full (G_PRIORITY_DEFAULT,
pid,
child_watch_died,
@@ -2679,7 +2686,7 @@ on_bus_acquired (GDBusConnection *connection,
g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (portal),
G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
portal_flatpak_set_version (PORTAL_FLATPAK (portal), 4);
portal_flatpak_set_version (PORTAL_FLATPAK (portal), 5);
portal_flatpak_set_supports (PORTAL_FLATPAK (portal), supports);
g_signal_connect (portal, "handle-spawn", G_CALLBACK (handle_spawn), NULL);

View File

@@ -29,6 +29,7 @@ typedef enum {
FLATPAK_SPAWN_FLAGS_WATCH_BUS = 1 << 4,
FLATPAK_SPAWN_FLAGS_EXPOSE_PIDS = 1 << 5,
FLATPAK_SPAWN_FLAGS_NOTIFY_START = 1 << 6,
FLATPAK_SPAWN_FLAGS_SHARE_PIDS = 1 << 7,
} FlatpakSpawnFlags;
typedef enum {
@@ -44,13 +45,18 @@ typedef enum {
FLATPAK_SPAWN_SUPPORT_FLAGS_EXPOSE_PIDS = 1 << 0,
} FlatpakSpawnSupportFlags;
/* The same flag is reused: this feature is available under the same
* circumstances */
#define FLATPAK_SPAWN_SUPPORT_FLAGS_SHARE_PIDS FLATPAK_SPAWN_SUPPORT_FLAGS_EXPOSE_PIDS
#define FLATPAK_SPAWN_FLAGS_ALL (FLATPAK_SPAWN_FLAGS_CLEAR_ENV | \
FLATPAK_SPAWN_FLAGS_LATEST_VERSION | \
FLATPAK_SPAWN_FLAGS_SANDBOX | \
FLATPAK_SPAWN_FLAGS_NO_NETWORK | \
FLATPAK_SPAWN_FLAGS_WATCH_BUS | \
FLATPAK_SPAWN_FLAGS_EXPOSE_PIDS | \
FLATPAK_SPAWN_FLAGS_NOTIFY_START)
FLATPAK_SPAWN_FLAGS_NOTIFY_START | \
FLATPAK_SPAWN_FLAGS_SHARE_PIDS)
#define FLATPAK_SPAWN_SANDBOX_FLAGS_ALL (FLATPAK_SPAWN_SANDBOX_FLAGS_SHARE_DISPLAY | \
FLATPAK_SPAWN_SANDBOX_FLAGS_SHARE_SOUND | \