This commit is contained in:
WavyEbuilder 2025-10-05 18:46:56 +01:00 committed by GitHub
commit 7382067bf0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 221 additions and 0 deletions

View file

@ -74,6 +74,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
pixman = dependency('pixman-1') pixman = dependency('pixman-1')
libevdev = dependency('libevdev') libevdev = dependency('libevdev')
libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.26.0') : null_dep libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.26.0') : null_dep
libselinux = dependency('libselinux', required: get_option('selinux'))
xcb = wlroots_features['xwayland'] ? dependency('xcb') : null_dep xcb = wlroots_features['xwayland'] ? dependency('xcb') : null_dep
drm = dependency('libdrm') drm = dependency('libdrm')
libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep
@ -110,6 +111,7 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd
conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind')
conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu')
conf_data.set10('HAVE_TRAY', have_tray) conf_data.set10('HAVE_TRAY', have_tray)
conf_data.set10('HAVE_SELINUX', libselinux.found())
foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY'] foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY']
conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput)) conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput))
endforeach endforeach

View file

@ -8,3 +8,4 @@ option('tray', type: 'feature', value: 'auto', description: 'Enable support for
option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray') option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray')
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library')
option('selinux', type: 'boolean', value: false, description: 'Enable support for SELinux access control')

View file

@ -26,6 +26,10 @@
#include "stringop.h" #include "stringop.h"
#include "util.h" #include "util.h"
#if HAVE_SELINUX
#include <selinux/selinux.h>
#endif
static bool terminate_request = false; static bool terminate_request = false;
static int exit_value = 0; static int exit_value = 0;
static struct rlimit original_nofile_rlimit = {0}; static struct rlimit original_nofile_rlimit = {0};
@ -199,6 +203,44 @@ static void handle_wlr_log(enum wlr_log_importance importance,
_sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args);
} }
#if HAVE_SELINUX
static sway_log_importance_t convert_selinux_log_importance(int type) {
switch (type) {
case SELINUX_ERROR:
return SWAY_ERROR;
case SELINUX_WARNING:
__attribute__ ((fallthrough));
case SELINUX_AVC:
__attribute__ ((fallthrough));
case SELINUX_INFO:
return SWAY_INFO;
default:
return SWAY_DEBUG;
}
}
static int log_callback_selinux(int type, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
const int space_needed = snprintf(NULL, 0, "[selinux] %s", fmt);
if (space_needed < 0) {
return -1;
}
char *buffer = calloc(space_needed + 1, sizeof(*buffer));
if (buffer == NULL) {
return -1;
}
sprintf(buffer, "[selinux] %s", fmt);
_sway_vlog(convert_selinux_log_importance(type), buffer, args);
free(buffer);
va_end(args);
return 0;
}
#endif
static const struct option long_options[] = { static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
{"config", required_argument, NULL, 'c'}, {"config", required_argument, NULL, 'c'},
@ -299,6 +341,11 @@ int main(int argc, char **argv) {
wlr_log_init(WLR_ERROR, handle_wlr_log); wlr_log_init(WLR_ERROR, handle_wlr_log);
} }
#if HAVE_SELINUX
// Initalize libselinux logging.
selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) { .func_log = log_callback_selinux });
#endif
sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); sway_log(SWAY_INFO, "Sway version " SWAY_VERSION);
sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR); sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR);
log_kernel(); log_kernel();

View file

