]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/capability-util.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / basic / capability-util.c
index 6b5e3e05216ef365ad284feb8a7b06ae0fa888c1..c1520d927901a3cbc7cd911afd835c44ca5d2843 100644 (file)
@@ -1,15 +1,14 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <errno.h>
-#include <grp.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/capability.h>
 #include <sys/prctl.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
 #include "capability-util.h"
+#include "cap-list.h"
 #include "fileio.h"
 #include "log.h"
 #include "macro.h"
@@ -32,8 +31,8 @@ int have_effective_cap(int value) {
         return fv == CAP_SET;
 }
 
-unsigned long cap_last_cap(void) {
-        static thread_local unsigned long saved;
+unsigned cap_last_cap(void) {
+        static thread_local unsigned saved;
         static thread_local bool valid = false;
         _cleanup_free_ char *content = NULL;
         unsigned long p = 0;
@@ -61,12 +60,12 @@ unsigned long cap_last_cap(void) {
         }
 
         /* fall back to syscall-probing for pre linux-3.2 */
-        p = (unsigned long) CAP_LAST_CAP;
+        p = MIN((unsigned long) CAP_LAST_CAP, 63U);
 
         if (prctl(PR_CAPBSET_READ, p) < 0) {
 
                 /* Hmm, look downwards, until we find one that works */
-                for (p--; p > 0; p --)
+                for (p--; p > 0; p--)
                         if (prctl(PR_CAPBSET_READ, p) >= 0)
                                 break;
 
@@ -85,22 +84,17 @@ unsigned long cap_last_cap(void) {
 }
 
 int capability_update_inherited_set(cap_t caps, uint64_t set) {
-        unsigned long i;
+        /* Add capabilities in the set to the inherited caps, drops capabilities not in the set.
+         * Do not apply them yet. */
 
-        /* Add capabilities in the set to the inherited caps. Do not apply
-         * them yet. */
-
-        for (i = 0; i < cap_last_cap(); i++) {
-
-                if (set & (UINT64_C(1) << i)) {
-                        cap_value_t v;
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
+                cap_flag_value_t flag = set & (UINT64_C(1) << i) ? CAP_SET : CAP_CLEAR;
+                cap_value_t v;
 
-                        v = (cap_value_t) i;
+                v = (cap_value_t) i;
 
-                        /* Make the capability inheritable. */
-                        if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0)
-                                return -errno;
-                }
+                if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, flag) < 0)
+                        return -errno;
         }
 
         return 0;
@@ -108,10 +102,27 @@ int capability_update_inherited_set(cap_t caps, uint64_t set) {
 
 int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
         _cleanup_cap_free_ cap_t caps = NULL;
-        unsigned long i;
         int r;
 
-        /* Add the capabilities to the ambient set. */
+        /* Remove capabilities requested in ambient set, but not in the bounding set */
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
+                if (set == 0)
+                        break;
+
+                if (FLAGS_SET(set, (UINT64_C(1) << i)) && prctl(PR_CAPBSET_READ, i) != 1) {
+                        log_debug("Ambient capability %s requested but missing from bounding set,"
+                                        " suppressing automatically.", capability_to_name(i));
+                        set &= ~(UINT64_C(1) << i);
+                }
+        }
+
+        /* Add the capabilities to the ambient set (an possibly also the inheritable set) */
+
+        /* Check that we can use PR_CAP_AMBIENT or quit early. */
+        if (!ambient_capabilities_supported())
+                return (set & all_capabilities()) == 0 ?
+                        0 : -EOPNOTSUPP; /* if actually no ambient caps are to be set, be silent,
+                                          * otherwise fail recognizably */
 
         if (also_inherit) {
                 caps = cap_get_proc();
@@ -126,42 +137,45 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
                         return -errno;
         }
 
-        for (i = 0; i < cap_last_cap(); i++) {
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
 
                 if (set & (UINT64_C(1) << i)) {
 
                         /* Add the capability to the ambient set. */
                         if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0)
                                 return -errno;
+                } else {
+
+                        /* Drop the capability so we don't inherit capabilities we didn't ask for. */
+                        r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0);
+                        if (r < 0)
+                                return -errno;
+
+                        if (r)
+                                if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0) < 0)
+                                        return -errno;
+
                 }
         }
 
         return 0;
 }
 
