diff options
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | config.d/security.in | 52 | ||||
-rw-r--r-- | config.in (renamed from config) | 8 | ||||
-rw-r--r-- | include/ipc.h | 2 | ||||
-rw-r--r-- | include/sway/commands.h | 19 | ||||
-rw-r--r-- | include/sway/config.h | 61 | ||||
-rw-r--r-- | include/sway/security.h | 14 | ||||
-rw-r--r-- | sway/CMakeLists.txt | 33 | ||||
-rw-r--r-- | sway/commands.c | 125 | ||||
-rw-r--r-- | sway/commands/commands.c | 23 | ||||
-rw-r--r-- | sway/commands/ipc.c | 140 | ||||
-rw-r--r-- | sway/commands/permit.c | 94 | ||||
-rw-r--r-- | sway/config.c | 72 | ||||
-rw-r--r-- | sway/extensions.c | 25 | ||||
-rw-r--r-- | sway/handlers.c | 50 | ||||
-rw-r--r-- | sway/ipc-server.c | 66 | ||||
-rw-r--r-- | sway/main.c | 66 | ||||
-rw-r--r-- | sway/security.c | 94 | ||||
-rw-r--r-- | sway/sway-security.7.txt | 250 | ||||
-rw-r--r-- | swaylock/CMakeLists.txt | 2 |
20 files changed, 1159 insertions, 44 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 83989ecd..e314fd73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -45,9 +45,11 @@ option(enable-swaybar "Enables the swaybar utility" YES) | |||
45 | option(enable-swaygrab "Enables the swaygrab utility" YES) | 45 | option(enable-swaygrab "Enables the swaygrab utility" YES) |
46 | option(enable-swaymsg "Enables the swaymsg utility" YES) | 46 | option(enable-swaymsg "Enables the swaymsg utility" YES) |
47 | option(enable-gdk-pixbuf "Use Pixbuf to support more image formats" YES) | 47 | option(enable-gdk-pixbuf "Use Pixbuf to support more image formats" YES) |
48 | option(enable-binding-event "Enables binding event subscription" YES) | ||
49 | option(zsh-completions "Zsh shell completions" NO) | 48 | option(zsh-completions "Zsh shell completions" NO) |
50 | option(default-wallpaper "Installs the default wallpaper" YES) | 49 | option(default-wallpaper "Installs the default wallpaper" YES) |
50 | option(ld-library-path "Configures sway's default LD_LIBRARY_PATH" "/usr/lib") | ||
51 | |||
52 | add_definitions(-D_LD_LIBRARY_PATH="${ld-library-path}") | ||
51 | 53 | ||
52 | find_package(JsonC REQUIRED) | 54 | find_package(JsonC REQUIRED) |
53 | find_package(PCRE REQUIRED) | 55 | find_package(PCRE REQUIRED) |
@@ -83,9 +85,6 @@ if (enable-gdk-pixbuf) | |||
83 | else() | 85 | else() |
84 | message(STATUS "Building without gdk-pixbuf, only png images supported.") | 86 | message(STATUS "Building without gdk-pixbuf, only png images supported.") |
85 | endif() | 87 | endif() |
86 | if(enable-binding-event) | ||
87 | add_definitions(-DSWAY_BINDING_EVENT=1) | ||
88 | endif() | ||
89 | 88 | ||
90 | include_directories(include) | 89 | include_directories(include) |
91 | 90 | ||
diff --git a/config.d/security.in b/config.d/security.in new file mode 100644 index 00000000..47592b05 --- /dev/null +++ b/config.d/security.in | |||
@@ -0,0 +1,52 @@ | |||
1 | # sway security rules | ||
2 | # | ||
3 | # Read sway-security(7) for details on how to secure your sway install. | ||
4 | # | ||
5 | # You MUST read this man page if you intend to attempt to secure your sway | ||
6 | # installation. | ||
7 | |||
8 | # Configures which programs are allowed to use which sway features | ||
9 | permit * fullscreen keyboard mouse ipc | ||
10 | permit __PREFIX__/bin/swaylock lock | ||
11 | permit __PREFIX__/bin/swaybar panel | ||
12 | permit __PREFIX__/bin/swaybg background | ||
13 | permit __PREFIX__/bin/swaygrab screenshot | ||
14 | |||
15 | # Configures which IPC features are enabled | ||
16 | ipc { | ||
17 | command enabled | ||
18 | outputs enabled | ||
19 | workspaces enabled | ||
20 | tree enabled | ||
21 | marks enabled | ||
22 | bar-config enabled | ||
23 | inputs enabled | ||
24 | |||
25 | events { | ||
26 | workspace enabled | ||
27 | output enabled | ||
28 | mode enabled | ||
29 | window enabled | ||
30 | modifier enabled | ||
31 | input enabled | ||
32 | binding disabled | ||
33 | } | ||
34 | } | ||
35 | |||
36 | # Limits the contexts from which certain commands are permitted | ||
37 | commands { | ||
38 | * all | ||
39 | |||
40 | fullscreen binding criteria | ||
41 | bindsym config | ||
42 | exit binding | ||
43 | kill binding | ||
44 | |||
45 | # You should not change these unless you know what you're doing - it could | ||
46 | # cripple your security | ||
47 | reload binding | ||
48 | restart binding | ||
49 | permit config | ||
50 | reject config | ||
51 | ipc config | ||
52 | } | ||
@@ -195,10 +195,4 @@ bar { | |||
195 | } | 195 | } |
196 | } | 196 | } |
197 | 197 | ||
198 | # You may want this: | 198 | include __SYSCONFDIR__/sway/config.d/* |
199 | # | ||
200 | # include ~/.config/sway/conf.d/* | ||
201 | # | ||
202 | # Protip: | ||
203 | # | ||
204 | # include ~/.config/sway/`hostname`/* | ||
diff --git a/include/ipc.h b/include/ipc.h index 496625ce..98390335 100644 --- a/include/ipc.h +++ b/include/ipc.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef _SWAY_IPC_H | 1 | #ifndef _SWAY_IPC_H |
2 | #define _SWAY_IPC_H | 2 | #define _SWAY_IPC_H |
3 | 3 | ||
4 | #define event_mask(ev) (1 << (ev & 0x7F)) | ||
5 | |||
4 | enum ipc_command_type { | 6 | enum ipc_command_type { |
5 | IPC_COMMAND = 0, | 7 | IPC_COMMAND = 0, |
6 | IPC_GET_WORKSPACES = 1, | 8 | IPC_GET_WORKSPACES = 1, |
diff --git a/include/sway/commands.h b/include/sway/commands.h index db5e94d9..3ab8d5af 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -18,7 +18,10 @@ enum cmd_status { | |||
18 | CMD_BLOCK_MODE, | 18 | CMD_BLOCK_MODE, |
19 | CMD_BLOCK_BAR, | 19 | CMD_BLOCK_BAR, |
20 | CMD_BLOCK_BAR_COLORS, | 20 | CMD_BLOCK_BAR_COLORS, |
21 | CMD_BLOCK_INPUT | 21 | CMD_BLOCK_INPUT, |
22 | CMD_BLOCK_COMMANDS, | ||
23 | CMD_BLOCK_IPC, | ||
24 | CMD_BLOCK_IPC_EVENTS, | ||
22 | }; | 25 | }; |
23 | 26 | ||
24 | /** | 27 | /** |
@@ -51,13 +54,17 @@ int sp_index; | |||
51 | /** | 54 | /** |
52 | * Parse and handles a command. | 55 | * Parse and handles a command. |
53 | */ | 56 | */ |
54 | struct cmd_results *handle_command(char *command); | 57 | struct cmd_results *handle_command(char *command, enum command_context context); |
55 | /** | 58 | /** |
56 | * Parse and handles a command during config file loading. | 59 | * Parse and handles a command during config file loading. |
57 | * | 60 | * |
58 | * Do not use this under normal conditions. | 61 | * Do not use this under normal conditions. |
59 | */ | 62 | */ |
60 | struct cmd_results *config_command(char *command, enum cmd_status block); | 63 | struct cmd_results *config_command(char *command, enum cmd_status block); |
64 | /* | ||
65 | * Parses a command policy rule. | ||
66 | */ | ||
67 | struct cmd_results *config_commands_command(char *exec); | ||
61 | 68 | ||
62 | /** | 69 | /** |
63 | * Allocates a cmd_results object. | 70 | * Allocates a cmd_results object. |
@@ -93,6 +100,7 @@ sway_cmd cmd_client_unfocused; | |||
93 | sway_cmd cmd_client_urgent; | 100 | sway_cmd cmd_client_urgent; |
94 | sway_cmd cmd_client_placeholder; | 101 | sway_cmd cmd_client_placeholder; |
95 | sway_cmd cmd_client_background; | 102 | sway_cmd cmd_client_background; |
103 | sway_cmd cmd_commands; | ||
96 | sway_cmd cmd_debuglog; | 104 | sway_cmd cmd_debuglog; |
97 | sway_cmd cmd_exec; | 105 | sway_cmd cmd_exec; |
98 | sway_cmd cmd_exec_always; | 106 | sway_cmd cmd_exec_always; |
@@ -112,6 +120,7 @@ sway_cmd cmd_gaps; | |||
112 | sway_cmd cmd_hide_edge_borders; | 120 | sway_cmd cmd_hide_edge_borders; |
113 | sway_cmd cmd_include; | 121 | sway_cmd cmd_include; |
114 | sway_cmd cmd_input; | 122 | sway_cmd cmd_input; |
123 | sway_cmd cmd_ipc; | ||
115 | sway_cmd cmd_kill; | 124 | sway_cmd cmd_kill; |
116 | sway_cmd cmd_layout; | 125 | sway_cmd cmd_layout; |
117 | sway_cmd cmd_log_colors; | 126 | sway_cmd cmd_log_colors; |
@@ -122,6 +131,8 @@ sway_cmd cmd_new_float; | |||
122 | sway_cmd cmd_new_window; | 131 | sway_cmd cmd_new_window; |
123 | sway_cmd cmd_orientation; | 132 | sway_cmd cmd_orientation; |
124 | sway_cmd cmd_output; | 133 | sway_cmd cmd_output; |
134 | sway_cmd cmd_permit; | ||
135 | sway_cmd cmd_reject; | ||
125 | sway_cmd cmd_reload; | 136 | sway_cmd cmd_reload; |
126 | sway_cmd cmd_resize; | 137 | sway_cmd cmd_resize; |
127 | sway_cmd cmd_scratchpad; | 138 | sway_cmd cmd_scratchpad; |
@@ -182,4 +193,8 @@ sway_cmd input_cmd_pointer_accel; | |||
182 | sway_cmd input_cmd_scroll_method; | 193 | sway_cmd input_cmd_scroll_method; |
183 | sway_cmd input_cmd_tap; | 194 | sway_cmd input_cmd_tap; |
184 | 195 | ||
196 | sway_cmd cmd_ipc_cmd; | ||
197 | sway_cmd cmd_ipc_events; | ||
198 | sway_cmd cmd_ipc_event_cmd; | ||
199 | |||
185 | #endif | 200 | #endif |
diff --git a/include/sway/config.h b/include/sway/config.h index 8d077ee7..2c6b83e7 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -103,9 +103,6 @@ struct pid_workspace { | |||
103 | time_t *time_added; | 103 | time_t *time_added; |
104 | }; | 104 | }; |
105 | 105 | ||
106 | void pid_workspace_add(struct pid_workspace *pw); | ||
107 | void free_pid_workspace(struct pid_workspace *pw); | ||
108 | |||
109 | struct bar_config { | 106 | struct bar_config { |
110 | /** | 107 | /** |
111 | * One of "dock", "hide", "invisible" | 108 | * One of "dock", "hide", "invisible" |
@@ -138,7 +135,7 @@ struct bar_config { | |||
138 | int height; // -1 not defined | 135 | int height; // -1 not defined |
139 | int tray_padding; | 136 | int tray_padding; |
140 | bool workspace_buttons; | 137 | bool workspace_buttons; |
141 | bool wrap_scroll; | 138 | bool wrap_scroll; |
142 | char *separator_symbol; | 139 | char *separator_symbol; |
143 | bool strip_workspace_numbers; | 140 | bool strip_workspace_numbers; |
144 | bool binding_mode_indicator; | 141 | bool binding_mode_indicator; |
@@ -184,6 +181,52 @@ enum edge_border_types { | |||
184 | E_BOTH /**< hide vertical and horizontal edge borders */ | 181 | E_BOTH /**< hide vertical and horizontal edge borders */ |
185 | }; | 182 | }; |
186 | 183 | ||
184 | enum command_context { | ||
185 | CONTEXT_CONFIG = 1, | ||
186 | CONTEXT_BINDING = 2, | ||
187 | CONTEXT_IPC = 4, | ||
188 | CONTEXT_CRITERIA = 8, | ||
189 | CONTEXT_ALL = 0xFFFFFFFF, | ||
190 | }; | ||
191 | |||
192 | struct command_policy { | ||
193 | char *command; | ||
194 | uint32_t context; | ||
195 | }; | ||
196 | |||
197 | enum secure_feature { | ||
198 | FEATURE_LOCK = 1, | ||
199 | FEATURE_PANEL = 2, | ||
200 | FEATURE_BACKGROUND = 4, | ||
201 | FEATURE_SCREENSHOT = 8, | ||
202 | FEATURE_FULLSCREEN = 16, | ||
203 | FEATURE_KEYBOARD = 32, | ||
204 | FEATURE_MOUSE = 64, | ||
205 | FEATURE_IPC = 128, | ||
206 | }; | ||
207 | |||
208 | struct feature_policy { | ||
209 | char *program; | ||
210 | uint32_t features; | ||
211 | }; | ||
212 | |||
213 | enum ipc_feature { | ||
214 | IPC_FEATURE_COMMAND = 1, | ||
215 | IPC_FEATURE_GET_WORKSPACES = 2, | ||
216 | IPC_FEATURE_GET_OUTPUTS = 4, | ||
217 | IPC_FEATURE_GET_TREE = 8, | ||
218 | IPC_FEATURE_GET_MARKS = 16, | ||
219 | IPC_FEATURE_GET_BAR_CONFIG = 32, | ||
220 | IPC_FEATURE_GET_VERSION = 64, | ||
221 | IPC_FEATURE_GET_INPUTS = 128, | ||
222 | IPC_FEATURE_EVENT_WORKSPACE = 256, | ||
223 | IPC_FEATURE_EVENT_OUTPUT = 512, | ||
224 | IPC_FEATURE_EVENT_MODE = 1024, | ||
225 | IPC_FEATURE_EVENT_WINDOW = 2048, | ||
226 | IPC_FEATURE_EVENT_BINDING = 4096, | ||
227 | IPC_FEATURE_EVENT_INPUT = 8192 | ||
228 | }; | ||
229 | |||
187 | /** | 230 | /** |
188 | * The configuration struct. The result of loading a config file. | 231 | * The configuration struct. The result of loading a config file. |
189 | */ | 232 | */ |
@@ -203,7 +246,7 @@ struct sway_config { | |||
203 | uint32_t floating_mod; | 246 | uint32_t floating_mod; |
204 | uint32_t dragging_key; | 247 | uint32_t dragging_key; |
205 | uint32_t resizing_key; | 248 | uint32_t resizing_key; |
206 | char *floating_scroll_up_cmd; | 249 | char *floating_scroll_up_cmd; |
207 | char *floating_scroll_down_cmd; | 250 | char *floating_scroll_down_cmd; |
208 | char *floating_scroll_left_cmd; | 251 | char *floating_scroll_left_cmd; |
209 | char *floating_scroll_right_cmd; | 252 | char *floating_scroll_right_cmd; |
@@ -252,8 +295,16 @@ struct sway_config { | |||
252 | int32_t floating_maximum_height; | 295 | int32_t floating_maximum_height; |
253 | int32_t floating_minimum_width; | 296 | int32_t floating_minimum_width; |
254 | int32_t floating_minimum_height; | 297 | int32_t floating_minimum_height; |
298 | |||
299 | // Security | ||
300 | list_t *command_policies; | ||
301 | list_t *feature_policies; | ||
302 | uint32_t ipc_policy; | ||
255 | }; | 303 | }; |
256 | 304 | ||
305 | void pid_workspace_add(struct pid_workspace *pw); | ||
306 | void free_pid_workspace(struct pid_workspace *pw); | ||
307 | |||
257 | /** | 308 | /** |
258 | * Loads the main config from the given path. is_active should be true when | 309 | * Loads the main config from the given path. is_active should be true when |
259 | * reloading the config. | 310 | * reloading the config. |
diff --git a/include/sway/security.h b/include/sway/security.h new file mode 100644 index 00000000..1cc85bee --- /dev/null +++ b/include/sway/security.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef _SWAY_SECURITY_H | ||
2 | #define _SWAY_SECURITY_H | ||
3 | #include <unistd.h> | ||
4 | #include "sway/config.h" | ||
5 | |||
6 | enum secure_feature get_feature_policy(pid_t pid); | ||
7 | enum command_context get_command_policy(const char *cmd); | ||
8 | |||
9 | const char *command_policy_str(enum command_context context); | ||
10 | |||
11 | struct feature_policy *alloc_feature_policy(const char *program); | ||
12 | struct command_policy *alloc_command_policy(const char *command); | ||
13 | |||
14 | #endif | ||
diff --git a/sway/CMakeLists.txt b/sway/CMakeLists.txt index bb9ea81f..d1afadb6 100644 --- a/sway/CMakeLists.txt +++ b/sway/CMakeLists.txt | |||
@@ -35,6 +35,7 @@ add_executable(sway | |||
35 | output.c | 35 | output.c |
36 | workspace.c | 36 | workspace.c |
37 | border.c | 37 | border.c |
38 | security.c | ||
38 | ) | 39 | ) |
39 | 40 | ||
40 | add_definitions( | 41 | add_definitions( |
@@ -54,6 +55,7 @@ target_link_libraries(sway | |||
54 | ${PANGO_LIBRARIES} | 55 | ${PANGO_LIBRARIES} |
55 | ${JSONC_LIBRARIES} | 56 | ${JSONC_LIBRARIES} |
56 | m | 57 | m |
58 | cap | ||
57 | ) | 59 | ) |
58 | 60 | ||
59 | install( | 61 | install( |
@@ -62,13 +64,34 @@ install( | |||
62 | DESTINATION bin | 64 | DESTINATION bin |
63 | COMPONENT runtime | 65 | COMPONENT runtime |
64 | ) | 66 | ) |
65 | install( | 67 | |
66 | FILES ${PROJECT_SOURCE_DIR}/config | 68 | add_custom_target(configs ALL) |
67 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sway/ | 69 | |
68 | COMPONENT configuration | 70 | function(add_config name source destination) |
69 | ) | 71 | add_custom_command( |
72 | OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} | ||
73 | COMMAND sed -r | ||
74 | 's?__PREFIX__?${CMAKE_INSTALL_PREFIX}?g\; s?__SYSCONFDIR__?${CMAKE_INSTALL_FULL_SYSCONFDIR}?g' | ||
75 | ${PROJECT_SOURCE_DIR}/${source}.in > ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} | ||
76 | DEPENDS ${PROJECT_SOURCE_DIR}/${source}.in | ||
77 | COMMENT "Generating config file ${source}" | ||
78 | ) | ||
79 | |||
80 | install( | ||
81 | FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} | ||
82 | DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/${destination} | ||
83 | COMPONENT configuration | ||
84 | ) | ||
85 | |||
86 | add_custom_target(config-${name} DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}) | ||
87 | add_dependencies(configs config-${name}) | ||
88 | endfunction() | ||
89 | |||
90 | add_config(config config sway) | ||
91 | add_config(security config.d/security sway/config.d) | ||
70 | 92 | ||
71 | add_manpage(sway 1) | 93 | add_manpage(sway 1) |
72 | add_manpage(sway 5) | 94 | add_manpage(sway 5) |
73 | add_manpage(sway-input 5) | 95 | add_manpage(sway-input 5) |
74 | add_manpage(sway-bar 5) | 96 | add_manpage(sway-bar 5) |
97 | add_manpage(sway-security 7) | ||
diff --git a/sway/commands.c b/sway/commands.c index de29a7af..d87d0084 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "sway/input_state.h" | 26 | #include "sway/input_state.h" |
27 | #include "sway/criteria.h" | 27 | #include "sway/criteria.h" |
28 | #include "sway/ipc-server.h" | 28 | #include "sway/ipc-server.h" |
29 | #include "sway/security.h" | ||
29 | #include "sway/input.h" | 30 | #include "sway/input.h" |
30 | #include "sway/border.h" | 31 | #include "sway/border.h" |
31 | #include "stringop.h" | 32 | #include "stringop.h" |
@@ -158,6 +159,7 @@ static struct cmd_handler handlers[] = { | |||
158 | { "client.placeholder", cmd_client_placeholder }, | 159 | { "client.placeholder", cmd_client_placeholder }, |
159 | { "client.unfocused", cmd_client_unfocused }, | 160 | { "client.unfocused", cmd_client_unfocused }, |
160 | { "client.urgent", cmd_client_urgent }, | 161 | { "client.urgent", cmd_client_urgent }, |
162 | { "commands", cmd_commands }, | ||
161 | { "debuglog", cmd_debuglog }, | 163 | { "debuglog", cmd_debuglog }, |
162 | { "default_orientation", cmd_orientation }, | 164 | { "default_orientation", cmd_orientation }, |
163 | { "exec", cmd_exec }, | 165 | { "exec", cmd_exec }, |
@@ -178,6 +180,7 @@ static struct cmd_handler handlers[] = { | |||
178 | { "hide_edge_borders", cmd_hide_edge_borders }, | 180 | { "hide_edge_borders", cmd_hide_edge_borders }, |
179 | { "include", cmd_include }, | 181 | { "include", cmd_include }, |
180 | { "input", cmd_input }, | 182 | { "input", cmd_input }, |
183 | { "ipc", cmd_ipc }, | ||
181 | { "kill", cmd_kill }, | 184 | { "kill", cmd_kill }, |
182 | { "layout", cmd_layout }, | 185 | { "layout", cmd_layout }, |
183 | { "log_colors", cmd_log_colors }, | 186 | { "log_colors", cmd_log_colors }, |
@@ -187,6 +190,8 @@ static struct cmd_handler handlers[] = { | |||
187 | { "new_float", cmd_new_float }, | 190 | { "new_float", cmd_new_float }, |
188 | { "new_window", cmd_new_window }, | 191 | { "new_window", cmd_new_window }, |
189 | { "output", cmd_output }, | 192 | { "output", cmd_output }, |
193 | { "permit", cmd_permit }, | ||
194 | { "reject", cmd_reject }, | ||
190 | { "reload", cmd_reload }, | 195 | { "reload", cmd_reload }, |
191 | { "resize", cmd_resize }, | 196 | { "resize", cmd_resize }, |
192 | { "scratchpad", cmd_scratchpad }, | 197 | { "scratchpad", cmd_scratchpad }, |
@@ -288,6 +293,26 @@ static struct cmd_handler bar_colors_handlers[] = { | |||
288 | { "urgent_workspace", bar_colors_cmd_urgent_workspace }, | 293 | { "urgent_workspace", bar_colors_cmd_urgent_workspace }, |
289 | }; | 294 | }; |
290 | 295 | ||
296 | static struct cmd_handler ipc_handlers[] = { | ||
297 | { "bar-config", cmd_ipc_cmd }, | ||
298 | { "command", cmd_ipc_cmd }, | ||
299 | { "events", cmd_ipc_events }, | ||
300 | { "inputs", cmd_ipc_cmd }, | ||
301 | { "marks", cmd_ipc_cmd }, | ||
302 | { "outputs", cmd_ipc_cmd }, | ||
303 | { "tree", cmd_ipc_cmd }, | ||
304 | { "workspaces", cmd_ipc_cmd }, | ||
305 | }; | ||
306 | |||
307 | static struct cmd_handler ipc_event_handlers[] = { | ||
308 | { "binding", cmd_ipc_event_cmd }, | ||
309 | { "input", cmd_ipc_event_cmd }, | ||
310 | { "mode", cmd_ipc_event_cmd }, | ||
311 | { "output", cmd_ipc_event_cmd }, | ||
312 | { "window", cmd_ipc_event_cmd }, | ||
313 | { "workspace", cmd_ipc_event_cmd }, | ||
314 | }; | ||
315 | |||
291 | static int handler_compare(const void *_a, const void *_b) { | 316 | static int handler_compare(const void *_a, const void *_b) { |
292 | const struct cmd_handler *a = _a; | 317 | const struct cmd_handler *a = _a; |
293 | const struct cmd_handler *b = _b; | 318 | const struct cmd_handler *b = _b; |
@@ -307,10 +332,17 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) { | |||
307 | sizeof(bar_colors_handlers) / sizeof(struct cmd_handler), | 332 | sizeof(bar_colors_handlers) / sizeof(struct cmd_handler), |
308 | sizeof(struct cmd_handler), handler_compare); | 333 | sizeof(struct cmd_handler), handler_compare); |
309 | } else if (block == CMD_BLOCK_INPUT) { | 334 | } else if (block == CMD_BLOCK_INPUT) { |
310 | sway_log(L_DEBUG, "looking at input handlers"); | ||
311 | res = bsearch(&d, input_handlers, | 335 | res = bsearch(&d, input_handlers, |
312 | sizeof(input_handlers) / sizeof(struct cmd_handler), | 336 | sizeof(input_handlers) / sizeof(struct cmd_handler), |
313 | sizeof(struct cmd_handler), handler_compare); | 337 | sizeof(struct cmd_handler), handler_compare); |
338 | } else if (block == CMD_BLOCK_IPC) { | ||
339 | res = bsearch(&d, ipc_handlers, | ||
340 | sizeof(ipc_handlers) / sizeof(struct cmd_handler), | ||
341 | sizeof(struct cmd_handler), handler_compare); | ||
342 | } else if (block == CMD_BLOCK_IPC_EVENTS) { | ||
343 | res = bsearch(&d, ipc_event_handlers, | ||
344 | sizeof(ipc_event_handlers) / sizeof(struct cmd_handler), | ||
345 | sizeof(struct cmd_handler), handler_compare); | ||
314 | } else { | 346 | } else { |
315 | res = bsearch(&d, handlers, | 347 | res = bsearch(&d, handlers, |
316 | sizeof(handlers) / sizeof(struct cmd_handler), | 348 | sizeof(handlers) / sizeof(struct cmd_handler), |
@@ -319,7 +351,7 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) { | |||
319 | return res; | 351 | return res; |
320 | } | 352 | } |
321 | 353 | ||
322 | struct cmd_results *handle_command(char *_exec) { | 354 | struct cmd_results *handle_command(char *_exec, enum command_context context) { |
323 | // Even though this function will process multiple commands we will only | 355 | // Even though this function will process multiple commands we will only |
324 | // return the last error, if any (for now). (Since we have access to an | 356 | // return the last error, if any (for now). (Since we have access to an |
325 | // error string we could e.g. concatonate all errors there.) | 357 | // error string we could e.g. concatonate all errors there.) |
@@ -393,6 +425,16 @@ struct cmd_results *handle_command(char *_exec) { | |||
393 | free_argv(argc, argv); | 425 | free_argv(argc, argv); |
394 | goto cleanup; | 426 | goto cleanup; |
395 | } | 427 | } |
428 | if (!(get_command_policy(argv[0]) & context)) { | ||
429 | if (results) { | ||
430 | free_cmd_results(results); | ||
431 | } | ||
432 | results = cmd_results_new(CMD_INVALID, cmd, | ||
433 | "Permission denied for %s via %s", cmd, | ||
434 | command_policy_str(context)); | ||
435 | free_argv(argc, argv); | ||
436 | goto cleanup; | ||
437 | } | ||
396 | struct cmd_results *res = handler->handle(argc-1, argv+1); | 438 | struct cmd_results *res = handler->handle(argc-1, argv+1); |
397 | if (res->status != CMD_SUCCESS) { | 439 | if (res->status != CMD_SUCCESS) { |
398 | free_argv(argc, argv); | 440 | free_argv(argc, argv); |
@@ -458,7 +500,84 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) { | |||
458 | } else { | 500 | } else { |
459 | results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented"); | 501 | results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented"); |
460 | } | 502 | } |
461 | cleanup: | 503 | |
504 | cleanup: | ||
505 | free_argv(argc, argv); | ||
506 | return results; | ||
507 | } | ||
508 | |||
509 | struct cmd_results *config_commands_command(char *exec) { | ||
510 | struct cmd_results *results = NULL; | ||
511 | int argc; | ||
512 | char **argv = split_args(exec, &argc); | ||
513 | if (!argc) { | ||
514 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
515 | goto cleanup; | ||
516 | } | ||
517 | |||
518 | // Find handler for the command this is setting a policy for | ||
519 | char *cmd = argv[0]; | ||
520 | |||
521 | if (strcmp(cmd, "}") == 0) { | ||
522 | results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); | ||
523 | goto cleanup; | ||
524 | } | ||
525 | |||
526 | struct cmd_handler *handler = find_handler(cmd, CMD_BLOCK_END); | ||
527 | if (!handler && strcmp(cmd, "*") != 0) { | ||
528 | char *input = cmd ? cmd : "(empty)"; | ||
529 | results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); | ||
530 | goto cleanup; | ||
531 | } | ||
532 | |||
533 | enum command_context context = 0; | ||
534 | |||
535 | struct { | ||
536 | char *name; | ||
537 | enum command_context context; | ||
538 | } context_names[] = { | ||
539 | { "config", CONTEXT_CONFIG }, | ||
540 | { "binding", CONTEXT_BINDING }, | ||
541 | { "ipc", CONTEXT_IPC }, | ||
542 | { "criteria", CONTEXT_CRITERIA }, | ||
543 | { "all", CONTEXT_ALL }, | ||
544 | }; | ||
545 | |||
546 | for (int i = 1; i < argc; ++i) { | ||
547 | size_t j; | ||
548 | for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) { | ||
549 | if (strcmp(context_names[j].name, argv[i]) == 0) { | ||
550 | break; | ||
551 | } | ||
552 | } | ||
553 | if (j == sizeof(context_names) / sizeof(context_names[0])) { | ||
554 | results = cmd_results_new(CMD_INVALID, cmd, | ||
555 | "Invalid command context %s", argv[i]); | ||
556 | goto cleanup; | ||
557 | } | ||
558 | context |= context_names[j].context; | ||
559 | } | ||
560 | |||
561 | struct command_policy *policy = NULL; | ||
562 | for (int i = 0; i < config->command_policies->length; ++i) { | ||
563 | struct command_policy *p = config->command_policies->items[i]; | ||
564 | if (strcmp(p->command, cmd) == 0) { | ||
565 | policy = p; | ||
566 | break; | ||
567 | } | ||
568 | } | ||
569 | if (!policy) { | ||
570 | policy = alloc_command_policy(cmd); | ||
571 | list_add(config->command_policies, policy); | ||
572 | } | ||
573 | policy->context = context; | ||
574 | |||
575 | sway_log(L_INFO, "Set command policy for %s to %d", | ||
576 | policy->command, policy->context); | ||
577 | |||
578 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
579 | |||
580 | cleanup: | ||
462 | free_argv(argc, argv); | 581 | free_argv(argc, argv); |
463 | return results; | 582 | return results; |
464 | } | 583 | } |
diff --git a/sway/commands/commands.c b/sway/commands/commands.c new file mode 100644 index 00000000..5d248e30 --- /dev/null +++ b/sway/commands/commands.c | |||
@@ -0,0 +1,23 @@ | |||
1 | #include <stdbool.h> | ||
2 | #include <string.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/config.h" | ||
5 | #include "list.h" | ||
6 | #include "log.h" | ||
7 | |||
8 | struct cmd_results *cmd_commands(int argc, char **argv) { | ||
9 | struct cmd_results *error = NULL; | ||
10 | if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) { | ||
11 | return error; | ||
12 | } | ||
13 | |||
14 | if (strcmp(argv[0], "{") != 0) { | ||
15 | return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration"); | ||
16 | } | ||
17 | |||
18 | if (!config->reading) { | ||
19 | return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file."); | ||
20 | } | ||
21 | |||
22 | return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL); | ||
23 | } | ||
diff --git a/sway/commands/ipc.c b/sway/commands/ipc.c new file mode 100644 index 00000000..222be0dd --- /dev/null +++ b/sway/commands/ipc.c | |||
@@ -0,0 +1,140 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/config.h" | ||
5 | #include "ipc.h" | ||
6 | #include "log.h" | ||
7 | #include "util.h" | ||
8 | |||
9 | struct cmd_results *cmd_ipc(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | |||
15 | if (config->reading && strcmp("{", argv[0]) != 0) { | ||
16 | return cmd_results_new(CMD_INVALID, "ipc", | ||
17 | "Expected '{' at start of IPC config definition."); | ||
18 | } | ||
19 | |||
20 | if (!config->reading) { | ||
21 | return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file."); | ||
22 | } | ||
23 | |||
24 | return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL); | ||
25 | } | ||
26 | |||
27 | struct cmd_results *cmd_ipc_events(int argc, char **argv) { | ||
28 | struct cmd_results *error = NULL; | ||
29 | if ((error = checkarg(argc, "events", EXPECTED_EQUAL_TO, 1))) { | ||
30 | return error; | ||
31 | } | ||
32 | |||
33 | if (config->reading && strcmp("{", argv[0]) != 0) { | ||
34 | return cmd_results_new(CMD_INVALID, "events", | ||
35 | "Expected '{' at start of IPC event config definition."); | ||
36 | } | ||
37 | |||
38 | if (!config->reading) { | ||
39 | return cmd_results_new(CMD_FAILURE, "events", "Can only be used in config file."); | ||
40 | } | ||
41 | |||
42 | return cmd_results_new(CMD_BLOCK_IPC_EVENTS, NULL, NULL); | ||
43 | } | ||
44 | |||
45 | struct cmd_results *cmd_ipc_cmd(int argc, char **argv) { | ||
46 | struct cmd_results *error = NULL; | ||
47 | if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) { | ||
48 | return error; | ||
49 | } | ||
50 | |||
51 | bool enabled; | ||
52 | if (strcmp(argv[0], "enabled") == 0) { | ||
53 | enabled = true; | ||
54 | } else if (strcmp(argv[0], "disabled") == 0) { | ||
55 | enabled = false; | ||
56 | } else { | ||
57 | return cmd_results_new(CMD_INVALID, argv[-1], | ||
58 | "Argument must be one of 'enabled' or 'disabled'"); | ||
59 | } | ||
60 | |||
61 | struct { | ||
62 | char *name; | ||
63 | enum ipc_feature type; | ||
64 | } types[] = { | ||
65 | { "command", IPC_FEATURE_COMMAND }, | ||
66 | { "workspaces", IPC_FEATURE_GET_WORKSPACES }, | ||
67 | { "outputs", IPC_FEATURE_GET_OUTPUTS }, | ||
68 | { "tree", IPC_FEATURE_GET_TREE }, | ||
69 | { "marks", IPC_FEATURE_GET_MARKS }, | ||
70 | { "bar-config", IPC_FEATURE_GET_BAR_CONFIG }, | ||
71 | { "inputs", IPC_FEATURE_GET_INPUTS }, | ||
72 | }; | ||
73 | |||
74 | uint32_t type = 0; | ||
75 | |||
76 | for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) { | ||
77 | if (strcmp(types[i].name, argv[-1]) == 0) { | ||
78 | type = types[i].type; | ||
79 | break; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | if (enabled) { | ||
84 | config->ipc_policy |= type; | ||
85 | sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]); | ||
86 | } else { | ||
87 | config->ipc_policy &= ~type; | ||
88 | sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]); | ||
89 | } | ||
90 | |||
91 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
92 | } | ||
93 | |||
94 | struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) { | ||
95 | struct cmd_results *error = NULL; | ||
96 | if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) { | ||
97 | return error; | ||
98 | } | ||
99 | |||
100 | bool enabled; | ||
101 | if (strcmp(argv[0], "enabled") == 0) { | ||
102 | enabled = true; | ||
103 | } else if (strcmp(argv[0], "disabled") == 0) { | ||
104 | enabled = false; | ||
105 | } else { | ||
106 | return cmd_results_new(CMD_INVALID, argv[-1], | ||
107 | "Argument must be one of 'enabled' or 'disabled'"); | ||
108 | } | ||
109 | |||
110 | struct { | ||
111 | char *name; | ||
112 | enum ipc_feature type; | ||
113 | } types[] = { | ||
114 | { "workspace", IPC_FEATURE_EVENT_WORKSPACE }, | ||
115 | { "output", IPC_FEATURE_EVENT_OUTPUT }, | ||
116 | { "mode", IPC_FEATURE_EVENT_MODE }, | ||
117 | { "window", IPC_FEATURE_EVENT_WINDOW }, | ||
118 | { "binding", IPC_FEATURE_EVENT_BINDING }, | ||
119 | { "input", IPC_FEATURE_EVENT_INPUT }, | ||
120 | }; | ||
121 | |||
122 | uint32_t type = 0; | ||
123 | |||
124 | for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) { | ||
125 | if (strcmp(types[i].name, argv[-1]) == 0) { | ||
126 | type = types[i].type; | ||
127 | break; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | if (enabled) { | ||
132 | config->ipc_policy |= type; | ||
133 | sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]); | ||
134 | } else { | ||
135 | config->ipc_policy &= ~type; | ||
136 | sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]); | ||
137 | } | ||
138 | |||
139 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
140 | } | ||
diff --git a/sway/commands/permit.c b/sway/commands/permit.c new file mode 100644 index 00000000..7a25e4ce --- /dev/null +++ b/sway/commands/permit.c | |||
@@ -0,0 +1,94 @@ | |||
1 | #include <string.h> | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/security.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | static enum secure_feature get_features(int argc, char **argv, | ||
8 | struct cmd_results **error) { | ||
9 | enum secure_feature features = 0; | ||
10 | |||
11 | struct { | ||
12 | char *name; | ||
13 | enum secure_feature feature; | ||
14 | } feature_names[] = { | ||
15 | { "lock", FEATURE_LOCK }, | ||
16 | { "panel", FEATURE_PANEL }, | ||
17 | { "background", FEATURE_BACKGROUND }, | ||
18 | { "screenshot", FEATURE_SCREENSHOT }, | ||
19 | { "fullscreen", FEATURE_FULLSCREEN }, | ||
20 | { "keyboard", FEATURE_KEYBOARD }, | ||
21 | { "mouse", FEATURE_MOUSE }, | ||
22 | { "ipc", FEATURE_IPC }, | ||
23 | }; | ||
24 | |||
25 | for (int i = 1; i < argc; ++i) { | ||
26 | size_t j; | ||
27 | for (j = 0; j < sizeof(feature_names) / sizeof(feature_names[0]); ++j) { | ||
28 | if (strcmp(feature_names[j].name, argv[i]) == 0) { | ||
29 | break; | ||
30 | } | ||
31 | } | ||
32 | if (j == sizeof(feature_names) / sizeof(feature_names[0])) { | ||
33 | *error = cmd_results_new(CMD_INVALID, | ||
34 | "permit", "Invalid feature grant %s", argv[i]); | ||
35 | return 0; | ||
36 | } | ||
37 | features |= feature_names[j].feature; | ||
38 | } | ||
39 | return features; | ||
40 | } | ||
41 | |||
42 | static struct feature_policy *get_policy(const char *name) { | ||
43 | struct feature_policy *policy = NULL; | ||
44 | for (int i = 0; i < config->feature_policies->length; ++i) { | ||
45 | struct feature_policy *p = config->feature_policies->items[i]; | ||
46 | if (strcmp(p->program, name) == 0) { | ||
47 | policy = p; | ||
48 | break; | ||
49 | } | ||
50 | } | ||
51 | if (!policy) { | ||
52 | policy = alloc_feature_policy(name); | ||
53 | list_add(config->feature_policies, policy); | ||
54 | } | ||
55 | return policy; | ||
56 | } | ||
57 | |||
58 | struct cmd_results *cmd_permit(int argc, char **argv) { | ||
59 | struct cmd_results *error = NULL; | ||
60 | if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) { | ||
61 | return error; | ||
62 | } | ||
63 | |||
64 | struct feature_policy *policy = get_policy(argv[0]); | ||
65 | policy->features |= get_features(argc, argv, &error); | ||
66 | |||
67 | if (error) { | ||
68 | return error; | ||
69 | } | ||
70 | |||
71 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", | ||
72 | policy->program, policy->features); | ||
73 | |||
74 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
75 | } | ||
76 | |||
77 | struct cmd_results *cmd_reject(int argc, char **argv) { | ||
78 | struct cmd_results *error = NULL; | ||
79 | if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) { | ||
80 | return error; | ||
81 | } | ||
82 | |||
83 | struct feature_policy *policy = get_policy(argv[0]); | ||
84 | policy->features &= ~get_features(argc, argv, &error); | ||
85 | |||
86 | if (error) { | ||
87 | return error; | ||
88 | } | ||
89 | |||
90 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", | ||
91 | policy->program, policy->features); | ||
92 | |||
93 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
94 | } | ||
diff --git a/sway/config.c b/sway/config.c index 7d5999d8..e737f83c 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -167,6 +167,16 @@ void free_pid_workspace(struct pid_workspace *pw) { | |||
167 | free(pw); | 167 | free(pw); |
168 | } | 168 | } |
169 | 169 | ||
170 | void free_command_policy(struct command_policy *policy) { | ||
171 | free(policy->command); | ||
172 | free(policy); | ||
173 | } | ||
174 | |||
175 | void free_feature_policy(struct feature_policy *policy) { | ||
176 | free(policy->program); | ||
177 | free(policy); | ||
178 | } | ||
179 | |||
170 | void free_config(struct sway_config *config) { | 180 | void free_config(struct sway_config *config) { |
171 | int i; | 181 | int i; |
172 | for (i = 0; i < config->symbols->length; ++i) { | 182 | for (i = 0; i < config->symbols->length; ++i) { |
@@ -211,6 +221,16 @@ void free_config(struct sway_config *config) { | |||
211 | } | 221 | } |
212 | list_free(config->output_configs); | 222 | list_free(config->output_configs); |
213 | 223 | ||
224 | for (i = 0; i < config->command_policies->length; ++i) { | ||
225 | free_command_policy(config->command_policies->items[i]); | ||
226 | } | ||
227 | list_free(config->command_policies); | ||
228 | |||
229 | for (i = 0; i < config->feature_policies->length; ++i) { | ||
230 | free_feature_policy(config->feature_policies->items[i]); | ||
231 | } | ||
232 | list_free(config->feature_policies); | ||
233 | |||
214 | list_free(config->active_bar_modifiers); | 234 | list_free(config->active_bar_modifiers); |
215 | free_flat_list(config->config_chain); | 235 | free_flat_list(config->config_chain); |
216 | free(config->font); | 236 | free(config->font); |
@@ -321,6 +341,11 @@ static void config_defaults(struct sway_config *config) { | |||
321 | config->border_colors.placeholder.child_border = 0x0C0C0CFF; | 341 | config->border_colors.placeholder.child_border = 0x0C0C0CFF; |
322 | 342 | ||
323 | config->border_colors.background = 0xFFFFFFFF; | 343 | config->border_colors.background = 0xFFFFFFFF; |
344 | |||
345 | // Security | ||
346 | config->command_policies = create_list(); | ||
347 | config->feature_policies = create_list(); | ||
348 | config->ipc_policy = UINT32_MAX; | ||
324 | } | 349 | } |
325 | 350 | ||
326 | static int compare_modifiers(const void *left, const void *right) { | 351 | static int compare_modifiers(const void *left, const void *right) { |
@@ -556,7 +581,13 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
556 | free(line); | 581 | free(line); |
557 | continue; | 582 | continue; |
558 | } | 583 | } |
559 | struct cmd_results *res = config_command(line, block); | 584 | struct cmd_results *res; |
585 | if (block == CMD_BLOCK_COMMANDS) { | ||
586 | // Special case | ||
587 | res = config_commands_command(line); | ||
588 | } else { | ||
589 | res = config_command(line, block); | ||
590 | } | ||
560 | switch(res->status) { | 591 | switch(res->status) { |
561 | case CMD_FAILURE: | 592 | case CMD_FAILURE: |
562 | case CMD_INVALID: | 593 | case CMD_INVALID: |
@@ -602,6 +633,30 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
602 | } | 633 | } |
603 | break; | 634 | break; |
604 | 635 | ||
636 | case CMD_BLOCK_COMMANDS: | ||
637 | if (block == CMD_BLOCK_END) { | ||
638 | block = CMD_BLOCK_COMMANDS; | ||
639 | } else { | ||
640 | sway_log(L_ERROR, "Invalid block '%s'", line); | ||
641 | } | ||
642 | break; | ||
643 | |||
644 | case CMD_BLOCK_IPC: | ||
645 | if (block == CMD_BLOCK_END) { | ||
646 | block = CMD_BLOCK_IPC; | ||
647 | } else { | ||
648 | sway_log(L_ERROR, "Invalid block '%s'", line); | ||
649 | } | ||
650 | break; | ||
651 | |||
652 | case CMD_BLOCK_IPC_EVENTS: | ||
653 | if (block == CMD_BLOCK_IPC) { | ||
654 | block = CMD_BLOCK_IPC_EVENTS; | ||
655 | } else { | ||
656 | sway_log(L_ERROR, "Invalid block '%s'", line); | ||
657 | } | ||
658 | break; | ||
659 | |||
605 | case CMD_BLOCK_END: | 660 | case CMD_BLOCK_END: |
606 | switch(block) { | 661 | switch(block) { |
607 | case CMD_BLOCK_MODE: | 662 | case CMD_BLOCK_MODE: |
@@ -627,6 +682,21 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
627 | block = CMD_BLOCK_BAR; | 682 | block = CMD_BLOCK_BAR; |
628 | break; | 683 | break; |
629 | 684 | ||
685 | case CMD_BLOCK_COMMANDS: | ||
686 | sway_log(L_DEBUG, "End of commands block"); | ||
687 | block = CMD_BLOCK_END; | ||
688 | break; | ||
689 | |||
690 | case CMD_BLOCK_IPC: | ||
691 | sway_log(L_DEBUG, "End of IPC block"); | ||
692 | block = CMD_BLOCK_END; | ||
693 | break; | ||
694 | |||
695 | case CMD_BLOCK_IPC_EVENTS: | ||
696 | sway_log(L_DEBUG, "End of IPC events block"); | ||
697 | block = CMD_BLOCK_IPC; | ||
698 | break; | ||
699 | |||
630 | case CMD_BLOCK_END: | 700 | case CMD_BLOCK_END: |
631 | sway_log(L_ERROR, "Unmatched }"); | 701 | sway_log(L_ERROR, "Unmatched }"); |
632 | break; | 702 | break; |
diff --git a/sway/extensions.c b/sway/extensions.c index 60cd8d41..96c7e60d 100644 --- a/sway/extensions.c +++ b/sway/extensions.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "sway/layout.h" | 7 | #include "sway/layout.h" |
8 | #include "sway/input_state.h" | 8 | #include "sway/input_state.h" |
9 | #include "sway/extensions.h" | 9 | #include "sway/extensions.h" |
10 | #include "sway/security.h" | ||
10 | #include "sway/ipc-server.h" | 11 | #include "sway/ipc-server.h" |
11 | #include "log.h" | 12 | #include "log.h" |
12 | 13 | ||
@@ -68,6 +69,12 @@ void lock_surface_destructor(struct wl_resource *resource) { | |||
68 | 69 | ||
69 | static void set_background(struct wl_client *client, struct wl_resource *resource, | 70 | static void set_background(struct wl_client *client, struct wl_resource *resource, |
70 | struct wl_resource *_output, struct wl_resource *surface) { | 71 | struct wl_resource *_output, struct wl_resource *surface) { |
72 | pid_t pid; | ||
73 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
74 | if (!(get_feature_policy(pid) & FEATURE_BACKGROUND)) { | ||
75 | sway_log(L_INFO, "Denying background feature to %d", pid); | ||
76 | return; | ||
77 | } | ||
71 | wlc_handle output = wlc_handle_from_wl_output_resource(_output); | 78 | wlc_handle output = wlc_handle_from_wl_output_resource(_output); |
72 | if (!output) { | 79 | if (!output) { |
73 | return; | 80 | return; |
@@ -86,6 +93,12 @@ static void set_background(struct wl_client *client, struct wl_resource *resourc | |||
86 | 93 | ||
87 | static void set_panel(struct wl_client *client, struct wl_resource *resource, | 94 | static void set_panel(struct wl_client *client, struct wl_resource *resource, |
88 | struct wl_resource *_output, struct wl_resource *surface) { | 95 | struct wl_resource *_output, struct wl_resource *surface) { |
96 | pid_t pid; | ||
97 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
98 | if (!(get_feature_policy(pid) & FEATURE_PANEL)) { | ||
99 | sway_log(L_INFO, "Denying panel feature to %d", pid); | ||
100 | return; | ||
101 | } | ||
89 | wlc_handle output = wlc_handle_from_wl_output_resource(_output); | 102 | wlc_handle output = wlc_handle_from_wl_output_resource(_output); |
90 | if (!output) { | 103 | if (!output) { |
91 | return; | 104 | return; |
@@ -111,6 +124,12 @@ static void desktop_unlock(struct wl_client *client, struct wl_resource *resourc | |||
111 | 124 | ||
112 | static void set_lock_surface(struct wl_client *client, struct wl_resource *resource, | 125 | static void set_lock_surface(struct wl_client *client, struct wl_resource *resource, |
113 | struct wl_resource *_output, struct wl_resource *surface) { | 126 | struct wl_resource *_output, struct wl_resource *surface) { |
127 | pid_t pid; | ||
128 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
129 | if (!(get_feature_policy(pid) & FEATURE_LOCK)) { | ||
130 | sway_log(L_INFO, "Denying lock feature to %d", pid); | ||
131 | return; | ||
132 | } | ||
114 | swayc_t *output = swayc_by_handle(wlc_handle_from_wl_output_resource(_output)); | 133 | swayc_t *output = swayc_by_handle(wlc_handle_from_wl_output_resource(_output)); |
115 | swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface)); | 134 | swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface)); |
116 | sway_log(L_DEBUG, "Setting lock surface to %p", view); | 135 | 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 | |||
155 | } | 174 | } |
156 | 175 | ||
157 | static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) { | 176 | static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) { |
177 | pid_t pid; | ||
178 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
179 | if (!(get_feature_policy(pid) & FEATURE_PANEL)) { | ||
180 | sway_log(L_INFO, "Denying panel feature to %d", pid); | ||
181 | return; | ||
182 | } | ||
158 | struct panel_config *config = find_or_create_panel_config(resource); | 183 | struct panel_config *config = find_or_create_panel_config(resource); |
159 | sway_log(L_DEBUG, "Panel position for wl_resource %p changed %d => %d", resource, config->panel_position, position); | 184 | sway_log(L_DEBUG, "Panel position for wl_resource %p changed %d => %d", resource, config->panel_position, position); |
160 | config->panel_position = position; | 185 | config->panel_position = position; |
diff --git a/sway/handlers.c b/sway/handlers.c index 2235bc8b..ee52ba38 100644 --- a/sway/handlers.c +++ b/sway/handlers.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "sway/criteria.h" | 21 | #include "sway/criteria.h" |
22 | #include "sway/ipc-server.h" | 22 | #include "sway/ipc-server.h" |
23 | #include "sway/input.h" | 23 | #include "sway/input.h" |
24 | #include "sway/security.h" | ||
24 | #include "list.h" | 25 | #include "list.h" |
25 | #include "stringop.h" | 26 | #include "stringop.h" |
26 | #include "log.h" | 27 | #include "log.h" |
@@ -385,7 +386,7 @@ static bool handle_view_created(wlc_handle handle) { | |||
385 | struct criteria *crit = criteria->items[i]; | 386 | struct criteria *crit = criteria->items[i]; |
386 | sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'", | 387 | sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'", |
387 | crit->crit_raw, newview, crit->cmdlist); | 388 | crit->crit_raw, newview, crit->cmdlist); |
388 | struct cmd_results *res = handle_command(crit->cmdlist); | 389 | struct cmd_results *res = handle_command(crit->cmdlist, CONTEXT_CRITERIA); |
389 | if (res->status != CMD_SUCCESS) { | 390 | if (res->status != CMD_SUCCESS) { |
390 | sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); | 391 | sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); |
391 | } | 392 | } |
@@ -516,8 +517,13 @@ static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geo | |||
516 | 517 | ||
517 | static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) { | 518 | static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) { |
518 | swayc_t *c = swayc_by_handle(view); | 519 | swayc_t *c = swayc_by_handle(view); |
520 | pid_t pid = wlc_view_get_pid(view); | ||
519 | switch (state) { | 521 | switch (state) { |
520 | case WLC_BIT_FULLSCREEN: | 522 | case WLC_BIT_FULLSCREEN: |
523 | if (!(get_feature_policy(pid) & FEATURE_FULLSCREEN)) { | ||
524 | sway_log(L_INFO, "Denying fullscreen to %d (%s)", pid, c->name); | ||
525 | break; | ||
526 | } | ||
521 | // i3 just lets it become fullscreen | 527 | // i3 just lets it become fullscreen |
522 | wlc_view_set_state(view, state, toggle); | 528 | wlc_view_set_state(view, state, toggle); |
523 | if (c) { | 529 | if (c) { |
@@ -579,7 +585,7 @@ static void handle_binding_command(struct sway_binding *binding) { | |||
579 | reload = true; | 585 | reload = true; |
580 | } | 586 | } |
581 | 587 | ||
582 | struct cmd_results *res = handle_command(binding->command); | 588 | struct cmd_results *res = handle_command(binding->command, CONTEXT_BINDING); |
583 | if (res->status != CMD_SUCCESS) { | 589 | if (res->status != CMD_SUCCESS) { |
584 | sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); | 590 | sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); |
585 | } | 591 | } |
@@ -719,6 +725,14 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier | |||
719 | } | 725 | } |
720 | 726 | ||
721 | list_free(candidates); | 727 | list_free(candidates); |
728 | |||
729 | swayc_t *focused = get_focused_container(&root_container); | ||
730 | if (focused->type == C_VIEW) { | ||
731 | pid_t pid = wlc_view_get_pid(focused->handle); | ||
732 | if (!(get_feature_policy(pid) & FEATURE_KEYBOARD)) { | ||
733 | return EVENT_HANDLED; | ||
734 | } | ||
735 | } | ||
722 | return EVENT_PASSTHROUGH; | 736 | return EVENT_PASSTHROUGH; |
723 | } | 737 | } |
724 | 738 | ||
@@ -775,6 +789,15 @@ static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct | |||
775 | } | 789 | } |
776 | 790 | ||
777 | pointer_position_set(&new_origin, false); | 791 | pointer_position_set(&new_origin, false); |
792 | |||
793 | swayc_t *focused = get_focused_container(&root_container); | ||
794 | if (focused->type == C_VIEW) { | ||
795 | pid_t pid = wlc_view_get_pid(focused->handle); | ||
796 | if (!(get_feature_policy(pid) & FEATURE_MOUSE)) { | ||
797 | return EVENT_HANDLED; | ||
798 | } | ||
799 | } | ||
800 | |||
778 | return EVENT_PASSTHROUGH; | 801 | return EVENT_PASSTHROUGH; |
779 | } | 802 | } |
780 | 803 | ||
@@ -842,6 +865,12 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w | |||
842 | 865 | ||
843 | // don't change focus or mode if fullscreen | 866 | // don't change focus or mode if fullscreen |
844 | if (swayc_is_fullscreen(focused)) { | 867 | if (swayc_is_fullscreen(focused)) { |
868 | if (focused->type == C_VIEW) { | ||
869 | pid_t pid = wlc_view_get_pid(focused->handle); | ||
870 | if (!(get_feature_policy(pid) & FEATURE_MOUSE)) { | ||
871 | return EVENT_HANDLED; | ||
872 | } | ||
873 | } | ||
845 | return EVENT_PASSTHROUGH; | 874 | return EVENT_PASSTHROUGH; |
846 | } | 875 | } |
847 | 876 | ||
@@ -884,6 +913,13 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w | |||
884 | return EVENT_HANDLED; | 913 | return EVENT_HANDLED; |
885 | } | 914 | } |
886 | 915 | ||
916 | if (focused->type == C_VIEW) { | ||
917 | pid_t pid = wlc_view_get_pid(focused->handle); | ||
918 | if (!(get_feature_policy(pid) & FEATURE_MOUSE)) { | ||
919 | return EVENT_HANDLED; | ||
920 | } | ||
921 | } | ||
922 | |||
887 | // Always send mouse release | 923 | // Always send mouse release |
888 | if (state == WLC_BUTTON_STATE_RELEASED) { | 924 | if (state == WLC_BUTTON_STATE_RELEASED) { |
889 | return EVENT_PASSTHROUGH; | 925 | return EVENT_PASSTHROUGH; |
@@ -900,18 +936,18 @@ bool handle_pointer_scroll(wlc_handle view, uint32_t time, const struct wlc_modi | |||
900 | int y_amount = (int)_amount[1]; | 936 | int y_amount = (int)_amount[1]; |
901 | 937 | ||
902 | if (x_amount > 0 && strcmp(config->floating_scroll_up_cmd, "")) { | 938 | if (x_amount > 0 && strcmp(config->floating_scroll_up_cmd, "")) { |
903 | handle_command(config->floating_scroll_up_cmd); | 939 | handle_command(config->floating_scroll_up_cmd, CONTEXT_BINDING); |
904 | return EVENT_HANDLED; | 940 | return EVENT_HANDLED; |
905 | } else if (x_amount < 0 && strcmp(config->floating_scroll_down_cmd, "")) { | 941 | } else if (x_amount < 0 && strcmp(config->floating_scroll_down_cmd, "")) { |
906 | handle_command(config->floating_scroll_down_cmd); | 942 | handle_command(config->floating_scroll_down_cmd, CONTEXT_BINDING); |
907 | return EVENT_HANDLED; | 943 | return EVENT_HANDLED; |
908 | } | 944 | } |
909 | 945 | ||
910 | if (y_amount > 0 && strcmp(config->floating_scroll_right_cmd, "")) { | 946 | if (y_amount > 0 && strcmp(config->floating_scroll_right_cmd, "")) { |
911 | handle_command(config->floating_scroll_right_cmd); | 947 | handle_command(config->floating_scroll_right_cmd, CONTEXT_BINDING); |
912 | return EVENT_HANDLED; | 948 | return EVENT_HANDLED; |
913 | } else if (y_amount < 0 && strcmp(config->floating_scroll_left_cmd, "")) { | 949 | } else if (y_amount < 0 && strcmp(config->floating_scroll_left_cmd, "")) { |
914 | handle_command(config->floating_scroll_left_cmd); | 950 | handle_command(config->floating_scroll_left_cmd, CONTEXT_BINDING); |
915 | return EVENT_HANDLED; | 951 | return EVENT_HANDLED; |
916 | } | 952 | } |
917 | } | 953 | } |
@@ -924,7 +960,7 @@ static void handle_wlc_ready(void) { | |||
924 | config->active = true; | 960 | config->active = true; |
925 | while (config->cmd_queue->length) { | 961 | while (config->cmd_queue->length) { |
926 | char *line = config->cmd_queue->items[0]; | 962 | char *line = config->cmd_queue->items[0]; |
927 | struct cmd_results *res = handle_command(line); | 963 | struct cmd_results *res = handle_command(line, CONTEXT_CONFIG); |
928 | if (res->status != CMD_SUCCESS) { | 964 | if (res->status != CMD_SUCCESS) { |
929 | sway_log(L_ERROR, "Error on line '%s': %s", line, res->error); | 965 | sway_log(L_ERROR, "Error on line '%s': %s", line, res->error); |
930 | } | 966 | } |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index ebb5ce58..c04c465a 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <libinput.h> | 15 | #include <libinput.h> |
16 | #include "sway/ipc-json.h" | 16 | #include "sway/ipc-json.h" |
17 | #include "sway/ipc-server.h" | 17 | #include "sway/ipc-server.h" |
18 | #include "sway/security.h" | ||
18 | #include "sway/config.h" | 19 | #include "sway/config.h" |
19 | #include "sway/commands.h" | 20 | #include "sway/commands.h" |
20 | #include "sway/input.h" | 21 | #include "sway/input.h" |
@@ -55,8 +56,6 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
55 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data); | 56 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data); |
56 | void ipc_get_outputs_callback(swayc_t *container, void *data); | 57 | void ipc_get_outputs_callback(swayc_t *container, void *data); |
57 | 58 | ||
58 | #define event_mask(ev) (1 << (ev & 0x7F)) | ||
59 | |||
60 | void ipc_init(void) { | 59 | void ipc_init(void) { |
61 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); | 60 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); |
62 | if (ipc_socket == -1) { | 61 | if (ipc_socket == -1) { |
@@ -126,6 +125,17 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
126 | return ipc_sockaddr; | 125 | return ipc_sockaddr; |
127 | } | 126 | } |
128 | 127 | ||
128 | static pid_t get_client_pid(int client_fd) { | ||
129 | struct ucred ucred; | ||
130 | socklen_t len = sizeof(struct ucred); | ||
131 | |||
132 | if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) { | ||
133 | return -1; | ||
134 | } | ||
135 | |||
136 | return ucred.pid; | ||
137 | } | ||
138 | |||
129 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 139 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
130 | (void) fd; (void) data; | 140 | (void) fd; (void) data; |
131 | sway_log(L_DEBUG, "Event on IPC listening socket"); | 141 | sway_log(L_DEBUG, "Event on IPC listening socket"); |
@@ -144,6 +154,15 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
144 | return 0; | 154 | return 0; |
145 | } | 155 | } |
146 | 156 | ||
157 | pid_t pid = get_client_pid(client_fd); | ||
158 | if (!(get_feature_policy(pid) & FEATURE_IPC)) { | ||
159 | sway_log(L_INFO, "Permission to connect to IPC socket denied to %d", pid); | ||
160 | const char *error = "{\"success\": false, \"message\": \"Permission denied\"}"; | ||
161 | write(client_fd, &error, sizeof(error)); | ||
162 | close(client_fd); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
147 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); | 166 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); |
148 | client->payload_length = 0; | 167 | client->payload_length = 0; |
149 | client->fd = client_fd; | 168 | client->fd = client_fd; |
@@ -309,10 +328,15 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
309 | } | 328 | } |
310 | buf[client->payload_length] = '\0'; | 329 | buf[client->payload_length] = '\0'; |
311 | 330 | ||
331 | const char *error_denied = "{ \"success\": false, \"error\": \"Permission denied\" }"; | ||
332 | |||
312 | switch (client->current_command) { | 333 | switch (client->current_command) { |
313 | case IPC_COMMAND: | 334 | case IPC_COMMAND: |
314 | { | 335 | { |
315 | struct cmd_results *results = handle_command(buf); | 336 | if (!(config->ipc_policy & IPC_FEATURE_COMMAND)) { |
337 | goto exit_denied; | ||
338 | } | ||
339 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); | ||
316 | const char *json = cmd_results_to_json(results); | 340 | const char *json = cmd_results_to_json(results); |
317 | char reply[256]; | 341 | char reply[256]; |
318 | int length = snprintf(reply, sizeof(reply), "%s", json); | 342 | int length = snprintf(reply, sizeof(reply), "%s", json); |
@@ -343,10 +367,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
343 | client->subscribed_events |= event_mask(IPC_EVENT_WINDOW); | 367 | client->subscribed_events |= event_mask(IPC_EVENT_WINDOW); |
344 | } else if (strcmp(event_type, "modifier") == 0) { | 368 | } else if (strcmp(event_type, "modifier") == 0) { |
345 | client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER); | 369 | client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER); |
346 | #if SWAY_BINDING_EVENT | ||
347 | } else if (strcmp(event_type, "binding") == 0) { | 370 | } else if (strcmp(event_type, "binding") == 0) { |
348 | client->subscribed_events |= event_mask(IPC_EVENT_BINDING); | 371 | client->subscribed_events |= event_mask(IPC_EVENT_BINDING); |
349 | #endif | ||
350 | } else { | 372 | } else { |
351 | ipc_send_reply(client, "{\"success\": false}", 18); | 373 | ipc_send_reply(client, "{\"success\": false}", 18); |
352 | json_object_put(request); | 374 | json_object_put(request); |
@@ -363,6 +385,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
363 | 385 | ||
364 | case IPC_GET_WORKSPACES: | 386 | case IPC_GET_WORKSPACES: |
365 | { | 387 | { |
388 | if (!(config->ipc_policy & IPC_FEATURE_GET_WORKSPACES)) { | ||
389 | goto exit_denied; | ||
390 | } | ||
366 | json_object *workspaces = json_object_new_array(); | 391 | json_object *workspaces = json_object_new_array(); |
367 | container_map(&root_container, ipc_get_workspaces_callback, workspaces); | 392 | container_map(&root_container, ipc_get_workspaces_callback, workspaces); |
368 | const char *json_string = json_object_to_json_string(workspaces); | 393 | const char *json_string = json_object_to_json_string(workspaces); |
@@ -373,6 +398,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
373 | 398 | ||
374 | case IPC_GET_INPUTS: | 399 | case IPC_GET_INPUTS: |
375 | { | 400 | { |
401 | if (!(config->ipc_policy & IPC_FEATURE_GET_INPUTS)) { | ||
402 | goto exit_denied; | ||
403 | } | ||
376 | json_object *inputs = json_object_new_array(); | 404 | json_object *inputs = json_object_new_array(); |
377 | if (input_devices) { | 405 | if (input_devices) { |
378 | for(int i=0; i<input_devices->length; i++) { | 406 | for(int i=0; i<input_devices->length; i++) { |
@@ -392,6 +420,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
392 | 420 | ||
393 | case IPC_GET_OUTPUTS: | 421 | case IPC_GET_OUTPUTS: |
394 | { | 422 | { |
423 | if (!(config->ipc_policy & IPC_FEATURE_GET_OUTPUTS)) { | ||
424 | goto exit_denied; | ||
425 | } | ||
395 | json_object *outputs = json_object_new_array(); | 426 | json_object *outputs = json_object_new_array(); |
396 | container_map(&root_container, ipc_get_outputs_callback, outputs); | 427 | container_map(&root_container, ipc_get_outputs_callback, outputs); |
397 | const char *json_string = json_object_to_json_string(outputs); | 428 | const char *json_string = json_object_to_json_string(outputs); |
@@ -402,6 +433,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
402 | 433 | ||
403 | case IPC_GET_TREE: | 434 | case IPC_GET_TREE: |
404 | { | 435 | { |
436 | if (!(config->ipc_policy & IPC_FEATURE_GET_TREE)) { | ||
437 | goto exit_denied; | ||
438 | } | ||
405 | json_object *tree = ipc_json_describe_container_recursive(&root_container); | 439 | json_object *tree = ipc_json_describe_container_recursive(&root_container); |
406 | const char *json_string = json_object_to_json_string(tree); | 440 | const char *json_string = json_object_to_json_string(tree); |
407 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 441 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); |
@@ -462,6 +496,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
462 | 496 | ||
463 | case IPC_GET_BAR_CONFIG: | 497 | case IPC_GET_BAR_CONFIG: |
464 | { | 498 | { |
499 | if (!(config->ipc_policy & IPC_FEATURE_GET_BAR_CONFIG)) { | ||
500 | goto exit_denied; | ||
501 | } | ||
465 | if (!buf[0]) { | 502 | if (!buf[0]) { |
466 | // Send list of configured bar IDs | 503 | // Send list of configured bar IDs |
467 | json_object *bars = json_object_new_array(); | 504 | json_object *bars = json_object_new_array(); |
@@ -502,6 +539,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
502 | goto exit_cleanup; | 539 | goto exit_cleanup; |
503 | } | 540 | } |
504 | 541 | ||
542 | exit_denied: | ||
543 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); | ||
544 | |||
505 | exit_cleanup: | 545 | exit_cleanup: |
506 | client->payload_length = 0; | 546 | client->payload_length = 0; |
507 | free(buf); | 547 | free(buf); |
@@ -566,6 +606,9 @@ void ipc_send_event(const char *json_string, enum ipc_command_type event) { | |||
566 | } | 606 | } |
567 | 607 | ||
568 | void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { | 608 | void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { |
609 | if (!(config->ipc_policy & IPC_FEATURE_EVENT_WORKSPACE)) { | ||
610 | return; | ||
611 | } | ||
569 | sway_log(L_DEBUG, "Sending workspace::%s event", change); | 612 | sway_log(L_DEBUG, "Sending workspace::%s event", change); |
570 | json_object *obj = json_object_new_object(); | 613 | json_object *obj = json_object_new_object(); |
571 | json_object_object_add(obj, "change", json_object_new_string(change)); | 614 | 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) { | |||
590 | } | 633 | } |
591 | 634 | ||
592 | void ipc_event_window(swayc_t *window, const char *change) { | 635 | void ipc_event_window(swayc_t *window, const char *change) { |
636 | if (!(config->ipc_policy & IPC_FEATURE_EVENT_WINDOW)) { | ||
637 | return; | ||
638 | } | ||
593 | sway_log(L_DEBUG, "Sending window::%s event", change); | 639 | sway_log(L_DEBUG, "Sending window::%s event", change); |
594 | json_object *obj = json_object_new_object(); | 640 | json_object *obj = json_object_new_object(); |
595 | json_object_object_add(obj, "change", json_object_new_string(change)); | 641 | json_object_object_add(obj, "change", json_object_new_string(change)); |
@@ -615,6 +661,9 @@ void ipc_event_barconfig_update(struct bar_config *bar) { | |||
615 | } | 661 | } |
616 | 662 | ||
617 | void ipc_event_mode(const char *mode) { | 663 | void ipc_event_mode(const char *mode) { |
664 | if (!(config->ipc_policy & IPC_FEATURE_EVENT_MODE)) { | ||
665 | return; | ||
666 | } | ||
618 | sway_log(L_DEBUG, "Sending mode::%s event", mode); | 667 | sway_log(L_DEBUG, "Sending mode::%s event", mode); |
619 | json_object *obj = json_object_new_object(); | 668 | json_object *obj = json_object_new_object(); |
620 | json_object_object_add(obj, "change", json_object_new_string(mode)); | 669 | 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) { | |||
639 | json_object_put(obj); // free | 688 | json_object_put(obj); // free |
640 | } | 689 | } |
641 | 690 | ||
642 | #if SWAY_BINDING_EVENT | ||
643 | static void ipc_event_binding(json_object *sb_obj) { | 691 | static void ipc_event_binding(json_object *sb_obj) { |
692 | if (!(config->ipc_policy & IPC_FEATURE_EVENT_BINDING)) { | ||
693 | return; | ||
694 | } | ||
644 | sway_log(L_DEBUG, "Sending binding::run event"); | 695 | sway_log(L_DEBUG, "Sending binding::run event"); |
645 | json_object *obj = json_object_new_object(); | 696 | json_object *obj = json_object_new_object(); |
646 | json_object_object_add(obj, "change", json_object_new_string("run")); | 697 | json_object_object_add(obj, "change", json_object_new_string("run")); |
@@ -651,10 +702,8 @@ static void ipc_event_binding(json_object *sb_obj) { | |||
651 | 702 | ||
652 | json_object_put(obj); // free | 703 | json_object_put(obj); // free |
653 | } | 704 | } |
654 | #endif | ||
655 | 705 | ||
656 | void ipc_event_binding_keyboard(struct sway_binding *sb) { | 706 | void ipc_event_binding_keyboard(struct sway_binding *sb) { |
657 | #if SWAY_BINDING_EVENT | ||
658 | json_object *sb_obj = json_object_new_object(); | 707 | json_object *sb_obj = json_object_new_object(); |
659 | json_object_object_add(sb_obj, "command", json_object_new_string(sb->command)); | 708 | json_object_object_add(sb_obj, "command", json_object_new_string(sb->command)); |
660 | 709 | ||
@@ -705,5 +754,4 @@ void ipc_event_binding_keyboard(struct sway_binding *sb) { | |||
705 | json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard")); | 754 | json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard")); |
706 | 755 | ||
707 | ipc_event_binding(sb_obj); | 756 | ipc_event_binding(sb_obj); |
708 | #endif | ||
709 | } | 757 | } |
diff --git a/sway/main.c b/sway/main.c index a040cec9..73c4b5f2 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -4,13 +4,16 @@ | |||
4 | #include <wlc/wlc.h> | 4 | #include <wlc/wlc.h> |
5 | #include <sys/wait.h> | 5 | #include <sys/wait.h> |
6 | #include <sys/types.h> | 6 | #include <sys/types.h> |
7 | #include <sys/stat.h> | ||
7 | #include <sys/un.h> | 8 | #include <sys/un.h> |
8 | #include <signal.h> | 9 | #include <signal.h> |
9 | #include <unistd.h> | 10 | #include <unistd.h> |
10 | #include <getopt.h> | 11 | #include <getopt.h> |
12 | #include <sys/capability.h> | ||
11 | #include "sway/extensions.h" | 13 | #include "sway/extensions.h" |
12 | #include "sway/layout.h" | 14 | #include "sway/layout.h" |
13 | #include "sway/config.h" | 15 | #include "sway/config.h" |
16 | #include "sway/security.h" | ||
14 | #include "sway/handlers.h" | 17 | #include "sway/handlers.h" |
15 | #include "sway/input.h" | 18 | #include "sway/input.h" |
16 | #include "sway/ipc-server.h" | 19 | #include "sway/ipc-server.h" |
@@ -142,6 +145,63 @@ static void log_kernel() { | |||
142 | fclose(f); | 145 | fclose(f); |
143 | } | 146 | } |
144 | 147 | ||
148 | static void security_sanity_check() { | ||
149 | // TODO: Notify users visually if this has issues | ||
150 | struct stat s; | ||
151 | if (stat("/proc", &s)) { | ||
152 | sway_log(L_ERROR, | ||
153 | "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); | ||
154 | } | ||
155 | cap_flag_value_t v; | ||
156 | cap_t cap = cap_get_proc(); | ||
157 | if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { | ||
158 | sway_log(L_ERROR, | ||
159 | "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); | ||
160 | } | ||
161 | if (cap) { | ||
162 | cap_free(cap); | ||
163 | } | ||
164 | if (!stat(SYSCONFDIR "/sway", &s)) { | ||
165 | if (s.st_uid != 0 || s.st_gid != 0 | ||
166 | || (s.st_mode & S_IWGRP) || (s.st_mode & S_IWOTH)) { | ||
167 | sway_log(L_ERROR, | ||
168 | "!! DANGER !! " SYSCONFDIR "/sway is not secure! It should be owned by root and set to 0755 at the minimum"); | ||
169 | } | ||
170 | } | ||
171 | struct { | ||
172 | char *command; | ||
173 | enum command_context context; | ||
174 | bool checked; | ||
175 | } expected[] = { | ||
176 | { "reload", CONTEXT_BINDING, false }, | ||
177 | { "restart", CONTEXT_BINDING, false }, | ||
178 | { "permit", CONTEXT_CONFIG, false }, | ||
179 | { "reject", CONTEXT_CONFIG, false }, | ||
180 | { "ipc", CONTEXT_CONFIG, false }, | ||
181 | }; | ||
182 | int expected_len = 5; | ||
183 | for (int i = 0; i < config->command_policies->length; ++i) { | ||
184 | struct command_policy *policy = config->command_policies->items[i]; | ||
185 | for (int j = 0; j < expected_len; ++j) { | ||
186 | if (strcmp(expected[j].command, policy->command) == 0) { | ||
187 | expected[j].checked = true; | ||
188 | if (expected[j].context != policy->context) { | ||
189 | sway_log(L_ERROR, | ||
190 | "!! DANGER !! Command security policy for %s should be set to %s", | ||
191 | expected[j].command, command_policy_str(expected[j].context)); | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | for (int j = 0; j < expected_len; ++j) { | ||
197 | if (!expected[j].checked) { | ||
198 | sway_log(L_ERROR, | ||
199 | "!! DANGER !! Command security policy for %s should be set to %s", | ||
200 | expected[j].command, command_policy_str(expected[j].context)); | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | |||
145 | int main(int argc, char **argv) { | 205 | int main(int argc, char **argv) { |
146 | static int verbose = 0, debug = 0, validate = 0; | 206 | static int verbose = 0, debug = 0, validate = 0; |
147 | 207 | ||
@@ -170,6 +230,10 @@ int main(int argc, char **argv) { | |||
170 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | 230 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" |
171 | "\n"; | 231 | "\n"; |
172 | 232 | ||
233 | // Security: | ||
234 | unsetenv("LD_PRELOAD"); | ||
235 | setenv("LD_LIBRARY_PATH", _LD_LIBRARY_PATH, 1); | ||
236 | |||
173 | int c; | 237 | int c; |
174 | while (1) { | 238 | while (1) { |
175 | int option_index = 0; | 239 | int option_index = 0; |
@@ -298,6 +362,8 @@ int main(int argc, char **argv) { | |||
298 | free(config_path); | 362 | free(config_path); |
299 | } | 363 | } |
300 | 364 | ||
365 | security_sanity_check(); | ||
366 | |||
301 | if (!terminate_request) { | 367 | if (!terminate_request) { |
302 | wlc_run(); | 368 | wlc_run(); |
303 | } | 369 | } |
diff --git a/sway/security.c b/sway/security.c new file mode 100644 index 00000000..f16fdd1f --- /dev/null +++ b/sway/security.c | |||
@@ -0,0 +1,94 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <stdio.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/security.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | struct feature_policy *alloc_feature_policy(const char *program) { | ||
8 | uint32_t default_policy = 0; | ||
9 | for (int i = 0; i < config->feature_policies->length; ++i) { | ||
10 | struct feature_policy *policy = config->feature_policies->items[i]; | ||
11 | if (strcmp(policy->program, "*") == 0) { | ||
12 | default_policy = policy->features; | ||
13 | break; | ||
14 | } | ||
15 | } | ||
16 | |||
17 | struct feature_policy *policy = malloc(sizeof(struct feature_policy)); | ||
18 | policy->program = strdup(program); | ||
19 | policy->features = default_policy; | ||
20 | return policy; | ||
21 | } | ||
22 | |||
23 | struct command_policy *alloc_command_policy(const char *command) { | ||
24 | struct command_policy *policy = malloc(sizeof(struct command_policy)); | ||
25 | policy->command = strdup(command); | ||
26 | policy->context = 0; | ||
27 | return policy; | ||
28 | } | ||
29 | |||
30 | enum secure_feature get_feature_policy(pid_t pid) { | ||
31 | const char *fmt = "/proc/%d/exe"; | ||
32 | int pathlen = snprintf(NULL, 0, fmt, pid); | ||
33 | char *path = malloc(pathlen + 1); | ||
34 | snprintf(path, pathlen + 1, fmt, pid); | ||
35 | static char link[2048]; | ||
36 | |||
37 | uint32_t default_policy = 0; | ||
38 | |||
39 | ssize_t len = readlink(path, link, sizeof(link)); | ||
40 | if (len < 0) { | ||
41 | sway_log(L_INFO, | ||
42 | "WARNING: unable to read %s for security check. Using default policy.", | ||
43 | path); | ||
44 | strcpy(link, "*"); | ||
45 | } else { | ||
46 | link[len] = '\0'; | ||
47 | } | ||
48 | free(path); | ||
49 | |||
50 | for (int i = 0; i < config->feature_policies->length; ++i) { | ||
51 | struct feature_policy *policy = config->feature_policies->items[i]; | ||
52 | if (strcmp(policy->program, "*") == 0) { | ||
53 | default_policy = policy->features; | ||
54 | } | ||
55 | if (strcmp(policy->program, link) == 0) { | ||
56 | return policy->features; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | return default_policy; | ||
61 | } | ||
62 | |||
63 | enum command_context get_command_policy(const char *cmd) { | ||
64 | uint32_t default_policy = 0; | ||
65 | |||
66 | for (int i = 0; i < config->command_policies->length; ++i) { | ||
67 | struct command_policy *policy = config->command_policies->items[i]; | ||
68 | if (strcmp(policy->command, "*") == 0) { | ||
69 | default_policy = policy->context; | ||
70 | } | ||
71 | if (strcmp(policy->command, cmd) == 0) { | ||
72 | return policy->context; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | return default_policy; | ||
77 | } | ||
78 | |||
79 | const char *command_policy_str(enum command_context context) { | ||
80 | switch (context) { | ||
81 | case CONTEXT_ALL: | ||
82 | return "all"; | ||
83 | case CONTEXT_CONFIG: | ||
84 | return "config"; | ||
85 | case CONTEXT_BINDING: | ||
86 | return "binding"; | ||
87 | case CONTEXT_IPC: | ||
88 | return "IPC"; | ||
89 | case CONTEXT_CRITERIA: | ||
90 | return "criteria"; | ||
91 | default: | ||
92 | return "unknown"; | ||
93 | } | ||
94 | } | ||
diff --git a/sway/sway-security.7.txt b/sway/sway-security.7.txt new file mode 100644 index 00000000..9a2581b1 --- /dev/null +++ b/sway/sway-security.7.txt | |||
@@ -0,0 +1,250 @@ | |||
1 | ///// | ||
2 | vim:set ts=4 sw=4 tw=82 noet: | ||
3 | ///// | ||
4 | sway-security (7) | ||
5 | ================= | ||
6 | |||
7 | Name | ||
8 | ---- | ||
9 | sway-security - Guidelines for securing your sway install | ||
10 | |||
11 | Security Overview | ||
12 | ----------------- | ||
13 | |||
14 | **Sway is NOT secure**. We are working on it but do not trust that we have it all | ||
15 | figured out yet. The following man page is provisional. | ||
16 | |||
17 | Securing sway requires careful configuration of your environment, the sort that's | ||
18 | usually best suited to a distro maintainer who wants to ship a secure sway | ||
19 | environment in their distro. Sway provides a number of means of securing it but | ||
20 | you must make a few changes external to sway first. | ||
21 | |||
22 | Configuration security | ||
23 | ---------------------- | ||
24 | |||
25 | Many of Sway's security features are configurable. It's important that a possibly | ||
26 | untrusted program is not able to edit this. Security rules are kept in | ||
27 | _/etc/sway/config.d/security_ (usually), which should only be writable by root. | ||
28 | However, configuration of security rules is not limited to this file - any config | ||
29 | file that sway loads (including i.e. _~/.config/sway/config_) should not be editable | ||
30 | by the user you intend to run programs as. One simple strategy is to use | ||
31 | /etc/sway/config instead of a config file in your home directory, but that doesn't | ||
32 | work well for multi-user systems. A more robust strategy is to run untrusted | ||
33 | programs as another user, or in a sandbox. Configuring this is up to you. | ||
34 | |||
35 | Note that _/etc/sway/config.d/*_ must be included explicitly from your config file. | ||
36 | This is done by default in /etc/sway/config but you must check your own config if | ||
37 | you choose to place it in other locations. | ||
38 | |||
39 | Environment security | ||
40 | -------------------- | ||
41 | |||
42 | LD_PRELOAD is a mechanism designed to ruin the security of your system. There are | ||
43 | a number of strategies for dealing with this but they all suck a little. In order | ||
44 | of most practical to least practical: | ||
45 | |||
46 | 1. Only run important programs via exec. Sway's exec command will ensure that | ||
47 | LD_PRELOAD is unset when running programs. | ||
48 | |||
49 | 2. Remove LD_PRELOAD support from your dynamic loader (requires patching libc). | ||
50 | This may break programs that rely on LD_PRELOAD for legitimate functionality, | ||
51 | but this is the most effective solution. | ||
52 | |||
53 | 3. Use static linking for important programs. Of course statically linked programs | ||
54 | are unaffected by the dynamic linking security dumpster fire. | ||
55 | |||
56 | Note that should you choose method 1, you MUST ensure that sway itself isn't | ||
57 | compromised by LD_PRELOAD. It probably isn't, but you can be sure by setting | ||
58 | /usr/bin/sway to a+s (setuid), which will instruct the dynamic linker not to | ||
59 | permit LD_PRELOAD for it (and will also run it as root, which sway will shortly | ||
60 | drop). You could also statically link sway itself. | ||
61 | |||
62 | Note that LD_LIBRARY_PATH has all of the same problems, and all of the same | ||
63 | solutions. | ||
64 | |||
65 | Read your log | ||
66 | ------------- | ||
67 | |||
68 | Sway does sanity checks and prints big red warnings to stderr if they fail. Read | ||
69 | them. | ||
70 | |||
71 | Feature policies | ||
72 | ---------------- | ||
73 | |||
74 | Certain sway features are security sensitive and may be configured with security | ||
75 | policies. These features are: | ||
76 | |||
77 | **background**:: | ||
78 | Permission for a program to become the background. | ||
79 | |||
80 | **fullscreen**:: | ||
81 | Permission to become fullscreen. Note that users can always make a window | ||
82 | fullscreen themselves with the fullscreen command. | ||
83 | |||
84 | **ipc**:: | ||
85 | Permission to connect to sway's IPC socket. | ||
86 | |||
87 | **keyboard**:: | ||
88 | Permission to receive keyboard events (only while they are focused). | ||
89 | |||
90 | **lock**:: | ||
91 | Permission for a program to act as a screen locker. This involves becoming | ||
92 | fullscreen (on all outputs) and receiving _all_ keyboard and mouse input for | ||
93 | the duration of the process. | ||
94 | |||
95 | **mouse**:: | ||
96 | Permission to receive mouse events (only while the mouse is over them). | ||
97 | |||
98 | **panel**:: | ||
99 | Permission for a program to stick its windows to the sides of the screen. | ||
100 | |||
101 | **screenshot**:: | ||
102 | Permission to take screenshots or record the screen. | ||
103 | |||
104 | By default, all programs are granted **fullscreen**, **keyboard**, **mouse**, and | ||
105 | **ipc** permissions. You can use the following config commands to control a | ||
106 | program's access: | ||
107 | |||
108 | **permit** <executable> <features...>:: | ||
109 | Permits <executable> to use <features> (each feature seperated by a space). | ||
110 | <executable> may be * to affect the default policy, or the full path to the | ||
111 | executable file. | ||
112 | |||
113 | **reject** <executable> <features...>:: | ||
114 | Disallows <executable> from using <features> (each feature seperated by a space). | ||
115 | <executable> may be * to affect the default policy, or the full path to the | ||
116 | executable file. | ||
117 | |||
118 | Note that policy enforcement requires procfs to be mounted at /proc and the sway | ||
119 | process to be able to access _/proc/[pid]/exe_ (see **procfs(5)** for details on | ||
120 | this access - setcap cap_sys_ptrace=eip /usr/bin/sway should do the trick). If | ||
121 | sway is unable to read _/proc/[pid]/exe_, it will apply the default policy. | ||
122 | |||
123 | To work correctly, sway's own programs require the following permissions: | ||
124 | |||
125 | - swaybg: background | ||
126 | - swaylock: lock, keyboard | ||
127 | - swaybar: panel, mouse, ipc | ||
128 | - swaygrab: screenshot, ipc | ||
129 | |||
130 | When you first declare a policy for an executable, it will inherit the default | ||
131 | policy. Further changes to the default policy will not retroactively affect which | ||
132 | permissions an earlier policy inherits. You must explicitly reject any features | ||
133 | from the default policy that you do not want an executable to receive permission | ||
134 | for. | ||
135 | |||
136 | Command policies | ||
137 | ---------------- | ||
138 | |||
139 | You can also control the context from which a command may execute. The different | ||
140 | contexts you can control are: | ||
141 | |||
142 | **config**:: | ||
143 | Can be run from your config file. | ||
144 | |||
145 | **binding**:: | ||
146 | Can be run from bindsym or bindcode commands. | ||
147 | |||
148 | **ipc**:: | ||
149 | Can be run by IPC clients. | ||
150 | |||
151 | **criteria**:: | ||
152 | Can be run when evaluating window criteria. | ||
153 | |||
154 | **all**:: | ||
155 | Shorthand for granting permission in all contexts. | ||
156 | |||
157 | By default a command is allowed to execute in any context. To configure this, open | ||
158 | a commands block and fill it with policies: | ||
159 | |||
160 | commands { | ||
161 | <name> <contexts...> | ||
162 | ... | ||
163 | } | ||
164 | |||
165 | For example, you could do this to limit the use of the focus command to just | ||
166 | binding and critiera: | ||
167 | |||
168 | commands { | ||
169 | focus binding criteria | ||
170 | } | ||
171 | |||
172 | Setting a command policy overwrites any previous policy that was in place. | ||
173 | |||
174 | IPC policies | ||
175 | ------------ | ||
176 | |||
177 | You may whitelist IPC access like so: | ||
178 | |||
179 | permit /usr/bin/swaybar ipc | ||
180 | permit /usr/bin/swaygrab ipc | ||
181 | # etc | ||
182 | |||
183 | Note that it's suggested you do not enable swaymsg to access IPC if you intend to | ||
184 | secure your IPC socket, because any program could just run swaymsg itself instead | ||
185 | of connecting to IPC directly. | ||
186 | |||
187 | You can also configure which features of IPC are available with an IPC block: | ||
188 | |||
189 | ipc { | ||
190 | ... | ||
191 | } | ||
192 | |||
193 | The following commands are available within this block: | ||
194 | |||
195 | **bar-config** <enabled|disabled>:: | ||
196 | Controls GET_BAR_CONFIG (required for swaybar to work at all). | ||
197 | |||
198 | **command** <enabled|disabled>:: | ||
199 | Controls executing sway commands via IPC. | ||
200 | |||
201 | **inputs** <enabled|disabled>:: | ||
202 | Controls GET_INPUTS (input device information). | ||
203 | |||
204 | **marks** <enabled|disabled>:: | ||
205 | Controls GET_MARKS. | ||
206 | |||
207 | **outputs** <enabled|disabled>:: | ||
208 | Controls GET_OUTPUTS. | ||
209 | |||
210 | **tree** <enabled|disabled>:: | ||
211 | Controls GET_TREE. | ||
212 | |||
213 | **workspaces** <enabled|disabled>:: | ||
214 | Controls GET_WORKSPACES. | ||
215 | |||
216 | You can also control which IPC events can be raised with an events block: | ||
217 | |||
218 | ipc { | ||
219 | events { | ||
220 | ... | ||
221 | } | ||
222 | } | ||
223 | |||
224 | The following commands are vaild within an ipc events block: | ||
225 | |||
226 | **binding** <enabled|disabled>:: | ||
227 | Controls keybinding notifications (disabled by default). | ||
228 | |||
229 | **input** <enabled|disabled>:: | ||
230 | Controls input device hotplugging notifications. | ||
231 | |||
232 | **mode** <enabled|disabled>:: | ||
233 | Controls output hotplugging notifications. | ||
234 | |||
235 | **output** <enabled|disabled>:: | ||
236 | Controls output hotplugging notifications. | ||
237 | |||
238 | **window** <enabled|disabled>:: | ||
239 | Controls window event notifications. | ||
240 | |||
241 | **workspace** <enabled|disabled>:: | ||
242 | Controls workspace notifications. | ||
243 | |||
244 | Disabling some of these may cause swaybar to behave incorrectly. | ||
245 | |||
246 | Authors | ||
247 | ------- | ||
248 | Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open | ||
249 | source contributors. For more information about sway development, see | ||
250 | <https://github.com/SirCmpwn/sway>. | ||
diff --git a/swaylock/CMakeLists.txt b/swaylock/CMakeLists.txt index febbd1af..4aec6424 100644 --- a/swaylock/CMakeLists.txt +++ b/swaylock/CMakeLists.txt | |||
@@ -42,7 +42,7 @@ install( | |||
42 | 42 | ||
43 | install( | 43 | install( |
44 | FILES ${CMAKE_CURRENT_SOURCE_DIR}/pam/swaylock | 44 | FILES ${CMAKE_CURRENT_SOURCE_DIR}/pam/swaylock |
45 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/pam.d/ | 45 | DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d/ |
46 | COMPONENT data | 46 | COMPONENT data |
47 | ) | 47 | ) |
48 | 48 | ||