diff --git a/include/permissions.h b/include/permissions.h new file mode 100644 index 00000000..afd97c8c --- /dev/null +++ b/include/permissions.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_PERMISSIONS_H +#define LABWC_PERMISSIONS_H + +#include +#include + +uint32_t permissions_from_interface_name(const char *s); +int permissions_context_create(struct wl_display *display, uint32_t permissions); +bool permissions_check(const struct wl_client *client, const struct wl_interface *iface); + +#endif diff --git a/src/meson.build b/src/meson.build index 330b5daf..693c162b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,7 @@ labwc_sources = files( 'action.c', 'buffer.c', + 'permissions.c', 'debug.c', 'desktop.c', 'dnd.c', diff --git a/src/permissions.c b/src/permissions.c new file mode 100644 index 00000000..914e9963 --- /dev/null +++ b/src/permissions.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include "permissions.h" +#include +#include +#include +#include +#include "common/mem.h" +#include "common/spawn.h" +#include "config/rcxml.h" + +struct permissions_context { + uint32_t permissions; + int fd; + struct wl_listener destroy; +}; + +enum lab_permission { + LAB_PERM_DRM_LEASE = 1 << 0, + LAB_PERM_GAMMA = 1 << 1, + LAB_PERM_OUTPUT = 1 << 2, + LAB_PERM_OUTPUT_POWER = 1 << 3, + LAB_PERM_INPUT_METHOD = 1 << 4, + LAB_PERM_VIRTUAL_POINTER = 1 << 5, + LAB_PERM_VIRTUAL_KEYBOARD = 1 << 6, + LAB_PERM_DMABUF = 1 << 7, + LAB_PERM_SCREENCOPY = 1 << 8, + LAB_PERM_DATA_CONTROL = 1 << 9, + LAB_PERM_IDLE_NOTIFIER = 1 << 10, + LAB_PERM_WORKSPACE = 1 << 11, + LAB_PERM_FOREIGN_TOPLEVEL = 1 << 12, + LAB_PERM_SESSION_LOCK = 1 << 13, + LAB_PERM_LAYER_SHELL = 1 << 14, +}; + +static void +permissions_context_handle_destroy(struct wl_listener *listener, void *data) +{ + struct permissions_context *context = wl_container_of(listener, context, destroy); + close(context->fd); + zfree(context); +} + +static uint32_t +permissions_get(const struct wl_client *client) +{ + struct wl_listener *listener = wl_client_get_destroy_listener( + (struct wl_client *)client, permissions_context_handle_destroy); + if (!listener) { + return 0; + } + + struct permissions_context *context = wl_container_of(listener, context, destroy); + return context->permissions; +} + +uint32_t +permissions_from_interface_name(const char *name) +{ + if (!strcmp(name, "wp_drm_lease_device_v1")) { + return LAB_PERM_DRM_LEASE; + } else if (!strcmp(name, "zwlr_gamma_control_manager_v1")) { + return LAB_PERM_GAMMA; + } else if (!strcmp(name, "zwlr_output_manager_v1")) { + return LAB_PERM_OUTPUT; + } else if (!strcmp(name, "zwlr_output_power_manager_v1")) { + return LAB_PERM_OUTPUT_POWER; + } else if (!strcmp(name, "zwp_input_method_manager_v2")) { + return LAB_PERM_INPUT_METHOD; + } else if (!strcmp(name, "zwlr_virtual_pointer_manager_v1")) { + return LAB_PERM_VIRTUAL_POINTER; + } else if (!strcmp(name, "zwp_virtual_keyboard_manager_v1")) { + return LAB_PERM_VIRTUAL_KEYBOARD; + } else if (!strcmp(name, "zwlr_export_dmabuf_manager_v1")) { + return LAB_PERM_DMABUF; + } else if (!strcmp(name, "zwlr_screencopy_manager_v1")) { + return LAB_PERM_SCREENCOPY; + } else if (!strcmp(name, "zwlr_data_control_manager_v1")) { + return LAB_PERM_DATA_CONTROL; + } else if (!strcmp(name, "ext_idle_notifier_v1")) { + return LAB_PERM_IDLE_NOTIFIER; + } else if (!strcmp(name, "zcosmic_workspace_manager_v1")) { + return LAB_PERM_WORKSPACE; + } else if (!strcmp(name, "zwlr_foreign_toplevel_manager_v1")) { + return LAB_PERM_FOREIGN_TOPLEVEL; + } else if (!strcmp(name, "ext_foreign_toplevel_list_v1")) { + return LAB_PERM_FOREIGN_TOPLEVEL; + } else if (!strcmp(name, "ext_session_lock_manager_v1")) { + return LAB_PERM_SESSION_LOCK; + } else if (!strcmp(name, "zwlr_layer_shell_v1")) { + return LAB_PERM_LAYER_SHELL; + } else { + return 0; + } +} + +int +permissions_context_create(struct wl_display *display, uint32_t permissions) +{ + if (permissions == 0) { + return -1; + } + + int sv[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) { + wlr_log_errno(WLR_ERROR, "socketpair failed"); + return -1; + } + + if (!set_cloexec(sv[0])) { + close(sv[0]); + close(sv[1]); + return -1; + } + + struct wl_client *client = wl_client_create(display, sv[0]); + if (!client) { + wlr_log(WLR_ERROR, "failed to create client"); + close(sv[0]); + close(sv[1]); + return -1; + } + + struct permissions_context *context = znew(*context); + context->permissions = permissions; + context->fd = sv[0]; + context->destroy.notify = permissions_context_handle_destroy; + wl_client_add_destroy_listener(client, &context->destroy); + + return sv[1]; +} + +bool +permissions_check(const struct wl_client *client, const struct wl_interface *iface) +{ + uint32_t permissions = permissions_get(client); + uint32_t required = permissions_from_interface_name(iface->name); + return (permissions & required) == required; +} diff --git a/src/server.c b/src/server.c index abaaaf0b..5cfbbe80 100644 --- a/src/server.c +++ b/src/server.c @@ -58,6 +58,7 @@ #include "menu/menu.h" #include "output.h" #include "output-virtual.h" +#include "permissions.h" #include "regions.h" #include "resize-indicator.h" #include "scaled-buffer/scaled-buffer.h" @@ -314,6 +315,10 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo } #endif + if (!permissions_check(client, iface)) { + return false; + } + /* Do not allow security_context_manager_v1 to clients with a security context attached */ const struct wlr_security_context_v1_state *security_context = wlr_security_context_manager_v1_lookup_client(