Security v2

This commit is contained in:
Daniel De Graaf 2020-09-06 18:45:00 -04:00
parent 7ca9ef12f8
commit 4f4b335737
8 changed files with 315 additions and 4 deletions

View file

@ -302,4 +302,6 @@ sway_cmd cmd_ipc_cmd;
sway_cmd cmd_ipc_events; sway_cmd cmd_ipc_events;
sway_cmd cmd_ipc_event_cmd; sway_cmd cmd_ipc_event_cmd;
sway_cmd cmd_security;
#endif #endif

24
include/sway/security.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
enum security_perm {
PRIV_LAYER_SHELL,
PRIV_OUTPUT_MANAGER,
PRIV_INPUT_METHOD,
PRIV_TEXT_INPUT,
PRIV_FOREIGN_TOPLEVEL_MANAGER,
PRIV_DMABUF_MANAGER,
PRIV_SCREENCOPY_MANAGER,
PRIV_DATA_CONTROL_MANAGER,
PRIV_GAMMA_CONTROL_MANAGER,
PRIV_INPUT_INHIBIT,
PRIV_INPUT_KEYBOARD_SHORTCUTS_INHIBIT,
PRIV_INPUT_VIRTUAL_KEYBOARD,
PRIV_INPUT_VIRTUAL_POINTER,
PRIV_OUTPUT_POWER_MANAGER,
PRIV_LAST, /* not an actual permission */
};
bool security_global_filter(const struct wl_client *client, const struct wl_global *global, void *data);
void security_set_permit(struct wl_client *client, uint32_t allowed);
uint32_t security_get_permit(const struct wl_client *client);

View file

