923a60
From ab2c6236a959fe53109cc36f3642b3a7a2051746 Mon Sep 17 00:00:00 2001
923a60
From: Ismo Puustinen <ismo.puustinen@intel.com>
923a60
Date: Thu, 31 Dec 2015 14:54:44 +0200
923a60
Subject: [PATCH] capabilities: added support for ambient capabilities.
923a60
923a60
This patch adds support for ambient capabilities in service files. The
923a60
idea with ambient capabilities is that the execed processes can run with
923a60
non-root user and get some inherited capabilities, without having any
923a60
need to add the capabilities to the executable file.
923a60
923a60
You need at least Linux 4.3 to use ambient capabilities. SecureBit
923a60
keep-caps is automatically added when you use ambient capabilities and
923a60
wish to change the user.
923a60
923a60
An example system service file might look like this:
923a60
923a60
[Unit]
923a60
Description=Service for testing caps
923a60
923a60
[Service]
923a60
ExecStart=/usr/bin/sleep 10000
923a60
User=nobody
923a60
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
923a60
923a60
After starting the service it has these capabilities:
923a60
923a60
CapInh: 0000000000003000
923a60
CapPrm: 0000000000003000
923a60
CapEff: 0000000000003000
923a60
CapBnd: 0000003fffffffff
923a60
CapAmb: 0000000000003000
923a60
923a60
Cherry-picked from: 755d4b6
923a60
Resolves: #1387398
923a60
---
923a60
 src/core/dbus-execute.c               | 19 ++++++
923a60
 src/core/execute.c                    | 90 ++++++++++++++++++++++-----
923a60
 src/core/execute.h                    |  2 +
923a60
 src/core/load-fragment-gperf.gperf.m4 |  1 +
923a60
 src/core/load-fragment.c              |  4 +-
923a60
 src/shared/capability.c               | 55 ++++++++++++++++
923a60
 src/shared/capability.h               |  3 +
923a60
 src/shared/missing.h                  | 16 +++++
923a60
 src/test/test-unit-file.c             |  1 +
923a60
 9 files changed, 174 insertions(+), 17 deletions(-)
923a60
923a60
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
923a60
index a564c53fae..817ef80d1f 100644
923a60
--- a/src/core/dbus-execute.c
923a60
+++ b/src/core/dbus-execute.c
923a60
@@ -327,6 +327,24 @@ static int property_get_capability_bounding_set(
923a60
         return sd_bus_message_append(reply, "t", c->capability_bounding_set);
923a60
 }
923a60
 
923a60
+static int property_get_ambient_capabilities(
923a60
+                sd_bus *bus,
923a60
+                const char *path,
923a60
+                const char *interface,
923a60
+                const char *property,
923a60
+                sd_bus_message *reply,
923a60
+                void *userdata,
923a60
+                sd_bus_error *error) {
923a60
+
923a60
+        ExecContext *c = userdata;
923a60
+
923a60
+        assert(bus);
923a60
+        assert(reply);
923a60
+        assert(c);
923a60
+
923a60
+        return sd_bus_message_append(reply, "t", c->capability_ambient_set);
923a60
+}
923a60
+
923a60
 static int property_get_capabilities(
923a60
                 sd_bus *bus,
923a60
                 const char *path,
923a60
@@ -637,6 +655,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
923a60
         SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
923a60
         SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
923a60
         SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST),
923a60
+        SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
923a60
         SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
923a60
         SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
923a60
         SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
923a60
diff --git a/src/core/execute.c b/src/core/execute.c
923a60
index 40db11e284..4265b9c348 100644
923a60
--- a/src/core/execute.c
923a60
+++ b/src/core/execute.c
923a60
@@ -696,12 +696,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
923a60
         /* Sets (but doesn't lookup) the uid and make sure we keep the
923a60
          * capabilities while doing so. */
923a60
 
