From: Lennart Poettering Date: Thu, 28 Nov 2024 11:59:21 +0000 (+0100) Subject: capability-util: generalize helper to acquire local caps X-Git-Tag: v258-rc1~1806^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1184626a269e38dcdfcd1042975a31d4eb30cd5e;p=thirdparty%2Fsystemd.git capability-util: generalize helper to acquire local caps This generalizes and modernizes the code to acquire set of local caps, based on the code for this in the condition logic. Uses PidRef, and acquires the full quintuplet of caps. This can be considered preparation to one day maybe build without libcap. --- diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index 8040eb399ac..f4fab22e731 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -8,8 +8,9 @@ #include #include "alloc-util.h" -#include "capability-util.h" #include "cap-list.h" +#include "capability-util.h" +#include "fd-util.h" #include "fileio.h" #include "log.h" #include "logarithm.h" @@ -17,6 +18,8 @@ #include "missing_prctl.h" #include "missing_threads.h" #include "parse-util.h" +#include "pidref.h" +#include "stat-util.h" #include "user-util.h" int have_effective_cap(int value) { @@ -607,3 +610,78 @@ int capability_get_ambient(uint64_t *ret) { *ret = a; return 1; } + +int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret) { + int r; + + if (!pidref_is_set(pidref)) + return -ESRCH; + if (pidref_is_remote(pidref)) + return -EREMOTE; + + const char *path = procfs_file_alloca(pidref->pid, "status"); + _cleanup_fclose_ FILE *f = fopen(path, "re"); + if (!f) { + if (errno == ENOENT && proc_mounted() == 0) + return -ENOSYS; + + return -errno; + } + + CapabilityQuintet q = CAPABILITY_QUINTET_NULL; + for (;;) { + _cleanup_free_ char *line = NULL; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + + static const struct { + const char *field; + size_t offset; + } fields[] = { + { "CapBnd:", offsetof(CapabilityQuintet, bounding) }, + { "CapInh:", offsetof(CapabilityQuintet, inheritable) }, + { "CapPrm:", offsetof(CapabilityQuintet, permitted) }, + { "CapEff:", offsetof(CapabilityQuintet, effective) }, + { "CapAmb:", offsetof(CapabilityQuintet, ambient) }, + }; + + FOREACH_ELEMENT(i, fields) { + + const char *p = first_word(line, i->field); + if (!p) + continue; + + uint64_t *v = (uint64_t*) ((uint8_t*) &q + i->offset); + + if (*v != CAP_MASK_UNSET) + return -EBADMSG; + + r = safe_atoux64(p, v); + if (r < 0) + return r; + + if (*v == CAP_MASK_UNSET) + return -EBADMSG; + } + } + + if (q.effective == CAP_MASK_UNSET || + q.inheritable == CAP_MASK_UNSET || + q.permitted == CAP_MASK_UNSET || + q.effective == CAP_MASK_UNSET || + q.ambient == CAP_MASK_UNSET) + return -EBADMSG; + + r = pidref_verify(pidref); + if (r < 0) + return r; + + if (ret) + *ret = q; + + return 0; +} diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index bb781940c4e..7f9160e0069 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -8,6 +8,7 @@ #include "macro.h" #include "missing_capability.h" +#include "pidref.h" /* Special marker used when storing a capabilities mask as "unset" */ #define CAP_MASK_UNSET UINT64_MAX @@ -84,3 +85,5 @@ bool capability_quintet_mangle(CapabilityQuintet *q); int capability_quintet_enforce(const CapabilityQuintet *q); int capability_get_ambient(uint64_t *ret); + +int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret); diff --git a/src/shared/condition.c b/src/shared/condition.c index ac23681a110..e4aa50c8f7f 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -21,6 +21,7 @@ #include "battery-util.h" #include "blockdev-util.h" #include "cap-list.h" +#include "capability-util.h" #include "cgroup-util.h" #include "compare-operator.h" #include "condition.h" @@ -701,45 +702,23 @@ static int condition_test_security(Condition *c, char **env) { } static int condition_test_capability(Condition *c, char **env) { - unsigned long long capabilities = (unsigned long long) -1; - _cleanup_fclose_ FILE *f = NULL; - int value, r; + int r; assert(c); assert(c->parameter); assert(c->type == CONDITION_CAPABILITY); /* If it's an invalid capability, we don't have it */ - value = capability_from_name(c->parameter); + int value = capability_from_name(c->parameter); if (value < 0) return -EINVAL; - /* If it's a valid capability we default to assume - * that we have it */ - - f = fopen("/proc/self/status", "re"); - if (!f) - return -errno; - - for (;;) { - _cleanup_free_ char *line = NULL; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) - break; - - const char *p = startswith(line, "CapBnd:"); - if (p) { - if (sscanf(p, "%llx", &capabilities) != 1) - return -EIO; - - break; - } - } + CapabilityQuintet q; + r = pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q); + if (r < 0) + return r; - return !!(capabilities & (1ULL << value)); + return !!(q.bounding & ((UINT64_C(1) << value))); } static int condition_test_needs_update(Condition *c, char **env) { diff --git a/src/test/test-capability.c b/src/test/test-capability.c index e497cccead2..6c23c1bb283 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -305,6 +305,18 @@ static void test_capability_get_ambient(void) { } } +static void test_pidref_get_capability(void) { + CapabilityQuintet q = CAPABILITY_QUINTET_NULL; + + assert_se(pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q) >= 0); + + assert_se(q.effective != CAP_MASK_UNSET); + assert_se(q.inheritable != CAP_MASK_UNSET); + assert_se(q.permitted != CAP_MASK_UNSET); + assert_se(q.effective != CAP_MASK_UNSET); + assert_se(q.ambient != CAP_MASK_UNSET); +} + int main(int argc, char *argv[]) { bool run_ambient; @@ -336,5 +348,7 @@ int main(int argc, char *argv[]) { test_capability_get_ambient(); + test_pidref_get_capability(); + return 0; }