From 1678024cd8e9a20bf07d1bd1dda76104b5418391 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Aug 2017 15:05:36 +0200 Subject: capability: change capability_bounding_set_drop() to be work without privileges when executing a NOP This way daemons which already dropped all caps may use the call to drop priviliges again, which becomes a non-failing NOP. --- src/basic/capability-util.c | 89 +++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 40 deletions(-) (limited to 'src/basic/capability-util.c') diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index 4a78b14d8..952bcc2d7 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -153,7 +153,7 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) { } int capability_bounding_set_drop(uint64_t keep, bool right_now) { - _cleanup_cap_free_ cap_t after_cap = NULL; + _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL; cap_flag_value_t fv; unsigned long i; int r; @@ -163,71 +163,80 @@ int capability_bounding_set_drop(uint64_t keep, bool right_now) { * executing init!), so get it back temporarily so that we can * call PR_CAPBSET_DROP. */ - after_cap = cap_get_proc(); - if (!after_cap) + before_cap = cap_get_proc(); + if (!before_cap) return -errno; - if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) + if (cap_get_flag(before_cap, 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(after_cap); - if (!temp_cap) { - r = -errno; - goto finish; - } + temp_cap = cap_dup(before_cap); + if (!temp_cap) + return -errno; - if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { - r = -errno; - goto finish; - } + if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) + return -errno; - if (cap_set_proc(temp_cap) < 0) { - r = -errno; - goto finish; - } + if (cap_set_proc(temp_cap) < 0) + 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. */ } + after_cap = cap_dup(before_cap); + if (!after_cap) + return -errno; + for (i = 0; i <= cap_last_cap(); i++) { + cap_value_t v; - if (!(keep & (UINT64_C(1) << i))) { - cap_value_t v; + if ((keep & (UINT64_C(1) << i))) + continue; - /* Drop it from the bounding set */ - if (prctl(PR_CAPBSET_DROP, i) < 0) { - r = -errno; + /* Drop it from the bounding set */ + if (prctl(PR_CAPBSET_DROP, i) < 0) { + r = -errno; + + /* If dropping the capability failed, let's see if we didn't have it in the first place. If so, + * continue anyway, as dropping a capability we didn't have in the first place doesn't really + * matter anyway. */ + if (prctl(PR_CAPBSET_READ, i) != 0) goto finish; - } - v = (cap_value_t) i; + } + v = (cap_value_t) i; + + /* Also drop it from the inheritable set, so + * that anything we exec() loses the + * capability for good. */ + if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { + r = -errno; + goto finish; + } - /* Also drop it from the inheritable set, so - * that anything we exec() loses the - * capability for good. */ - if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { + /* If we shall apply this right now drop it + * also from our own capability sets. */ + if (right_now) { + if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || + cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { r = -errno; goto finish; } - - /* If we shall apply this right now drop it - * also from our own capability sets. */ - if (right_now) { - if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || - cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { - r = -errno; - goto finish; - } - } } } r = 0; finish: - if (cap_set_proc(after_cap) < 0) - return -errno; + if (cap_set_proc(after_cap) < 0) { + /* If there are no actual changes anyway then let's ignore this error. */ + if (cap_compare(before_cap, after_cap) != 0) + r = -errno; + } return r; } -- cgit v1.2.3