923a60
-        if (context->capabilities) {
923a60
-                _cleanup_cap_free_ cap_t d = NULL;
923a60
-                static const cap_value_t bits[] = {
923a60
-                        CAP_SETUID,   /* Necessary so that we can run setresuid() below */
923a60
-                        CAP_SETPCAP   /* Necessary so that we can set PR_SET_SECUREBITS later on */
923a60
-                };
923a60
+        if (context->capabilities || context->capability_ambient_set != 0) {
923a60
 
923a60
                 /* First step: If we need to keep capabilities but
923a60
                  * drop privileges we need to make sure we keep our
923a60
@@ -717,16 +712,24 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
923a60
                 /* Second step: set the capabilities. This will reduce
923a60
                  * the capabilities to the minimum we need. */
923a60
 
923a60
-                d = cap_dup(context->capabilities);
923a60
-                if (!d)
923a60
-                        return -errno;
923a60
+                if (context->capabilities) {
923a60
+                        _cleanup_cap_free_ cap_t d = NULL;
923a60
+                        static const cap_value_t bits[] = {
923a60
+                                CAP_SETUID,   /* Necessary so that we can run setresuid() below */
923a60
+                                CAP_SETPCAP   /* Necessary so that we can set PR_SET_SECUREBITS later on */
923a60
+                        };
923a60
 
923a60
-                if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
923a60
-                    cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
923a60
-                        return -errno;
923a60
+                        d = cap_dup(context->capabilities);
923a60
+                        if (!d)
923a60
+                                return -errno;
923a60
 
923a60
-                if (cap_set_proc(d) < 0)
923a60
-                        return -errno;
923a60
+                        if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
923a60
+                            cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
923a60
+                                return -errno;
923a60
+
923a60
+                        if (cap_set_proc(d) < 0)
923a60
+                                return -errno;
923a60
+                }
923a60
         }
923a60
 
923a60
         /* Third step: actually set the uids */
923a60
@@ -1723,6 +1726,8 @@ static int exec_child(
923a60
 
923a60
         if (params->apply_permissions) {
923a60
 
923a60
+                int secure_bits = context->secure_bits;
923a60
+
923a60
                 for (i = 0; i < _RLIMIT_MAX; i++) {
923a60
                         if (!context->rlimit[i])
923a60
                                 continue;
923a60
@@ -1750,6 +1755,30 @@ static int exec_child(
923a60
                         }
923a60
                 }
923a60
 #endif
923a60
+                /* This is done before enforce_user, but ambient set
923a60
+                 * does not survive over setresuid() if keep_caps is not set. */
923a60
+                if (context->capability_ambient_set != 0) {
923a60
+                        r = capability_ambient_set_apply(context->capability_ambient_set, true);
923a60
+                        if (r < 0) {
923a60
+                                *exit_status = EXIT_CAPABILITIES;
923a60
+                                return r;
923a60
+                        }
923a60
+
923a60
+                        if (context->capabilities) {
923a60
+
923a60
+                                /* The capabilities in ambient set need to be also in the inherited
923a60
+                                 * set. If they aren't, trying to get them will fail. Add the ambient
923a60
+                                 * set inherited capabilities to the capability set in the context.
923a60
+                                 * This is needed because if capabilities are set (using "Capabilities="
923a60
+                                 * keyword), they will override whatever we set now. */
923a60
+
923a60
+                                r = capability_update_inherited_set(context->capabilities, context->capability_ambient_set);
923a60
+                                if (r < 0) {
923a60
+                                        *exit_status = EXIT_CAPABILITIES;
923a60
+                                        return r;
923a60
+                                }
923a60
+                        }
923a60
+                }
923a60
 
923a60
                 if (context->user) {
923a60
                         r = enforce_user(context, uid);
923a60
@@ -1757,14 +1786,32 @@ static int exec_child(
923a60
                                 *exit_status = EXIT_USER;
923a60
                                 return r;
923a60
                         }
923a60
+                        if (context->capability_ambient_set != 0) {
923a60
+
923a60
+                                /* Fix the ambient capabilities after user change. */
923a60
+                                r = capability_ambient_set_apply(context->capability_ambient_set, false);
923a60
+                                if (r < 0) {
923a60
+                                        *exit_status = EXIT_CAPABILITIES;
923a60
+                                        return r;
923a60
+                                }
923a60
+
923a60
+                                /* If we were asked to change user and ambient capabilities
923a60
+                                 * were requested, we had to add keep-caps to the securebits
923a60
+                                 * so that we would maintain the inherited capability set
923a60
+                                 * through the setresuid(). Make sure that the bit is added
923a60
+                                 * also to the context secure_bits so that we don't try to
923a60
+                                 * drop the bit away next. */
923a60
+
923a60
+                                 secure_bits |= 1<
923a60
+                        }
923a60
                 }
923a60
 
923a60
                 /* PR_GET_SECUREBITS is not privileged, while
923a60
                  * PR_SET_SECUREBITS is. So to suppress
923a60
                  * potential EPERMs we'll try not to call
923a60
                  * PR_SET_SECUREBITS unless necessary. */
923a60
-                if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
923a60
-                        if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
923a60
+                if (prctl(PR_GET_SECUREBITS) != secure_bits)
923a60
+                        if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
923a60
                                 *exit_status = EXIT_SECUREBITS;
923a60
                                 return -errno;
923a60
                         }
923a60
@@ -2431,6 +2478,17 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
923a60
                 fputs("\n", f);
923a60
         }
923a60
 
923a60
+        if (c->capability_ambient_set != 0) {
923a60
+                unsigned long l;
923a60
+                fprintf(f, "%sAmbientCapabilities:", prefix);
923a60
+
923a60
+                for (l = 0; l <= cap_last_cap(); l++)
923a60
+                        if (c->capability_ambient_set & (UINT64_C(1) << l))
923a60
+                                fprintf(f, " %s", strna(capability_to_name(l)));
923a60
+
923a60
+                fputs("\n", f);
923a60
+        }
923a60
+
923a60
         if (c->user)
923a60
                 fprintf(f, "%sUser: %s\n", prefix, c->user);
923a60
         if (c->group)
923a60
diff --git a/src/core/execute.h b/src/core/execute.h
923a60
index 40f7b794c5..00bf99cbea 100644
923a60
--- a/src/core/execute.h
923a60
+++ b/src/core/execute.h
923a60
@@ -152,6 +152,8 @@ struct ExecContext {
923a60
 
923a60
         uint64_t capability_bounding_set;
923a60
 
923a60
+        uint64_t capability_ambient_set;
923a60
+
923a60
         cap_t capabilities;
923a60
         int secure_bits;
923a60
 
923a60
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
923a60
index e4ce292100..f996032cf2 100644
923a60
--- a/src/core/load-fragment-gperf.gperf.m4
923a60
+++ b/src/core/load-fragment-gperf.gperf.m4
923a60
@@ -48,6 +48,7 @@ $1.SyslogLevelPrefix,            config_parse_bool,                  0,
923a60
 $1.Capabilities,                 config_parse_exec_capabilities,     0,                             offsetof($1, exec_context)
923a60
 $1.SecureBits,                   config_parse_exec_secure_bits,      0,                             offsetof($1, exec_context)
923a60
 $1.CapabilityBoundingSet,        config_parse_capability_set,        0,                             offsetof($1, exec_context.capability_bounding_set)
923a60
+$1.AmbientCapabilities,          config_parse_capability_set,        0,                             offsetof($1, exec_context.capability_ambient_set)
923a60
 $1.TimerSlackNSec,               config_parse_nsec,                  0,                             offsetof($1, exec_context.timer_slack_nsec)
923a60
 $1.NoNewPrivileges,              config_parse_no_new_privileges,     0,                             offsetof($1, exec_context)
923a60
 m4_ifdef(`HAVE_SECCOMP',
923a60
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
923a60
index dbaaf2fee7..7d1ac6c251 100644
923a60
--- a/src/core/load-fragment.c
923a60
+++ b/src/core/load-fragment.c
923a60
@@ -61,6 +61,7 @@
923a60
 #include "af-list.h"
923a60
 #include "cap-list.h"
923a60
 #include "bus-internal.h"
923a60
+#include "capability.h"
923a60
 
923a60
 #ifdef HAVE_SECCOMP
923a60
 #include "seccomp-util.h"
923a60
@@ -1044,6 +1045,7 @@ int config_parse_capability_set(
923a60
 
923a60
         if (strcmp(lvalue, "CapabilityBoundingSet") == 0)
923a60
                 initial = CAP_ALL; /* initialized to all bits on */
923a60
+        /* else "AmbientCapabilities" initialized to all bits off */
923a60
 
923a60
         p = rvalue;
923a60
         for (;;) {
923a60
@@ -1062,7 +1064,7 @@ int config_parse_capability_set(
923a60
 
923a60
                 cap = capability_from_name(word);
923a60
                 if (cap < 0) {
923a60
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding set, ignoring: %s", word);
923a60
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word);
923a60
                         continue;
923a60
                 }
923a60
 
923a60
diff --git a/src/shared/capability.c b/src/shared/capability.c
923a60
index 3ed31df5ab..6e3d7d22e0 100644
923a60
--- a/src/shared/capability.c
923a60
+++ b/src/shared/capability.c
923a60
@@ -98,6 +98,61 @@ unsigned long cap_last_cap(void) {
923a60
         return p;
923a60
 }
923a60
 
923a60
+int capability_update_inherited_set(cap_t caps, uint64_t set) {
923a60
+        unsigned long i;
923a60
+
923a60
+        /* Add capabilities in the set to the inherited caps. Do not apply
923a60
+         * them yet. */
923a60
+
923a60
+        for (i = 0; i < cap_last_cap(); i++) {
923a60
+
923a60
+                if (set & (UINT64_C(1) << i)) {
923a60
+                        cap_value_t v;
923a60
+
923a60
+                        v = (cap_value_t) i;
923a60
+
923a60
+                        /* Make the capability inheritable. */
923a60
+                        if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0)
923a60
+                                return -errno;
923a60
+                }
923a60
+        }
923a60
+
923a60
+        return 0;
923a60
+}
923a60
+
923a60
+int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
923a60
+        unsigned long i;
923a60
+        _cleanup_cap_free_ cap_t caps = NULL;
923a60
+
923a60
+        /* Add the capabilities to the ambient set. */
923a60
+
923a60
+        if (also_inherit) {
923a60
+                int r;
923a60
+                caps = cap_get_proc();
923a60
+                if (!caps)
923a60
+                        return -errno;
923a60
+
923a60
+                r = capability_update_inherited_set(caps, set);
923a60
+                if (r < 0)
923a60
+                        return -errno;
923a60
+
923a60
+                if (cap_set_proc(caps) < 0)
923a60
+                        return -errno;
923a60
+        }
923a60
+
923a60
+        for (i = 0; i < cap_last_cap(); i++) {
923a60
+
923a60
+                if (set & (UINT64_C(1) << i)) {
923a60
+
923a60
+                        /* Add the capability to the ambient set. */
923a60
+                        if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0)
923a60
+                                return -errno;
923a60
+                }
923a60
+        }
923a60
+
923a60
+        return 0;
923a60
+}
923a60
+
923a60
 int capability_bounding_set_drop(uint64_t keep, bool right_now) {
923a60
         _cleanup_cap_free_ cap_t after_cap = NULL;
923a60
         cap_flag_value_t fv;
923a60
diff --git a/src/shared/capability.h b/src/shared/capability.h
923a60
index 04cd6e54e2..76a7568561 100644
923a60
--- a/src/shared/capability.h
923a60
+++ b/src/shared/capability.h
923a60
@@ -36,6 +36,9 @@ int capability_bounding_set_drop_usermode(uint64_t keep);
923a60
 
923a60
 int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilites);
923a60
 
923a60
+int capability_ambient_set_apply(uint64_t set, bool also_inherit);
923a60
+int capability_update_inherited_set(cap_t caps, uint64_t ambient_set);
923a60
+
923a60
 int drop_capability(cap_value_t cv);
923a60
 
923a60
 DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free);
923a60
diff --git a/src/shared/missing.h b/src/shared/missing.h
923a60
index 4b36a9c93a..a7771bc996 100644
923a60
--- a/src/shared/missing.h
923a60
+++ b/src/shared/missing.h
923a60
@@ -1004,3 +1004,19 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns
923a60
 #ifndef KCMP_FILE
923a60
 #define KCMP_FILE 0
923a60
 #endif
923a60
+
923a60
+#ifndef PR_CAP_AMBIENT
923a60
+#define PR_CAP_AMBIENT 47
923a60
+#endif
923a60
+
923a60
+#ifndef PR_CAP_AMBIENT_IS_SET
923a60
+#define PR_CAP_AMBIENT_IS_SET 1
923a60
+#endif
923a60
+
923a60
+#ifndef PR_CAP_AMBIENT_RAISE
923a60
+#define PR_CAP_AMBIENT_RAISE 2
923a60
+#endif
923a60
+
923a60
+#ifndef PR_CAP_AMBIENT_CLEAR_ALL
923a60
+#define PR_CAP_AMBIENT_CLEAR_ALL 4
923a60
+#endif
923a60
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
923a60
index 38ecfe972e..cfa3d23166 100644
923a60
--- a/src/test/test-unit-file.c
923a60
+++ b/src/test/test-unit-file.c
923a60
@@ -38,6 +38,7 @@
923a60
 #include "strv.h"
923a60
 #include "fileio.h"
923a60
 #include "test-helper.h"
923a60
+#include "capability.h"
923a60
 
923a60
 static int test_unit_file_get_set(void) {
923a60
         int r;