mirror of
https://github.com/systemd/systemd.git
synced 2024-11-02 02:21:44 +03:00
cgroups: beef up DeviceAllow= syntax a bit
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).
This commit is contained in:
parent
74c48bf5a8
commit
8e8b5d2e6d
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user