diff --git a/common/flatpak-context-private.h b/common/flatpak-context-private.h index 3d1b3418..5a9ebe4c 100644 --- a/common/flatpak-context-private.h +++ b/common/flatpak-context-private.h @@ -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 { diff --git a/common/flatpak-context.c b/common/flatpak-context.c index 2d98d5c9..297a89ef 100644 --- a/common/flatpak-context.c +++ b/common/flatpak-context.c @@ -61,6 +61,7 @@ const char *flatpak_context_sockets[] = { "pcsc", "cups", "gpg-agent", + "inherit-wayland-socket", NULL }; diff --git a/common/flatpak-run-sockets.c b/common/flatpak-run-sockets.c index a20e9f34..be120f36 100644 --- a/common/flatpak-run-sockets.c +++ b/common/flatpak-run-sockets.c @@ -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) diff --git a/common/flatpak-run-wayland-private.h b/common/flatpak-run-wayland-private.h index 14389a91..ad00d234 100644 --- a/common/flatpak-run-wayland-private.h +++ b/common/flatpak-run-wayland-private.h @@ -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 diff --git a/common/flatpak-run-wayland.c b/common/flatpak-run-wayland.c index 0670ab68..1aafddbb 100644 --- a/common/flatpak-run-wayland.c +++ b/common/flatpak-run-wayland.c @@ -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; } diff --git a/doc/flatpak-build-finish.xml b/doc/flatpak-build-finish.xml index 2c7a1393..50394baa 100644 --- a/doc/flatpak-build-finish.xml +++ b/doc/flatpak-build-finish.xml @@ -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. 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. diff --git a/doc/flatpak-build.xml b/doc/flatpak-build.xml index a9e29f5b..d197800f 100644 --- a/doc/flatpak-build.xml +++ b/doc/flatpak-build.xml @@ -149,7 +149,7 @@ Expose a well-known socket to the application. This overrides to the Context section from the application 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. @@ -161,7 +161,7 @@ Don't expose a well-known socket to the application. This overrides to the Context section from the application 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. diff --git a/doc/flatpak-metadata.xml b/doc/flatpak-metadata.xml index a8ab2ee9..d847ed7a 100644 --- a/doc/flatpak-metadata.xml +++ b/doc/flatpak-metadata.xml @@ -152,7 +152,7 @@ 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 diff --git a/doc/flatpak-override.xml b/doc/flatpak-override.xml index 352919da..a74b30f0 100644 --- a/doc/flatpak-override.xml +++ b/doc/flatpak-override.xml @@ -137,7 +137,7 @@ Expose a well-known socket to the application. This overrides to the Context section from the application 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. @@ -149,7 +149,7 @@ Don't expose a well-known socket to the application. This overrides to the Context section from the application 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. diff --git a/doc/flatpak-run.xml b/doc/flatpak-run.xml index 7a9611cb..2cb46b20 100644 --- a/doc/flatpak-run.xml +++ b/doc/flatpak-run.xml @@ -308,7 +308,7 @@ Expose a well known socket to the application. This overrides to the Context section from the application 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. @@ -320,7 +320,7 @@ Don't expose a well known socket to the application. This overrides to the Context section from the application 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. diff --git a/tests/test-exports.c b/tests/test-exports.c index 8a3a4cfe..a20ed0c9 100644 --- a/tests/test-exports.c +++ b/tests/test-exports.c @@ -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");