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 @@ #include #include +#include "sway/security.h" #include "sway/commands.h" #include "sway/config.h" #include "ipc.h" #include "log.h" #include "util.h" +static struct ipc_policy *current_policy = NULL; + struct cmd_results *cmd_ipc(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) { return error; } - if (config->reading && strcmp("{", argv[0]) != 0) { + const char *program = argv[0]; + + if (config->reading && strcmp("{", argv[1]) != 0) { return cmd_results_new(CMD_INVALID, "ipc", "Expected '{' at start of IPC config definition."); } @@ -26,6 +31,8 @@ struct cmd_results *cmd_ipc(int argc, char **argv) { "This command is only permitted to run from " SYSCONFDIR "/sway/security"); } + current_policy = alloc_ipc_policy(program); + return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL); } @@ -86,10 +93,10 @@ struct cmd_results *cmd_ipc_cmd(int argc, char **argv) { } if (enabled) { - //config->ipc_policy |= type; - sway_log(L_DEBUG, "Enabled IPC %s feature %d", argv[-1], (int)type); + current_policy->features |= type; + sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]); } else { - //config->ipc_policy &= ~type; + current_policy->features &= ~type; sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]); } @@ -134,10 +141,10 @@ struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) { } if (enabled) { - //config->ipc_policy |= type; - sway_log(L_DEBUG, "Enabled IPC %s event %d", argv[-1], (int)type); + current_policy->features |= type; + sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]); } else { - //config->ipc_policy &= ~type; + current_policy->features &= ~type; sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]); } 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 { struct wlc_event_source *event_source; int fd; uint32_t payload_length; + uint32_t security_policy; enum ipc_command_type current_command; enum ipc_command_type subscribed_events; }; @@ -125,7 +126,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) { return ipc_sockaddr; } -/* static pid_t get_client_pid(int client_fd) { // FreeBSD supports getting uid/gid, but not pid #ifdef __linux__ @@ -141,7 +141,6 @@ static pid_t get_client_pid(int client_fd) { return -1; #endif } -*/ int ipc_handle_connection(int fd, uint32_t mask, void *data) { (void) fd; (void) data; @@ -172,6 +171,9 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { client->subscribed_events = 0; client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); + pid_t pid = get_client_pid(client->fd); + client->security_policy = get_ipc_policy(pid); + list_add(ipc_client_list, client); return 0; @@ -342,6 +344,9 @@ void ipc_client_handle_command(struct ipc_client *client) { switch (client->current_command) { case IPC_COMMAND: { + if (!(client->security_policy & IPC_FEATURE_COMMAND)) { + goto exit_denied; + } struct cmd_results *results = handle_command(buf, CONTEXT_IPC); const char *json = cmd_results_to_json(results); char reply[256]; @@ -353,6 +358,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_SUBSCRIBE: { + // TODO: Check if they're permitted to use these events struct json_object *request = json_tokener_parse(buf); if (request == NULL) { ipc_send_reply(client, "{\"success\": false}", 18); @@ -391,6 +397,9 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_WORKSPACES: { + if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { + goto exit_denied; + } json_object *workspaces = json_object_new_array(); container_map(&root_container, ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); @@ -401,6 +410,9 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_INPUTS: { + if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { + goto exit_denied; + } json_object *inputs = json_object_new_array(); if (input_devices) { for(int i=0; ilength; i++) { @@ -424,6 +436,9 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_OUTPUTS: { + if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { + goto exit_denied; + } json_object *outputs = json_object_new_array(); container_map(&root_container, ipc_get_outputs_callback, outputs); const char *json_string = json_object_to_json_string(outputs); @@ -434,6 +449,9 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_TREE: { + if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { + goto exit_denied; + } json_object *tree = ipc_json_describe_container_recursive(&root_container); const char *json_string = json_object_to_json_string(tree); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); @@ -498,6 +516,9 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_BAR_CONFIG: { + if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) { + goto exit_denied; + } if (!buf[0]) { // Send list of configured bar IDs json_object *bars = json_object_new_array(); @@ -538,7 +559,7 @@ void ipc_client_handle_command(struct ipc_client *client) { goto exit_cleanup; } -//exit_denied: +exit_denied: ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); exit_cleanup: @@ -589,10 +610,33 @@ void ipc_get_outputs_callback(swayc_t *container, void *data) { } void ipc_send_event(const char *json_string, enum ipc_command_type event) { + static struct { + enum ipc_command_type event; + enum ipc_feature feature; + } security_mappings[] = { + { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE }, + { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT }, + { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE }, + { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW }, + { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING }, + { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT } + }; + + uint32_t security_mask = 0; + for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) { + if (security_mappings[i].event == event) { + security_mask = security_mappings[i].feature; + break; + } + } + int i; struct ipc_client *client; for (i = 0; i < ipc_client_list->length; i++) { client = ipc_client_list->items[i]; + if (!(client->security_policy & security_mask)) { + continue; + } if ((client->subscribed_events & event_mask(event)) == 0) { continue; }