From 256d6f3f2f25f0a624b81ec78c274bd74538f850 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 23 Oct 2025 23:27:34 +0900 Subject: [PATCH] capability-util: rework capability_gain_cap_setpcap() and capability_bounding_set_drop() This makes the functions use CapabilityQuintet, capability_get(), and capability_apply(). --- src/basic/capability-util.c | 74 ++++++++++++++----------------------- src/basic/capability-util.h | 2 +- src/core/exec-invoke.c | 2 +- 3 files changed, 30 insertions(+), 48 deletions(-) diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index 801292ac798..b632dadf233 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -197,59 +197,48 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) { return 0; } -int capability_gain_cap_setpcap(cap_t *ret_before_caps) { - _cleanup_cap_free_ cap_t caps = NULL; - cap_flag_value_t fv; - caps = cap_get_proc(); - if (!caps) - return -errno; - - 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; +int capability_gain_cap_setpcap(void) { + CapabilityQuintet q; + int r; - temp_cap = cap_dup(caps); - if (!temp_cap) - return -errno; + r = capability_get(&q); + if (r < 0) + return r; - if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) - return -errno; + if (BIT_SET(q.effective, CAP_SETPCAP)) + return 1; /* We already have capability. */ - if (cap_set_proc(temp_cap) < 0) - log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m"); + SET_BIT(q.effective, CAP_SETPCAP); + r = capability_apply(&q); + if (r < 0) { /* 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 or try to * set securebits. */ + log_debug_errno(r, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m"); + return 0; } - if (ret_before_caps) - /* Return the capabilities as they have been before setting CAP_SETPCAP */ - *ret_before_caps = TAKE_PTR(caps); - return 0; + return 1; /* acquired */ } int capability_bounding_set_drop(uint64_t keep, bool right_now) { - _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL; - int r; + int k, 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); + CapabilityQuintet q; + r = capability_get(&q); if (r < 0) return r; + CapabilityQuintet saved = q; - after_cap = cap_dup(before_cap); - if (!after_cap) - return -errno; + r = capability_gain_cap_setpcap(); + if (r < 0) + return r; for (unsigned i = 0; i <= cap_last_cap(); i++) { - cap_value_t v; - if (BIT_SET(keep, i)) continue; @@ -263,33 +252,26 @@ int capability_bounding_set_drop(uint64_t keep, bool right_now) { if (prctl(PR_CAPBSET_READ, i) != 0) goto finish; } - 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; - } + CLEAR_BIT(q.inheritable, i); /* 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; - } + CLEAR_BIT(q.effective, i); + CLEAR_BIT(q.permitted, i); } } r = 0; finish: - if (cap_set_proc(after_cap) < 0) { + k = capability_apply(&q); + if (k < 0) /* If there are no actual changes anyway then let's ignore this error. */ - if (cap_compare(before_cap, after_cap) != 0) - r = -errno; - } + if (!capability_quintet_equal(&q, &saved)) + return k; return r; } diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index e911c7bae20..656b7a4a46e 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -48,7 +48,7 @@ int capability_get(CapabilityQuintet *ret); unsigned cap_last_cap(void); int have_effective_cap(unsigned cap); int have_inheritable_cap(unsigned cap); -int capability_gain_cap_setpcap(cap_t *ret_before_caps); +int capability_gain_cap_setpcap(void); 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/exec-invoke.c b/src/core/exec-invoke.c index de59e1148ec..5ea22fd429d 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -6165,7 +6165,7 @@ int exec_invoke( * * Hence there is no security impact to raise it in the effective set before execve */ - r = capability_gain_cap_setpcap(/* ret_before_caps = */ NULL); + r = capability_gain_cap_setpcap(); if (r < 0) { *exit_status = EXIT_CAPABILITIES; return log_error_errno(r, "Failed to gain CAP_SETPCAP for setting secure bits"); -- 2.47.3