capabilities: keep bounding set in non-inverted format.

Change the capability bounding set parser and logic so that the bounding
set is kept as a positive set internally. This means that the set
reflects those capabilities that we want to keep instead of drop.
This commit is contained in:
Ismo Puustinen 2016-01-08 00:00:04 +02:00
parent f466acdc63
commit a103496ca5
13 changed files with 71 additions and 61 deletions

View file

@ -96,7 +96,7 @@ unsigned long cap_last_cap(void) {
return p;
}
int capability_bounding_set_drop(uint64_t drop, bool right_now) {
int capability_bounding_set_drop(uint64_t keep, bool right_now) {
_cleanup_cap_free_ cap_t after_cap = NULL;
cap_flag_value_t fv;
unsigned long i;
@ -137,7 +137,7 @@ int capability_bounding_set_drop(uint64_t drop, bool right_now) {
for (i = 0; i <= cap_last_cap(); i++) {
if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
if (!(keep & (UINT64_C(1) << i))) {
cap_value_t v;
/* Drop it from the bounding set */
@ -176,7 +176,7 @@ finish:
return r;
}
static int drop_from_file(const char *fn, uint64_t drop) {
static int drop_from_file(const char *fn, uint64_t keep) {
int r, k;
uint32_t hi, lo;
uint64_t current, after;
@ -196,7 +196,7 @@ static int drop_from_file(const char *fn, uint64_t drop) {
return -EIO;
current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
after = current & ~drop;
after = current & keep;
if (current == after)
return 0;
@ -213,14 +213,14 @@ static int drop_from_file(const char *fn, uint64_t drop) {
return r;
}
int capability_bounding_set_drop_usermode(uint64_t drop) {
int capability_bounding_set_drop_usermode(uint64_t keep) {
int r;
r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep);
if (r < 0)
return r;
r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep);
if (r < 0)
return r;
@ -257,7 +257,7 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
/* Drop all caps from the bounding set, except the ones we want */
r = capability_bounding_set_drop(~keep_capabilities, true);
r = capability_bounding_set_drop(keep_capabilities, true);
if (r < 0)
return log_error_errno(r, "Failed to drop capabilities: %m");

View file

@ -29,10 +29,12 @@
#include "macro.h"
#include "util.h"
#define CAP_ALL (uint64_t) -1
unsigned long cap_last_cap(void);
int have_effective_cap(int value);
int capability_bounding_set_drop(uint64_t drop, bool right_now);
int capability_bounding_set_drop_usermode(uint64_t drop);
int capability_bounding_set_drop(uint64_t keep, bool right_now);
int capability_bounding_set_drop_usermode(uint64_t keep);
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
@ -46,3 +48,9 @@ static inline void cap_free_charpp(char **p) {
cap_free(*p);
}
#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
static inline bool cap_test_all(uint64_t caps) {
uint64_t m;
m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1;
return (caps & m) == m;
}

View file

@ -293,9 +293,7 @@ static int property_get_capability_bounding_set(
assert(reply);
assert(c);
/* We store this negated internally, to match the kernel, but
* we expose it normalized. */
return sd_bus_message_append(reply, "t", ~c->capability_bounding_set_drop);
return sd_bus_message_append(reply, "t", c->capability_bounding_set);
}
static int property_get_capabilities(

View file

@ -1866,8 +1866,8 @@ static int exec_child(
}
}
if (context->capability_bounding_set_drop) {
r = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
if (!cap_test_all(context->capability_bounding_set)) {
r = capability_bounding_set_drop(context->capability_bounding_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return r;
@ -2114,6 +2114,7 @@ void exec_context_init(ExecContext *c) {
c->timer_slack_nsec = NSEC_INFINITY;
c->personality = PERSONALITY_INVALID;
c->runtime_directory_mode = 0755;
c->capability_bounding_set = CAP_ALL;
}
void exec_context_done(ExecContext *c) {
@ -2517,12 +2518,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
(c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
(c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
if (c->capability_bounding_set_drop) {
if (c->capability_bounding_set != CAP_ALL) {
unsigned long l;
fprintf(f, "%sCapabilityBoundingSet:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l)))
if (c->capability_bounding_set & (UINT64_C(1) << l))
fprintf(f, " %s", strna(capability_to_name(l)));
fputs("\n", f);

View file

@ -155,7 +155,7 @@ struct ExecContext {
char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
unsigned long mount_flags;
uint64_t capability_bounding_set_drop;
uint64_t capability_bounding_set;
cap_t capabilities;
int secure_bits;

View file

@ -47,7 +47,7 @@ $1.SyslogLevel, config_parse_log_level, 0,
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context)
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
$1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop)
$1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set)
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
$1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context)
m4_ifdef(`HAVE_SECCOMP',

View file

@ -38,6 +38,7 @@
#include "bus-internal.h"
#include "bus-util.h"
#include "cap-list.h"
#include "capability-util.h"
#include "cgroup.h"
#include "conf-parser.h"
#include "cpu-set-util.h"
@ -1024,7 +1025,7 @@ int config_parse_exec_secure_bits(const char *unit,
return 0;
}
int config_parse_bounding_set(
int config_parse_capability_set(
const char *unit,
const char *filename,
unsigned line,
@ -1036,8 +1037,8 @@ int config_parse_bounding_set(
void *data,
void *userdata) {
uint64_t *capability_bounding_set_drop = data;
uint64_t capability_bounding_set, sum = 0;
uint64_t *capability_set = data;
uint64_t sum = 0, initial = 0;
bool invert = false;
const char *p;
@ -1051,10 +1052,8 @@ int config_parse_bounding_set(
rvalue++;
}
/* Note that we store this inverted internally, since the
* kernel wants it like this. But we actually expose it
* non-inverted everywhere to have a fully normalized
* interface. */
if (strcmp(lvalue, "CapabilityBoundingSet") == 0)
initial = CAP_ALL; /* initialized to all bits on */
p = rvalue;
for (;;) {
@ -1080,11 +1079,14 @@ int config_parse_bounding_set(
sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap;
}
capability_bounding_set = invert ? ~sum : sum;
if (*capability_bounding_set_drop != 0 && capability_bounding_set != 0)
*capability_bounding_set_drop = ~(~*capability_bounding_set_drop | capability_bounding_set);
sum = invert ? ~sum : sum;
if (sum == 0 || *capability_set == initial)
/* "" or uninitialized data -> replace */
*capability_set = sum;
else
*capability_bounding_set_drop = ~capability_bounding_set;
/* previous data -> merge */
*capability_set |= sum;
return 0;
}
@ -4002,7 +4004,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_log_level, "LEVEL" },
{ config_parse_exec_capabilities, "CAPABILITIES" },
{ config_parse_exec_secure_bits, "SECUREBITS" },
{ config_parse_bounding_set, "BOUNDINGSET" },
{ config_parse_capability_set, "BOUNDINGSET" },
{ config_parse_limit, "LIMIT" },
{ config_parse_unit_deps, "UNIT [...]" },
{ config_parse_exec, "PATH [ARGUMENT [...]]" },

View file

@ -56,7 +56,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, uns
int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_capabilities(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_capability_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bytes_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_sec_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);

View file

@ -117,7 +117,7 @@ static usec_t arg_runtime_watchdog = 0;
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
static char **arg_default_environment = NULL;
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
static uint64_t arg_capability_bounding_set_drop = 0;
static uint64_t arg_capability_bounding_set = CAP_ALL;
static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
static Set* arg_syscall_archs = NULL;
@ -644,7 +644,7 @@ static int parse_config_file(void) {
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
{ "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
{ "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
{ "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
#ifdef HAVE_SECCOMP
{ "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
#endif
@ -1631,14 +1631,14 @@ int main(int argc, char *argv[]) {
if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
log_error_errno(errno, "Failed to adjust timer slack: %m");
if (arg_capability_bounding_set_drop) {
r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
if (!cap_test_all(arg_capability_bounding_set)) {
r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
if (r < 0) {
log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
error_message = "Failed to drop capability bounding set of usermode helpers";
goto finish;
}
r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
r = capability_bounding_set_drop(arg_capability_bounding_set, true);
if (r < 0) {
log_emergency_errno(r, "Failed to drop capability bounding set: %m");
error_message = "Failed to drop capability bounding set";

View file

@ -3231,7 +3231,7 @@ int unit_patch_contexts(Unit *u) {
ec->no_new_privileges = true;
if (ec->private_devices)
ec->capability_bounding_set_drop |= (uint64_t) 1ULL << (uint64_t) CAP_MKNOD;
ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD);
}
cc = unit_get_cgroup_context(u);

View file

@ -134,7 +134,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
if (unshare(CLONE_NEWNET) < 0)
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
r = capability_bounding_set_drop(~retain, true);
r = capability_bounding_set_drop(retain, true);
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
@ -208,7 +208,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
if (unshare(CLONE_NEWNET) < 0)
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
r = capability_bounding_set_drop(~retain, true);
r = capability_bounding_set_drop(retain, true);
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");

View file

@ -1482,7 +1482,7 @@ static int setup_journal(const char *directory) {
}
static int drop_capabilities(void) {
return capability_bounding_set_drop(~arg_retain, false);
return capability_bounding_set_drop(arg_retain, false);
}
static int reset_audit_loginuid(void) {

View file

@ -28,6 +28,7 @@
#include <unistd.h>
#include "alloc-util.h"
#include "capability-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hashmap.h"
@ -625,8 +626,8 @@ static uint64_t make_cap(int cap) {
return ((uint64_t) 1ULL << (uint64_t) cap);
}
static void test_config_parse_bounding_set(void) {
/* int config_parse_bounding_set(
static void test_config_parse_capability_set(void) {
/* int config_parse_capability_set(
const char *unit,
const char *filename,
unsigned line,
@ -638,38 +639,38 @@ static void test_config_parse_bounding_set(void) {
void *data,
void *userdata) */
int r;
uint64_t capability_bounding_set_drop = 0;
uint64_t capability_bounding_set = 0;
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, "CAP_NET_RAW",
&capability_bounding_set_drop, NULL);
&capability_bounding_set, NULL);
assert_se(r >= 0);
assert_se(capability_bounding_set_drop == ~make_cap(CAP_NET_RAW));
assert_se(capability_bounding_set == make_cap(CAP_NET_RAW));
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
&capability_bounding_set_drop, NULL);
&capability_bounding_set, NULL);
assert_se(r >= 0);
assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, "",
&capability_bounding_set_drop, NULL);
&capability_bounding_set, NULL);
assert_se(r >= 0);
assert_se(capability_bounding_set_drop == ~((uint64_t) 0ULL));
assert_se(capability_bounding_set == UINT64_C(0));
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, "~",
&capability_bounding_set_drop, NULL);
&capability_bounding_set, NULL);
assert_se(r >= 0);
assert_se(capability_bounding_set_drop == (uint64_t) 0ULL);
assert_se(cap_test_all(capability_bounding_set));
capability_bounding_set_drop = 0;
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
capability_bounding_set = 0;
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
&capability_bounding_set_drop, NULL);
&capability_bounding_set, NULL);
assert_se(r >= 0);
assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
}
static void test_config_parse_rlimit(void) {
@ -829,7 +830,7 @@ int main(int argc, char *argv[]) {
r = test_unit_file_get_set();
test_config_parse_exec();
test_config_parse_bounding_set();
test_config_parse_capability_set();
test_config_parse_rlimit();
test_config_parse_pass_environ();
test_load_env_file_1();