diff options
-rw-r--r-- | include/sway/config.h | 17 | ||||
-rw-r--r-- | include/sway/security.h | 6 | ||||
-rw-r--r-- | security.d/00-defaults.in (renamed from security.in) | 38 | ||||
-rw-r--r-- | sway/CMakeLists.txt | 2 | ||||
-rw-r--r-- | sway/commands.c | 2 | ||||
-rw-r--r-- | sway/commands/commands.c | 8 | ||||
-rw-r--r-- | sway/commands/ipc.c | 28 | ||||
-rw-r--r-- | sway/commands/permit.c | 21 | ||||
-rw-r--r-- | sway/config.c | 54 | ||||
-rw-r--r-- | sway/ipc-server.c | 72 | ||||
-rw-r--r-- | sway/main.c | 7 | ||||
-rw-r--r-- | sway/security.c | 54 | ||||
-rw-r--r-- | sway/sway-security.7.txt | 34 |
13 files changed, 228 insertions, 115 deletions
diff --git a/include/sway/config.h b/include/sway/config.h index febde63d..d77fbd51 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -203,7 +203,6 @@ enum secure_feature { | |||
203 | FEATURE_FULLSCREEN = 16, | 203 | FEATURE_FULLSCREEN = 16, |
204 | FEATURE_KEYBOARD = 32, | 204 | FEATURE_KEYBOARD = 32, |
205 | FEATURE_MOUSE = 64, | 205 | FEATURE_MOUSE = 64, |
206 | FEATURE_IPC = 128, | ||
207 | }; | 206 | }; |
208 | 207 | ||
209 | struct feature_policy { | 208 | struct feature_policy { |
@@ -225,7 +224,17 @@ enum ipc_feature { | |||
225 | IPC_FEATURE_EVENT_MODE = 1024, | 224 | IPC_FEATURE_EVENT_MODE = 1024, |
226 | IPC_FEATURE_EVENT_WINDOW = 2048, | 225 | IPC_FEATURE_EVENT_WINDOW = 2048, |
227 | IPC_FEATURE_EVENT_BINDING = 4096, | 226 | IPC_FEATURE_EVENT_BINDING = 4096, |
228 | IPC_FEATURE_EVENT_INPUT = 8192 | 227 | IPC_FEATURE_EVENT_INPUT = 8192, |
228 | |||
229 | IPC_FEATURE_ALL_COMMANDS = 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, | ||
230 | IPC_FEATURE_ALL_EVENTS = 256 | 512 | 1024 | 2048 | 4096 | 8192, | ||
231 | |||
232 | IPC_FEATURE_ALL = IPC_FEATURE_ALL_COMMANDS | IPC_FEATURE_ALL_EVENTS, | ||
233 | }; | ||
234 | |||
235 | struct ipc_policy { | ||
236 | char *program; | ||
237 | uint32_t features; | ||
229 | }; | 238 | }; |
230 | 239 | ||
231 | /** | 240 | /** |
@@ -300,7 +309,7 @@ struct sway_config { | |||
300 | // Security | 309 | // Security |
301 | list_t *command_policies; | 310 | list_t *command_policies; |
302 | list_t *feature_policies; | 311 | list_t *feature_policies; |
303 | uint32_t ipc_policy; | 312 | list_t *ipc_policies; |
304 | }; | 313 | }; |
305 | 314 | ||
306 | void pid_workspace_add(struct pid_workspace *pw); | 315 | void pid_workspace_add(struct pid_workspace *pw); |
@@ -331,6 +340,8 @@ void free_config(struct sway_config *config); | |||
331 | */ | 340 | */ |
332 | char *do_var_replacement(char *str); | 341 | char *do_var_replacement(char *str); |
333 | 342 | ||
343 | struct cmd_results *check_security_config(); | ||
344 | |||
334 | int input_identifier_cmp(const void *item, const void *data); | 345 | int input_identifier_cmp(const void *item, const void *data); |
335 | void merge_input_config(struct input_config *dst, struct input_config *src); | 346 | void merge_input_config(struct input_config *dst, struct input_config *src); |
336 | void apply_input_config(struct input_config *ic, struct libinput_device *dev); | 347 | void apply_input_config(struct input_config *ic, struct libinput_device *dev); |
diff --git a/include/sway/security.h b/include/sway/security.h index 1cc85bee..c3a5cfd4 100644 --- a/include/sway/security.h +++ b/include/sway/security.h | |||
@@ -3,12 +3,14 @@ | |||
3 | #include <unistd.h> | 3 | #include <unistd.h> |
4 | #include "sway/config.h" | 4 | #include "sway/config.h" |
5 | 5 | ||
6 | enum secure_feature get_feature_policy(pid_t pid); | 6 | uint32_t get_feature_policy(pid_t pid); |
7 | enum command_context get_command_policy(const char *cmd); | 7 | uint32_t get_ipc_policy(pid_t pid); |
8 | uint32_t get_command_policy(const char *cmd); | ||
8 | 9 | ||
9 | const char *command_policy_str(enum command_context context); | 10 | const char *command_policy_str(enum command_context context); |
10 | 11 | ||
11 | struct feature_policy *alloc_feature_policy(const char *program); | 12 | struct feature_policy *alloc_feature_policy(const char *program); |
13 | struct ipc_policy *alloc_ipc_policy(const char *program); | ||
12 | struct command_policy *alloc_command_policy(const char *command); | 14 | struct command_policy *alloc_command_policy(const char *command); |
13 | 15 | ||
14 | #endif | 16 | #endif |
diff --git a/security.in b/security.d/00-defaults.in index 16897ade..34831c65 100644 --- a/security.in +++ b/security.d/00-defaults.in | |||
@@ -5,36 +5,42 @@ | |||
5 | # You MUST read this man page if you intend to attempt to secure your sway | 5 | # You MUST read this man page if you intend to attempt to secure your sway |
6 | # installation. | 6 | # installation. |
7 | # | 7 | # |
8 | # This file should live at __SYSCONFDIR__/sway/security and will be | 8 | # DO NOT CHANGE THIS FILE. Override these defaults by writing new files in |
9 | # automatically read by sway. | 9 | # __SYSCONFDIR__/sway/security.d/* |
10 | 10 | ||
11 | # Configures which programs are allowed to use which sway features | 11 | # Configures enabled compositor features for specific programs |
12 | permit * fullscreen keyboard mouse ipc | 12 | permit * fullscreen keyboard mouse |
13 | permit __PREFIX__/bin/swaylock lock | 13 | permit __PREFIX__/bin/swaylock lock |
14 | permit __PREFIX__/bin/swaybar panel | ||
15 | permit __PREFIX__/bin/swaybg background | 14 | permit __PREFIX__/bin/swaybg background |
16 | permit __PREFIX__/bin/swaygrab screenshot | 15 | permit __PREFIX__/bin/swaygrab screenshot |
16 | permit __PREFIX__/bin/swaybar panel | ||
17 | 17 | ||
18 | # Configures which IPC features are enabled | 18 | # Configures enabled IPC features for specific programs |
19 | ipc { | 19 | ipc __PREFIX__/bin/swaymsg { |
20 | command enabled | 20 | * enabled |
21 | |||
22 | events { | ||
23 | * disabled | ||
24 | } | ||
25 | } | ||
26 | |||
27 | ipc __PREFIX__/bin/swaybar { | ||
28 | bar-config enabled | ||
21 | outputs enabled | 29 | outputs enabled |
22 | workspaces enabled | 30 | workspaces enabled |
23 | tree enabled | 31 | command enabled |
24 | marks enabled | ||
25 | bar-config enabled | ||
26 | inputs enabled | ||
27 | 32 | ||
28 | events { | 33 | events { |
29 | workspace enabled | 34 | workspace enabled |
30 | output enabled | ||
31 | mode enabled | 35 | mode enabled |
32 | window enabled | ||
33 | input enabled | ||
34 | binding disabled | ||
35 | } | 36 | } |
36 | } | 37 | } |
37 | 38 | ||
39 | ipc __PREFIX__/bin/swaygrab { | ||
40 | outputs enabled | ||
41 | tree enabled | ||
42 | } | ||
43 | |||
38 | # Limits the contexts from which certain commands are permitted | 44 | # Limits the contexts from which certain commands are permitted |
39 | commands { | 45 | commands { |
40 | * all | 46 | * all |
diff --git a/sway/CMakeLists.txt b/sway/CMakeLists.txt index d5453003..981f8a07 100644 --- a/sway/CMakeLists.txt +++ b/sway/CMakeLists.txt | |||
@@ -91,7 +91,7 @@ function(add_config name source destination) | |||
91 | endfunction() | 91 | endfunction() |
92 | 92 | ||
93 | add_config(config config sway) | 93 | add_config(config config sway) |
94 | add_config(security security sway) | 94 | add_config(00-defaults security.d/00-defaults sway/security.d) |
95 | 95 | ||
96 | add_manpage(sway 1) | 96 | add_manpage(sway 1) |
97 | add_manpage(sway 5) | 97 | add_manpage(sway 5) |
diff --git a/sway/commands.c b/sway/commands.c index c15cb00a..068e8866 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -297,6 +297,7 @@ static struct cmd_handler bar_colors_handlers[] = { | |||
297 | }; | 297 | }; |
298 | 298 | ||
299 | static struct cmd_handler ipc_handlers[] = { | 299 | static struct cmd_handler ipc_handlers[] = { |
300 | { "*", cmd_ipc_cmd }, | ||
300 | { "bar-config", cmd_ipc_cmd }, | 301 | { "bar-config", cmd_ipc_cmd }, |
301 | { "command", cmd_ipc_cmd }, | 302 | { "command", cmd_ipc_cmd }, |
302 | { "events", cmd_ipc_events }, | 303 | { "events", cmd_ipc_events }, |
@@ -308,6 +309,7 @@ static struct cmd_handler ipc_handlers[] = { | |||
308 | }; | 309 | }; |
309 | 310 | ||
310 | static struct cmd_handler ipc_event_handlers[] = { | 311 | static struct cmd_handler ipc_event_handlers[] = { |
312 | { "*", cmd_ipc_event_cmd }, | ||
311 | { "binding", cmd_ipc_event_cmd }, | 313 | { "binding", cmd_ipc_event_cmd }, |
312 | { "input", cmd_ipc_event_cmd }, | 314 | { "input", cmd_ipc_event_cmd }, |
313 | { "mode", cmd_ipc_event_cmd }, | 315 | { "mode", cmd_ipc_event_cmd }, |
diff --git a/sway/commands/commands.c b/sway/commands/commands.c index 8c7ed487..0c64970c 100644 --- a/sway/commands/commands.c +++ b/sway/commands/commands.c | |||
@@ -10,6 +10,9 @@ struct cmd_results *cmd_commands(int argc, char **argv) { | |||
10 | if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) { | 10 | if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) { |
11 | return error; | 11 | return error; |
12 | } | 12 | } |
13 | if ((error = check_security_config())) { | ||
14 | return error; | ||
15 | } | ||
13 | 16 | ||
14 | if (strcmp(argv[0], "{") != 0) { | 17 | if (strcmp(argv[0], "{") != 0) { |
15 | return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration"); | 18 | return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration"); |
@@ -19,10 +22,5 @@ struct cmd_results *cmd_commands(int argc, char **argv) { | |||
19 | return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file."); | 22 | return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file."); |
20 | } | 23 | } |
21 | 24 | ||
22 | if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) { | ||
23 | return cmd_results_new(CMD_INVALID, "permit", | ||
24 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); | ||
25 | } | ||
26 | |||
27 | return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL); | 25 | return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL); |
28 | } | 26 | } |
diff --git a/sway/commands/ipc.c b/sway/commands/ipc.c index 113a975b..8a7b849f 100644 --- a/sway/commands/ipc.c +++ b/sway/commands/ipc.c | |||
@@ -1,18 +1,26 @@ | |||
1 | #include <stdio.h> | 1 | #include <stdio.h> |
2 | #include <string.h> | 2 | #include <string.h> |
3 | #include "sway/security.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 5 | #include "sway/config.h" |
5 | #include "ipc.h" | 6 | #include "ipc.h" |
6 | #include "log.h" | 7 | #include "log.h" |
7 | #include "util.h" | 8 | #include "util.h" |
8 | 9 | ||
10 | static struct ipc_policy *current_policy = NULL; | ||
11 | |||
9 | struct cmd_results *cmd_ipc(int argc, char **argv) { | 12 | struct cmd_results *cmd_ipc(int argc, char **argv) { |
10 | struct cmd_results *error = NULL; | 13 | struct cmd_results *error = NULL; |
11 | if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) { | 14 | if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) { |
15 | return error; | ||
16 | } | ||
17 | if ((error = check_security_config())) { | ||
12 | return error; | 18 | return error; |
13 | } | 19 | } |
14 | 20 | ||
15 | if (config->reading && strcmp("{", argv[0]) != 0) { | 21 | const char *program = argv[0]; |
22 | |||
23 | if (config->reading && strcmp("{", argv[1]) != 0) { | ||
16 | return cmd_results_new(CMD_INVALID, "ipc", | 24 | return cmd_results_new(CMD_INVALID, "ipc", |
17 | "Expected '{' at start of IPC config definition."); | 25 | "Expected '{' at start of IPC config definition."); |
18 | } | 26 | } |
@@ -21,10 +29,8 @@ struct cmd_results *cmd_ipc(int argc, char **argv) { | |||
21 | return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file."); | 29 | return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file."); |
22 | } | 30 | } |
23 | 31 | ||
24 | if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) { | 32 | current_policy = alloc_ipc_policy(program); |
25 | return cmd_results_new(CMD_INVALID, "permit", | 33 | list_add(config->ipc_policies, current_policy); |
26 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); | ||
27 | } | ||
28 | 34 | ||
29 | return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL); | 35 | return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL); |
30 | } | 36 | } |
@@ -67,6 +73,7 @@ struct cmd_results *cmd_ipc_cmd(int argc, char **argv) { | |||
67 | char *name; | 73 | char *name; |
68 | enum ipc_feature type; | 74 | enum ipc_feature type; |
69 | } types[] = { | 75 | } types[] = { |
76 | { "*", IPC_FEATURE_ALL_COMMANDS }, | ||
70 | { "command", IPC_FEATURE_COMMAND }, | 77 | { "command", IPC_FEATURE_COMMAND }, |
71 | { "workspaces", IPC_FEATURE_GET_WORKSPACES }, | 78 | { "workspaces", IPC_FEATURE_GET_WORKSPACES }, |
72 | { "outputs", IPC_FEATURE_GET_OUTPUTS }, | 79 | { "outputs", IPC_FEATURE_GET_OUTPUTS }, |
@@ -86,10 +93,10 @@ struct cmd_results *cmd_ipc_cmd(int argc, char **argv) { | |||
86 | } | 93 | } |
87 | 94 | ||
88 | if (enabled) { | 95 | if (enabled) { |
89 | config->ipc_policy |= type; | 96 | current_policy->features |= type; |
90 | sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]); | 97 | sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]); |
91 | } else { | 98 | } else { |
92 | config->ipc_policy &= ~type; | 99 | current_policy->features &= ~type; |
93 | sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]); | 100 | sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]); |
94 | } | 101 | } |
95 | 102 | ||
@@ -116,6 +123,7 @@ struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) { | |||
116 | char *name; | 123 | char *name; |
117 | enum ipc_feature type; | 124 | enum ipc_feature type; |
118 | } types[] = { | 125 | } types[] = { |
126 | { "*", IPC_FEATURE_ALL_EVENTS }, | ||
119 | { "workspace", IPC_FEATURE_EVENT_WORKSPACE }, | 127 | { "workspace", IPC_FEATURE_EVENT_WORKSPACE }, |
120 | { "output", IPC_FEATURE_EVENT_OUTPUT }, | 128 | { "output", IPC_FEATURE_EVENT_OUTPUT }, |
121 | { "mode", IPC_FEATURE_EVENT_MODE }, | 129 | { "mode", IPC_FEATURE_EVENT_MODE }, |
@@ -134,10 +142,10 @@ struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) { | |||
134 | } | 142 | } |
135 | 143 | ||
136 | if (enabled) { | 144 | if (enabled) { |
137 | config->ipc_policy |= type; | 145 | current_policy->features |= type; |
138 | sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]); | 146 | sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]); |
139 | } else { | 147 | } else { |
140 | config->ipc_policy &= ~type; | 148 | current_policy->features &= ~type; |
141 | sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]); | 149 | sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]); |
142 | } | 150 | } |
143 | 151 | ||
diff --git a/sway/commands/permit.c b/sway/commands/permit.c index 1b2a30bf..e2bec2e2 100644 --- a/sway/commands/permit.c +++ b/sway/commands/permit.c | |||
@@ -19,7 +19,6 @@ static enum secure_feature get_features(int argc, char **argv, | |||
19 | { "fullscreen", FEATURE_FULLSCREEN }, | 19 | { "fullscreen", FEATURE_FULLSCREEN }, |
20 | { "keyboard", FEATURE_KEYBOARD }, | 20 | { "keyboard", FEATURE_KEYBOARD }, |
21 | { "mouse", FEATURE_MOUSE }, | 21 | { "mouse", FEATURE_MOUSE }, |
22 | { "ipc", FEATURE_IPC }, | ||
23 | }; | 22 | }; |
24 | 23 | ||
25 | for (int i = 1; i < argc; ++i) { | 24 | for (int i = 1; i < argc; ++i) { |
@@ -63,19 +62,13 @@ struct cmd_results *cmd_permit(int argc, char **argv) { | |||
63 | if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) { | 62 | if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) { |
64 | return error; | 63 | return error; |
65 | } | 64 | } |
66 | 65 | if ((error = check_security_config())) { | |
67 | if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) { | 66 | return error; |
68 | return cmd_results_new(CMD_INVALID, "permit", | ||
69 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); | ||
70 | } | 67 | } |
71 | 68 | ||
72 | struct feature_policy *policy = get_policy(argv[0]); | 69 | struct feature_policy *policy = get_policy(argv[0]); |
73 | policy->features |= get_features(argc, argv, &error); | 70 | policy->features |= get_features(argc, argv, &error); |
74 | 71 | ||
75 | if (error) { | ||
76 | return error; | ||
77 | } | ||
78 | |||
79 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", | 72 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", |
80 | policy->program, policy->features); | 73 | policy->program, policy->features); |
81 | 74 | ||
@@ -87,19 +80,13 @@ struct cmd_results *cmd_reject(int argc, char **argv) { | |||
87 | if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) { | 80 | if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) { |
88 | return error; | 81 | return error; |
89 | } | 82 | } |
90 | 83 | if ((error = check_security_config())) { | |
91 | if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) { | 84 | return error; |
92 | return cmd_results_new(CMD_INVALID, "permit", | ||
93 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); | ||
94 | } | 85 | } |
95 | 86 | ||
96 | struct feature_policy *policy = get_policy(argv[0]); | 87 | struct feature_policy *policy = get_policy(argv[0]); |
97 | policy->features &= ~get_features(argc, argv, &error); | 88 | policy->features &= ~get_features(argc, argv, &error); |
98 | 89 | ||
99 | if (error) { | ||
100 | return error; | ||
101 | } | ||
102 | |||
103 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", | 90 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", |
104 | policy->program, policy->features); | 91 | policy->program, policy->features); |
105 | 92 | ||
diff --git a/sway/config.c b/sway/config.c index 9e758c90..88e6fad1 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <libinput.h> | 11 | #include <libinput.h> |
12 | #include <limits.h> | 12 | #include <limits.h> |
13 | #include <float.h> | 13 | #include <float.h> |
14 | #include <dirent.h> | ||
14 | #include "wayland-desktop-shell-server-protocol.h" | 15 | #include "wayland-desktop-shell-server-protocol.h" |
15 | #include "sway/commands.h" | 16 | #include "sway/commands.h" |
16 | #include "sway/config.h" | 17 | #include "sway/config.h" |
@@ -379,7 +380,7 @@ static void config_defaults(struct sway_config *config) { | |||
379 | // Security | 380 | // Security |
380 | if (!(config->command_policies = create_list())) goto cleanup; | 381 | if (!(config->command_policies = create_list())) goto cleanup; |
381 | if (!(config->feature_policies = create_list())) goto cleanup; | 382 | if (!(config->feature_policies = create_list())) goto cleanup; |
382 | config->ipc_policy = UINT32_MAX; | 383 | if (!(config->ipc_policies = create_list())) goto cleanup; |
383 | 384 | ||
384 | return; | 385 | return; |
385 | cleanup: | 386 | cleanup: |
@@ -485,6 +486,10 @@ static bool load_config(const char *path, struct sway_config *config) { | |||
485 | return true; | 486 | return true; |
486 | } | 487 | } |
487 | 488 | ||
489 | static int qstrcmp(const void* a, const void* b) { | ||
490 | return strcmp(*((char**) a), *((char**) b)); | ||
491 | } | ||
492 | |||
488 | bool load_main_config(const char *file, bool is_active) { | 493 | bool load_main_config(const char *file, bool is_active) { |
489 | input_init(); | 494 | input_init(); |
490 | 495 | ||
@@ -512,7 +517,43 @@ bool load_main_config(const char *file, bool is_active) { | |||
512 | list_add(config->config_chain, path); | 517 | list_add(config->config_chain, path); |
513 | 518 | ||
514 | config->reading = true; | 519 | config->reading = true; |
515 | bool success = load_config(SYSCONFDIR "/sway/security", config); | 520 | |
521 | // Read security configs | ||
522 | bool success = true; | ||
523 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | ||
524 | if (!dir) { | ||
525 | sway_log(L_ERROR, "%s does not exist, sway will have no security configuration" | ||
526 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | ||
527 | } else { | ||
528 | list_t *secconfigs = create_list(); | ||
529 | char *base = SYSCONFDIR "/sway/security.d/"; | ||
530 | struct dirent *ent = readdir(dir); | ||
531 | while (ent != NULL) { | ||
532 | if (ent->d_type == DT_REG) { | ||
533 | char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); | ||
534 | strcpy(_path, base); | ||
535 | strcat(_path, ent->d_name); | ||
536 | list_add(secconfigs, _path); | ||
537 | } | ||
538 | ent = readdir(dir); | ||
539 | } | ||
540 | closedir(dir); | ||
541 | |||
542 | list_qsort(secconfigs, qstrcmp); | ||
543 | for (int i = 0; i < secconfigs->length; ++i) { | ||
544 | char *_path = secconfigs->items[i]; | ||
545 | struct stat s; | ||
546 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (s.st_mode & 0777) != 0644) { | ||
547 | sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644", _path); | ||
548 | success = false; | ||
549 | } else { | ||
550 | success = success && load_config(_path, config); | ||
551 | } | ||
552 | } | ||
553 | |||
554 | free_flat_list(secconfigs); | ||
555 | } | ||
556 | |||
516 | success = success && load_config(path, config); | 557 | success = success && load_config(path, config); |
517 | 558 | ||
518 | if (is_active) { | 559 | if (is_active) { |
@@ -620,6 +661,15 @@ bool load_include_configs(const char *path, struct sway_config *config) { | |||
620 | return true; | 661 | return true; |
621 | } | 662 | } |
622 | 663 | ||
664 | struct cmd_results *check_security_config() { | ||
665 | if (!current_config_path || strncmp(SYSCONFDIR "/sway/security.d/", current_config_path, | ||
666 | strlen(SYSCONFDIR "/sway/security.d/")) != 0) { | ||
667 | return cmd_results_new(CMD_INVALID, "permit", | ||
668 | "This command is only permitted to run from " SYSCONFDIR "/sway/security.d/*"); | ||
669 | } | ||
670 | return NULL; | ||
671 | } | ||
672 | |||
623 | bool read_config(FILE *file, struct sway_config *config) { | 673 | bool read_config(FILE *file, struct sway_config *config) { |
624 | bool success = true; | 674 | bool success = true; |
625 | enum cmd_status block = CMD_BLOCK_END; | 675 | enum cmd_status block = CMD_BLOCK_END; |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index be6e411a..eddae461 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -35,6 +35,7 @@ struct ipc_client { | |||
35 | struct wlc_event_source *event_source; | 35 | struct wlc_event_source *event_source; |
36 | int fd; | 36 | int fd; |
37 | uint32_t payload_length; | 37 | uint32_t payload_length; |
38 | uint32_t security_policy; | ||
38 | enum ipc_command_type current_command; | 39 | enum ipc_command_type current_command; |
39 | enum ipc_command_type subscribed_events; | 40 | enum ipc_command_type subscribed_events; |
40 | }; | 41 | }; |
@@ -159,17 +160,6 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
159 | return 0; | 160 | return 0; |
160 | } | 161 | } |
161 | 162 | ||
162 | pid_t pid = get_client_pid(client_fd); | ||
163 | if (!(get_feature_policy(pid) & FEATURE_IPC)) { | ||
164 | sway_log(L_INFO, "Permission to connect to IPC socket denied to %d", pid); | ||
165 | const char *error = "{\"success\": false, \"message\": \"Permission denied\"}"; | ||
166 | if (write(client_fd, &error, sizeof(error)) < (int)sizeof(error)) { | ||
167 | sway_log(L_DEBUG, "Failed to write entire error"); | ||
168 | } | ||
169 | close(client_fd); | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); | 163 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); |
174 | if (!client) { | 164 | if (!client) { |
175 | sway_log(L_ERROR, "Unable to allocate ipc client"); | 165 | sway_log(L_ERROR, "Unable to allocate ipc client"); |
@@ -181,6 +171,9 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
181 | client->subscribed_events = 0; | 171 | client->subscribed_events = 0; |
182 | client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); | 172 | client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); |
183 | 173 | ||
174 | pid_t pid = get_client_pid(client->fd); | ||
175 | client->security_policy = get_ipc_policy(pid); | ||
176 | |||
184 | list_add(ipc_client_list, client); | 177 | list_add(ipc_client_list, client); |
185 | 178 | ||
186 | return 0; | 179 | return 0; |
@@ -248,8 +241,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
248 | return 0; | 241 | return 0; |
249 | } | 242 | } |
250 | 243 | ||
251 | void ipc_client_disconnect(struct ipc_client *client) | 244 | void ipc_client_disconnect(struct ipc_client *client) { |
252 | { | ||
253 | if (!sway_assert(client != NULL, "client != NULL")) { | 245 | if (!sway_assert(client != NULL, "client != NULL")) { |
254 | return; | 246 | return; |
255 | } | 247 | } |
@@ -333,8 +325,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
333 | ipc_client_disconnect(client); | 325 | ipc_client_disconnect(client); |
334 | return; | 326 | return; |
335 | } | 327 | } |
336 | if (client->payload_length > 0) | 328 | if (client->payload_length > 0) { |
337 | { | ||
338 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); | 329 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); |
339 | if (received == -1) | 330 | if (received == -1) |
340 | { | 331 | { |
@@ -351,7 +342,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
351 | switch (client->current_command) { | 342 | switch (client->current_command) { |
352 | case IPC_COMMAND: | 343 | case IPC_COMMAND: |
353 | { | 344 | { |
354 | if (!(config->ipc_policy & IPC_FEATURE_COMMAND)) { | 345 | if (!(client->security_policy & IPC_FEATURE_COMMAND)) { |
355 | goto exit_denied; | 346 | goto exit_denied; |
356 | } | 347 | } |
357 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); | 348 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); |
@@ -365,6 +356,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
365 | 356 | ||
366 | case IPC_SUBSCRIBE: | 357 | case IPC_SUBSCRIBE: |
367 | { | 358 | { |
359 | // TODO: Check if they're permitted to use these events | ||
368 | struct json_object *request = json_tokener_parse(buf); | 360 | struct json_object *request = json_tokener_parse(buf); |
369 | if (request == NULL) { | 361 | if (request == NULL) { |
370 | ipc_send_reply(client, "{\"success\": false}", 18); | 362 | ipc_send_reply(client, "{\"success\": false}", 18); |
@@ -403,7 +395,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
403 | 395 | ||
404 | case IPC_GET_WORKSPACES: | 396 | case IPC_GET_WORKSPACES: |
405 | { | 397 | { |
406 | if (!(config->ipc_policy & IPC_FEATURE_GET_WORKSPACES)) { | 398 | if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) { |
407 | goto exit_denied; | 399 | goto exit_denied; |
408 | } | 400 | } |
409 | json_object *workspaces = json_object_new_array(); | 401 | json_object *workspaces = json_object_new_array(); |
@@ -416,7 +408,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
416 | 408 | ||
417 | case IPC_GET_INPUTS: | 409 | case IPC_GET_INPUTS: |
418 | { | 410 | { |
419 | if (!(config->ipc_policy & IPC_FEATURE_GET_INPUTS)) { | 411 | if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) { |
420 | goto exit_denied; | 412 | goto exit_denied; |
421 | } | 413 | } |
422 | json_object *inputs = json_object_new_array(); | 414 | json_object *inputs = json_object_new_array(); |
@@ -442,7 +434,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
442 | 434 | ||
443 | case IPC_GET_OUTPUTS: | 435 | case IPC_GET_OUTPUTS: |
444 | { | 436 | { |
445 | if (!(config->ipc_policy & IPC_FEATURE_GET_OUTPUTS)) { | 437 | if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) { |
446 | goto exit_denied; | 438 | goto exit_denied; |
447 | } | 439 | } |
448 | json_object *outputs = json_object_new_array(); | 440 | json_object *outputs = json_object_new_array(); |
@@ -455,7 +447,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
455 | 447 | ||
456 | case IPC_GET_TREE: | 448 | case IPC_GET_TREE: |
457 | { | 449 | { |
458 | if (!(config->ipc_policy & IPC_FEATURE_GET_TREE)) { | 450 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { |
459 | goto exit_denied; | 451 | goto exit_denied; |
460 | } | 452 | } |
461 | json_object *tree = ipc_json_describe_container_recursive(&root_container); | 453 | json_object *tree = ipc_json_describe_container_recursive(&root_container); |
@@ -522,7 +514,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
522 | 514 | ||
523 | case IPC_GET_BAR_CONFIG: | 515 | case IPC_GET_BAR_CONFIG: |
524 | { | 516 | { |
525 | if (!(config->ipc_policy & IPC_FEATURE_GET_BAR_CONFIG)) { | 517 | if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) { |
526 | goto exit_denied; | 518 | goto exit_denied; |
527 | } | 519 | } |
528 | if (!buf[0]) { | 520 | if (!buf[0]) { |
@@ -567,6 +559,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
567 | 559 | ||
568 | exit_denied: | 560 | exit_denied: |
569 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); | 561 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); |
562 | sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command); | ||
570 | 563 | ||
571 | exit_cleanup: | 564 | exit_cleanup: |
572 | client->payload_length = 0; | 565 | client->payload_length = 0; |
@@ -594,6 +587,8 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
594 | return false; | 587 | return false; |
595 | } | 588 | } |
596 | 589 | ||
590 | sway_log(L_DEBUG, "Send IPC reply: %s", payload); | ||
591 | |||
597 | return true; | 592 | return true; |
598 | } | 593 | } |
599 | 594 | ||
@@ -616,10 +611,33 @@ void ipc_get_outputs_callback(swayc_t *container, void *data) { | |||
616 | } | 611 | } |
617 | 612 | ||
618 | void ipc_send_event(const char *json_string, enum ipc_command_type event) { | 613 | void ipc_send_event(const char *json_string, enum ipc_command_type event) { |
614 | static struct { | ||
615 | enum ipc_command_type event; | ||
616 | enum ipc_feature feature; | ||
617 | } security_mappings[] = { | ||
618 | { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE }, | ||
619 | { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT }, | ||
620 | { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE }, | ||
621 | { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW }, | ||
622 | { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING }, | ||
623 | { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT } | ||
624 | }; | ||
625 | |||
626 | uint32_t security_mask = 0; | ||
627 | for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) { | ||
628 | if (security_mappings[i].event == event) { | ||
629 | security_mask = security_mappings[i].feature; | ||
630 | break; | ||
631 | } | ||
632 | } | ||
633 | |||
619 | int i; | 634 | int i; |
620 | struct ipc_client *client; | 635 | struct ipc_client *client; |
621 | for (i = 0; i < ipc_client_list->length; i++) { | 636 | for (i = 0; i < ipc_client_list->length; i++) { |
622 | client = ipc_client_list->items[i]; | 637 | client = ipc_client_list->items[i]; |
638 | if (!(client->security_policy & security_mask)) { | ||
639 | continue; | ||
640 | } | ||
623 | if ((client->subscribed_events & event_mask(event)) == 0) { | 641 | if ((client->subscribed_events & event_mask(event)) == 0) { |
624 | continue; | 642 | continue; |
625 | } | 643 | } |
@@ -632,9 +650,6 @@ void ipc_send_event(const char *json_string, enum ipc_command_type event) { | |||
632 | } | 650 | } |
633 | 651 | ||
634 | void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { | 652 | void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { |
635 | if (!(config->ipc_policy & IPC_FEATURE_EVENT_WORKSPACE)) { | ||
636 | return; | ||
637 | } | ||
638 | sway_log(L_DEBUG, "Sending workspace::%s event", change); | 653 | sway_log(L_DEBUG, "Sending workspace::%s event", change); |
639 | json_object *obj = json_object_new_object(); | 654 | json_object *obj = json_object_new_object(); |
640 | json_object_object_add(obj, "change", json_object_new_string(change)); | 655 | json_object_object_add(obj, "change", json_object_new_string(change)); |
@@ -659,9 +674,6 @@ void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { | |||
659 | } | 674 | } |
660 | 675 | ||
661 | void ipc_event_window(swayc_t *window, const char *change) { | 676 | void ipc_event_window(swayc_t *window, const char *change) { |
662 | if (!(config->ipc_policy & IPC_FEATURE_EVENT_WINDOW)) { | ||
663 | return; | ||
664 | } | ||
665 | sway_log(L_DEBUG, "Sending window::%s event", change); | 677 | sway_log(L_DEBUG, "Sending window::%s event", change); |
666 | json_object *obj = json_object_new_object(); | 678 | json_object *obj = json_object_new_object(); |
667 | json_object_object_add(obj, "change", json_object_new_string(change)); | 679 | json_object_object_add(obj, "change", json_object_new_string(change)); |
@@ -687,9 +699,6 @@ void ipc_event_barconfig_update(struct bar_config *bar) { | |||
687 | } | 699 | } |
688 | 700 | ||
689 | void ipc_event_mode(const char *mode) { | 701 | void ipc_event_mode(const char *mode) { |
690 | if (!(config->ipc_policy & IPC_FEATURE_EVENT_MODE)) { | ||
691 | return; | ||
692 | } | ||
693 | sway_log(L_DEBUG, "Sending mode::%s event", mode); | 702 | sway_log(L_DEBUG, "Sending mode::%s event", mode); |
694 | json_object *obj = json_object_new_object(); | 703 | json_object *obj = json_object_new_object(); |
695 | json_object_object_add(obj, "change", json_object_new_string(mode)); | 704 | json_object_object_add(obj, "change", json_object_new_string(mode)); |
@@ -715,9 +724,6 @@ void ipc_event_modifier(uint32_t modifier, const char *state) { | |||
715 | } | 724 | } |
716 | 725 | ||
717 | static void ipc_event_binding(json_object *sb_obj) { | 726 | static void ipc_event_binding(json_object *sb_obj) { |
718 | if (!(config->ipc_policy & IPC_FEATURE_EVENT_BINDING)) { | ||
719 | return; | ||
720 | } | ||
721 | sway_log(L_DEBUG, "Sending binding::run event"); | 727 | sway_log(L_DEBUG, "Sending binding::run event"); |
722 | json_object *obj = json_object_new_object(); | 728 | json_object *obj = json_object_new_object(); |
723 | json_object_object_add(obj, "change", json_object_new_string("run")); | 729 | json_object_object_add(obj, "change", json_object_new_string("run")); |
diff --git a/sway/main.c b/sway/main.c index 1c4c56c0..0151e078 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -175,13 +175,6 @@ static void security_sanity_check() { | |||
175 | cap_free(cap); | 175 | cap_free(cap); |
176 | } | 176 | } |
177 | #endif | 177 | #endif |
178 | if (!stat(SYSCONFDIR "/sway", &s)) { | ||
179 | if (s.st_uid != 0 || s.st_gid != 0 | ||
180 | || (s.st_mode & S_IWGRP) || (s.st_mode & S_IWOTH)) { | ||
181 | sway_log(L_ERROR, | ||
182 | "!! DANGER !! " SYSCONFDIR "/sway is not secure! It should be owned by root and set to 0755 at the minimum"); | ||
183 | } | ||
184 | } | ||
185 | } | 178 | } |
186 | 179 | ||
187 | int main(int argc, char **argv) { | 180 | int main(int argc, char **argv) { |
diff --git a/sway/security.c b/sway/security.c index 41a3b94b..9dfc7d2d 100644 --- a/sway/security.c +++ b/sway/security.c | |||
@@ -27,6 +27,29 @@ struct feature_policy *alloc_feature_policy(const char *program) { | |||
27 | return policy; | 27 | return policy; |
28 | } | 28 | } |
29 | 29 | ||
30 | struct ipc_policy *alloc_ipc_policy(const char *program) { | ||
31 | uint32_t default_policy = 0; | ||
32 | for (int i = 0; i < config->ipc_policies->length; ++i) { | ||
33 | struct ipc_policy *policy = config->ipc_policies->items[i]; | ||
34 | if (strcmp(policy->program, "*") == 0) { | ||
35 | default_policy = policy->features; | ||
36 | break; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | struct ipc_policy *policy = malloc(sizeof(struct ipc_policy)); | ||
41 | if (!policy) { | ||
42 | return NULL; | ||
43 | } | ||
44 | policy->program = strdup(program); | ||
45 | if (!policy->program) { | ||
46 | free(policy); | ||
47 | return NULL; | ||
48 | } | ||
49 | policy->features = default_policy; | ||
50 | return policy; | ||
51 | } | ||
52 | |||
30 | struct command_policy *alloc_command_policy(const char *command) { | 53 | struct command_policy *alloc_command_policy(const char *command) { |
31 | struct command_policy *policy = malloc(sizeof(struct command_policy)); | 54 | struct command_policy *policy = malloc(sizeof(struct command_policy)); |
32 | if (!policy) { | 55 | if (!policy) { |
@@ -41,7 +64,7 @@ struct command_policy *alloc_command_policy(const char *command) { | |||
41 | return policy; | 64 | return policy; |
42 | } | 65 | } |
43 | 66 | ||
44 | enum secure_feature get_feature_policy(pid_t pid) { | 67 | static const char *get_pid_exe(pid_t pid) { |
45 | #ifdef __FreeBSD__ | 68 | #ifdef __FreeBSD__ |
46 | const char *fmt = "/proc/%d/file"; | 69 | const char *fmt = "/proc/%d/file"; |
47 | #else | 70 | #else |
@@ -52,9 +75,8 @@ enum secure_feature get_feature_policy(pid_t pid) { | |||
52 | if (path) { | 75 | if (path) { |
53 | snprintf(path, pathlen + 1, fmt, pid); | 76 | snprintf(path, pathlen + 1, fmt, pid); |
54 | } | 77 | } |
55 | static char link[2048]; | ||
56 | 78 | ||
57 | uint32_t default_policy = 0; | 79 | static char link[2048]; |
58 | 80 | ||
59 | ssize_t len = !path ? -1 : readlink(path, link, sizeof(link)); | 81 | ssize_t len = !path ? -1 : readlink(path, link, sizeof(link)); |
60 | if (len < 0) { | 82 | if (len < 0) { |
@@ -67,6 +89,13 @@ enum secure_feature get_feature_policy(pid_t pid) { | |||
67 | } | 89 | } |
68 | free(path); | 90 | free(path); |
69 | 91 | ||
92 | return link; | ||
93 | } | ||
94 | |||
95 | uint32_t get_feature_policy(pid_t pid) { | ||
96 | uint32_t default_policy = 0; | ||
97 | const char *link = get_pid_exe(pid); | ||
98 | |||
70 | for (int i = 0; i < config->feature_policies->length; ++i) { | 99 | for (int i = 0; i < config->feature_policies->length; ++i) { |
71 | struct feature_policy *policy = config->feature_policies->items[i]; | 100 | struct feature_policy *policy = config->feature_policies->items[i]; |
72 | if (strcmp(policy->program, "*") == 0) { | 101 | if (strcmp(policy->program, "*") == 0) { |
@@ -80,7 +109,24 @@ enum secure_feature get_feature_policy(pid_t pid) { | |||
80 | return default_policy; | 109 | return default_policy; |
81 | } | 110 | } |
82 | 111 | ||
83 | enum command_context get_command_policy(const char *cmd) { | 112 | uint32_t get_ipc_policy(pid_t pid) { |
113 | uint32_t default_policy = 0; | ||
114 | const char *link = get_pid_exe(pid); | ||
115 | |||
116 | for (int i = 0; i < config->ipc_policies->length; ++i) { | ||
117 | struct ipc_policy *policy = config->ipc_policies->items[i]; | ||
118 | if (strcmp(policy->program, "*") == 0) { | ||
119 | default_policy = policy->features; | ||
120 | } | ||
121 | if (strcmp(policy->program, link) == 0) { | ||
122 | return policy->features; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | return default_policy; | ||
127 | } | ||
128 | |||
129 | uint32_t get_command_policy(const char *cmd) { | ||
84 | uint32_t default_policy = 0; | 130 | uint32_t default_policy = 0; |
85 | 131 | ||
86 | for (int i = 0; i < config->command_policies->length; ++i) { | 132 | for (int i = 0; i < config->command_policies->length; ++i) { |
diff --git a/sway/sway-security.7.txt b/sway/sway-security.7.txt index 7d8aa4ad..fb47ffcf 100644 --- a/sway/sway-security.7.txt +++ b/sway/sway-security.7.txt | |||
@@ -19,8 +19,13 @@ 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 | 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. | 20 | you must make a few changes external to sway first. |
21 | 21 | ||
22 | Security-related configuration is only valid in /etc/sway/config (or whatever path | 22 | Configuration of security features is limited to files in the security directory |
23 | is appropriate for your system). | 23 | (this is likely /etc/sway/security.d/*, but depends on your installation prefix). |
24 | Files in this directory must be owned by root:root and chmod 644. The default | ||
25 | security configuration is installed to /etc/sway/security.d/00-defaults, and | ||
26 | should not be modified - it will be updated with the latest recommended security | ||
27 | defaults between releases. To override the defaults, you should add more files to | ||
28 | this directory. | ||
24 | 29 | ||
25 | Environment security | 30 | Environment security |
26 | -------------------- | 31 | -------------------- |
@@ -160,22 +165,20 @@ Setting a command policy overwrites any previous policy that was in place. | |||
160 | IPC policies | 165 | IPC policies |
161 | ------------ | 166 | ------------ |
162 | 167 | ||
163 | You may whitelist IPC access like so: | 168 | Disabling IPC access via swaymsg is encouraged if you intend to secure the IPC |
169 | socket, because any program that can execute swaymsg could circumvent its own | ||
170 | security policy by simply invoking swaymsg. | ||
164 | 171 | ||
165 | permit /usr/bin/swaybar ipc | 172 | You can configure which features of IPC are available for particular clients: |
166 | permit /usr/bin/swaygrab ipc | ||
167 | # etc | ||
168 | 173 | ||
169 | Note that it's suggested you do not enable swaymsg to access IPC if you intend to | 174 | ipc <executable> { |
170 | secure your IPC socket, because any program could just run swaymsg itself instead | ||
171 | of connecting to IPC directly. | ||
172 | |||
173 | You can also configure which features of IPC are available with an IPC block: | ||
174 | |||
175 | ipc { | ||
176 | ... | 175 | ... |
177 | } | 176 | } |
178 | 177 | ||
178 | You may use * for <executable> to configure the default policy for all clients. | ||
179 | Configuring IPC policies for specific executables is not supported on FreeBSD, and | ||
180 | the default policy will be applied to all IPC connections. | ||
181 | |||
179 | The following commands are available within this block: | 182 | The following commands are available within this block: |
180 | 183 | ||
181 | **bar-config** <enabled|disabled>:: | 184 | **bar-config** <enabled|disabled>:: |
@@ -201,7 +204,7 @@ The following commands are available within this block: | |||
201 | 204 | ||
202 | You can also control which IPC events can be raised with an events block: | 205 | You can also control which IPC events can be raised with an events block: |
203 | 206 | ||
204 | ipc { | 207 | ipc <executable> { |
205 | events { | 208 | events { |
206 | ... | 209 | ... |
207 | } | 210 | } |
@@ -227,7 +230,8 @@ The following commands are vaild within an ipc events block: | |||
227 | **workspace** <enabled|disabled>:: | 230 | **workspace** <enabled|disabled>:: |
228 | Controls workspace notifications. | 231 | Controls workspace notifications. |
229 | 232 | ||
230 | Disabling some of these may cause swaybar to behave incorrectly. | 233 | In each of these blocks, you may use * (as in "* enabled" or "* disabled") to |
234 | control access to every feature at once. | ||
231 | 235 | ||
232 | Authors | 236 | Authors |
233 | ------- | 237 | ------- |