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-swaygrab "Enables the swaygrab utility" YES)
 | 
				
			||||||
option(enable-swaymsg "Enables the swaymsg utility" YES)
 | 
					option(enable-swaymsg "Enables the swaymsg utility" YES)
 | 
				
			||||||
option(enable-gdk-pixbuf "Use Pixbuf to support more image formats" 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(zsh-completions "Zsh shell completions" NO)
 | 
				
			||||||
option(default-wallpaper "Installs the default wallpaper" YES)
 | 
					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(JsonC REQUIRED)
 | 
				
			||||||
find_package(PCRE REQUIRED)
 | 
					find_package(PCRE REQUIRED)
 | 
				
			||||||
| 
						 | 
					@ -83,9 +85,6 @@ if (enable-gdk-pixbuf)
 | 
				
			||||||
else()
 | 
					else()
 | 
				
			||||||
	message(STATUS "Building without gdk-pixbuf, only png images supported.")
 | 
						message(STATUS "Building without gdk-pixbuf, only png images supported.")
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
if(enable-binding-event)
 | 
					 | 
				
			||||||
	add_definitions(-DSWAY_BINDING_EVENT=1)
 | 
					 | 
				
			||||||
endif()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
include_directories(include)
 | 
					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 __SYSCONFDIR__/sway/config.d/*
 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# include ~/.config/sway/conf.d/*
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Protip:
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# include ~/.config/sway/`hostname`/*
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
#ifndef _SWAY_IPC_H
 | 
					#ifndef _SWAY_IPC_H
 | 
				
			||||||
#define _SWAY_IPC_H
 | 
					#define _SWAY_IPC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define event_mask(ev) (1 << (ev & 0x7F))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum ipc_command_type {
 | 
					enum ipc_command_type {
 | 
				
			||||||
	IPC_COMMAND = 0,
 | 
						IPC_COMMAND = 0,
 | 
				
			||||||
	IPC_GET_WORKSPACES = 1,
 | 
						IPC_GET_WORKSPACES = 1,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,10 @@ enum cmd_status {
 | 
				
			||||||
	CMD_BLOCK_MODE,
 | 
						CMD_BLOCK_MODE,
 | 
				
			||||||
	CMD_BLOCK_BAR,
 | 
						CMD_BLOCK_BAR,
 | 
				
			||||||
	CMD_BLOCK_BAR_COLORS,
 | 
						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.
 | 
					 * 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.
 | 
					 * Parse and handles a command during config file loading.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Do not use this under normal conditions.
 | 
					 * Do not use this under normal conditions.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct cmd_results *config_command(char *command, enum cmd_status block);
 | 
					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.
 | 
					 * Allocates a cmd_results object.
 | 
				
			||||||
| 
						 | 
					@ -93,6 +100,7 @@ sway_cmd cmd_client_unfocused;
 | 
				
			||||||
sway_cmd cmd_client_urgent;
 | 
					sway_cmd cmd_client_urgent;
 | 
				
			||||||
sway_cmd cmd_client_placeholder;
 | 
					sway_cmd cmd_client_placeholder;
 | 
				
			||||||
sway_cmd cmd_client_background;
 | 
					sway_cmd cmd_client_background;
 | 
				
			||||||
 | 
					sway_cmd cmd_commands;
 | 
				
			||||||
sway_cmd cmd_debuglog;
 | 
					sway_cmd cmd_debuglog;
 | 
				
			||||||
sway_cmd cmd_exec;
 | 
					sway_cmd cmd_exec;
 | 
				
			||||||
sway_cmd cmd_exec_always;
 | 
					sway_cmd cmd_exec_always;
 | 
				
			||||||
| 
						 | 
					@ -112,6 +120,7 @@ sway_cmd cmd_gaps;
 | 
				
			||||||
sway_cmd cmd_hide_edge_borders;
 | 
					sway_cmd cmd_hide_edge_borders;
 | 
				
			||||||
sway_cmd cmd_include;
 | 
					sway_cmd cmd_include;
 | 
				
			||||||
sway_cmd cmd_input;
 | 
					sway_cmd cmd_input;
 | 
				
			||||||
 | 
					sway_cmd cmd_ipc;
 | 
				
			||||||
sway_cmd cmd_kill;
 | 
					sway_cmd cmd_kill;
 | 
				
			||||||
sway_cmd cmd_layout;
 | 
					sway_cmd cmd_layout;
 | 
				
			||||||
sway_cmd cmd_log_colors;
 | 
					sway_cmd cmd_log_colors;
 | 
				
			||||||
| 
						 | 
					@ -122,6 +131,8 @@ sway_cmd cmd_new_float;
 | 
				
			||||||
sway_cmd cmd_new_window;
 | 
					sway_cmd cmd_new_window;
 | 
				
			||||||
sway_cmd cmd_orientation;
 | 
					sway_cmd cmd_orientation;
 | 
				
			||||||
sway_cmd cmd_output;
 | 
					sway_cmd cmd_output;
 | 
				
			||||||
 | 
					sway_cmd cmd_permit;
 | 
				
			||||||
 | 
					sway_cmd cmd_reject;
 | 
				
			||||||
sway_cmd cmd_reload;
 | 
					sway_cmd cmd_reload;
 | 
				
			||||||
sway_cmd cmd_resize;
 | 
					sway_cmd cmd_resize;
 | 
				
			||||||
sway_cmd cmd_scratchpad;
 | 
					sway_cmd cmd_scratchpad;
 | 
				
			||||||
| 
						 | 
					@ -182,4 +193,8 @@ sway_cmd input_cmd_pointer_accel;
 | 
				
			||||||
sway_cmd input_cmd_scroll_method;
 | 
					sway_cmd input_cmd_scroll_method;
 | 
				
			||||||
sway_cmd input_cmd_tap;
 | 
					sway_cmd input_cmd_tap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sway_cmd cmd_ipc_cmd;
 | 
				
			||||||
 | 
					sway_cmd cmd_ipc_events;
 | 
				
			||||||
 | 
					sway_cmd cmd_ipc_event_cmd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,9 +103,6 @@ struct pid_workspace {
 | 
				
			||||||
	time_t *time_added;
 | 
						time_t *time_added;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pid_workspace_add(struct pid_workspace *pw);
 | 
					 | 
				
			||||||
void free_pid_workspace(struct pid_workspace *pw);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct bar_config {
 | 
					struct bar_config {
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * One of "dock", "hide", "invisible"
 | 
						 * One of "dock", "hide", "invisible"
 | 
				
			||||||
| 
						 | 
					@ -138,7 +135,7 @@ struct bar_config {
 | 
				
			||||||
	int height; // -1 not defined
 | 
						int height; // -1 not defined
 | 
				
			||||||
	int tray_padding;
 | 
						int tray_padding;
 | 
				
			||||||
	bool workspace_buttons;
 | 
						bool workspace_buttons;
 | 
				
			||||||
    bool wrap_scroll;
 | 
						bool wrap_scroll;
 | 
				
			||||||
	char *separator_symbol;
 | 
						char *separator_symbol;
 | 
				
			||||||
	bool strip_workspace_numbers;
 | 
						bool strip_workspace_numbers;
 | 
				
			||||||
	bool binding_mode_indicator;
 | 
						bool binding_mode_indicator;
 | 
				
			||||||
| 
						 | 
					@ -184,6 +181,52 @@ enum edge_border_types {
 | 
				
			||||||
	E_BOTH		/**< hide vertical and horizontal edge borders */
 | 
						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.
 | 
					 * The configuration struct. The result of loading a config file.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -203,7 +246,7 @@ struct sway_config {
 | 
				
			||||||
	uint32_t floating_mod;
 | 
						uint32_t floating_mod;
 | 
				
			||||||
	uint32_t dragging_key;
 | 
						uint32_t dragging_key;
 | 
				
			||||||
	uint32_t resizing_key;
 | 
						uint32_t resizing_key;
 | 
				
			||||||
    	char *floating_scroll_up_cmd;
 | 
						char *floating_scroll_up_cmd;
 | 
				
			||||||
	char *floating_scroll_down_cmd;
 | 
						char *floating_scroll_down_cmd;
 | 
				
			||||||
	char *floating_scroll_left_cmd;
 | 
						char *floating_scroll_left_cmd;
 | 
				
			||||||
	char *floating_scroll_right_cmd;
 | 
						char *floating_scroll_right_cmd;
 | 
				
			||||||
| 
						 | 
					@ -252,8 +295,16 @@ struct sway_config {
 | 
				
			||||||
	int32_t floating_maximum_height;
 | 
						int32_t floating_maximum_height;
 | 
				
			||||||
	int32_t floating_minimum_width;
 | 
						int32_t floating_minimum_width;
 | 
				
			||||||
	int32_t floating_minimum_height;
 | 
						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
 | 
					 * Loads the main config from the given path. is_active should be true when
 | 
				
			||||||
 * reloading the config.
 | 
					 * 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
 | 
						output.c
 | 
				
			||||||
	workspace.c
 | 
						workspace.c
 | 
				
			||||||
	border.c
 | 
						border.c
 | 
				
			||||||
 | 
					    security.c
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_definitions(
 | 
					add_definitions(
 | 
				
			||||||
| 
						 | 
					@ -54,6 +55,7 @@ target_link_libraries(sway
 | 
				
			||||||
	${PANGO_LIBRARIES}
 | 
						${PANGO_LIBRARIES}
 | 
				
			||||||
	${JSONC_LIBRARIES}
 | 
						${JSONC_LIBRARIES}
 | 
				
			||||||
	m
 | 
						m
 | 
				
			||||||
 | 
					    cap
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
install(
 | 
					install(
 | 
				
			||||||
| 
						 | 
					@ -62,13 +64,34 @@ install(
 | 
				
			||||||
	DESTINATION bin
 | 
						DESTINATION bin
 | 
				
			||||||
	COMPONENT runtime
 | 
						COMPONENT runtime
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
install(
 | 
					
 | 
				
			||||||
	FILES ${PROJECT_SOURCE_DIR}/config
 | 
					add_custom_target(configs ALL)
 | 
				
			||||||
	DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sway/
 | 
					
 | 
				
			||||||
	COMPONENT configuration
 | 
					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 1)
 | 
				
			||||||
add_manpage(sway 5)
 | 
					add_manpage(sway 5)
 | 
				
			||||||
add_manpage(sway-input 5)
 | 
					add_manpage(sway-input 5)
 | 
				
			||||||
add_manpage(sway-bar 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/input_state.h"
 | 
				
			||||||
#include "sway/criteria.h"
 | 
					#include "sway/criteria.h"
 | 
				
			||||||
#include "sway/ipc-server.h"
 | 
					#include "sway/ipc-server.h"
 | 
				
			||||||
 | 
					#include "sway/security.h"
 | 
				
			||||||
#include "sway/input.h"
 | 
					#include "sway/input.h"
 | 
				
			||||||
#include "sway/border.h"
 | 
					#include "sway/border.h"
 | 
				
			||||||
#include "stringop.h"
 | 
					#include "stringop.h"
 | 
				
			||||||
| 
						 | 
					@ -158,6 +159,7 @@ static struct cmd_handler handlers[] = {
 | 
				
			||||||
	{ "client.placeholder", cmd_client_placeholder },
 | 
						{ "client.placeholder", cmd_client_placeholder },
 | 
				
			||||||
	{ "client.unfocused", cmd_client_unfocused },
 | 
						{ "client.unfocused", cmd_client_unfocused },
 | 
				
			||||||
	{ "client.urgent", cmd_client_urgent },
 | 
						{ "client.urgent", cmd_client_urgent },
 | 
				
			||||||
 | 
						{ "commands", cmd_commands },
 | 
				
			||||||
	{ "debuglog", cmd_debuglog },
 | 
						{ "debuglog", cmd_debuglog },
 | 
				
			||||||
	{ "default_orientation", cmd_orientation },
 | 
						{ "default_orientation", cmd_orientation },
 | 
				
			||||||
	{ "exec", cmd_exec },
 | 
						{ "exec", cmd_exec },
 | 
				
			||||||
| 
						 | 
					@ -178,6 +180,7 @@ static struct cmd_handler handlers[] = {
 | 
				
			||||||
	{ "hide_edge_borders", cmd_hide_edge_borders },
 | 
						{ "hide_edge_borders", cmd_hide_edge_borders },
 | 
				
			||||||
	{ "include", cmd_include },
 | 
						{ "include", cmd_include },
 | 
				
			||||||
	{ "input", cmd_input },
 | 
						{ "input", cmd_input },
 | 
				
			||||||
 | 
						{ "ipc", cmd_ipc },
 | 
				
			||||||
	{ "kill", cmd_kill },
 | 
						{ "kill", cmd_kill },
 | 
				
			||||||
	{ "layout", cmd_layout },
 | 
						{ "layout", cmd_layout },
 | 
				
			||||||
	{ "log_colors", cmd_log_colors },
 | 
						{ "log_colors", cmd_log_colors },
 | 
				
			||||||
| 
						 | 
					@ -187,6 +190,8 @@ static struct cmd_handler handlers[] = {
 | 
				
			||||||
	{ "new_float", cmd_new_float },
 | 
						{ "new_float", cmd_new_float },
 | 
				
			||||||
	{ "new_window", cmd_new_window },
 | 
						{ "new_window", cmd_new_window },
 | 
				
			||||||
	{ "output", cmd_output },
 | 
						{ "output", cmd_output },
 | 
				
			||||||
 | 
						{ "permit", cmd_permit },
 | 
				
			||||||
 | 
						{ "reject", cmd_reject },
 | 
				
			||||||
	{ "reload", cmd_reload },
 | 
						{ "reload", cmd_reload },
 | 
				
			||||||
	{ "resize", cmd_resize },
 | 
						{ "resize", cmd_resize },
 | 
				
			||||||
	{ "scratchpad", cmd_scratchpad },
 | 
						{ "scratchpad", cmd_scratchpad },
 | 
				
			||||||
| 
						 | 
					@ -288,6 +293,26 @@ static struct cmd_handler bar_colors_handlers[] = {
 | 
				
			||||||
	{ "urgent_workspace", bar_colors_cmd_urgent_workspace },
 | 
						{ "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) {
 | 
					static int handler_compare(const void *_a, const void *_b) {
 | 
				
			||||||
	const struct cmd_handler *a = _a;
 | 
						const struct cmd_handler *a = _a;
 | 
				
			||||||
	const struct cmd_handler *b = _b;
 | 
						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(bar_colors_handlers) / sizeof(struct cmd_handler),
 | 
				
			||||||
			sizeof(struct cmd_handler), handler_compare);
 | 
								sizeof(struct cmd_handler), handler_compare);
 | 
				
			||||||
	} else if (block == CMD_BLOCK_INPUT) {
 | 
						} else if (block == CMD_BLOCK_INPUT) {
 | 
				
			||||||
		sway_log(L_DEBUG, "looking at input handlers");
 | 
					 | 
				
			||||||
		res = bsearch(&d, input_handlers,
 | 
							res = bsearch(&d, input_handlers,
 | 
				
			||||||
			sizeof(input_handlers) / sizeof(struct cmd_handler),
 | 
								sizeof(input_handlers) / sizeof(struct cmd_handler),
 | 
				
			||||||
			sizeof(struct cmd_handler), handler_compare);
 | 
								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 {
 | 
						} else {
 | 
				
			||||||
		res = bsearch(&d, handlers,
 | 
							res = bsearch(&d, handlers,
 | 
				
			||||||
			sizeof(handlers) / sizeof(struct cmd_handler),
 | 
								sizeof(handlers) / sizeof(struct cmd_handler),
 | 
				
			||||||
| 
						 | 
					@ -319,7 +351,7 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
 | 
				
			||||||
	return res;
 | 
						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
 | 
						// 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
 | 
						// return the last error, if any (for now). (Since we have access to an
 | 
				
			||||||
	// error string we could e.g. concatonate all errors there.)
 | 
						// 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);
 | 
									free_argv(argc, argv);
 | 
				
			||||||
				goto cleanup;
 | 
									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);
 | 
								struct cmd_results *res = handler->handle(argc-1, argv+1);
 | 
				
			||||||
			if (res->status != CMD_SUCCESS) {
 | 
								if (res->status != CMD_SUCCESS) {
 | 
				
			||||||
				free_argv(argc, argv);
 | 
									free_argv(argc, argv);
 | 
				
			||||||
| 
						 | 
					@ -458,7 +500,84 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented");
 | 
							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);
 | 
						free_argv(argc, argv);
 | 
				
			||||||
	return results;
 | 
						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);
 | 
						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) {
 | 
					void free_config(struct sway_config *config) {
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
	for (i = 0; i < config->symbols->length; ++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);
 | 
						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);
 | 
						list_free(config->active_bar_modifiers);
 | 
				
			||||||
	free_flat_list(config->config_chain);
 | 
						free_flat_list(config->config_chain);
 | 
				
			||||||
	free(config->font);
 | 
						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.placeholder.child_border = 0x0C0C0CFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config->border_colors.background = 0xFFFFFFFF;
 | 
						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) {
 | 
					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);
 | 
								free(line);
 | 
				
			||||||
			continue;
 | 
								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) {
 | 
							switch(res->status) {
 | 
				
			||||||
		case CMD_FAILURE:
 | 
							case CMD_FAILURE:
 | 
				
			||||||
		case CMD_INVALID:
 | 
							case CMD_INVALID:
 | 
				
			||||||
| 
						 | 
					@ -602,6 +633,30 @@ bool read_config(FILE *file, struct sway_config *config) {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			break;
 | 
								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:
 | 
							case CMD_BLOCK_END:
 | 
				
			||||||
			switch(block) {
 | 
								switch(block) {
 | 
				
			||||||
			case CMD_BLOCK_MODE:
 | 
								case CMD_BLOCK_MODE:
 | 
				
			||||||
| 
						 | 
					@ -627,6 +682,21 @@ bool read_config(FILE *file, struct sway_config *config) {
 | 
				
			||||||
				block = CMD_BLOCK_BAR;
 | 
									block = CMD_BLOCK_BAR;
 | 
				
			||||||
				break;
 | 
									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:
 | 
								case CMD_BLOCK_END:
 | 
				
			||||||
				sway_log(L_ERROR, "Unmatched }");
 | 
									sway_log(L_ERROR, "Unmatched }");
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@
 | 
				
			||||||
#include "sway/layout.h"
 | 
					#include "sway/layout.h"
 | 
				
			||||||
#include "sway/input_state.h"
 | 
					#include "sway/input_state.h"
 | 
				
			||||||
#include "sway/extensions.h"
 | 
					#include "sway/extensions.h"
 | 
				
			||||||
 | 
					#include "sway/security.h"
 | 
				
			||||||
#include "sway/ipc-server.h"
 | 
					#include "sway/ipc-server.h"
 | 
				
			||||||
#include "log.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,
 | 
					static void set_background(struct wl_client *client, struct wl_resource *resource,
 | 
				
			||||||
		struct wl_resource *_output, struct wl_resource *surface) {
 | 
							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);
 | 
						wlc_handle output = wlc_handle_from_wl_output_resource(_output);
 | 
				
			||||||
	if (!output) {
 | 
						if (!output) {
 | 
				
			||||||
		return;
 | 
							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,
 | 
					static void set_panel(struct wl_client *client, struct wl_resource *resource,
 | 
				
			||||||
		struct wl_resource *_output, struct wl_resource *surface) {
 | 
							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);
 | 
						wlc_handle output = wlc_handle_from_wl_output_resource(_output);
 | 
				
			||||||
	if (!output) {
 | 
						if (!output) {
 | 
				
			||||||
		return;
 | 
							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,
 | 
					static void set_lock_surface(struct wl_client *client, struct wl_resource *resource,
 | 
				
			||||||
		struct wl_resource *_output, struct wl_resource *surface) {
 | 
							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 *output = swayc_by_handle(wlc_handle_from_wl_output_resource(_output));
 | 
				
			||||||
	swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface));
 | 
						swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface));
 | 
				
			||||||
	sway_log(L_DEBUG, "Setting lock surface to %p", view);
 | 
						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) {
 | 
					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);
 | 
						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);
 | 
						sway_log(L_DEBUG, "Panel position for wl_resource %p changed %d => %d", resource, config->panel_position, position);
 | 
				
			||||||
	config->panel_position = position;
 | 
						config->panel_position = position;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@
 | 
				
			||||||
#include "sway/criteria.h"
 | 
					#include "sway/criteria.h"
 | 
				
			||||||
#include "sway/ipc-server.h"
 | 
					#include "sway/ipc-server.h"
 | 
				
			||||||
#include "sway/input.h"
 | 
					#include "sway/input.h"
 | 
				
			||||||
 | 
					#include "sway/security.h"
 | 
				
			||||||
#include "list.h"
 | 
					#include "list.h"
 | 
				
			||||||
#include "stringop.h"
 | 
					#include "stringop.h"
 | 
				
			||||||
#include "log.h"
 | 
					#include "log.h"
 | 
				
			||||||
| 
						 | 
					@ -385,7 +386,7 @@ static bool handle_view_created(wlc_handle handle) {
 | 
				
			||||||
			struct criteria *crit = criteria->items[i];
 | 
								struct criteria *crit = criteria->items[i];
 | 
				
			||||||
			sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'",
 | 
								sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'",
 | 
				
			||||||
					crit->crit_raw, newview, crit->cmdlist);
 | 
										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) {
 | 
								if (res->status != CMD_SUCCESS) {
 | 
				
			||||||
				sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
 | 
									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) {
 | 
					static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) {
 | 
				
			||||||
	swayc_t *c = swayc_by_handle(view);
 | 
						swayc_t *c = swayc_by_handle(view);
 | 
				
			||||||
 | 
						pid_t pid = wlc_view_get_pid(view);
 | 
				
			||||||
	switch (state) {
 | 
						switch (state) {
 | 
				
			||||||
	case WLC_BIT_FULLSCREEN:
 | 
						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
 | 
							// i3 just lets it become fullscreen
 | 
				
			||||||
		wlc_view_set_state(view, state, toggle);
 | 
							wlc_view_set_state(view, state, toggle);
 | 
				
			||||||
		if (c) {
 | 
							if (c) {
 | 
				
			||||||
| 
						 | 
					@ -579,7 +585,7 @@ static void handle_binding_command(struct sway_binding *binding) {
 | 
				
			||||||
		reload = true;
 | 
							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) {
 | 
						if (res->status != CMD_SUCCESS) {
 | 
				
			||||||
		sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
 | 
							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);
 | 
						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;
 | 
						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);
 | 
						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;
 | 
						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
 | 
						// don't change focus or mode if fullscreen
 | 
				
			||||||
	if (swayc_is_fullscreen(focused)) {
 | 
						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;
 | 
							return EVENT_PASSTHROUGH;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -884,6 +913,13 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w
 | 
				
			||||||
		return EVENT_HANDLED;
 | 
							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
 | 
						// Always send mouse release
 | 
				
			||||||
	if (state == WLC_BUTTON_STATE_RELEASED) {
 | 
						if (state == WLC_BUTTON_STATE_RELEASED) {
 | 
				
			||||||
		return EVENT_PASSTHROUGH;
 | 
							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];
 | 
							int y_amount = (int)_amount[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (x_amount > 0 && strcmp(config->floating_scroll_up_cmd, "")) {
 | 
							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;
 | 
								return EVENT_HANDLED;
 | 
				
			||||||
		} else if (x_amount < 0 && strcmp(config->floating_scroll_down_cmd, "")) {
 | 
							} 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;
 | 
								return EVENT_HANDLED;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (y_amount > 0 && strcmp(config->floating_scroll_right_cmd, "")) {
 | 
							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;
 | 
								return EVENT_HANDLED;
 | 
				
			||||||
		} else if (y_amount < 0 && strcmp(config->floating_scroll_left_cmd, "")) {
 | 
							} 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;
 | 
								return EVENT_HANDLED;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -924,7 +960,7 @@ static void handle_wlc_ready(void) {
 | 
				
			||||||
	config->active = true;
 | 
						config->active = true;
 | 
				
			||||||
	while (config->cmd_queue->length) {
 | 
						while (config->cmd_queue->length) {
 | 
				
			||||||
		char *line = config->cmd_queue->items[0];
 | 
							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) {
 | 
							if (res->status != CMD_SUCCESS) {
 | 
				
			||||||
			sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
 | 
								sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,7 @@
 | 
				
			||||||
#include <libinput.h>
 | 
					#include <libinput.h>
 | 
				
			||||||
#include "sway/ipc-json.h"
 | 
					#include "sway/ipc-json.h"
 | 
				
			||||||
#include "sway/ipc-server.h"
 | 
					#include "sway/ipc-server.h"
 | 
				
			||||||
 | 
					#include "sway/security.h"
 | 
				
			||||||
#include "sway/config.h"
 | 
					#include "sway/config.h"
 | 
				
			||||||
#include "sway/commands.h"
 | 
					#include "sway/commands.h"
 | 
				
			||||||
#include "sway/input.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_workspaces_callback(swayc_t *workspace, void *data);
 | 
				
			||||||
void ipc_get_outputs_callback(swayc_t *container, void *data);
 | 
					void ipc_get_outputs_callback(swayc_t *container, void *data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define event_mask(ev) (1 << (ev & 0x7F))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ipc_init(void) {
 | 
					void ipc_init(void) {
 | 
				
			||||||
	ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 | 
						ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 | 
				
			||||||
	if (ipc_socket == -1) {
 | 
						if (ipc_socket == -1) {
 | 
				
			||||||
| 
						 | 
					@ -126,6 +125,17 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
 | 
				
			||||||
	return ipc_sockaddr;
 | 
						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) {
 | 
					int ipc_handle_connection(int fd, uint32_t mask, void *data) {
 | 
				
			||||||
	(void) fd; (void) data;
 | 
						(void) fd; (void) data;
 | 
				
			||||||
	sway_log(L_DEBUG, "Event on IPC listening socket");
 | 
						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;
 | 
							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));
 | 
						struct ipc_client* client = malloc(sizeof(struct ipc_client));
 | 
				
			||||||
	client->payload_length = 0;
 | 
						client->payload_length = 0;
 | 
				
			||||||
	client->fd = client_fd;
 | 
						client->fd = client_fd;
 | 
				
			||||||
| 
						 | 
					@ -309,10 +328,15 @@ void ipc_client_handle_command(struct ipc_client *client) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	buf[client->payload_length] = '\0';
 | 
						buf[client->payload_length] = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char *error_denied = "{ \"success\": false, \"error\": \"Permission denied\" }";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (client->current_command) {
 | 
						switch (client->current_command) {
 | 
				
			||||||
	case IPC_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);
 | 
							const char *json = cmd_results_to_json(results);
 | 
				
			||||||
		char reply[256];
 | 
							char reply[256];
 | 
				
			||||||
		int length = snprintf(reply, sizeof(reply), "%s", json);
 | 
							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);
 | 
									client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
 | 
				
			||||||
			} else if (strcmp(event_type, "modifier") == 0) {
 | 
								} else if (strcmp(event_type, "modifier") == 0) {
 | 
				
			||||||
				client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
 | 
									client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
 | 
				
			||||||
#if SWAY_BINDING_EVENT
 | 
					 | 
				
			||||||
			} else if (strcmp(event_type, "binding") == 0) {
 | 
								} else if (strcmp(event_type, "binding") == 0) {
 | 
				
			||||||
				client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
 | 
									client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ipc_send_reply(client, "{\"success\": false}", 18);
 | 
									ipc_send_reply(client, "{\"success\": false}", 18);
 | 
				
			||||||
				json_object_put(request);
 | 
									json_object_put(request);
 | 
				
			||||||
| 
						 | 
					@ -363,6 +385,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case IPC_GET_WORKSPACES:
 | 
						case IPC_GET_WORKSPACES:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							if (!(config->ipc_policy & IPC_FEATURE_GET_WORKSPACES)) {
 | 
				
			||||||
 | 
								goto exit_denied;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		json_object *workspaces = json_object_new_array();
 | 
							json_object *workspaces = json_object_new_array();
 | 
				
			||||||
		container_map(&root_container, ipc_get_workspaces_callback, workspaces);
 | 
							container_map(&root_container, ipc_get_workspaces_callback, workspaces);
 | 
				
			||||||
		const char *json_string = json_object_to_json_string(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:
 | 
						case IPC_GET_INPUTS:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							if (!(config->ipc_policy & IPC_FEATURE_GET_INPUTS)) {
 | 
				
			||||||
 | 
								goto exit_denied;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		json_object *inputs = json_object_new_array();
 | 
							json_object *inputs = json_object_new_array();
 | 
				
			||||||
		if (input_devices) {
 | 
							if (input_devices) {
 | 
				
			||||||
			for(int i=0; i<input_devices->length; i++) {
 | 
								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:
 | 
						case IPC_GET_OUTPUTS:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							if (!(config->ipc_policy & IPC_FEATURE_GET_OUTPUTS)) {
 | 
				
			||||||
 | 
								goto exit_denied;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		json_object *outputs = json_object_new_array();
 | 
							json_object *outputs = json_object_new_array();
 | 
				
			||||||
		container_map(&root_container, ipc_get_outputs_callback, outputs);
 | 
							container_map(&root_container, ipc_get_outputs_callback, outputs);
 | 
				
			||||||
		const char *json_string = json_object_to_json_string(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:
 | 
						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);
 | 
							json_object *tree = ipc_json_describe_container_recursive(&root_container);
 | 
				
			||||||
		const char *json_string = json_object_to_json_string(tree);
 | 
							const char *json_string = json_object_to_json_string(tree);
 | 
				
			||||||
		ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
 | 
							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:
 | 
						case IPC_GET_BAR_CONFIG:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							if (!(config->ipc_policy & IPC_FEATURE_GET_BAR_CONFIG)) {
 | 
				
			||||||
 | 
								goto exit_denied;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (!buf[0]) {
 | 
							if (!buf[0]) {
 | 
				
			||||||
			// Send list of configured bar IDs
 | 
								// Send list of configured bar IDs
 | 
				
			||||||
			json_object *bars = json_object_new_array();
 | 
								json_object *bars = json_object_new_array();
 | 
				
			||||||
| 
						 | 
					@ -502,6 +539,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
 | 
				
			||||||
		goto exit_cleanup;
 | 
							goto exit_cleanup;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit_denied:
 | 
				
			||||||
 | 
						ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exit_cleanup:
 | 
					exit_cleanup:
 | 
				
			||||||
	client->payload_length = 0;
 | 
						client->payload_length = 0;
 | 
				
			||||||
	free(buf);
 | 
						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) {
 | 
					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);
 | 
						sway_log(L_DEBUG, "Sending workspace::%s event", change);
 | 
				
			||||||
	json_object *obj = json_object_new_object();
 | 
						json_object *obj = json_object_new_object();
 | 
				
			||||||
	json_object_object_add(obj, "change", json_object_new_string(change));
 | 
						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) {
 | 
					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);
 | 
						sway_log(L_DEBUG, "Sending window::%s event", change);
 | 
				
			||||||
	json_object *obj = json_object_new_object();
 | 
						json_object *obj = json_object_new_object();
 | 
				
			||||||
	json_object_object_add(obj, "change", json_object_new_string(change));
 | 
						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) {
 | 
					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);
 | 
						sway_log(L_DEBUG, "Sending mode::%s event", mode);
 | 
				
			||||||
	json_object *obj = json_object_new_object();
 | 
						json_object *obj = json_object_new_object();
 | 
				
			||||||
	json_object_object_add(obj, "change", json_object_new_string(mode));
 | 
						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
 | 
						json_object_put(obj); // free
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if SWAY_BINDING_EVENT
 | 
					 | 
				
			||||||
static void ipc_event_binding(json_object *sb_obj) {
 | 
					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");
 | 
						sway_log(L_DEBUG, "Sending binding::run event");
 | 
				
			||||||
	json_object *obj = json_object_new_object();
 | 
						json_object *obj = json_object_new_object();
 | 
				
			||||||
	json_object_object_add(obj, "change", json_object_new_string("run"));
 | 
						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
 | 
						json_object_put(obj); // free
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ipc_event_binding_keyboard(struct sway_binding *sb) {
 | 
					void ipc_event_binding_keyboard(struct sway_binding *sb) {
 | 
				
			||||||
#if SWAY_BINDING_EVENT
 | 
					 | 
				
			||||||
	json_object *sb_obj = json_object_new_object();
 | 
						json_object *sb_obj = json_object_new_object();
 | 
				
			||||||
	json_object_object_add(sb_obj, "command", json_object_new_string(sb->command));
 | 
						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"));
 | 
						json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ipc_event_binding(sb_obj);
 | 
						ipc_event_binding(sb_obj);
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										66
									
								
								sway/main.c
									
										
									
									
									
								
							
							
						
						
									
										66
									
								
								sway/main.c
									
										
									
									
									
								
							| 
						 | 
					@ -4,13 +4,16 @@
 | 
				
			||||||
#include <wlc/wlc.h>
 | 
					#include <wlc/wlc.h>
 | 
				
			||||||
#include <sys/wait.h>
 | 
					#include <sys/wait.h>
 | 
				
			||||||
#include <sys/types.h>
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <sys/stat.h>
 | 
				
			||||||
#include <sys/un.h>
 | 
					#include <sys/un.h>
 | 
				
			||||||
#include <signal.h>
 | 
					#include <signal.h>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <getopt.h>
 | 
					#include <getopt.h>
 | 
				
			||||||
 | 
					#include <sys/capability.h>
 | 
				
			||||||
#include "sway/extensions.h"
 | 
					#include "sway/extensions.h"
 | 
				
			||||||
#include "sway/layout.h"
 | 
					#include "sway/layout.h"
 | 
				
			||||||
#include "sway/config.h"
 | 
					#include "sway/config.h"
 | 
				
			||||||
 | 
					#include "sway/security.h"
 | 
				
			||||||
#include "sway/handlers.h"
 | 
					#include "sway/handlers.h"
 | 
				
			||||||
#include "sway/input.h"
 | 
					#include "sway/input.h"
 | 
				
			||||||
#include "sway/ipc-server.h"
 | 
					#include "sway/ipc-server.h"
 | 
				
			||||||
| 
						 | 
					@ -142,6 +145,63 @@ static void log_kernel() {
 | 
				
			||||||
	fclose(f);
 | 
						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) {
 | 
					int main(int argc, char **argv) {
 | 
				
			||||||
	static int verbose = 0, debug = 0, validate = 0;
 | 
						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"
 | 
							"      --get-socketpath   Gets the IPC socket path and prints it, then exits.\n"
 | 
				
			||||||
		"\n";
 | 
							"\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Security:
 | 
				
			||||||
 | 
						unsetenv("LD_PRELOAD");
 | 
				
			||||||
 | 
						setenv("LD_LIBRARY_PATH", _LD_LIBRARY_PATH, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int c;
 | 
						int c;
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
		int option_index = 0;
 | 
							int option_index = 0;
 | 
				
			||||||
| 
						 | 
					@ -298,6 +362,8 @@ int main(int argc, char **argv) {
 | 
				
			||||||
		free(config_path);
 | 
							free(config_path);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						security_sanity_check();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!terminate_request) {
 | 
						if (!terminate_request) {
 | 
				
			||||||
		wlc_run();
 | 
							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(
 | 
					install(
 | 
				
			||||||
	FILES ${CMAKE_CURRENT_SOURCE_DIR}/pam/swaylock
 | 
						FILES ${CMAKE_CURRENT_SOURCE_DIR}/pam/swaylock
 | 
				
			||||||
	DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/pam.d/
 | 
					    DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d/
 | 
				
			||||||
	COMPONENT data
 | 
						COMPONENT data
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue