devcgroup_inode_mknod and devcgroup_inode_permission hooks are
called at place where already the corresponding lsm hooks
security_inode_mknod and security_inode_permission are called
to govern device access. Though introduce a small LSM which
implements those two security hooks instead of the additional
explicit devcgroup calls. The explicit API will be removed when
corresponding subsystems will drop the direct call to devcgroup
hooks.
Signed-off-by: Michael Weiß <michael.weiss@aisec.fraunhofer.de>
---
init/Kconfig | 4 +
security/Kconfig | 1 +
security/Makefile | 2 +-
security/device_cgroup/Kconfig | 7 ++
security/device_cgroup/Makefile | 4 +
security/{ => device_cgroup}/device_cgroup.c | 0
security/device_cgroup/lsm.c | 82 ++++++++++++++++++++
7 files changed, 99 insertions(+), 1 deletion(-)
create mode 100644 security/device_cgroup/Kconfig
create mode 100644 security/device_cgroup/Makefile
rename security/{ => device_cgroup}/device_cgroup.c (100%)
create mode 100644 security/device_cgroup/lsm.c
@@ -1111,6 +1111,8 @@ config PROC_PID_CPUSET
config CGROUP_DEVICE
bool "Device controller"
+ select SECURITY
+ select SECURITY_DEVICE_CGROUP
help
Provides a cgroup controller implementing whitelists for
devices which a process in the cgroup can mknod or open.
@@ -1136,6 +1138,8 @@ config CGROUP_BPF
bool "Support for eBPF programs attached to cgroups"
depends on BPF_SYSCALL
select SOCK_CGROUP_DATA
+ select SECURITY
+ select SECURITY_DEVICE_CGROUP
help
Allow attaching eBPF programs to a cgroup using the bpf(2)
syscall command BPF_PROG_ATTACH.
@@ -194,6 +194,7 @@ source "security/yama/Kconfig"
source "security/safesetid/Kconfig"
source "security/lockdown/Kconfig"
source "security/landlock/Kconfig"
+source "security/device_cgroup/Kconfig"
source "security/integrity/Kconfig"
@@ -21,7 +21,7 @@ obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
-obj-$(CONFIG_CGROUPS) += device_cgroup.o
+obj-$(CONFIG_SECURITY_DEVICE_CGROUP) += device_cgroup/
obj-$(CONFIG_BPF_LSM) += bpf/
obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
new file mode 100644
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SECURITY_DEVICE_CGROUP
+ bool "Device Cgroup Support"
+ depends on SECURITY
+ help
+ Provides the necessary security framework integration
+ for cgroup device controller implementations.
new file mode 100644
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SECURITY_DEVICE_CGROUP) += devcgroup.o
+
+devcgroup-y := lsm.o device_cgroup.o
similarity index 100%
rename from security/device_cgroup.c
rename to security/device_cgroup/device_cgroup.c
new file mode 100644
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device cgroup security module
+ *
+ * This file contains device cgroup LSM hooks.
+ *
+ * Copyright (C) 2023 Fraunhofer AISEC. All rights reserved.
+ * Based on code copied from <file:include/linux/device_cgroups.h> (which has no copyright)
+ *
+ * Authors: Michael Weiß <michael.weiss@aisec.fraunhofer.de>
+ */
+
+#include <linux/bpf-cgroup.h>
+#include <linux/device_cgroup.h>
+#include <linux/lsm_hooks.h>
+
+static int devcg_inode_permission(struct inode *inode, int mask)
+{
+ short type, access = 0;
+
+ if (likely(!inode->i_rdev))
+ return 0;
+
+ if (S_ISBLK(inode->i_mode))
+ type = DEVCG_DEV_BLOCK;
+ else if (S_ISCHR(inode->i_mode))
+ type = DEVCG_DEV_CHAR;
+ else
+ return 0;
+
+ if (mask & MAY_WRITE)
+ access |= DEVCG_ACC_WRITE;
+ if (mask & MAY_READ)
+ access |= DEVCG_ACC_READ;
+
+ return devcgroup_check_permission(type, imajor(inode), iminor(inode),
+ access);
+}
+
+static int __devcg_inode_mknod(int mode, dev_t dev, short access)
+{
+ short type;
+
+ if (!S_ISBLK(mode) && !S_ISCHR(mode))
+ return 0;
+
+ if (S_ISCHR(mode) && dev == WHITEOUT_DEV)
+ return 0;
+
+ if (S_ISBLK(mode))
+ type = DEVCG_DEV_BLOCK;
+ else
+ type = DEVCG_DEV_CHAR;
+
+ return devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
+ access);
+}
+
+static int devcg_inode_mknod(struct inode *dir, struct dentry *dentry,
+ umode_t mode, dev_t dev)
+{
+ return __devcg_inode_mknod(mode, dev, DEVCG_ACC_MKNOD);
+}
+
+static struct security_hook_list devcg_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(inode_permission, devcg_inode_permission),
+ LSM_HOOK_INIT(inode_mknod, devcg_inode_mknod),
+};
+
+static int __init devcgroup_init(void)
+{
+ security_add_hooks(devcg_hooks, ARRAY_SIZE(devcg_hooks),
+ "devcgroup");
+ pr_info("device cgroup initialized\n");
+ return 0;
+}
+
+DEFINE_LSM(devcgroup) = {
+ .name = "devcgroup",
+ .order = LSM_ORDER_FIRST,
+ .init = devcgroup_init,
+};