]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
capability-util: introduce capability_get() and use it in have_effective_cap()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 23 Oct 2025 14:07:13 +0000 (23:07 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 23 Oct 2025 16:52:59 +0000 (01:52 +0900)
capability_get() is a wrapper of capget() syscall and converts its
result to CapabilityQuintet.

This also introduce have_inheritable_cap(), which is similar to
have_effective_cap(). It is currently unused, but will be used later.

src/basic/capability-util.c
src/basic/capability-util.h

index bf7fd151c8a9fe9e415b23be12946aeb5ddf3a80..ee665df6f9bfa3b5bab0a1d170fdfb6c9249e634 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdatomic.h>
 #include <stdio.h>
 #include <sys/prctl.h>
+#include <sys/syscall.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
 #include "stat-util.h"
 #include "user-util.h"
 
+int capability_get(CapabilityQuintet *ret) {
+        assert(ret);
+
+        struct __user_cap_header_struct hdr = {
+                .version = _LINUX_CAPABILITY_VERSION_3,
+                .pid = getpid_cached(),
+        };
+
+        assert_cc(_LINUX_CAPABILITY_U32S_3 == 2);
+        struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
+        if (syscall(SYS_capget, &hdr, data) < 0)
+                return -errno;
+
+        *ret = (CapabilityQuintet) {
+                .effective = (uint64_t) data[0].effective | ((uint64_t) data[1].effective << 32),
+                .bounding = UINT64_MAX,
+                .inheritable = (uint64_t) data[0].inheritable | ((uint64_t) data[1].inheritable << 32),
+                .permitted = (uint64_t) data[0].permitted | ((uint64_t) data[1].permitted << 32),
+                .ambient = UINT64_MAX,
+        };
+        return 0;
+}
+
 unsigned cap_last_cap(void) {
         static atomic_int saved = INT_MAX;
         int r, c;
@@ -74,19 +98,30 @@ unsigned cap_last_cap(void) {
         return c;
 }
 
-int have_effective_cap(int value) {
-        _cleanup_cap_free_ cap_t cap = NULL;
-        cap_flag_value_t fv = CAP_CLEAR; /* To avoid false-positive use-of-uninitialized-value error reported
-                                          * by fuzzers. */
+int have_effective_cap(unsigned cap) {
+        CapabilityQuintet q;
+        int r;
 
-        cap = cap_get_proc();
-        if (!cap)
-                return -errno;
+        assert(cap <= CAP_LIMIT);
 
-        if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
-                return -errno;
+        r = capability_get(&q);
+        if (r < 0)
+                return r;
+
+        return BIT_SET(q.effective, cap);
+}
+
+int have_inheritable_cap(unsigned cap) {
+        CapabilityQuintet q;
+        int r;
+
+        assert(cap <= CAP_LIMIT);
+
+        r = capability_get(&q);
+        if (r < 0)
+                return r;
 
-        return fv == CAP_SET;
+        return BIT_SET(q.inheritable, cap);
 }
 
 int capability_update_inherited_set(cap_t caps, uint64_t set) {
index d658bbe0659bdb4297feca5c51e64ca8ccf30b1f..78bd75db7b62abbd5102511f07161aa6a9c47bd8 100644 (file)
@@ -43,8 +43,11 @@ static inline bool capability_is_set(uint64_t v) {
         return v != CAP_MASK_UNSET;
 }
 
+int capability_get(CapabilityQuintet *ret);
+
 unsigned cap_last_cap(void);
-int have_effective_cap(int value);
+int have_effective_cap(unsigned cap);
+int have_inheritable_cap(unsigned cap);
 int capability_gain_cap_setpcap(cap_t *ret_before_caps);
 int capability_bounding_set_drop(uint64_t keep, bool right_now);
 int capability_bounding_set_drop_usermode(uint64_t keep);