Implement basic static security checks with PID

This commit is contained in:
emersion 2018-11-08 11:54:28 +01:00
parent 4a21981855
commit 50d84b8512
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
3 changed files with 157 additions and 22 deletions

View file

@ -1,18 +1,13 @@
#ifndef _SWAY_SECURITY_H
#define _SWAY_SECURITY_H
#include <unistd.h>
#include <stdbool.h>
#include <wayland-server.h>
#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

View file

@ -4,27 +4,28 @@
#include <pango/pangocairo.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#include <wlr/util/log.h>
#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);

View file

@ -1,10 +1,145 @@
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <wlr/util/log.h>
#include <wayland-server-core.h>
#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;
}