Limit the usage of WAYLAND_SOCKET to an opt-in feature

1. For security context creation, only relies on WAYLAND_DISPLAY, do not
   use WAYLAND_SOCKET since the file descriptor defined by WAYLAND_SOCKET
   can be only consumed once.
2. Due to the incompatiblity between WAYLAND_SOCKET and the security
   context, add a new permission --socket=inherit-wayland-socket
   to limit the usage of WAYLAND_SOCKET to an opt-in feature. Only when
   this flag is set, WAYLAND_SOCKET will be passed to the sandbox.
3. When WAYLAND_SOCKET is not inherited, set FD_CLOEXEC to avoid it to
   be leaked the to sandbox.

Closes: #5614
This commit is contained in:
Weng Xuetian
2023-12-08 15:12:04 -08:00
committed by Simon McVittie
parent 6e3cc82af3
commit 0402e1614c
11 changed files with 114 additions and 19 deletions

View File

@@ -50,6 +50,7 @@ typedef enum {
FLATPAK_CONTEXT_SOCKET_PCSC = 1 << 7,
FLATPAK_CONTEXT_SOCKET_CUPS = 1 << 8,
FLATPAK_CONTEXT_SOCKET_GPG_AGENT = 1 << 9,
FLATPAK_CONTEXT_SOCKET_INHERIT_WAYLAND_SOCKET = 1 << 10,
} FlatpakContextSockets;
typedef enum {

View File

@@ -61,6 +61,7 @@ const char *flatpak_context_sockets[] = {
"pcsc",
"cups",
"gpg-agent",
"inherit-wayland-socket",
NULL
};

View File

@@ -178,12 +178,15 @@ flatpak_run_add_socket_args_environment (FlatpakBwrap *bwrap,
{
gboolean has_wayland = FALSE;
gboolean allow_x11;
gboolean inherit_wayland_socket;
if (sockets & FLATPAK_CONTEXT_SOCKET_WAYLAND)
{
g_info ("Allowing wayland access");
g_assert (app_id && instance_id);
has_wayland = flatpak_run_add_wayland_args (bwrap, app_id, instance_id);
inherit_wayland_socket = (sockets & FLATPAK_CONTEXT_SOCKET_INHERIT_WAYLAND_SOCKET) != 0;
has_wayland = flatpak_run_add_wayland_args (bwrap, app_id, instance_id,
inherit_wayland_socket);
}
if ((sockets & FLATPAK_CONTEXT_SOCKET_FALLBACK_X11) != 0)

View File

@@ -31,6 +31,7 @@ G_BEGIN_DECLS
gboolean
flatpak_run_add_wayland_args (FlatpakBwrap *bwrap,
const char *app_id,
const char *instance_id);
const char *instance_id,
gboolean inherit_wayland_socket);
G_END_DECLS

View File

@@ -30,6 +30,38 @@
#include "flatpak-utils-private.h"
static char *
get_wayland_socket_path (void)
{
g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir ();
const char *wayland_display;
wayland_display = g_getenv ("WAYLAND_DISPLAY");
if (!wayland_display)
wayland_display = "wayland-0";
if (wayland_display[0] == '/')
return g_strdup (wayland_display);
return g_build_filename (user_runtime_dir, wayland_display, NULL);
}
static int
get_wayland_socket_fd (void)
{
const char *wayland_socket_fd;
guint64 fd;
wayland_socket_fd = g_getenv ("WAYLAND_SOCKET");
if (!wayland_socket_fd)
return -1;
if (!g_ascii_string_to_unsigned (wayland_socket_fd, 10, 0, INT_MAX, &fd, NULL))
return -1;
return (int) fd;
}
#ifdef ENABLE_WAYLAND_SECURITY_CONTEXT
static void registry_handle_global (void *data, struct wl_registry *registry,
@@ -57,6 +89,29 @@ static const struct wl_registry_listener registry_listener = {
.global_remove = registry_handle_global_remove,
};
/* Similar to wl_display_connect (), but does not use WAYLAND_SOCKET,
* which can only be used once, and also does not unset environment
* variables, which would not be thread-safe. */
static struct wl_display *
connect_to_wayland_display (void)
{
struct sockaddr_un sockaddr = {0};
g_autofree char *socket_path = NULL;
glnx_autofd int fd = -1;
socket_path = get_wayland_socket_path ();
fd = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (fd < 0)
return NULL;
sockaddr.sun_family = AF_UNIX;
snprintf (sockaddr.sun_path, sizeof (sockaddr.sun_path), "%s", socket_path);
if (connect (fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) < 0)
return NULL;
return wl_display_connect_to_fd (g_steal_fd (&fd));
}
static char *
create_wl_socket (char *template)
{
@@ -94,7 +149,17 @@ flatpak_run_add_wayland_security_context_args (FlatpakBwrap *bwrap,
*available_out = TRUE;
display = wl_display_connect (NULL);
/* We don't use wl_display_connect () here, for two reasons:
* 1. It would unsetenv ("WAYLAND_SOCKET"), which is not thread-safe.
* 2. If the compositor has set WAYLAND_SOCKET to a special, higher-privileged
* socket, the application should be able to get those privileges for its first
* connection; but that fd can only be used once, so having flatpak itself
* do that first connection would defeat that mechanism.
*
* We still set up a security context for the second and subsequent connections
* to Wayland from within the sandbox.
*/
display = connect_to_wayland_display ();
if (!display)
return FALSE;
@@ -171,14 +236,15 @@ out:
gboolean
flatpak_run_add_wayland_args (FlatpakBwrap *bwrap,
const char *app_id,
const char *instance_id)
const char *instance_id,
gboolean inherit_wayland_socket)
{
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;
int fd;
#ifdef ENABLE_WAYLAND_SECURITY_CONTEXT
gboolean security_context_available = FALSE;
@@ -194,10 +260,7 @@ flatpak_run_add_wayland_args (FlatpakBwrap *bwrap,
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);
wayland_socket = get_wayland_socket_path ();
if (!g_str_has_prefix (wayland_display, "wayland-") ||
strchr (wayland_display, '/') != NULL)
@@ -217,5 +280,29 @@ flatpak_run_add_wayland_args (FlatpakBwrap *bwrap,
NULL);
flatpak_bwrap_add_runtime_dir_member (bwrap, wayland_display);
}
/* If inherit-wayland-socket is not set, unset WAYLAND_SOCKET unconditionally
* without checking the validity of the value of WAYLAND_SOCKET. */
if (!inherit_wayland_socket)
flatpak_bwrap_unset_env (bwrap, "WAYLAND_SOCKET");
fd = get_wayland_socket_fd ();
if (fd >= 0)
{
if (inherit_wayland_socket)
{
flatpak_bwrap_add_fd (bwrap, fd);
}
else
{
/* Make sure the fd is close-on-execute so it won't be inherited by the
* application. We do this in preference to closing it, because if this function
* was somehow called twice, and the same fd number was reused for an
* unrelated purpose, we don't want to close the unrelated fd the
* second time. */
fcntl (fd, F_SETFD, FD_CLOEXEC);
}
}
return res;
}

View File

@@ -134,7 +134,7 @@
Expose a well-known socket to the application. This updates
the [Context] group in the metadata.
SOCKET must be one of: x11, wayland, fallback-x11, pulseaudio, system-bus, session-bus,
ssh-auth, pcsc, cups, gpg-agent.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
This option can be used multiple times.
</para><para>
The fallback-x11 option makes the X11 socket available only if
@@ -151,7 +151,7 @@
Don't expose a well known socket to the application. This updates
the [Context] group in the metadata.
SOCKET must be one of: x11, wayland, fallback-x11, pulseaudio, system-bus, session-bus,
ssh-auth, pcsc, cups, gpg-agent.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
This option can be used multiple times.
</para></listitem>
</varlistentry>

View File

@@ -149,7 +149,7 @@
Expose a well-known socket to the application. This overrides to
the Context section from the application metadata.
<arg choice="plain">SOCKET</arg> must be one of: x11, wayland, fallback-x11, pulseaudio, system-bus, session-bus,
ssh-auth, pcsc, cups, gpg-agent.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
This option can be used multiple times.
</para></listitem>
</varlistentry>
@@ -161,7 +161,7 @@
Don't expose a well-known socket to the application. This overrides to
the Context section from the application metadata.
<arg choice="plain">SOCKET</arg> must be one of: x11, wayland, fallback-x11, pulseaudio, system-bus, session-bus,
ssh-auth, pcsc, cups, gpg-agent.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
This option can be used multiple times.
</para></listitem>
</varlistentry>

View File

@@ -152,7 +152,7 @@
<listitem><para>
List of well-known sockets to make available in the sandbox.
Possible sockets: x11, wayland, fallback-x11, pulseaudio, session-bus, system-bus,
ssh-auth, pcsc, cups.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
When making a socket available, flatpak also sets
well-known environment variables like DISPLAY or
DBUS_SYSTEM_BUS_ADDRESS to let the application

View File

@@ -137,7 +137,7 @@
Expose a well-known socket to the application. This overrides to
the Context section from the application metadata.
<arg choice="plain">SOCKET</arg> must be one of: x11, wayland, fallback-x11, pulseaudio, system-bus, session-bus,
ssh-auth, pcsc, cups, gpg-agent.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
This option can be used multiple times.
</para></listitem>
</varlistentry>
@@ -149,7 +149,7 @@
Don't expose a well-known socket to the application. This overrides to
the Context section from the application metadata.
<arg choice="plain">SOCKET</arg> must be one of: x11, wayland, fallback-x11, pulseaudio, system-bus, session-bus,
ssh-auth, pcsc, cups, gpg-agent.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
This option can be used multiple times.
</para></listitem>
</varlistentry>

View File

@@ -308,7 +308,7 @@
Expose a well known socket to the application. This overrides to
the Context section from the application metadata.
<arg choice="plain">SOCKET</arg> must be one of: x11, wayland, fallback-x11, pulseaudio, system-bus, session-bus,
ssh-auth, pcsc, cups, gpg-agent.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
This option can be used multiple times.
</para></listitem>
</varlistentry>
@@ -320,7 +320,7 @@
Don't expose a well known socket to the application. This overrides to
the Context section from the application metadata.
<arg choice="plain">SOCKET</arg> must be one of: x11, wayland, fallback-x11, pulseaudio, system-bus, session-bus,
ssh-auth, pcsc, cups, gpg-agent.
ssh-auth, pcsc, cups, gpg-agent, inherit-wayland-socket.
This option can be used multiple times.
</para></listitem>
</varlistentry>

View File

@@ -226,7 +226,7 @@ test_full_context (void)
FLATPAK_METADATA_GROUP_CONTEXT,
FLATPAK_METADATA_KEY_SOCKETS,
"x11;wayland;pulseaudio;session-bus;system-bus;"
"fallback-x11;ssh-auth;pcsc;cups;");
"fallback-x11;ssh-auth;pcsc;cups;inherit-wayland-socket;");
g_key_file_set_value (keyfile,
FLATPAK_METADATA_GROUP_CONTEXT,
FLATPAK_METADATA_KEY_DEVICES,
@@ -281,6 +281,7 @@ test_full_context (void)
g_assert_cmpuint (context->sockets, ==,
(FLATPAK_CONTEXT_SOCKET_X11 |
FLATPAK_CONTEXT_SOCKET_WAYLAND |
FLATPAK_CONTEXT_SOCKET_INHERIT_WAYLAND_SOCKET |
FLATPAK_CONTEXT_SOCKET_PULSEAUDIO |
FLATPAK_CONTEXT_SOCKET_SESSION_BUS |
FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS |
@@ -375,6 +376,7 @@ test_full_context (void)
i = 0;
g_assert_cmpstr (strv[i++], ==, "cups");
g_assert_cmpstr (strv[i++], ==, "fallback-x11");
g_assert_cmpstr (strv[i++], ==, "inherit-wayland-socket");
g_assert_cmpstr (strv[i++], ==, "pcsc");
g_assert_cmpstr (strv[i++], ==, "pulseaudio");
g_assert_cmpstr (strv[i++], ==, "session-bus");