@ -37,6 +37,7 @@ struct sway_server {
struct wl_listener compositor_new_surface; struct wl_listener compositor_new_surface;
struct wlr_data_device_manager *data_device_manager; struct wlr_data_device_manager *data_device_manager;
struct wlr_gamma_control_manager_v1 *gamma_control_manager;
struct sway_input_manager *input; struct sway_input_manager *input;
@ -85,6 +86,10 @@ struct sway_server {
struct wlr_text_input_manager_v3 *text_input; struct wlr_text_input_manager_v3 *text_input;
struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
struct wlr_export_dmabuf_manager_v1 *dmabuf_manager;
struct wlr_screencopy_manager_v1 *screencopy_manager;
struct wlr_data_control_manager_v1 *data_control_manager;
size_t txn_timeout_ms; size_t txn_timeout_ms;
list_t *transactions; list_t *transactions;
list_t *dirty_nodes; list_t *dirty_nodes;

View file

@ -81,6 +81,7 @@ static struct cmd_handler handlers[] = {
{ "output", cmd_output }, { "output", cmd_output },
{ "popup_during_fullscreen", cmd_popup_during_fullscreen }, { "popup_during_fullscreen", cmd_popup_during_fullscreen },
{ "seat", cmd_seat }, { "seat", cmd_seat },
{ "security", cmd_security },
{ "set", cmd_set }, { "set", cmd_set },
{ "show_marks", cmd_show_marks }, { "show_marks", cmd_show_marks },
{ "smart_borders", cmd_smart_borders }, { "smart_borders", cmd_smart_borders },

129
sway/commands/security.c Normal file
View file

@ -0,0 +1,129 @@
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
#include "sway/security.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "list.h"
#include "log.h"
struct find_by_cid_data {
uint32_t cid;
struct wl_client *target;
};
static void find_by_cid_iter(struct sway_container *container, void* data) {
struct find_by_cid_data *ctx = data;
if (!container->view)
return;
if (ctx->cid != container->node.id)
return;
if (!container->view->surface)
return;
ctx->target = wl_resource_get_client(container->view->surface->resource);
}
static void do_cmd(struct wl_client *target, int mode, uint32_t privs) {
uint32_t allow;
switch (mode) {
case 0:
security_set_permit(target, privs);
break;
case 1:
allow = security_get_permit(target);
allow |= privs;
security_set_permit(target, allow);
break;
case 2:
allow = security_get_permit(target);
allow &= ~privs;
security_set_permit(target, allow);
break;
}
}
struct cmd_results *cmd_security(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "security", EXPECTED_AT_LEAST, 3))) {
return error;
}
char* op = argv[0];
char* who = argv[1];
uint32_t privs = 0;
int mode;
if (strcmp(op, "set") == 0) {
mode = 0;
} else if (strcmp(op, "permit") == 0) {
mode = 1;
} else if (strcmp(op, "deny") == 0) {
mode = 2;
} else {
return cmd_results_new(CMD_INVALID, "Invalid operation", op);
}
for(int i = 2; i < argc; i++) {
char* arg = argv[i];
if (strcmp(arg, "*") == 0)
privs |= ~0;
else if (strcmp(arg, "layer_shell") == 0)
privs |= 1 << PRIV_LAYER_SHELL;
else if (strcmp(arg, "output_manager") == 0)
privs |= 1 << PRIV_OUTPUT_MANAGER;
else if (strcmp(arg, "input_method") == 0)
privs |= 1 << PRIV_INPUT_METHOD;
else if (strcmp(arg, "text_input") == 0)
privs |= 1 << PRIV_TEXT_INPUT;
else if (strcmp(arg, "foreign_toplevel_manager") == 0)
privs |= 1 << PRIV_FOREIGN_TOPLEVEL_MANAGER;
else if (strcmp(arg, "dmabuf_manager") == 0)
privs |= 1 << PRIV_DMABUF_MANAGER;
else if (strcmp(arg, "screencopy_manager") == 0)
privs |= 1 << PRIV_SCREENCOPY_MANAGER;
else if (strcmp(arg, "data_control_manager") == 0)
privs |= 1 << PRIV_DATA_CONTROL_MANAGER;
else if (strcmp(arg, "gamma_control_manager") == 0)
privs |= 1 << PRIV_GAMMA_CONTROL_MANAGER;
else if (strcmp(arg, "input_inhibit") == 0)
privs |= 1 << PRIV_INPUT_INHIBIT;
else if (strcmp(arg, "input_keyboard_shortcuts_inhibit") == 0)
privs |= 1 << PRIV_INPUT_KEYBOARD_SHORTCUTS_INHIBIT;
else if (strcmp(arg, "input_virtual_keyboard") == 0)
privs |= 1 << PRIV_INPUT_VIRTUAL_KEYBOARD;
else if (strcmp(arg, "input_virtual_pointer") == 0)
privs |= 1 << PRIV_INPUT_VIRTUAL_POINTER;
else if (strcmp(arg, "output_power_manager") == 0)
privs |= 1 << PRIV_OUTPUT_POWER_MANAGER;
else
return cmd_results_new(CMD_INVALID, "Unknown permission", arg);
}
if (strcmp(who, "*") == 0) {
do_cmd(NULL, mode, privs);
} else if (strncmp(who, "cid=", 4) == 0) {
struct find_by_cid_data data = {
.cid = strtoul(who + 4, NULL, 0),
};
root_for_each_container(find_by_cid_iter, &data);
if (!data.target)
return cmd_results_new(CMD_INVALID, "Client not found", who);
do_cmd(data.target, mode, privs);
} else if (strncmp(who, "pid=", 4) == 0) {
pid_t target_pid = strtoul(who + 4, NULL, 0);
struct wl_client* client;
struct wl_list *clients = wl_display_get_client_list(server.wl_display);
wl_client_for_each(client, clients) {
pid_t pid;
wl_client_get_credentials(client, &pid, NULL, NULL);
if (pid == target_pid)
do_cmd(client, mode, privs);
}
} else {
// TODO support other criteria? Only secure things like pid
return cmd_results_new(CMD_INVALID, "Invalid target exmpression", who);
}
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -6,6 +6,7 @@ sway_sources = files(
'ipc-json.c', 'ipc-json.c',
'ipc-server.c', 'ipc-server.c',
'main.c', 'main.c',
'security.c',
'server.c', 'server.c',
'swaynag.c', 'swaynag.c',
'xdg_decoration.c', 'xdg_decoration.c',
@ -96,6 +97,7 @@ sway_sources = files(
'commands/seat/pointer_constraint.c', 'commands/seat/pointer_constraint.c',
'commands/seat/shortcuts_inhibitor.c', 'commands/seat/shortcuts_inhibitor.c',
'commands/seat/xcursor_theme.c', 'commands/seat/xcursor_theme.c',
'commands/security.c',
'commands/set.c', 'commands/set.c',
'commands/show_marks.c', 'commands/show_marks.c',
'commands/shortcuts_inhibitor.c', 'commands/shortcuts_inhibitor.c',

145
sway/security.c Normal file
View file

@ -0,0 +1,145 @@
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/headless.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/noop.h>
#include <wlr/backend/session.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_gtk_primary_selection.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include "config.h"
#include "list.h"
#include "log.h"
#include "sway/config.h"
#include "sway/desktop/idle_inhibit_v1.h"
#include "sway/input/input-manager.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/security.h"
#include "sway/tree/root.h"
struct security_permit {
uint32_t allowed;
const struct wl_client *client;
struct wl_listener on_destroy;
};
static uint32_t security_allow_default = ~0;
static list_t *security_permit_list;
bool security_global_filter(const struct wl_client *client, const struct wl_global *global, void *data)
{
struct sway_server *server = data;
enum security_perm perm;
if (global == server->layer_shell->global)
perm = PRIV_LAYER_SHELL;
else if (global == server->output_manager_v1->global)
perm = PRIV_OUTPUT_MANAGER;
else if (global == server->input_method->global)
perm = PRIV_INPUT_METHOD;
else if (global == server->text_input->global)
perm = PRIV_TEXT_INPUT;
else if (global == server->foreign_toplevel_manager->global)
perm = PRIV_FOREIGN_TOPLEVEL_MANAGER;
else if (global == server->dmabuf_manager->global)
perm = PRIV_DMABUF_MANAGER;
else if (global == server->screencopy_manager->global )
perm = PRIV_SCREENCOPY_MANAGER;
else if (global == server->data_control_manager->global)
perm = PRIV_DATA_CONTROL_MANAGER;
else if (global == server->gamma_control_manager->global)
perm = PRIV_GAMMA_CONTROL_MANAGER;
else if (global == server->input->inhibit->global)
perm = PRIV_INPUT_INHIBIT;
else if (global == server->input->keyboard_shortcuts_inhibit->global)
perm = PRIV_INPUT_KEYBOARD_SHORTCUTS_INHIBIT;
else if (global == server->input->virtual_keyboard->global)
perm = PRIV_INPUT_VIRTUAL_KEYBOARD;
else if (global == server->input->virtual_pointer->global)
perm = PRIV_INPUT_VIRTUAL_POINTER;
else if (global == server->output_power_manager_v1->global)
perm = PRIV_OUTPUT_POWER_MANAGER;
else
return true;
uint32_t priv_bit = 1 << perm;
uint32_t allowed = security_get_permit(client);
if (allowed & priv_bit)
return true;
return false;
}
static void security_client_destroy(struct wl_listener *listener, void *client_v)
{
const struct wl_client *client = client_v;
if (!security_permit_list)
return;
for (int i = 0; i < security_permit_list->length; ++i) {
struct security_permit *item = security_permit_list->items[i];
if (client != item->client)
continue;
list_del(security_permit_list, i);
free(item);
return;
}
}
uint32_t security_get_permit(const struct wl_client *client)
{
if (client && security_permit_list) {
for (int i = 0; i < security_permit_list->length; ++i) {
struct security_permit *item = security_permit_list->items[i];
if (client == item->client)
return item->allowed;
}
}
return security_allow_default;
}
void security_set_permit(struct wl_client *client, uint32_t allowed) {
struct security_permit *found = NULL;
if (client == NULL) {
security_allow_default = allowed;
return;
}
if (!security_permit_list)
security_permit_list = create_list();
for (int i = 0; i < security_permit_list->length; ++i) {
struct security_permit *item = security_permit_list->items[i];
if (client == item->client) {
found = item;
break;
}
}
if (!found) {
found = malloc(sizeof(*found));
found->client = client;
found->on_destroy.notify = security_client_destroy;
list_add(security_permit_list, found);
wl_client_add_destroy_listener(client, &found->on_destroy);
}
found->allowed = allowed;
}

View file

@ -34,6 +34,7 @@
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/server.h" #include "sway/server.h"
#include "sway/security.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
#include "sway/xwayland.h" #include "sway/xwayland.h"
@ -68,7 +69,7 @@ bool server_init(struct sway_server *server) {
server->data_device_manager = server->data_device_manager =
wlr_data_device_manager_create(server->wl_display); wlr_data_device_manager_create(server->wl_display);
wlr_gamma_control_manager_v1_create(server->wl_display); server->gamma_control_manager = wlr_gamma_control_manager_v1_create(server->wl_display);
wlr_gtk_primary_selection_device_manager_create(server->wl_display); wlr_gtk_primary_selection_device_manager_create(server->wl_display);
server->new_output.notify = handle_new_output; server->new_output.notify = handle_new_output;
@ -145,12 +146,14 @@ bool server_init(struct sway_server *server) {
server->foreign_toplevel_manager = server->foreign_toplevel_manager =
wlr_foreign_toplevel_manager_v1_create(server->wl_display); wlr_foreign_toplevel_manager_v1_create(server->wl_display);
wlr_export_dmabuf_manager_v1_create(server->wl_display); server->dmabuf_manager = wlr_export_dmabuf_manager_v1_create(server->wl_display);
wlr_screencopy_manager_v1_create(server->wl_display); server->screencopy_manager = wlr_screencopy_manager_v1_create(server->wl_display);
wlr_data_control_manager_v1_create(server->wl_display); server->data_control_manager = wlr_data_control_manager_v1_create(server->wl_display);
wlr_primary_selection_v1_device_manager_create(server->wl_display); wlr_primary_selection_v1_device_manager_create(server->wl_display);
wlr_viewporter_create(server->wl_display); wlr_viewporter_create(server->wl_display);
wl_display_set_global_filter(server->wl_display, security_global_filter, server);
server->socket = wl_display_add_socket_auto(server->wl_display); server->socket = wl_display_add_socket_auto(server->wl_display);
if (!server->socket) { if (!server->socket) {
sway_log(SWAY_ERROR, "Unable to open wayland socket"); sway_log(SWAY_ERROR, "Unable to open wayland socket");