@ -224,6 +224,7 @@ sway_deps = [
jsonc, jsonc,
libevdev, libevdev,
libinput, libinput,
libselinux,
libudev, libudev,
math, math,
pango, pango,

View file

@ -1,7 +1,12 @@
#ifdef __linux__
#define _DEFAULT_SOURCE
#endif
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/headless.h> #include <wlr/backend/headless.h>
@ -72,6 +77,10 @@
#include <wlr/types/wlr_drm_lease_v1.h> #include <wlr/types/wlr_drm_lease_v1.h>
#endif #endif
#if HAVE_SELINUX
#include <selinux/selinux.h>
#endif
#define SWAY_XDG_SHELL_VERSION 5 #define SWAY_XDG_SHELL_VERSION 5
#define SWAY_LAYER_SHELL_VERSION 4 #define SWAY_LAYER_SHELL_VERSION 4
#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1
@ -93,6 +102,163 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
} }
#endif #endif
#if HAVE_SELINUX
static const char *get_selinux_protocol(const struct wl_global *global) {
#if WLR_HAS_DRM_BACKEND
if (server.drm_lease_manager != NULL) {
struct wlr_drm_lease_device_v1 *drm_lease_dev;
wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) {
if (drm_lease_dev->global == global) {
return "drm_lease";
}
}
}
#endif
if (global == server.output_manager_v1->global) {
return "output_manager";
}
if (global == server.output_power_manager_v1->global) {
return "output_power_manager";
}
if (global == server.input_method->global) {
return "input_method";
}
if (global == server.foreign_toplevel_list->global) {
return "foreign_toplevel_list";
}
if (global == server.foreign_toplevel_manager->global) {
return "foreign_toplevel_manager";
}
if (global == server.wlr_data_control_manager_v1->global) {
return "data_control_manager";
}
if (global == server.ext_data_control_manager_v1->global) {
return "data_control_manager";
}
if (global == server.screencopy_manager_v1->global) {
return "screencopy_manager";
}
if (global == server.ext_image_copy_capture_manager_v1->global) {
return "image_copy_capture_manager";
}
if (global == server.export_dmabuf_manager_v1->global) {
return "export_dmabuf_manager";
}
if (global == server.security_context_manager_v1->global) {
return "security_context_manager";
}
if (global == server.gamma_control_manager_v1->global) {
return "gamma_control_manager";
}
if (global == server.layer_shell->global) {
return "layer_shell";
}
if (global == server.session_lock.manager->global) {
return "session_lock_manager";
}
if (global == server.input->keyboard_shortcuts_inhibit->global) {
return "keyboard_shortcuts_inhibit";
}
if (global == server.input->virtual_keyboard->global) {
return "virtual_keyboard";
}
if (global == server.input->virtual_pointer->global) {
return "virtual_pointer";
}
if (global == server.input->transient_seat_manager->global) {
return "transient_seat_manager";
}
if (global == server.xdg_output_manager_v1->global) {
return "xdg_output_manager";
}
return NULL;
}
#endif
static bool check_access_selinux(const struct wl_global *global,
const struct wl_client *client) {
#if HAVE_SELINUX
if (is_selinux_enabled() == 0) {
// SELinux not running
return true;
}
const char *protocol = get_selinux_protocol(global);
if (protocol == NULL) {
return true; // Not a privileged protocol, access granted
}
char *client_context = NULL;
socklen_t len = NAME_MAX;
int r;
const int sockfd = wl_client_get_fd((struct wl_client *)client);
do {
char *new_context = realloc(client_context, len);
if (new_context == NULL) {
free(client_context);
return false;
}
client_context = new_context;
r = getsockopt(sockfd, SOL_SOCKET, SO_PEERSEC, client_context, &len);
if (r < 0 && errno != ERANGE) {
free(client_context);
return false;
}
} while (r < 0 && errno == ERANGE);
if (client_context == NULL) {
return true; // Getting NULL back for SO_PEERSEC means that an LSM
// that provides security contexts is not running.
}
r = security_getenforce();
// If we can't determine if SELinux is enforcing or not, proceed as if enforcing.
const bool enforcing = !r;
char *compositor_context = NULL;
if (getcon_raw(&compositor_context) < 0) {
_sway_log(SWAY_ERROR, "[selinux] getcon_raw() failed: %s", strerror(errno));
free(client_context);
// We can't get our own context. Only allow the access if not in enforcing mode.
return !enforcing;
}
if (compositor_context == NULL) {
_sway_log(SWAY_ERROR, "[selinux] getcon_raw() returned NULL");
free(client_context);
// We can't get our own context. Only allow the access if not in enforcing mode.
return !enforcing;
}
static const char *const tclass = "wayland";
errno = 0;
r = selinux_check_access(client_context, compositor_context, tclass, protocol, NULL);
_sway_log(SWAY_DEBUG, "[selinux] access check scon=%s tcon=%s tclass=%s perm=%s",
client_context, compositor_context, tclass, protocol);
if (r < 0) {
// EINVAL for contexts unknown to policy.
if (errno != EACCES || errno != EINVAL) {
_sway_log(SWAY_INFO, "[selinux] access check failed: %s", strerror(errno));
free(client_context);
free(compositor_context);
// selinux_check_access failed. Only allow the access if not in enforcing mode.
return !enforcing;
}
_sway_log(SWAY_INFO, "[selinux] access check denied: %s", strerror(errno));
}
free(client_context);
free(compositor_context);
return enforcing ? (r == 0) : true;
#else
return true;
#endif
}
static bool is_privileged(const struct wl_global *global) { static bool is_privileged(const struct wl_global *global) {
#if WLR_HAS_DRM_BACKEND #if WLR_HAS_DRM_BACKEND
if (server.drm_lease_manager != NULL) { if (server.drm_lease_manager != NULL) {
@ -136,6 +302,10 @@ static bool filter_global(const struct wl_client *client,
} }
#endif #endif
if (!check_access_selinux(global, client)) {
return false;
}
// Restrict usage of privileged protocols to unsandboxed clients // Restrict usage of privileged protocols to unsandboxed clients
// TODO: add a way for users to configure an allow-list // TODO: add a way for users to configure an allow-list
const struct wlr_security_context_v1_state *security_context = const struct wlr_security_context_v1_state *security_context =