mirror of
https://github.com/swaywm/sway.git
synced 2024-11-11 04:54:14 +01:00
commit
5778c59a2f
@ -45,9 +45,11 @@ option(enable-swaybar "Enables the swaybar utility" YES)
|
||||
option(enable-swaygrab "Enables the swaygrab utility" YES)
|
||||
option(enable-swaymsg "Enables the swaymsg utility" YES)
|
||||
option(enable-gdk-pixbuf "Use Pixbuf to support more image formats" YES)
|
||||
option(enable-binding-event "Enables binding event subscription" YES)
|
||||
option(zsh-completions "Zsh shell completions" NO)
|
||||
option(default-wallpaper "Installs the default wallpaper" YES)
|
||||
option(ld-library-path "Configures sway's default LD_LIBRARY_PATH" "/usr/lib")
|
||||
|
||||
add_definitions(-D_LD_LIBRARY_PATH="${ld-library-path}")
|
||||
|
||||
find_package(JsonC REQUIRED)
|
||||
find_package(PCRE REQUIRED)
|
||||
@ -83,9 +85,6 @@ if (enable-gdk-pixbuf)
|
||||
else()
|
||||
message(STATUS "Building without gdk-pixbuf, only png images supported.")
|
||||
endif()
|
||||
if(enable-binding-event)
|
||||
add_definitions(-DSWAY_BINDING_EVENT=1)
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
|
||||
|
52
config.d/security.in
Normal file
52
config.d/security.in
Normal file
@ -0,0 +1,52 @@
|
||||
# sway security rules
|
||||
#
|
||||
# Read sway-security(7) for details on how to secure your sway install.
|
||||
#
|
||||
# You MUST read this man page if you intend to attempt to secure your sway
|
||||
# installation.
|
||||
|
||||
# Configures which programs are allowed to use which sway features
|
||||
permit * fullscreen keyboard mouse ipc
|
||||
permit __PREFIX__/bin/swaylock lock
|
||||
permit __PREFIX__/bin/swaybar panel
|
||||
permit __PREFIX__/bin/swaybg background
|
||||
permit __PREFIX__/bin/swaygrab screenshot
|
||||
|
||||
# Configures which IPC features are enabled
|
||||
ipc {
|
||||
command enabled
|
||||
outputs enabled
|
||||
workspaces enabled
|
||||
tree enabled
|
||||
marks enabled
|
||||
bar-config enabled
|
||||
inputs enabled
|
||||
|
||||
events {
|
||||
workspace enabled
|
||||
output enabled
|
||||
mode enabled
|
||||
window enabled
|
||||
modifier enabled
|
||||
input enabled
|
||||
binding disabled
|
||||
}
|
||||
}
|
||||
|
||||
# Limits the contexts from which certain commands are permitted
|
||||
commands {
|
||||
* all
|
||||
|
||||
fullscreen binding criteria
|
||||
bindsym config
|
||||
exit binding
|
||||
kill binding
|
||||
|
||||
# You should not change these unless you know what you're doing - it could
|
||||
# cripple your security
|
||||
reload binding
|
||||
restart binding
|
||||
permit config
|
||||
reject config
|
||||
ipc config
|
||||
}
|
@ -195,10 +195,4 @@ bar {
|
||||
}
|
||||
}
|
||||
|
||||
# You may want this:
|
||||
#
|
||||
# include ~/.config/sway/conf.d/*
|
||||
#
|
||||
# Protip:
|
||||
#
|
||||
# include ~/.config/sway/`hostname`/*
|
||||
include __SYSCONFDIR__/sway/config.d/*
|
@ -1,6 +1,8 @@
|
||||
#ifndef _SWAY_IPC_H
|
||||
#define _SWAY_IPC_H
|
||||
|
||||
#define event_mask(ev) (1 << (ev & 0x7F))
|
||||
|
||||
enum ipc_command_type {
|
||||
IPC_COMMAND = 0,
|
||||
IPC_GET_WORKSPACES = 1,
|
||||
|
@ -18,7 +18,10 @@ enum cmd_status {
|
||||
CMD_BLOCK_MODE,
|
||||
CMD_BLOCK_BAR,
|
||||
CMD_BLOCK_BAR_COLORS,
|
||||
CMD_BLOCK_INPUT
|
||||
CMD_BLOCK_INPUT,
|
||||
CMD_BLOCK_COMMANDS,
|
||||
CMD_BLOCK_IPC,
|
||||
CMD_BLOCK_IPC_EVENTS,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -51,13 +54,17 @@ int sp_index;
|
||||
/**
|
||||
* Parse and handles a command.
|
||||
*/
|
||||
struct cmd_results *handle_command(char *command);
|
||||
struct cmd_results *handle_command(char *command, enum command_context context);
|
||||
/**
|
||||
* Parse and handles a command during config file loading.
|
||||
*
|
||||
* Do not use this under normal conditions.
|
||||
*/
|
||||
struct cmd_results *config_command(char *command, enum cmd_status block);
|
||||
/*
|
||||
* Parses a command policy rule.
|
||||
*/
|
||||
struct cmd_results *config_commands_command(char *exec);
|
||||
|
||||
/**
|
||||
* Allocates a cmd_results object.
|
||||
@ -93,6 +100,7 @@ sway_cmd cmd_client_unfocused;
|
||||
sway_cmd cmd_client_urgent;
|
||||
sway_cmd cmd_client_placeholder;
|
||||
sway_cmd cmd_client_background;
|
||||
sway_cmd cmd_commands;
|
||||
sway_cmd cmd_debuglog;
|
||||
sway_cmd cmd_exec;
|
||||
sway_cmd cmd_exec_always;
|
||||
@ -112,6 +120,7 @@ sway_cmd cmd_gaps;
|
||||
sway_cmd cmd_hide_edge_borders;
|
||||
sway_cmd cmd_include;
|
||||
sway_cmd cmd_input;
|
||||
sway_cmd cmd_ipc;
|
||||
sway_cmd cmd_kill;
|
||||
sway_cmd cmd_layout;
|
||||
sway_cmd cmd_log_colors;
|
||||
@ -122,6 +131,8 @@ sway_cmd cmd_new_float;
|
||||
sway_cmd cmd_new_window;
|
||||
sway_cmd cmd_orientation;
|
||||
sway_cmd cmd_output;
|
||||
sway_cmd cmd_permit;
|
||||
sway_cmd cmd_reject;
|
||||
sway_cmd cmd_reload;
|
||||
sway_cmd cmd_resize;
|
||||
sway_cmd cmd_scratchpad;
|
||||
@ -182,4 +193,8 @@ sway_cmd input_cmd_pointer_accel;
|
||||
sway_cmd input_cmd_scroll_method;
|
||||
sway_cmd input_cmd_tap;
|
||||
|
||||
sway_cmd cmd_ipc_cmd;
|
||||
sway_cmd cmd_ipc_events;
|
||||
sway_cmd cmd_ipc_event_cmd;
|
||||
|
||||
#endif
|
||||
|
@ -103,9 +103,6 @@ struct pid_workspace {
|
||||
time_t *time_added;
|
||||
};
|
||||
|
||||
void pid_workspace_add(struct pid_workspace *pw);
|
||||
void free_pid_workspace(struct pid_workspace *pw);
|
||||
|
||||
struct bar_config {
|
||||
/**
|
||||
* One of "dock", "hide", "invisible"
|
||||
@ -138,7 +135,7 @@ struct bar_config {
|
||||
int height; // -1 not defined
|
||||
int tray_padding;
|
||||
bool workspace_buttons;
|
||||
bool wrap_scroll;
|
||||
bool wrap_scroll;
|
||||
char *separator_symbol;
|
||||
bool strip_workspace_numbers;
|
||||
bool binding_mode_indicator;
|
||||
@ -184,6 +181,52 @@ enum edge_border_types {
|
||||
E_BOTH /**< hide vertical and horizontal edge borders */
|
||||
};
|
||||
|
||||
enum command_context {
|
||||
CONTEXT_CONFIG = 1,
|
||||
CONTEXT_BINDING = 2,
|
||||
CONTEXT_IPC = 4,
|
||||
CONTEXT_CRITERIA = 8,
|
||||
CONTEXT_ALL = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
struct command_policy {
|
||||
char *command;
|
||||
uint32_t context;
|
||||
};
|
||||
|
||||
enum secure_feature {
|
||||
FEATURE_LOCK = 1,
|
||||
FEATURE_PANEL = 2,
|
||||
FEATURE_BACKGROUND = 4,
|
||||
FEATURE_SCREENSHOT = 8,
|
||||
FEATURE_FULLSCREEN = 16,
|
||||
FEATURE_KEYBOARD = 32,
|
||||
FEATURE_MOUSE = 64,
|
||||
FEATURE_IPC = 128,
|
||||
};
|
||||
|
||||
struct feature_policy {
|
||||
char *program;
|
||||
uint32_t features;
|
||||
};
|
||||
|
||||
enum ipc_feature {
|
||||
IPC_FEATURE_COMMAND = 1,
|
||||
IPC_FEATURE_GET_WORKSPACES = 2,
|
||||
IPC_FEATURE_GET_OUTPUTS = 4,
|
||||
IPC_FEATURE_GET_TREE = 8,
|
||||
IPC_FEATURE_GET_MARKS = 16,
|
||||
IPC_FEATURE_GET_BAR_CONFIG = 32,
|
||||
IPC_FEATURE_GET_VERSION = 64,
|
||||
IPC_FEATURE_GET_INPUTS = 128,
|
||||
IPC_FEATURE_EVENT_WORKSPACE = 256,
|
||||
IPC_FEATURE_EVENT_OUTPUT = 512,
|
||||
IPC_FEATURE_EVENT_MODE = 1024,
|
||||
IPC_FEATURE_EVENT_WINDOW = 2048,
|
||||
IPC_FEATURE_EVENT_BINDING = 4096,
|
||||
IPC_FEATURE_EVENT_INPUT = 8192
|
||||
};
|
||||
|
||||
/**
|
||||
* The configuration struct. The result of loading a config file.
|
||||
*/
|
||||
@ -203,7 +246,7 @@ struct sway_config {
|
||||
uint32_t floating_mod;
|
||||
uint32_t dragging_key;
|
||||
uint32_t resizing_key;
|
||||
char *floating_scroll_up_cmd;
|
||||
char *floating_scroll_up_cmd;
|
||||
char *floating_scroll_down_cmd;
|
||||
char *floating_scroll_left_cmd;
|
||||
char *floating_scroll_right_cmd;
|
||||
@ -252,8 +295,16 @@ struct sway_config {
|
||||
int32_t floating_maximum_height;
|
||||
int32_t floating_minimum_width;
|
||||
int32_t floating_minimum_height;
|
||||
|
||||
// Security
|
||||
list_t *command_policies;
|
||||
list_t *feature_policies;
|
||||
uint32_t ipc_policy;
|
||||
};
|
||||
|
||||
void pid_workspace_add(struct pid_workspace *pw);
|
||||
void free_pid_workspace(struct pid_workspace *pw);
|
||||
|
||||
/**
|
||||
* Loads the main config from the given path. is_active should be true when
|
||||
* reloading the config.
|
||||
|
14
include/sway/security.h
Normal file
14
include/sway/security.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef _SWAY_SECURITY_H
|
||||
#define _SWAY_SECURITY_H
|
||||
#include <unistd.h>
|
||||
#include "sway/config.h"
|
||||
|
||||
enum secure_feature get_feature_policy(pid_t pid);
|
||||
enum command_context get_command_policy(const char *cmd);
|
||||
|
||||
const char *command_policy_str(enum command_context context);
|
||||
|
||||
struct feature_policy *alloc_feature_policy(const char *program);
|
||||
struct command_policy *alloc_command_policy(const char *command);
|
||||
|
||||
#endif
|
@ -35,6 +35,7 @@ add_executable(sway
|
||||
output.c
|
||||
workspace.c
|
||||
border.c
|
||||
security.c
|
||||
)
|
||||
|
||||
add_definitions(
|
||||
@ -54,6 +55,7 @@ target_link_libraries(sway
|
||||
${PANGO_LIBRARIES}
|
||||
${JSONC_LIBRARIES}
|
||||
m
|
||||
cap
|
||||
)
|
||||
|
||||
install(
|
||||
@ -62,13 +64,34 @@ install(
|
||||
DESTINATION bin
|
||||
COMPONENT runtime
|
||||
)
|
||||
install(
|
||||
FILES ${PROJECT_SOURCE_DIR}/config
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sway/
|
||||
COMPONENT configuration
|
||||
)
|
||||
|
||||
add_custom_target(configs ALL)
|
||||
|
||||
function(add_config name source destination)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
|
||||
COMMAND sed -r
|
||||
's?__PREFIX__?${CMAKE_INSTALL_PREFIX}?g\; s?__SYSCONFDIR__?${CMAKE_INSTALL_FULL_SYSCONFDIR}?g'
|
||||
${PROJECT_SOURCE_DIR}/${source}.in > ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
|
||||
DEPENDS ${PROJECT_SOURCE_DIR}/${source}.in
|
||||
COMMENT "Generating config file ${source}"
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/${destination}
|
||||
COMPONENT configuration
|
||||
)
|
||||
|
||||
add_custom_target(config-${name} DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name})
|
||||
add_dependencies(configs config-${name})
|
||||
endfunction()
|
||||
|
||||
add_config(config config sway)
|
||||
add_config(security config.d/security sway/config.d)
|
||||
|
||||
add_manpage(sway 1)
|
||||
add_manpage(sway 5)
|
||||
add_manpage(sway-input 5)
|
||||
add_manpage(sway-bar 5)
|
||||
add_manpage(sway-security 7)
|
||||
|
125
sway/commands.c
125
sway/commands.c
@ -26,6 +26,7 @@
|
||||
#include "sway/input_state.h"
|
||||
#include "sway/criteria.h"
|
||||
#include "sway/ipc-server.h"
|
||||
#include "sway/security.h"
|
||||
#include "sway/input.h"
|
||||
#include "sway/border.h"
|
||||
#include "stringop.h"
|
||||
@ -158,6 +159,7 @@ static struct cmd_handler handlers[] = {
|
||||
{ "client.placeholder", cmd_client_placeholder },
|
||||
{ "client.unfocused", cmd_client_unfocused },
|
||||
{ "client.urgent", cmd_client_urgent },
|
||||
{ "commands", cmd_commands },
|
||||
{ "debuglog", cmd_debuglog },
|
||||
{ "default_orientation", cmd_orientation },
|
||||
{ "exec", cmd_exec },
|
||||
@ -178,6 +180,7 @@ static struct cmd_handler handlers[] = {
|
||||
{ "hide_edge_borders", cmd_hide_edge_borders },
|
||||
{ "include", cmd_include },
|
||||
{ "input", cmd_input },
|
||||
{ "ipc", cmd_ipc },
|
||||
{ "kill", cmd_kill },
|
||||
{ "layout", cmd_layout },
|
||||
{ "log_colors", cmd_log_colors },
|
||||
@ -187,6 +190,8 @@ static struct cmd_handler handlers[] = {
|
||||
{ "new_float", cmd_new_float },
|
||||
{ "new_window", cmd_new_window },
|
||||
{ "output", cmd_output },
|
||||
{ "permit", cmd_permit },
|
||||
{ "reject", cmd_reject },
|
||||
{ "reload", cmd_reload },
|
||||
{ "resize", cmd_resize },
|
||||
{ "scratchpad", cmd_scratchpad },
|
||||
@ -288,6 +293,26 @@ static struct cmd_handler bar_colors_handlers[] = {
|
||||
{ "urgent_workspace", bar_colors_cmd_urgent_workspace },
|
||||
};
|
||||
|
||||
static struct cmd_handler ipc_handlers[] = {
|
||||
{ "bar-config", cmd_ipc_cmd },
|
||||
{ "command", cmd_ipc_cmd },
|
||||
{ "events", cmd_ipc_events },
|
||||
{ "inputs", cmd_ipc_cmd },
|
||||
{ "marks", cmd_ipc_cmd },
|
||||
{ "outputs", cmd_ipc_cmd },
|
||||
{ "tree", cmd_ipc_cmd },
|
||||
{ "workspaces", cmd_ipc_cmd },
|
||||
};
|
||||
|
||||
static struct cmd_handler ipc_event_handlers[] = {
|
||||
{ "binding", cmd_ipc_event_cmd },
|
||||
{ "input", cmd_ipc_event_cmd },
|
||||
{ "mode", cmd_ipc_event_cmd },
|
||||
{ "output", cmd_ipc_event_cmd },
|
||||
{ "window", cmd_ipc_event_cmd },
|
||||
{ "workspace", cmd_ipc_event_cmd },
|
||||
};
|
||||
|
||||
static int handler_compare(const void *_a, const void *_b) {
|
||||
const struct cmd_handler *a = _a;
|
||||
const struct cmd_handler *b = _b;
|
||||
@ -307,10 +332,17 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
|
||||
sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
|
||||
sizeof(struct cmd_handler), handler_compare);
|
||||
} else if (block == CMD_BLOCK_INPUT) {
|
||||
sway_log(L_DEBUG, "looking at input handlers");
|
||||
res = bsearch(&d, input_handlers,
|
||||
sizeof(input_handlers) / sizeof(struct cmd_handler),
|
||||
sizeof(struct cmd_handler), handler_compare);
|
||||
} else if (block == CMD_BLOCK_IPC) {
|
||||
res = bsearch(&d, ipc_handlers,
|
||||
sizeof(ipc_handlers) / sizeof(struct cmd_handler),
|
||||
sizeof(struct cmd_handler), handler_compare);
|
||||
} else if (block == CMD_BLOCK_IPC_EVENTS) {
|
||||
res = bsearch(&d, ipc_event_handlers,
|
||||
sizeof(ipc_event_handlers) / sizeof(struct cmd_handler),
|
||||
sizeof(struct cmd_handler), handler_compare);
|
||||
} else {
|
||||
res = bsearch(&d, handlers,
|
||||
sizeof(handlers) / sizeof(struct cmd_handler),
|
||||
@ -319,7 +351,7 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
|
||||
return res;
|
||||
}
|
||||
|
||||
struct cmd_results *handle_command(char *_exec) {
|
||||
struct cmd_results *handle_command(char *_exec, enum command_context context) {
|
||||
// Even though this function will process multiple commands we will only
|
||||
// return the last error, if any (for now). (Since we have access to an
|
||||
// error string we could e.g. concatonate all errors there.)
|
||||
@ -393,6 +425,16 @@ struct cmd_results *handle_command(char *_exec) {
|
||||
free_argv(argc, argv);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!(get_command_policy(argv[0]) & context)) {
|
||||
if (results) {
|
||||
free_cmd_results(results);
|
||||
}
|
||||
results = cmd_results_new(CMD_INVALID, cmd,
|
||||
"Permission denied for %s via %s", cmd,
|
||||
command_policy_str(context));
|
||||
free_argv(argc, argv);
|
||||
goto cleanup;
|
||||
}
|
||||
struct cmd_results *res = handler->handle(argc-1, argv+1);
|
||||
if (res->status != CMD_SUCCESS) {
|
||||
free_argv(argc, argv);
|
||||
@ -458,7 +500,84 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
|
||||
} else {
|
||||
results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented");
|
||||
}
|
||||
cleanup:
|
||||
|
||||
cleanup:
|
||||
free_argv(argc, argv);
|
||||
return results;
|
||||
}
|
||||
|
||||
struct cmd_results *config_commands_command(char *exec) {
|
||||
struct cmd_results *results = NULL;
|
||||
int argc;
|
||||
char **argv = split_args(exec, &argc);
|
||||
if (!argc) {
|
||||
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Find handler for the command this is setting a policy for
|
||||
char *cmd = argv[0];
|
||||
|
||||
if (strcmp(cmd, "}") == 0) {
|
||||
results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
struct cmd_handler *handler = find_handler(cmd, CMD_BLOCK_END);
|
||||
if (!handler && strcmp(cmd, "*") != 0) {
|
||||
char *input = cmd ? cmd : "(empty)";
|
||||
results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
enum command_context context = 0;
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
enum command_context context;
|
||||
} context_names[] = {
|
||||
{ "config", CONTEXT_CONFIG },
|
||||
{ "binding", CONTEXT_BINDING },
|
||||
{ "ipc", CONTEXT_IPC },
|
||||
{ "criteria", CONTEXT_CRITERIA },
|
||||
{ "all", CONTEXT_ALL },
|
||||
};
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
size_t j;
|
||||
for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) {
|
||||
if (strcmp(context_names[j].name, argv[i]) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == sizeof(context_names) / sizeof(context_names[0])) {
|
||||
results = cmd_results_new(CMD_INVALID, cmd,
|
||||
"Invalid command context %s", argv[i]);
|
||||
goto cleanup;
|
||||
}
|
||||
context |= context_names[j].context;
|
||||
}
|
||||
|
||||
struct command_policy *policy = NULL;
|
||||
for (int i = 0; i < config->command_policies->length; ++i) {
|
||||
struct command_policy *p = config->command_policies->items[i];
|
||||
if (strcmp(p->command, cmd) == 0) {
|
||||
policy = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!policy) {
|
||||
policy = alloc_command_policy(cmd);
|
||||
list_add(config->command_policies, policy);
|
||||
}
|
||||
policy->context = context;
|
||||
|
||||
sway_log(L_INFO, "Set command policy for %s to %d",
|
||||
policy->command, policy->context);
|
||||
|
||||
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||
|
||||
cleanup:
|
||||
free_argv(argc, argv);
|
||||
return results;
|
||||
}
|
||||
|
23
sway/commands/commands.c
Normal file
23
sway/commands/commands.c
Normal file
@ -0,0 +1,23 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "sway/commands.h"
|
||||
#include "sway/config.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
||||
struct cmd_results *cmd_commands(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (strcmp(argv[0], "{") != 0) {
|
||||
return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration");
|
||||
}
|
||||
|
||||
if (!config->reading) {
|
||||
return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file.");
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL);
|
||||
}
|
140
sway/commands/ipc.c
Normal file
140
sway/commands/ipc.c
Normal file
@ -0,0 +1,140 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sway/commands.h"
|
||||
#include "sway/config.h"
|
||||
#include "ipc.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
struct cmd_results *cmd_ipc(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (config->reading && strcmp("{", argv[0]) != 0) {
|
||||
return cmd_results_new(CMD_INVALID, "ipc",
|
||||
"Expected '{' at start of IPC config definition.");
|
||||
}
|
||||
|
||||
if (!config->reading) {
|
||||
return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file.");
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL);
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_ipc_events(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "events", EXPECTED_EQUAL_TO, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (config->reading && strcmp("{", argv[0]) != 0) {
|
||||
return cmd_results_new(CMD_INVALID, "events",
|
||||
"Expected '{' at start of IPC event config definition.");
|
||||
}
|
||||
|
||||
if (!config->reading) {
|
||||
return cmd_results_new(CMD_FAILURE, "events", "Can only be used in config file.");
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_BLOCK_IPC_EVENTS, NULL, NULL);
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_ipc_cmd(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
bool enabled;
|
||||
if (strcmp(argv[0], "enabled") == 0) {
|
||||
enabled = true;
|
||||
} else if (strcmp(argv[0], "disabled") == 0) {
|
||||
enabled = false;
|
||||
} else {
|
||||
return cmd_results_new(CMD_INVALID, argv[-1],
|
||||
"Argument must be one of 'enabled' or 'disabled'");
|
||||
}
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
enum ipc_feature type;
|
||||
} types[] = {
|
||||
{ "command", IPC_FEATURE_COMMAND },
|
||||
{ "workspaces", IPC_FEATURE_GET_WORKSPACES },
|
||||
{ "outputs", IPC_FEATURE_GET_OUTPUTS },
|
||||
{ "tree", IPC_FEATURE_GET_TREE },
|
||||
{ "marks", IPC_FEATURE_GET_MARKS },
|
||||
{ "bar-config", IPC_FEATURE_GET_BAR_CONFIG },
|
||||
{ "inputs", IPC_FEATURE_GET_INPUTS },
|
||||
};
|
||||
|
||||
uint32_t type = 0;
|
||||
|
||||
for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
|
||||
if (strcmp(types[i].name, argv[-1]) == 0) {
|
||||
type = types[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
config->ipc_policy |= type;
|
||||
sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]);
|
||||
} else {
|
||||
config->ipc_policy &= ~type;
|
||||
sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]);
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
bool enabled;
|
||||
if (strcmp(argv[0], "enabled") == 0) {
|
||||
enabled = true;
|
||||
} else if (strcmp(argv[0], "disabled") == 0) {
|
||||
enabled = false;
|
||||
} else {
|
||||
return cmd_results_new(CMD_INVALID, argv[-1],
|
||||
"Argument must be one of 'enabled' or 'disabled'");
|
||||
}
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
enum ipc_feature type;
|
||||
} types[] = {
|
||||
{ "workspace", IPC_FEATURE_EVENT_WORKSPACE },
|
||||
{ "output", IPC_FEATURE_EVENT_OUTPUT },
|
||||
{ "mode", IPC_FEATURE_EVENT_MODE },
|
||||
{ "window", IPC_FEATURE_EVENT_WINDOW },
|
||||
{ "binding", IPC_FEATURE_EVENT_BINDING },
|
||||
{ "input", IPC_FEATURE_EVENT_INPUT },
|
||||
};
|
||||
|
||||
uint32_t type = 0;
|
||||
|
||||
for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
|
||||
if (strcmp(types[i].name, argv[-1]) == 0) {
|
||||
type = types[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
config->ipc_policy |= type;
|
||||
sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]);
|
||||
} else {
|
||||
config->ipc_policy &= ~type;
|
||||
sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]);
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||
}
|
94
sway/commands/permit.c
Normal file
94
sway/commands/permit.c
Normal file
@ -0,0 +1,94 @@
|
||||
#include <string.h>
|
||||
#include "sway/commands.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/security.h"
|
||||
#include "log.h"
|
||||
|
||||
static enum secure_feature get_features(int argc, char **argv,
|
||||
struct cmd_results **error) {
|
||||
enum secure_feature features = 0;
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
enum secure_feature feature;
|
||||
} feature_names[] = {
|
||||
{ "lock", FEATURE_LOCK },
|
||||
{ "panel", FEATURE_PANEL },
|
||||
{ "background", FEATURE_BACKGROUND },
|
||||
{ "screenshot", FEATURE_SCREENSHOT },
|
||||
{ "fullscreen", FEATURE_FULLSCREEN },
|
||||
{ "keyboard", FEATURE_KEYBOARD },
|
||||
{ "mouse", FEATURE_MOUSE },
|
||||
{ "ipc", FEATURE_IPC },
|
||||
};
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
size_t j;
|
||||
for (j = 0; j < sizeof(feature_names) / sizeof(feature_names[0]); ++j) {
|
||||
if (strcmp(feature_names[j].name, argv[i]) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == sizeof(feature_names) / sizeof(feature_names[0])) {
|
||||
*error = cmd_results_new(CMD_INVALID,
|
||||
"permit", "Invalid feature grant %s", argv[i]);
|
||||
return 0;
|
||||
}
|
||||
features |= feature_names[j].feature;
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
||||
static struct feature_policy *get_policy(const char *name) {
|
||||
struct feature_policy *policy = NULL;
|
||||
for (int i = 0; i < config->feature_policies->length; ++i) {
|
||||
struct feature_policy *p = config->feature_policies->items[i];
|
||||
if (strcmp(p->program, name) == 0) {
|
||||
policy = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!policy) {
|
||||
policy = alloc_feature_policy(name);
|
||||
list_add(config->feature_policies, policy);
|
||||
}
|
||||
return policy;
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_permit(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
struct feature_policy *policy = get_policy(argv[0]);
|
||||
policy->features |= get_features(argc, argv, &error);
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
sway_log(L_DEBUG, "Permissions granted to %s for features %d",
|
||||
policy->program, policy->features);
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_reject(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
struct feature_policy *policy = get_policy(argv[0]);
|
||||
policy->features &= ~get_features(argc, argv, &error);
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
sway_log(L_DEBUG, "Permissions granted to %s for features %d",
|
||||
policy->program, policy->features);
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||
}
|
@ -167,6 +167,16 @@ void free_pid_workspace(struct pid_workspace *pw) {
|
||||
free(pw);
|
||||
}
|
||||
|
||||
void free_command_policy(struct command_policy *policy) {
|
||||
free(policy->command);
|
||||
free(policy);
|
||||
}
|
||||
|
||||
void free_feature_policy(struct feature_policy *policy) {
|
||||
free(policy->program);
|
||||
free(policy);
|
||||
}
|
||||
|
||||
void free_config(struct sway_config *config) {
|
||||
int i;
|
||||
for (i = 0; i < config->symbols->length; ++i) {
|
||||
@ -211,6 +221,16 @@ void free_config(struct sway_config *config) {
|
||||
}
|
||||
list_free(config->output_configs);
|
||||
|
||||
for (i = 0; i < config->command_policies->length; ++i) {
|
||||
free_command_policy(config->command_policies->items[i]);
|
||||
}
|
||||
list_free(config->command_policies);
|
||||
|
||||
for (i = 0; i < config->feature_policies->length; ++i) {
|
||||
free_feature_policy(config->feature_policies->items[i]);
|
||||
}
|
||||
list_free(config->feature_policies);
|
||||
|
||||
list_free(config->active_bar_modifiers);
|
||||
free_flat_list(config->config_chain);
|
||||
free(config->font);
|
||||
@ -321,6 +341,11 @@ static void config_defaults(struct sway_config *config) {
|
||||
config->border_colors.placeholder.child_border = 0x0C0C0CFF;
|
||||
|
||||
config->border_colors.background = 0xFFFFFFFF;
|
||||
|
||||
// Security
|
||||
config->command_policies = create_list();
|
||||
config->feature_policies = create_list();
|
||||
config->ipc_policy = UINT32_MAX;
|
||||
}
|
||||
|
||||
static int compare_modifiers(const void *left, const void *right) {
|
||||
@ -556,7 +581,13 @@ bool read_config(FILE *file, struct sway_config *config) {
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
struct cmd_results *res = config_command(line, block);
|
||||
struct cmd_results *res;
|
||||
if (block == CMD_BLOCK_COMMANDS) {
|
||||
// Special case
|
||||
res = config_commands_command(line);
|
||||
} else {
|
||||
res = config_command(line, block);
|
||||
}
|
||||
switch(res->status) {
|
||||
case CMD_FAILURE:
|
||||
case CMD_INVALID:
|
||||
@ -602,6 +633,30 @@ bool read_config(FILE *file, struct sway_config *config) {
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_BLOCK_COMMANDS:
|
||||
if (block == CMD_BLOCK_END) {
|
||||
block = CMD_BLOCK_COMMANDS;
|
||||
} else {
|
||||
sway_log(L_ERROR, "Invalid block '%s'", line);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_BLOCK_IPC:
|
||||
if (block == CMD_BLOCK_END) {
|
||||
block = CMD_BLOCK_IPC;
|
||||
} else {
|
||||
sway_log(L_ERROR, "Invalid block '%s'", line);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_BLOCK_IPC_EVENTS:
|
||||
if (block == CMD_BLOCK_IPC) {
|
||||
block = CMD_BLOCK_IPC_EVENTS;
|
||||
} else {
|
||||
sway_log(L_ERROR, "Invalid block '%s'", line);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_BLOCK_END:
|
||||
switch(block) {
|
||||
case CMD_BLOCK_MODE:
|
||||
@ -627,6 +682,21 @@ bool read_config(FILE *file, struct sway_config *config) {
|
||||
block = CMD_BLOCK_BAR;
|
||||
break;
|
||||
|
||||
case CMD_BLOCK_COMMANDS:
|
||||
sway_log(L_DEBUG, "End of commands block");
|
||||
block = CMD_BLOCK_END;
|
||||
break;
|
||||
|
||||
case CMD_BLOCK_IPC:
|
||||
sway_log(L_DEBUG, "End of IPC block");
|
||||
block = CMD_BLOCK_END;
|
||||
break;
|
||||
|
||||
case CMD_BLOCK_IPC_EVENTS:
|
||||
sway_log(L_DEBUG, "End of IPC events block");
|
||||
block = CMD_BLOCK_IPC;
|
||||
break;
|
||||
|
||||
case CMD_BLOCK_END:
|
||||
sway_log(L_ERROR, "Unmatched }");
|
||||
break;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "sway/layout.h"
|
||||
#include "sway/input_state.h"
|
||||
#include "sway/extensions.h"
|
||||
#include "sway/security.h"
|
||||
#include "sway/ipc-server.h"
|
||||
#include "log.h"
|
||||
|
||||
@ -68,6 +69,12 @@ void lock_surface_destructor(struct wl_resource *resource) {
|
||||
|
||||
static void set_background(struct wl_client *client, struct wl_resource *resource,
|
||||
struct wl_resource *_output, struct wl_resource *surface) {
|
||||
pid_t pid;
|
||||
wl_client_get_credentials(client, &pid, NULL, NULL);
|
||||
if (!(get_feature_policy(pid) & FEATURE_BACKGROUND)) {
|
||||
sway_log(L_INFO, "Denying background feature to %d", pid);
|
||||
return;
|
||||
}
|
||||
wlc_handle output = wlc_handle_from_wl_output_resource(_output);
|
||||
if (!output) {
|
||||
return;
|
||||
@ -86,6 +93,12 @@ static void set_background(struct wl_client *client, struct wl_resource *resourc
|
||||
|
||||
static void set_panel(struct wl_client *client, struct wl_resource *resource,
|
||||
struct wl_resource *_output, struct wl_resource *surface) {
|
||||
pid_t pid;
|
||||
wl_client_get_credentials(client, &pid, NULL, NULL);
|
||||
if (!(get_feature_policy(pid) & FEATURE_PANEL)) {
|
||||
sway_log(L_INFO, "Denying panel feature to %d", pid);
|
||||
return;
|
||||
}
|
||||
wlc_handle output = wlc_handle_from_wl_output_resource(_output);
|
||||
if (!output) {
|
||||
return;
|
||||
@ -111,6 +124,12 @@ static void desktop_unlock(struct wl_client *client, struct wl_resource *resourc
|
||||
|
||||
static void set_lock_surface(struct wl_client *client, struct wl_resource *resource,
|
||||
struct wl_resource *_output, struct wl_resource *surface) {
|
||||
pid_t pid;
|
||||
wl_client_get_credentials(client, &pid, NULL, NULL);
|
||||
if (!(get_feature_policy(pid) & FEATURE_LOCK)) {
|
||||
sway_log(L_INFO, "Denying lock feature to %d", pid);
|
||||
return;
|
||||
}
|
||||
swayc_t *output = swayc_by_handle(wlc_handle_from_wl_output_resource(_output));
|
||||
swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface));
|
||||
sway_log(L_DEBUG, "Setting lock surface to %p", view);
|
||||
@ -155,6 +174,12 @@ static void desktop_ready(struct wl_client *client, struct wl_resource *resource
|
||||
}
|
||||
|
||||
static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) {
|
||||
pid_t pid;
|
||||
wl_client_get_credentials(client, &pid, NULL, NULL);
|
||||
if (!(get_feature_policy(pid) & FEATURE_PANEL)) {
|
||||
sway_log(L_INFO, "Denying panel feature to %d", pid);
|
||||
return;
|
||||
}
|
||||
struct panel_config *config = find_or_create_panel_config(resource);
|
||||
sway_log(L_DEBUG, "Panel position for wl_resource %p changed %d => %d", resource, config->panel_position, position);
|
||||
config->panel_position = position;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "sway/criteria.h"
|
||||
#include "sway/ipc-server.h"
|
||||
#include "sway/input.h"
|
||||
#include "sway/security.h"
|
||||
#include "list.h"
|
||||
#include "stringop.h"
|
||||
#include "log.h"
|
||||
@ -385,7 +386,7 @@ static bool handle_view_created(wlc_handle handle) {
|
||||
struct criteria *crit = criteria->items[i];
|
||||
sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'",
|
||||
crit->crit_raw, newview, crit->cmdlist);
|
||||
struct cmd_results *res = handle_command(crit->cmdlist);
|
||||
struct cmd_results *res = handle_command(crit->cmdlist, CONTEXT_CRITERIA);
|
||||
if (res->status != CMD_SUCCESS) {
|
||||
sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
|
||||
}
|
||||
@ -516,8 +517,13 @@ static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geo
|
||||
|
||||
static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) {
|
||||
swayc_t *c = swayc_by_handle(view);
|
||||
pid_t pid = wlc_view_get_pid(view);
|
||||
switch (state) {
|
||||
case WLC_BIT_FULLSCREEN:
|
||||
if (!(get_feature_policy(pid) & FEATURE_FULLSCREEN)) {
|
||||
sway_log(L_INFO, "Denying fullscreen to %d (%s)", pid, c->name);
|
||||
break;
|
||||
}
|
||||
// i3 just lets it become fullscreen
|
||||
wlc_view_set_state(view, state, toggle);
|
||||
if (c) {
|
||||
@ -579,7 +585,7 @@ static void handle_binding_command(struct sway_binding *binding) {
|
||||
reload = true;
|
||||
}
|
||||
|
||||
struct cmd_results *res = handle_command(binding->command);
|
||||
struct cmd_results *res = handle_command(binding->command, CONTEXT_BINDING);
|
||||
if (res->status != CMD_SUCCESS) {
|
||||
sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
|
||||
}
|
||||
@ -719,6 +725,14 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier
|
||||
}
|
||||
|
||||
list_free(candidates);
|
||||
|
||||
swayc_t *focused = get_focused_container(&root_container);
|
||||
if (focused->type == C_VIEW) {
|
||||
pid_t pid = wlc_view_get_pid(focused->handle);
|
||||
if (!(get_feature_policy(pid) & FEATURE_KEYBOARD)) {
|
||||
return EVENT_HANDLED;
|
||||
}
|
||||
}
|
||||
return EVENT_PASSTHROUGH;
|
||||
}
|
||||
|
||||
@ -775,6 +789,15 @@ static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct
|
||||
}
|
||||
|
||||
pointer_position_set(&new_origin, false);
|
||||
|
||||
swayc_t *focused = get_focused_container(&root_container);
|
||||
if (focused->type == C_VIEW) {
|
||||
pid_t pid = wlc_view_get_pid(focused->handle);
|
||||
if (!(get_feature_policy(pid) & FEATURE_MOUSE)) {
|
||||
return EVENT_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
return EVENT_PASSTHROUGH;
|
||||
}
|
||||
|
||||
@ -842,6 +865,12 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w
|
||||
|
||||
// don't change focus or mode if fullscreen
|
||||
if (swayc_is_fullscreen(focused)) {
|
||||
if (focused->type == C_VIEW) {
|
||||
pid_t pid = wlc_view_get_pid(focused->handle);
|
||||
if (!(get_feature_policy(pid) & FEATURE_MOUSE)) {
|
||||
return EVENT_HANDLED;
|
||||
}
|
||||
}
|
||||
return EVENT_PASSTHROUGH;
|
||||
}
|
||||
|
||||
@ -884,6 +913,13 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w
|
||||
return EVENT_HANDLED;
|
||||
}
|
||||
|
||||
if (focused->type == C_VIEW) {
|
||||
pid_t pid = wlc_view_get_pid(focused->handle);
|
||||
if (!(get_feature_policy(pid) & FEATURE_MOUSE)) {
|
||||
return EVENT_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
// Always send mouse release
|
||||
if (state == WLC_BUTTON_STATE_RELEASED) {
|
||||
return EVENT_PASSTHROUGH;
|
||||
@ -900,18 +936,18 @@ bool handle_pointer_scroll(wlc_handle view, uint32_t time, const struct wlc_modi
|
||||
int y_amount = (int)_amount[1];
|
||||
|
||||
if (x_amount > 0 && strcmp(config->floating_scroll_up_cmd, "")) {
|
||||
handle_command(config->floating_scroll_up_cmd);
|
||||
handle_command(config->floating_scroll_up_cmd, CONTEXT_BINDING);
|
||||
return EVENT_HANDLED;
|
||||
} else if (x_amount < 0 && strcmp(config->floating_scroll_down_cmd, "")) {
|
||||
handle_command(config->floating_scroll_down_cmd);
|
||||
handle_command(config->floating_scroll_down_cmd, CONTEXT_BINDING);
|
||||
return EVENT_HANDLED;
|
||||
}
|
||||
|
||||
if (y_amount > 0 && strcmp(config->floating_scroll_right_cmd, "")) {
|
||||
handle_command(config->floating_scroll_right_cmd);
|
||||
handle_command(config->floating_scroll_right_cmd, CONTEXT_BINDING);
|
||||
return EVENT_HANDLED;
|
||||
} else if (y_amount < 0 && strcmp(config->floating_scroll_left_cmd, "")) {
|
||||
handle_command(config->floating_scroll_left_cmd);
|
||||
handle_command(config->floating_scroll_left_cmd, CONTEXT_BINDING);
|
||||
return EVENT_HANDLED;
|
||||
}
|
||||
}
|
||||
@ -924,7 +960,7 @@ static void handle_wlc_ready(void) {
|
||||
config->active = true;
|
||||
while (config->cmd_queue->length) {
|
||||
char *line = config->cmd_queue->items[0];
|
||||
struct cmd_results *res = handle_command(line);
|
||||
struct cmd_results *res = handle_command(line, CONTEXT_CONFIG);
|
||||
if (res->status != CMD_SUCCESS) {
|
||||
sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <libinput.h>
|
||||
#include "sway/ipc-json.h"
|
||||
#include "sway/ipc-server.h"
|
||||
#include "sway/security.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/commands.h"
|
||||
#include "sway/input.h"
|
||||
@ -55,8 +56,6 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
|
||||
void ipc_get_workspaces_callback(swayc_t *workspace, void *data);
|
||||
void ipc_get_outputs_callback(swayc_t *container, void *data);
|
||||
|
||||
#define event_mask(ev) (1 << (ev & 0x7F))
|
||||
|
||||
void ipc_init(void) {
|
||||
ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||
if (ipc_socket == -1) {
|
||||
@ -126,6 +125,17 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
|
||||
return ipc_sockaddr;
|
||||
}
|
||||
|
||||
static pid_t get_client_pid(int client_fd) {
|
||||
struct ucred ucred;
|
||||
socklen_t len = sizeof(struct ucred);
|
||||
|
||||
if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ucred.pid;
|
||||
}
|
||||
|
||||
int ipc_handle_connection(int fd, uint32_t mask, void *data) {
|
||||
(void) fd; (void) data;
|
||||
sway_log(L_DEBUG, "Event on IPC listening socket");
|
||||
@ -144,6 +154,15 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid_t pid = get_client_pid(client_fd);
|
||||
if (!(get_feature_policy(pid) & FEATURE_IPC)) {
|
||||
sway_log(L_INFO, "Permission to connect to IPC socket denied to %d", pid);
|
||||
const char *error = "{\"success\": false, \"message\": \"Permission denied\"}";
|
||||
write(client_fd, &error, sizeof(error));
|
||||
close(client_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipc_client* client = malloc(sizeof(struct ipc_client));
|
||||
client->payload_length = 0;
|
||||
client->fd = client_fd;
|
||||
@ -309,10 +328,15 @@ void ipc_client_handle_command(struct ipc_client *client) {
|
||||
}
|
||||
buf[client->payload_length] = '\0';
|
||||
|
||||
const char *error_denied = "{ \"success\": false, \"error\": \"Permission denied\" }";
|
||||
|
||||
switch (client->current_command) {
|
||||
case IPC_COMMAND:
|
||||
{
|
||||
struct cmd_results *results = handle_command(buf);
|
||||
if (!(config->ipc_policy & IPC_FEATURE_COMMAND)) {
|
||||
goto exit_denied;
|
||||
}
|
||||
struct cmd_results *results = handle_command(buf, CONTEXT_IPC);
|
||||
const char *json = cmd_results_to_json(results);
|
||||
char reply[256];
|
||||
int length = snprintf(reply, sizeof(reply), "%s", json);
|
||||
@ -343,10 +367,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
|
||||
client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
|
||||
} else if (strcmp(event_type, "modifier") == 0) {
|
||||
client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
|
||||
#if SWAY_BINDING_EVENT
|
||||
} else if (strcmp(event_type, "binding") == 0) {
|
||||
client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
|
||||
#endif
|
||||
} else {
|
||||
ipc_send_reply(client, "{\"success\": false}", 18);
|
||||
json_object_put(request);
|
||||
@ -363,6 +385,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
|
||||
|
||||
case IPC_GET_WORKSPACES:
|
||||
{
|
||||
if (!(config->ipc_policy & IPC_FEATURE_GET_WORKSPACES)) {
|
||||
goto exit_denied;
|
||||
}
|
||||
json_object *workspaces = json_object_new_array();
|
||||
container_map(&root_container, ipc_get_workspaces_callback, workspaces);
|
||||
const char *json_string = json_object_to_json_string(workspaces);
|
||||
@ -373,6 +398,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
|
||||
|
||||
case IPC_GET_INPUTS:
|
||||
{
|
||||
if (!(config->ipc_policy & IPC_FEATURE_GET_INPUTS)) {
|
||||
goto exit_denied;
|
||||
}
|
||||
json_object *inputs = json_object_new_array();
|
||||
if (input_devices) {
|
||||
for(int i=0; i<input_devices->length; i++) {
|
||||
@ -392,6 +420,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
|
||||
|
||||
case IPC_GET_OUTPUTS:
|
||||
{
|
||||
if (!(config->ipc_policy & IPC_FEATURE_GET_OUTPUTS)) {
|
||||
goto exit_denied;
|
||||
}
|
||||
json_object *outputs = json_object_new_array();
|
||||
container_map(&root_container, ipc_get_outputs_callback, outputs);
|
||||
const char *json_string = json_object_to_json_string(outputs);
|
||||
@ -402,6 +433,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
|
||||
|
||||
case IPC_GET_TREE:
|
||||
{
|
||||
if (!(config->ipc_policy & IPC_FEATURE_GET_TREE)) {
|
||||
goto exit_denied;
|
||||
}
|
||||
json_object *tree = ipc_json_describe_container_recursive(&root_container);
|
||||
const char *json_string = json_object_to_json_string(tree);
|
||||
ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
|
||||
@ -462,6 +496,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
|
||||
|
||||
case IPC_GET_BAR_CONFIG:
|
||||
{
|
||||
if (!(config->ipc_policy & IPC_FEATURE_GET_BAR_CONFIG)) {
|
||||
goto exit_denied;
|
||||
}
|
||||
if (!buf[0]) {
|
||||
// Send list of configured bar IDs
|
||||
json_object *bars = json_object_new_array();
|
||||
@ -502,6 +539,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
exit_denied:
|
||||
ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
|
||||
|
||||
exit_cleanup:
|
||||
client->payload_length = 0;
|
||||
free(buf);
|
||||
@ -566,6 +606,9 @@ void ipc_send_event(const char *json_string, enum ipc_command_type event) {
|
||||
}
|
||||
|
||||
void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) {
|
||||
if (!(config->ipc_policy & IPC_FEATURE_EVENT_WORKSPACE)) {
|
||||
return;
|
||||
}
|
||||
sway_log(L_DEBUG, "Sending workspace::%s event", change);
|
||||
json_object *obj = json_object_new_object();
|
||||
json_object_object_add(obj, "change", json_object_new_string(change));
|
||||
@ -590,6 +633,9 @@ void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) {
|
||||
}
|
||||
|
||||
void ipc_event_window(swayc_t *window, const char *change) {
|
||||
if (!(config->ipc_policy & IPC_FEATURE_EVENT_WINDOW)) {
|
||||
return;
|
||||
}
|
||||
sway_log(L_DEBUG, "Sending window::%s event", change);
|
||||
json_object *obj = json_object_new_object();
|
||||
json_object_object_add(obj, "change", json_object_new_string(change));
|
||||
@ -615,6 +661,9 @@ void ipc_event_barconfig_update(struct bar_config *bar) {
|
||||
}
|
||||
|
||||
void ipc_event_mode(const char *mode) {
|
||||
if (!(config->ipc_policy & IPC_FEATURE_EVENT_MODE)) {
|
||||
return;
|
||||
}
|
||||
sway_log(L_DEBUG, "Sending mode::%s event", mode);
|
||||
json_object *obj = json_object_new_object();
|
||||
json_object_object_add(obj, "change", json_object_new_string(mode));
|
||||
@ -639,8 +688,10 @@ void ipc_event_modifier(uint32_t modifier, const char *state) {
|
||||
json_object_put(obj); // free
|
||||
}
|
||||
|
||||
#if SWAY_BINDING_EVENT
|
||||
static void ipc_event_binding(json_object *sb_obj) {
|
||||
if (!(config->ipc_policy & IPC_FEATURE_EVENT_BINDING)) {
|
||||
return;
|
||||
}
|
||||
sway_log(L_DEBUG, "Sending binding::run event");
|
||||
json_object *obj = json_object_new_object();
|
||||
json_object_object_add(obj, "change", json_object_new_string("run"));
|
||||
@ -651,10 +702,8 @@ static void ipc_event_binding(json_object *sb_obj) {
|
||||
|
||||
json_object_put(obj); // free
|
||||
}
|
||||
#endif
|
||||
|
||||
void ipc_event_binding_keyboard(struct sway_binding *sb) {
|
||||
#if SWAY_BINDING_EVENT
|
||||
json_object *sb_obj = json_object_new_object();
|
||||
json_object_object_add(sb_obj, "command", json_object_new_string(sb->command));
|
||||
|
||||
@ -705,5 +754,4 @@ void ipc_event_binding_keyboard(struct sway_binding *sb) {
|
||||
json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard"));
|
||||
|
||||
ipc_event_binding(sb_obj);
|
||||
#endif
|
||||
}
|
||||
|
66
sway/main.c
66
sway/main.c
@ -4,13 +4,16 @@
|
||||
#include <wlc/wlc.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/capability.h>
|
||||
#include "sway/extensions.h"
|
||||
#include "sway/layout.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/security.h"
|
||||
#include "sway/handlers.h"
|
||||
#include "sway/input.h"
|
||||
#include "sway/ipc-server.h"
|
||||
@ -142,6 +145,63 @@ static void log_kernel() {
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void security_sanity_check() {
|
||||
// TODO: Notify users visually if this has issues
|
||||
struct stat s;
|
||||
if (stat("/proc", &s)) {
|
||||
sway_log(L_ERROR,
|
||||
"!! DANGER !! /proc is not available - sway CANNOT enforce security rules!");
|
||||
}
|
||||
cap_flag_value_t v;
|
||||
cap_t cap = cap_get_proc();
|
||||
if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) {
|
||||
sway_log(L_ERROR,
|
||||
"!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users.");
|
||||
}
|
||||
if (cap) {
|
||||
cap_free(cap);
|
||||
}
|
||||
if (!stat(SYSCONFDIR "/sway", &s)) {
|
||||
if (s.st_uid != 0 || s.st_gid != 0
|
||||
|| (s.st_mode & S_IWGRP) || (s.st_mode & S_IWOTH)) {
|
||||
sway_log(L_ERROR,
|
||||
"!! DANGER !! " SYSCONFDIR "/sway is not secure! It should be owned by root and set to 0755 at the minimum");
|
||||
}
|
||||
}
|
||||
struct {
|
||||
char *command;
|
||||
enum command_context context;
|
||||
bool checked;
|
||||
} expected[] = {
|
||||
{ "reload", CONTEXT_BINDING, false },
|
||||
{ "restart", CONTEXT_BINDING, false },
|
||||
{ "permit", CONTEXT_CONFIG, false },
|
||||
{ "reject", CONTEXT_CONFIG, false },
|
||||
{ "ipc", CONTEXT_CONFIG, false },
|
||||
};
|
||||
int expected_len = 5;
|
||||
for (int i = 0; i < config->command_policies->length; ++i) {
|
||||
struct command_policy *policy = config->command_policies->items[i];
|
||||
for (int j = 0; j < expected_len; ++j) {
|
||||
if (strcmp(expected[j].command, policy->command) == 0) {
|
||||
expected[j].checked = true;
|
||||
if (expected[j].context != policy->context) {
|
||||
sway_log(L_ERROR,
|
||||
"!! DANGER !! Command security policy for %s should be set to %s",
|
||||
expected[j].command, command_policy_str(expected[j].context));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < expected_len; ++j) {
|
||||
if (!expected[j].checked) {
|
||||
sway_log(L_ERROR,
|
||||
"!! DANGER !! Command security policy for %s should be set to %s",
|
||||
expected[j].command, command_policy_str(expected[j].context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
static int verbose = 0, debug = 0, validate = 0;
|
||||
|
||||
@ -170,6 +230,10 @@ int main(int argc, char **argv) {
|
||||
" --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
|
||||
"\n";
|
||||
|
||||
// Security:
|
||||
unsetenv("LD_PRELOAD");
|
||||
setenv("LD_LIBRARY_PATH", _LD_LIBRARY_PATH, 1);
|
||||
|
||||
int c;
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
@ -298,6 +362,8 @@ int main(int argc, char **argv) {
|
||||
free(config_path);
|
||||
}
|
||||
|
||||
security_sanity_check();
|
||||
|
||||
if (!terminate_request) {
|
||||
wlc_run();
|
||||
}
|
||||
|
94
sway/security.c
Normal file
94
sway/security.c
Normal file
@ -0,0 +1,94 @@
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include "sway/config.h"
|
||||
#include "sway/security.h"
|
||||
#include "log.h"
|
||||
|
||||
struct feature_policy *alloc_feature_policy(const char *program) {
|
||||
uint32_t default_policy = 0;
|
||||
for (int i = 0; i < config->feature_policies->length; ++i) {
|
||||
struct feature_policy *policy = config->feature_policies->items[i];
|
||||
if (strcmp(policy->program, "*") == 0) {
|
||||
default_policy = policy->features;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct feature_policy *policy = malloc(sizeof(struct feature_policy));
|
||||
policy->program = strdup(program);
|
||||
policy->features = default_policy;
|
||||
return policy;
|
||||
}
|
||||
|
||||
struct command_policy *alloc_command_policy(const char *command) {
|
||||
struct command_policy *policy = malloc(sizeof(struct command_policy));
|
||||
policy->command = strdup(command);
|
||||
policy->context = 0;
|
||||
return policy;
|
||||
}
|
||||
|
||||
enum secure_feature get_feature_policy(pid_t pid) {
|
||||
const char *fmt = "/proc/%d/exe";
|
||||
int pathlen = snprintf(NULL, 0, fmt, pid);
|
||||
char *path = malloc(pathlen + 1);
|
||||
snprintf(path, pathlen + 1, fmt, pid);
|
||||
static char link[2048];
|
||||
|
||||
uint32_t default_policy = 0;
|
||||
|
||||
ssize_t len = readlink(path, link, sizeof(link));
|
||||
if (len < 0) {
|
||||
sway_log(L_INFO,
|
||||
"WARNING: unable to read %s for security check. Using default policy.",
|
||||
path);
|
||||
strcpy(link, "*");
|
||||
} else {
|
||||
link[len] = '\0';
|
||||
}
|
||||
free(path);
|
||||
|
||||
for (int i = 0; i < config->feature_policies->length; ++i) {
|
||||
struct feature_policy *policy = config->feature_policies->items[i];
|
||||
if (strcmp(policy->program, "*") == 0) {
|
||||
default_policy = policy->features;
|
||||
}
|
||||
if (strcmp(policy->program, link) == 0) {
|
||||
return policy->features;
|
||||
}
|
||||
}
|
||||
|
||||
return default_policy;
|
||||
}
|
||||
|
||||
enum command_context get_command_policy(const char *cmd) {
|
||||
uint32_t default_policy = 0;
|
||||
|
||||
for (int i = 0; i < config->command_policies->length; ++i) {
|
||||
struct command_policy *policy = config->command_policies->items[i];
|
||||
if (strcmp(policy->command, "*") == 0) {
|
||||
default_policy = policy->context;
|
||||
}
|
||||
if (strcmp(policy->command, cmd) == 0) {
|
||||
return policy->context;
|
||||
}
|
||||
}
|
||||
|
||||
return default_policy;
|
||||
}
|
||||
|
||||
const char *command_policy_str(enum command_context context) {
|
||||
switch (context) {
|
||||
case CONTEXT_ALL:
|
||||
return "all";
|
||||
case CONTEXT_CONFIG:
|
||||
return "config";
|
||||
case CONTEXT_BINDING:
|
||||
return "binding";
|
||||
case CONTEXT_IPC:
|
||||
return "IPC";
|
||||
case CONTEXT_CRITERIA:
|
||||
return "criteria";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
250
sway/sway-security.7.txt
Normal file
250
sway/sway-security.7.txt
Normal file
@ -0,0 +1,250 @@
|
||||
/////
|
||||
vim:set ts=4 sw=4 tw=82 noet:
|
||||
/////
|
||||
sway-security (7)
|
||||
=================
|
||||
|
||||
Name
|
||||
----
|
||||
sway-security - Guidelines for securing your sway install
|
||||
|
||||
Security Overview
|
||||
-----------------
|
||||
|
||||
**Sway is NOT secure**. We are working on it but do not trust that we have it all
|
||||
figured out yet. The following man page is provisional.
|
||||
|
||||
Securing sway requires careful configuration of your environment, the sort that's
|
||||
usually best suited to a distro maintainer who wants to ship a secure sway
|
||||
environment in their distro. Sway provides a number of means of securing it but
|
||||
you must make a few changes external to sway first.
|
||||
|
||||
Configuration security
|
||||
----------------------
|
||||
|
||||
Many of Sway's security features are configurable. It's important that a possibly
|
||||
untrusted program is not able to edit this. Security rules are kept in
|
||||
_/etc/sway/config.d/security_ (usually), which should only be writable by root.
|
||||
However, configuration of security rules is not limited to this file - any config
|
||||
file that sway loads (including i.e. _~/.config/sway/config_) should not be editable
|
||||
by the user you intend to run programs as. One simple strategy is to use
|
||||
/etc/sway/config instead of a config file in your home directory, but that doesn't
|
||||
work well for multi-user systems. A more robust strategy is to run untrusted
|
||||
programs as another user, or in a sandbox. Configuring this is up to you.
|
||||
|
||||
Note that _/etc/sway/config.d/*_ must be included explicitly from your config file.
|
||||
This is done by default in /etc/sway/config but you must check your own config if
|
||||
you choose to place it in other locations.
|
||||
|
||||
Environment security
|
||||
--------------------
|
||||
|
||||
LD_PRELOAD is a mechanism designed to ruin the security of your system. There are
|
||||
a number of strategies for dealing with this but they all suck a little. In order
|
||||
of most practical to least practical:
|
||||
|
||||
1. Only run important programs via exec. Sway's exec command will ensure that
|
||||
LD_PRELOAD is unset when running programs.
|
||||
|
||||
2. Remove LD_PRELOAD support from your dynamic loader (requires patching libc).
|
||||
This may break programs that rely on LD_PRELOAD for legitimate functionality,
|
||||
but this is the most effective solution.
|
||||
|
||||
3. Use static linking for important programs. Of course statically linked programs
|
||||
are unaffected by the dynamic linking security dumpster fire.
|
||||
|
||||
Note that should you choose method 1, you MUST ensure that sway itself isn't
|
||||
compromised by LD_PRELOAD. It probably isn't, but you can be sure by setting
|
||||
/usr/bin/sway to a+s (setuid), which will instruct the dynamic linker not to
|
||||
permit LD_PRELOAD for it (and will also run it as root, which sway will shortly
|
||||
drop). You could also statically link sway itself.
|
||||
|
||||
Note that LD_LIBRARY_PATH has all of the same problems, and all of the same
|
||||
solutions.
|
||||
|
||||
Read your log
|
||||
-------------
|
||||
|
||||
Sway does sanity checks and prints big red warnings to stderr if they fail. Read
|
||||
them.
|
||||
|
||||
Feature policies
|
||||
----------------
|
||||
|
||||
Certain sway features are security sensitive and may be configured with security
|
||||
policies. These features are:
|
||||
|
||||
**background**::
|
||||
Permission for a program to become the background.
|
||||
|
||||
**fullscreen**::
|
||||
Permission to become fullscreen. Note that users can always make a window
|
||||
fullscreen themselves with the fullscreen command.
|
||||
|
||||
**ipc**::
|
||||
Permission to connect to sway's IPC socket.
|
||||
|
||||
**keyboard**::
|
||||
Permission to receive keyboard events (only while they are focused).
|
||||
|
||||
**lock**::
|
||||
Permission for a program to act as a screen locker. This involves becoming
|
||||
fullscreen (on all outputs) and receiving _all_ keyboard and mouse input for
|
||||
the duration of the process.
|
||||
|
||||
**mouse**::
|
||||
Permission to receive mouse events (only while the mouse is over them).
|
||||
|
||||
**panel**::
|
||||
Permission for a program to stick its windows to the sides of the screen.
|
||||
|
||||
**screenshot**::
|
||||
Permission to take screenshots or record the screen.
|
||||
|
||||
By default, all programs are granted **fullscreen**, **keyboard**, **mouse**, and
|
||||
**ipc** permissions. You can use the following config commands to control a
|
||||
program's access:
|
||||
|
||||
**permit** <executable> <features...>::
|
||||
Permits <executable> to use <features> (each feature seperated by a space).
|
||||
<executable> may be * to affect the default policy, or the full path to the
|
||||
executable file.
|
||||
|
||||
**reject** <executable> <features...>::
|
||||
Disallows <executable> from using <features> (each feature seperated by a space).
|
||||
<executable> may be * to affect the default policy, or the full path to the
|
||||
executable file.
|
||||
|
||||
Note that policy enforcement requires procfs to be mounted at /proc and the sway
|
||||
process to be able to access _/proc/[pid]/exe_ (see **procfs(5)** for details on
|
||||
this access - setcap cap_sys_ptrace=eip /usr/bin/sway should do the trick). If
|
||||
sway is unable to read _/proc/[pid]/exe_, it will apply the default policy.
|
||||
|
||||
To work correctly, sway's own programs require the following permissions:
|
||||
|
||||
- swaybg: background
|
||||
- swaylock: lock, keyboard
|
||||
- swaybar: panel, mouse, ipc
|
||||
- swaygrab: screenshot, ipc
|
||||
|
||||
When you first declare a policy for an executable, it will inherit the default
|
||||
policy. Further changes to the default policy will not retroactively affect which
|
||||
permissions an earlier policy inherits. You must explicitly reject any features
|
||||
from the default policy that you do not want an executable to receive permission
|
||||
for.
|
||||
|
||||
Command policies
|
||||
----------------
|
||||
|
||||
You can also control the context from which a command may execute. The different
|
||||
contexts you can control are:
|
||||
|
||||
**config**::
|
||||
Can be run from your config file.
|
||||
|
||||
**binding**::
|
||||
Can be run from bindsym or bindcode commands.
|
||||
|
||||
**ipc**::
|
||||
Can be run by IPC clients.
|
||||
|
||||
**criteria**::
|
||||
Can be run when evaluating window criteria.
|
||||
|
||||
**all**::
|
||||
Shorthand for granting permission in all contexts.
|
||||
|
||||
By default a command is allowed to execute in any context. To configure this, open
|
||||
a commands block and fill it with policies:
|
||||
|
||||
commands {
|
||||
<name> <contexts...>
|
||||
...
|
||||
}
|
||||
|
||||
For example, you could do this to limit the use of the focus command to just
|
||||
binding and critiera:
|
||||
|
||||
commands {
|
||||
focus binding criteria
|
||||
}
|
||||
|
||||
Setting a command policy overwrites any previous policy that was in place.
|
||||
|
||||
IPC policies
|
||||
------------
|
||||
|
||||
You may whitelist IPC access like so:
|
||||
|
||||
permit /usr/bin/swaybar ipc
|
||||
permit /usr/bin/swaygrab ipc
|
||||
# etc
|
||||
|
||||
Note that it's suggested you do not enable swaymsg to access IPC if you intend to
|
||||
secure your IPC socket, because any program could just run swaymsg itself instead
|
||||
of connecting to IPC directly.
|
||||
|
||||
You can also configure which features of IPC are available with an IPC block:
|
||||
|
||||
ipc {
|
||||
...
|
||||
}
|
||||
|
||||
The following commands are available within this block:
|
||||
|
||||
**bar-config** <enabled|disabled>::
|
||||
Controls GET_BAR_CONFIG (required for swaybar to work at all).
|
||||
|
||||
**command** <enabled|disabled>::
|
||||
Controls executing sway commands via IPC.
|
||||
|
||||
**inputs** <enabled|disabled>::
|
||||
Controls GET_INPUTS (input device information).
|
||||
|
||||
**marks** <enabled|disabled>::
|
||||
Controls GET_MARKS.
|
||||
|
||||
**outputs** <enabled|disabled>::
|
||||
Controls GET_OUTPUTS.
|
||||
|
||||
**tree** <enabled|disabled>::
|
||||
Controls GET_TREE.
|
||||
|
||||
**workspaces** <enabled|disabled>::
|
||||
Controls GET_WORKSPACES.
|
||||
|
||||
You can also control which IPC events can be raised with an events block:
|
||||
|
||||
ipc {
|
||||
events {
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
The following commands are vaild within an ipc events block:
|
||||
|
||||
**binding** <enabled|disabled>::
|
||||
Controls keybinding notifications (disabled by default).
|
||||
|
||||
**input** <enabled|disabled>::
|
||||
Controls input device hotplugging notifications.
|
||||
|
||||
**mode** <enabled|disabled>::
|
||||
Controls output hotplugging notifications.
|
||||
|
||||
**output** <enabled|disabled>::
|
||||
Controls output hotplugging notifications.
|
||||
|
||||
**window** <enabled|disabled>::
|
||||
Controls window event notifications.
|
||||
|
||||
**workspace** <enabled|disabled>::
|
||||
Controls workspace notifications.
|
||||
|
||||
Disabling some of these may cause swaybar to behave incorrectly.
|
||||
|
||||
Authors
|
||||
-------
|
||||
Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
|
||||
source contributors. For more information about sway development, see
|
||||
<https://github.com/SirCmpwn/sway>.
|
@ -42,7 +42,7 @@ install(
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/pam/swaylock
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/pam.d/
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d/
|
||||
COMPONENT data
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user