diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-18 10:48:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-18 10:48:07 -0700 |
commit | 2fc0e7892c10734c1b7c613ef04836d57d4676d5 (patch) | |
tree | c577dccc02b96f4f2683acdc6eef6fb1e5e6b113 /security | |
parent | 89721e3038d181bacbd6be54354b513fdf1b4f10 (diff) | |
parent | 5bf9e57e634bd72a97b4b12c87186fc052a6a116 (diff) |
Merge tag 'landlock-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock updates from Mickaël Salaün:
"This brings ioctl control to Landlock, contributed by Günther Noack.
This also adds him as a Landlock reviewer, and fixes an issue in the
sample"
* tag 'landlock-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
MAINTAINERS: Add Günther Noack as Landlock reviewer
fs/ioctl: Add a comment to keep the logic in sync with LSM policies
MAINTAINERS: Notify Landlock maintainers about changes to fs/ioctl.c
landlock: Document IOCTL support
samples/landlock: Add support for LANDLOCK_ACCESS_FS_IOCTL_DEV
selftests/landlock: Exhaustive test for the IOCTL allow-list
selftests/landlock: Check IOCTL restrictions for named UNIX domain sockets
selftests/landlock: Test IOCTLs on named pipes
selftests/landlock: Test ioctl(2) and ftruncate(2) with open(O_PATH)
selftests/landlock: Test IOCTL with memfds
selftests/landlock: Test IOCTL support
landlock: Add IOCTL access right for character and block devices
samples/landlock: Fix incorrect free in populate_ruleset_net
Diffstat (limited to 'security')
-rw-r--r-- | security/landlock/fs.c | 225 | ||||
-rw-r--r-- | security/landlock/limits.h | 2 | ||||
-rw-r--r-- | security/landlock/syscalls.c | 2 |
3 files changed, 224 insertions, 5 deletions
diff --git a/security/landlock/fs.c b/security/landlock/fs.c index c15559432d3d..22d8b7c28074 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -5,8 +5,11 @@ * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> * Copyright © 2018-2020 ANSSI * Copyright © 2021-2022 Microsoft Corporation + * Copyright © 2022 Günther Noack <gnoack3000@gmail.com> + * Copyright © 2023-2024 Google LLC */ +#include <asm/ioctls.h> #include <kunit/test.h> #include <linux/atomic.h> #include <linux/bitops.h> @@ -14,6 +17,7 @@ #include <linux/compiler_types.h> #include <linux/dcache.h> #include <linux/err.h> +#include <linux/falloc.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> @@ -29,6 +33,7 @@ #include <linux/types.h> #include <linux/wait_bit.h> #include <linux/workqueue.h> +#include <uapi/linux/fiemap.h> #include <uapi/linux/landlock.h> #include "common.h" @@ -84,6 +89,160 @@ static const struct landlock_object_underops landlock_fs_underops = { .release = release_inode }; +/* IOCTL helpers */ + +/** + * is_masked_device_ioctl - Determine whether an IOCTL command is always + * permitted with Landlock for device files. These commands can not be + * restricted on device files by enforcing a Landlock policy. + * + * @cmd: The IOCTL command that is supposed to be run. + * + * By default, any IOCTL on a device file requires the + * LANDLOCK_ACCESS_FS_IOCTL_DEV right. However, we blanket-permit some + * commands, if: + * + * 1. The command is implemented in fs/ioctl.c's do_vfs_ioctl(), + * not in f_ops->unlocked_ioctl() or f_ops->compat_ioctl(). + * + * 2. The command is harmless when invoked on devices. + * + * We also permit commands that do not make sense for devices, but where the + * do_vfs_ioctl() implementation returns a more conventional error code. + * + * Any new IOCTL commands that are implemented in fs/ioctl.c's do_vfs_ioctl() + * should be considered for inclusion here. + * + * Returns: true if the IOCTL @cmd can not be restricted with Landlock for + * device files. + */ +static __attribute_const__ bool is_masked_device_ioctl(const unsigned int cmd) +{ + switch (cmd) { + /* + * FIOCLEX, FIONCLEX, FIONBIO and FIOASYNC manipulate the FD's + * close-on-exec and the file's buffered-IO and async flags. These + * operations are also available through fcntl(2), and are + * unconditionally permitted in Landlock. + */ + case FIOCLEX: + case FIONCLEX: + case FIONBIO: + case FIOASYNC: + /* + * FIOQSIZE queries the size of a regular file, directory, or link. + * + * We still permit it, because it always returns -ENOTTY for + * other file types. + */ + case FIOQSIZE: + /* + * FIFREEZE and FITHAW freeze and thaw the file system which the + * given file belongs to. Requires CAP_SYS_ADMIN. + * + * These commands operate on the file system's superblock rather + * than on the file itself. The same operations can also be + * done through any other file or directory on the same file + * system, so it is safe to permit these. + */ + case FIFREEZE: + case FITHAW: + /* + * FS_IOC_FIEMAP queries information about the allocation of + * blocks within a file. + * + * This IOCTL command only makes sense for regular files and is + * not implemented by devices. It is harmless to permit. + */ + case FS_IOC_FIEMAP: + /* + * FIGETBSZ queries the file system's block size for a file or + * directory. + * + * This command operates on the file system's superblock rather + * than on the file itself. The same operation can also be done + * through any other file or directory on the same file system, + * so it is safe to permit it. + */ + case FIGETBSZ: + /* + * FICLONE, FICLONERANGE and FIDEDUPERANGE make files share + * their underlying storage ("reflink") between source and + * destination FDs, on file systems which support that. + * + * These IOCTL commands only apply to regular files + * and are harmless to permit for device files. + */ + case FICLONE: + case FICLONERANGE: + case FIDEDUPERANGE: + /* + * FS_IOC_GETFSUUID and FS_IOC_GETFSSYSFSPATH both operate on + * the file system superblock, not on the specific file, so + * these operations are available through any other file on the + * same file system as well. + */ + case FS_IOC_GETFSUUID: + case FS_IOC_GETFSSYSFSPATH: + return true; + + /* + * FIONREAD, FS_IOC_GETFLAGS, FS_IOC_SETFLAGS, FS_IOC_FSGETXATTR and + * FS_IOC_FSSETXATTR are forwarded to device implementations. + */ + + /* + * file_ioctl() commands (FIBMAP, FS_IOC_RESVSP, FS_IOC_RESVSP64, + * FS_IOC_UNRESVSP, FS_IOC_UNRESVSP64 and FS_IOC_ZERO_RANGE) are + * forwarded to device implementations, so not permitted. + */ + + /* Other commands are guarded by the access right. */ + default: + return false; + } +} + +/* + * is_masked_device_ioctl_compat - same as the helper above, but checking the + * "compat" IOCTL commands. + * + * The IOCTL commands with special handling in compat-mode should behave the + * same as their non-compat counterparts. + */ +static __attribute_const__ bool +is_masked_device_ioctl_compat(const unsigned int cmd) +{ + switch (cmd) { + /* FICLONE is permitted, same as in the non-compat variant. */ + case FICLONE: + return true; + +#if defined(CONFIG_X86_64) + /* + * FS_IOC_RESVSP_32, FS_IOC_RESVSP64_32, FS_IOC_UNRESVSP_32, + * FS_IOC_UNRESVSP64_32, FS_IOC_ZERO_RANGE_32: not blanket-permitted, + * for consistency with their non-compat variants. + */ + case FS_IOC_RESVSP_32: + case FS_IOC_RESVSP64_32: + case FS_IOC_UNRESVSP_32: + case FS_IOC_UNRESVSP64_32: + case FS_IOC_ZERO_RANGE_32: +#endif + + /* + * FS_IOC32_GETFLAGS, FS_IOC32_SETFLAGS are forwarded to their device + * implementations. + */ + case FS_IOC32_GETFLAGS: + case FS_IOC32_SETFLAGS: + return false; + default: + return is_masked_device_ioctl(cmd); + } +} + /* Ruleset management */ static struct landlock_object *get_inode_object(struct inode *const inode) @@ -148,7 +307,8 @@ retry: LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \ - LANDLOCK_ACCESS_FS_TRUNCATE) + LANDLOCK_ACCESS_FS_TRUNCATE | \ + LANDLOCK_ACCESS_FS_IOCTL_DEV) /* clang-format on */ /* @@ -1332,11 +1492,18 @@ static int hook_file_alloc_security(struct file *const file) return 0; } +static bool is_device(const struct file *const file) +{ + const struct inode *inode = file_inode(file); + + return S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode); +} + static int hook_file_open(struct file *const file) { layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; - access_mask_t open_access_request, full_access_request, allowed_access; - const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; + access_mask_t open_access_request, full_access_request, allowed_access, + optional_access; const struct landlock_ruleset *const dom = get_fs_domain(landlock_cred(file->f_cred)->domain); @@ -1354,6 +1521,10 @@ static int hook_file_open(struct file *const file) * We look up more access than what we immediately need for open(), so * that we can later authorize operations on opened files. */ + optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; + if (is_device(file)) + optional_access |= LANDLOCK_ACCESS_FS_IOCTL_DEV; + full_access_request = open_access_request | optional_access; if (is_access_to_paths_allowed( @@ -1410,6 +1581,52 @@ static int hook_file_truncate(struct file *const file) return -EACCES; } +static int hook_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + access_mask_t allowed_access = landlock_file(file)->allowed_access; + + /* + * It is the access rights at the time of opening the file which + * determine whether IOCTL can be used on the opened file later. + * + * The access right is attached to the opened file in hook_file_open(). + */ + if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV) + return 0; + + if (!is_device(file)) + return 0; + + if (is_masked_device_ioctl(cmd)) + return 0; + + return -EACCES; +} + +static int hook_file_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) +{ + access_mask_t allowed_access = landlock_file(file)->allowed_access; + + /* + * It is the access rights at the time of opening the file which + * determine whether IOCTL can be used on the opened file later. + * + * The access right is attached to the opened file in hook_file_open(). + */ + if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV) + return 0; + + if (!is_device(file)) + return 0; + + if (is_masked_device_ioctl_compat(cmd)) + return 0; + + return -EACCES; +} + static struct security_hook_list landlock_hooks[] __ro_after_init = { LSM_HOOK_INIT(inode_free_security, hook_inode_free_security), @@ -1432,6 +1649,8 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = { LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), LSM_HOOK_INIT(file_open, hook_file_open), LSM_HOOK_INIT(file_truncate, hook_file_truncate), + LSM_HOOK_INIT(file_ioctl, hook_file_ioctl), + LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat), }; __init void landlock_add_fs_hooks(void) diff --git a/security/landlock/limits.h b/security/landlock/limits.h index 93c9c6f91556..20fdb5ff3514 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -18,7 +18,7 @@ #define LANDLOCK_MAX_NUM_LAYERS 16 #define LANDLOCK_MAX_NUM_RULES U32_MAX -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) #define LANDLOCK_SHIFT_ACCESS_FS 0 diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 6788e73b6681..03b470f5a85a 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -149,7 +149,7 @@ static const struct file_operations ruleset_fops = { .write = fop_dummy_write, }; -#define LANDLOCK_ABI_VERSION 4 +#define LANDLOCK_ABI_VERSION 5 /** * sys_landlock_create_ruleset - Create a new ruleset |