diff options
author | Drew DeVault <sir@cmpwn.com> | 2017-02-20 06:11:43 -0500 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2017-02-20 06:11:56 -0500 |
commit | b10721b89e3f3992b2476c55237a25dbeb0bce46 (patch) | |
tree | 8898271a6b93b848bd5e75c6f037a6140d119d1b | |
parent | Revise IPC security configuration (diff) | |
download | sway-b10721b89e3f3992b2476c55237a25dbeb0bce46.tar.gz sway-b10721b89e3f3992b2476c55237a25dbeb0bce46.tar.zst sway-b10721b89e3f3992b2476c55237a25dbeb0bce46.zip |
Add initial support code for new IPC security
-rw-r--r-- | include/sway/config.h | 8 | ||||
-rw-r--r-- | include/sway/security.h | 6 | ||||
-rw-r--r-- | sway/commands/ipc.c | 12 | ||||
-rw-r--r-- | sway/commands/permit.c | 1 | ||||
-rw-r--r-- | sway/config.c | 2 | ||||
-rw-r--r-- | sway/ipc-server.c | 45 | ||||
-rw-r--r-- | sway/security.c | 54 |
7 files changed, 70 insertions, 58 deletions
diff --git a/include/sway/config.h b/include/sway/config.h index febde63d..c3a916b1 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 { |
@@ -228,6 +227,11 @@ enum ipc_feature { | |||
228 | IPC_FEATURE_EVENT_INPUT = 8192 | 227 | IPC_FEATURE_EVENT_INPUT = 8192 |
229 | }; | 228 | }; |
230 | 229 | ||
230 | struct ipc_policy { | ||
231 | char *program; | ||
232 | uint32_t features; | ||
233 | }; | ||
234 | |||
231 | /** | 235 | /** |
232 | * The configuration struct. The result of loading a config file. | 236 | * The configuration struct. The result of loading a config file. |
233 | */ | 237 | */ |
@@ -300,7 +304,7 @@ struct sway_config { | |||
300 | // Security | 304 | // Security |
301 | list_t *command_policies; | 305 | list_t *command_policies; |
302 | list_t *feature_policies; | 306 | list_t *feature_policies; |
303 | uint32_t ipc_policy; | 307 | list_t *ipc_policies; |
304 | }; | 308 | }; |
305 | 309 | ||
306 | void pid_workspace_add(struct pid_workspace *pw); | 310 | void pid_workspace_add(struct pid_workspace *pw); |
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/sway/commands/ipc.c b/sway/commands/ipc.c index 113a975b..44d7a010 100644 --- a/sway/commands/ipc.c +++ b/sway/commands/ipc.c | |||
@@ -86,10 +86,10 @@ struct cmd_results *cmd_ipc_cmd(int argc, char **argv) { | |||
86 | } | 86 | } |
87 | 87 | ||
88 | if (enabled) { | 88 | if (enabled) { |
89 | config->ipc_policy |= type; | 89 | //config->ipc_policy |= type; |
90 | sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]); | 90 | sway_log(L_DEBUG, "Enabled IPC %s feature %d", argv[-1], (int)type); |
91 | } else { | 91 | } else { |
92 | config->ipc_policy &= ~type; | 92 | //config->ipc_policy &= ~type; |
93 | sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]); | 93 | sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]); |
94 | } | 94 | } |
95 | 95 | ||
@@ -134,10 +134,10 @@ struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) { | |||
134 | } | 134 | } |
135 | 135 | ||
136 | if (enabled) { | 136 | if (enabled) { |
137 | config->ipc_policy |= type; | 137 | //config->ipc_policy |= type; |
138 | sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]); | 138 | sway_log(L_DEBUG, "Enabled IPC %s event %d", argv[-1], (int)type); |
139 | } else { | 139 | } else { |
140 | config->ipc_policy &= ~type; | 140 | //config->ipc_policy &= ~type; |
141 | sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]); | 141 | sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]); |
142 | } | 142 | } |
143 | 143 | ||
diff --git a/sway/commands/permit.c b/sway/commands/permit.c index 1b2a30bf..6eb71816 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) { |
diff --git a/sway/config.c b/sway/config.c index 9e758c90..4a3c953d 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -379,7 +379,7 @@ static void config_defaults(struct sway_config *config) { | |||
379 | // Security | 379 | // Security |
380 | if (!(config->command_policies = create_list())) goto cleanup; | 380 | if (!(config->command_policies = create_list())) goto cleanup; |
381 | if (!(config->feature_policies = create_list())) goto cleanup; | 381 | if (!(config->feature_policies = create_list())) goto cleanup; |
382 | config->ipc_policy = UINT32_MAX; | 382 | if (!(config->ipc_policies = create_list())) goto cleanup; |
383 | 383 | ||
384 | return; | 384 | return; |
385 | cleanup: | 385 | cleanup: |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index be6e411a..dfe88ed6 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -125,6 +125,7 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
125 | return ipc_sockaddr; | 125 | return ipc_sockaddr; |
126 | } | 126 | } |
127 | 127 | ||
128 | /* | ||
128 | static pid_t get_client_pid(int client_fd) { | 129 | static pid_t get_client_pid(int client_fd) { |
129 | // FreeBSD supports getting uid/gid, but not pid | 130 | // FreeBSD supports getting uid/gid, but not pid |
130 | #ifdef __linux__ | 131 | #ifdef __linux__ |
@@ -140,6 +141,7 @@ static pid_t get_client_pid(int client_fd) { | |||
140 | return -1; | 141 | return -1; |
141 | #endif | 142 | #endif |
142 | } | 143 | } |
144 | */ | ||
143 | 145 | ||
144 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 146 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
145 | (void) fd; (void) data; | 147 | (void) fd; (void) data; |
@@ -159,17 +161,6 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
159 | return 0; | 161 | return 0; |
160 | } | 162 | } |
161 | 163 | ||
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)); | 164 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); |
174 | if (!client) { | 165 | if (!client) { |
175 | sway_log(L_ERROR, "Unable to allocate ipc client"); | 166 | sway_log(L_ERROR, "Unable to allocate ipc client"); |
@@ -351,9 +342,6 @@ 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)) { | ||
355 | goto exit_denied; | ||
356 | } | ||
357 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); | 345 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); |
358 | const char *json = cmd_results_to_json(results); | 346 | const char *json = cmd_results_to_json(results); |
359 | char reply[256]; | 347 | char reply[256]; |
@@ -403,9 +391,6 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
403 | 391 | ||
404 | case IPC_GET_WORKSPACES: | 392 | case IPC_GET_WORKSPACES: |
405 | { | 393 | { |
406 | if (!(config->ipc_policy & IPC_FEATURE_GET_WORKSPACES)) { | ||
407 | goto exit_denied; | ||
408 | } | ||
409 | json_object *workspaces = json_object_new_array(); | 394 | json_object *workspaces = json_object_new_array(); |
410 | container_map(&root_container, ipc_get_workspaces_callback, workspaces); | 395 | container_map(&root_container, ipc_get_workspaces_callback, workspaces); |
411 | const char *json_string = json_object_to_json_string(workspaces); | 396 | const char *json_string = json_object_to_json_string(workspaces); |
@@ -416,9 +401,6 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
416 | 401 | ||
417 | case IPC_GET_INPUTS: | 402 | case IPC_GET_INPUTS: |
418 | { | 403 | { |
419 | if (!(config->ipc_policy & IPC_FEATURE_GET_INPUTS)) { | ||
420 | goto exit_denied; | ||
421 | } | ||
422 | json_object *inputs = json_object_new_array(); | 404 | json_object *inputs = json_object_new_array(); |
423 | if (input_devices) { | 405 | if (input_devices) { |
424 | for(int i=0; i<input_devices->length; i++) { | 406 | for(int i=0; i<input_devices->length; i++) { |
@@ -442,9 +424,6 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
442 | 424 | ||
443 | case IPC_GET_OUTPUTS: | 425 | case IPC_GET_OUTPUTS: |
444 | { | 426 | { |
445 | if (!(config->ipc_policy & IPC_FEATURE_GET_OUTPUTS)) { | ||
446 | goto exit_denied; | ||
447 | } | ||
448 | json_object *outputs = json_object_new_array(); | 427 | json_object *outputs = json_object_new_array(); |
449 | container_map(&root_container, ipc_get_outputs_callback, outputs); | 428 | container_map(&root_container, ipc_get_outputs_callback, outputs); |
450 | const char *json_string = json_object_to_json_string(outputs); | 429 | const char *json_string = json_object_to_json_string(outputs); |
@@ -455,9 +434,6 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
455 | 434 | ||
456 | case IPC_GET_TREE: | 435 | case IPC_GET_TREE: |
457 | { | 436 | { |
458 | if (!(config->ipc_policy & IPC_FEATURE_GET_TREE)) { | ||
459 | goto exit_denied; | ||
460 | } | ||
461 | json_object *tree = ipc_json_describe_container_recursive(&root_container); | 437 | json_object *tree = ipc_json_describe_container_recursive(&root_container); |
462 | const char *json_string = json_object_to_json_string(tree); | 438 | const char *json_string = json_object_to_json_string(tree); |
463 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 439 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); |
@@ -522,9 +498,6 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
522 | 498 | ||
523 | case IPC_GET_BAR_CONFIG: | 499 | case IPC_GET_BAR_CONFIG: |
524 | { | 500 | { |
525 | if (!(config->ipc_policy & IPC_FEATURE_GET_BAR_CONFIG)) { | ||
526 | goto exit_denied; | ||
527 | } | ||
528 | if (!buf[0]) { | 501 | if (!buf[0]) { |
529 | // Send list of configured bar IDs | 502 | // Send list of configured bar IDs |
530 | json_object *bars = json_object_new_array(); | 503 | json_object *bars = json_object_new_array(); |
@@ -565,7 +538,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
565 | goto exit_cleanup; | 538 | goto exit_cleanup; |
566 | } | 539 | } |
567 | 540 | ||
568 | exit_denied: | 541 | //exit_denied: |
569 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); | 542 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); |
570 | 543 | ||
571 | exit_cleanup: | 544 | exit_cleanup: |
@@ -632,9 +605,6 @@ void ipc_send_event(const char *json_string, enum ipc_command_type event) { | |||
632 | } | 605 | } |
633 | 606 | ||
634 | void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { | 607 | 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); | 608 | sway_log(L_DEBUG, "Sending workspace::%s event", change); |
639 | json_object *obj = json_object_new_object(); | 609 | json_object *obj = json_object_new_object(); |
640 | json_object_object_add(obj, "change", json_object_new_string(change)); | 610 | json_object_object_add(obj, "change", json_object_new_string(change)); |
@@ -659,9 +629,6 @@ void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { | |||
659 | } | 629 | } |
660 | 630 | ||
661 | void ipc_event_window(swayc_t *window, const char *change) { | 631 | 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); | 632 | sway_log(L_DEBUG, "Sending window::%s event", change); |
666 | json_object *obj = json_object_new_object(); | 633 | json_object *obj = json_object_new_object(); |
667 | json_object_object_add(obj, "change", json_object_new_string(change)); | 634 | json_object_object_add(obj, "change", json_object_new_string(change)); |
@@ -687,9 +654,6 @@ void ipc_event_barconfig_update(struct bar_config *bar) { | |||
687 | } | 654 | } |
688 | 655 | ||
689 | void ipc_event_mode(const char *mode) { | 656 | 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); | 657 | sway_log(L_DEBUG, "Sending mode::%s event", mode); |
694 | json_object *obj = json_object_new_object(); | 658 | json_object *obj = json_object_new_object(); |
695 | json_object_object_add(obj, "change", json_object_new_string(mode)); | 659 | json_object_object_add(obj, "change", json_object_new_string(mode)); |
@@ -715,9 +679,6 @@ void ipc_event_modifier(uint32_t modifier, const char *state) { | |||
715 | } | 679 | } |
716 | 680 | ||
717 | static void ipc_event_binding(json_object *sb_obj) { | 681 | 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"); | 682 | sway_log(L_DEBUG, "Sending binding::run event"); |
722 | json_object *obj = json_object_new_object(); | 683 | json_object *obj = json_object_new_object(); |
723 | json_object_object_add(obj, "change", json_object_new_string("run")); | 684 | json_object_object_add(obj, "change", json_object_new_string("run")); |
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) { |