]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
capability-util: generalize helper to acquire local caps
authorLennart Poettering <lennart@poettering.net>
Thu, 28 Nov 2024 11:59:21 +0000 (12:59 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 17 Dec 2024 18:06:54 +0000 (19:06 +0100)
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.

src/basic/capability-util.c
src/basic/capability-util.h
src/shared/condition.c
src/test/test-capability.c

index 8040eb399ace89708bc3ea0a8c38c00283436424..f4fab22e7319f514e97f4559cface2439c3c77cf 100644 (file)
@@ -8,8 +8,9 @@
 #include <unistd.h>
 
 #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;
+}
index bb781940c4ee94a8891a363a39cdf75b37b8277a..7f9160e0069f27b4c0424dfc9a7910fac0219a62 100644 (file)
@@ -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);
index ac23681a110c14d63e222322e2faf3e4dbe1cd1b..e4aa50c8f7fa897c85f43cbfd1ce9595ad283d15 100644 (file)
@@ -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) {
index e497cccead2352239334fd945440c1d843af1c08..6c23c1bb283dd8513acdab60c79b056c6fe5982b 100644 (file)
@@ -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;
 }