mirror of
https://github.com/systemd/systemd.git
synced 2025-03-10 16:58:28 +03:00
cgroup: turn device cgroup controller "rwm" strings into proper flags
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.
This commit is contained in:
parent
8e3fb774ae
commit
a104481144
@ -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();
|
||||
}
|
||||
|
||||
|
@ -16,45 +16,25 @@
|
||||
|
||||
#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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
const char *val;
|
||||
|
||||
if (a->r)
|
||||
acc[k++] = 'r';
|
||||
if (a->w)
|
||||
acc[k++] = 'w';
|
||||
if (a->m)
|
||||
acc[k++] = 'm';
|
||||
if (k == 0)
|
||||
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;
|
||||
|
@ -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_;
|
||||
|
@ -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)
|
||||
|
@ -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="))) {
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user