nspawn: don't hard fail when setting capabilities
The OCI changes in #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 (hard fail if using OCI and log only if using nspawn cmdline). Fixes #12539
This commit is contained in:
committed by
Lennart Poettering
parent
a5a4dfa1bc
commit
f66ad46066
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "capability-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
@@ -364,6 +365,43 @@ bool ambient_capabilities_supported(void) {
|
||||
return cache;
|
||||
}
|
||||
|
||||
bool capability_quintet_mangle(CapabilityQuintet *q) {
|
||||
unsigned long i;
|
||||
uint64_t combined, drop = 0;
|
||||
bool ambient_supported;
|
||||
|
||||
assert(q);
|
||||
|
||||
combined = q->effective | q->bounding | q->inheritable | q->permitted;
|
||||
|
||||
ambient_supported = q->ambient != (uint64_t) -1;
|
||||
if (ambient_supported)
|
||||
combined |= q->ambient;
|
||||
|
||||
for (i = 0; i <= cap_last_cap(); i++) {
|
||||
unsigned long bit = UINT64_C(1) << i;
|
||||
if (!FLAGS_SET(combined, bit))
|
||||
continue;
|
||||
|
||||
if (prctl(PR_CAPBSET_READ, i) > 0)
|
||||
continue;
|
||||
|
||||
drop |= bit;
|
||||
|
||||
log_debug("Not in the current bounding set: %s", capability_to_name(i));
|
||||
}
|
||||
|
||||
q->effective &= ~drop;
|
||||
q->bounding &= ~drop;
|
||||
q->inheritable &= ~drop;
|
||||
q->permitted &= ~drop;
|
||||
|
||||
if (ambient_supported)
|
||||
q->ambient &= ~drop;
|
||||
|
||||
return drop != 0; /* Let the caller know we changed something */
|
||||
}
|
||||
|
||||
int capability_quintet_enforce(const CapabilityQuintet *q) {
|
||||
_cleanup_cap_free_ cap_t c = NULL, modified = NULL;
|
||||
int r;
|
||||
|
||||
@@ -69,4 +69,9 @@ static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
|
||||
q->ambient != (uint64_t) -1;
|
||||
}
|
||||
|
||||
/* Mangles the specified caps quintet taking the current bounding set into account:
|
||||
* drops all caps from all five sets if our bounding set doesn't allow them.
|
||||
* Returns true if the quintet was modified. */
|
||||
bool capability_quintet_mangle(CapabilityQuintet *q);
|
||||
|
||||
int capability_quintet_enforce(const CapabilityQuintet *q);
|
||||
|
||||
+12
-1
@@ -2317,7 +2317,11 @@ static int drop_capabilities(uid_t uid) {
|
||||
|
||||
if (q.ambient == (uint64_t) -1 && ambient_capabilities_supported())
|
||||
q.ambient = 0;
|
||||
} else
|
||||
|
||||
if (capability_quintet_mangle(&q))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Cannot set capabilities that are not in the current bounding set.");
|
||||
|
||||
} else {
|
||||
q = (CapabilityQuintet) {
|
||||
.bounding = arg_caps_retain,
|
||||
.effective = uid == 0 ? arg_caps_retain : 0,
|
||||
@@ -2326,6 +2330,13 @@ static int drop_capabilities(uid_t uid) {
|
||||
.ambient = ambient_capabilities_supported() ? 0 : (uint64_t) -1,
|
||||
};
|
||||
|
||||
/* If we're not using OCI, proceed with mangled capabilities (so we don't error out)
|
||||
* in order to maintain the same behavior as systemd < 242. */
|
||||
if (capability_quintet_mangle(&q))
|
||||
log_warning("Some capabilities will not be set because they are not in the current bounding set.");
|
||||
|
||||
}
|
||||
|
||||
return capability_quintet_enforce(&q);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user