]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cgroup: turn device cgroup controller "rwm" strings into proper flags
authorLennart Poettering <lennart@poettering.net>
Tue, 10 Oct 2023 14:01:16 +0000 (16:01 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Tue, 17 Oct 2023 22:27:01 +0000 (23:27 +0100)
We generally prefer dealing with parsed data instead of original
strings, do so for the "rwm" strings too. We have to convert this to
flags for the primary backend implementation (BPF) anyway, hence we
can do this early to have simpler, shorter and more normalized code.

src/analyze/analyze-security.c
src/core/bpf-devices.c
src/core/bpf-devices.h
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-cgroup.c
src/core/execute-serialize.c
src/core/load-fragment.c
src/core/unit.c
src/test/test-bpf-devices.c
src/test/test-cgroup-mask.c

index fb743a895f381d84c277609ca6332396d768bb3a..730f07092eb32cccd1d0e7a187864e6ee7876849 100644 (file)
@@ -2629,9 +2629,9 @@ static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, Security
 
                 LIST_FOREACH(device_allow, a, g->device_allow)
                         if (strv_extendf(&info->device_allow,
-                                         "%s:%s%s%s",
+                                         "%s:%s",
                                          a->path,
-                                         a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "") < 0)
+                                         cgroup_device_permissions_to_string(a->permissions)) < 0)
                                 return log_oom();
         }
 
index c79cda5b76d9cacfc06339f827aaa6961b3a13d8..06d2146dcdd35a93da44c33b875bbe1743cb295d 100644 (file)
 
 #define PASS_JUMP_OFF 4096
 
-static int bpf_access_type(const char *acc) {
-        int r = 0;
-
-        assert(acc);
-
-        for (; *acc; acc++)
-                switch (*acc) {
-                case 'r':
-                        r |= BPF_DEVCG_ACC_READ;
-                        break;
-                case 'w':
-                        r |= BPF_DEVCG_ACC_WRITE;
-                        break;
-                case 'm':
-                        r |= BPF_DEVCG_ACC_MKNOD;
-                        break;
-                default:
-                        return -EINVAL;
-                }
-
-        return r;
-}
+/* Ensure the high level flags we use and the low-level BPF flags exposed on the kernel are defined the same way */
+assert_cc((unsigned) BPF_DEVCG_ACC_MKNOD == (unsigned) CGROUP_DEVICE_MKNOD);
+assert_cc((unsigned) BPF_DEVCG_ACC_READ  == (unsigned) CGROUP_DEVICE_READ);
+assert_cc((unsigned) BPF_DEVCG_ACC_WRITE == (unsigned) CGROUP_DEVICE_WRITE);
 
 static int bpf_prog_allow_list_device(
                 BPFProgram *prog,
                 char type,
                 int major,
                 int minor,
-                const char *acc) {
+                CGroupDevicePermissions p) {
 
-        int r, access;
+        int r;
 
         assert(prog);
-        assert(acc);
 
-        log_trace("%s: %c %d:%d %s", __func__, type, major, minor, acc);
+        log_trace("%s: %c %d:%d %s", __func__, type, major, minor, cgroup_device_permissions_to_string(p));
 
-        access = bpf_access_type(acc);
-        if (access <= 0)
+        if (p <= 0 || p >= _CGROUP_DEVICE_PERMISSIONS_MAX)
                 return -EINVAL;
 
         assert(IN_SET(type, 'b', 'c'));
@@ -62,7 +42,7 @@ static int bpf_prog_allow_list_device(
 
         const struct bpf_insn insn[] = {
                 BPF_MOV32_REG(BPF_REG_1, BPF_REG_3),
-                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, access),
+                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, p),
                 BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 4), /* compare access type */
 
                 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, bpf_type, 3),  /* compare device type */
@@ -71,7 +51,7 @@ static int bpf_prog_allow_list_device(
                 BPF_JMP_A(PASS_JUMP_OFF),                      /* jump to PASS */
         };
 
-        if (FLAGS_SET(access, BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD))
+        if (p == _CGROUP_DEVICE_PERMISSIONS_ALL)
                 r = bpf_program_add_instructions(prog, insn + 3, ELEMENTSOF(insn) - 3);
         else
                 r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
