]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
capability-util: use capability_get() and _apply() in capability_quintet_enforce()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 23 Oct 2025 14:34:31 +0000 (23:34 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 23 Oct 2025 16:52:59 +0000 (01:52 +0900)
src/basic/capability-util.c

index b15a8b462c7d652135b26629192c45bca82cfa0e..751e99366708313e4550f2f2c878febcc5263d37 100644 (file)
@@ -423,156 +423,68 @@ bool capability_quintet_mangle(CapabilityQuintet *q) {
 }
 
 int capability_quintet_enforce(const CapabilityQuintet *q) {
-        _cleanup_cap_free_ cap_t c = NULL, modified = NULL;
+        CapabilityQuintet c;
+        bool modified = false;
         int r;
 
-        if (q->ambient != CAP_MASK_UNSET) {
-                bool changed = false;
-
-                c = cap_get_proc();
-                if (!c)
-                        return -errno;
+        if (q->ambient != CAP_MASK_UNSET ||
+            q->inheritable != CAP_MASK_UNSET ||
+            q->permitted != CAP_MASK_UNSET ||
+            q->effective != CAP_MASK_UNSET) {
+                r = capability_get(&c);
+                if (r < 0)
+                        return r;
+        }
 
+        if (q->ambient != CAP_MASK_UNSET) {
                 /* In order to raise the ambient caps set we first need to raise the matching
                  * inheritable + permitted cap */
-                for (unsigned i = 0; i <= cap_last_cap(); i++) {
-                        uint64_t m = UINT64_C(1) << i;
-                        cap_value_t cv = (cap_value_t) i;
-                        cap_flag_value_t old_value_inheritable, old_value_permitted;
-
-                        if ((q->ambient & m) == 0)
-                                continue;
-
-                        if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value_inheritable) < 0)
-                                return -errno;
-                        if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value_permitted) < 0)
-                                return -errno;
+                if (!FLAGS_SET(c.permitted, q->ambient) ||
+                    !FLAGS_SET(c.inheritable, q->ambient)) {
 
-                        if (old_value_inheritable == CAP_SET && old_value_permitted == CAP_SET)
-                                continue;
-
-                        if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, CAP_SET) < 0)
-                                return -errno;
-                        if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, CAP_SET) < 0)
-                                return -errno;
+                        c.permitted |= q->ambient;
+                        c.inheritable |= q->ambient;
 
-                        changed = true;
+                        r = capability_apply(&c);
+                        if (r < 0)
+                                return r;
                 }
 
