mirror of
				https://github.com/swaywm/sway.git
				synced 2025-11-03 09:01:43 -05:00 
			
		
		
		
	
						commit
						5778c59a2f
					
				
					 20 changed files with 1159 additions and 44 deletions
				
			
		| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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 ${PROJECT_SOURCE_DIR}/config
 | 
			
		||||
	DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sway/
 | 
			
		||||
        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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										123
									
								
								sway/commands.c
									
										
									
									
									
								
							
							
						
						
									
										123
									
								
								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,6 +500,83 @@ 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:
 | 
			
		||||
	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…
	
	Add table
		Add a link
		
	
		Reference in a new issue