2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2019-11-08 15:51:28 +01:00
|
|
|
|
|
|
|
#include <fnmatch.h>
|
2019-04-10 12:55:53 +02:00
|
|
|
#include <linux/bpf_insn.h>
|
2018-10-08 23:33:05 +02:00
|
|
|
|
|
|
|
#include "bpf-devices.h"
|
|
|
|
#include "bpf-program.h"
|
2019-11-08 15:51:28 +01:00
|
|
|
#include "fd-util.h"
|
|
|
|
#include "fileio.h"
|
2019-11-08 16:09:28 +01:00
|
|
|
#include "nulstr-util.h"
|
2019-11-08 15:51:28 +01:00
|
|
|
#include "parse-util.h"
|
|
|
|
#include "stat-util.h"
|
|
|
|
#include "stdio-util.h"
|
|
|
|
#include "string-util.h"
|
2018-10-08 23:33:05 +02:00
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
static int bpf_prog_allow_list_device(
|
2020-01-07 18:21:26 +01:00
|
|
|
BPFProgram *prog,
|
|
|
|
char type,
|
|
|
|
int major,
|
|
|
|
int minor,
|
|
|
|
const char *acc) {
|
|
|
|
|
2018-10-08 23:33:05 +02:00
|
|
|
int r, access;
|
|
|
|
|
|
|
|
assert(prog);
|
|
|
|
assert(acc);
|
|
|
|
|
2019-11-09 12:34:30 +01:00
|
|
|
log_trace("%s: %c %d:%d %s", __func__, type, major, minor, acc);
|
|
|
|
|
2018-10-08 23:33:05 +02:00
|
|
|
access = bpf_access_type(acc);
|
|
|
|
if (access <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-11-09 12:32:44 +01:00
|
|
|
assert(IN_SET(type, 'b', 'c'));
|
|
|
|
const int bpf_type = type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK;
|
|
|
|
|
2019-11-08 08:55:54 +01:00
|
|
|
const struct bpf_insn insn[] = {
|
2019-11-10 13:19:51 +01:00
|
|
|
BPF_MOV32_REG(BPF_REG_1, BPF_REG_3),
|
2019-11-08 08:55:54 +01:00
|
|
|
BPF_ALU32_IMM(BPF_AND, BPF_REG_1, access),
|
2019-11-10 13:19:51 +01:00
|
|
|
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 */
|
|
|
|
BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 2), /* compare major */
|
|
|
|
BPF_JMP_IMM(BPF_JNE, BPF_REG_5, minor, 1), /* compare minor */
|
|
|
|
BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
|
2019-11-08 08:55:54 +01:00
|
|
|
};
|
2018-10-08 23:33:05 +02:00
|
|
|
|
2019-11-10 13:19:51 +01:00
|
|
|
if (FLAGS_SET(access, BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD))
|
|
|
|
r = bpf_program_add_instructions(prog, insn + 3, ELEMENTSOF(insn) - 3);
|
|
|
|
else
|
|
|
|
r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
|
2018-10-08 23:33:05 +02:00
|
|
|
if (r < 0)
|
|
|
|
log_error_errno(r, "Extending device control BPF program failed: %m");
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
static int bpf_prog_allow_list_major(
|
2020-01-07 18:21:26 +01:00
|
|
|
BPFProgram *prog,
|
|
|
|
char type,
|
|
|
|
int major,
|
|
|
|
const char *acc) {
|
|
|
|
|
2018-10-08 23:33:05 +02:00
|
|
|
int r, access;
|
|
|
|
|
|
|
|
assert(prog);
|
|
|
|
assert(acc);
|
|
|
|
|
2019-11-09 12:34:30 +01:00
|
|
|
log_trace("%s: %c %d:* %s", __func__, type, major, acc);
|
|
|
|
|
2018-10-08 23:33:05 +02:00
|
|
|
access = bpf_access_type(acc);
|
|
|
|
if (access <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-11-09 12:32:44 +01:00
|
|
|
assert(IN_SET(type, 'b', 'c'));
|
|
|
|
const int bpf_type = type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK;
|
|
|
|
|
2019-11-08 08:55:54 +01:00
|
|
|
const struct bpf_insn insn[] = {
|
2019-11-10 13:19:51 +01:00
|
|
|
BPF_MOV32_REG(BPF_REG_1, BPF_REG_3),
|
2019-11-08 08:55:54 +01:00
|
|
|
BPF_ALU32_IMM(BPF_AND, BPF_REG_1, access),
|
2019-11-10 13:19:51 +01:00
|
|
|
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 */
|
|
|
|
BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 1), /* compare major */
|
|
|
|
BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
|
2019-11-08 08:55:54 +01:00
|
|
|
};
|
2018-10-08 23:33:05 +02:00
|
|
|
|
2019-11-10 13:19:51 +01:00
|
|
|
if (FLAGS_SET(access, BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD))
|
|
|
|
r = bpf_program_add_instructions(prog, insn + 3, ELEMENTSOF(insn) - 3);
|
|
|
|
else
|
|
|
|
r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
|
2018-10-08 23:33:05 +02:00
|
|
|
if (r < 0)
|
|
|
|
log_error_errno(r, "Extending device control BPF program failed: %m");
|
2018-06-29 12:09:29 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
static int bpf_prog_allow_list_class(
|
2020-01-07 18:21:26 +01:00
|
|
|
BPFProgram *prog,
|
|
|
|
char type,
|
|
|
|
const char *acc) {
|
|
|
|
|
2018-06-29 12:09:29 +02:00
|
|
|
int r, access;
|
|
|
|
|
|
|
|
assert(prog);
|
|
|
|
assert(acc);
|
|
|
|
|
2019-11-09 12:34:30 +01:00
|
|
|
log_trace("%s: %c *:* %s", __func__, type, acc);
|
|
|
|
|
2018-06-29 12:09:29 +02:00
|
|
|
access = bpf_access_type(acc);
|
|
|
|
if (access <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-11-09 12:32:44 +01:00
|
|
|
assert(IN_SET(type, 'b', 'c'));
|
|
|
|
const int bpf_type = type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK;
|
|
|
|
|
2019-11-08 08:55:54 +01:00
|
|
|
const struct bpf_insn insn[] = {
|
2019-11-10 13:19:51 +01:00
|
|
|
BPF_MOV32_REG(BPF_REG_1, BPF_REG_3),
|
2019-11-08 08:55:54 +01:00
|
|
|
BPF_ALU32_IMM(BPF_AND, BPF_REG_1, access),
|
2019-11-10 13:19:51 +01:00
|
|
|
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 */
|
2019-11-08 08:55:54 +01:00
|
|
|
};
|
2018-06-29 12:09:29 +02:00
|
|
|
|
2019-11-10 13:19:51 +01:00
|
|
|
if (FLAGS_SET(access, BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD))
|
|
|
|
r = bpf_program_add_instructions(prog, insn + 3, ELEMENTSOF(insn) - 3);
|
|
|
|
else
|
|
|
|
r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
|
2018-06-29 12:09:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
log_error_errno(r, "Extending device control BPF program failed: %m");
|
2018-10-08 23:33:05 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2020-01-07 18:21:26 +01:00
|
|
|
int bpf_devices_cgroup_init(
|
|
|
|
BPFProgram **ret,
|
|
|
|
CGroupDevicePolicy policy,
|
2020-06-23 08:31:16 +02:00
|
|
|
bool allow_list) {
|
2020-01-07 18:21:26 +01:00
|
|
|
|
2019-11-08 08:55:54 +01:00
|
|
|
const struct bpf_insn pre_insn[] = {
|
2018-10-08 23:33:05 +02:00
|
|
|
/* load device type to r2 */
|
2019-11-10 13:13:31 +01:00
|
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
|
2018-10-08 23:33:05 +02:00
|
|
|
offsetof(struct bpf_cgroup_dev_ctx, access_type)),
|
2019-11-10 13:13:31 +01:00
|
|
|
BPF_ALU32_IMM(BPF_AND, BPF_REG_2, 0xFFFF),
|
2018-10-08 23:33:05 +02:00
|
|
|
|
|
|
|
/* load access type to r3 */
|
|
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
|
|
|
|
offsetof(struct bpf_cgroup_dev_ctx, access_type)),
|
|
|
|
BPF_ALU32_IMM(BPF_RSH, BPF_REG_3, 16),
|
|
|
|
|
|
|
|
/* load major number to r4 */
|
|
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1,
|
|
|
|
offsetof(struct bpf_cgroup_dev_ctx, major)),
|
|
|
|
|
|
|
|
/* load minor number to r5 */
|
|
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_5, BPF_REG_1,
|
|
|
|
offsetof(struct bpf_cgroup_dev_ctx, minor)),
|
|
|
|
};
|
|
|
|
|
|
|
|
_cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
if (policy == CGROUP_DEVICE_POLICY_AUTO && !allow_list)
|
2018-10-08 23:33:05 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Loading device control BPF program failed: %m");
|
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
if (policy == CGROUP_DEVICE_POLICY_CLOSED || allow_list) {
|
2018-10-08 23:33:05 +02:00
|
|
|
r = bpf_program_add_instructions(prog, pre_insn, ELEMENTSOF(pre_insn));
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Extending device control BPF program failed: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret = TAKE_PTR(prog);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-08 20:33:10 +01:00
|
|
|
int bpf_devices_apply_policy(
|
|
|
|
BPFProgram *prog,
|
|
|
|
CGroupDevicePolicy policy,
|
2020-06-23 08:31:16 +02:00
|
|
|
bool allow_list,
|
2019-11-08 20:33:10 +01:00
|
|
|
const char *cgroup_path,
|
|
|
|
BPFProgram **prog_installed) {
|
2019-11-09 11:50:25 +01:00
|
|
|
|
|
|
|
_cleanup_free_ char *controller_path = NULL;
|
2018-10-08 23:33:05 +02:00
|
|
|
int r;
|
|
|
|
|
2019-11-08 20:33:10 +01:00
|
|
|
/* This will assign *keep_program if everything goes well. */
|
2019-11-09 11:50:25 +01:00
|
|
|
|
|
|
|
if (!prog)
|
|
|
|
goto finish;
|
2018-10-08 23:33:05 +02:00
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
const bool deny_everything = policy == CGROUP_DEVICE_POLICY_STRICT && !allow_list;
|
2018-10-08 23:33:05 +02:00
|
|
|
|
2019-11-08 08:55:54 +01:00
|
|
|
const struct bpf_insn post_insn[] = {
|
|
|
|
/* return DENY */
|
|
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
|
|
BPF_JMP_A(1),
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct bpf_insn exit_insn[] = {
|
|
|
|
/* finally return DENY if deny_everything else ALLOW */
|
|
|
|
BPF_MOV64_IMM(BPF_REG_0, deny_everything ? 0 : 1),
|
|
|
|
BPF_EXIT_INSN()
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!deny_everything) {
|
2018-10-08 23:33:05 +02:00
|
|
|
r = bpf_program_add_instructions(prog, post_insn, ELEMENTSOF(post_insn));
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Extending device control BPF program failed: %m");
|
|
|
|
|
|
|
|
/* Fixup PASS_JUMP_OFF jump offsets. */
|
2019-11-08 08:55:54 +01:00
|
|
|
for (size_t off = 0; off < prog->n_instructions; off++) {
|
2018-10-08 23:33:05 +02:00
|
|
|
struct bpf_insn *ins = &prog->instructions[off];
|
|
|
|
|
|
|
|
if (ins->code == (BPF_JMP | BPF_JA) && ins->off == PASS_JUMP_OFF)
|
|
|
|
ins->off = prog->n_instructions - off - 1;
|
|
|
|
}
|
2019-11-08 08:55:54 +01:00
|
|
|
}
|
2018-10-08 23:33:05 +02:00
|
|
|
|
|
|
|
r = bpf_program_add_instructions(prog, exit_insn, ELEMENTSOF(exit_insn));
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Extending device control BPF program failed: %m");
|
|
|
|
|
2019-11-08 20:33:10 +01:00
|
|
|
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, NULL, &controller_path);
|
2018-10-08 23:33:05 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to determine cgroup path: %m");
|
|
|
|
|
2019-11-08 20:33:10 +01:00
|
|
|
r = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE, controller_path, BPF_F_ALLOW_MULTI);
|
2018-10-08 23:33:05 +02:00
|
|
|
if (r < 0)
|
2019-11-08 20:33:10 +01:00
|
|
|
return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m",
|
|
|
|
cgroup_path);
|
2018-10-08 23:33:05 +02:00
|
|
|
|
2019-11-09 11:50:25 +01:00
|
|
|
finish:
|
2018-11-12 10:53:47 +01:00
|
|
|
/* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
|
2019-11-09 11:50:25 +01:00
|
|
|
if (prog_installed) {
|
|
|
|
bpf_program_unref(*prog_installed);
|
|
|
|
*prog_installed = bpf_program_ref(prog);
|
|
|
|
}
|
2018-10-08 23:33:05 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bpf_devices_supported(void) {
|
2019-11-08 08:55:54 +01:00
|
|
|
const struct bpf_insn trivial[] = {
|
2018-10-08 23:33:05 +02:00
|
|
|
BPF_MOV64_IMM(BPF_REG_0, 1),
|
|
|
|
BPF_EXIT_INSN()
|
|
|
|
};
|
|
|
|
|
|
|
|
_cleanup_(bpf_program_unrefp) BPFProgram *program = NULL;
|
|
|
|
static int supported = -1;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
/* Checks whether BPF device controller is supported. For this, we check five things:
|
|
|
|
*
|
|
|
|
* a) whether we are privileged
|
|
|
|
* b) whether the unified hierarchy is being used
|
|
|
|
* c) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_DEVICE programs, which we require
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (supported >= 0)
|
|
|
|
return supported;
|
|
|
|
|
|
|
|
if (geteuid() != 0) {
|
|
|
|
log_debug("Not enough privileges, BPF device control is not supported.");
|
|
|
|
return supported = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
|
|
|
|
if (r == 0) {
|
|
|
|
log_debug("Not running with unified cgroups, BPF device control is not supported.");
|
|
|
|
return supported = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &program);
|
|
|
|
if (r < 0) {
|
|
|
|
log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
|
|
|
|
return supported = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = bpf_program_add_instructions(program, trivial, ELEMENTSOF(trivial));
|
|
|
|
if (r < 0) {
|
|
|
|
log_debug_errno(r, "Can't add trivial instructions to CGROUP DEVICE BPF program, BPF device control is not supported: %m");
|
|
|
|
return supported = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = bpf_program_load_kernel(program, NULL, 0);
|
|
|
|
if (r < 0) {
|
|
|
|
log_debug_errno(r, "Can't load kernel CGROUP DEVICE BPF program, BPF device control is not supported: %m");
|
|
|
|
return supported = 0;
|
|
|
|
}
|
|
|
|
|
2018-11-12 10:52:05 +01:00
|
|
|
return supported = 1;
|
2018-10-08 23:33:05 +02:00
|
|
|
}
|
2019-11-08 15:51:28 +01:00
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
static int allow_list_device_pattern(
|
2020-01-07 18:21:26 +01:00
|
|
|
BPFProgram *prog,
|
|
|
|
const char *path,
|
|
|
|
char type,
|
|
|
|
const unsigned *maj,
|
|
|
|
const unsigned *min,
|
|
|
|
const char *acc) {
|
|
|
|
|
2019-11-08 15:51:28 +01:00
|
|
|
assert(IN_SET(type, 'b', 'c'));
|
|
|
|
|
|
|
|
if (cg_all_unified() > 0) {
|
|
|
|
if (!prog)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (maj && min)
|
2020-06-23 08:31:16 +02:00
|
|
|
return bpf_prog_allow_list_device(prog, type, *maj, *min, acc);
|
2019-11-08 15:51:28 +01:00
|
|
|
else if (maj)
|
2020-06-23 08:31:16 +02:00
|
|
|
return bpf_prog_allow_list_major(prog, type, *maj, acc);
|
2019-11-08 15:51:28 +01:00
|
|
|
else
|
2020-06-23 08:31:16 +02:00
|
|
|
return bpf_prog_allow_list_class(prog, type, acc);
|
2019-11-08 15:51:28 +01:00
|
|
|
|
|
|
|
} 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);
|
|
|
|
else if (maj)
|
|
|
|
xsprintf(buf, "%c %u:* %s", type, *maj, acc);
|
|
|
|
else
|
|
|
|
xsprintf(buf, "%c *:* %s", type, acc);
|
|
|
|
|
|
|
|
/* Changing the devices list of a populated cgroup might result in EINVAL, hence ignore
|
|
|
|
* EINVAL here. */
|
|
|
|
|
|
|
|
r = cg_set_attribute("devices", path, "devices.allow", buf);
|
|
|
|
if (r < 0)
|
|
|
|
log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING,
|
|
|
|
r, "Failed to set devices.allow on %s: %m", path);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
int bpf_devices_allow_list_device(
|
2020-01-07 18:21:26 +01:00
|
|
|
BPFProgram *prog,
|
|
|
|
const char *path,
|
|
|
|
const char *node,
|
|
|
|
const char *acc) {
|
|
|
|
|
2019-11-08 15:51:28 +01:00
|
|
|
mode_t mode;
|
|
|
|
dev_t rdev;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(path);
|
|
|
|
assert(acc);
|
|
|
|
assert(strlen(acc) <= 3);
|
|
|
|
|
2019-11-09 12:34:30 +01:00
|
|
|
log_trace("%s: %s %s", __func__, node, acc);
|
|
|
|
|
2019-11-08 15:51:28 +01:00
|
|
|
/* 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
|
|
|
|
* means clients can use these path without the device node actually around */
|
|
|
|
r = device_path_parse_major_minor(node, &mode, &rdev);
|
|
|
|
if (r < 0) {
|
|
|
|
if (r != -ENODEV)
|
|
|
|
return log_warning_errno(r, "Couldn't parse major/minor from device path '%s': %m", node);
|
|
|
|
|
|
|
|
struct stat st;
|
|
|
|
if (stat(node, &st) < 0)
|
|
|
|
return log_warning_errno(errno, "Couldn't stat device %s: %m", node);
|
|
|
|
|
|
|
|
if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode))
|
|
|
|
return log_warning_errno(SYNTHETIC_ERRNO(ENODEV), "%s is not a device.", node);
|
|
|
|
|
|
|
|
mode = st.st_mode;
|
|
|
|
rdev = (dev_t) st.st_rdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned maj = major(rdev), min = minor(rdev);
|
2020-06-23 08:31:16 +02:00
|
|
|
return allow_list_device_pattern(prog, path, S_ISCHR(mode) ? 'c' : 'b', &maj, &min, acc);
|
2019-11-08 15:51:28 +01:00
|
|
|
}
|
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
int bpf_devices_allow_list_major(
|
2020-01-07 18:21:26 +01:00
|
|
|
BPFProgram *prog,
|
|
|
|
const char *path,
|
|
|
|
const char *name,
|
|
|
|
char type,
|
|
|
|
const char *acc) {
|
|
|
|
|
2019-11-08 15:51:28 +01:00
|
|
|
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 */
|
2020-06-23 08:31:16 +02:00
|
|
|
return allow_list_device_pattern(prog, path, type, NULL, NULL, acc);
|
2019-11-08 15:51:28 +01:00
|
|
|
|
|
|
|
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. */
|
2020-06-23 08:31:16 +02:00
|
|
|
return allow_list_device_pattern(prog, path, type, &maj, NULL, acc);
|
2019-11-08 15:51:28 +01:00
|
|
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
2019-11-10 23:08:21 +01:00
|
|
|
bool good = false, any = false;
|
2019-11-08 15:51:28 +01:00
|
|
|
|
|
|
|
f = fopen("/proc/devices", "re");
|
|
|
|
if (!f)
|
|
|
|
return log_warning_errno(errno, "Cannot open /proc/devices to resolve %s: %m", name);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
_cleanup_free_ char *line = NULL;
|
|
|
|
char *w, *p;
|
|
|
|
|
|
|
|
r = read_line(f, LONG_LINE_MAX, &line);
|
|
|
|
if (r < 0)
|
|
|
|
return log_warning_errno(r, "Failed to read /proc/devices: %m");
|
|
|
|
if (r == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (type == 'c' && streq(line, "Character devices:")) {
|
|
|
|
good = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == 'b' && streq(line, "Block devices:")) {
|
|
|
|
good = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isempty(line)) {
|
|
|
|
good = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!good)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
p = strstrip(line);
|
|
|
|
|
|
|
|
w = strpbrk(p, WHITESPACE);
|
|
|
|
if (!w)
|
|
|
|
continue;
|
|
|
|
*w = 0;
|
|
|
|
|
|
|
|
r = safe_atou(p, &maj);
|
|
|
|
if (r < 0)
|
|
|
|
continue;
|
|
|
|
if (maj <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
w++;
|
|
|
|
w += strspn(w, WHITESPACE);
|
|
|
|
|
|
|
|
if (fnmatch(name, w, 0) != 0)
|
|
|
|
continue;
|
|
|
|
|
2019-11-10 23:08:21 +01:00
|
|
|
any = true;
|
2020-06-23 08:31:16 +02:00
|
|
|
(void) allow_list_device_pattern(prog, path, type, &maj, NULL, acc);
|
2019-11-08 15:51:28 +01:00
|
|
|
}
|
|
|
|
|
2019-11-10 23:08:21 +01:00
|
|
|
if (!any)
|
|
|
|
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
|
2020-06-23 08:31:16 +02:00
|
|
|
"Device allow list pattern \"%s\" did not match anything.", name);
|
2019-11-10 23:08:21 +01:00
|
|
|
|
2019-11-08 15:51:28 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2019-11-08 16:09:28 +01:00
|
|
|
|
2020-06-23 08:31:16 +02:00
|
|
|
int bpf_devices_allow_list_static(
|
2020-01-07 18:21:26 +01:00
|
|
|
BPFProgram *prog,
|
|
|
|
const char *path) {
|
|
|
|
|
2019-11-08 16:09:28 +01:00
|
|
|
static const char auto_devices[] =
|
|
|
|
"/dev/null\0" "rwm\0"
|
|
|
|
"/dev/zero\0" "rwm\0"
|
|
|
|
"/dev/full\0" "rwm\0"
|
|
|
|
"/dev/random\0" "rwm\0"
|
|
|
|
"/dev/urandom\0" "rwm\0"
|
|
|
|
"/dev/tty\0" "rwm\0"
|
|
|
|
"/dev/ptmx\0" "rwm\0"
|
|
|
|
/* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */
|
|
|
|
"/run/systemd/inaccessible/chr\0" "rwm\0"
|
|
|
|
"/run/systemd/inaccessible/blk\0" "rwm\0";
|
|
|
|
int r = 0, k;
|
|
|
|
|
|
|
|
const char *node, *acc;
|
|
|
|
NULSTR_FOREACH_PAIR(node, acc, auto_devices) {
|
2020-06-23 08:31:16 +02:00
|
|
|
k = bpf_devices_allow_list_device(prog, path, node, acc);
|
2019-11-08 16:09:28 +01:00
|
|
|
if (r >= 0 && k < 0)
|
|
|
|
r = k;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PTS (/dev/pts) devices may not be duplicated, but accessed */
|
2020-06-23 08:31:16 +02:00
|
|
|
k = bpf_devices_allow_list_major(prog, path, "pts", 'c', "rw");
|
2019-11-08 16:09:28 +01:00
|
|
|
if (r >= 0 && k < 0)
|
|
|
|
r = k;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|