@@ -85,17 +65,15 @@ static int bpf_prog_allow_list_major(
                 BPFProgram *prog,
                 char type,
                 int major,
-                const char *acc) {
+                CGroupDevicePermissions p) {
 
-        int r, access;
+        int r;
 
         assert(prog);
-        assert(acc);
 
-        log_trace("%s: %c %d:* %s", __func__, type, major, acc);
+        log_trace("%s: %c %d:* %s", __func__, type, major, cgroup_device_permissions_to_string(p));
 
-        access = bpf_access_type(acc);
-        if (access <= 0)
+        if (p <= 0 || p >= _CGROUP_DEVICE_PERMISSIONS_MAX)
                 return -EINVAL;
 
         assert(IN_SET(type, 'b', 'c'));
@@ -103,7 +81,7 @@ static int bpf_prog_allow_list_major(
 
         const struct bpf_insn insn[] = {
                 BPF_MOV32_REG(BPF_REG_1, BPF_REG_3),
-                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, access),
+                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, p),
                 BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 3), /* compare access type */
 
                 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, bpf_type, 2),  /* compare device type */
@@ -111,7 +89,7 @@ static int bpf_prog_allow_list_major(
                 BPF_JMP_A(PASS_JUMP_OFF),                      /* jump to PASS */
         };
 
-        if (FLAGS_SET(access, BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD))
+        if (p == _CGROUP_DEVICE_PERMISSIONS_ALL)
                 r = bpf_program_add_instructions(prog, insn + 3, ELEMENTSOF(insn) - 3);
         else
                 r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
@@ -124,17 +102,15 @@ static int bpf_prog_allow_list_major(
 static int bpf_prog_allow_list_class(
                 BPFProgram *prog,
                 char type,
-                const char *acc) {
+                CGroupDevicePermissions p) {
 
-        int r, access;
+        int r;
 
         assert(prog);
-        assert(acc);
 
-        log_trace("%s: %c *:* %s", __func__, type, acc);
+        log_trace("%s: %c *:* %s", __func__, type, cgroup_device_permissions_to_string(p));
 
-        access = bpf_access_type(acc);
-        if (access <= 0)
+        if (p <= 0 || p >= _CGROUP_DEVICE_PERMISSIONS_MAX)
                 return -EINVAL;
 
         assert(IN_SET(type, 'b', 'c'));
@@ -142,14 +118,14 @@ static int bpf_prog_allow_list_class(
 
         const struct bpf_insn insn[] = {
                 BPF_MOV32_REG(BPF_REG_1, BPF_REG_3),
-                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, access),
+                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, p),
                 BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 2), /* compare access type */
 
                 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, bpf_type, 1), /* compare device type */
                 BPF_JMP_A(PASS_JUMP_OFF),                     /* jump to PASS */
         };
 
-        if (FLAGS_SET(access, BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD))
+        if (p == _CGROUP_DEVICE_PERMISSIONS_ALL)
                 r = bpf_program_add_instructions(prog, insn + 3, ELEMENTSOF(insn) - 3);
         else
                 r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