-                if (changed)
-                        if (cap_set_proc(c) < 0)
-                                return -errno;
-
-                r = capability_ambient_set_apply(q->ambient, false);
+                r = capability_ambient_set_apply(q->ambient, /* also_inherit= */ false);
                 if (r < 0)
                         return r;
         }
 
         if (q->inheritable != CAP_MASK_UNSET || q->permitted != CAP_MASK_UNSET || q->effective != CAP_MASK_UNSET) {
-                bool changed = false;
-
-                if (!c) {
-                        c = cap_get_proc();
-                        if (!c)
-                                return -errno;
-                }
-
-                for (unsigned i = 0; i <= cap_last_cap(); i++) {
-                        uint64_t m = UINT64_C(1) << i;
-                        cap_value_t cv = (cap_value_t) i;
-
-                        if (q->inheritable != CAP_MASK_UNSET) {
-                                cap_flag_value_t old_value, new_value;
-
-                                if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value) < 0) {
-                                        if (errno == EINVAL) /* If the kernel knows more caps than this
-                                                              * version of libcap, then this will return
-                                                              * EINVAL. In that case, simply ignore it,
-                                                              * pretend it doesn't exist. */
-                                                continue;
-
-                                        return -errno;
-                                }
-
-                                new_value = (q->inheritable & m) ? CAP_SET : CAP_CLEAR;
-
-                                if (old_value != new_value) {
-                                        changed = true;
-
-                                        if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, new_value) < 0)
-                                                return -errno;
-                                }
-                        }
+                if (!FLAGS_SET(c.effective, q->effective) ||
+                    !FLAGS_SET(c.permitted, q->permitted) ||
+                    !FLAGS_SET(c.inheritable, q->inheritable)) {
 
-                        if (q->permitted != CAP_MASK_UNSET) {
-                                cap_flag_value_t old_value, new_value;
+                        c.effective |= q->effective;
+                        c.permitted |= q->permitted;
+                        c.inheritable |= q->inheritable;
 
-                                if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value) < 0) {
-                                        if (errno == EINVAL)
-                                                continue;
-
-                                        return -errno;
-                                }
-
-                                new_value = (q->permitted & m) ? CAP_SET : CAP_CLEAR;
-
-                                if (old_value != new_value) {
-                                        changed = true;
-
-                                        if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, new_value) < 0)
-                                                return -errno;
-                                }
-                        }
-
-                        if (q->effective != CAP_MASK_UNSET) {
-                                cap_flag_value_t old_value, new_value;
-
-                                if (cap_get_flag(c, cv, CAP_EFFECTIVE, &old_value) < 0) {
-                                        if (errno == EINVAL)
-                                                continue;
-
-                                        return -errno;
-                                }
-
-                                new_value = (q->effective & m) ? CAP_SET : CAP_CLEAR;
-
-                                if (old_value != new_value) {
-                                        changed = true;
-
-                                        if (cap_set_flag(c, CAP_EFFECTIVE, 1, &cv, new_value) < 0)
-                                                return -errno;
-                                }
-                        }
-                }
-
-                if (changed) {
-                        /* In order to change the bounding caps, we need to keep CAP_SETPCAP for a bit
+                        /* Now, let's enforce the caps for the first time. Note that this is where we acquire
+                         * caps in any of the sets we currently don't have. We have to do this before
+                         * dropping the bounding caps below, since at that point we can never acquire new
+                         * caps in inherited/permitted/effective anymore, but only lose them.
+                         *
+                         * In order to change the bounding caps, we need to keep CAP_SETPCAP for a bit
                          * longer. Let's add it to our list hence for now. */
-                        if (q->bounding != CAP_MASK_UNSET) {
-                                cap_value_t cv = CAP_SETPCAP;
+                        if (q->bounding != CAP_MASK_UNSET &&
+                            (!BIT_SET(c.effective, CAP_SETPCAP) || !BIT_SET(c.permitted, CAP_SETPCAP))) {
+                                CapabilityQuintet tmp = c;
 
-                                modified = cap_dup(c);
-                                if (!modified)
-                                        return -ENOMEM;
-
-                                if (cap_set_flag(modified, CAP_PERMITTED, 1, &cv, CAP_SET) < 0)
-                                        return -errno;
-                                if (cap_set_flag(modified, CAP_EFFECTIVE, 1, &cv, CAP_SET) < 0)
-                                        return -errno;
+                                SET_BIT(c.effective, CAP_SETPCAP);
+                                SET_BIT(c.permitted, CAP_SETPCAP);
 
-                                if (cap_compare(modified, c) == 0) {
-                                        /* No change? then drop this nonsense again */
-                                        cap_free(modified);
-                                        modified = NULL;
-                                }
-                        }
+                                modified = true;
 
-                        /* Now, let's enforce the caps for the first time. Note that this is where we acquire
-                         * caps in any of the sets we currently don't have. We have to do this before
-                         * dropping the bounding caps below, since at that point we can never acquire new
-                         * caps in inherited/permitted/effective anymore, but only lose them. */
-                        if (cap_set_proc(modified ?: c) < 0)
-                                return -errno;
+                                r = capability_apply(&tmp);
+                        } else
+                                r = capability_apply(&c);
+                        if (r < 0)
+                                return r;
                 }
         }
 
@@ -586,9 +498,11 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
          * we have already set only in the CAP_SETPCAP bit, which we needed for dropping the bounding
          * bits. This call only undoes bits and doesn't acquire any which means the bounding caps don't
          * matter. */
-        if (modified)
-                if (cap_set_proc(c) < 0)
-                        return -errno;
+        if (modified) {
+                r = capability_apply(&c);
+                if (r < 0)
+                        return r;
+        }
 
         return 0;
 }