diff options
-rw-r--r-- | sway/commands/ipc.c | 23 | ||||
-rw-r--r-- | sway/ipc-server.c | 50 |
2 files changed, 62 insertions, 11 deletions
diff --git a/sway/commands/ipc.c b/sway/commands/ipc.c index 44d7a010..6b29706e 100644 --- a/sway/commands/ipc.c +++ b/sway/commands/ipc.c | |||
@@ -1,18 +1,23 @@ | |||
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))) { |
12 | return error; | 15 | return error; |
13 | } | 16 | } |
14 | 17 | ||
15 | if (config->reading && strcmp("{", argv[0]) != 0) { | 18 | const char *program = argv[0]; |
19 | |||
20 | if (config->reading && strcmp("{", argv[1]) != 0) { | ||
16 | return cmd_results_new(CMD_INVALID, "ipc", | 21 | return cmd_results_new(CMD_INVALID, "ipc", |
17 | "Expected '{' at start of IPC config definition."); | 22 | "Expected '{' at start of IPC config definition."); |
18 | } | 23 | } |
@@ -26,6 +31,8 @@ struct cmd_results *cmd_ipc(int argc, char **argv) { | |||
26 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); | 31 | "This command is only permitted to run from " SYSCONFDIR "/sway/security"); |
27 | } | 32 | } |
28 | 33 | ||
34 | current_policy = alloc_ipc_policy(program); | ||
35 | |||
29 | return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL); | 36 | return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL); |
30 | } | 37 | } |
31 | 38 | ||
@@ -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 %d", argv[-1], (int)type); | 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 | ||
@@ -134,10 +141,10 @@ struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) { | |||
134 | } | 141 | } |
135 | 142 | ||
136 | if (enabled) { | 143 | if (enabled) { |
137 | //config->ipc_policy |= type; | 144 | current_policy->features |= type; |
138 | sway_log(L_DEBUG, "Enabled IPC %s event %d", argv[-1], (int)type); | 145 | sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]); |
139 | } else { | 146 | } else { |
140 | //config->ipc_policy &= ~type; | 147 | current_policy->features &= ~type; |
141 | sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]); | 148 | sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]); |
142 | } | 149 | } |
143 | 150 | ||
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index dfe88ed6..20a19b44 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 | }; |
@@ -125,7 +126,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
125 | return ipc_sockaddr; | 126 | return ipc_sockaddr; |
126 | } | 127 | } |
127 | 128 | ||
128 | /* | ||
129 | static pid_t get_client_pid(int client_fd) { | 129 | static pid_t get_client_pid(int client_fd) { |
130 | // FreeBSD supports getting uid/gid, but not pid | 130 | // FreeBSD supports getting uid/gid, but not pid |
131 | #ifdef __linux__ | 131 | #ifdef __linux__ |
@@ -141,7 +141,6 @@ static pid_t get_client_pid(int client_fd) { | |||
141 | return -1; | 141 | return -1; |
142 | #endif | 142 | #endif |
143 | } | 143 | } |
144 | */ | ||
145 | 144 | ||
146 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 145 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
147 | (void) fd; (void) data; | 146 | (void) fd; (void) data; |
@@ -172,6 +171,9 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
172 | client->subscribed_events = 0; | 171 | client->subscribed_events = 0; |
173 | 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); |
174 | 173 | ||
174 | pid_t pid = get_client_pid(client->fd); | ||
175 | client->security_policy = get_ipc_policy(pid); | ||
176 | |||
175 | list_add(ipc_client_list, client); | 177 | list_add(ipc_client_list, client); |
176 | 178 | ||
177 | return 0; | 179 | return 0; |
@@ -342,6 +344,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
342 | switch (client->current_command) { | 344 | switch (client->current_command) { |
343 | case IPC_COMMAND: | 345 | case IPC_COMMAND: |
344 | { | 346 | { |
347 | if (!(client->security_policy & IPC_FEATURE_COMMAND)) { | ||
348 | goto exit_denied; | ||
349 | } | ||
345 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); | 350 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); |
346 | const char *json = cmd_results_to_json(results); | 351 | const char *json = cmd_results_to_json(results); |
347 | char reply[256]; | 352 | char reply[256]; |
@@ -353,6 +358,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
353 | 358 | ||
354 | case IPC_SUBSCRIBE: | 359 | case IPC_SUBSCRIBE: |
355 | { | 360 | { |
361 | // TODO: Check if they're permitted to use these events | ||
356 | struct json_object *request = json_tokener_parse(buf); | 362 | struct json_object *request = json_tokener_parse(buf); |
357 | if (request == NULL) { | 363 | if (request == NULL) { |
358 | ipc_send_reply(client, "{\"success\": false}", 18); | 364 | ipc_send_reply(client, "{\"success\": false}", 18); |
@@ -391,6 +397,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
391 | 397 | ||
392 | case IPC_GET_WORKSPACES: | 398 | case IPC_GET_WORKSPACES: |
393 | { | 399 | { |
400 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | ||
401 | goto exit_denied; | ||
402 | } | ||
394 | json_object *workspaces = json_object_new_array(); | 403 | json_object *workspaces = json_object_new_array(); |
395 | container_map(&root_container, ipc_get_workspaces_callback, workspaces); | 404 | container_map(&root_container, ipc_get_workspaces_callback, workspaces); |
396 | const char *json_string = json_object_to_json_string(workspaces); | 405 | const char *json_string = json_object_to_json_string(workspaces); |
@@ -401,6 +410,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
401 | 410 | ||
402 | case IPC_GET_INPUTS: | 411 | case IPC_GET_INPUTS: |
403 | { | 412 | { |
413 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | ||
414 | goto exit_denied; | ||
415 | } | ||
404 | json_object *inputs = json_object_new_array(); | 416 | json_object *inputs = json_object_new_array(); |
405 | if (input_devices) { | 417 | if (input_devices) { |
406 | for(int i=0; i<input_devices->length; i++) { | 418 | for(int i=0; i<input_devices->length; i++) { |
@@ -424,6 +436,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
424 | 436 | ||
425 | case IPC_GET_OUTPUTS: | 437 | case IPC_GET_OUTPUTS: |
426 | { | 438 | { |
439 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | ||
440 | goto exit_denied; | ||
441 | } | ||
427 | json_object *outputs = json_object_new_array(); | 442 | json_object *outputs = json_object_new_array(); |
428 | container_map(&root_container, ipc_get_outputs_callback, outputs); | 443 | container_map(&root_container, ipc_get_outputs_callback, outputs); |
429 | const char *json_string = json_object_to_json_string(outputs); | 444 | const char *json_string = json_object_to_json_string(outputs); |
@@ -434,6 +449,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
434 | 449 | ||
435 | case IPC_GET_TREE: | 450 | case IPC_GET_TREE: |
436 | { | 451 | { |
452 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | ||
453 | goto exit_denied; | ||
454 | } | ||
437 | json_object *tree = ipc_json_describe_container_recursive(&root_container); | 455 | json_object *tree = ipc_json_describe_container_recursive(&root_container); |
438 | const char *json_string = json_object_to_json_string(tree); | 456 | const char *json_string = json_object_to_json_string(tree); |
439 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 457 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); |
@@ -498,6 +516,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
498 | 516 | ||
499 | case IPC_GET_BAR_CONFIG: | 517 | case IPC_GET_BAR_CONFIG: |
500 | { | 518 | { |
519 | if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) { | ||
520 | goto exit_denied; | ||
521 | } | ||
501 | if (!buf[0]) { | 522 | if (!buf[0]) { |
502 | // Send list of configured bar IDs | 523 | // Send list of configured bar IDs |
503 | json_object *bars = json_object_new_array(); | 524 | json_object *bars = json_object_new_array(); |
@@ -538,7 +559,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
538 | goto exit_cleanup; | 559 | goto exit_cleanup; |
539 | } | 560 | } |
540 | 561 | ||
541 | //exit_denied: | 562 | exit_denied: |
542 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); | 563 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); |
543 | 564 | ||
544 | exit_cleanup: | 565 | exit_cleanup: |
@@ -589,10 +610,33 @@ void ipc_get_outputs_callback(swayc_t *container, void *data) { | |||
589 | } | 610 | } |
590 | 611 | ||
591 | void ipc_send_event(const char *json_string, enum ipc_command_type event) { | 612 | void ipc_send_event(const char *json_string, enum ipc_command_type event) { |
613 | static struct { | ||
614 | enum ipc_command_type event; | ||
615 | enum ipc_feature feature; | ||
616 | } security_mappings[] = { | ||
617 | { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE }, | ||
618 | { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT }, | ||
619 | { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE }, | ||
620 | { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW }, | ||
621 | { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING }, | ||
622 | { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT } | ||
623 | }; | ||
624 | |||
625 | uint32_t security_mask = 0; | ||
626 | for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) { | ||
627 | if (security_mappings[i].event == event) { | ||
628 | security_mask = security_mappings[i].feature; | ||
629 | break; | ||
630 | } | ||
631 | } | ||
632 | |||
592 | int i; | 633 | int i; |
593 | struct ipc_client *client; | 634 | struct ipc_client *client; |
594 | for (i = 0; i < ipc_client_list->length; i++) { | 635 | for (i = 0; i < ipc_client_list->length; i++) { |
595 | client = ipc_client_list->items[i]; | 636 | client = ipc_client_list->items[i]; |
637 | if (!(client->security_policy & security_mask)) { | ||
638 | continue; | ||
639 | } | ||
596 | if ((client->subscribed_events & event_mask(event)) == 0) { | 640 | if ((client->subscribed_events & event_mask(event)) == 0) { |
597 | continue; | 641 | continue; |
598 | } | 642 | } |