Skip to content

Commit

Permalink
nspawn: don't hard fail when setting capabilities
Browse files Browse the repository at this point in the history
The OCI changes in systemd#9762 broke a use case in which we use nspawn from
inside a container that has dropped capabilities from the bounding set
that nspawn expected to retain. In an attempt to keep OCI compliance
and support our use case, I made hard failing on setting capabilities
not in the bounding set optional and log about it.

Fixes systemd#12539
  • Loading branch information
anitazha committed Jun 4, 2019
1 parent 7192323 commit 4bd380f
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 5 deletions.
28 changes: 26 additions & 2 deletions src/basic/capability-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,14 @@ bool ambient_capabilities_supported(void) {
return cache;
}

int capability_quintet_enforce(const CapabilityQuintet *q) {
int capability_quintet_enforce(const CapabilityQuintet *q, bool skip_if_not_in_bset) {
_cleanup_cap_free_ cap_t c = NULL, modified = NULL;
int r;

if (q->ambient != (uint64_t) -1) {
unsigned long i;
bool changed = false;
uint64_t ambient_set = q->ambient;

c = cap_get_proc();
if (!c)
Expand All @@ -379,6 +380,7 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
/* In order to raise the ambient caps set we first need to raise the matching inheritable + permitted
* cap */
for (i = 0; i <= cap_last_cap(); i++) {
bool cap_in_bset = prctl(PR_CAPBSET_READ, i) == 1;
uint64_t m = UINT64_C(1) << i;
cap_value_t cv = (cap_value_t) i;
cap_flag_value_t old_value_inheritable, old_value_permitted;
Expand All @@ -394,6 +396,12 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
if (old_value_inheritable == CAP_SET && old_value_permitted == CAP_SET)
continue;

if (!cap_in_bset && skip_if_not_in_bset) {
ambient_set &= ~m;
log_debug("Not setting a capability in the ambient set");
continue;
}

if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, CAP_SET) < 0)
return -errno;
if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, CAP_SET) < 0)
Expand All @@ -406,7 +414,7 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
if (cap_set_proc(c) < 0)
return -errno;

r = capability_ambient_set_apply(q->ambient, false);
r = capability_ambient_set_apply(ambient_set, false);
if (r < 0)
return r;
}
Expand All @@ -422,6 +430,7 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
}

for (i = 0; i <= cap_last_cap(); i++) {
bool cap_in_bset = prctl(PR_CAPBSET_READ, i) == 1;
uint64_t m = UINT64_C(1) << i;
cap_value_t cv = (cap_value_t) i;

Expand All @@ -441,6 +450,11 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
new_value = (q->inheritable & m) ? CAP_SET : CAP_CLEAR;

if (old_value != new_value) {
if (new_value == CAP_SET && !cap_in_bset && skip_if_not_in_bset) {
log_debug("Not setting a capability in the inheritable set");
continue;
}

changed = true;

if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, new_value) < 0)
Expand All @@ -461,6 +475,11 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
new_value = (q->permitted & m) ? CAP_SET : CAP_CLEAR;

if (old_value != new_value) {
if (new_value == CAP_SET && !cap_in_bset && skip_if_not_in_bset) {
log_debug("Not setting a capability in the permitted set");
continue;
}

changed = true;

if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, new_value) < 0)
Expand All @@ -481,6 +500,11 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
new_value = (q->effective & m) ? CAP_SET : CAP_CLEAR;

if (old_value != new_value) {
if (new_value == CAP_SET && !cap_in_bset && skip_if_not_in_bset) {
log_debug("Not setting a capability in the effective set");
continue;
}

changed = true;

if (cap_set_flag(c, CAP_EFFECTIVE, 1, &cv, new_value) < 0)
Expand Down
5 changes: 4 additions & 1 deletion src/basic/capability-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,7 @@ static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
q->ambient != (uint64_t) -1;
}

int capability_quintet_enforce(const CapabilityQuintet *q);
/* Dropping unsupported capabilities doesn't error out but setting them does.
* If you don't want to hard fail when setting a capability not in the current
* bounding set, set skip_if_not_in_bset to true. */
int capability_quintet_enforce(const CapabilityQuintet *q, bool skip_if_not_in_bset);
12 changes: 10 additions & 2 deletions src/nspawn/nspawn.c
Original file line number Diff line number Diff line change
Expand Up @@ -2286,6 +2286,7 @@ static int setup_journal(const char *directory) {

static int drop_capabilities(uid_t uid) {
CapabilityQuintet q;
bool skip_if_not_in_bset;

/* Let's initialize all five capability sets to something valid. If the quintet was configured via
* OCI use that, but fill in missing bits. If it wasn't then derive the quintet in full from
Expand All @@ -2308,7 +2309,9 @@ static int drop_capabilities(uid_t uid) {

if (q.ambient == (uint64_t) -1 && ambient_capabilities_supported())
q.ambient = 0;
} else

skip_if_not_in_bset = false;
} else {
q = (CapabilityQuintet) {
.bounding = arg_caps_retain,
.effective = uid == 0 ? arg_caps_retain : 0,
Expand All @@ -2317,7 +2320,12 @@ static int drop_capabilities(uid_t uid) {
.ambient = ambient_capabilities_supported() ? 0 : (uint64_t) -1,
};

return capability_quintet_enforce(&q);
/* If we're not using OCI, don't hard fail if a capability cannot be set in order to
* maintain the same behavior as systemd < v242. */
skip_if_not_in_bset = true;
}

return capability_quintet_enforce(&q, skip_if_not_in_bset);
}

static int reset_audit_loginuid(void) {
Expand Down

0 comments on commit 4bd380f

Please sign in to comment.