]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
capability: keep CAP_SETPCAP while dropping bounding caps
authorLennart Poettering <lennart@poettering.net>
Wed, 6 Mar 2019 10:31:20 +0000 (11:31 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 15 Mar 2019 14:33:09 +0000 (15:33 +0100)
The kernel only allows dropping bounding caps as long as we have
CAP_SETPCAP. Hence, let's keep that before dropping the bounding caps,
and afterwards drop them too.

src/basic/capability-util.c

index b944ee6ea12ad7cfc7c990c1bc97715ed07e70d7..b351f23618ddf3e6f09a072dc16bf85c2230052d 100644 (file)
@@ -363,6 +363,7 @@ bool ambient_capabilities_supported(void) {
 
 int capability_quintet_enforce(const CapabilityQuintet *q) {
         _cleanup_cap_free_ cap_t c = NULL;
+        bool need_set_proc_again = false;
         int r;
 
         if (q->ambient != (uint64_t) -1) {
@@ -393,7 +394,6 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
 
                         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;
 
@@ -472,9 +472,39 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
                         }
                 }
 
-                if (changed)
-                        if (cap_set_proc(c) < 0)
+                if (changed) {
+                        _cleanup_cap_free_ cap_t modified = NULL;
+
+                        /* 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 != (uint64_t) -1) {
+                                cap_value_t cv = CAP_SETPCAP;
+
+                                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;
+
+                                if (cap_compare(modified, c) == 0) {
+                                        /* No change? then drop this nonsense again */
+                                        cap_free(modified);
+                                        modified = NULL;
+                                }
+                        }
+
+                        /* 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
+                         * droppoing 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;
+
+                        need_set_proc_again = !!modified;
+                }
         }
 
         if (q->bounding != (uint64_t) -1) {
@@ -483,5 +513,13 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
                         return r;
         }
 
+        /* If needed, let's now set the caps again, this time in the final version, which differs from what
+         * 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 (need_set_proc_again)
+                if (cap_set_proc(c) < 0)
+                        return -errno;
+
         return 0;
 }