From: Anita Zhang Date: Mon, 3 Jun 2019 23:25:43 +0000 (-0700) Subject: nspawn: don't hard fail when setting capabilities X-Git-Tag: v243-rc1~256 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f66ad46066a9911192f0b49eb06dae7dafc0c983;p=thirdparty%2Fsystemd.git 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 --- diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index e3ed14f806c..d62bb62de47 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -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; diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index e69b2fbb957..b5bce29ab53 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -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); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 5079918ae15..1c0187ae5c1 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -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); }