diff options
author | Drew DeVault <sir@cmpwn.com> | 2017-02-20 07:42:08 -0500 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2017-02-20 07:51:31 -0500 |
commit | 126ce571dab09d84d8ee1b760981dbba7cbc1000 (patch) | |
tree | c13e957c752e3ae0798945e2e0be2af99da7dc68 | |
parent | Add * policies and fix bug (diff) | |
download | sway-126ce571dab09d84d8ee1b760981dbba7cbc1000.tar.gz sway-126ce571dab09d84d8ee1b760981dbba7cbc1000.tar.zst sway-126ce571dab09d84d8ee1b760981dbba7cbc1000.zip |
Read configs from /etc/sway/security.d/*
-rw-r--r-- | include/sway/config.h | 2 | ||||
-rw-r--r-- | security.d/00-defaults.in | 5 | ||||
-rw-r--r-- | sway/commands/commands.c | 8 | ||||
-rw-r--r-- | sway/commands/ipc.c | 8 | ||||
-rw-r--r-- | sway/commands/permit.c | 20 | ||||
-rw-r--r-- | sway/config.c | 52 | ||||
-rw-r--r-- | sway/ipc-server.c | 15 | ||||
-rw-r--r-- | sway/main.c | 7 | ||||
-rw-r--r-- | sway/sway-security.7.txt | 2 |
9 files changed, 77 insertions, 42 deletions
diff --git a/include/sway/config.h b/include/sway/config.h index ba49b9a0..d77fbd51 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -340,6 +340,8 @@ void free_config(struct sway_config *config); | |||
340 | */ | 340 | */ |
341 | char *do_var_replacement(char *str); | 341 | char *do_var_replacement(char *str); |
342 | 342 | ||
343 | struct cmd_results *check_security_config(); | ||
344 | |||
343 | int input_identifier_cmp(const void *item, const void *data); | 345 | int input_identifier_cmp(const void *item, const void *data); |
344 | 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); |
345 | 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/security.d/00-defaults.in b/security.d/00-defaults.in index 99859edd..f319e446 100644 --- a/security.d/00-defaults.in +++ b/security.d/00-defaults.in | |||
@@ -29,6 +29,11 @@ ipc __PREFIX__/bin/swaybar { | |||
29 | outputs enabled | 29 | outputs enabled |
30 | workspaces enabled | 30 | workspaces enabled |
31 | command enabled | 31 | command enabled |
32 | |||
33 | events { | ||
34 | workspace enabled | ||
35 | mode enabled | ||
36 | } | ||
32 | } | 37 | } |
33 | 38 | ||
34 | ipc __PREFIX__/bin/swaygrab { | 39 | ipc __PREFIX__/bin/swaygrab { |
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 d49aab64..8a7b849f 100644 --- a/sway/commands/ipc.c +++ b/sway/commands/ipc.c | |||
@@ -14,6 +14,9 @@ struct cmd_results *cmd_ipc(int argc, char **argv) { | |||
14 | if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) { | 14 | if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) { |
15 | return error; | 15 | return error; |
16 | } | 16 | } |
17 | if ((error = check_security_config())) { | ||
18 | return error; | ||
19 | } | ||
17 | 20 | ||
18 | const char *program = argv[0]; | 21 | const char *program = argv[0]; |
19 | 22 | ||
@@ -26,11 +29,6 @@ struct cmd_results *cmd_ipc(int argc, char **argv) { | |||
26 | 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."); |
27 | } | 30 | } |
28 | 31 | ||
29 | if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) { | ||
30 | return cmd_results_new(CMD_INVALID, "permit", | ||
31 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); | ||
32 | } | ||
33 | |||
34 | current_policy = alloc_ipc_policy(program); | 32 | current_policy = alloc_ipc_policy(program); |
35 | list_add(config->ipc_policies, current_policy); | 33 | list_add(config->ipc_policies, current_policy); |
36 | 34 | ||
diff --git a/sway/commands/permit.c b/sway/commands/permit.c index 6eb71816..e2bec2e2 100644 --- a/sway/commands/permit.c +++ b/sway/commands/permit.c | |||
@@ -62,19 +62,13 @@ struct cmd_results *cmd_permit(int argc, char **argv) { | |||
62 | if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) { | 62 | if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) { |
63 | return error; | 63 | return error; |
64 | } | 64 | } |
65 | 65 | if ((error = check_security_config())) { | |
66 | if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) { | 66 | return error; |
67 | return cmd_results_new(CMD_INVALID, "permit", | ||
68 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); | ||
69 | } | 67 | } |
70 | 68 | ||
71 | struct feature_policy *policy = get_policy(argv[0]); | 69 | struct feature_policy *policy = get_policy(argv[0]); |
72 | policy->features |= get_features(argc, argv, &error); | 70 | policy->features |= get_features(argc, argv, &error); |
73 | 71 | ||
74 | if (error) { | ||
75 | return error; | ||
76 | } | ||
77 | |||
78 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", | 72 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", |
79 | policy->program, policy->features); | 73 | policy->program, policy->features); |
80 | 74 | ||
@@ -86,19 +80,13 @@ struct cmd_results *cmd_reject(int argc, char **argv) { | |||
86 | if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) { | 80 | if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) { |
87 | return error; | 81 | return error; |
88 | } | 82 | } |
89 | 83 | if ((error = check_security_config())) { | |
90 | if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) { | 84 | return error; |
91 | return cmd_results_new(CMD_INVALID, "permit", | ||
92 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); | ||
93 | } | 85 | } |
94 | 86 | ||
95 | struct feature_policy *policy = get_policy(argv[0]); | 87 | struct feature_policy *policy = get_policy(argv[0]); |
96 | policy->features &= ~get_features(argc, argv, &error); | 88 | policy->features &= ~get_features(argc, argv, &error); |
97 | 89 | ||
98 | if (error) { | ||
99 | return error; | ||
100 | } | ||
101 | |||
102 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", | 90 | sway_log(L_DEBUG, "Permissions granted to %s for features %d", |
103 | policy->program, policy->features); | 91 | policy->program, policy->features); |
104 | 92 | ||
diff --git a/sway/config.c b/sway/config.c index 4a3c953d..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" |
@@ -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 20a19b44..eddae461 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -241,8 +241,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
241 | return 0; | 241 | return 0; |
242 | } | 242 | } |
243 | 243 | ||
244 | void ipc_client_disconnect(struct ipc_client *client) | 244 | void ipc_client_disconnect(struct ipc_client *client) { |
245 | { | ||
246 | if (!sway_assert(client != NULL, "client != NULL")) { | 245 | if (!sway_assert(client != NULL, "client != NULL")) { |
247 | return; | 246 | return; |
248 | } | 247 | } |
@@ -326,8 +325,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
326 | ipc_client_disconnect(client); | 325 | ipc_client_disconnect(client); |
327 | return; | 326 | return; |
328 | } | 327 | } |
329 | if (client->payload_length > 0) | 328 | if (client->payload_length > 0) { |
330 | { | ||
331 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); | 329 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); |
332 | if (received == -1) | 330 | if (received == -1) |
333 | { | 331 | { |
@@ -397,7 +395,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
397 | 395 | ||
398 | case IPC_GET_WORKSPACES: | 396 | case IPC_GET_WORKSPACES: |
399 | { | 397 | { |
400 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | 398 | if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) { |
401 | goto exit_denied; | 399 | goto exit_denied; |
402 | } | 400 | } |
403 | json_object *workspaces = json_object_new_array(); | 401 | json_object *workspaces = json_object_new_array(); |
@@ -410,7 +408,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
410 | 408 | ||
411 | case IPC_GET_INPUTS: | 409 | case IPC_GET_INPUTS: |
412 | { | 410 | { |
413 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | 411 | if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) { |
414 | goto exit_denied; | 412 | goto exit_denied; |
415 | } | 413 | } |
416 | json_object *inputs = json_object_new_array(); | 414 | json_object *inputs = json_object_new_array(); |
@@ -436,7 +434,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
436 | 434 | ||
437 | case IPC_GET_OUTPUTS: | 435 | case IPC_GET_OUTPUTS: |
438 | { | 436 | { |
439 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | 437 | if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) { |
440 | goto exit_denied; | 438 | goto exit_denied; |
441 | } | 439 | } |
442 | json_object *outputs = json_object_new_array(); | 440 | json_object *outputs = json_object_new_array(); |
@@ -561,6 +559,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
561 | 559 | ||
562 | exit_denied: | 560 | exit_denied: |
563 | 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); | ||
564 | 563 | ||
565 | exit_cleanup: | 564 | exit_cleanup: |
566 | client->payload_length = 0; | 565 | client->payload_length = 0; |
@@ -588,6 +587,8 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
588 | return false; | 587 | return false; |
589 | } | 588 | } |
590 | 589 | ||
590 | sway_log(L_DEBUG, "Send IPC reply: %s", payload); | ||
591 | |||
591 | return true; | 592 | return true; |
592 | } | 593 | } |
593 | 594 | ||
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/sway-security.7.txt b/sway/sway-security.7.txt index 98e3f5ac..fb47ffcf 100644 --- a/sway/sway-security.7.txt +++ b/sway/sway-security.7.txt | |||
@@ -21,7 +21,7 @@ you must make a few changes external to sway first. | |||
21 | 21 | ||
22 | Configuration of security features is limited to files in the security directory | 22 | Configuration of security features is limited to files in the security directory |
23 | (this is likely /etc/sway/security.d/*, but depends on your installation prefix). | 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 600. The default | 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 | 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 | 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 | 27 | defaults between releases. To override the defaults, you should add more files to |