1cb2c7d1aSMickaël Salaün // SPDX-License-Identifier: GPL-2.0-only 2cb2c7d1aSMickaël Salaün /* 3cb2c7d1aSMickaël Salaün * Landlock LSM - Filesystem management and hooks 4cb2c7d1aSMickaël Salaün * 5cb2c7d1aSMickaël Salaün * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6cb2c7d1aSMickaël Salaün * Copyright © 2018-2020 ANSSI 7b91c3e4eSMickaël Salaün * Copyright © 2021-2022 Microsoft Corporation 8b25f7415SGünther Noack * Copyright © 2022 Günther Noack <gnoack3000@gmail.com> 9b25f7415SGünther Noack * Copyright © 2023-2024 Google LLC 10cb2c7d1aSMickaël Salaün */ 11cb2c7d1aSMickaël Salaün 12b25f7415SGünther Noack #include <asm/ioctls.h> 13b4007fd2SMickaël Salaün #include <kunit/test.h> 14cb2c7d1aSMickaël Salaün #include <linux/atomic.h> 15cb2c7d1aSMickaël Salaün #include <linux/bitops.h> 16cb2c7d1aSMickaël Salaün #include <linux/bits.h> 17cb2c7d1aSMickaël Salaün #include <linux/compiler_types.h> 18cb2c7d1aSMickaël Salaün #include <linux/dcache.h> 19cb2c7d1aSMickaël Salaün #include <linux/err.h> 20b25f7415SGünther Noack #include <linux/falloc.h> 21cb2c7d1aSMickaël Salaün #include <linux/fs.h> 22cb2c7d1aSMickaël Salaün #include <linux/init.h> 23cb2c7d1aSMickaël Salaün #include <linux/kernel.h> 24cb2c7d1aSMickaël Salaün #include <linux/limits.h> 25cb2c7d1aSMickaël Salaün #include <linux/list.h> 26cb2c7d1aSMickaël Salaün #include <linux/lsm_hooks.h> 27cb2c7d1aSMickaël Salaün #include <linux/mount.h> 28cb2c7d1aSMickaël Salaün #include <linux/namei.h> 29cb2c7d1aSMickaël Salaün #include <linux/path.h> 30cb2c7d1aSMickaël Salaün #include <linux/rcupdate.h> 31cb2c7d1aSMickaël Salaün #include <linux/spinlock.h> 32cb2c7d1aSMickaël Salaün #include <linux/stat.h> 33cb2c7d1aSMickaël Salaün #include <linux/types.h> 34cb2c7d1aSMickaël Salaün #include <linux/wait_bit.h> 35cb2c7d1aSMickaël Salaün #include <linux/workqueue.h> 36b25f7415SGünther Noack #include <uapi/linux/fiemap.h> 37cb2c7d1aSMickaël Salaün #include <uapi/linux/landlock.h> 38cb2c7d1aSMickaël Salaün 39cb2c7d1aSMickaël Salaün #include "common.h" 40cb2c7d1aSMickaël Salaün #include "cred.h" 41cb2c7d1aSMickaël Salaün #include "fs.h" 42cb2c7d1aSMickaël Salaün #include "limits.h" 43cb2c7d1aSMickaël Salaün #include "object.h" 44cb2c7d1aSMickaël Salaün #include "ruleset.h" 45cb2c7d1aSMickaël Salaün #include "setup.h" 46cb2c7d1aSMickaël Salaün 47cb2c7d1aSMickaël Salaün /* Underlying object management */ 48cb2c7d1aSMickaël Salaün 49cb2c7d1aSMickaël Salaün static void release_inode(struct landlock_object *const object) 50cb2c7d1aSMickaël Salaün __releases(object->lock) 51cb2c7d1aSMickaël Salaün { 52cb2c7d1aSMickaël Salaün struct inode *const inode = object->underobj; 53cb2c7d1aSMickaël Salaün struct super_block *sb; 54cb2c7d1aSMickaël Salaün 55cb2c7d1aSMickaël Salaün if (!inode) { 56cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 57cb2c7d1aSMickaël Salaün return; 58cb2c7d1aSMickaël Salaün } 59cb2c7d1aSMickaël Salaün 60cb2c7d1aSMickaël Salaün /* 61cb2c7d1aSMickaël Salaün * Protects against concurrent use by hook_sb_delete() of the reference 62cb2c7d1aSMickaël Salaün * to the underlying inode. 63cb2c7d1aSMickaël Salaün */ 64cb2c7d1aSMickaël Salaün object->underobj = NULL; 65cb2c7d1aSMickaël Salaün /* 66cb2c7d1aSMickaël Salaün * Makes sure that if the filesystem is concurrently unmounted, 67cb2c7d1aSMickaël Salaün * hook_sb_delete() will wait for us to finish iput(). 68cb2c7d1aSMickaël Salaün */ 69cb2c7d1aSMickaël Salaün sb = inode->i_sb; 70cb2c7d1aSMickaël Salaün atomic_long_inc(&landlock_superblock(sb)->inode_refs); 71cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 72cb2c7d1aSMickaël Salaün /* 73cb2c7d1aSMickaël Salaün * Because object->underobj was not NULL, hook_sb_delete() and 74cb2c7d1aSMickaël Salaün * get_inode_object() guarantee that it is safe to reset 75cb2c7d1aSMickaël Salaün * landlock_inode(inode)->object while it is not NULL. It is therefore 76cb2c7d1aSMickaël Salaün * not necessary to lock inode->i_lock. 77cb2c7d1aSMickaël Salaün */ 78cb2c7d1aSMickaël Salaün rcu_assign_pointer(landlock_inode(inode)->object, NULL); 79cb2c7d1aSMickaël Salaün /* 80cb2c7d1aSMickaël Salaün * Now, new rules can safely be tied to @inode with get_inode_object(). 81cb2c7d1aSMickaël Salaün */ 82cb2c7d1aSMickaël Salaün 83cb2c7d1aSMickaël Salaün iput(inode); 84cb2c7d1aSMickaël Salaün if (atomic_long_dec_and_test(&landlock_superblock(sb)->inode_refs)) 85cb2c7d1aSMickaël Salaün wake_up_var(&landlock_superblock(sb)->inode_refs); 86cb2c7d1aSMickaël Salaün } 87cb2c7d1aSMickaël Salaün 88cb2c7d1aSMickaël Salaün static const struct landlock_object_underops landlock_fs_underops = { 89cb2c7d1aSMickaël Salaün .release = release_inode 90cb2c7d1aSMickaël Salaün }; 91cb2c7d1aSMickaël Salaün 92b25f7415SGünther Noack /* IOCTL helpers */ 93b25f7415SGünther Noack 94b25f7415SGünther Noack /** 95b25f7415SGünther Noack * is_masked_device_ioctl - Determine whether an IOCTL command is always 96b25f7415SGünther Noack * permitted with Landlock for device files. These commands can not be 97b25f7415SGünther Noack * restricted on device files by enforcing a Landlock policy. 98b25f7415SGünther Noack * 99b25f7415SGünther Noack * @cmd: The IOCTL command that is supposed to be run. 100b25f7415SGünther Noack * 101b25f7415SGünther Noack * By default, any IOCTL on a device file requires the 102b25f7415SGünther Noack * LANDLOCK_ACCESS_FS_IOCTL_DEV right. However, we blanket-permit some 103b25f7415SGünther Noack * commands, if: 104b25f7415SGünther Noack * 105b25f7415SGünther Noack * 1. The command is implemented in fs/ioctl.c's do_vfs_ioctl(), 106b25f7415SGünther Noack * not in f_ops->unlocked_ioctl() or f_ops->compat_ioctl(). 107b25f7415SGünther Noack * 108b25f7415SGünther Noack * 2. The command is harmless when invoked on devices. 109b25f7415SGünther Noack * 110b25f7415SGünther Noack * We also permit commands that do not make sense for devices, but where the 111b25f7415SGünther Noack * do_vfs_ioctl() implementation returns a more conventional error code. 112b25f7415SGünther Noack * 113b25f7415SGünther Noack * Any new IOCTL commands that are implemented in fs/ioctl.c's do_vfs_ioctl() 114b25f7415SGünther Noack * should be considered for inclusion here. 115b25f7415SGünther Noack * 116b25f7415SGünther Noack * Returns: true if the IOCTL @cmd can not be restricted with Landlock for 117b25f7415SGünther Noack * device files. 118b25f7415SGünther Noack */ 119b25f7415SGünther Noack static __attribute_const__ bool is_masked_device_ioctl(const unsigned int cmd) 120b25f7415SGünther Noack { 121b25f7415SGünther Noack switch (cmd) { 122b25f7415SGünther Noack /* 123b25f7415SGünther Noack * FIOCLEX, FIONCLEX, FIONBIO and FIOASYNC manipulate the FD's 124b25f7415SGünther Noack * close-on-exec and the file's buffered-IO and async flags. These 125b25f7415SGünther Noack * operations are also available through fcntl(2), and are 126b25f7415SGünther Noack * unconditionally permitted in Landlock. 127b25f7415SGünther Noack */ 128b25f7415SGünther Noack case FIOCLEX: 129b25f7415SGünther Noack case FIONCLEX: 130b25f7415SGünther Noack case FIONBIO: 131b25f7415SGünther Noack case FIOASYNC: 132b25f7415SGünther Noack /* 133b25f7415SGünther Noack * FIOQSIZE queries the size of a regular file, directory, or link. 134b25f7415SGünther Noack * 135b25f7415SGünther Noack * We still permit it, because it always returns -ENOTTY for 136b25f7415SGünther Noack * other file types. 137b25f7415SGünther Noack */ 138b25f7415SGünther Noack case FIOQSIZE: 139b25f7415SGünther Noack /* 140b25f7415SGünther Noack * FIFREEZE and FITHAW freeze and thaw the file system which the 141b25f7415SGünther Noack * given file belongs to. Requires CAP_SYS_ADMIN. 142b25f7415SGünther Noack * 143b25f7415SGünther Noack * These commands operate on the file system's superblock rather 144b25f7415SGünther Noack * than on the file itself. The same operations can also be 145b25f7415SGünther Noack * done through any other file or directory on the same file 146b25f7415SGünther Noack * system, so it is safe to permit these. 147b25f7415SGünther Noack */ 148b25f7415SGünther Noack case FIFREEZE: 149b25f7415SGünther Noack case FITHAW: 150b25f7415SGünther Noack /* 151b25f7415SGünther Noack * FS_IOC_FIEMAP queries information about the allocation of 152b25f7415SGünther Noack * blocks within a file. 153b25f7415SGünther Noack * 154b25f7415SGünther Noack * This IOCTL command only makes sense for regular files and is 155b25f7415SGünther Noack * not implemented by devices. It is harmless to permit. 156b25f7415SGünther Noack */ 157b25f7415SGünther Noack case FS_IOC_FIEMAP: 158b25f7415SGünther Noack /* 159b25f7415SGünther Noack * FIGETBSZ queries the file system's block size for a file or 160b25f7415SGünther Noack * directory. 161b25f7415SGünther Noack * 162b25f7415SGünther Noack * This command operates on the file system's superblock rather 163b25f7415SGünther Noack * than on the file itself. The same operation can also be done 164b25f7415SGünther Noack * through any other file or directory on the same file system, 165b25f7415SGünther Noack * so it is safe to permit it. 166b25f7415SGünther Noack */ 167b25f7415SGünther Noack case FIGETBSZ: 168b25f7415SGünther Noack /* 169b25f7415SGünther Noack * FICLONE, FICLONERANGE and FIDEDUPERANGE make files share 170b25f7415SGünther Noack * their underlying storage ("reflink") between source and 171b25f7415SGünther Noack * destination FDs, on file systems which support that. 172b25f7415SGünther Noack * 173b25f7415SGünther Noack * These IOCTL commands only apply to regular files 174b25f7415SGünther Noack * and are harmless to permit for device files. 175b25f7415SGünther Noack */ 176b25f7415SGünther Noack case FICLONE: 177b25f7415SGünther Noack case FICLONERANGE: 178b25f7415SGünther Noack case FIDEDUPERANGE: 179b25f7415SGünther Noack /* 180b25f7415SGünther Noack * FS_IOC_GETFSUUID and FS_IOC_GETFSSYSFSPATH both operate on 181b25f7415SGünther Noack * the file system superblock, not on the specific file, so 182b25f7415SGünther Noack * these operations are available through any other file on the 183b25f7415SGünther Noack * same file system as well. 184b25f7415SGünther Noack */ 185b25f7415SGünther Noack case FS_IOC_GETFSUUID: 186b25f7415SGünther Noack case FS_IOC_GETFSSYSFSPATH: 187b25f7415SGünther Noack return true; 188b25f7415SGünther Noack 189b25f7415SGünther Noack /* 190b25f7415SGünther Noack * FIONREAD, FS_IOC_GETFLAGS, FS_IOC_SETFLAGS, FS_IOC_FSGETXATTR and 191b25f7415SGünther Noack * FS_IOC_FSSETXATTR are forwarded to device implementations. 192b25f7415SGünther Noack */ 193b25f7415SGünther Noack 194b25f7415SGünther Noack /* 195b25f7415SGünther Noack * file_ioctl() commands (FIBMAP, FS_IOC_RESVSP, FS_IOC_RESVSP64, 196b25f7415SGünther Noack * FS_IOC_UNRESVSP, FS_IOC_UNRESVSP64 and FS_IOC_ZERO_RANGE) are 197b25f7415SGünther Noack * forwarded to device implementations, so not permitted. 198b25f7415SGünther Noack */ 199b25f7415SGünther Noack 200b25f7415SGünther Noack /* Other commands are guarded by the access right. */ 201b25f7415SGünther Noack default: 202b25f7415SGünther Noack return false; 203b25f7415SGünther Noack } 204b25f7415SGünther Noack } 205b25f7415SGünther Noack 206b25f7415SGünther Noack /* 207b25f7415SGünther Noack * is_masked_device_ioctl_compat - same as the helper above, but checking the 208b25f7415SGünther Noack * "compat" IOCTL commands. 209b25f7415SGünther Noack * 210b25f7415SGünther Noack * The IOCTL commands with special handling in compat-mode should behave the 211b25f7415SGünther Noack * same as their non-compat counterparts. 212b25f7415SGünther Noack */ 213b25f7415SGünther Noack static __attribute_const__ bool 214b25f7415SGünther Noack is_masked_device_ioctl_compat(const unsigned int cmd) 215b25f7415SGünther Noack { 216b25f7415SGünther Noack switch (cmd) { 217b25f7415SGünther Noack /* FICLONE is permitted, same as in the non-compat variant. */ 218b25f7415SGünther Noack case FICLONE: 219b25f7415SGünther Noack return true; 220b25f7415SGünther Noack 221b25f7415SGünther Noack #if defined(CONFIG_X86_64) 222b25f7415SGünther Noack /* 223b25f7415SGünther Noack * FS_IOC_RESVSP_32, FS_IOC_RESVSP64_32, FS_IOC_UNRESVSP_32, 224b25f7415SGünther Noack * FS_IOC_UNRESVSP64_32, FS_IOC_ZERO_RANGE_32: not blanket-permitted, 225b25f7415SGünther Noack * for consistency with their non-compat variants. 226b25f7415SGünther Noack */ 227b25f7415SGünther Noack case FS_IOC_RESVSP_32: 228b25f7415SGünther Noack case FS_IOC_RESVSP64_32: 229b25f7415SGünther Noack case FS_IOC_UNRESVSP_32: 230b25f7415SGünther Noack case FS_IOC_UNRESVSP64_32: 231b25f7415SGünther Noack case FS_IOC_ZERO_RANGE_32: 232b25f7415SGünther Noack #endif 233b25f7415SGünther Noack 234b25f7415SGünther Noack /* 235b25f7415SGünther Noack * FS_IOC32_GETFLAGS, FS_IOC32_SETFLAGS are forwarded to their device 236b25f7415SGünther Noack * implementations. 237b25f7415SGünther Noack */ 238b25f7415SGünther Noack case FS_IOC32_GETFLAGS: 239b25f7415SGünther Noack case FS_IOC32_SETFLAGS: 240b25f7415SGünther Noack return false; 241b25f7415SGünther Noack default: 242b25f7415SGünther Noack return is_masked_device_ioctl(cmd); 243b25f7415SGünther Noack } 244b25f7415SGünther Noack } 245b25f7415SGünther Noack 246cb2c7d1aSMickaël Salaün /* Ruleset management */ 247cb2c7d1aSMickaël Salaün 248cb2c7d1aSMickaël Salaün static struct landlock_object *get_inode_object(struct inode *const inode) 249cb2c7d1aSMickaël Salaün { 250cb2c7d1aSMickaël Salaün struct landlock_object *object, *new_object; 251cb2c7d1aSMickaël Salaün struct landlock_inode_security *inode_sec = landlock_inode(inode); 252cb2c7d1aSMickaël Salaün 253cb2c7d1aSMickaël Salaün rcu_read_lock(); 254cb2c7d1aSMickaël Salaün retry: 255cb2c7d1aSMickaël Salaün object = rcu_dereference(inode_sec->object); 256cb2c7d1aSMickaël Salaün if (object) { 257cb2c7d1aSMickaël Salaün if (likely(refcount_inc_not_zero(&object->usage))) { 258cb2c7d1aSMickaël Salaün rcu_read_unlock(); 259cb2c7d1aSMickaël Salaün return object; 260cb2c7d1aSMickaël Salaün } 261cb2c7d1aSMickaël Salaün /* 262cb2c7d1aSMickaël Salaün * We are racing with release_inode(), the object is going 263cb2c7d1aSMickaël Salaün * away. Wait for release_inode(), then retry. 264cb2c7d1aSMickaël Salaün */ 265cb2c7d1aSMickaël Salaün spin_lock(&object->lock); 266cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 267cb2c7d1aSMickaël Salaün goto retry; 268cb2c7d1aSMickaël Salaün } 269cb2c7d1aSMickaël Salaün rcu_read_unlock(); 270cb2c7d1aSMickaël Salaün 271cb2c7d1aSMickaël Salaün /* 272cb2c7d1aSMickaël Salaün * If there is no object tied to @inode, then create a new one (without 273cb2c7d1aSMickaël Salaün * holding any locks). 274cb2c7d1aSMickaël Salaün */ 275cb2c7d1aSMickaël Salaün new_object = landlock_create_object(&landlock_fs_underops, inode); 276cb2c7d1aSMickaël Salaün if (IS_ERR(new_object)) 277cb2c7d1aSMickaël Salaün return new_object; 278cb2c7d1aSMickaël Salaün 279cb2c7d1aSMickaël Salaün /* 280cb2c7d1aSMickaël Salaün * Protects against concurrent calls to get_inode_object() or 281cb2c7d1aSMickaël Salaün * hook_sb_delete(). 282cb2c7d1aSMickaël Salaün */ 283cb2c7d1aSMickaël Salaün spin_lock(&inode->i_lock); 284cb2c7d1aSMickaël Salaün if (unlikely(rcu_access_pointer(inode_sec->object))) { 285cb2c7d1aSMickaël Salaün /* Someone else just created the object, bail out and retry. */ 286cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 287cb2c7d1aSMickaël Salaün kfree(new_object); 288cb2c7d1aSMickaël Salaün 289cb2c7d1aSMickaël Salaün rcu_read_lock(); 290cb2c7d1aSMickaël Salaün goto retry; 291cb2c7d1aSMickaël Salaün } 292cb2c7d1aSMickaël Salaün 293cb2c7d1aSMickaël Salaün /* 294cb2c7d1aSMickaël Salaün * @inode will be released by hook_sb_delete() on its superblock 295cb2c7d1aSMickaël Salaün * shutdown, or by release_inode() when no more ruleset references the 296cb2c7d1aSMickaël Salaün * related object. 297cb2c7d1aSMickaël Salaün */ 298cb2c7d1aSMickaël Salaün ihold(inode); 299cb2c7d1aSMickaël Salaün rcu_assign_pointer(inode_sec->object, new_object); 300cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 301cb2c7d1aSMickaël Salaün return new_object; 302cb2c7d1aSMickaël Salaün } 303cb2c7d1aSMickaël Salaün 304cb2c7d1aSMickaël Salaün /* All access rights that can be tied to files. */ 3056cc2df8eSMickaël Salaün /* clang-format off */ 306cb2c7d1aSMickaël Salaün #define ACCESS_FILE ( \ 307cb2c7d1aSMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | \ 308cb2c7d1aSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE | \ 309b9f5ce27SGünther Noack LANDLOCK_ACCESS_FS_READ_FILE | \ 310b25f7415SGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE | \ 311b25f7415SGünther Noack LANDLOCK_ACCESS_FS_IOCTL_DEV) 3126cc2df8eSMickaël Salaün /* clang-format on */ 313cb2c7d1aSMickaël Salaün 314cb2c7d1aSMickaël Salaün /* 315cb2c7d1aSMickaël Salaün * @path: Should have been checked by get_path_from_fd(). 316cb2c7d1aSMickaël Salaün */ 317cb2c7d1aSMickaël Salaün int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, 3185f2ff33eSMickaël Salaün const struct path *const path, 3195f2ff33eSMickaël Salaün access_mask_t access_rights) 320cb2c7d1aSMickaël Salaün { 321cb2c7d1aSMickaël Salaün int err; 322a4ac404bSKonstantin Meskhidze struct landlock_id id = { 323a4ac404bSKonstantin Meskhidze .type = LANDLOCK_KEY_INODE, 324a4ac404bSKonstantin Meskhidze }; 325cb2c7d1aSMickaël Salaün 326cb2c7d1aSMickaël Salaün /* Files only get access rights that make sense. */ 32706a1c40aSMickaël Salaün if (!d_is_dir(path->dentry) && 32806a1c40aSMickaël Salaün (access_rights | ACCESS_FILE) != ACCESS_FILE) 329cb2c7d1aSMickaël Salaün return -EINVAL; 330cb2c7d1aSMickaël Salaün if (WARN_ON_ONCE(ruleset->num_layers != 1)) 331cb2c7d1aSMickaël Salaün return -EINVAL; 332cb2c7d1aSMickaël Salaün 333cb2c7d1aSMickaël Salaün /* Transforms relative access rights to absolute ones. */ 33413fc6455SKonstantin Meskhidze access_rights |= LANDLOCK_MASK_ACCESS_FS & 335d7220364SMickaël Salaün ~landlock_get_fs_access_mask(ruleset, 0); 336a4ac404bSKonstantin Meskhidze id.key.object = get_inode_object(d_backing_inode(path->dentry)); 337a4ac404bSKonstantin Meskhidze if (IS_ERR(id.key.object)) 338a4ac404bSKonstantin Meskhidze return PTR_ERR(id.key.object); 339cb2c7d1aSMickaël Salaün mutex_lock(&ruleset->lock); 340a4ac404bSKonstantin Meskhidze err = landlock_insert_rule(ruleset, id, access_rights); 341cb2c7d1aSMickaël Salaün mutex_unlock(&ruleset->lock); 342cb2c7d1aSMickaël Salaün /* 343cb2c7d1aSMickaël Salaün * No need to check for an error because landlock_insert_rule() 344cb2c7d1aSMickaël Salaün * increments the refcount for the new object if needed. 345cb2c7d1aSMickaël Salaün */ 346a4ac404bSKonstantin Meskhidze landlock_put_object(id.key.object); 347cb2c7d1aSMickaël Salaün return err; 348cb2c7d1aSMickaël Salaün } 349cb2c7d1aSMickaël Salaün 350cb2c7d1aSMickaël Salaün /* Access-control management */ 351cb2c7d1aSMickaël Salaün 3522cd7cd6eSMickaël Salaün /* 3532cd7cd6eSMickaël Salaün * The lifetime of the returned rule is tied to @domain. 3542cd7cd6eSMickaël Salaün * 3552cd7cd6eSMickaël Salaün * Returns NULL if no rule is found or if @dentry is negative. 3562cd7cd6eSMickaël Salaün */ 3578fd80721SGünther Noack static const struct landlock_rule * 3582cd7cd6eSMickaël Salaün find_rule(const struct landlock_ruleset *const domain, 3592cd7cd6eSMickaël Salaün const struct dentry *const dentry) 360cb2c7d1aSMickaël Salaün { 361cb2c7d1aSMickaël Salaün const struct landlock_rule *rule; 362cb2c7d1aSMickaël Salaün const struct inode *inode; 363a4ac404bSKonstantin Meskhidze struct landlock_id id = { 364a4ac404bSKonstantin Meskhidze .type = LANDLOCK_KEY_INODE, 365a4ac404bSKonstantin Meskhidze }; 366cb2c7d1aSMickaël Salaün 3672cd7cd6eSMickaël Salaün /* Ignores nonexistent leafs. */ 3682cd7cd6eSMickaël Salaün if (d_is_negative(dentry)) 3692cd7cd6eSMickaël Salaün return NULL; 3702cd7cd6eSMickaël Salaün 3712cd7cd6eSMickaël Salaün inode = d_backing_inode(dentry); 372cb2c7d1aSMickaël Salaün rcu_read_lock(); 373a4ac404bSKonstantin Meskhidze id.key.object = rcu_dereference(landlock_inode(inode)->object); 374a4ac404bSKonstantin Meskhidze rule = landlock_find_rule(domain, id); 375cb2c7d1aSMickaël Salaün rcu_read_unlock(); 3762cd7cd6eSMickaël Salaün return rule; 3772cd7cd6eSMickaël Salaün } 3782cd7cd6eSMickaël Salaün 3798ba0005fSMickaël Salaün /* 3809da82b20SMickaël Salaün * Allows access to pseudo filesystems that will never be mountable (e.g. 3819da82b20SMickaël Salaün * sockfs, pipefs), but can still be reachable through 3829da82b20SMickaël Salaün * /proc/<pid>/fd/<file-descriptor> 3839da82b20SMickaël Salaün */ 384da279087SGünther Noack static bool is_nouser_or_private(const struct dentry *dentry) 3859da82b20SMickaël Salaün { 3869da82b20SMickaël Salaün return (dentry->d_sb->s_flags & SB_NOUSER) || 3879da82b20SMickaël Salaün (d_is_positive(dentry) && 3889da82b20SMickaël Salaün unlikely(IS_PRIVATE(d_backing_inode(dentry)))); 3899da82b20SMickaël Salaün } 3909da82b20SMickaël Salaün 391d7220364SMickaël Salaün static access_mask_t 392d7220364SMickaël Salaün get_raw_handled_fs_accesses(const struct landlock_ruleset *const domain) 393b91c3e4eSMickaël Salaün { 394d7220364SMickaël Salaün access_mask_t access_dom = 0; 395b91c3e4eSMickaël Salaün size_t layer_level; 396b91c3e4eSMickaël Salaün 39755e55920SMickaël Salaün for (layer_level = 0; layer_level < domain->num_layers; layer_level++) 398d7220364SMickaël Salaün access_dom |= 399d7220364SMickaël Salaün landlock_get_raw_fs_access_mask(domain, layer_level); 400d7220364SMickaël Salaün return access_dom; 401b91c3e4eSMickaël Salaün } 402b91c3e4eSMickaël Salaün 403d7220364SMickaël Salaün static access_mask_t 404d7220364SMickaël Salaün get_handled_fs_accesses(const struct landlock_ruleset *const domain) 405d7220364SMickaël Salaün { 406d7220364SMickaël Salaün /* Handles all initially denied by default access rights. */ 407d7220364SMickaël Salaün return get_raw_handled_fs_accesses(domain) | 408d7220364SMickaël Salaün LANDLOCK_ACCESS_FS_INITIALLY_DENIED; 409d7220364SMickaël Salaün } 410d7220364SMickaël Salaün 41163817febSMickaël Salaün static const struct landlock_ruleset * 41263817febSMickaël Salaün get_fs_domain(const struct landlock_ruleset *const domain) 413d7220364SMickaël Salaün { 41463817febSMickaël Salaün if (!domain || !get_raw_handled_fs_accesses(domain)) 415d7220364SMickaël Salaün return NULL; 416d7220364SMickaël Salaün 41763817febSMickaël Salaün return domain; 41863817febSMickaël Salaün } 41963817febSMickaël Salaün 42063817febSMickaël Salaün static const struct landlock_ruleset *get_current_fs_domain(void) 42163817febSMickaël Salaün { 42263817febSMickaël Salaün return get_fs_domain(landlock_get_current_domain()); 423d7220364SMickaël Salaün } 424d7220364SMickaël Salaün 425b91c3e4eSMickaël Salaün /* 426b91c3e4eSMickaël Salaün * Check that a destination file hierarchy has more restrictions than a source 427b91c3e4eSMickaël Salaün * file hierarchy. This is only used for link and rename actions. 428b91c3e4eSMickaël Salaün * 429b91c3e4eSMickaël Salaün * @layer_masks_child2: Optional child masks. 430b91c3e4eSMickaël Salaün */ 431da279087SGünther Noack static bool no_more_access( 432b91c3e4eSMickaël Salaün const layer_mask_t (*const layer_masks_parent1)[LANDLOCK_NUM_ACCESS_FS], 433b91c3e4eSMickaël Salaün const layer_mask_t (*const layer_masks_child1)[LANDLOCK_NUM_ACCESS_FS], 434b91c3e4eSMickaël Salaün const bool child1_is_directory, 435b91c3e4eSMickaël Salaün const layer_mask_t (*const layer_masks_parent2)[LANDLOCK_NUM_ACCESS_FS], 436b91c3e4eSMickaël Salaün const layer_mask_t (*const layer_masks_child2)[LANDLOCK_NUM_ACCESS_FS], 437b91c3e4eSMickaël Salaün const bool child2_is_directory) 438b91c3e4eSMickaël Salaün { 439b91c3e4eSMickaël Salaün unsigned long access_bit; 440b91c3e4eSMickaël Salaün 441b91c3e4eSMickaël Salaün for (access_bit = 0; access_bit < ARRAY_SIZE(*layer_masks_parent2); 442b91c3e4eSMickaël Salaün access_bit++) { 443b91c3e4eSMickaël Salaün /* Ignores accesses that only make sense for directories. */ 444b91c3e4eSMickaël Salaün const bool is_file_access = 445b91c3e4eSMickaël Salaün !!(BIT_ULL(access_bit) & ACCESS_FILE); 446b91c3e4eSMickaël Salaün 447b91c3e4eSMickaël Salaün if (child1_is_directory || is_file_access) { 448b91c3e4eSMickaël Salaün /* 449b91c3e4eSMickaël Salaün * Checks if the destination restrictions are a 450b91c3e4eSMickaël Salaün * superset of the source ones (i.e. inherited access 451b91c3e4eSMickaël Salaün * rights without child exceptions): 452b91c3e4eSMickaël Salaün * restrictions(parent2) >= restrictions(child1) 453b91c3e4eSMickaël Salaün */ 454b91c3e4eSMickaël Salaün if ((((*layer_masks_parent1)[access_bit] & 455b91c3e4eSMickaël Salaün (*layer_masks_child1)[access_bit]) | 456b91c3e4eSMickaël Salaün (*layer_masks_parent2)[access_bit]) != 457b91c3e4eSMickaël Salaün (*layer_masks_parent2)[access_bit]) 458b91c3e4eSMickaël Salaün return false; 459b91c3e4eSMickaël Salaün } 460b91c3e4eSMickaël Salaün 461b91c3e4eSMickaël Salaün if (!layer_masks_child2) 462b91c3e4eSMickaël Salaün continue; 463b91c3e4eSMickaël Salaün if (child2_is_directory || is_file_access) { 464b91c3e4eSMickaël Salaün /* 465b91c3e4eSMickaël Salaün * Checks inverted restrictions for RENAME_EXCHANGE: 466b91c3e4eSMickaël Salaün * restrictions(parent1) >= restrictions(child2) 467b91c3e4eSMickaël Salaün */ 468b91c3e4eSMickaël Salaün if ((((*layer_masks_parent2)[access_bit] & 469b91c3e4eSMickaël Salaün (*layer_masks_child2)[access_bit]) | 470b91c3e4eSMickaël Salaün (*layer_masks_parent1)[access_bit]) != 471b91c3e4eSMickaël Salaün (*layer_masks_parent1)[access_bit]) 472b91c3e4eSMickaël Salaün return false; 473b91c3e4eSMickaël Salaün } 474b91c3e4eSMickaël Salaün } 475b91c3e4eSMickaël Salaün return true; 476b91c3e4eSMickaël Salaün } 477b91c3e4eSMickaël Salaün 478b4007fd2SMickaël Salaün #define NMA_TRUE(...) KUNIT_EXPECT_TRUE(test, no_more_access(__VA_ARGS__)) 479b4007fd2SMickaël Salaün #define NMA_FALSE(...) KUNIT_EXPECT_FALSE(test, no_more_access(__VA_ARGS__)) 480b4007fd2SMickaël Salaün 481b4007fd2SMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 482b4007fd2SMickaël Salaün 483b4007fd2SMickaël Salaün static void test_no_more_access(struct kunit *const test) 484b4007fd2SMickaël Salaün { 485b4007fd2SMickaël Salaün const layer_mask_t rx0[LANDLOCK_NUM_ACCESS_FS] = { 486b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0), 487b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = BIT_ULL(0), 488b4007fd2SMickaël Salaün }; 489b4007fd2SMickaël Salaün const layer_mask_t mx0[LANDLOCK_NUM_ACCESS_FS] = { 490b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0), 491b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_REG)] = BIT_ULL(0), 492b4007fd2SMickaël Salaün }; 493b4007fd2SMickaël Salaün const layer_mask_t x0[LANDLOCK_NUM_ACCESS_FS] = { 494b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0), 495b4007fd2SMickaël Salaün }; 496b4007fd2SMickaël Salaün const layer_mask_t x1[LANDLOCK_NUM_ACCESS_FS] = { 497b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(1), 498b4007fd2SMickaël Salaün }; 499b4007fd2SMickaël Salaün const layer_mask_t x01[LANDLOCK_NUM_ACCESS_FS] = { 500b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0) | 501b4007fd2SMickaël Salaün BIT_ULL(1), 502b4007fd2SMickaël Salaün }; 503b4007fd2SMickaël Salaün const layer_mask_t allows_all[LANDLOCK_NUM_ACCESS_FS] = {}; 504b4007fd2SMickaël Salaün 505b4007fd2SMickaël Salaün /* Checks without restriction. */ 506b4007fd2SMickaël Salaün NMA_TRUE(&x0, &allows_all, false, &allows_all, NULL, false); 507b4007fd2SMickaël Salaün NMA_TRUE(&allows_all, &x0, false, &allows_all, NULL, false); 508b4007fd2SMickaël Salaün NMA_FALSE(&x0, &x0, false, &allows_all, NULL, false); 509b4007fd2SMickaël Salaün 510b4007fd2SMickaël Salaün /* 511b4007fd2SMickaël Salaün * Checks that we can only refer a file if no more access could be 512b4007fd2SMickaël Salaün * inherited. 513b4007fd2SMickaël Salaün */ 514b4007fd2SMickaël Salaün NMA_TRUE(&x0, &x0, false, &rx0, NULL, false); 515b4007fd2SMickaël Salaün NMA_TRUE(&rx0, &rx0, false, &rx0, NULL, false); 516b4007fd2SMickaël Salaün NMA_FALSE(&rx0, &rx0, false, &x0, NULL, false); 517b4007fd2SMickaël Salaün NMA_FALSE(&rx0, &rx0, false, &x1, NULL, false); 518b4007fd2SMickaël Salaün 519b4007fd2SMickaël Salaün /* Checks allowed referring with different nested domains. */ 520b4007fd2SMickaël Salaün NMA_TRUE(&x0, &x1, false, &x0, NULL, false); 521b4007fd2SMickaël Salaün NMA_TRUE(&x1, &x0, false, &x0, NULL, false); 522b4007fd2SMickaël Salaün NMA_TRUE(&x0, &x01, false, &x0, NULL, false); 523b4007fd2SMickaël Salaün NMA_TRUE(&x0, &x01, false, &rx0, NULL, false); 524b4007fd2SMickaël Salaün NMA_TRUE(&x01, &x0, false, &x0, NULL, false); 525b4007fd2SMickaël Salaün NMA_TRUE(&x01, &x0, false, &rx0, NULL, false); 526b4007fd2SMickaël Salaün NMA_FALSE(&x01, &x01, false, &x0, NULL, false); 527b4007fd2SMickaël Salaün 528b4007fd2SMickaël Salaün /* Checks that file access rights are also enforced for a directory. */ 529b4007fd2SMickaël Salaün NMA_FALSE(&rx0, &rx0, true, &x0, NULL, false); 530b4007fd2SMickaël Salaün 531b4007fd2SMickaël Salaün /* Checks that directory access rights don't impact file referring... */ 532b4007fd2SMickaël Salaün NMA_TRUE(&mx0, &mx0, false, &x0, NULL, false); 533b4007fd2SMickaël Salaün /* ...but only directory referring. */ 534b4007fd2SMickaël Salaün NMA_FALSE(&mx0, &mx0, true, &x0, NULL, false); 535b4007fd2SMickaël Salaün 536b4007fd2SMickaël Salaün /* Checks directory exchange. */ 537b4007fd2SMickaël Salaün NMA_TRUE(&mx0, &mx0, true, &mx0, &mx0, true); 538b4007fd2SMickaël Salaün NMA_TRUE(&mx0, &mx0, true, &mx0, &x0, true); 539b4007fd2SMickaël Salaün NMA_FALSE(&mx0, &mx0, true, &x0, &mx0, true); 540b4007fd2SMickaël Salaün NMA_FALSE(&mx0, &mx0, true, &x0, &x0, true); 541b4007fd2SMickaël Salaün NMA_FALSE(&mx0, &mx0, true, &x1, &x1, true); 542b4007fd2SMickaël Salaün 543b4007fd2SMickaël Salaün /* Checks file exchange with directory access rights... */ 544b4007fd2SMickaël Salaün NMA_TRUE(&mx0, &mx0, false, &mx0, &mx0, false); 545b4007fd2SMickaël Salaün NMA_TRUE(&mx0, &mx0, false, &mx0, &x0, false); 546b4007fd2SMickaël Salaün NMA_TRUE(&mx0, &mx0, false, &x0, &mx0, false); 547b4007fd2SMickaël Salaün NMA_TRUE(&mx0, &mx0, false, &x0, &x0, false); 548b4007fd2SMickaël Salaün /* ...and with file access rights. */ 549b4007fd2SMickaël Salaün NMA_TRUE(&rx0, &rx0, false, &rx0, &rx0, false); 550b4007fd2SMickaël Salaün NMA_TRUE(&rx0, &rx0, false, &rx0, &x0, false); 551b4007fd2SMickaël Salaün NMA_FALSE(&rx0, &rx0, false, &x0, &rx0, false); 552b4007fd2SMickaël Salaün NMA_FALSE(&rx0, &rx0, false, &x0, &x0, false); 553b4007fd2SMickaël Salaün NMA_FALSE(&rx0, &rx0, false, &x1, &x1, false); 554b4007fd2SMickaël Salaün 555b4007fd2SMickaël Salaün /* 556b4007fd2SMickaël Salaün * Allowing the following requests should not be a security risk 557b4007fd2SMickaël Salaün * because domain 0 denies execute access, and domain 1 is always 558b4007fd2SMickaël Salaün * nested with domain 0. However, adding an exception for this case 559b4007fd2SMickaël Salaün * would mean to check all nested domains to make sure none can get 560b4007fd2SMickaël Salaün * more privileges (e.g. processes only sandboxed by domain 0). 561b4007fd2SMickaël Salaün * Moreover, this behavior (i.e. composition of N domains) could then 562b4007fd2SMickaël Salaün * be inconsistent compared to domain 1's ruleset alone (e.g. it might 563b4007fd2SMickaël Salaün * be denied to link/rename with domain 1's ruleset, whereas it would 564b4007fd2SMickaël Salaün * be allowed if nested on top of domain 0). Another drawback would be 565b4007fd2SMickaël Salaün * to create a cover channel that could enable sandboxed processes to 566b4007fd2SMickaël Salaün * infer most of the filesystem restrictions from their domain. To 567b4007fd2SMickaël Salaün * make it simple, efficient, safe, and more consistent, this case is 568b4007fd2SMickaël Salaün * always denied. 569b4007fd2SMickaël Salaün */ 570b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, false, &x0, NULL, false); 571b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, false, &rx0, NULL, false); 572b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, true, &x0, NULL, false); 573b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, true, &rx0, NULL, false); 574b4007fd2SMickaël Salaün 575b4007fd2SMickaël Salaün /* Checks the same case of exclusive domains with a file... */ 576b4007fd2SMickaël Salaün NMA_TRUE(&x1, &x1, false, &x01, NULL, false); 577b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, false, &x01, &x0, false); 578b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, false, &x01, &x01, false); 579b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, false, &x0, &x0, false); 580b4007fd2SMickaël Salaün /* ...and with a directory. */ 581b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, false, &x0, &x0, true); 582b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, true, &x0, &x0, false); 583b4007fd2SMickaël Salaün NMA_FALSE(&x1, &x1, true, &x0, &x0, true); 584b4007fd2SMickaël Salaün } 585b4007fd2SMickaël Salaün 586b4007fd2SMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 587b4007fd2SMickaël Salaün 588b4007fd2SMickaël Salaün #undef NMA_TRUE 589b4007fd2SMickaël Salaün #undef NMA_FALSE 590b4007fd2SMickaël Salaün 591b91c3e4eSMickaël Salaün /* 592b91c3e4eSMickaël Salaün * Removes @layer_masks accesses that are not requested. 593b91c3e4eSMickaël Salaün * 594b91c3e4eSMickaël Salaün * Returns true if the request is allowed, false otherwise. 595b91c3e4eSMickaël Salaün */ 596da279087SGünther Noack static bool 597b91c3e4eSMickaël Salaün scope_to_request(const access_mask_t access_request, 598b91c3e4eSMickaël Salaün layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]) 599b91c3e4eSMickaël Salaün { 600b91c3e4eSMickaël Salaün const unsigned long access_req = access_request; 601b91c3e4eSMickaël Salaün unsigned long access_bit; 602b91c3e4eSMickaël Salaün 603b91c3e4eSMickaël Salaün if (WARN_ON_ONCE(!layer_masks)) 604b91c3e4eSMickaël Salaün return true; 605b91c3e4eSMickaël Salaün 606b91c3e4eSMickaël Salaün for_each_clear_bit(access_bit, &access_req, ARRAY_SIZE(*layer_masks)) 607b91c3e4eSMickaël Salaün (*layer_masks)[access_bit] = 0; 608b91c3e4eSMickaël Salaün return !memchr_inv(layer_masks, 0, sizeof(*layer_masks)); 609b91c3e4eSMickaël Salaün } 610b91c3e4eSMickaël Salaün 611b4007fd2SMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 612b4007fd2SMickaël Salaün 613b4007fd2SMickaël Salaün static void test_scope_to_request_with_exec_none(struct kunit *const test) 614b4007fd2SMickaël Salaün { 615b4007fd2SMickaël Salaün /* Allows everything. */ 616b4007fd2SMickaël Salaün layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; 617b4007fd2SMickaël Salaün 618b4007fd2SMickaël Salaün /* Checks and scopes with execute. */ 619b4007fd2SMickaël Salaün KUNIT_EXPECT_TRUE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE, 620b4007fd2SMickaël Salaün &layer_masks)); 621b4007fd2SMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 622b4007fd2SMickaël Salaün layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]); 623b4007fd2SMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 624b4007fd2SMickaël Salaün layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]); 625b4007fd2SMickaël Salaün } 626b4007fd2SMickaël Salaün 627b4007fd2SMickaël Salaün static void test_scope_to_request_with_exec_some(struct kunit *const test) 628b4007fd2SMickaël Salaün { 629b4007fd2SMickaël Salaün /* Denies execute and write. */ 630b4007fd2SMickaël Salaün layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = { 631b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0), 632b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(1), 633b4007fd2SMickaël Salaün }; 634b4007fd2SMickaël Salaün 635b4007fd2SMickaël Salaün /* Checks and scopes with execute. */ 636b4007fd2SMickaël Salaün KUNIT_EXPECT_FALSE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE, 637b4007fd2SMickaël Salaün &layer_masks)); 638b4007fd2SMickaël Salaün KUNIT_EXPECT_EQ(test, BIT_ULL(0), 639b4007fd2SMickaël Salaün layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]); 640b4007fd2SMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 641b4007fd2SMickaël Salaün layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]); 642b4007fd2SMickaël Salaün } 643b4007fd2SMickaël Salaün 644b4007fd2SMickaël Salaün static void test_scope_to_request_without_access(struct kunit *const test) 645b4007fd2SMickaël Salaün { 646b4007fd2SMickaël Salaün /* Denies execute and write. */ 647b4007fd2SMickaël Salaün layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = { 648b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0), 649b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(1), 650b4007fd2SMickaël Salaün }; 651b4007fd2SMickaël Salaün 652b4007fd2SMickaël Salaün /* Checks and scopes without access request. */ 653b4007fd2SMickaël Salaün KUNIT_EXPECT_TRUE(test, scope_to_request(0, &layer_masks)); 654b4007fd2SMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 655b4007fd2SMickaël Salaün layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]); 656b4007fd2SMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 657b4007fd2SMickaël Salaün layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]); 658b4007fd2SMickaël Salaün } 659b4007fd2SMickaël Salaün 660b4007fd2SMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 661b4007fd2SMickaël Salaün 662b91c3e4eSMickaël Salaün /* 663b91c3e4eSMickaël Salaün * Returns true if there is at least one access right different than 664b91c3e4eSMickaël Salaün * LANDLOCK_ACCESS_FS_REFER. 665b91c3e4eSMickaël Salaün */ 666da279087SGünther Noack static bool 667b91c3e4eSMickaël Salaün is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS], 6685f2ff33eSMickaël Salaün const access_mask_t access_request) 669cb2c7d1aSMickaël Salaün { 670b91c3e4eSMickaël Salaün unsigned long access_bit; 671b91c3e4eSMickaël Salaün /* LANDLOCK_ACCESS_FS_REFER alone must return -EXDEV. */ 672b91c3e4eSMickaël Salaün const unsigned long access_check = access_request & 673b91c3e4eSMickaël Salaün ~LANDLOCK_ACCESS_FS_REFER; 674cb2c7d1aSMickaël Salaün 675b91c3e4eSMickaël Salaün if (!layer_masks) 676b91c3e4eSMickaël Salaün return false; 677b91c3e4eSMickaël Salaün 678b91c3e4eSMickaël Salaün for_each_set_bit(access_bit, &access_check, ARRAY_SIZE(*layer_masks)) { 679b91c3e4eSMickaël Salaün if ((*layer_masks)[access_bit]) 680b91c3e4eSMickaël Salaün return true; 681b91c3e4eSMickaël Salaün } 682b91c3e4eSMickaël Salaün return false; 683b91c3e4eSMickaël Salaün } 684b91c3e4eSMickaël Salaün 685b4007fd2SMickaël Salaün #define IE_TRUE(...) KUNIT_EXPECT_TRUE(test, is_eacces(__VA_ARGS__)) 686b4007fd2SMickaël Salaün #define IE_FALSE(...) KUNIT_EXPECT_FALSE(test, is_eacces(__VA_ARGS__)) 687b4007fd2SMickaël Salaün 688b4007fd2SMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 689b4007fd2SMickaël Salaün 690b4007fd2SMickaël Salaün static void test_is_eacces_with_none(struct kunit *const test) 691b4007fd2SMickaël Salaün { 692b4007fd2SMickaël Salaün const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; 693b4007fd2SMickaël Salaün 694b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, 0); 695b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER); 696b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE); 697b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE); 698b4007fd2SMickaël Salaün } 699b4007fd2SMickaël Salaün 700b4007fd2SMickaël Salaün static void test_is_eacces_with_refer(struct kunit *const test) 701b4007fd2SMickaël Salaün { 702b4007fd2SMickaël Salaün const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = { 703b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = BIT_ULL(0), 704b4007fd2SMickaël Salaün }; 705b4007fd2SMickaël Salaün 706b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, 0); 707b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER); 708b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE); 709b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE); 710b4007fd2SMickaël Salaün } 711b4007fd2SMickaël Salaün 712b4007fd2SMickaël Salaün static void test_is_eacces_with_write(struct kunit *const test) 713b4007fd2SMickaël Salaün { 714b4007fd2SMickaël Salaün const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = { 715b4007fd2SMickaël Salaün [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(0), 716b4007fd2SMickaël Salaün }; 717b4007fd2SMickaël Salaün 718b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, 0); 719b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER); 720b4007fd2SMickaël Salaün IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE); 721b4007fd2SMickaël Salaün 722b4007fd2SMickaël Salaün IE_TRUE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE); 723b4007fd2SMickaël Salaün } 724b4007fd2SMickaël Salaün 725b4007fd2SMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 726b4007fd2SMickaël Salaün 727b4007fd2SMickaël Salaün #undef IE_TRUE 728b4007fd2SMickaël Salaün #undef IE_FALSE 729b4007fd2SMickaël Salaün 730b91c3e4eSMickaël Salaün /** 731106794c4SGünther Noack * is_access_to_paths_allowed - Check accesses for requests with a common path 732b91c3e4eSMickaël Salaün * 733b91c3e4eSMickaël Salaün * @domain: Domain to check against. 734b91c3e4eSMickaël Salaün * @path: File hierarchy to walk through. 735b91c3e4eSMickaël Salaün * @access_request_parent1: Accesses to check, once @layer_masks_parent1 is 736b91c3e4eSMickaël Salaün * equal to @layer_masks_parent2 (if any). This is tied to the unique 737b91c3e4eSMickaël Salaün * requested path for most actions, or the source in case of a refer action 738b91c3e4eSMickaël Salaün * (i.e. rename or link), or the source and destination in case of 739b91c3e4eSMickaël Salaün * RENAME_EXCHANGE. 740b91c3e4eSMickaël Salaün * @layer_masks_parent1: Pointer to a matrix of layer masks per access 741b91c3e4eSMickaël Salaün * masks, identifying the layers that forbid a specific access. Bits from 742b91c3e4eSMickaël Salaün * this matrix can be unset according to the @path walk. An empty matrix 743b91c3e4eSMickaël Salaün * means that @domain allows all possible Landlock accesses (i.e. not only 744b91c3e4eSMickaël Salaün * those identified by @access_request_parent1). This matrix can 745b91c3e4eSMickaël Salaün * initially refer to domain layer masks and, when the accesses for the 746b91c3e4eSMickaël Salaün * destination and source are the same, to requested layer masks. 747b91c3e4eSMickaël Salaün * @dentry_child1: Dentry to the initial child of the parent1 path. This 748b91c3e4eSMickaël Salaün * pointer must be NULL for non-refer actions (i.e. not link nor rename). 749b91c3e4eSMickaël Salaün * @access_request_parent2: Similar to @access_request_parent1 but for a 750b91c3e4eSMickaël Salaün * request involving a source and a destination. This refers to the 751b91c3e4eSMickaël Salaün * destination, except in case of RENAME_EXCHANGE where it also refers to 752b91c3e4eSMickaël Salaün * the source. Must be set to 0 when using a simple path request. 753b91c3e4eSMickaël Salaün * @layer_masks_parent2: Similar to @layer_masks_parent1 but for a refer 754b91c3e4eSMickaël Salaün * action. This must be NULL otherwise. 755b91c3e4eSMickaël Salaün * @dentry_child2: Dentry to the initial child of the parent2 path. This 756b91c3e4eSMickaël Salaün * pointer is only set for RENAME_EXCHANGE actions and must be NULL 757b91c3e4eSMickaël Salaün * otherwise. 758b91c3e4eSMickaël Salaün * 759b91c3e4eSMickaël Salaün * This helper first checks that the destination has a superset of restrictions 760b91c3e4eSMickaël Salaün * compared to the source (if any) for a common path. Because of 761b91c3e4eSMickaël Salaün * RENAME_EXCHANGE actions, source and destinations may be swapped. It then 762b91c3e4eSMickaël Salaün * checks that the collected accesses and the remaining ones are enough to 763b91c3e4eSMickaël Salaün * allow the request. 764b91c3e4eSMickaël Salaün * 765b91c3e4eSMickaël Salaün * Returns: 766106794c4SGünther Noack * - true if the access request is granted; 767106794c4SGünther Noack * - false otherwise. 768b91c3e4eSMickaël Salaün */ 769106794c4SGünther Noack static bool is_access_to_paths_allowed( 770b91c3e4eSMickaël Salaün const struct landlock_ruleset *const domain, 771b91c3e4eSMickaël Salaün const struct path *const path, 772b91c3e4eSMickaël Salaün const access_mask_t access_request_parent1, 773b91c3e4eSMickaël Salaün layer_mask_t (*const layer_masks_parent1)[LANDLOCK_NUM_ACCESS_FS], 774b91c3e4eSMickaël Salaün const struct dentry *const dentry_child1, 775b91c3e4eSMickaël Salaün const access_mask_t access_request_parent2, 776b91c3e4eSMickaël Salaün layer_mask_t (*const layer_masks_parent2)[LANDLOCK_NUM_ACCESS_FS], 777b91c3e4eSMickaël Salaün const struct dentry *const dentry_child2) 778b91c3e4eSMickaël Salaün { 779b91c3e4eSMickaël Salaün bool allowed_parent1 = false, allowed_parent2 = false, is_dom_check, 780b91c3e4eSMickaël Salaün child1_is_directory = true, child2_is_directory = true; 781b91c3e4eSMickaël Salaün struct path walker_path; 782b91c3e4eSMickaël Salaün access_mask_t access_masked_parent1, access_masked_parent2; 783b91c3e4eSMickaël Salaün layer_mask_t _layer_masks_child1[LANDLOCK_NUM_ACCESS_FS], 784b91c3e4eSMickaël Salaün _layer_masks_child2[LANDLOCK_NUM_ACCESS_FS]; 785b91c3e4eSMickaël Salaün layer_mask_t(*layer_masks_child1)[LANDLOCK_NUM_ACCESS_FS] = NULL, 786b91c3e4eSMickaël Salaün (*layer_masks_child2)[LANDLOCK_NUM_ACCESS_FS] = NULL; 787b91c3e4eSMickaël Salaün 788b91c3e4eSMickaël Salaün if (!access_request_parent1 && !access_request_parent2) 789106794c4SGünther Noack return true; 790cb2c7d1aSMickaël Salaün if (WARN_ON_ONCE(!domain || !path)) 791106794c4SGünther Noack return true; 7929da82b20SMickaël Salaün if (is_nouser_or_private(path->dentry)) 793106794c4SGünther Noack return true; 794b91c3e4eSMickaël Salaün if (WARN_ON_ONCE(domain->num_layers < 1 || !layer_masks_parent1)) 795106794c4SGünther Noack return false; 796cb2c7d1aSMickaël Salaün 797b91c3e4eSMickaël Salaün if (unlikely(layer_masks_parent2)) { 798b91c3e4eSMickaël Salaün if (WARN_ON_ONCE(!dentry_child1)) 799106794c4SGünther Noack return false; 800b91c3e4eSMickaël Salaün /* 801b91c3e4eSMickaël Salaün * For a double request, first check for potential privilege 802b91c3e4eSMickaël Salaün * escalation by looking at domain handled accesses (which are 803b91c3e4eSMickaël Salaün * a superset of the meaningful requested accesses). 804b91c3e4eSMickaël Salaün */ 805b91c3e4eSMickaël Salaün access_masked_parent1 = access_masked_parent2 = 806d7220364SMickaël Salaün get_handled_fs_accesses(domain); 807b91c3e4eSMickaël Salaün is_dom_check = true; 808b91c3e4eSMickaël Salaün } else { 809b91c3e4eSMickaël Salaün if (WARN_ON_ONCE(dentry_child1 || dentry_child2)) 810106794c4SGünther Noack return false; 811b91c3e4eSMickaël Salaün /* For a simple request, only check for requested accesses. */ 812b91c3e4eSMickaël Salaün access_masked_parent1 = access_request_parent1; 813b91c3e4eSMickaël Salaün access_masked_parent2 = access_request_parent2; 814b91c3e4eSMickaël Salaün is_dom_check = false; 815b91c3e4eSMickaël Salaün } 8168ba0005fSMickaël Salaün 817b91c3e4eSMickaël Salaün if (unlikely(dentry_child1)) { 8187a11275cSKonstantin Meskhidze landlock_unmask_layers( 8197a11275cSKonstantin Meskhidze find_rule(domain, dentry_child1), 8200e741011SKonstantin Meskhidze landlock_init_layer_masks( 8210e741011SKonstantin Meskhidze domain, LANDLOCK_MASK_ACCESS_FS, 8227a11275cSKonstantin Meskhidze &_layer_masks_child1, LANDLOCK_KEY_INODE), 8237a11275cSKonstantin Meskhidze &_layer_masks_child1, ARRAY_SIZE(_layer_masks_child1)); 824b91c3e4eSMickaël Salaün layer_masks_child1 = &_layer_masks_child1; 825b91c3e4eSMickaël Salaün child1_is_directory = d_is_dir(dentry_child1); 8268ba0005fSMickaël Salaün } 827b91c3e4eSMickaël Salaün if (unlikely(dentry_child2)) { 8287a11275cSKonstantin Meskhidze landlock_unmask_layers( 8297a11275cSKonstantin Meskhidze find_rule(domain, dentry_child2), 8300e741011SKonstantin Meskhidze landlock_init_layer_masks( 8310e741011SKonstantin Meskhidze domain, LANDLOCK_MASK_ACCESS_FS, 8327a11275cSKonstantin Meskhidze &_layer_masks_child2, LANDLOCK_KEY_INODE), 8337a11275cSKonstantin Meskhidze &_layer_masks_child2, ARRAY_SIZE(_layer_masks_child2)); 834b91c3e4eSMickaël Salaün layer_masks_child2 = &_layer_masks_child2; 835b91c3e4eSMickaël Salaün child2_is_directory = d_is_dir(dentry_child2); 8368ba0005fSMickaël Salaün } 837cb2c7d1aSMickaël Salaün 838cb2c7d1aSMickaël Salaün walker_path = *path; 839cb2c7d1aSMickaël Salaün path_get(&walker_path); 840cb2c7d1aSMickaël Salaün /* 841cb2c7d1aSMickaël Salaün * We need to walk through all the hierarchy to not miss any relevant 842cb2c7d1aSMickaël Salaün * restriction. 843cb2c7d1aSMickaël Salaün */ 844cb2c7d1aSMickaël Salaün while (true) { 845cb2c7d1aSMickaël Salaün struct dentry *parent_dentry; 846b91c3e4eSMickaël Salaün const struct landlock_rule *rule; 847cb2c7d1aSMickaël Salaün 848b91c3e4eSMickaël Salaün /* 849b91c3e4eSMickaël Salaün * If at least all accesses allowed on the destination are 850b91c3e4eSMickaël Salaün * already allowed on the source, respectively if there is at 851b91c3e4eSMickaël Salaün * least as much as restrictions on the destination than on the 852b91c3e4eSMickaël Salaün * source, then we can safely refer files from the source to 853b91c3e4eSMickaël Salaün * the destination without risking a privilege escalation. 854b91c3e4eSMickaël Salaün * This also applies in the case of RENAME_EXCHANGE, which 855b91c3e4eSMickaël Salaün * implies checks on both direction. This is crucial for 856b91c3e4eSMickaël Salaün * standalone multilayered security policies. Furthermore, 857b91c3e4eSMickaël Salaün * this helps avoid policy writers to shoot themselves in the 858b91c3e4eSMickaël Salaün * foot. 859b91c3e4eSMickaël Salaün */ 860b91c3e4eSMickaël Salaün if (unlikely(is_dom_check && 861b91c3e4eSMickaël Salaün no_more_access( 862b91c3e4eSMickaël Salaün layer_masks_parent1, layer_masks_child1, 863b91c3e4eSMickaël Salaün child1_is_directory, layer_masks_parent2, 864b91c3e4eSMickaël Salaün layer_masks_child2, 865b91c3e4eSMickaël Salaün child2_is_directory))) { 866b91c3e4eSMickaël Salaün allowed_parent1 = scope_to_request( 867b91c3e4eSMickaël Salaün access_request_parent1, layer_masks_parent1); 868b91c3e4eSMickaël Salaün allowed_parent2 = scope_to_request( 869b91c3e4eSMickaël Salaün access_request_parent2, layer_masks_parent2); 870b91c3e4eSMickaël Salaün 871b91c3e4eSMickaël Salaün /* Stops when all accesses are granted. */ 872b91c3e4eSMickaël Salaün if (allowed_parent1 && allowed_parent2) 873b91c3e4eSMickaël Salaün break; 874b91c3e4eSMickaël Salaün 875b91c3e4eSMickaël Salaün /* 876b91c3e4eSMickaël Salaün * Now, downgrades the remaining checks from domain 877b91c3e4eSMickaël Salaün * handled accesses to requested accesses. 878b91c3e4eSMickaël Salaün */ 879b91c3e4eSMickaël Salaün is_dom_check = false; 880b91c3e4eSMickaël Salaün access_masked_parent1 = access_request_parent1; 881b91c3e4eSMickaël Salaün access_masked_parent2 = access_request_parent2; 882b91c3e4eSMickaël Salaün } 883b91c3e4eSMickaël Salaün 884b91c3e4eSMickaël Salaün rule = find_rule(domain, walker_path.dentry); 8850e741011SKonstantin Meskhidze allowed_parent1 = landlock_unmask_layers( 8867a11275cSKonstantin Meskhidze rule, access_masked_parent1, layer_masks_parent1, 8877a11275cSKonstantin Meskhidze ARRAY_SIZE(*layer_masks_parent1)); 8880e741011SKonstantin Meskhidze allowed_parent2 = landlock_unmask_layers( 8897a11275cSKonstantin Meskhidze rule, access_masked_parent2, layer_masks_parent2, 8907a11275cSKonstantin Meskhidze ARRAY_SIZE(*layer_masks_parent2)); 891b91c3e4eSMickaël Salaün 892cb2c7d1aSMickaël Salaün /* Stops when a rule from each layer grants access. */ 893b91c3e4eSMickaël Salaün if (allowed_parent1 && allowed_parent2) 894cb2c7d1aSMickaël Salaün break; 895cb2c7d1aSMickaël Salaün jump_up: 896cb2c7d1aSMickaël Salaün if (walker_path.dentry == walker_path.mnt->mnt_root) { 897cb2c7d1aSMickaël Salaün if (follow_up(&walker_path)) { 898cb2c7d1aSMickaël Salaün /* Ignores hidden mount points. */ 899cb2c7d1aSMickaël Salaün goto jump_up; 900cb2c7d1aSMickaël Salaün } else { 901cb2c7d1aSMickaël Salaün /* 902cb2c7d1aSMickaël Salaün * Stops at the real root. Denies access 903cb2c7d1aSMickaël Salaün * because not all layers have granted access. 904cb2c7d1aSMickaël Salaün */ 905cb2c7d1aSMickaël Salaün break; 906cb2c7d1aSMickaël Salaün } 907cb2c7d1aSMickaël Salaün } 908cb2c7d1aSMickaël Salaün if (unlikely(IS_ROOT(walker_path.dentry))) { 909cb2c7d1aSMickaël Salaün /* 910cb2c7d1aSMickaël Salaün * Stops at disconnected root directories. Only allows 911cb2c7d1aSMickaël Salaün * access to internal filesystems (e.g. nsfs, which is 912cb2c7d1aSMickaël Salaün * reachable through /proc/<pid>/ns/<namespace>). 913cb2c7d1aSMickaël Salaün */ 914b91c3e4eSMickaël Salaün allowed_parent1 = allowed_parent2 = 915b91c3e4eSMickaël Salaün !!(walker_path.mnt->mnt_flags & MNT_INTERNAL); 916cb2c7d1aSMickaël Salaün break; 917cb2c7d1aSMickaël Salaün } 918cb2c7d1aSMickaël Salaün parent_dentry = dget_parent(walker_path.dentry); 919cb2c7d1aSMickaël Salaün dput(walker_path.dentry); 920cb2c7d1aSMickaël Salaün walker_path.dentry = parent_dentry; 921cb2c7d1aSMickaël Salaün } 922cb2c7d1aSMickaël Salaün path_put(&walker_path); 923b91c3e4eSMickaël Salaün 924106794c4SGünther Noack return allowed_parent1 && allowed_parent2; 925b91c3e4eSMickaël Salaün } 926b91c3e4eSMickaël Salaün 927da279087SGünther Noack static int check_access_path(const struct landlock_ruleset *const domain, 928b91c3e4eSMickaël Salaün const struct path *const path, 929b91c3e4eSMickaël Salaün access_mask_t access_request) 930b91c3e4eSMickaël Salaün { 931b91c3e4eSMickaël Salaün layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; 932b91c3e4eSMickaël Salaün 9337a11275cSKonstantin Meskhidze access_request = landlock_init_layer_masks( 9347a11275cSKonstantin Meskhidze domain, access_request, &layer_masks, LANDLOCK_KEY_INODE); 935106794c4SGünther Noack if (is_access_to_paths_allowed(domain, path, access_request, 936106794c4SGünther Noack &layer_masks, NULL, 0, NULL, NULL)) 937106794c4SGünther Noack return 0; 938106794c4SGünther Noack return -EACCES; 939cb2c7d1aSMickaël Salaün } 940cb2c7d1aSMickaël Salaün 9418fd80721SGünther Noack static int current_check_access_path(const struct path *const path, 9425f2ff33eSMickaël Salaün const access_mask_t access_request) 943cb2c7d1aSMickaël Salaün { 944d7220364SMickaël Salaün const struct landlock_ruleset *const dom = get_current_fs_domain(); 945cb2c7d1aSMickaël Salaün 946cb2c7d1aSMickaël Salaün if (!dom) 947cb2c7d1aSMickaël Salaün return 0; 948cb2c7d1aSMickaël Salaün return check_access_path(dom, path, access_request); 949cb2c7d1aSMickaël Salaün } 950cb2c7d1aSMickaël Salaün 951da279087SGünther Noack static access_mask_t get_mode_access(const umode_t mode) 9529da82b20SMickaël Salaün { 9539da82b20SMickaël Salaün switch (mode & S_IFMT) { 9549da82b20SMickaël Salaün case S_IFLNK: 9559da82b20SMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_SYM; 9569da82b20SMickaël Salaün case 0: 9579da82b20SMickaël Salaün /* A zero mode translates to S_IFREG. */ 9589da82b20SMickaël Salaün case S_IFREG: 9599da82b20SMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_REG; 9609da82b20SMickaël Salaün case S_IFDIR: 9619da82b20SMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_DIR; 9629da82b20SMickaël Salaün case S_IFCHR: 9639da82b20SMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_CHAR; 9649da82b20SMickaël Salaün case S_IFBLK: 9659da82b20SMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_BLOCK; 9669da82b20SMickaël Salaün case S_IFIFO: 9679da82b20SMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_FIFO; 9689da82b20SMickaël Salaün case S_IFSOCK: 9699da82b20SMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_SOCK; 9709da82b20SMickaël Salaün default: 9719da82b20SMickaël Salaün WARN_ON_ONCE(1); 9729da82b20SMickaël Salaün return 0; 9739da82b20SMickaël Salaün } 9749da82b20SMickaël Salaün } 9759da82b20SMickaël Salaün 976da279087SGünther Noack static access_mask_t maybe_remove(const struct dentry *const dentry) 9779da82b20SMickaël Salaün { 9789da82b20SMickaël Salaün if (d_is_negative(dentry)) 9799da82b20SMickaël Salaün return 0; 9809da82b20SMickaël Salaün return d_is_dir(dentry) ? LANDLOCK_ACCESS_FS_REMOVE_DIR : 9819da82b20SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE; 9829da82b20SMickaël Salaün } 9839da82b20SMickaël Salaün 984b91c3e4eSMickaël Salaün /** 985b91c3e4eSMickaël Salaün * collect_domain_accesses - Walk through a file path and collect accesses 986b91c3e4eSMickaël Salaün * 987b91c3e4eSMickaël Salaün * @domain: Domain to check against. 988b91c3e4eSMickaël Salaün * @mnt_root: Last directory to check. 989b91c3e4eSMickaël Salaün * @dir: Directory to start the walk from. 990b91c3e4eSMickaël Salaün * @layer_masks_dom: Where to store the collected accesses. 991b91c3e4eSMickaël Salaün * 992b91c3e4eSMickaël Salaün * This helper is useful to begin a path walk from the @dir directory to a 993b91c3e4eSMickaël Salaün * @mnt_root directory used as a mount point. This mount point is the common 994b91c3e4eSMickaël Salaün * ancestor between the source and the destination of a renamed and linked 995b91c3e4eSMickaël Salaün * file. While walking from @dir to @mnt_root, we record all the domain's 996b91c3e4eSMickaël Salaün * allowed accesses in @layer_masks_dom. 997b91c3e4eSMickaël Salaün * 998106794c4SGünther Noack * This is similar to is_access_to_paths_allowed() but much simpler because it 999106794c4SGünther Noack * only handles walking on the same mount point and only checks one set of 1000106794c4SGünther Noack * accesses. 1001b91c3e4eSMickaël Salaün * 1002b91c3e4eSMickaël Salaün * Returns: 1003b91c3e4eSMickaël Salaün * - true if all the domain access rights are allowed for @dir; 1004b91c3e4eSMickaël Salaün * - false if the walk reached @mnt_root. 1005b91c3e4eSMickaël Salaün */ 1006b91c3e4eSMickaël Salaün static bool collect_domain_accesses( 1007b91c3e4eSMickaël Salaün const struct landlock_ruleset *const domain, 1008b91c3e4eSMickaël Salaün const struct dentry *const mnt_root, struct dentry *dir, 1009b91c3e4eSMickaël Salaün layer_mask_t (*const layer_masks_dom)[LANDLOCK_NUM_ACCESS_FS]) 1010b91c3e4eSMickaël Salaün { 1011b91c3e4eSMickaël Salaün unsigned long access_dom; 1012b91c3e4eSMickaël Salaün bool ret = false; 1013b91c3e4eSMickaël Salaün 1014b91c3e4eSMickaël Salaün if (WARN_ON_ONCE(!domain || !mnt_root || !dir || !layer_masks_dom)) 1015b91c3e4eSMickaël Salaün return true; 1016b91c3e4eSMickaël Salaün if (is_nouser_or_private(dir)) 1017b91c3e4eSMickaël Salaün return true; 1018b91c3e4eSMickaël Salaün 10190e741011SKonstantin Meskhidze access_dom = landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS, 10207a11275cSKonstantin Meskhidze layer_masks_dom, 10217a11275cSKonstantin Meskhidze LANDLOCK_KEY_INODE); 1022b91c3e4eSMickaël Salaün 1023b91c3e4eSMickaël Salaün dget(dir); 1024b91c3e4eSMickaël Salaün while (true) { 1025b91c3e4eSMickaël Salaün struct dentry *parent_dentry; 1026b91c3e4eSMickaël Salaün 1027b91c3e4eSMickaël Salaün /* Gets all layers allowing all domain accesses. */ 10280e741011SKonstantin Meskhidze if (landlock_unmask_layers(find_rule(domain, dir), access_dom, 10297a11275cSKonstantin Meskhidze layer_masks_dom, 10307a11275cSKonstantin Meskhidze ARRAY_SIZE(*layer_masks_dom))) { 1031b91c3e4eSMickaël Salaün /* 1032b91c3e4eSMickaël Salaün * Stops when all handled accesses are allowed by at 1033b91c3e4eSMickaël Salaün * least one rule in each layer. 1034b91c3e4eSMickaël Salaün */ 1035b91c3e4eSMickaël Salaün ret = true; 1036b91c3e4eSMickaël Salaün break; 1037b91c3e4eSMickaël Salaün } 1038b91c3e4eSMickaël Salaün 1039b91c3e4eSMickaël Salaün /* We should not reach a root other than @mnt_root. */ 1040b91c3e4eSMickaël Salaün if (dir == mnt_root || WARN_ON_ONCE(IS_ROOT(dir))) 1041b91c3e4eSMickaël Salaün break; 1042b91c3e4eSMickaël Salaün 1043b91c3e4eSMickaël Salaün parent_dentry = dget_parent(dir); 1044b91c3e4eSMickaël Salaün dput(dir); 1045b91c3e4eSMickaël Salaün dir = parent_dentry; 1046b91c3e4eSMickaël Salaün } 1047b91c3e4eSMickaël Salaün dput(dir); 1048b91c3e4eSMickaël Salaün return ret; 1049b91c3e4eSMickaël Salaün } 1050b91c3e4eSMickaël Salaün 1051b91c3e4eSMickaël Salaün /** 1052b91c3e4eSMickaël Salaün * current_check_refer_path - Check if a rename or link action is allowed 1053b91c3e4eSMickaël Salaün * 1054b91c3e4eSMickaël Salaün * @old_dentry: File or directory requested to be moved or linked. 1055b91c3e4eSMickaël Salaün * @new_dir: Destination parent directory. 1056b91c3e4eSMickaël Salaün * @new_dentry: Destination file or directory. 1057b91c3e4eSMickaël Salaün * @removable: Sets to true if it is a rename operation. 1058b91c3e4eSMickaël Salaün * @exchange: Sets to true if it is a rename operation with RENAME_EXCHANGE. 1059b91c3e4eSMickaël Salaün * 1060b91c3e4eSMickaël Salaün * Because of its unprivileged constraints, Landlock relies on file hierarchies 1061b91c3e4eSMickaël Salaün * (and not only inodes) to tie access rights to files. Being able to link or 1062b91c3e4eSMickaël Salaün * rename a file hierarchy brings some challenges. Indeed, moving or linking a 1063b91c3e4eSMickaël Salaün * file (i.e. creating a new reference to an inode) can have an impact on the 1064b91c3e4eSMickaël Salaün * actions allowed for a set of files if it would change its parent directory 1065b91c3e4eSMickaël Salaün * (i.e. reparenting). 1066b91c3e4eSMickaël Salaün * 1067b91c3e4eSMickaël Salaün * To avoid trivial access right bypasses, Landlock first checks if the file or 1068b91c3e4eSMickaël Salaün * directory requested to be moved would gain new access rights inherited from 1069b91c3e4eSMickaël Salaün * its new hierarchy. Before returning any error, Landlock then checks that 1070b91c3e4eSMickaël Salaün * the parent source hierarchy and the destination hierarchy would allow the 1071b91c3e4eSMickaël Salaün * link or rename action. If it is not the case, an error with EACCES is 1072b91c3e4eSMickaël Salaün * returned to inform user space that there is no way to remove or create the 1073b91c3e4eSMickaël Salaün * requested source file type. If it should be allowed but the new inherited 1074b91c3e4eSMickaël Salaün * access rights would be greater than the source access rights, then the 1075b91c3e4eSMickaël Salaün * kernel returns an error with EXDEV. Prioritizing EACCES over EXDEV enables 1076b91c3e4eSMickaël Salaün * user space to abort the whole operation if there is no way to do it, or to 1077b91c3e4eSMickaël Salaün * manually copy the source to the destination if this remains allowed, e.g. 1078b91c3e4eSMickaël Salaün * because file creation is allowed on the destination directory but not direct 1079b91c3e4eSMickaël Salaün * linking. 1080b91c3e4eSMickaël Salaün * 1081b91c3e4eSMickaël Salaün * To achieve this goal, the kernel needs to compare two file hierarchies: the 1082b91c3e4eSMickaël Salaün * one identifying the source file or directory (including itself), and the 1083b91c3e4eSMickaël Salaün * destination one. This can be seen as a multilayer partial ordering problem. 1084b91c3e4eSMickaël Salaün * The kernel walks through these paths and collects in a matrix the access 1085b91c3e4eSMickaël Salaün * rights that are denied per layer. These matrices are then compared to see 1086b91c3e4eSMickaël Salaün * if the destination one has more (or the same) restrictions as the source 1087b91c3e4eSMickaël Salaün * one. If this is the case, the requested action will not return EXDEV, which 1088b91c3e4eSMickaël Salaün * doesn't mean the action is allowed. The parent hierarchy of the source 1089b91c3e4eSMickaël Salaün * (i.e. parent directory), and the destination hierarchy must also be checked 1090b91c3e4eSMickaël Salaün * to verify that they explicitly allow such action (i.e. referencing, 1091b91c3e4eSMickaël Salaün * creation and potentially removal rights). The kernel implementation is then 1092b91c3e4eSMickaël Salaün * required to rely on potentially four matrices of access rights: one for the 1093b91c3e4eSMickaël Salaün * source file or directory (i.e. the child), a potentially other one for the 1094b91c3e4eSMickaël Salaün * other source/destination (in case of RENAME_EXCHANGE), one for the source 1095b91c3e4eSMickaël Salaün * parent hierarchy and a last one for the destination hierarchy. These 1096b91c3e4eSMickaël Salaün * ephemeral matrices take some space on the stack, which limits the number of 1097b91c3e4eSMickaël Salaün * layers to a deemed reasonable number: 16. 1098b91c3e4eSMickaël Salaün * 1099b91c3e4eSMickaël Salaün * Returns: 1100b91c3e4eSMickaël Salaün * - 0 if access is allowed; 1101b91c3e4eSMickaël Salaün * - -EXDEV if @old_dentry would inherit new access rights from @new_dir; 1102b91c3e4eSMickaël Salaün * - -EACCES if file removal or creation is denied. 1103b91c3e4eSMickaël Salaün */ 1104b91c3e4eSMickaël Salaün static int current_check_refer_path(struct dentry *const old_dentry, 1105b91c3e4eSMickaël Salaün const struct path *const new_dir, 1106b91c3e4eSMickaël Salaün struct dentry *const new_dentry, 1107b91c3e4eSMickaël Salaün const bool removable, const bool exchange) 1108b91c3e4eSMickaël Salaün { 1109d7220364SMickaël Salaün const struct landlock_ruleset *const dom = get_current_fs_domain(); 1110b91c3e4eSMickaël Salaün bool allow_parent1, allow_parent2; 1111b91c3e4eSMickaël Salaün access_mask_t access_request_parent1, access_request_parent2; 1112b91c3e4eSMickaël Salaün struct path mnt_dir; 111388da52ccSMickaël Salaün struct dentry *old_parent; 1114d9818b3eSMickaël Salaün layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {}, 1115d9818b3eSMickaël Salaün layer_masks_parent2[LANDLOCK_NUM_ACCESS_FS] = {}; 1116b91c3e4eSMickaël Salaün 1117b91c3e4eSMickaël Salaün if (!dom) 1118b91c3e4eSMickaël Salaün return 0; 1119b91c3e4eSMickaël Salaün if (WARN_ON_ONCE(dom->num_layers < 1)) 1120b91c3e4eSMickaël Salaün return -EACCES; 1121b91c3e4eSMickaël Salaün if (unlikely(d_is_negative(old_dentry))) 1122b91c3e4eSMickaël Salaün return -ENOENT; 1123b91c3e4eSMickaël Salaün if (exchange) { 1124b91c3e4eSMickaël Salaün if (unlikely(d_is_negative(new_dentry))) 1125b91c3e4eSMickaël Salaün return -ENOENT; 1126b91c3e4eSMickaël Salaün access_request_parent1 = 1127b91c3e4eSMickaël Salaün get_mode_access(d_backing_inode(new_dentry)->i_mode); 1128b91c3e4eSMickaël Salaün } else { 1129b91c3e4eSMickaël Salaün access_request_parent1 = 0; 1130b91c3e4eSMickaël Salaün } 1131b91c3e4eSMickaël Salaün access_request_parent2 = 1132b91c3e4eSMickaël Salaün get_mode_access(d_backing_inode(old_dentry)->i_mode); 1133b91c3e4eSMickaël Salaün if (removable) { 1134b91c3e4eSMickaël Salaün access_request_parent1 |= maybe_remove(old_dentry); 1135b91c3e4eSMickaël Salaün access_request_parent2 |= maybe_remove(new_dentry); 1136b91c3e4eSMickaël Salaün } 1137b91c3e4eSMickaël Salaün 1138b91c3e4eSMickaël Salaün /* The mount points are the same for old and new paths, cf. EXDEV. */ 1139b91c3e4eSMickaël Salaün if (old_dentry->d_parent == new_dir->dentry) { 1140b91c3e4eSMickaël Salaün /* 1141b91c3e4eSMickaël Salaün * The LANDLOCK_ACCESS_FS_REFER access right is not required 1142b91c3e4eSMickaël Salaün * for same-directory referer (i.e. no reparenting). 1143b91c3e4eSMickaël Salaün */ 11440e741011SKonstantin Meskhidze access_request_parent1 = landlock_init_layer_masks( 1145b91c3e4eSMickaël Salaün dom, access_request_parent1 | access_request_parent2, 11467a11275cSKonstantin Meskhidze &layer_masks_parent1, LANDLOCK_KEY_INODE); 1147106794c4SGünther Noack if (is_access_to_paths_allowed( 1148106794c4SGünther Noack dom, new_dir, access_request_parent1, 1149106794c4SGünther Noack &layer_masks_parent1, NULL, 0, NULL, NULL)) 1150106794c4SGünther Noack return 0; 1151106794c4SGünther Noack return -EACCES; 1152b91c3e4eSMickaël Salaün } 1153b91c3e4eSMickaël Salaün 1154b91c3e4eSMickaël Salaün access_request_parent1 |= LANDLOCK_ACCESS_FS_REFER; 1155b91c3e4eSMickaël Salaün access_request_parent2 |= LANDLOCK_ACCESS_FS_REFER; 1156b91c3e4eSMickaël Salaün 1157b91c3e4eSMickaël Salaün /* Saves the common mount point. */ 1158b91c3e4eSMickaël Salaün mnt_dir.mnt = new_dir->mnt; 1159b91c3e4eSMickaël Salaün mnt_dir.dentry = new_dir->mnt->mnt_root; 1160b91c3e4eSMickaël Salaün 116188da52ccSMickaël Salaün /* 116288da52ccSMickaël Salaün * old_dentry may be the root of the common mount point and 116388da52ccSMickaël Salaün * !IS_ROOT(old_dentry) at the same time (e.g. with open_tree() and 116488da52ccSMickaël Salaün * OPEN_TREE_CLONE). We do not need to call dget(old_parent) because 116588da52ccSMickaël Salaün * we keep a reference to old_dentry. 116688da52ccSMickaël Salaün */ 116788da52ccSMickaël Salaün old_parent = (old_dentry == mnt_dir.dentry) ? old_dentry : 116888da52ccSMickaël Salaün old_dentry->d_parent; 116988da52ccSMickaël Salaün 1170b91c3e4eSMickaël Salaün /* new_dir->dentry is equal to new_dentry->d_parent */ 117188da52ccSMickaël Salaün allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry, old_parent, 1172b91c3e4eSMickaël Salaün &layer_masks_parent1); 1173b91c3e4eSMickaël Salaün allow_parent2 = collect_domain_accesses( 1174b91c3e4eSMickaël Salaün dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2); 1175b91c3e4eSMickaël Salaün 1176b91c3e4eSMickaël Salaün if (allow_parent1 && allow_parent2) 1177b91c3e4eSMickaël Salaün return 0; 1178b91c3e4eSMickaël Salaün 1179b91c3e4eSMickaël Salaün /* 1180b91c3e4eSMickaël Salaün * To be able to compare source and destination domain access rights, 1181b91c3e4eSMickaël Salaün * take into account the @old_dentry access rights aggregated with its 1182b91c3e4eSMickaël Salaün * parent access rights. This will be useful to compare with the 1183b91c3e4eSMickaël Salaün * destination parent access rights. 1184b91c3e4eSMickaël Salaün */ 1185106794c4SGünther Noack if (is_access_to_paths_allowed( 1186106794c4SGünther Noack dom, &mnt_dir, access_request_parent1, &layer_masks_parent1, 1187106794c4SGünther Noack old_dentry, access_request_parent2, &layer_masks_parent2, 1188106794c4SGünther Noack exchange ? new_dentry : NULL)) 1189106794c4SGünther Noack return 0; 1190106794c4SGünther Noack 1191106794c4SGünther Noack /* 1192106794c4SGünther Noack * This prioritizes EACCES over EXDEV for all actions, including 1193106794c4SGünther Noack * renames with RENAME_EXCHANGE. 1194106794c4SGünther Noack */ 1195106794c4SGünther Noack if (likely(is_eacces(&layer_masks_parent1, access_request_parent1) || 1196106794c4SGünther Noack is_eacces(&layer_masks_parent2, access_request_parent2))) 1197106794c4SGünther Noack return -EACCES; 1198106794c4SGünther Noack 1199106794c4SGünther Noack /* 1200106794c4SGünther Noack * Gracefully forbids reparenting if the destination directory 1201106794c4SGünther Noack * hierarchy is not a superset of restrictions of the source directory 1202106794c4SGünther Noack * hierarchy, or if LANDLOCK_ACCESS_FS_REFER is not allowed by the 1203106794c4SGünther Noack * source or the destination. 1204106794c4SGünther Noack */ 1205106794c4SGünther Noack return -EXDEV; 1206b91c3e4eSMickaël Salaün } 1207b91c3e4eSMickaël Salaün 1208cb2c7d1aSMickaël Salaün /* Inode hooks */ 1209cb2c7d1aSMickaël Salaün 121063dff3e4SPaul Moore static void hook_inode_free_security_rcu(void *inode_security) 1211cb2c7d1aSMickaël Salaün { 121263dff3e4SPaul Moore struct landlock_inode_security *inode_sec; 121363dff3e4SPaul Moore 1214cb2c7d1aSMickaël Salaün /* 1215cb2c7d1aSMickaël Salaün * All inodes must already have been untied from their object by 1216cb2c7d1aSMickaël Salaün * release_inode() or hook_sb_delete(). 1217cb2c7d1aSMickaël Salaün */ 121863dff3e4SPaul Moore inode_sec = inode_security + landlock_blob_sizes.lbs_inode; 121963dff3e4SPaul Moore WARN_ON_ONCE(inode_sec->object); 1220cb2c7d1aSMickaël Salaün } 1221cb2c7d1aSMickaël Salaün 1222cb2c7d1aSMickaël Salaün /* Super-block hooks */ 1223cb2c7d1aSMickaël Salaün 1224cb2c7d1aSMickaël Salaün /* 1225cb2c7d1aSMickaël Salaün * Release the inodes used in a security policy. 1226cb2c7d1aSMickaël Salaün * 1227cb2c7d1aSMickaël Salaün * Cf. fsnotify_unmount_inodes() and invalidate_inodes() 1228cb2c7d1aSMickaël Salaün */ 1229cb2c7d1aSMickaël Salaün static void hook_sb_delete(struct super_block *const sb) 1230cb2c7d1aSMickaël Salaün { 1231cb2c7d1aSMickaël Salaün struct inode *inode, *prev_inode = NULL; 1232cb2c7d1aSMickaël Salaün 1233cb2c7d1aSMickaël Salaün if (!landlock_initialized) 1234cb2c7d1aSMickaël Salaün return; 1235cb2c7d1aSMickaël Salaün 1236cb2c7d1aSMickaël Salaün spin_lock(&sb->s_inode_list_lock); 1237cb2c7d1aSMickaël Salaün list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { 1238cb2c7d1aSMickaël Salaün struct landlock_object *object; 1239cb2c7d1aSMickaël Salaün 1240cb2c7d1aSMickaël Salaün /* Only handles referenced inodes. */ 1241cb2c7d1aSMickaël Salaün if (!atomic_read(&inode->i_count)) 1242cb2c7d1aSMickaël Salaün continue; 1243cb2c7d1aSMickaël Salaün 1244cb2c7d1aSMickaël Salaün /* 1245cb2c7d1aSMickaël Salaün * Protects against concurrent modification of inode (e.g. 1246cb2c7d1aSMickaël Salaün * from get_inode_object()). 1247cb2c7d1aSMickaël Salaün */ 1248cb2c7d1aSMickaël Salaün spin_lock(&inode->i_lock); 1249cb2c7d1aSMickaël Salaün /* 1250cb2c7d1aSMickaël Salaün * Checks I_FREEING and I_WILL_FREE to protect against a race 1251cb2c7d1aSMickaël Salaün * condition when release_inode() just called iput(), which 1252cb2c7d1aSMickaël Salaün * could lead to a NULL dereference of inode->security or a 1253cb2c7d1aSMickaël Salaün * second call to iput() for the same Landlock object. Also 1254cb2c7d1aSMickaël Salaün * checks I_NEW because such inode cannot be tied to an object. 1255cb2c7d1aSMickaël Salaün */ 1256cb2c7d1aSMickaël Salaün if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) { 1257cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 1258cb2c7d1aSMickaël Salaün continue; 1259cb2c7d1aSMickaël Salaün } 1260cb2c7d1aSMickaël Salaün 1261cb2c7d1aSMickaël Salaün rcu_read_lock(); 1262cb2c7d1aSMickaël Salaün object = rcu_dereference(landlock_inode(inode)->object); 1263cb2c7d1aSMickaël Salaün if (!object) { 1264cb2c7d1aSMickaël Salaün rcu_read_unlock(); 1265cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 1266cb2c7d1aSMickaël Salaün continue; 1267cb2c7d1aSMickaël Salaün } 1268cb2c7d1aSMickaël Salaün /* Keeps a reference to this inode until the next loop walk. */ 1269cb2c7d1aSMickaël Salaün __iget(inode); 1270cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 1271cb2c7d1aSMickaël Salaün 1272cb2c7d1aSMickaël Salaün /* 1273cb2c7d1aSMickaël Salaün * If there is no concurrent release_inode() ongoing, then we 1274cb2c7d1aSMickaël Salaün * are in charge of calling iput() on this inode, otherwise we 1275cb2c7d1aSMickaël Salaün * will just wait for it to finish. 1276cb2c7d1aSMickaël Salaün */ 1277cb2c7d1aSMickaël Salaün spin_lock(&object->lock); 1278cb2c7d1aSMickaël Salaün if (object->underobj == inode) { 1279cb2c7d1aSMickaël Salaün object->underobj = NULL; 1280cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 1281cb2c7d1aSMickaël Salaün rcu_read_unlock(); 1282cb2c7d1aSMickaël Salaün 1283cb2c7d1aSMickaël Salaün /* 1284cb2c7d1aSMickaël Salaün * Because object->underobj was not NULL, 1285cb2c7d1aSMickaël Salaün * release_inode() and get_inode_object() guarantee 1286cb2c7d1aSMickaël Salaün * that it is safe to reset 1287cb2c7d1aSMickaël Salaün * landlock_inode(inode)->object while it is not NULL. 1288cb2c7d1aSMickaël Salaün * It is therefore not necessary to lock inode->i_lock. 1289cb2c7d1aSMickaël Salaün */ 1290cb2c7d1aSMickaël Salaün rcu_assign_pointer(landlock_inode(inode)->object, NULL); 1291cb2c7d1aSMickaël Salaün /* 1292cb2c7d1aSMickaël Salaün * At this point, we own the ihold() reference that was 1293cb2c7d1aSMickaël Salaün * originally set up by get_inode_object() and the 1294cb2c7d1aSMickaël Salaün * __iget() reference that we just set in this loop 1295cb2c7d1aSMickaël Salaün * walk. Therefore the following call to iput() will 1296cb2c7d1aSMickaël Salaün * not sleep nor drop the inode because there is now at 1297cb2c7d1aSMickaël Salaün * least two references to it. 1298cb2c7d1aSMickaël Salaün */ 1299cb2c7d1aSMickaël Salaün iput(inode); 1300cb2c7d1aSMickaël Salaün } else { 1301cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 1302cb2c7d1aSMickaël Salaün rcu_read_unlock(); 1303cb2c7d1aSMickaël Salaün } 1304cb2c7d1aSMickaël Salaün 1305cb2c7d1aSMickaël Salaün if (prev_inode) { 1306cb2c7d1aSMickaël Salaün /* 1307cb2c7d1aSMickaël Salaün * At this point, we still own the __iget() reference 1308cb2c7d1aSMickaël Salaün * that we just set in this loop walk. Therefore we 1309cb2c7d1aSMickaël Salaün * can drop the list lock and know that the inode won't 1310cb2c7d1aSMickaël Salaün * disappear from under us until the next loop walk. 1311cb2c7d1aSMickaël Salaün */ 1312cb2c7d1aSMickaël Salaün spin_unlock(&sb->s_inode_list_lock); 1313cb2c7d1aSMickaël Salaün /* 1314cb2c7d1aSMickaël Salaün * We can now actually put the inode reference from the 1315cb2c7d1aSMickaël Salaün * previous loop walk, which is not needed anymore. 1316cb2c7d1aSMickaël Salaün */ 1317cb2c7d1aSMickaël Salaün iput(prev_inode); 1318cb2c7d1aSMickaël Salaün cond_resched(); 1319cb2c7d1aSMickaël Salaün spin_lock(&sb->s_inode_list_lock); 1320cb2c7d1aSMickaël Salaün } 1321cb2c7d1aSMickaël Salaün prev_inode = inode; 1322cb2c7d1aSMickaël Salaün } 1323cb2c7d1aSMickaël Salaün spin_unlock(&sb->s_inode_list_lock); 1324cb2c7d1aSMickaël Salaün 1325cb2c7d1aSMickaël Salaün /* Puts the inode reference from the last loop walk, if any. */ 1326cb2c7d1aSMickaël Salaün if (prev_inode) 1327cb2c7d1aSMickaël Salaün iput(prev_inode); 1328cb2c7d1aSMickaël Salaün /* Waits for pending iput() in release_inode(). */ 132906a1c40aSMickaël Salaün wait_var_event(&landlock_superblock(sb)->inode_refs, 133006a1c40aSMickaël Salaün !atomic_long_read(&landlock_superblock(sb)->inode_refs)); 1331cb2c7d1aSMickaël Salaün } 1332cb2c7d1aSMickaël Salaün 1333cb2c7d1aSMickaël Salaün /* 1334cb2c7d1aSMickaël Salaün * Because a Landlock security policy is defined according to the filesystem 1335cb2c7d1aSMickaël Salaün * topology (i.e. the mount namespace), changing it may grant access to files 1336cb2c7d1aSMickaël Salaün * not previously allowed. 1337cb2c7d1aSMickaël Salaün * 1338cb2c7d1aSMickaël Salaün * To make it simple, deny any filesystem topology modification by landlocked 1339cb2c7d1aSMickaël Salaün * processes. Non-landlocked processes may still change the namespace of a 1340cb2c7d1aSMickaël Salaün * landlocked process, but this kind of threat must be handled by a system-wide 1341cb2c7d1aSMickaël Salaün * access-control security policy. 1342cb2c7d1aSMickaël Salaün * 1343cb2c7d1aSMickaël Salaün * This could be lifted in the future if Landlock can safely handle mount 1344cb2c7d1aSMickaël Salaün * namespace updates requested by a landlocked process. Indeed, we could 1345cb2c7d1aSMickaël Salaün * update the current domain (which is currently read-only) by taking into 1346cb2c7d1aSMickaël Salaün * account the accesses of the source and the destination of a new mount point. 1347cb2c7d1aSMickaël Salaün * However, it would also require to make all the child domains dynamically 1348cb2c7d1aSMickaël Salaün * inherit these new constraints. Anyway, for backward compatibility reasons, 1349cb2c7d1aSMickaël Salaün * a dedicated user space option would be required (e.g. as a ruleset flag). 1350cb2c7d1aSMickaël Salaün */ 1351cb2c7d1aSMickaël Salaün static int hook_sb_mount(const char *const dev_name, 1352cb2c7d1aSMickaël Salaün const struct path *const path, const char *const type, 1353cb2c7d1aSMickaël Salaün const unsigned long flags, void *const data) 1354cb2c7d1aSMickaël Salaün { 1355d7220364SMickaël Salaün if (!get_current_fs_domain()) 1356cb2c7d1aSMickaël Salaün return 0; 1357cb2c7d1aSMickaël Salaün return -EPERM; 1358cb2c7d1aSMickaël Salaün } 1359cb2c7d1aSMickaël Salaün 1360cb2c7d1aSMickaël Salaün static int hook_move_mount(const struct path *const from_path, 1361cb2c7d1aSMickaël Salaün const struct path *const to_path) 1362cb2c7d1aSMickaël Salaün { 1363d7220364SMickaël Salaün if (!get_current_fs_domain()) 1364cb2c7d1aSMickaël Salaün return 0; 1365cb2c7d1aSMickaël Salaün return -EPERM; 1366cb2c7d1aSMickaël Salaün } 1367cb2c7d1aSMickaël Salaün 1368cb2c7d1aSMickaël Salaün /* 1369cb2c7d1aSMickaël Salaün * Removing a mount point may reveal a previously hidden file hierarchy, which 1370cb2c7d1aSMickaël Salaün * may then grant access to files, which may have previously been forbidden. 1371cb2c7d1aSMickaël Salaün */ 1372cb2c7d1aSMickaël Salaün static int hook_sb_umount(struct vfsmount *const mnt, const int flags) 1373cb2c7d1aSMickaël Salaün { 1374d7220364SMickaël Salaün if (!get_current_fs_domain()) 1375cb2c7d1aSMickaël Salaün return 0; 1376cb2c7d1aSMickaël Salaün return -EPERM; 1377cb2c7d1aSMickaël Salaün } 1378cb2c7d1aSMickaël Salaün 1379cb2c7d1aSMickaël Salaün static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts) 1380cb2c7d1aSMickaël Salaün { 1381d7220364SMickaël Salaün if (!get_current_fs_domain()) 1382cb2c7d1aSMickaël Salaün return 0; 1383cb2c7d1aSMickaël Salaün return -EPERM; 1384cb2c7d1aSMickaël Salaün } 1385cb2c7d1aSMickaël Salaün 1386cb2c7d1aSMickaël Salaün /* 1387cb2c7d1aSMickaël Salaün * pivot_root(2), like mount(2), changes the current mount namespace. It must 1388cb2c7d1aSMickaël Salaün * then be forbidden for a landlocked process. 1389cb2c7d1aSMickaël Salaün * 1390cb2c7d1aSMickaël Salaün * However, chroot(2) may be allowed because it only changes the relative root 1391cb2c7d1aSMickaël Salaün * directory of the current process. Moreover, it can be used to restrict the 1392cb2c7d1aSMickaël Salaün * view of the filesystem. 1393cb2c7d1aSMickaël Salaün */ 1394cb2c7d1aSMickaël Salaün static int hook_sb_pivotroot(const struct path *const old_path, 1395cb2c7d1aSMickaël Salaün const struct path *const new_path) 1396cb2c7d1aSMickaël Salaün { 1397d7220364SMickaël Salaün if (!get_current_fs_domain()) 1398cb2c7d1aSMickaël Salaün return 0; 1399cb2c7d1aSMickaël Salaün return -EPERM; 1400cb2c7d1aSMickaël Salaün } 1401cb2c7d1aSMickaël Salaün 1402cb2c7d1aSMickaël Salaün /* Path hooks */ 1403cb2c7d1aSMickaël Salaün 1404cb2c7d1aSMickaël Salaün static int hook_path_link(struct dentry *const old_dentry, 1405cb2c7d1aSMickaël Salaün const struct path *const new_dir, 1406cb2c7d1aSMickaël Salaün struct dentry *const new_dentry) 1407cb2c7d1aSMickaël Salaün { 1408b91c3e4eSMickaël Salaün return current_check_refer_path(old_dentry, new_dir, new_dentry, false, 1409b91c3e4eSMickaël Salaün false); 1410cb2c7d1aSMickaël Salaün } 1411cb2c7d1aSMickaël Salaün 1412cb2c7d1aSMickaël Salaün static int hook_path_rename(const struct path *const old_dir, 1413cb2c7d1aSMickaël Salaün struct dentry *const old_dentry, 1414cb2c7d1aSMickaël Salaün const struct path *const new_dir, 1415100f59d9SMickaël Salaün struct dentry *const new_dentry, 1416100f59d9SMickaël Salaün const unsigned int flags) 1417cb2c7d1aSMickaël Salaün { 1418b91c3e4eSMickaël Salaün /* old_dir refers to old_dentry->d_parent and new_dir->mnt */ 1419b91c3e4eSMickaël Salaün return current_check_refer_path(old_dentry, new_dir, new_dentry, true, 1420b91c3e4eSMickaël Salaün !!(flags & RENAME_EXCHANGE)); 1421cb2c7d1aSMickaël Salaün } 1422cb2c7d1aSMickaël Salaün 1423cb2c7d1aSMickaël Salaün static int hook_path_mkdir(const struct path *const dir, 1424cb2c7d1aSMickaël Salaün struct dentry *const dentry, const umode_t mode) 1425cb2c7d1aSMickaël Salaün { 1426cb2c7d1aSMickaël Salaün return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_DIR); 1427cb2c7d1aSMickaël Salaün } 1428cb2c7d1aSMickaël Salaün 1429cb2c7d1aSMickaël Salaün static int hook_path_mknod(const struct path *const dir, 1430cb2c7d1aSMickaël Salaün struct dentry *const dentry, const umode_t mode, 1431cb2c7d1aSMickaël Salaün const unsigned int dev) 1432cb2c7d1aSMickaël Salaün { 1433d7220364SMickaël Salaün const struct landlock_ruleset *const dom = get_current_fs_domain(); 1434cb2c7d1aSMickaël Salaün 1435cb2c7d1aSMickaël Salaün if (!dom) 1436cb2c7d1aSMickaël Salaün return 0; 1437cb2c7d1aSMickaël Salaün return check_access_path(dom, dir, get_mode_access(mode)); 1438cb2c7d1aSMickaël Salaün } 1439cb2c7d1aSMickaël Salaün 1440cb2c7d1aSMickaël Salaün static int hook_path_symlink(const struct path *const dir, 144106a1c40aSMickaël Salaün struct dentry *const dentry, 144206a1c40aSMickaël Salaün const char *const old_name) 1443cb2c7d1aSMickaël Salaün { 1444cb2c7d1aSMickaël Salaün return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_SYM); 1445cb2c7d1aSMickaël Salaün } 1446cb2c7d1aSMickaël Salaün 1447cb2c7d1aSMickaël Salaün static int hook_path_unlink(const struct path *const dir, 1448cb2c7d1aSMickaël Salaün struct dentry *const dentry) 1449cb2c7d1aSMickaël Salaün { 1450cb2c7d1aSMickaël Salaün return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_FILE); 1451cb2c7d1aSMickaël Salaün } 1452cb2c7d1aSMickaël Salaün 1453cb2c7d1aSMickaël Salaün static int hook_path_rmdir(const struct path *const dir, 1454cb2c7d1aSMickaël Salaün struct dentry *const dentry) 1455cb2c7d1aSMickaël Salaün { 1456cb2c7d1aSMickaël Salaün return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR); 1457cb2c7d1aSMickaël Salaün } 1458cb2c7d1aSMickaël Salaün 1459b9f5ce27SGünther Noack static int hook_path_truncate(const struct path *const path) 1460b9f5ce27SGünther Noack { 1461b9f5ce27SGünther Noack return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE); 1462b9f5ce27SGünther Noack } 1463b9f5ce27SGünther Noack 1464cb2c7d1aSMickaël Salaün /* File hooks */ 1465cb2c7d1aSMickaël Salaün 1466b9f5ce27SGünther Noack /** 1467b9f5ce27SGünther Noack * get_required_file_open_access - Get access needed to open a file 1468b9f5ce27SGünther Noack * 1469b9f5ce27SGünther Noack * @file: File being opened. 1470b9f5ce27SGünther Noack * 1471b9f5ce27SGünther Noack * Returns the access rights that are required for opening the given file, 1472b9f5ce27SGünther Noack * depending on the file type and open mode. 1473b9f5ce27SGünther Noack */ 14743406ebadSGünther Noack static access_mask_t 1475b9f5ce27SGünther Noack get_required_file_open_access(const struct file *const file) 1476cb2c7d1aSMickaël Salaün { 14775f2ff33eSMickaël Salaün access_mask_t access = 0; 1478cb2c7d1aSMickaël Salaün 1479cb2c7d1aSMickaël Salaün if (file->f_mode & FMODE_READ) { 1480cb2c7d1aSMickaël Salaün /* A directory can only be opened in read mode. */ 1481cb2c7d1aSMickaël Salaün if (S_ISDIR(file_inode(file)->i_mode)) 1482cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_READ_DIR; 1483cb2c7d1aSMickaël Salaün access = LANDLOCK_ACCESS_FS_READ_FILE; 1484cb2c7d1aSMickaël Salaün } 1485cb2c7d1aSMickaël Salaün if (file->f_mode & FMODE_WRITE) 1486cb2c7d1aSMickaël Salaün access |= LANDLOCK_ACCESS_FS_WRITE_FILE; 1487cb2c7d1aSMickaël Salaün /* __FMODE_EXEC is indeed part of f_flags, not f_mode. */ 1488cb2c7d1aSMickaël Salaün if (file->f_flags & __FMODE_EXEC) 1489cb2c7d1aSMickaël Salaün access |= LANDLOCK_ACCESS_FS_EXECUTE; 1490cb2c7d1aSMickaël Salaün return access; 1491cb2c7d1aSMickaël Salaün } 1492cb2c7d1aSMickaël Salaün 1493b9f5ce27SGünther Noack static int hook_file_alloc_security(struct file *const file) 1494b9f5ce27SGünther Noack { 1495b9f5ce27SGünther Noack /* 1496b9f5ce27SGünther Noack * Grants all access rights, even if most of them are not checked later 1497b9f5ce27SGünther Noack * on. It is more consistent. 1498b9f5ce27SGünther Noack * 1499b9f5ce27SGünther Noack * Notably, file descriptors for regular files can also be acquired 1500b9f5ce27SGünther Noack * without going through the file_open hook, for example when using 1501b9f5ce27SGünther Noack * memfd_create(2). 1502b9f5ce27SGünther Noack */ 1503b9f5ce27SGünther Noack landlock_file(file)->allowed_access = LANDLOCK_MASK_ACCESS_FS; 1504b9f5ce27SGünther Noack return 0; 1505b9f5ce27SGünther Noack } 1506b9f5ce27SGünther Noack 1507b25f7415SGünther Noack static bool is_device(const struct file *const file) 1508b25f7415SGünther Noack { 1509b25f7415SGünther Noack const struct inode *inode = file_inode(file); 1510b25f7415SGünther Noack 1511b25f7415SGünther Noack return S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode); 1512b25f7415SGünther Noack } 1513b25f7415SGünther Noack 1514cb2c7d1aSMickaël Salaün static int hook_file_open(struct file *const file) 1515cb2c7d1aSMickaël Salaün { 1516b9f5ce27SGünther Noack layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; 1517b25f7415SGünther Noack access_mask_t open_access_request, full_access_request, allowed_access, 1518b25f7415SGünther Noack optional_access; 151963817febSMickaël Salaün const struct landlock_ruleset *const dom = 152063817febSMickaël Salaün get_fs_domain(landlock_cred(file->f_cred)->domain); 1521cb2c7d1aSMickaël Salaün 1522cb2c7d1aSMickaël Salaün if (!dom) 1523cb2c7d1aSMickaël Salaün return 0; 1524b9f5ce27SGünther Noack 1525cb2c7d1aSMickaël Salaün /* 1526b9f5ce27SGünther Noack * Because a file may be opened with O_PATH, get_required_file_open_access() 1527b9f5ce27SGünther Noack * may return 0. This case will be handled with a future Landlock 1528cb2c7d1aSMickaël Salaün * evolution. 1529cb2c7d1aSMickaël Salaün */ 1530b9f5ce27SGünther Noack open_access_request = get_required_file_open_access(file); 1531b9f5ce27SGünther Noack 1532b9f5ce27SGünther Noack /* 1533b9f5ce27SGünther Noack * We look up more access than what we immediately need for open(), so 1534b9f5ce27SGünther Noack * that we can later authorize operations on opened files. 1535b9f5ce27SGünther Noack */ 1536b25f7415SGünther Noack optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; 1537b25f7415SGünther Noack if (is_device(file)) 1538b25f7415SGünther Noack optional_access |= LANDLOCK_ACCESS_FS_IOCTL_DEV; 1539b25f7415SGünther Noack 1540b9f5ce27SGünther Noack full_access_request = open_access_request | optional_access; 1541b9f5ce27SGünther Noack 1542b9f5ce27SGünther Noack if (is_access_to_paths_allowed( 1543b9f5ce27SGünther Noack dom, &file->f_path, 15440e741011SKonstantin Meskhidze landlock_init_layer_masks(dom, full_access_request, 15457a11275cSKonstantin Meskhidze &layer_masks, LANDLOCK_KEY_INODE), 1546b9f5ce27SGünther Noack &layer_masks, NULL, 0, NULL, NULL)) { 1547b9f5ce27SGünther Noack allowed_access = full_access_request; 1548b9f5ce27SGünther Noack } else { 1549b9f5ce27SGünther Noack unsigned long access_bit; 1550b9f5ce27SGünther Noack const unsigned long access_req = full_access_request; 1551b9f5ce27SGünther Noack 1552b9f5ce27SGünther Noack /* 1553b9f5ce27SGünther Noack * Calculate the actual allowed access rights from layer_masks. 1554b9f5ce27SGünther Noack * Add each access right to allowed_access which has not been 1555b9f5ce27SGünther Noack * vetoed by any layer. 1556b9f5ce27SGünther Noack */ 1557b9f5ce27SGünther Noack allowed_access = 0; 1558b9f5ce27SGünther Noack for_each_set_bit(access_bit, &access_req, 1559b9f5ce27SGünther Noack ARRAY_SIZE(layer_masks)) { 1560b9f5ce27SGünther Noack if (!layer_masks[access_bit]) 1561b9f5ce27SGünther Noack allowed_access |= BIT_ULL(access_bit); 1562b9f5ce27SGünther Noack } 1563b9f5ce27SGünther Noack } 1564b9f5ce27SGünther Noack 1565b9f5ce27SGünther Noack /* 1566b9f5ce27SGünther Noack * For operations on already opened files (i.e. ftruncate()), it is the 1567b9f5ce27SGünther Noack * access rights at the time of open() which decide whether the 1568b9f5ce27SGünther Noack * operation is permitted. Therefore, we record the relevant subset of 1569b9f5ce27SGünther Noack * file access rights in the opened struct file. 1570b9f5ce27SGünther Noack */ 1571b9f5ce27SGünther Noack landlock_file(file)->allowed_access = allowed_access; 1572b9f5ce27SGünther Noack 1573b9f5ce27SGünther Noack if ((open_access_request & allowed_access) == open_access_request) 1574b9f5ce27SGünther Noack return 0; 1575b9f5ce27SGünther Noack 1576b9f5ce27SGünther Noack return -EACCES; 1577b9f5ce27SGünther Noack } 1578b9f5ce27SGünther Noack 1579b9f5ce27SGünther Noack static int hook_file_truncate(struct file *const file) 1580b9f5ce27SGünther Noack { 1581b9f5ce27SGünther Noack /* 1582b9f5ce27SGünther Noack * Allows truncation if the truncate right was available at the time of 1583b9f5ce27SGünther Noack * opening the file, to get a consistent access check as for read, write 1584b9f5ce27SGünther Noack * and execute operations. 1585b9f5ce27SGünther Noack * 1586b9f5ce27SGünther Noack * Note: For checks done based on the file's Landlock allowed access, we 1587b9f5ce27SGünther Noack * enforce them independently of whether the current thread is in a 1588b9f5ce27SGünther Noack * Landlock domain, so that open files passed between independent 1589b9f5ce27SGünther Noack * processes retain their behaviour. 1590b9f5ce27SGünther Noack */ 1591b9f5ce27SGünther Noack if (landlock_file(file)->allowed_access & LANDLOCK_ACCESS_FS_TRUNCATE) 1592b9f5ce27SGünther Noack return 0; 1593b9f5ce27SGünther Noack return -EACCES; 1594cb2c7d1aSMickaël Salaün } 1595cb2c7d1aSMickaël Salaün 1596b25f7415SGünther Noack static int hook_file_ioctl(struct file *file, unsigned int cmd, 1597b25f7415SGünther Noack unsigned long arg) 1598b25f7415SGünther Noack { 1599b25f7415SGünther Noack access_mask_t allowed_access = landlock_file(file)->allowed_access; 1600b25f7415SGünther Noack 1601b25f7415SGünther Noack /* 1602b25f7415SGünther Noack * It is the access rights at the time of opening the file which 1603b25f7415SGünther Noack * determine whether IOCTL can be used on the opened file later. 1604b25f7415SGünther Noack * 1605b25f7415SGünther Noack * The access right is attached to the opened file in hook_file_open(). 1606b25f7415SGünther Noack */ 1607b25f7415SGünther Noack if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV) 1608b25f7415SGünther Noack return 0; 1609b25f7415SGünther Noack 1610b25f7415SGünther Noack if (!is_device(file)) 1611b25f7415SGünther Noack return 0; 1612b25f7415SGünther Noack 1613b25f7415SGünther Noack if (is_masked_device_ioctl(cmd)) 1614b25f7415SGünther Noack return 0; 1615b25f7415SGünther Noack 1616b25f7415SGünther Noack return -EACCES; 1617b25f7415SGünther Noack } 1618b25f7415SGünther Noack 1619b25f7415SGünther Noack static int hook_file_ioctl_compat(struct file *file, unsigned int cmd, 1620b25f7415SGünther Noack unsigned long arg) 1621b25f7415SGünther Noack { 1622b25f7415SGünther Noack access_mask_t allowed_access = landlock_file(file)->allowed_access; 1623b25f7415SGünther Noack 1624b25f7415SGünther Noack /* 1625b25f7415SGünther Noack * It is the access rights at the time of opening the file which 1626b25f7415SGünther Noack * determine whether IOCTL can be used on the opened file later. 1627b25f7415SGünther Noack * 1628b25f7415SGünther Noack * The access right is attached to the opened file in hook_file_open(). 1629b25f7415SGünther Noack */ 1630b25f7415SGünther Noack if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV) 1631b25f7415SGünther Noack return 0; 1632b25f7415SGünther Noack 1633b25f7415SGünther Noack if (!is_device(file)) 1634b25f7415SGünther Noack return 0; 1635b25f7415SGünther Noack 1636b25f7415SGünther Noack if (is_masked_device_ioctl_compat(cmd)) 1637b25f7415SGünther Noack return 0; 1638b25f7415SGünther Noack 1639b25f7415SGünther Noack return -EACCES; 1640b25f7415SGünther Noack } 1641b25f7415SGünther Noack 1642*54a6e6bbSTahera Fahimi static void hook_file_set_fowner(struct file *file) 1643*54a6e6bbSTahera Fahimi { 1644*54a6e6bbSTahera Fahimi struct landlock_ruleset *new_dom, *prev_dom; 1645*54a6e6bbSTahera Fahimi 1646*54a6e6bbSTahera Fahimi /* 1647*54a6e6bbSTahera Fahimi * Lock already held by __f_setown(), see commit 26f204380a3c ("fs: Fix 1648*54a6e6bbSTahera Fahimi * file_set_fowner LSM hook inconsistencies"). 1649*54a6e6bbSTahera Fahimi */ 1650*54a6e6bbSTahera Fahimi lockdep_assert_held(&file_f_owner(file)->lock); 1651*54a6e6bbSTahera Fahimi new_dom = landlock_get_current_domain(); 1652*54a6e6bbSTahera Fahimi landlock_get_ruleset(new_dom); 1653*54a6e6bbSTahera Fahimi prev_dom = landlock_file(file)->fown_domain; 1654*54a6e6bbSTahera Fahimi landlock_file(file)->fown_domain = new_dom; 1655*54a6e6bbSTahera Fahimi 1656*54a6e6bbSTahera Fahimi /* Called in an RCU read-side critical section. */ 1657*54a6e6bbSTahera Fahimi landlock_put_ruleset_deferred(prev_dom); 1658*54a6e6bbSTahera Fahimi } 1659*54a6e6bbSTahera Fahimi 1660*54a6e6bbSTahera Fahimi static void hook_file_free_security(struct file *file) 1661*54a6e6bbSTahera Fahimi { 1662*54a6e6bbSTahera Fahimi landlock_put_ruleset_deferred(landlock_file(file)->fown_domain); 1663*54a6e6bbSTahera Fahimi } 1664*54a6e6bbSTahera Fahimi 1665f22f9aafSPaul Moore static struct security_hook_list landlock_hooks[] __ro_after_init = { 166663dff3e4SPaul Moore LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu), 1667cb2c7d1aSMickaël Salaün 1668cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_delete, hook_sb_delete), 1669cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_mount, hook_sb_mount), 1670cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(move_mount, hook_move_mount), 1671cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_umount, hook_sb_umount), 1672cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_remount, hook_sb_remount), 1673cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_pivotroot, hook_sb_pivotroot), 1674cb2c7d1aSMickaël Salaün 1675cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_link, hook_path_link), 1676cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_rename, hook_path_rename), 1677cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_mkdir, hook_path_mkdir), 1678cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_mknod, hook_path_mknod), 1679cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_symlink, hook_path_symlink), 1680cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_unlink, hook_path_unlink), 1681cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_rmdir, hook_path_rmdir), 1682b9f5ce27SGünther Noack LSM_HOOK_INIT(path_truncate, hook_path_truncate), 1683cb2c7d1aSMickaël Salaün 1684b9f5ce27SGünther Noack LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), 1685cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(file_open, hook_file_open), 1686b9f5ce27SGünther Noack LSM_HOOK_INIT(file_truncate, hook_file_truncate), 1687b25f7415SGünther Noack LSM_HOOK_INIT(file_ioctl, hook_file_ioctl), 1688b25f7415SGünther Noack LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat), 1689*54a6e6bbSTahera Fahimi LSM_HOOK_INIT(file_set_fowner, hook_file_set_fowner), 1690*54a6e6bbSTahera Fahimi LSM_HOOK_INIT(file_free_security, hook_file_free_security), 1691cb2c7d1aSMickaël Salaün }; 1692cb2c7d1aSMickaël Salaün 1693cb2c7d1aSMickaël Salaün __init void landlock_add_fs_hooks(void) 1694cb2c7d1aSMickaël Salaün { 1695cb2c7d1aSMickaël Salaün security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), 1696f3b8788cSCasey Schaufler &landlock_lsmid); 1697cb2c7d1aSMickaël Salaün } 1698b4007fd2SMickaël Salaün 1699b4007fd2SMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 1700b4007fd2SMickaël Salaün 1701b4007fd2SMickaël Salaün /* clang-format off */ 1702b4007fd2SMickaël Salaün static struct kunit_case test_cases[] = { 1703b4007fd2SMickaël Salaün KUNIT_CASE(test_no_more_access), 1704b4007fd2SMickaël Salaün KUNIT_CASE(test_scope_to_request_with_exec_none), 1705b4007fd2SMickaël Salaün KUNIT_CASE(test_scope_to_request_with_exec_some), 1706b4007fd2SMickaël Salaün KUNIT_CASE(test_scope_to_request_without_access), 1707b4007fd2SMickaël Salaün KUNIT_CASE(test_is_eacces_with_none), 1708b4007fd2SMickaël Salaün KUNIT_CASE(test_is_eacces_with_refer), 1709b4007fd2SMickaël Salaün KUNIT_CASE(test_is_eacces_with_write), 1710b4007fd2SMickaël Salaün {} 1711b4007fd2SMickaël Salaün }; 1712b4007fd2SMickaël Salaün /* clang-format on */ 1713b4007fd2SMickaël Salaün 1714b4007fd2SMickaël Salaün static struct kunit_suite test_suite = { 1715b4007fd2SMickaël Salaün .name = "landlock_fs", 1716b4007fd2SMickaël Salaün .test_cases = test_cases, 1717b4007fd2SMickaël Salaün }; 1718b4007fd2SMickaël Salaün 1719b4007fd2SMickaël Salaün kunit_test_suite(test_suite); 1720b4007fd2SMickaël Salaün 1721b4007fd2SMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 1722