From 5fc80fb5b776fa22b01472baecec4d26d1af811b Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 6 Feb 2025 22:30:20 -0500 Subject: selinux: always check the file label in selinux_kernel_read_file() Commit 2039bda1fa8d ("LSM: Add "contents" flag to kernel_read_file hook") added a new flag to the security_kernel_read_file() LSM hook, "contents", which was set if a file was being read in its entirety or if it was the first chunk read in a multi-step process. The SELinux LSM callback was updated to only check against the file label if this "contents" flag was set, meaning that in multi-step reads the file label was not considered in the access control decision after the initial chunk. Thankfully the only in-tree user that performs a multi-step read is the "bcm-vk" driver and it is loading firmware, not a kernel module, so there are no security regressions to worry about. However, we still want to ensure that the SELinux code does the right thing, and *always* checks the file label, especially as there is a chance the file could change between chunk reads. Fixes: 2039bda1fa8d ("LSM: Add "contents" flag to kernel_read_file hook") Signed-off-by: Paul Moore --- security/selinux/hooks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7b867dfec88b..a80e3f01153f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4134,7 +4134,7 @@ static int selinux_kernel_read_file(struct file *file, switch (id) { case READING_MODULE: - rc = selinux_kernel_module_from_file(contents ? file : NULL); + rc = selinux_kernel_module_from_file(file); break; default: break; -- cgit v1.2.3 From 2c2b1e059792f610bae4fee8ed517b8ce9c585fb Mon Sep 17 00:00:00 2001 From: "\"Kipp N. Davis\"" Date: Tue, 11 Feb 2025 13:21:59 -0500 Subject: selinux: add permission checks for loading other kinds of kernel files Although the LSM hooks for loading kernel modules were later generalized to cover loading other kinds of files, SELinux didn't implement corresponding permission checks, leaving only the module case covered. Define and add new permission checks for these other cases. Signed-off-by: Cameron K. Williams Signed-off-by: Kipp N. Davis Acked-by: Stephen Smalley [PM: merge fuzz, line length, and spacing fixes] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 58 ++++++++++++++++++++++++++++++------- security/selinux/include/classmap.h | 4 ++- 2 files changed, 51 insertions(+), 11 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a80e3f01153f..0fc70dcf1409 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4096,7 +4096,7 @@ static int selinux_kernel_module_request(char *kmod_name) SYSTEM__MODULE_REQUEST, &ad); } -static int selinux_kernel_module_from_file(struct file *file) +static int selinux_kernel_load_from_file(struct file *file, u32 requested) { struct common_audit_data ad; struct inode_security_struct *isec; @@ -4104,12 +4104,8 @@ static int selinux_kernel_module_from_file(struct file *file) u32 sid = current_sid(); int rc; - /* init_module */ if (file == NULL) - return avc_has_perm(sid, sid, SECCLASS_SYSTEM, - SYSTEM__MODULE_LOAD, NULL); - - /* finit_module */ + return avc_has_perm(sid, sid, SECCLASS_SYSTEM, requested, NULL); ad.type = LSM_AUDIT_DATA_FILE; ad.u.file = file; @@ -4122,8 +4118,7 @@ static int selinux_kernel_module_from_file(struct file *file) } isec = inode_security(file_inode(file)); - return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, - SYSTEM__MODULE_LOAD, &ad); + return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, requested, &ad); } static int selinux_kernel_read_file(struct file *file, @@ -4132,9 +4127,30 @@ static int selinux_kernel_read_file(struct file *file, { int rc = 0; + BUILD_BUG_ON_MSG(READING_MAX_ID > 7, + "New kernel_read_file_id introduced; update SELinux!"); + switch (id) { + case READING_FIRMWARE: + rc = selinux_kernel_load_from_file(file, SYSTEM__FIRMWARE_LOAD); + break; case READING_MODULE: - rc = selinux_kernel_module_from_file(file); + rc = selinux_kernel_load_from_file(file, SYSTEM__MODULE_LOAD); + break; + case READING_KEXEC_IMAGE: + rc = selinux_kernel_load_from_file(file, + SYSTEM__KEXEC_IMAGE_LOAD); + break; + case READING_KEXEC_INITRAMFS: + rc = selinux_kernel_load_from_file(file, + SYSTEM__KEXEC_INITRAMFS_LOAD); + break; + case READING_POLICY: + rc = selinux_kernel_load_from_file(file, SYSTEM__POLICY_LOAD); + break; + case READING_X509_CERTIFICATE: + rc = selinux_kernel_load_from_file(file, + SYSTEM__X509_CERTIFICATE_LOAD); break; default: break; @@ -4147,9 +4163,31 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents) { int rc = 0; + BUILD_BUG_ON_MSG(LOADING_MAX_ID > 7, + "New kernel_load_data_id introduced; update SELinux!"); + switch (id) { + case LOADING_FIRMWARE: + rc = selinux_kernel_load_from_file(NULL, SYSTEM__FIRMWARE_LOAD); + break; case LOADING_MODULE: - rc = selinux_kernel_module_from_file(NULL); + rc = selinux_kernel_load_from_file(NULL, SYSTEM__MODULE_LOAD); + break; + case LOADING_KEXEC_IMAGE: + rc = selinux_kernel_load_from_file(NULL, + SYSTEM__KEXEC_IMAGE_LOAD); + break; + case LOADING_KEXEC_INITRAMFS: + rc = selinux_kernel_load_from_file(NULL, + SYSTEM__KEXEC_INITRAMFS_LOAD); + break; + case LOADING_POLICY: + rc = selinux_kernel_load_from_file(NULL, + SYSTEM__POLICY_LOAD); + break; + case LOADING_X509_CERTIFICATE: + rc = selinux_kernel_load_from_file(NULL, + SYSTEM__X509_CERTIFICATE_LOAD); break; default: break; diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 03e82477dce9..cfac41d12f7d 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -63,7 +63,9 @@ const struct security_class_mapping secclass_map[] = { { "process2", { "nnp_transition", "nosuid_transition", NULL } }, { "system", { "ipc_info", "syslog_read", "syslog_mod", "syslog_console", - "module_request", "module_load", NULL } }, + "module_request", "module_load", "firmware_load", + "kexec_image_load", "kexec_initramfs_load", "policy_load", + "x509_certificate_load", NULL } }, { "capability", { COMMON_CAP_PERMS, NULL } }, { "filesystem", { "mount", "remount", "unmount", "getattr", "relabelfrom", -- cgit v1.2.3