Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lsm: new security_file_ioctl_compat() hook

Some ioctl commands do not require ioctl permission, but are routed to
other permissions such as FILE_GETATTR or FILE_SETATTR. This routing is
done by comparing the ioctl cmd to a set of 64-bit flags (FS_IOC_*).

However, if a 32-bit process is running on a 64-bit kernel, it emits
32-bit flags (FS_IOC32_*) for certain ioctl operations. These flags are
being checked erroneously, which leads to these ioctl operations being
routed to the ioctl permission, rather than the correct file
permissions.

This was also noted in a RED-PEN finding from a while back -
"/* RED-PEN how should LSM module know it's handling 32bit? */".

This patch introduces a new hook, security_file_ioctl_compat(), that is
called from the compat ioctl syscall. All current LSMs have been changed
to support this hook.

Reviewing the three places where we are currently using
security_file_ioctl(), it appears that only SELinux needs a dedicated
compat change; TOMOYO and SMACK appear to be functional without any
change.

Cc: stable@vger.kernel.org
Fixes: 0b24dcb7f2f7 ("Revert "selinux: simplify ioctl checking"")
Signed-off-by: Alfred Piccioni <alpic@google.com>
Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: subject tweak, line length fixes, and alignment corrections]
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Alfred Piccioni and committed by
Paul Moore
f1bb47a3 ea67677d

+60 -2
+1 -2
fs/ioctl.c
··· 920 920 if (!f.file) 921 921 return -EBADF; 922 922 923 - /* RED-PEN how should LSM module know it's handling 32bit? */ 924 - error = security_file_ioctl(f.file, cmd, arg); 923 + error = security_file_ioctl_compat(f.file, cmd, arg); 925 924 if (error) 926 925 goto out; 927 926
+2
include/linux/lsm_hook_defs.h
··· 171 171 LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file) 172 172 LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, 173 173 unsigned long arg) 174 + LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, 175 + unsigned long arg) 174 176 LSM_HOOK(int, 0, mmap_addr, unsigned long addr) 175 177 LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot, 176 178 unsigned long prot, unsigned long flags)
+9
include/linux/security.h
··· 394 394 int security_file_alloc(struct file *file); 395 395 void security_file_free(struct file *file); 396 396 int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); 397 + int security_file_ioctl_compat(struct file *file, unsigned int cmd, 398 + unsigned long arg); 397 399 int security_mmap_file(struct file *file, unsigned long prot, 398 400 unsigned long flags); 399 401 int security_mmap_addr(unsigned long addr); ··· 1000 998 1001 999 static inline int security_file_ioctl(struct file *file, unsigned int cmd, 1002 1000 unsigned long arg) 1001 + { 1002 + return 0; 1003 + } 1004 + 1005 + static inline int security_file_ioctl_compat(struct file *file, 1006 + unsigned int cmd, 1007 + unsigned long arg) 1003 1008 { 1004 1009 return 0; 1005 1010 }
+18
security/security.c
··· 2732 2732 } 2733 2733 EXPORT_SYMBOL_GPL(security_file_ioctl); 2734 2734 2735 + /** 2736 + * security_file_ioctl_compat() - Check if an ioctl is allowed in compat mode 2737 + * @file: associated file 2738 + * @cmd: ioctl cmd 2739 + * @arg: ioctl arguments 2740 + * 2741 + * Compat version of security_file_ioctl() that correctly handles 32-bit 2742 + * processes running on 64-bit kernels. 2743 + * 2744 + * Return: Returns 0 if permission is granted. 2745 + */ 2746 + int security_file_ioctl_compat(struct file *file, unsigned int cmd, 2747 + unsigned long arg) 2748 + { 2749 + return call_int_hook(file_ioctl_compat, 0, file, cmd, arg); 2750 + } 2751 + EXPORT_SYMBOL_GPL(security_file_ioctl_compat); 2752 + 2735 2753 static inline unsigned long mmap_prot(struct file *file, unsigned long prot) 2736 2754 { 2737 2755 /*
+28
security/selinux/hooks.c
··· 3732 3732 return error; 3733 3733 } 3734 3734 3735 + static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, 3736 + unsigned long arg) 3737 + { 3738 + /* 3739 + * If we are in a 64-bit kernel running 32-bit userspace, we need to 3740 + * make sure we don't compare 32-bit flags to 64-bit flags. 3741 + */ 3742 + switch (cmd) { 3743 + case FS_IOC32_GETFLAGS: 3744 + cmd = FS_IOC_GETFLAGS; 3745 + break; 3746 + case FS_IOC32_SETFLAGS: 3747 + cmd = FS_IOC_SETFLAGS; 3748 + break; 3749 + case FS_IOC32_GETVERSION: 3750 + cmd = FS_IOC_GETVERSION; 3751 + break; 3752 + case FS_IOC32_SETVERSION: 3753 + cmd = FS_IOC_SETVERSION; 3754 + break; 3755 + default: 3756 + break; 3757 + } 3758 + 3759 + return selinux_file_ioctl(file, cmd, arg); 3760 + } 3761 + 3735 3762 static int default_noexec __ro_after_init; 3736 3763 3737 3764 static int file_map_prot_check(struct file *file, unsigned long prot, int shared) ··· 7149 7122 LSM_HOOK_INIT(file_permission, selinux_file_permission), 7150 7123 LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), 7151 7124 LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), 7125 + LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat), 7152 7126 LSM_HOOK_INIT(mmap_file, selinux_mmap_file), 7153 7127 LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr), 7154 7128 LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
+1
security/smack/smack_lsm.c
··· 5051 5051 5052 5052 LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security), 5053 5053 LSM_HOOK_INIT(file_ioctl, smack_file_ioctl), 5054 + LSM_HOOK_INIT(file_ioctl_compat, smack_file_ioctl), 5054 5055 LSM_HOOK_INIT(file_lock, smack_file_lock), 5055 5056 LSM_HOOK_INIT(file_fcntl, smack_file_fcntl), 5056 5057 LSM_HOOK_INIT(mmap_file, smack_mmap_file),
+1
security/tomoyo/tomoyo.c
··· 574 574 LSM_HOOK_INIT(path_rename, tomoyo_path_rename), 575 575 LSM_HOOK_INIT(inode_getattr, tomoyo_inode_getattr), 576 576 LSM_HOOK_INIT(file_ioctl, tomoyo_file_ioctl), 577 + LSM_HOOK_INIT(file_ioctl_compat, tomoyo_file_ioctl), 577 578 LSM_HOOK_INIT(path_chmod, tomoyo_path_chmod), 578 579 LSM_HOOK_INIT(path_chown, tomoyo_path_chown), 579 580 LSM_HOOK_INIT(path_chroot, tomoyo_path_chroot),