@@ -333,7 +309,7 @@ static int allow_list_device_pattern(
                 char type,
                 const unsigned *maj,
                 const unsigned *min,
-                const char *acc) {
+                CGroupDevicePermissions p) {
 
         assert(IN_SET(type, 'b', 'c'));
 
@@ -342,22 +318,22 @@ static int allow_list_device_pattern(
                         return 0;
 
                 if (maj && min)
-                        return bpf_prog_allow_list_device(prog, type, *maj, *min, acc);
+                        return bpf_prog_allow_list_device(prog, type, *maj, *min, p);
                 else if (maj)
-                        return bpf_prog_allow_list_major(prog, type, *maj, acc);
+                        return bpf_prog_allow_list_major(prog, type, *maj, p);
                 else
-                        return bpf_prog_allow_list_class(prog, type, acc);
+                        return bpf_prog_allow_list_class(prog, type, p);
 
         } else {
                 char buf[2+DECIMAL_STR_MAX(unsigned)*2+2+4];
                 int r;
 
                 if (maj && min)
-                        xsprintf(buf, "%c %u:%u %s", type, *maj, *min, acc);
+                        xsprintf(buf, "%c %u:%u %s", type, *maj, *min, cgroup_device_permissions_to_string(p));
                 else if (maj)
-                        xsprintf(buf, "%c %u:* %s", type, *maj, acc);
+                        xsprintf(buf, "%c %u:* %s", type, *maj, cgroup_device_permissions_to_string(p));
                 else
-                        xsprintf(buf, "%c *:* %s", type, acc);
+                        xsprintf(buf, "%c *:* %s", type, cgroup_device_permissions_to_string(p));
 
                 /* Changing the devices list of a populated cgroup might result in EINVAL, hence ignore
                  * EINVAL here. */
@@ -375,17 +351,16 @@ int bpf_devices_allow_list_device(
                 BPFProgram *prog,
                 const char *path,
                 const char *node,
-                const char *acc) {
+                CGroupDevicePermissions p) {
 
         mode_t mode;
         dev_t rdev;
         int r;
 
         assert(path);
-        assert(acc);
-        assert(strlen(acc) <= 3);
+        assert(p >= 0 && p < _CGROUP_DEVICE_PERMISSIONS_MAX);
 
-        log_trace("%s: %s %s", __func__, node, acc);
+        log_trace("%s: %s %s", __func__, node, cgroup_device_permissions_to_string(p));
 
         /* Some special handling for /dev/block/%u:%u, /dev/char/%u:%u, /run/systemd/inaccessible/chr and
          * /run/systemd/inaccessible/blk paths. Instead of stat()ing these we parse out the major/minor directly. This
@@ -407,7 +382,7 @@ int bpf_devices_allow_list_device(
         }
 
         unsigned maj = major(rdev), min = minor(rdev);
-        return allow_list_device_pattern(prog, path, S_ISCHR(mode) ? 'c' : 'b', &maj, &min, acc);
+        return allow_list_device_pattern(prog, path, S_ISCHR(mode) ? 'c' : 'b', &maj, &min, p);
 }
 
 int bpf_devices_allow_list_major(
@@ -415,23 +390,23 @@ int bpf_devices_allow_list_major(
                 const char *path,
                 const char *name,
                 char type,
-                const char *acc) {
+                CGroupDevicePermissions permissions) {
 
         unsigned maj;
         int r;
 
         assert(path);
-        assert(acc);
         assert(IN_SET(type, 'b', 'c'));
+        assert(permissions >= 0 && permissions < _CGROUP_DEVICE_PERMISSIONS_MAX);
 
         if (streq(name, "*"))
                 /* If the name is a wildcard, then apply this list to all devices of this type */
-                return allow_list_device_pattern(prog, path, type, NULL, NULL, acc);
+                return allow_list_device_pattern(prog, path, type, NULL, NULL, permissions);
 
         if (safe_atou(name, &maj) >= 0 && DEVICE_MAJOR_VALID(maj))
                 /* The name is numeric and suitable as major. In that case, let's take its major, and create
                  * the entry directly. */
-                return allow_list_device_pattern(prog, path, type, &maj, NULL, acc);
+                return allow_list_device_pattern(prog, path, type, &maj, NULL, permissions);
 
         _cleanup_fclose_ FILE *f = NULL;
         bool good = false, any = false;
@@ -488,7 +463,7 @@ int bpf_devices_allow_list_major(
                         continue;
 
                 any = true;
-                (void) allow_list_device_pattern(prog, path, type, &maj, NULL, acc);
+                (void) allow_list_device_pattern(prog, path, type, &maj, NULL, permissions);
         }
 
         if (!any)
@@ -516,13 +491,13 @@ int bpf_devices_allow_list_static(
         int r = 0, k;
 
         NULSTR_FOREACH_PAIR(node, acc, auto_devices) {
-                k = bpf_devices_allow_list_device(prog, path, node, acc);
+                k = bpf_devices_allow_list_device(prog, path, node, cgroup_device_permissions_from_string(acc));
                 if (r >= 0 && k < 0)
                         r = k;
         }
 
         /* PTS (/dev/pts) devices may not be duplicated, but accessed */
-        k = bpf_devices_allow_list_major(prog, path, "pts", 'c', "rw");
+        k = bpf_devices_allow_list_major(prog, path, "pts", 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
         if (r >= 0 && k < 0)
                 r = k;
 
index 51063640a895c679a2f744ed15de8a7e19f95b62..5660e1a03a60265d3b57fd60ba695c62e5bca0d2 100644 (file)
@@ -16,6 +16,6 @@ int bpf_devices_apply_policy(
                 BPFProgram **prog_installed);
 
 int bpf_devices_supported(void);
-int bpf_devices_allow_list_device(BPFProgram *prog, const char *path, const char *node, const char *acc);
-int bpf_devices_allow_list_major(BPFProgram *prog, const char *path, const char *name, char type, const char *acc);
+int bpf_devices_allow_list_device(BPFProgram *prog, const char *path, const char *node, CGroupDevicePermissions p);
+int bpf_devices_allow_list_major(BPFProgram *prog, const char *path, const char *name, char type, CGroupDevicePermissions p);
 int bpf_devices_allow_list_static(BPFProgram *prog, const char *path);
index c2bf3af20cd44c08dd7d5a9069d96580ed3d18f0..dd46bf92373ed72274362bf4b4859c054b089f70 100644 (file)
@@ -455,6 +455,46 @@ static char *format_cgroup_memory_limit_comparison(char *buf, size_t l, Unit *u,
         return buf;
 }
 
+const char *cgroup_device_permissions_to_string(CGroupDevicePermissions p) {
+        static const char *table[_CGROUP_DEVICE_PERMISSIONS_MAX] = {
+                /* Lets simply define a table with every possible combination. As long as those are just 8 we
+                 * can get away with it. If this ever grows to more we need to revisit this logic though. */
+                [0]                                                          = "",
+                [CGROUP_DEVICE_READ]                                         = "r",
+                [CGROUP_DEVICE_WRITE]                                        = "w",
+                [CGROUP_DEVICE_MKNOD]                                        = "m",
+                [CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE]                     = "rw",
+                [CGROUP_DEVICE_READ|CGROUP_DEVICE_MKNOD]                     = "rm",
+                [CGROUP_DEVICE_WRITE|CGROUP_DEVICE_MKNOD]                    = "wm",
+                [CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE|CGROUP_DEVICE_MKNOD] = "rwm",
+        };
+
+        if (p < 0 || p >= _CGROUP_DEVICE_PERMISSIONS_MAX)
+                return NULL;
+
+        return table[p];
+}
+
+CGroupDevicePermissions cgroup_device_permissions_from_string(const char *s) {
+        CGroupDevicePermissions p = 0;
+
+        if (!s)
+                return _CGROUP_DEVICE_PERMISSIONS_INVALID;
+
+        for (const char *c = s; *c; c++) {
+                if (*c == 'r')
+                        p |= CGROUP_DEVICE_READ;
+                else if (*c == 'w')
+                        p |= CGROUP_DEVICE_WRITE;
+                else if (*c == 'm')
+                        p |= CGROUP_DEVICE_MKNOD;
+                else
+                        return _CGROUP_DEVICE_PERMISSIONS_INVALID;
+        }
+
+        return p;
+}
+
 void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
         _cleanup_free_ char *disable_controllers_str = NULL, *delegate_controllers_str = NULL, *cpuset_cpus = NULL, *cpuset_mems = NULL, *startup_cpuset_cpus = NULL, *startup_cpuset_mems = NULL;
         CGroupContext *c;
@@ -590,10 +630,10 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
 
         LIST_FOREACH(device_allow, a, c->device_allow)
                 fprintf(f,
-                        "%sDeviceAllow: %s %s%s%s\n",
+                        "%sDeviceAllow: %s %s\n",
                         prefix,
                         a->path,
-                        a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
+                        cgroup_device_permissions_to_string(a->permissions));
 
         LIST_FOREACH(device_weights, iw, c->io_device_weights)
                 fprintf(f,
@@ -704,13 +744,16 @@ void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE
         }
 }
 
-int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, const char *mode) {
+int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p) {
         _cleanup_free_ CGroupDeviceAllow *a = NULL;
         _cleanup_free_ char *d = NULL;
 
         assert(c);
         assert(dev);
-        assert(isempty(mode) || in_charset(mode, "rwm"));
+        assert(p >= 0 && p < _CGROUP_DEVICE_PERMISSIONS_MAX);
+
+        if (p == 0)
+                p = _CGROUP_DEVICE_PERMISSIONS_ALL;
 
         a = new(CGroupDeviceAllow, 1);
         if (!a)
@@ -722,9 +765,7 @@ int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, const cha
 
         *a = (CGroupDeviceAllow) {
                 .path = TAKE_PTR(d),
-                .r = isempty(mode) || strchr(mode, 'r'),
-                .w = isempty(mode) || strchr(mode, 'w'),
-                .m = isempty(mode) || strchr(mode, 'm'),
+                .permissions = p,
         };
 
         LIST_PREPEND(device_allow, c->device_allow, a);
@@ -733,21 +774,21 @@ int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, const cha
         return 0;
 }
 
-int cgroup_context_add_or_update_device_allow(CGroupContext *c, const char *dev, const char *mode) {
+int cgroup_context_add_or_update_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p) {
         assert(c);
         assert(dev);
-        assert(isempty(mode) || in_charset(mode, "rwm"));
+        assert(p >= 0 && p < _CGROUP_DEVICE_PERMISSIONS_MAX);
+
+        if (p == 0)
+                p = _CGROUP_DEVICE_PERMISSIONS_ALL;
 
         LIST_FOREACH(device_allow, b, c->device_allow)
                 if (path_equal(b->path, dev)) {
-                        b->r = isempty(mode) || strchr(mode, 'r');
-                        b->w = isempty(mode) || strchr(mode, 'w');
-                        b->m = isempty(mode) || strchr(mode, 'm');
-
+                        b->permissions = p;
                         return 0;
                 }
 
-        return cgroup_context_add_device_allow(c, dev, mode);
+        return cgroup_context_add_device_allow(c, dev, p);
 }
 
 int cgroup_context_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *bpffs_path) {
@@ -1520,25 +1561,17 @@ static int cgroup_apply_devices(Unit *u) {
 
         bool any = allow_list_static;
         LIST_FOREACH(device_allow, a, c->device_allow) {
-                char acc[4], *val;
-                unsigned k = 0;
-
-                if (a->r)
-                        acc[k++] = 'r';
-                if (a->w)
-                        acc[k++] = 'w';
-                if (a->m)
-                        acc[k++] = 'm';
-                if (k == 0)
+                const char *val;
+
+                if (a->permissions == 0)
                         continue;
-                acc[k++] = 0;
 
                 if (path_startswith(a->path, "/dev/"))
-                        r = bpf_devices_allow_list_device(prog, path, a->path, acc);
+                        r = bpf_devices_allow_list_device(prog, path, a->path, a->permissions);
                 else if ((val = startswith(a->path, "block-")))
-                        r = bpf_devices_allow_list_major(prog, path, val, 'b', acc);
+                        r = bpf_devices_allow_list_major(prog, path, val, 'b', a->permissions);
                 else if ((val = startswith(a->path, "char-")))
-                        r = bpf_devices_allow_list_major(prog, path, val, 'c', acc);
+                        r = bpf_devices_allow_list_major(prog, path, val, 'c', a->permissions);
                 else {
                         log_unit_debug(u, "Ignoring device '%s' while writing cgroup attribute.", a->path);
                         continue;
index 5b5e868a58fedd665a964c4c801c8243696a7e4c..e043c639f89e41e29b0f13e44574cdbc59262dbf 100644 (file)
@@ -59,12 +59,20 @@ typedef enum FreezerAction {
         _FREEZER_ACTION_INVALID = -EINVAL,
 } FreezerAction;
 
+typedef enum CGroupDevicePermissions {
+        /* We reuse the same bit meanings the kernel's BPF_DEVCG_ACC_xyz definitions use */
+        CGROUP_DEVICE_MKNOD                = 1 << 0,
+        CGROUP_DEVICE_READ                 = 1 << 1,
+        CGROUP_DEVICE_WRITE                = 1 << 2,
+        _CGROUP_DEVICE_PERMISSIONS_MAX     = 1 << 3,
+        _CGROUP_DEVICE_PERMISSIONS_ALL     = _CGROUP_DEVICE_PERMISSIONS_MAX - 1,
+        _CGROUP_DEVICE_PERMISSIONS_INVALID = -EINVAL,
+} CGroupDevicePermissions;
+
 struct CGroupDeviceAllow {
         LIST_FIELDS(CGroupDeviceAllow, device_allow);
         char *path;
-        bool r:1;
-        bool w:1;
-        bool m:1;
+        CGroupDevicePermissions permissions;
 };
 
 struct CGroupIODeviceWeight {
@@ -282,8 +290,8 @@ static inline bool cgroup_context_want_memory_pressure(const CGroupContext *c) {
                 (c->memory_pressure_watch == CGROUP_PRESSURE_WATCH_AUTO && c->memory_accounting);
 }
 
-int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, const char *mode);
-int cgroup_context_add_or_update_device_allow(CGroupContext *c, const char *dev, const char *mode);
+int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
+int cgroup_context_add_or_update_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
 int cgroup_context_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
 
 void unit_modify_nft_set(Unit *u, bool add);
@@ -388,3 +396,6 @@ FreezerAction freezer_action_from_string(const char *s) _pure_;
 
 const char* cgroup_pressure_watch_to_string(CGroupPressureWatch a) _const_;
 CGroupPressureWatch cgroup_pressure_watch_from_string(const char *s) _pure_;
+
+const char *cgroup_device_permissions_to_string(CGroupDevicePermissions p) _const_;
+CGroupDevicePermissions cgroup_device_permissions_from_string(const char *s) _pure_;
index 681b86dd876665898e7ac2622b2fddfc7e82dbf0..4237e694c01223bba2dffbe7c50218bf51746647 100644 (file)
@@ -281,19 +281,7 @@ static int property_get_device_allow(
                 return r;
 
         LIST_FOREACH(device_allow, a, c->device_allow) {
-                unsigned k = 0;
-                char rwm[4];
-
-                if (a->r)
-                        rwm[k++] = 'r';
-                if (a->w)
-                        rwm[k++] = 'w';
-                if (a->m)
-                        rwm[k++] = 'm';
-
-                rwm[k] = 0;
-
-                r = sd_bus_message_append(reply, "(ss)", a->path, rwm);
+                r = sd_bus_message_append(reply, "(ss)", a->path, cgroup_device_permissions_to_string(a->permissions));
                 if (r < 0)
                         return r;
         }
@@ -1828,17 +1816,21 @@ int bus_cgroup_set_property(
                         return r;
 
                 while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
+                        CGroupDevicePermissions p;
 
                         if (!valid_device_allow_pattern(path) || strpbrk(path, WHITESPACE))
                                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node or pattern");
 
                         if (isempty(rwm))
-                                rwm = "rwm";
-                        else if (!in_charset(rwm, "rwm"))
-                                return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
+                                p = _CGROUP_DEVICE_PERMISSIONS_ALL;
+                        else {
+                                p = cgroup_device_permissions_from_string(rwm);
+                                if (p < 0)
+                                        return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
+                        }
 
                         if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                                r = cgroup_context_add_or_update_device_allow(c, path, rwm);
+                                r = cgroup_context_add_or_update_device_allow(c, path, p);
                                 if (r < 0)
                                         return r;
                         }
@@ -1869,7 +1861,7 @@ int bus_cgroup_set_property(
 
                         fputs("DeviceAllow=\n", f);
                         LIST_FOREACH(device_allow, a, c->device_allow)
-                                fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
+                                fprintf(f, "DeviceAllow=%s %s\n", a->path, cgroup_device_permissions_to_string(a->permissions));
 
                         r = memstream_finalize(&m, &buf, NULL);
                         if (r < 0)
index b006f9c526629af20be213ce3ee0ff7a8635b10e..dd010660d6a68f875bba0d28885ce22b4e4bc52b 100644 (file)
@@ -343,9 +343,9 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) {
         }
 
         LIST_FOREACH(device_allow, a, c->device_allow) {
-                r = serialize_item_format(f, "exec-cgroup-context-device-allow", "%s %s%s%s",
+                r = serialize_item_format(f, "exec-cgroup-context-device-allow", "%s %s",
                                           a->path,
-                                          a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
+                                          cgroup_device_permissions_to_string(a->permissions));
                 if (r < 0)
                         return r;
         }
@@ -787,16 +787,19 @@ static int exec_cgroup_context_deserialize(CGroupContext *c, FILE *f) {
                                 return r;
                 } else if ((val = startswith(l, "exec-cgroup-context-device-allow="))) {
                         _cleanup_free_ char *path = NULL, *rwm = NULL;
+                        CGroupDevicePermissions p;
 
                         r = extract_many_words(&val, " ", 0, &path, &rwm, NULL);
                         if (r < 0)
                                 return r;
                         if (r == 0)
                                 return -EINVAL;
-                        if (!isempty(rwm) && !in_charset(rwm, "rwm"))
-                                return -EINVAL;
 
-                        r = cgroup_context_add_or_update_device_allow(c, path, rwm);
+                        p = isempty(rwm) ? 0 : cgroup_device_permissions_from_string(rwm);
+                        if (p < 0)
+                                return p;
+
+                        r = cgroup_context_add_or_update_device_allow(c, path, p);
                         if (r < 0)
                                 return r;
                 } else if ((val = startswith(l, "exec-cgroup-context-io-device-weight="))) {
index 3b737bf2f368e2d9f3fa53609d2b5dae563da782..ece2ec840f0fbb0a83e37d1f160a385d2dcff8a0 100644 (file)
@@ -4151,6 +4151,7 @@ int config_parse_device_allow(
                 void *userdata) {
 
         _cleanup_free_ char *path = NULL, *resolved = NULL;
+        CGroupDevicePermissions permissions;
         CGroupContext *c = data;
         const char *p = rvalue;
         int r;
@@ -4190,12 +4191,13 @@ int config_parse_device_allow(
                 }
         }
 
-        if (!isempty(p) && !in_charset(p, "rwm")) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device rights '%s', ignoring.", p);
+        permissions = isempty(p) ? 0 : cgroup_device_permissions_from_string(p);
+        if (permissions < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, permissions, "Invalid device rights '%s', ignoring.", p);
                 return 0;
         }
 
-        return cgroup_context_add_device_allow(c, resolved, p);
+        return cgroup_context_add_device_allow(c, resolved, permissions);
 }
 
 int config_parse_io_device_weight(
index aa809843f731d4257f34fedac37ff499675dadee..4aef2e97de3acd600ae422d9e0d674d500b442fb 100644 (file)
@@ -4370,12 +4370,12 @@ int unit_patch_contexts(Unit *u) {
 
                                 /* When RootImage= or MountImages= is specified, the following devices are touched. */
                                 FOREACH_STRING(p, "/dev/loop-control", "/dev/mapper/control") {
-                                        r = cgroup_context_add_device_allow(cc, p, "rw");
+                                        r = cgroup_context_add_device_allow(cc, p, CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
                                         if (r < 0)
                                                 return r;
                                 }
                                 FOREACH_STRING(p, "block-loop", "block-blkext", "block-device-mapper") {
-                                        r = cgroup_context_add_device_allow(cc, p, "rwm");
+                                        r = cgroup_context_add_device_allow(cc, p, CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE|CGROUP_DEVICE_MKNOD);
                                         if (r < 0)
                                                 return r;
                                 }
@@ -4390,14 +4390,14 @@ int unit_patch_contexts(Unit *u) {
                         }
 
                         if (ec->protect_clock) {
-                                r = cgroup_context_add_device_allow(cc, "char-rtc", "r");
+                                r = cgroup_context_add_device_allow(cc, "char-rtc", CGROUP_DEVICE_READ);
                                 if (r < 0)
                                         return r;
                         }
 
                         /* If there are encrypted credentials we might need to access the TPM. */
                         if (exec_context_has_encrypted_credentials(ec)) {
-                                r = cgroup_context_add_device_allow(cc, "char-tpm", "rw");
+                                r = cgroup_context_add_device_allow(cc, "char-tpm", CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
                                 if (r < 0)
                                         return r;
                         }
index 438e49bb18f73a1f3fa826e5501dac63bb2f2d56..4bd606ecb3660fab2138f5fc1f4e0df1f2a8f7a7 100644 (file)
@@ -61,13 +61,13 @@ static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_p
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
         assert_se(r >= 0);
 
-        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/null", "rw");
+        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/null", CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
         assert_se(r >= 0);
 
-        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/random", "r");
+        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/random", CGROUP_DEVICE_READ);
         assert_se(r >= 0);
 
-        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/zero", "w");
+        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/zero", CGROUP_DEVICE_WRITE);
         assert_se(r >= 0);
 
         r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
@@ -138,7 +138,7 @@ static void test_policy_allow_list_major(const char *pattern, const char *cgroup
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
         assert_se(r >= 0);
 
-        r = bpf_devices_allow_list_major(prog, cgroup_path, pattern, 'c', "rw");
+        r = bpf_devices_allow_list_major(prog, cgroup_path, pattern, 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
         assert_se(r >= 0);
 
         r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
@@ -197,7 +197,7 @@ static void test_policy_allow_list_major_star(char type, const char *cgroup_path
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
         assert_se(r >= 0);
 
-        r = bpf_devices_allow_list_major(prog, cgroup_path, "*", type, "rw");
+        r = bpf_devices_allow_list_major(prog, cgroup_path, "*", type, CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
         assert_se(r >= 0);
 
         r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
@@ -229,7 +229,7 @@ static void test_policy_empty(bool add_mismatched, const char *cgroup_path, BPFP
         assert_se(r >= 0);
 
         if (add_mismatched) {
-                r = bpf_devices_allow_list_major(prog, cgroup_path, "foobarxxx", 'c', "rw");
+                r = bpf_devices_allow_list_major(prog, cgroup_path, "foobarxxx", 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
                 assert_se(r < 0);
         }
 
index 9d7c29f348fe5a4f43fb0bae47ba5cb41f855303..bfc8fac8772b44cc8db1c312780f8ba7154e0361 100644 (file)
@@ -156,4 +156,29 @@ TEST(cg_mask_to_string) {
         test_cg_mask_to_string_one(CGROUP_MASK_IO|CGROUP_MASK_BLKIO, "io blkio");
 }
 
+static void cgroup_device_permissions_test_normalize(const char *a, const char *b) {
+        assert_se(streq_ptr(cgroup_device_permissions_to_string(cgroup_device_permissions_from_string(a)), b));
+}
+
+TEST(cgroup_device_permissions) {
+        for (CGroupDevicePermissions p = 0; p < _CGROUP_DEVICE_PERMISSIONS_MAX; p++) {
+                const char *s;
+
+                assert_se(s = cgroup_device_permissions_to_string(p));
+                assert_se(cgroup_device_permissions_from_string(s) == p);
+        }
+
+        cgroup_device_permissions_test_normalize("", "");
+        cgroup_device_permissions_test_normalize("rw", "rw");
+        cgroup_device_permissions_test_normalize("wr", "rw");
+        cgroup_device_permissions_test_normalize("wwrr", "rw");
+        cgroup_device_permissions_test_normalize("mmmmmmmmmmmmmm", "m");
+        cgroup_device_permissions_test_normalize("mmmmrrrrmmmwwmwmwmwmwmrmrmr", "rwm");
+
+        assert_se(cgroup_device_permissions_from_string(NULL) == -EINVAL);
+        assert_se(cgroup_device_permissions_from_string("rwq") == -EINVAL);
+        assert_se(cgroup_device_permissions_from_string("RW") == -EINVAL);
+        assert_se(cgroup_device_permissions_from_string("") == 0);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);