Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
#define _XOPEN_SOURCE 500
|
|
|
|
#include <string.h>
|
|
|
|
#include "sway/commands.h"
|
|
|
|
#include "sway/config.h"
|
|
|
|
#include "sway/security.h"
|
Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
#include "util.h"
|
|
|
|
#include "log.h"
|
|
|
|
|
|
|
|
static enum secure_feature get_features(int argc, char **argv,
|
|
|
|
struct cmd_results **error) {
|
|
|
|
enum secure_feature features = 0;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
char *name;
|
|
|
|
enum secure_feature feature;
|
|
|
|
} feature_names[] = {
|
|
|
|
{ "lock", FEATURE_LOCK },
|
|
|
|
{ "panel", FEATURE_PANEL },
|
|
|
|
{ "background", FEATURE_BACKGROUND },
|
|
|
|
{ "screenshot", FEATURE_SCREENSHOT },
|
|
|
|
{ "fullscreen", FEATURE_FULLSCREEN },
|
|
|
|
{ "keyboard", FEATURE_KEYBOARD },
|
|
|
|
{ "mouse", FEATURE_MOUSE },
|
|
|
|
};
|
|
|
|
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
|
size_t j;
|
|
|
|
for (j = 0; j < sizeof(feature_names) / sizeof(feature_names[0]); ++j) {
|
|
|
|
if (strcmp(feature_names[j].name, argv[i]) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (j == sizeof(feature_names) / sizeof(feature_names[0])) {
|
|
|
|
*error = cmd_results_new(CMD_INVALID,
|
|
|
|
"permit", "Invalid feature grant %s", argv[i]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
features |= feature_names[j].feature;
|
|
|
|
}
|
|
|
|
return features;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cmd_results *cmd_permit(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if ((error = check_security_config())) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
bool assign_perms = true;
|
|
|
|
char *program = NULL;
|
|
|
|
|
Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
if (!strcmp(argv[0], "*")) {
|
|
|
|
program = strdup(argv[0]);
|
|
|
|
} else {
|
|
|
|
program = resolve_path(argv[0]);
|
|
|
|
}
|
|
|
|
if (!program) {
|
|
|
|
sway_assert(program, "Unable to resolve IPC permit target '%s'."
|
|
|
|
" will issue empty policy", argv[0]);
|
|
|
|
assign_perms = false;
|
|
|
|
program = strdup(argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct feature_policy *policy = get_feature_policy(program);
|
|
|
|
if (policy && assign_perms) {
|
Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
policy->features |= get_features(argc, argv, &error);
|
|
|
|
sway_log(L_DEBUG, "Permissions granted to %s for features %d",
|
|
|
|
policy->program, policy->features);
|
Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
}
|
|
|
|
|
Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
free(program);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cmd_results *cmd_reject(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if ((error = check_security_config())) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
char *program = NULL;
|
|
|
|
if (!strcmp(argv[0], "*")) {
|
|
|
|
program = strdup(argv[0]);
|
|
|
|
} else {
|
|
|
|
program = resolve_path(argv[0]);
|
|
|
|
}
|
|
|
|
if (!program) {
|
|
|
|
// Punt
|
|
|
|
sway_log(L_INFO, "Unable to resolve IPC reject target '%s'."
|
|
|
|
" Will use provided path", argv[0]);
|
|
|
|
program = strdup(argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct feature_policy *policy = get_feature_policy(program);
|
|
|
|
policy->features &= ~get_features(argc, argv, &error);
|
|
|
|
|
|
|
|
sway_log(L_DEBUG, "Permissions granted to %s for features %d",
|
|
|
|
policy->program, policy->features);
|
|
|
|
|
Handle symlinks as IPC security targets
- 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.
8 years ago
|
|
|
free(program);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|