]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cgroups: beef up DeviceAllow= syntax a bit
authorLennart Poettering <lennart@poettering.net>
Fri, 29 Jun 2018 10:09:29 +0000 (12:09 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 29 Nov 2018 19:21:39 +0000 (20:21 +0100)
Previously we'd allow pattern expressions such as "char-input" to match
all input devices. Internally, this would look up the right major to
test in /proc/devices. With this commit the syntax is slightly extended:

- "char-*" can be used to match any kind of character device, and
  similar "block-*. This expression would work previously already, but
  instead of actually installing a wildcard match it would install many
  individual matches for everything listed in /proc/devices.

- "char-<MAJOR>" with "<MAJOR>" being a numerical parameter works now
  too. This allows clients to install whitelist items by specifying the
  major directly.

The main reason to add these is to provide limited compat support for
clients that for some reason contain whitelists with major/minor numbers
(such as OCI containers).

src/core/bpf-devices.c
src/core/bpf-devices.h
src/core/cgroup.c

index d8915244a7a28aab8a6b453133febe60731cf7ab..dade7f04906a64a7fc898d5d2314934e8f308212 100644 (file)
@@ -84,6 +84,32 @@ int cgroup_bpf_whitelist_major(BPFProgram *prog, int type, int major, const char
         return r;
 }
 
+int cgroup_bpf_whitelist_class(BPFProgram *prog, int type, const char *acc) {
+        struct bpf_insn insn[] = {
+                BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 5), /* compare device type */
+                BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
+                BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
+                BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 1), /* compare access type */
+                BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
+        };
+        int r, access;
+
+        assert(prog);
+        assert(acc);
+
+        access = bpf_access_type(acc);
+        if (access <= 0)
+                return -EINVAL;
+
+        insn[2].imm = access;
+
+        r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
+        if (r < 0)
+                log_error_errno(r, "Extending device control BPF program failed: %m");
+
+        return r;
+}
+
 int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist) {
         struct bpf_insn pre_insn[] = {
                 /* load device type to r2 */
index f9a6eec028dd482ec66e463a4bd3a359320d4c5b..8d3de3bd9407fdd0c87655a0000f84bd5221c62f 100644 (file)
@@ -11,6 +11,7 @@ int bpf_devices_supported(void);
 
 int cgroup_bpf_whitelist_device(BPFProgram *p, int type, int major, int minor, const char *acc);
 int cgroup_bpf_whitelist_major(BPFProgram *p, int type, int major, const char *acc);
+int cgroup_bpf_whitelist_class(BPFProgram *prog, int type, const char *acc);
 
 int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist);
 int cgroup_apply_device_bpf(Unit *u, BPFProgram *p, CGroupDevicePolicy policy, bool whitelist);
index 8f3e646ad6e49454672de58522787b9b1236c23a..dd9b992ef1a3f8d87e6b4b3f2ed7c5bf92c9ffb1 100644 (file)
@@ -509,21 +509,64 @@ static int whitelist_device(BPFProgram *prog, const char *path, const char *node
 
 static int whitelist_major(BPFProgram *prog, const char *path, const char *name, char type, const char *acc) {
         _cleanup_fclose_ FILE *f = NULL;
-        char *p, *w;
+        char buf[2+DECIMAL_STR_MAX(unsigned)+3+4];
         bool good = false;
+        unsigned maj;
         int r;
 
         assert(path);
         assert(acc);
         assert(IN_SET(type, 'b', 'c'));
 
+        if (streq(name, "*")) {
+                /* If the name is a wildcard, then apply this list to all devices of this type */
+
+                if (cg_all_unified() > 0) {
+                        if (!prog)
+                                return 0;
+
+                        (void) cgroup_bpf_whitelist_class(prog, type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK, acc);
+                } else {
+                        xsprintf(buf, "%c *:* %s", type, acc);
+
+                        r = cg_set_attribute("devices", path, "devices.allow", buf);
+                        if (r < 0)
+                                log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+                                               "Failed to set devices.allow on %s: %m", path);
+                        return 0;
+                }
+        }
+
+        if (safe_atou(name, &maj) >= 0 && DEVICE_MAJOR_VALID(maj)) {
+                /* The name is numeric and suitable as major. In that case, let's take is major, and create the entry
+                 * directly */
+
+                if (cg_all_unified() > 0) {
+                        if (!prog)
+                                return 0;
+
+                        (void) cgroup_bpf_whitelist_major(prog,
+                                                          type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK,
+                                                          maj, acc);
+                } else {
+                        xsprintf(buf, "%c %u:* %s", type, maj, acc);
+
+                        r = cg_set_attribute("devices", path, "devices.allow", buf);
+                        if (r < 0)
+                                log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+                                               "Failed to set devices.allow on %s: %m", path);
+                }
+
+                return 0;
+        }
+
         f = fopen("/proc/devices", "re");
         if (!f)
                 return log_warning_errno(errno, "Cannot open /proc/devices to resolve %s (%c): %m", name, type);
 
         for (;;) {
                 _cleanup_free_ char *line = NULL;
-                unsigned maj;
+                char *w, *p;
 
                 r = read_line(f, LONG_LINE_MAX, &line);
                 if (r < 0)
@@ -576,8 +619,6 @@ static int whitelist_major(BPFProgram *prog, const char *path, const char *name,
                                                           type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK,
                                                           maj, acc);
                 } else {
-                        char buf[2+DECIMAL_STR_MAX(unsigned)+3+4];
-
                         sprintf(buf,
                                 "%c %u:* %s",
                                 type,
@@ -1179,7 +1220,7 @@ static void cgroup_context_apply(
                         else if ((val = startswith(a->path, "char-")))
                                 (void) whitelist_major(prog, path, val, 'c', acc);
                         else
-                                log_unit_debug(u, "Ignoring device %s while writing cgroup attribute.", a->path);
+                                log_unit_debug(u, "Ignoring device '%s' while writing cgroup attribute.", a->path);
                 }
 
                 r = cgroup_apply_device_bpf(u, prog, c->device_policy, c->device_allow);