capabilities: keep bounding set in non-inverted format.
authorIsmo Puustinen <ismo.puustinen@intel.com>
Thu, 7 Jan 2016 22:00:04 +0000 (00:00 +0200)
committerIsmo Puustinen <ismo.puustinen@intel.com>
Tue, 12 Jan 2016 10:14:50 +0000 (12:14 +0200)
Change the capability bounding set parser and logic so that the bounding
set is kept as a positive set internally. This means that the set
reflects those capabilities that we want to keep instead of drop.

13 files changed:
src/basic/capability-util.c
src/basic/capability-util.h
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/main.c
src/core/unit.c
src/import/import-common.c
src/nspawn/nspawn.c
src/test/test-unit-file.c

index fef722b..881f0f6 100644 (file)
@@ -96,7 +96,7 @@ unsigned long cap_last_cap(void) {
         return p;
 }
 
-int capability_bounding_set_drop(uint64_t drop, bool right_now) {
+int capability_bounding_set_drop(uint64_t keep, bool right_now) {
         _cleanup_cap_free_ cap_t after_cap = NULL;
         cap_flag_value_t fv;
         unsigned long i;
@@ -137,7 +137,7 @@ int capability_bounding_set_drop(uint64_t drop, bool right_now) {
 
         for (i = 0; i <= cap_last_cap(); i++) {
 
-                if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
+                if (!(keep & (UINT64_C(1) << i))) {
                         cap_value_t v;
 
                         /* Drop it from the bounding set */
@@ -176,7 +176,7 @@ finish:
         return r;
 }
 
-static int drop_from_file(const char *fn, uint64_t drop) {
+static int drop_from_file(const char *fn, uint64_t keep) {
         int r, k;
         uint32_t hi, lo;
         uint64_t current, after;
@@ -196,7 +196,7 @@ static int drop_from_file(const char *fn, uint64_t drop) {
                 return -EIO;
 
         current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
-        after = current & ~drop;
+        after = current & keep;
 
         if (current == after)
                 return 0;
@@ -213,14 +213,14 @@ static int drop_from_file(const char *fn, uint64_t drop) {
         return r;
 }
 
-int capability_bounding_set_drop_usermode(uint64_t drop) {
+int capability_bounding_set_drop_usermode(uint64_t keep) {
         int r;
 
-        r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
+        r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep);
         if (r < 0)
                 return r;
 
-        r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
+        r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep);
         if (r < 0)
                 return r;
 
@@ -257,7 +257,7 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
                 return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
 
         /* Drop all caps from the bounding set, except the ones we want */
-        r = capability_bounding_set_drop(~keep_capabilities, true);
+        r = capability_bounding_set_drop(keep_capabilities, true);
         if (r < 0)
                 return log_error_errno(r, "Failed to drop capabilities: %m");
 
index 6bbf731..f6a48b7 100644 (file)
 #include "macro.h"
 #include "util.h"
 
+#define CAP_ALL (uint64_t) -1
+
 unsigned long cap_last_cap(void);
 int have_effective_cap(int value);
-int capability_bounding_set_drop(uint64_t drop, bool right_now);
-int capability_bounding_set_drop_usermode(uint64_t drop);
+int capability_bounding_set_drop(uint64_t keep, bool right_now);
+int capability_bounding_set_drop_usermode(uint64_t keep);
 
 int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
 
@@ -46,3 +48,9 @@ static inline void cap_free_charpp(char **p) {
                 cap_free(*p);
 }
 #define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
+
+static inline bool cap_test_all(uint64_t caps) {
+        uint64_t m;
+        m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1;
+        return (caps & m) == m;
+}
index 1f736b2..825cef7 100644 (file)
@@ -293,9 +293,7 @@ static int property_get_capability_bounding_set(
         assert(reply);
         assert(c);
 
-        /* We store this negated internally, to match the kernel, but
-         * we expose it normalized. */
-        return sd_bus_message_append(reply, "t", ~c->capability_bounding_set_drop);
+        return sd_bus_message_append(reply, "t", c->capability_bounding_set);
 }
 
 static int property_get_capabilities(
index 9b76861..7aeb5f1 100644 (file)
@@ -1866,8 +1866,8 @@ static int exec_child(
                         }
                 }
 
-                if (context->capability_bounding_set_drop) {
-                        r = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
+                if (!cap_test_all(context->capability_bounding_set)) {
+                        r = capability_bounding_set_drop(context->capability_bounding_set, false);
                         if (r < 0) {
                                 *exit_status = EXIT_CAPABILITIES;
                                 return r;
@@ -2114,6 +2114,7 @@ void exec_context_init(ExecContext *c) {
         c->timer_slack_nsec = NSEC_INFINITY;
         c->personality = PERSONALITY_INVALID;
         c->runtime_directory_mode = 0755;
+        c->capability_bounding_set = CAP_ALL;
 }
 
 void exec_context_done(ExecContext *c) {
@@ -2517,12 +2518,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                         (c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
                         (c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
 
-        if (c->capability_bounding_set_drop) {
+        if (c->capability_bounding_set != CAP_ALL) {
                 unsigned long l;
                 fprintf(f, "%sCapabilityBoundingSet:", prefix);
 
                 for (l = 0; l <= cap_last_cap(); l++)
-                        if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l)))
+                        if (c->capability_bounding_set & (UINT64_C(1) << l))
                                 fprintf(f, " %s", strna(capability_to_name(l)));
 
                 fputs("\n", f);
index be5be9f..9d2cdb8 100644 (file)
@@ -155,7 +155,7 @@ struct ExecContext {
         char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
         unsigned long mount_flags;
 
-        uint64_t capability_bounding_set_drop;
+        uint64_t capability_bounding_set;
 
         cap_t capabilities;
         int secure_bits;
index 0408b9a..862cb8d 100644 (file)
@@ -47,7 +47,7 @@ $1.SyslogLevel,                  config_parse_log_level,             0,
 $1.SyslogLevelPrefix,            config_parse_bool,                  0,                             offsetof($1, exec_context.syslog_level_prefix)
 $1.Capabilities,                 config_parse_exec_capabilities,     0,                             offsetof($1, exec_context)
 $1.SecureBits,                   config_parse_exec_secure_bits,      0,                             offsetof($1, exec_context)
-$1.CapabilityBoundingSet,        config_parse_bounding_set,          0,                             offsetof($1, exec_context.capability_bounding_set_drop)
+$1.CapabilityBoundingSet,        config_parse_capability_set,        0,                             offsetof($1, exec_context.capability_bounding_set)
 $1.TimerSlackNSec,               config_parse_nsec,                  0,                             offsetof($1, exec_context.timer_slack_nsec)
 $1.NoNewPrivileges,              config_parse_no_new_privileges,     0,                             offsetof($1, exec_context)
 m4_ifdef(`HAVE_SECCOMP',
index cb553e1..da14337 100644 (file)
@@ -38,6 +38,7 @@
 #include "bus-internal.h"
 #include "bus-util.h"
 #include "cap-list.h"
+#include "capability-util.h"
 #include "cgroup.h"
 #include "conf-parser.h"
 #include "cpu-set-util.h"
@@ -1024,7 +1025,7 @@ int config_parse_exec_secure_bits(const char *unit,
         return 0;
 }
 
-int config_parse_bounding_set(
+int config_parse_capability_set(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1036,8 +1037,8 @@ int config_parse_bounding_set(
                 void *data,
                 void *userdata) {
 
-        uint64_t *capability_bounding_set_drop = data;
-        uint64_t capability_bounding_set, sum = 0;
+        uint64_t *capability_set = data;
+        uint64_t sum = 0, initial = 0;
         bool invert = false;
         const char *p;
 
@@ -1051,10 +1052,8 @@ int config_parse_bounding_set(
                 rvalue++;
         }
 
-        /* Note that we store this inverted internally, since the
-         * kernel wants it like this. But we actually expose it
-         * non-inverted everywhere to have a fully normalized
-         * interface. */
+        if (strcmp(lvalue, "CapabilityBoundingSet") == 0)
+                initial = CAP_ALL; /* initialized to all bits on */
 
         p = rvalue;
         for (;;) {
@@ -1080,11 +1079,14 @@ int config_parse_bounding_set(
                 sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap;
         }
 
-        capability_bounding_set = invert ? ~sum : sum;
-        if (*capability_bounding_set_drop != 0 && capability_bounding_set != 0)
-                *capability_bounding_set_drop = ~(~*capability_bounding_set_drop | capability_bounding_set);
+        sum = invert ? ~sum : sum;
+
+        if (sum == 0 || *capability_set == initial)
+                /* "" or uninitialized data -> replace */
+                *capability_set = sum;
         else
-                *capability_bounding_set_drop = ~capability_bounding_set;
+                /* previous data -> merge */
+                *capability_set |= sum;
 
         return 0;
 }
@@ -4002,7 +4004,7 @@ void unit_dump_config_items(FILE *f) {
                 { config_parse_log_level,             "LEVEL" },
                 { config_parse_exec_capabilities,     "CAPABILITIES" },
                 { config_parse_exec_secure_bits,      "SECUREBITS" },
-                { config_parse_bounding_set,          "BOUNDINGSET" },
+                { config_parse_capability_set,        "BOUNDINGSET" },
                 { config_parse_limit,                 "LIMIT" },
                 { config_parse_unit_deps,             "UNIT [...]" },
                 { config_parse_exec,                  "PATH [ARGUMENT [...]]" },
index a451fc1..f0027a6 100644 (file)
@@ -56,7 +56,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, uns
 int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_exec_capabilities(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_capability_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_bytes_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_sec_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
index f9de540..7a428fc 100644 (file)
@@ -117,7 +117,7 @@ static usec_t arg_runtime_watchdog = 0;
 static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
 static char **arg_default_environment = NULL;
 static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
-static uint64_t arg_capability_bounding_set_drop = 0;
+static uint64_t arg_capability_bounding_set = CAP_ALL;
 static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
 static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
 static Set* arg_syscall_archs = NULL;
@@ -644,7 +644,7 @@ static int parse_config_file(void) {
                 { "Manager", "JoinControllers",           config_parse_join_controllers, 0, &arg_join_controllers                  },
                 { "Manager", "RuntimeWatchdogSec",        config_parse_sec,              0, &arg_runtime_watchdog                  },
                 { "Manager", "ShutdownWatchdogSec",       config_parse_sec,              0, &arg_shutdown_watchdog                 },
-                { "Manager", "CapabilityBoundingSet",     config_parse_bounding_set,     0, &arg_capability_bounding_set_drop      },
+                { "Manager", "CapabilityBoundingSet",     config_parse_capability_set,   0, &arg_capability_bounding_set           },
 #ifdef HAVE_SECCOMP
                 { "Manager", "SystemCallArchitectures",   config_parse_syscall_archs,    0, &arg_syscall_archs                     },
 #endif
@@ -1631,14 +1631,14 @@ int main(int argc, char *argv[]) {
                 if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
                         log_error_errno(errno, "Failed to adjust timer slack: %m");
 
-        if (arg_capability_bounding_set_drop) {
-                r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
+        if (!cap_test_all(arg_capability_bounding_set)) {
+                r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
                 if (r < 0) {
                         log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
                         error_message = "Failed to drop capability bounding set of usermode helpers";
                         goto finish;
                 }
-                r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
+                r = capability_bounding_set_drop(arg_capability_bounding_set, true);
                 if (r < 0) {
                         log_emergency_errno(r, "Failed to drop capability bounding set: %m");
                         error_message = "Failed to drop capability bounding set";
index b977ac7..e1bc6c7 100644 (file)
@@ -3231,7 +3231,7 @@ int unit_patch_contexts(Unit *u) {
                         ec->no_new_privileges = true;
 
                 if (ec->private_devices)
-                        ec->capability_bounding_set_drop |= (uint64_t) 1ULL << (uint64_t) CAP_MKNOD;
+                        ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD);
         }
 
         cc = unit_get_cgroup_context(u);
index a8551ca..8a48bd7 100644 (file)
@@ -134,7 +134,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
                 if (unshare(CLONE_NEWNET) < 0)
                         log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
 
-                r = capability_bounding_set_drop(~retain, true);
+                r = capability_bounding_set_drop(retain, true);
                 if (r < 0)
                         log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
 
@@ -208,7 +208,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
                 if (unshare(CLONE_NEWNET) < 0)
                         log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
 
-                r = capability_bounding_set_drop(~retain, true);
+                r = capability_bounding_set_drop(retain, true);
                 if (r < 0)
                         log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
 
index a4e13bd..d619206 100644 (file)
@@ -1482,7 +1482,7 @@ static int setup_journal(const char *directory) {
 }
 
 static int drop_capabilities(void) {
-        return capability_bounding_set_drop(~arg_retain, false);
+        return capability_bounding_set_drop(arg_retain, false);
 }
 
 static int reset_audit_loginuid(void) {
index 0b3630f..cd1e4e4 100644 (file)
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "capability-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "hashmap.h"
@@ -625,8 +626,8 @@ static uint64_t make_cap(int cap) {
         return ((uint64_t) 1ULL << (uint64_t) cap);
 }
 
-static void test_config_parse_bounding_set(void) {
-        /* int config_parse_bounding_set(
+static void test_config_parse_capability_set(void) {
+        /* int config_parse_capability_set(
                  const char *unit,
                  const char *filename,
                  unsigned line,
@@ -638,38 +639,38 @@ static void test_config_parse_bounding_set(void) {
                  void *data,
                  void *userdata) */
         int r;
-        uint64_t capability_bounding_set_drop = 0;
+        uint64_t capability_bounding_set = 0;
 
-        r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+        r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
                               "CapabilityBoundingSet", 0, "CAP_NET_RAW",
-                              &capability_bounding_set_drop, NULL);
+                              &capability_bounding_set, NULL);
         assert_se(r >= 0);
-        assert_se(capability_bounding_set_drop == ~make_cap(CAP_NET_RAW));
+        assert_se(capability_bounding_set == make_cap(CAP_NET_RAW));
 
-        r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+        r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
                               "CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
-                              &capability_bounding_set_drop, NULL);
+                              &capability_bounding_set, NULL);
         assert_se(r >= 0);
-        assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
+        assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
 
-        r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+        r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
                               "CapabilityBoundingSet", 0, "",
-                              &capability_bounding_set_drop, NULL);
+                              &capability_bounding_set, NULL);
         assert_se(r >= 0);
-        assert_se(capability_bounding_set_drop == ~((uint64_t) 0ULL));
+        assert_se(capability_bounding_set == UINT64_C(0));
 
-        r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+        r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
                               "CapabilityBoundingSet", 0, "~",
-                              &capability_bounding_set_drop, NULL);
+                              &capability_bounding_set, NULL);
         assert_se(r >= 0);
-        assert_se(capability_bounding_set_drop == (uint64_t) 0ULL);
+        assert_se(cap_test_all(capability_bounding_set));
 
-        capability_bounding_set_drop = 0;
-        r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+        capability_bounding_set = 0;
+        r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
                               "CapabilityBoundingSet", 0, "  'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
-                              &capability_bounding_set_drop, NULL);
+                              &capability_bounding_set, NULL);
         assert_se(r >= 0);
-        assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
+        assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
 }
 
 static void test_config_parse_rlimit(void) {
@@ -829,7 +830,7 @@ int main(int argc, char *argv[]) {
 
         r = test_unit_file_get_set();
         test_config_parse_exec();
-        test_config_parse_bounding_set();
+        test_config_parse_capability_set();
         test_config_parse_rlimit();
         test_config_parse_pass_environ();
         test_load_env_file_1();