Skip to content

Commit f1bb47a

Browse files
alpiccionipcmoore
authored andcommitted
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: 0b24dcb ("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>
1 parent ea67677 commit f1bb47a

File tree

7 files changed

+60
-2
lines changed

7 files changed

+60
-2
lines changed

fs/ioctl.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,8 +920,7 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
920920
if (!f.file)
921921
return -EBADF;
922922

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);
925924
if (error)
926925
goto out;
927926

include/linux/lsm_hook_defs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ LSM_HOOK(int, 0, file_alloc_security, struct file *file)
171171
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
172172
LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
173173
unsigned long arg)
174+
LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
175+
unsigned long arg)
174176
LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
175177
LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
176178
unsigned long prot, unsigned long flags)

include/linux/security.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ int security_file_permission(struct file *file, int mask);
394394
int security_file_alloc(struct file *file);
395395
void security_file_free(struct file *file);
396396
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);
397399
int security_mmap_file(struct file *file, unsigned long prot,
398400
unsigned long flags);
399401
int security_mmap_addr(unsigned long addr);
@@ -1002,6 +1004,13 @@ static inline int security_file_ioctl(struct file *file, unsigned int cmd,
10021004
return 0;
10031005
}
10041006

1007+
static inline int security_file_ioctl_compat(struct file *file,
1008+
unsigned int cmd,
1009+
unsigned long arg)
1010+
{
1011+
return 0;
1012+
}
1013+
10051014
static inline int security_mmap_file(struct file *file, unsigned long prot,
10061015
unsigned long flags)
10071016
{

security/security.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2732,6 +2732,24 @@ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
27322732
}
27332733
EXPORT_SYMBOL_GPL(security_file_ioctl);
27342734

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+
27352753
static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
27362754
{
27372755
/*

security/selinux/hooks.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3732,6 +3732,33 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
37323732
return error;
37333733
}
37343734

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+
37353762
static int default_noexec __ro_after_init;
37363763

37373764
static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
@@ -7122,6 +7149,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
71227149
LSM_HOOK_INIT(file_permission, selinux_file_permission),
71237150
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
71247151
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
7152+
LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
71257153
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
71267154
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
71277155
LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),

security/smack/smack_lsm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5051,6 +5051,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
50515051

50525052
LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security),
50535053
LSM_HOOK_INIT(file_ioctl, smack_file_ioctl),
5054+
LSM_HOOK_INIT(file_ioctl_compat, smack_file_ioctl),
50545055
LSM_HOOK_INIT(file_lock, smack_file_lock),
50555056
LSM_HOOK_INIT(file_fcntl, smack_file_fcntl),
50565057
LSM_HOOK_INIT(mmap_file, smack_mmap_file),

security/tomoyo/tomoyo.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ static struct security_hook_list tomoyo_hooks[] __ro_after_init = {
574574
LSM_HOOK_INIT(path_rename, tomoyo_path_rename),
575575
LSM_HOOK_INIT(inode_getattr, tomoyo_inode_getattr),
576576
LSM_HOOK_INIT(file_ioctl, tomoyo_file_ioctl),
577+
LSM_HOOK_INIT(file_ioctl_compat, tomoyo_file_ioctl),
577578
LSM_HOOK_INIT(path_chmod, tomoyo_path_chmod),
578579
LSM_HOOK_INIT(path_chown, tomoyo_path_chown),
579580
LSM_HOOK_INIT(path_chroot, tomoyo_path_chroot),

0 commit comments

Comments
 (0)