From 50d84b8512a767a81f4878a53d016664ec081260 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 8 Nov 2018 11:54:28 +0100 Subject: [PATCH] Implement basic static security checks with PID --- include/sway/security.h | 15 ++--- sway/main.c | 25 +++++--- sway/security.c | 139 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 157 insertions(+), 22 deletions(-) diff --git a/include/sway/security.h b/include/sway/security.h index 0edffdfa7..fa1dcf177 100644 --- a/include/sway/security.h +++ b/include/sway/security.h @@ -1,18 +1,13 @@ #ifndef _SWAY_SECURITY_H #define _SWAY_SECURITY_H -#include + +#include +#include #include "sway/config.h" -uint32_t get_feature_policy_mask(pid_t pid); -uint32_t get_ipc_policy_mask(pid_t pid); -uint32_t get_command_policy_mask(const char *cmd); +bool load_security(struct wl_display *display); +bool check_security_rule(const char *cmd, const char *global); -struct feature_policy *get_feature_policy(const char *name); - -const char *command_policy_str(enum command_context context); - -struct feature_policy *alloc_feature_policy(const char *program); -struct ipc_policy *alloc_ipc_policy(const char *program); struct command_policy *alloc_command_policy(const char *command); #endif diff --git a/sway/main.c b/sway/main.c index 920cea11c..bef5a40fa 100644 --- a/sway/main.c +++ b/sway/main.c @@ -4,27 +4,28 @@ #include #include #include -#include #include +#include #include #include #include -#include #include +#include #include #include -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/debug.h" -#include "sway/desktop/transaction.h" -#include "sway/server.h" -#include "sway/swaynag.h" -#include "sway/tree/root.h" -#include "sway/ipc-server.h" #include "ipc-client.h" #include "log.h" #include "readline.h" #include "stringop.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/debug.h" +#include "sway/desktop/transaction.h" +#include "sway/ipc-server.h" +#include "sway/security.h" +#include "sway/server.h" +#include "sway/swaynag.h" +#include "sway/tree/root.h" #include "util.h" static bool terminate_request = false; @@ -372,6 +373,10 @@ int main(int argc, char **argv) { return valid ? 0 : 1; } + if (!load_security(server.wl_display)) { + sway_terminate(EXIT_FAILURE); + } + setenv("WAYLAND_DISPLAY", server.socket, true); if (!load_main_config(config_path, false, false)) { sway_terminate(EXIT_FAILURE); diff --git a/sway/security.c b/sway/security.c index cc0d3f661..23af3ebf2 100644 --- a/sway/security.c +++ b/sway/security.c @@ -1,10 +1,145 @@ -#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200809L #include #include +#include +#include +#include +#include #include "sway/security.h" +struct sway_security_rule { + char *command; + char *global; + struct wl_list link; +}; + +static struct wl_list rules; + +static struct sway_security_rule *security_rule_create(const char *cmd, + const char *global) { + struct sway_security_rule *rule = + calloc(1, sizeof(struct sway_security_rule)); + if (rule == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + if (cmd != NULL) { + rule->command = strdup(cmd); + if (rule->command == NULL) { + goto err; + } + } + if (global != NULL) { + rule->global = strdup(global); + if (rule->global == NULL) { + goto err; + } + } + wl_list_insert(&rules, &rule->link); + return rule; + +err: + wlr_log(WLR_ERROR, "Allocation failed"); + free(rule->global); + free(rule->command); + free(rule); + return NULL; +} + +static bool command_from_pid(char cmd[static PATH_MAX + 1], pid_t pid) { +#ifdef __linux__ + char link_path[PATH_MAX]; + snprintf(link_path, sizeof(link_path), "/proc/%d/exe", pid); + + ssize_t n = readlink(link_path, cmd, PATH_MAX); + if (n < 0) { + wlr_log_errno(WLR_ERROR, "Failed to readlink() %s", link_path); + return false; + } + cmd[n] = '\0'; + return true; +#else + return false; +#endif +} + +static bool global_filter(const struct wl_client *client, + const struct wl_global *global, void *data) { + pid_t pid = 0; + wl_client_get_credentials((struct wl_client *)client, &pid, NULL, NULL); + if (pid == 0) { + wlr_log(WLR_DEBUG, "Host doesn't support Wayland credentials, " + "cannot enforce security rules"); + return true; + } + + char cmd[PATH_MAX + 1]; + if (!command_from_pid(cmd, pid)) { + wlr_log(WLR_ERROR, "Failed to get command path from PID %d", pid); + return false; + } + + const struct wl_interface *interface = wl_global_get_interface(global); + bool ok = check_security_rule(cmd, interface->name); + if (ok) { + wlr_log(WLR_DEBUG, "Allowing %s to bind to %s", cmd, interface->name); + } else { + wlr_log(WLR_DEBUG, "Denying %s from binding to %s", cmd, interface->name); + } + return ok; +} + +bool load_security(struct wl_display *display) { + wl_list_init(&rules); + + wl_display_set_global_filter(display, global_filter, NULL); + + // TODO: move this in a file + security_rule_create(NULL, "wl_shm"); + security_rule_create(NULL, "wl_drm"); + security_rule_create(NULL, "wl_compositor"); + security_rule_create(NULL, "wl_subcompositor"); + security_rule_create(NULL, "wl_data_device_manager"); + security_rule_create(NULL, "wl_seat"); + security_rule_create(NULL, "wl_output"); + security_rule_create(NULL, "zwp_linux_dmabuf_v1"); + security_rule_create(NULL, "gtk_primary_selection_device_manager"); + security_rule_create(NULL, "zxdg_output_manager_v1"); + security_rule_create(NULL, "org_kde_kwin_idle"); + security_rule_create(NULL, "zwp_idle_inhibit_manager_v1"); + security_rule_create(NULL, "zxdg_shell_v6"); + security_rule_create(NULL, "xdg_wm_base"); + security_rule_create(NULL, "org_kde_kwin_server_decoration_manager"); + security_rule_create(NULL, "zxdg_decoration_manager_v1"); + security_rule_create(NULL, "wp_presentation"); + // gamma_control_manager + // zwlr_gamma_control_manager_v1 + // zwlr_layer_shell_v1 + // zwlr_export_dmabuf_manager_v1 + // zwlr_screencopy_manager_v1 + // zwp_virtual_keyboard_manager_v1 + // zwlr_input_inhibit_manager_v1 + + return true; +} + +bool check_security_rule(const char *cmd, const char *global) { + struct sway_security_rule *rule; + wl_list_for_each(rule, &rules, link) { + if (rule->command != NULL && strcmp(cmd, rule->command) != 0) { + continue; + } + if (rule->global != NULL && strcmp(global, rule->global) != 0) { + continue; + } + return true; + } + return false; +} + + struct command_policy *alloc_command_policy(const char *command) { - struct command_policy *policy = malloc(sizeof(struct command_policy)); + struct command_policy *policy = calloc(1, sizeof(struct command_policy)); if (!policy) { return NULL; }