-int capability_bounding_set_drop(uint64_t keep, bool right_now) {
-        _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL;
+int capability_gain_cap_setpcap(cap_t *ret_before_caps) {
+        _cleanup_cap_free_ cap_t caps = NULL;
         cap_flag_value_t fv;
-        unsigned long i;
-        int r;
-
-        /* If we are run as PID 1 we will lack CAP_SETPCAP by default
-         * in the effective set (yes, the kernel drops that when
-         * executing init!), so get it back temporarily so that we can
-         * call PR_CAPBSET_DROP. */
-
-        before_cap = cap_get_proc();
-        if (!before_cap)
+        caps = cap_get_proc();
+        if (!caps)
                 return -errno;
 
-        if (cap_get_flag(before_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
+        if (cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
                 return -errno;
 
         if (fv != CAP_SET) {
                 _cleanup_cap_free_ cap_t temp_cap = NULL;
                 static const cap_value_t v = CAP_SETPCAP;
 
-                temp_cap = cap_dup(before_cap);
+                temp_cap = cap_dup(caps);
                 if (!temp_cap)
                         return -errno;
 
@@ -172,14 +186,33 @@ int capability_bounding_set_drop(uint64_t keep, bool right_now) {
                         log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m");
 
                 /* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this just means
-                 * we'll fail later, when we actually intend to drop some capabilities. */
+                 * we'll fail later, when we actually intend to drop some capabilities or try to set securebits. */
         }
+        if (ret_before_caps)
+                /* Return the capabilities as they have been before setting CAP_SETPCAP */
+                *ret_before_caps = TAKE_PTR(caps);
+
+        return 0;
+}
+
+int capability_bounding_set_drop(uint64_t keep, bool right_now) {
+        _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL;
+        int r;
+
+        /* If we are run as PID 1 we will lack CAP_SETPCAP by default
+         * in the effective set (yes, the kernel drops that when
+         * executing init!), so get it back temporarily so that we can
+         * call PR_CAPBSET_DROP. */
+
+        r = capability_gain_cap_setpcap(&before_cap);
+        if (r < 0)
+                return r;
 
         after_cap = cap_dup(before_cap);
         if (!after_cap)
                 return -errno;
 
-        for (i = 0; i <= cap_last_cap(); i++) {
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
                 cap_value_t v;
 
                 if ((keep & (UINT64_C(1) << i)))
@@ -238,23 +271,20 @@ static int drop_from_file(const char *fn, uint64_t keep) {
         if (r < 0)
                 return r;
 
-        assert_cc(sizeof(hi) == sizeof(unsigned));
-        assert_cc(sizeof(lo) == sizeof(unsigned));
-
-        k = sscanf(p, "%u %u", &lo, &hi);
+        k = sscanf(p, "%" PRIu32 " %" PRIu32, &lo, &hi);
         if (k != 2)
                 return -EIO;
 
-        current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
+        current = (uint64_t) lo | ((uint64_t) hi << 32);
         after = current & keep;
 
         if (current == after)
                 return 0;
 
-        lo = (unsigned) (after & 0xFFFFFFFFULL);
-        hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL);
+        lo = after & UINT32_C(0xFFFFFFFF);
+        hi = (after >> 32) & UINT32_C(0xFFFFFFFF);
 
-        return write_string_filef(fn, WRITE_STRING_FILE_CREATE, "%u %u", lo, hi);
+        return write_string_filef(fn, 0, "%" PRIu32 " %" PRIu32, lo, hi);
 }
 
 int capability_bounding_set_drop_usermode(uint64_t keep) {
@@ -367,22 +397,56 @@ bool ambient_capabilities_supported(void) {
         return cache;
 }
 
+bool capability_quintet_mangle(CapabilityQuintet *q) {
+        uint64_t combined, drop = 0;
+        bool ambient_supported;
+
+        assert(q);
+
+        combined = q->effective | q->bounding | q->inheritable | q->permitted;
+
+        ambient_supported = q->ambient != (uint64_t) -1;
+        if (ambient_supported)
+                combined |= q->ambient;
+
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
+                unsigned long bit = UINT64_C(1) << i;
+                if (!FLAGS_SET(combined, bit))
+                        continue;
+
+                if (prctl(PR_CAPBSET_READ, i) > 0)
+                        continue;
+
+                drop |= bit;
+
+                log_debug("Not in the current bounding set: %s", capability_to_name(i));
+        }
+
+        q->effective &= ~drop;
+        q->bounding &= ~drop;
+        q->inheritable &= ~drop;
+        q->permitted &= ~drop;
+
+        if (ambient_supported)
+                q->ambient &= ~drop;
+
+        return drop != 0; /* Let the caller know we changed something */
+}
+
 int capability_quintet_enforce(const CapabilityQuintet *q) {
-        _cleanup_cap_free_ cap_t c = NULL;
-        bool need_set_proc_again = false;
+        _cleanup_cap_free_ cap_t c = NULL, modified = NULL;
         int r;
 
         if (q->ambient != (uint64_t) -1) {
-                unsigned long i;
                 bool changed = false;
 
                 c = cap_get_proc();
                 if (!c)
                         return -errno;
 
-                /* In order to raise the ambient caps set we first need to raise the matching inheritable + permitted
-                 * cap */
-                for (i = 0; i <= cap_last_cap(); i++) {
+                /* 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;
@@ -417,7 +481,6 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
 
         if (q->inheritable != (uint64_t) -1 || q->permitted != (uint64_t) -1 || q->effective != (uint64_t) -1) {
                 bool changed = false;
-                unsigned long i;
 
                 if (!c) {
                         c = cap_get_proc();
@@ -425,7 +488,7 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
                                 return -errno;
                 }
 
-                for (i = 0; i <= cap_last_cap(); i++) {
+                for (unsigned i = 0; i <= cap_last_cap(); i++) {
                         uint64_t m = UINT64_C(1) << i;
                         cap_value_t cv = (cap_value_t) i;
 
@@ -494,8 +557,6 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
                 }
 
                 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) {
@@ -519,12 +580,10 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
 
                         /* 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.*/
+                         * 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;
-
-                        need_set_proc_again = !!modified;
                 }
         }
 
@@ -538,7 +597,7 @@ 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 (need_set_proc_again)
+        if (modified)
                 if (cap_set_proc(c) < 0)
                         return -errno;