2ad8850398
- When policies are allocated, the ipc target path goes through symlink resolution. The result is used as the canonical for matching pids to policies at runtime. In particular, this matches up with the target of the `/proc/<pid>/exe`. - There's a possible race condition if this isn't done correctly, read below. Originally, validate_ipc_target() always tried to resolve its argument for symlinks, and returned a parogram target string if it validates. This created a possible race condition with security implications. The problem is that get_feature_policy() first independently resolved the policy target in order to check whether a policy already exists. If it didn't find any, it called alloc_feature_policy() which called validate_ipc_target() which resolved the policy target again. In the time between the two checks, the symlink could be altered, and a lucky attacker could fool the program into thinking that a policy doesn't exist for a target, and then switch the symlink to point at another file. At the very least this could allow him to create two policies for the same program target, and possibly to bypass security by associating the permissions for one target with another, or force default permissions to apply to a target for which a more specific rule has been configured. So we don't that. Instead, the policy target is resolved once and that result is used for the rest of the lookup/creation process.
162 lines
4.1 KiB
C
162 lines
4.1 KiB
C
#define _XOPEN_SOURCE 500
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#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, 2))) {
|
|
return error;
|
|
}
|
|
if ((error = check_security_config())) {
|
|
return error;
|
|
}
|
|
|
|
char *program = NULL;
|
|
|
|
if (!strcmp(argv[0], "*")) {
|
|
program = strdup(argv[0]);
|
|
} else if (!(program = resolve_path(argv[0]))) {
|
|
return cmd_results_new(
|
|
CMD_INVALID, "ipc", "Unable to resolve IPC Policy target.");
|
|
}
|
|
if (config->reading && strcmp("{", argv[1]) != 0) {
|
|
return cmd_results_new(CMD_INVALID, "ipc",
|
|
"Expected '{' at start of IPC config definition.");
|
|
}
|
|
|
|
if (!config->reading) {
|
|
return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file.");
|
|
}
|
|
|
|
current_policy = alloc_ipc_policy(program);
|
|
list_add(config->ipc_policies, current_policy);
|
|
|
|
free(program);
|
|
return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL);
|
|
}
|
|
|
|
struct cmd_results *cmd_ipc_events(int argc, char **argv) {
|
|
struct cmd_results *error = NULL;
|
|
if ((error = checkarg(argc, "events", EXPECTED_EQUAL_TO, 1))) {
|
|
return error;
|
|
}
|
|
|
|
if (config->reading && strcmp("{", argv[0]) != 0) {
|
|
return cmd_results_new(CMD_INVALID, "events",
|
|
"Expected '{' at start of IPC event config definition.");
|
|
}
|
|
|
|
if (!config->reading) {
|
|
return cmd_results_new(CMD_FAILURE, "events", "Can only be used in config file.");
|
|
}
|
|
|
|
return cmd_results_new(CMD_BLOCK_IPC_EVENTS, NULL, NULL);
|
|
}
|
|
|
|
struct cmd_results *cmd_ipc_cmd(int argc, char **argv) {
|
|
struct cmd_results *error = NULL;
|
|
if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
|
|
return error;
|
|
}
|
|
|
|
bool enabled;
|
|
if (strcmp(argv[0], "enabled") == 0) {
|
|
enabled = true;
|
|
} else if (strcmp(argv[0], "disabled") == 0) {
|
|
enabled = false;
|
|
} else {
|
|
return cmd_results_new(CMD_INVALID, argv[-1],
|
|
"Argument must be one of 'enabled' or 'disabled'");
|
|
}
|
|
|
|
struct {
|
|
char *name;
|
|
enum ipc_feature type;
|
|
} types[] = {
|
|
{ "*", IPC_FEATURE_ALL_COMMANDS },
|
|
{ "command", IPC_FEATURE_COMMAND },
|
|
{ "workspaces", IPC_FEATURE_GET_WORKSPACES },
|
|
{ "outputs", IPC_FEATURE_GET_OUTPUTS },
|
|
{ "tree", IPC_FEATURE_GET_TREE },
|
|
{ "marks", IPC_FEATURE_GET_MARKS },
|
|
{ "bar-config", IPC_FEATURE_GET_BAR_CONFIG },
|
|
{ "inputs", IPC_FEATURE_GET_INPUTS },
|
|
};
|
|
|
|
uint32_t type = 0;
|
|
|
|
for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
|
|
if (strcmp(types[i].name, argv[-1]) == 0) {
|
|
type = types[i].type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (enabled) {
|
|
current_policy->features |= type;
|
|
sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]);
|
|
} else {
|
|
current_policy->features &= ~type;
|
|
sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]);
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
}
|
|
|
|
struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) {
|
|
struct cmd_results *error = NULL;
|
|
if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
|
|
return error;
|
|
}
|
|
|
|
bool enabled;
|
|
if (strcmp(argv[0], "enabled") == 0) {
|
|
enabled = true;
|
|
} else if (strcmp(argv[0], "disabled") == 0) {
|
|
enabled = false;
|
|
} else {
|
|
return cmd_results_new(CMD_INVALID, argv[-1],
|
|
"Argument must be one of 'enabled' or 'disabled'");
|
|
}
|
|
|
|
struct {
|
|
char *name;
|
|
enum ipc_feature type;
|
|
} types[] = {
|
|
{ "*", IPC_FEATURE_ALL_EVENTS },
|
|
{ "workspace", IPC_FEATURE_EVENT_WORKSPACE },
|
|
{ "output", IPC_FEATURE_EVENT_OUTPUT },
|
|
{ "mode", IPC_FEATURE_EVENT_MODE },
|
|
{ "window", IPC_FEATURE_EVENT_WINDOW },
|
|
{ "binding", IPC_FEATURE_EVENT_BINDING },
|
|
{ "input", IPC_FEATURE_EVENT_INPUT },
|
|
};
|
|
|
|
uint32_t type = 0;
|
|
|
|
for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
|
|
if (strcmp(types[i].name, argv[-1]) == 0) {
|
|
type = types[i].type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (enabled) {
|
|
current_policy->features |= type;
|
|
sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]);
|
|
} else {
|
|
current_policy->features &= ~type;
|
|
sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]);
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
}
|