diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index 5a4d020f52..ae269e8a8a 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -161,28 +161,21 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) { return 0; } -int capability_bounding_set_drop(uint64_t keep, bool right_now) { - _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL; +int capability_gain_cap_setpcap(cap_t *ret_before_caps) { + _cleanup_cap_free_ cap_t caps = NULL; cap_flag_value_t fv; - int r; - - /* If we are run as PID 1 we will lack CAP_SETPCAP by default - * in the effective set (yes, the kernel drops that when - * executing init!), so get it back temporarily so that we can - * call PR_CAPBSET_DROP. */ - - before_cap = cap_get_proc(); - if (!before_cap) + caps = cap_get_proc(); + if (!caps) return -errno; - if (cap_get_flag(before_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) + if (cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) return -errno; if (fv != CAP_SET) { _cleanup_cap_free_ cap_t temp_cap = NULL; static const cap_value_t v = CAP_SETPCAP; - temp_cap = cap_dup(before_cap); + temp_cap = cap_dup(caps); if (!temp_cap) return -errno; @@ -193,8 +186,27 @@ int capability_bounding_set_drop(uint64_t keep, bool right_now) { log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m"); /* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this just means - * we'll fail later, when we actually intend to drop some capabilities. */ + * we'll fail later, when we actually intend to drop some capabilities or try to set securebits. */ } + if (ret_before_caps) + /* Return the capabilities as they have been before setting CAP_SETPCAP */ + *ret_before_caps = TAKE_PTR(caps); + + return 0; +} + +int capability_bounding_set_drop(uint64_t keep, bool right_now) { + _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL; + int r; + + /* If we are run as PID 1 we will lack CAP_SETPCAP by default + * in the effective set (yes, the kernel drops that when + * executing init!), so get it back temporarily so that we can + * call PR_CAPBSET_DROP. */ + + r = capability_gain_cap_setpcap(&before_cap); + if (r < 0) + return r; after_cap = cap_dup(before_cap); if (!after_cap) diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index fcc59daedc..fdf6ef8462 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -14,6 +14,7 @@ unsigned cap_last_cap(void); int have_effective_cap(int value); +int capability_gain_cap_setpcap(cap_t *return_caps); int capability_bounding_set_drop(uint64_t keep, bool right_now); int capability_bounding_set_drop_usermode(uint64_t keep); diff --git a/src/core/execute.c b/src/core/execute.c index f493accf2e..4274d39285 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1077,26 +1077,42 @@ static int enforce_groups(gid_t gid, const gid_t *supplementary_gids, int ngids) return 0; } +static int set_securebits(int bits, int mask) { + int current, applied; + current = prctl(PR_GET_SECUREBITS); + if (current < 0) + return -errno; + /* Clear all securebits defined in mask and set bits */ + applied = (current & ~mask) | bits; + if (current == applied) + return 0; + if (prctl(PR_SET_SECUREBITS, applied) < 0) + return -errno; + return 1; +} + static int enforce_user(const ExecContext *context, uid_t uid) { assert(context); + int r; if (!uid_is_valid(uid)) return 0; /* Sets (but doesn't look up) the uid and make sure we keep the - * capabilities while doing so. */ + * capabilities while doing so. For setting secure bits the capability CAP_SETPCAP is + * required, so we also need keep-caps in this case. + */ - if (context->capability_ambient_set != 0) { + if (context->capability_ambient_set != 0 || context->secure_bits != 0) { /* First step: If we need to keep capabilities but * drop privileges we need to make sure we keep our * caps, while we drop privileges. */ if (uid != 0) { - int sb = context->secure_bits | 1<