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 7cb2c7d1aSMickaël Salaün */ 8cb2c7d1aSMickaël Salaün 9cb2c7d1aSMickaël Salaün #include <linux/atomic.h> 10cb2c7d1aSMickaël Salaün #include <linux/bitops.h> 11cb2c7d1aSMickaël Salaün #include <linux/bits.h> 12cb2c7d1aSMickaël Salaün #include <linux/compiler_types.h> 13cb2c7d1aSMickaël Salaün #include <linux/dcache.h> 14cb2c7d1aSMickaël Salaün #include <linux/err.h> 15cb2c7d1aSMickaël Salaün #include <linux/fs.h> 16cb2c7d1aSMickaël Salaün #include <linux/init.h> 17cb2c7d1aSMickaël Salaün #include <linux/kernel.h> 18cb2c7d1aSMickaël Salaün #include <linux/limits.h> 19cb2c7d1aSMickaël Salaün #include <linux/list.h> 20cb2c7d1aSMickaël Salaün #include <linux/lsm_hooks.h> 21cb2c7d1aSMickaël Salaün #include <linux/mount.h> 22cb2c7d1aSMickaël Salaün #include <linux/namei.h> 23cb2c7d1aSMickaël Salaün #include <linux/path.h> 24cb2c7d1aSMickaël Salaün #include <linux/rcupdate.h> 25cb2c7d1aSMickaël Salaün #include <linux/spinlock.h> 26cb2c7d1aSMickaël Salaün #include <linux/stat.h> 27cb2c7d1aSMickaël Salaün #include <linux/types.h> 28cb2c7d1aSMickaël Salaün #include <linux/wait_bit.h> 29cb2c7d1aSMickaël Salaün #include <linux/workqueue.h> 30cb2c7d1aSMickaël Salaün #include <uapi/linux/landlock.h> 31cb2c7d1aSMickaël Salaün 32cb2c7d1aSMickaël Salaün #include "common.h" 33cb2c7d1aSMickaël Salaün #include "cred.h" 34cb2c7d1aSMickaël Salaün #include "fs.h" 35cb2c7d1aSMickaël Salaün #include "limits.h" 36cb2c7d1aSMickaël Salaün #include "object.h" 37cb2c7d1aSMickaël Salaün #include "ruleset.h" 38cb2c7d1aSMickaël Salaün #include "setup.h" 39cb2c7d1aSMickaël Salaün 40cb2c7d1aSMickaël Salaün /* Underlying object management */ 41cb2c7d1aSMickaël Salaün 42cb2c7d1aSMickaël Salaün static void release_inode(struct landlock_object *const object) 43cb2c7d1aSMickaël Salaün __releases(object->lock) 44cb2c7d1aSMickaël Salaün { 45cb2c7d1aSMickaël Salaün struct inode *const inode = object->underobj; 46cb2c7d1aSMickaël Salaün struct super_block *sb; 47cb2c7d1aSMickaël Salaün 48cb2c7d1aSMickaël Salaün if (!inode) { 49cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 50cb2c7d1aSMickaël Salaün return; 51cb2c7d1aSMickaël Salaün } 52cb2c7d1aSMickaël Salaün 53cb2c7d1aSMickaël Salaün /* 54cb2c7d1aSMickaël Salaün * Protects against concurrent use by hook_sb_delete() of the reference 55cb2c7d1aSMickaël Salaün * to the underlying inode. 56cb2c7d1aSMickaël Salaün */ 57cb2c7d1aSMickaël Salaün object->underobj = NULL; 58cb2c7d1aSMickaël Salaün /* 59cb2c7d1aSMickaël Salaün * Makes sure that if the filesystem is concurrently unmounted, 60cb2c7d1aSMickaël Salaün * hook_sb_delete() will wait for us to finish iput(). 61cb2c7d1aSMickaël Salaün */ 62cb2c7d1aSMickaël Salaün sb = inode->i_sb; 63cb2c7d1aSMickaël Salaün atomic_long_inc(&landlock_superblock(sb)->inode_refs); 64cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 65cb2c7d1aSMickaël Salaün /* 66cb2c7d1aSMickaël Salaün * Because object->underobj was not NULL, hook_sb_delete() and 67cb2c7d1aSMickaël Salaün * get_inode_object() guarantee that it is safe to reset 68cb2c7d1aSMickaël Salaün * landlock_inode(inode)->object while it is not NULL. It is therefore 69cb2c7d1aSMickaël Salaün * not necessary to lock inode->i_lock. 70cb2c7d1aSMickaël Salaün */ 71cb2c7d1aSMickaël Salaün rcu_assign_pointer(landlock_inode(inode)->object, NULL); 72cb2c7d1aSMickaël Salaün /* 73cb2c7d1aSMickaël Salaün * Now, new rules can safely be tied to @inode with get_inode_object(). 74cb2c7d1aSMickaël Salaün */ 75cb2c7d1aSMickaël Salaün 76cb2c7d1aSMickaël Salaün iput(inode); 77cb2c7d1aSMickaël Salaün if (atomic_long_dec_and_test(&landlock_superblock(sb)->inode_refs)) 78cb2c7d1aSMickaël Salaün wake_up_var(&landlock_superblock(sb)->inode_refs); 79cb2c7d1aSMickaël Salaün } 80cb2c7d1aSMickaël Salaün 81cb2c7d1aSMickaël Salaün static const struct landlock_object_underops landlock_fs_underops = { 82cb2c7d1aSMickaël Salaün .release = release_inode 83cb2c7d1aSMickaël Salaün }; 84cb2c7d1aSMickaël Salaün 85cb2c7d1aSMickaël Salaün /* Ruleset management */ 86cb2c7d1aSMickaël Salaün 87cb2c7d1aSMickaël Salaün static struct landlock_object *get_inode_object(struct inode *const inode) 88cb2c7d1aSMickaël Salaün { 89cb2c7d1aSMickaël Salaün struct landlock_object *object, *new_object; 90cb2c7d1aSMickaël Salaün struct landlock_inode_security *inode_sec = landlock_inode(inode); 91cb2c7d1aSMickaël Salaün 92cb2c7d1aSMickaël Salaün rcu_read_lock(); 93cb2c7d1aSMickaël Salaün retry: 94cb2c7d1aSMickaël Salaün object = rcu_dereference(inode_sec->object); 95cb2c7d1aSMickaël Salaün if (object) { 96cb2c7d1aSMickaël Salaün if (likely(refcount_inc_not_zero(&object->usage))) { 97cb2c7d1aSMickaël Salaün rcu_read_unlock(); 98cb2c7d1aSMickaël Salaün return object; 99cb2c7d1aSMickaël Salaün } 100cb2c7d1aSMickaël Salaün /* 101cb2c7d1aSMickaël Salaün * We are racing with release_inode(), the object is going 102cb2c7d1aSMickaël Salaün * away. Wait for release_inode(), then retry. 103cb2c7d1aSMickaël Salaün */ 104cb2c7d1aSMickaël Salaün spin_lock(&object->lock); 105cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 106cb2c7d1aSMickaël Salaün goto retry; 107cb2c7d1aSMickaël Salaün } 108cb2c7d1aSMickaël Salaün rcu_read_unlock(); 109cb2c7d1aSMickaël Salaün 110cb2c7d1aSMickaël Salaün /* 111cb2c7d1aSMickaël Salaün * If there is no object tied to @inode, then create a new one (without 112cb2c7d1aSMickaël Salaün * holding any locks). 113cb2c7d1aSMickaël Salaün */ 114cb2c7d1aSMickaël Salaün new_object = landlock_create_object(&landlock_fs_underops, inode); 115cb2c7d1aSMickaël Salaün if (IS_ERR(new_object)) 116cb2c7d1aSMickaël Salaün return new_object; 117cb2c7d1aSMickaël Salaün 118cb2c7d1aSMickaël Salaün /* 119cb2c7d1aSMickaël Salaün * Protects against concurrent calls to get_inode_object() or 120cb2c7d1aSMickaël Salaün * hook_sb_delete(). 121cb2c7d1aSMickaël Salaün */ 122cb2c7d1aSMickaël Salaün spin_lock(&inode->i_lock); 123cb2c7d1aSMickaël Salaün if (unlikely(rcu_access_pointer(inode_sec->object))) { 124cb2c7d1aSMickaël Salaün /* Someone else just created the object, bail out and retry. */ 125cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 126cb2c7d1aSMickaël Salaün kfree(new_object); 127cb2c7d1aSMickaël Salaün 128cb2c7d1aSMickaël Salaün rcu_read_lock(); 129cb2c7d1aSMickaël Salaün goto retry; 130cb2c7d1aSMickaël Salaün } 131cb2c7d1aSMickaël Salaün 132cb2c7d1aSMickaël Salaün /* 133cb2c7d1aSMickaël Salaün * @inode will be released by hook_sb_delete() on its superblock 134cb2c7d1aSMickaël Salaün * shutdown, or by release_inode() when no more ruleset references the 135cb2c7d1aSMickaël Salaün * related object. 136cb2c7d1aSMickaël Salaün */ 137cb2c7d1aSMickaël Salaün ihold(inode); 138cb2c7d1aSMickaël Salaün rcu_assign_pointer(inode_sec->object, new_object); 139cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 140cb2c7d1aSMickaël Salaün return new_object; 141cb2c7d1aSMickaël Salaün } 142cb2c7d1aSMickaël Salaün 143cb2c7d1aSMickaël Salaün /* All access rights that can be tied to files. */ 1446cc2df8eSMickaël Salaün /* clang-format off */ 145cb2c7d1aSMickaël Salaün #define ACCESS_FILE ( \ 146cb2c7d1aSMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | \ 147cb2c7d1aSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE | \ 148cb2c7d1aSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE) 1496cc2df8eSMickaël Salaün /* clang-format on */ 150cb2c7d1aSMickaël Salaün 151cb2c7d1aSMickaël Salaün /* 152cb2c7d1aSMickaël Salaün * @path: Should have been checked by get_path_from_fd(). 153cb2c7d1aSMickaël Salaün */ 154cb2c7d1aSMickaël Salaün int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, 155cb2c7d1aSMickaël Salaün const struct path *const path, u32 access_rights) 156cb2c7d1aSMickaël Salaün { 157cb2c7d1aSMickaël Salaün int err; 158cb2c7d1aSMickaël Salaün struct landlock_object *object; 159cb2c7d1aSMickaël Salaün 160cb2c7d1aSMickaël Salaün /* Files only get access rights that make sense. */ 161*06a1c40aSMickaël Salaün if (!d_is_dir(path->dentry) && 162*06a1c40aSMickaël Salaün (access_rights | ACCESS_FILE) != ACCESS_FILE) 163cb2c7d1aSMickaël Salaün return -EINVAL; 164cb2c7d1aSMickaël Salaün if (WARN_ON_ONCE(ruleset->num_layers != 1)) 165cb2c7d1aSMickaël Salaün return -EINVAL; 166cb2c7d1aSMickaël Salaün 167cb2c7d1aSMickaël Salaün /* Transforms relative access rights to absolute ones. */ 168cb2c7d1aSMickaël Salaün access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->fs_access_masks[0]; 169cb2c7d1aSMickaël Salaün object = get_inode_object(d_backing_inode(path->dentry)); 170cb2c7d1aSMickaël Salaün if (IS_ERR(object)) 171cb2c7d1aSMickaël Salaün return PTR_ERR(object); 172cb2c7d1aSMickaël Salaün mutex_lock(&ruleset->lock); 173cb2c7d1aSMickaël Salaün err = landlock_insert_rule(ruleset, object, access_rights); 174cb2c7d1aSMickaël Salaün mutex_unlock(&ruleset->lock); 175cb2c7d1aSMickaël Salaün /* 176cb2c7d1aSMickaël Salaün * No need to check for an error because landlock_insert_rule() 177cb2c7d1aSMickaël Salaün * increments the refcount for the new object if needed. 178cb2c7d1aSMickaël Salaün */ 179cb2c7d1aSMickaël Salaün landlock_put_object(object); 180cb2c7d1aSMickaël Salaün return err; 181cb2c7d1aSMickaël Salaün } 182cb2c7d1aSMickaël Salaün 183cb2c7d1aSMickaël Salaün /* Access-control management */ 184cb2c7d1aSMickaël Salaün 185*06a1c40aSMickaël Salaün static inline u64 unmask_layers(const struct landlock_ruleset *const domain, 186*06a1c40aSMickaël Salaün const struct path *const path, 187*06a1c40aSMickaël Salaün const u32 access_request, u64 layer_mask) 188cb2c7d1aSMickaël Salaün { 189cb2c7d1aSMickaël Salaün const struct landlock_rule *rule; 190cb2c7d1aSMickaël Salaün const struct inode *inode; 191cb2c7d1aSMickaël Salaün size_t i; 192cb2c7d1aSMickaël Salaün 193cb2c7d1aSMickaël Salaün if (d_is_negative(path->dentry)) 194cb2c7d1aSMickaël Salaün /* Ignore nonexistent leafs. */ 195cb2c7d1aSMickaël Salaün return layer_mask; 196cb2c7d1aSMickaël Salaün inode = d_backing_inode(path->dentry); 197cb2c7d1aSMickaël Salaün rcu_read_lock(); 198*06a1c40aSMickaël Salaün rule = landlock_find_rule( 199*06a1c40aSMickaël Salaün domain, rcu_dereference(landlock_inode(inode)->object)); 200cb2c7d1aSMickaël Salaün rcu_read_unlock(); 201cb2c7d1aSMickaël Salaün if (!rule) 202cb2c7d1aSMickaël Salaün return layer_mask; 203cb2c7d1aSMickaël Salaün 204cb2c7d1aSMickaël Salaün /* 205cb2c7d1aSMickaël Salaün * An access is granted if, for each policy layer, at least one rule 206cb2c7d1aSMickaël Salaün * encountered on the pathwalk grants the requested accesses, 207cb2c7d1aSMickaël Salaün * regardless of their position in the layer stack. We must then check 208cb2c7d1aSMickaël Salaün * the remaining layers for each inode, from the first added layer to 209cb2c7d1aSMickaël Salaün * the last one. 210cb2c7d1aSMickaël Salaün */ 211cb2c7d1aSMickaël Salaün for (i = 0; i < rule->num_layers; i++) { 212cb2c7d1aSMickaël Salaün const struct landlock_layer *const layer = &rule->layers[i]; 213cb2c7d1aSMickaël Salaün const u64 layer_level = BIT_ULL(layer->level - 1); 214cb2c7d1aSMickaël Salaün 215cb2c7d1aSMickaël Salaün /* Checks that the layer grants access to the full request. */ 216cb2c7d1aSMickaël Salaün if ((layer->access & access_request) == access_request) { 217cb2c7d1aSMickaël Salaün layer_mask &= ~layer_level; 218cb2c7d1aSMickaël Salaün 219cb2c7d1aSMickaël Salaün if (layer_mask == 0) 220cb2c7d1aSMickaël Salaün return layer_mask; 221cb2c7d1aSMickaël Salaün } 222cb2c7d1aSMickaël Salaün } 223cb2c7d1aSMickaël Salaün return layer_mask; 224cb2c7d1aSMickaël Salaün } 225cb2c7d1aSMickaël Salaün 226cb2c7d1aSMickaël Salaün static int check_access_path(const struct landlock_ruleset *const domain, 227cb2c7d1aSMickaël Salaün const struct path *const path, u32 access_request) 228cb2c7d1aSMickaël Salaün { 229cb2c7d1aSMickaël Salaün bool allowed = false; 230cb2c7d1aSMickaël Salaün struct path walker_path; 231cb2c7d1aSMickaël Salaün u64 layer_mask; 232cb2c7d1aSMickaël Salaün size_t i; 233cb2c7d1aSMickaël Salaün 234cb2c7d1aSMickaël Salaün /* Make sure all layers can be checked. */ 235cb2c7d1aSMickaël Salaün BUILD_BUG_ON(BITS_PER_TYPE(layer_mask) < LANDLOCK_MAX_NUM_LAYERS); 236cb2c7d1aSMickaël Salaün 237cb2c7d1aSMickaël Salaün if (!access_request) 238cb2c7d1aSMickaël Salaün return 0; 239cb2c7d1aSMickaël Salaün if (WARN_ON_ONCE(!domain || !path)) 240cb2c7d1aSMickaël Salaün return 0; 241cb2c7d1aSMickaël Salaün /* 242cb2c7d1aSMickaël Salaün * Allows access to pseudo filesystems that will never be mountable 243cb2c7d1aSMickaël Salaün * (e.g. sockfs, pipefs), but can still be reachable through 244cb2c7d1aSMickaël Salaün * /proc/<pid>/fd/<file-descriptor> . 245cb2c7d1aSMickaël Salaün */ 246cb2c7d1aSMickaël Salaün if ((path->dentry->d_sb->s_flags & SB_NOUSER) || 247cb2c7d1aSMickaël Salaün (d_is_positive(path->dentry) && 248cb2c7d1aSMickaël Salaün unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))) 249cb2c7d1aSMickaël Salaün return 0; 250cb2c7d1aSMickaël Salaün if (WARN_ON_ONCE(domain->num_layers < 1)) 251cb2c7d1aSMickaël Salaün return -EACCES; 252cb2c7d1aSMickaël Salaün 253cb2c7d1aSMickaël Salaün /* Saves all layers handling a subset of requested accesses. */ 254cb2c7d1aSMickaël Salaün layer_mask = 0; 255cb2c7d1aSMickaël Salaün for (i = 0; i < domain->num_layers; i++) { 256cb2c7d1aSMickaël Salaün if (domain->fs_access_masks[i] & access_request) 257cb2c7d1aSMickaël Salaün layer_mask |= BIT_ULL(i); 258cb2c7d1aSMickaël Salaün } 259cb2c7d1aSMickaël Salaün /* An access request not handled by the domain is allowed. */ 260cb2c7d1aSMickaël Salaün if (layer_mask == 0) 261cb2c7d1aSMickaël Salaün return 0; 262cb2c7d1aSMickaël Salaün 263cb2c7d1aSMickaël Salaün walker_path = *path; 264cb2c7d1aSMickaël Salaün path_get(&walker_path); 265cb2c7d1aSMickaël Salaün /* 266cb2c7d1aSMickaël Salaün * We need to walk through all the hierarchy to not miss any relevant 267cb2c7d1aSMickaël Salaün * restriction. 268cb2c7d1aSMickaël Salaün */ 269cb2c7d1aSMickaël Salaün while (true) { 270cb2c7d1aSMickaël Salaün struct dentry *parent_dentry; 271cb2c7d1aSMickaël Salaün 272*06a1c40aSMickaël Salaün layer_mask = unmask_layers(domain, &walker_path, access_request, 273*06a1c40aSMickaël Salaün layer_mask); 274cb2c7d1aSMickaël Salaün if (layer_mask == 0) { 275cb2c7d1aSMickaël Salaün /* Stops when a rule from each layer grants access. */ 276cb2c7d1aSMickaël Salaün allowed = true; 277cb2c7d1aSMickaël Salaün break; 278cb2c7d1aSMickaël Salaün } 279cb2c7d1aSMickaël Salaün 280cb2c7d1aSMickaël Salaün jump_up: 281cb2c7d1aSMickaël Salaün if (walker_path.dentry == walker_path.mnt->mnt_root) { 282cb2c7d1aSMickaël Salaün if (follow_up(&walker_path)) { 283cb2c7d1aSMickaël Salaün /* Ignores hidden mount points. */ 284cb2c7d1aSMickaël Salaün goto jump_up; 285cb2c7d1aSMickaël Salaün } else { 286cb2c7d1aSMickaël Salaün /* 287cb2c7d1aSMickaël Salaün * Stops at the real root. Denies access 288cb2c7d1aSMickaël Salaün * because not all layers have granted access. 289cb2c7d1aSMickaël Salaün */ 290cb2c7d1aSMickaël Salaün allowed = false; 291cb2c7d1aSMickaël Salaün break; 292cb2c7d1aSMickaël Salaün } 293cb2c7d1aSMickaël Salaün } 294cb2c7d1aSMickaël Salaün if (unlikely(IS_ROOT(walker_path.dentry))) { 295cb2c7d1aSMickaël Salaün /* 296cb2c7d1aSMickaël Salaün * Stops at disconnected root directories. Only allows 297cb2c7d1aSMickaël Salaün * access to internal filesystems (e.g. nsfs, which is 298cb2c7d1aSMickaël Salaün * reachable through /proc/<pid>/ns/<namespace>). 299cb2c7d1aSMickaël Salaün */ 300cb2c7d1aSMickaël Salaün allowed = !!(walker_path.mnt->mnt_flags & MNT_INTERNAL); 301cb2c7d1aSMickaël Salaün break; 302cb2c7d1aSMickaël Salaün } 303cb2c7d1aSMickaël Salaün parent_dentry = dget_parent(walker_path.dentry); 304cb2c7d1aSMickaël Salaün dput(walker_path.dentry); 305cb2c7d1aSMickaël Salaün walker_path.dentry = parent_dentry; 306cb2c7d1aSMickaël Salaün } 307cb2c7d1aSMickaël Salaün path_put(&walker_path); 308cb2c7d1aSMickaël Salaün return allowed ? 0 : -EACCES; 309cb2c7d1aSMickaël Salaün } 310cb2c7d1aSMickaël Salaün 311cb2c7d1aSMickaël Salaün static inline int current_check_access_path(const struct path *const path, 312cb2c7d1aSMickaël Salaün const u32 access_request) 313cb2c7d1aSMickaël Salaün { 314cb2c7d1aSMickaël Salaün const struct landlock_ruleset *const dom = 315cb2c7d1aSMickaël Salaün landlock_get_current_domain(); 316cb2c7d1aSMickaël Salaün 317cb2c7d1aSMickaël Salaün if (!dom) 318cb2c7d1aSMickaël Salaün return 0; 319cb2c7d1aSMickaël Salaün return check_access_path(dom, path, access_request); 320cb2c7d1aSMickaël Salaün } 321cb2c7d1aSMickaël Salaün 322cb2c7d1aSMickaël Salaün /* Inode hooks */ 323cb2c7d1aSMickaël Salaün 324cb2c7d1aSMickaël Salaün static void hook_inode_free_security(struct inode *const inode) 325cb2c7d1aSMickaël Salaün { 326cb2c7d1aSMickaël Salaün /* 327cb2c7d1aSMickaël Salaün * All inodes must already have been untied from their object by 328cb2c7d1aSMickaël Salaün * release_inode() or hook_sb_delete(). 329cb2c7d1aSMickaël Salaün */ 330cb2c7d1aSMickaël Salaün WARN_ON_ONCE(landlock_inode(inode)->object); 331cb2c7d1aSMickaël Salaün } 332cb2c7d1aSMickaël Salaün 333cb2c7d1aSMickaël Salaün /* Super-block hooks */ 334cb2c7d1aSMickaël Salaün 335cb2c7d1aSMickaël Salaün /* 336cb2c7d1aSMickaël Salaün * Release the inodes used in a security policy. 337cb2c7d1aSMickaël Salaün * 338cb2c7d1aSMickaël Salaün * Cf. fsnotify_unmount_inodes() and invalidate_inodes() 339cb2c7d1aSMickaël Salaün */ 340cb2c7d1aSMickaël Salaün static void hook_sb_delete(struct super_block *const sb) 341cb2c7d1aSMickaël Salaün { 342cb2c7d1aSMickaël Salaün struct inode *inode, *prev_inode = NULL; 343cb2c7d1aSMickaël Salaün 344cb2c7d1aSMickaël Salaün if (!landlock_initialized) 345cb2c7d1aSMickaël Salaün return; 346cb2c7d1aSMickaël Salaün 347cb2c7d1aSMickaël Salaün spin_lock(&sb->s_inode_list_lock); 348cb2c7d1aSMickaël Salaün list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { 349cb2c7d1aSMickaël Salaün struct landlock_object *object; 350cb2c7d1aSMickaël Salaün 351cb2c7d1aSMickaël Salaün /* Only handles referenced inodes. */ 352cb2c7d1aSMickaël Salaün if (!atomic_read(&inode->i_count)) 353cb2c7d1aSMickaël Salaün continue; 354cb2c7d1aSMickaël Salaün 355cb2c7d1aSMickaël Salaün /* 356cb2c7d1aSMickaël Salaün * Protects against concurrent modification of inode (e.g. 357cb2c7d1aSMickaël Salaün * from get_inode_object()). 358cb2c7d1aSMickaël Salaün */ 359cb2c7d1aSMickaël Salaün spin_lock(&inode->i_lock); 360cb2c7d1aSMickaël Salaün /* 361cb2c7d1aSMickaël Salaün * Checks I_FREEING and I_WILL_FREE to protect against a race 362cb2c7d1aSMickaël Salaün * condition when release_inode() just called iput(), which 363cb2c7d1aSMickaël Salaün * could lead to a NULL dereference of inode->security or a 364cb2c7d1aSMickaël Salaün * second call to iput() for the same Landlock object. Also 365cb2c7d1aSMickaël Salaün * checks I_NEW because such inode cannot be tied to an object. 366cb2c7d1aSMickaël Salaün */ 367cb2c7d1aSMickaël Salaün if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) { 368cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 369cb2c7d1aSMickaël Salaün continue; 370cb2c7d1aSMickaël Salaün } 371cb2c7d1aSMickaël Salaün 372cb2c7d1aSMickaël Salaün rcu_read_lock(); 373cb2c7d1aSMickaël Salaün object = rcu_dereference(landlock_inode(inode)->object); 374cb2c7d1aSMickaël Salaün if (!object) { 375cb2c7d1aSMickaël Salaün rcu_read_unlock(); 376cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 377cb2c7d1aSMickaël Salaün continue; 378cb2c7d1aSMickaël Salaün } 379cb2c7d1aSMickaël Salaün /* Keeps a reference to this inode until the next loop walk. */ 380cb2c7d1aSMickaël Salaün __iget(inode); 381cb2c7d1aSMickaël Salaün spin_unlock(&inode->i_lock); 382cb2c7d1aSMickaël Salaün 383cb2c7d1aSMickaël Salaün /* 384cb2c7d1aSMickaël Salaün * If there is no concurrent release_inode() ongoing, then we 385cb2c7d1aSMickaël Salaün * are in charge of calling iput() on this inode, otherwise we 386cb2c7d1aSMickaël Salaün * will just wait for it to finish. 387cb2c7d1aSMickaël Salaün */ 388cb2c7d1aSMickaël Salaün spin_lock(&object->lock); 389cb2c7d1aSMickaël Salaün if (object->underobj == inode) { 390cb2c7d1aSMickaël Salaün object->underobj = NULL; 391cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 392cb2c7d1aSMickaël Salaün rcu_read_unlock(); 393cb2c7d1aSMickaël Salaün 394cb2c7d1aSMickaël Salaün /* 395cb2c7d1aSMickaël Salaün * Because object->underobj was not NULL, 396cb2c7d1aSMickaël Salaün * release_inode() and get_inode_object() guarantee 397cb2c7d1aSMickaël Salaün * that it is safe to reset 398cb2c7d1aSMickaël Salaün * landlock_inode(inode)->object while it is not NULL. 399cb2c7d1aSMickaël Salaün * It is therefore not necessary to lock inode->i_lock. 400cb2c7d1aSMickaël Salaün */ 401cb2c7d1aSMickaël Salaün rcu_assign_pointer(landlock_inode(inode)->object, NULL); 402cb2c7d1aSMickaël Salaün /* 403cb2c7d1aSMickaël Salaün * At this point, we own the ihold() reference that was 404cb2c7d1aSMickaël Salaün * originally set up by get_inode_object() and the 405cb2c7d1aSMickaël Salaün * __iget() reference that we just set in this loop 406cb2c7d1aSMickaël Salaün * walk. Therefore the following call to iput() will 407cb2c7d1aSMickaël Salaün * not sleep nor drop the inode because there is now at 408cb2c7d1aSMickaël Salaün * least two references to it. 409cb2c7d1aSMickaël Salaün */ 410cb2c7d1aSMickaël Salaün iput(inode); 411cb2c7d1aSMickaël Salaün } else { 412cb2c7d1aSMickaël Salaün spin_unlock(&object->lock); 413cb2c7d1aSMickaël Salaün rcu_read_unlock(); 414cb2c7d1aSMickaël Salaün } 415cb2c7d1aSMickaël Salaün 416cb2c7d1aSMickaël Salaün if (prev_inode) { 417cb2c7d1aSMickaël Salaün /* 418cb2c7d1aSMickaël Salaün * At this point, we still own the __iget() reference 419cb2c7d1aSMickaël Salaün * that we just set in this loop walk. Therefore we 420cb2c7d1aSMickaël Salaün * can drop the list lock and know that the inode won't 421cb2c7d1aSMickaël Salaün * disappear from under us until the next loop walk. 422cb2c7d1aSMickaël Salaün */ 423cb2c7d1aSMickaël Salaün spin_unlock(&sb->s_inode_list_lock); 424cb2c7d1aSMickaël Salaün /* 425cb2c7d1aSMickaël Salaün * We can now actually put the inode reference from the 426cb2c7d1aSMickaël Salaün * previous loop walk, which is not needed anymore. 427cb2c7d1aSMickaël Salaün */ 428cb2c7d1aSMickaël Salaün iput(prev_inode); 429cb2c7d1aSMickaël Salaün cond_resched(); 430cb2c7d1aSMickaël Salaün spin_lock(&sb->s_inode_list_lock); 431cb2c7d1aSMickaël Salaün } 432cb2c7d1aSMickaël Salaün prev_inode = inode; 433cb2c7d1aSMickaël Salaün } 434cb2c7d1aSMickaël Salaün spin_unlock(&sb->s_inode_list_lock); 435cb2c7d1aSMickaël Salaün 436cb2c7d1aSMickaël Salaün /* Puts the inode reference from the last loop walk, if any. */ 437cb2c7d1aSMickaël Salaün if (prev_inode) 438cb2c7d1aSMickaël Salaün iput(prev_inode); 439cb2c7d1aSMickaël Salaün /* Waits for pending iput() in release_inode(). */ 440*06a1c40aSMickaël Salaün wait_var_event(&landlock_superblock(sb)->inode_refs, 441*06a1c40aSMickaël Salaün !atomic_long_read(&landlock_superblock(sb)->inode_refs)); 442cb2c7d1aSMickaël Salaün } 443cb2c7d1aSMickaël Salaün 444cb2c7d1aSMickaël Salaün /* 445cb2c7d1aSMickaël Salaün * Because a Landlock security policy is defined according to the filesystem 446cb2c7d1aSMickaël Salaün * topology (i.e. the mount namespace), changing it may grant access to files 447cb2c7d1aSMickaël Salaün * not previously allowed. 448cb2c7d1aSMickaël Salaün * 449cb2c7d1aSMickaël Salaün * To make it simple, deny any filesystem topology modification by landlocked 450cb2c7d1aSMickaël Salaün * processes. Non-landlocked processes may still change the namespace of a 451cb2c7d1aSMickaël Salaün * landlocked process, but this kind of threat must be handled by a system-wide 452cb2c7d1aSMickaël Salaün * access-control security policy. 453cb2c7d1aSMickaël Salaün * 454cb2c7d1aSMickaël Salaün * This could be lifted in the future if Landlock can safely handle mount 455cb2c7d1aSMickaël Salaün * namespace updates requested by a landlocked process. Indeed, we could 456cb2c7d1aSMickaël Salaün * update the current domain (which is currently read-only) by taking into 457cb2c7d1aSMickaël Salaün * account the accesses of the source and the destination of a new mount point. 458cb2c7d1aSMickaël Salaün * However, it would also require to make all the child domains dynamically 459cb2c7d1aSMickaël Salaün * inherit these new constraints. Anyway, for backward compatibility reasons, 460cb2c7d1aSMickaël Salaün * a dedicated user space option would be required (e.g. as a ruleset flag). 461cb2c7d1aSMickaël Salaün */ 462cb2c7d1aSMickaël Salaün static int hook_sb_mount(const char *const dev_name, 463cb2c7d1aSMickaël Salaün const struct path *const path, const char *const type, 464cb2c7d1aSMickaël Salaün const unsigned long flags, void *const data) 465cb2c7d1aSMickaël Salaün { 466cb2c7d1aSMickaël Salaün if (!landlock_get_current_domain()) 467cb2c7d1aSMickaël Salaün return 0; 468cb2c7d1aSMickaël Salaün return -EPERM; 469cb2c7d1aSMickaël Salaün } 470cb2c7d1aSMickaël Salaün 471cb2c7d1aSMickaël Salaün static int hook_move_mount(const struct path *const from_path, 472cb2c7d1aSMickaël Salaün const struct path *const to_path) 473cb2c7d1aSMickaël Salaün { 474cb2c7d1aSMickaël Salaün if (!landlock_get_current_domain()) 475cb2c7d1aSMickaël Salaün return 0; 476cb2c7d1aSMickaël Salaün return -EPERM; 477cb2c7d1aSMickaël Salaün } 478cb2c7d1aSMickaël Salaün 479cb2c7d1aSMickaël Salaün /* 480cb2c7d1aSMickaël Salaün * Removing a mount point may reveal a previously hidden file hierarchy, which 481cb2c7d1aSMickaël Salaün * may then grant access to files, which may have previously been forbidden. 482cb2c7d1aSMickaël Salaün */ 483cb2c7d1aSMickaël Salaün static int hook_sb_umount(struct vfsmount *const mnt, const int flags) 484cb2c7d1aSMickaël Salaün { 485cb2c7d1aSMickaël Salaün if (!landlock_get_current_domain()) 486cb2c7d1aSMickaël Salaün return 0; 487cb2c7d1aSMickaël Salaün return -EPERM; 488cb2c7d1aSMickaël Salaün } 489cb2c7d1aSMickaël Salaün 490cb2c7d1aSMickaël Salaün static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts) 491cb2c7d1aSMickaël Salaün { 492cb2c7d1aSMickaël Salaün if (!landlock_get_current_domain()) 493cb2c7d1aSMickaël Salaün return 0; 494cb2c7d1aSMickaël Salaün return -EPERM; 495cb2c7d1aSMickaël Salaün } 496cb2c7d1aSMickaël Salaün 497cb2c7d1aSMickaël Salaün /* 498cb2c7d1aSMickaël Salaün * pivot_root(2), like mount(2), changes the current mount namespace. It must 499cb2c7d1aSMickaël Salaün * then be forbidden for a landlocked process. 500cb2c7d1aSMickaël Salaün * 501cb2c7d1aSMickaël Salaün * However, chroot(2) may be allowed because it only changes the relative root 502cb2c7d1aSMickaël Salaün * directory of the current process. Moreover, it can be used to restrict the 503cb2c7d1aSMickaël Salaün * view of the filesystem. 504cb2c7d1aSMickaël Salaün */ 505cb2c7d1aSMickaël Salaün static int hook_sb_pivotroot(const struct path *const old_path, 506cb2c7d1aSMickaël Salaün const struct path *const new_path) 507cb2c7d1aSMickaël Salaün { 508cb2c7d1aSMickaël Salaün if (!landlock_get_current_domain()) 509cb2c7d1aSMickaël Salaün return 0; 510cb2c7d1aSMickaël Salaün return -EPERM; 511cb2c7d1aSMickaël Salaün } 512cb2c7d1aSMickaël Salaün 513cb2c7d1aSMickaël Salaün /* Path hooks */ 514cb2c7d1aSMickaël Salaün 515cb2c7d1aSMickaël Salaün static inline u32 get_mode_access(const umode_t mode) 516cb2c7d1aSMickaël Salaün { 517cb2c7d1aSMickaël Salaün switch (mode & S_IFMT) { 518cb2c7d1aSMickaël Salaün case S_IFLNK: 519cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_SYM; 520cb2c7d1aSMickaël Salaün case 0: 521cb2c7d1aSMickaël Salaün /* A zero mode translates to S_IFREG. */ 522cb2c7d1aSMickaël Salaün case S_IFREG: 523cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_REG; 524cb2c7d1aSMickaël Salaün case S_IFDIR: 525cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_DIR; 526cb2c7d1aSMickaël Salaün case S_IFCHR: 527cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_CHAR; 528cb2c7d1aSMickaël Salaün case S_IFBLK: 529cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_BLOCK; 530cb2c7d1aSMickaël Salaün case S_IFIFO: 531cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_FIFO; 532cb2c7d1aSMickaël Salaün case S_IFSOCK: 533cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_MAKE_SOCK; 534cb2c7d1aSMickaël Salaün default: 535cb2c7d1aSMickaël Salaün WARN_ON_ONCE(1); 536cb2c7d1aSMickaël Salaün return 0; 537cb2c7d1aSMickaël Salaün } 538cb2c7d1aSMickaël Salaün } 539cb2c7d1aSMickaël Salaün 540cb2c7d1aSMickaël Salaün /* 541cb2c7d1aSMickaël Salaün * Creating multiple links or renaming may lead to privilege escalations if not 542cb2c7d1aSMickaël Salaün * handled properly. Indeed, we must be sure that the source doesn't gain more 543cb2c7d1aSMickaël Salaün * privileges by being accessible from the destination. This is getting more 544cb2c7d1aSMickaël Salaün * complex when dealing with multiple layers. The whole picture can be seen as 545cb2c7d1aSMickaël Salaün * a multilayer partial ordering problem. A future version of Landlock will 546cb2c7d1aSMickaël Salaün * deal with that. 547cb2c7d1aSMickaël Salaün */ 548cb2c7d1aSMickaël Salaün static int hook_path_link(struct dentry *const old_dentry, 549cb2c7d1aSMickaël Salaün const struct path *const new_dir, 550cb2c7d1aSMickaël Salaün struct dentry *const new_dentry) 551cb2c7d1aSMickaël Salaün { 552cb2c7d1aSMickaël Salaün const struct landlock_ruleset *const dom = 553cb2c7d1aSMickaël Salaün landlock_get_current_domain(); 554cb2c7d1aSMickaël Salaün 555cb2c7d1aSMickaël Salaün if (!dom) 556cb2c7d1aSMickaël Salaün return 0; 557cb2c7d1aSMickaël Salaün /* The mount points are the same for old and new paths, cf. EXDEV. */ 558cb2c7d1aSMickaël Salaün if (old_dentry->d_parent != new_dir->dentry) 559cb2c7d1aSMickaël Salaün /* Gracefully forbids reparenting. */ 560cb2c7d1aSMickaël Salaün return -EXDEV; 561cb2c7d1aSMickaël Salaün if (unlikely(d_is_negative(old_dentry))) 562cb2c7d1aSMickaël Salaün return -ENOENT; 563*06a1c40aSMickaël Salaün return check_access_path( 564*06a1c40aSMickaël Salaün dom, new_dir, 565cb2c7d1aSMickaël Salaün get_mode_access(d_backing_inode(old_dentry)->i_mode)); 566cb2c7d1aSMickaël Salaün } 567cb2c7d1aSMickaël Salaün 568cb2c7d1aSMickaël Salaün static inline u32 maybe_remove(const struct dentry *const dentry) 569cb2c7d1aSMickaël Salaün { 570cb2c7d1aSMickaël Salaün if (d_is_negative(dentry)) 571cb2c7d1aSMickaël Salaün return 0; 572cb2c7d1aSMickaël Salaün return d_is_dir(dentry) ? LANDLOCK_ACCESS_FS_REMOVE_DIR : 573cb2c7d1aSMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE; 574cb2c7d1aSMickaël Salaün } 575cb2c7d1aSMickaël Salaün 576cb2c7d1aSMickaël Salaün static int hook_path_rename(const struct path *const old_dir, 577cb2c7d1aSMickaël Salaün struct dentry *const old_dentry, 578cb2c7d1aSMickaël Salaün const struct path *const new_dir, 579cb2c7d1aSMickaël Salaün struct dentry *const new_dentry) 580cb2c7d1aSMickaël Salaün { 581cb2c7d1aSMickaël Salaün const struct landlock_ruleset *const dom = 582cb2c7d1aSMickaël Salaün landlock_get_current_domain(); 583cb2c7d1aSMickaël Salaün 584cb2c7d1aSMickaël Salaün if (!dom) 585cb2c7d1aSMickaël Salaün return 0; 586cb2c7d1aSMickaël Salaün /* The mount points are the same for old and new paths, cf. EXDEV. */ 587cb2c7d1aSMickaël Salaün if (old_dir->dentry != new_dir->dentry) 588cb2c7d1aSMickaël Salaün /* Gracefully forbids reparenting. */ 589cb2c7d1aSMickaël Salaün return -EXDEV; 590cb2c7d1aSMickaël Salaün if (unlikely(d_is_negative(old_dentry))) 591cb2c7d1aSMickaël Salaün return -ENOENT; 592cb2c7d1aSMickaël Salaün /* RENAME_EXCHANGE is handled because directories are the same. */ 593*06a1c40aSMickaël Salaün return check_access_path( 594*06a1c40aSMickaël Salaün dom, old_dir, 595*06a1c40aSMickaël Salaün maybe_remove(old_dentry) | maybe_remove(new_dentry) | 596cb2c7d1aSMickaël Salaün get_mode_access(d_backing_inode(old_dentry)->i_mode)); 597cb2c7d1aSMickaël Salaün } 598cb2c7d1aSMickaël Salaün 599cb2c7d1aSMickaël Salaün static int hook_path_mkdir(const struct path *const dir, 600cb2c7d1aSMickaël Salaün struct dentry *const dentry, const umode_t mode) 601cb2c7d1aSMickaël Salaün { 602cb2c7d1aSMickaël Salaün return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_DIR); 603cb2c7d1aSMickaël Salaün } 604cb2c7d1aSMickaël Salaün 605cb2c7d1aSMickaël Salaün static int hook_path_mknod(const struct path *const dir, 606cb2c7d1aSMickaël Salaün struct dentry *const dentry, const umode_t mode, 607cb2c7d1aSMickaël Salaün const unsigned int dev) 608cb2c7d1aSMickaël Salaün { 609cb2c7d1aSMickaël Salaün const struct landlock_ruleset *const dom = 610cb2c7d1aSMickaël Salaün landlock_get_current_domain(); 611cb2c7d1aSMickaël Salaün 612cb2c7d1aSMickaël Salaün if (!dom) 613cb2c7d1aSMickaël Salaün return 0; 614cb2c7d1aSMickaël Salaün return check_access_path(dom, dir, get_mode_access(mode)); 615cb2c7d1aSMickaël Salaün } 616cb2c7d1aSMickaël Salaün 617cb2c7d1aSMickaël Salaün static int hook_path_symlink(const struct path *const dir, 618*06a1c40aSMickaël Salaün struct dentry *const dentry, 619*06a1c40aSMickaël Salaün const char *const old_name) 620cb2c7d1aSMickaël Salaün { 621cb2c7d1aSMickaël Salaün return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_SYM); 622cb2c7d1aSMickaël Salaün } 623cb2c7d1aSMickaël Salaün 624cb2c7d1aSMickaël Salaün static int hook_path_unlink(const struct path *const dir, 625cb2c7d1aSMickaël Salaün struct dentry *const dentry) 626cb2c7d1aSMickaël Salaün { 627cb2c7d1aSMickaël Salaün return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_FILE); 628cb2c7d1aSMickaël Salaün } 629cb2c7d1aSMickaël Salaün 630cb2c7d1aSMickaël Salaün static int hook_path_rmdir(const struct path *const dir, 631cb2c7d1aSMickaël Salaün struct dentry *const dentry) 632cb2c7d1aSMickaël Salaün { 633cb2c7d1aSMickaël Salaün return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR); 634cb2c7d1aSMickaël Salaün } 635cb2c7d1aSMickaël Salaün 636cb2c7d1aSMickaël Salaün /* File hooks */ 637cb2c7d1aSMickaël Salaün 638cb2c7d1aSMickaël Salaün static inline u32 get_file_access(const struct file *const file) 639cb2c7d1aSMickaël Salaün { 640cb2c7d1aSMickaël Salaün u32 access = 0; 641cb2c7d1aSMickaël Salaün 642cb2c7d1aSMickaël Salaün if (file->f_mode & FMODE_READ) { 643cb2c7d1aSMickaël Salaün /* A directory can only be opened in read mode. */ 644cb2c7d1aSMickaël Salaün if (S_ISDIR(file_inode(file)->i_mode)) 645cb2c7d1aSMickaël Salaün return LANDLOCK_ACCESS_FS_READ_DIR; 646cb2c7d1aSMickaël Salaün access = LANDLOCK_ACCESS_FS_READ_FILE; 647cb2c7d1aSMickaël Salaün } 648cb2c7d1aSMickaël Salaün if (file->f_mode & FMODE_WRITE) 649cb2c7d1aSMickaël Salaün access |= LANDLOCK_ACCESS_FS_WRITE_FILE; 650cb2c7d1aSMickaël Salaün /* __FMODE_EXEC is indeed part of f_flags, not f_mode. */ 651cb2c7d1aSMickaël Salaün if (file->f_flags & __FMODE_EXEC) 652cb2c7d1aSMickaël Salaün access |= LANDLOCK_ACCESS_FS_EXECUTE; 653cb2c7d1aSMickaël Salaün return access; 654cb2c7d1aSMickaël Salaün } 655cb2c7d1aSMickaël Salaün 656cb2c7d1aSMickaël Salaün static int hook_file_open(struct file *const file) 657cb2c7d1aSMickaël Salaün { 658cb2c7d1aSMickaël Salaün const struct landlock_ruleset *const dom = 659cb2c7d1aSMickaël Salaün landlock_get_current_domain(); 660cb2c7d1aSMickaël Salaün 661cb2c7d1aSMickaël Salaün if (!dom) 662cb2c7d1aSMickaël Salaün return 0; 663cb2c7d1aSMickaël Salaün /* 664cb2c7d1aSMickaël Salaün * Because a file may be opened with O_PATH, get_file_access() may 665cb2c7d1aSMickaël Salaün * return 0. This case will be handled with a future Landlock 666cb2c7d1aSMickaël Salaün * evolution. 667cb2c7d1aSMickaël Salaün */ 668cb2c7d1aSMickaël Salaün return check_access_path(dom, &file->f_path, get_file_access(file)); 669cb2c7d1aSMickaël Salaün } 670cb2c7d1aSMickaël Salaün 671cb2c7d1aSMickaël Salaün static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { 672cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(inode_free_security, hook_inode_free_security), 673cb2c7d1aSMickaël Salaün 674cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_delete, hook_sb_delete), 675cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_mount, hook_sb_mount), 676cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(move_mount, hook_move_mount), 677cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_umount, hook_sb_umount), 678cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_remount, hook_sb_remount), 679cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(sb_pivotroot, hook_sb_pivotroot), 680cb2c7d1aSMickaël Salaün 681cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_link, hook_path_link), 682cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_rename, hook_path_rename), 683cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_mkdir, hook_path_mkdir), 684cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_mknod, hook_path_mknod), 685cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_symlink, hook_path_symlink), 686cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_unlink, hook_path_unlink), 687cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(path_rmdir, hook_path_rmdir), 688cb2c7d1aSMickaël Salaün 689cb2c7d1aSMickaël Salaün LSM_HOOK_INIT(file_open, hook_file_open), 690cb2c7d1aSMickaël Salaün }; 691cb2c7d1aSMickaël Salaün 692cb2c7d1aSMickaël Salaün __init void landlock_add_fs_hooks(void) 693cb2c7d1aSMickaël Salaün { 694cb2c7d1aSMickaël Salaün security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), 695cb2c7d1aSMickaël Salaün LANDLOCK_NAME); 696cb2c7d1aSMickaël Salaün } 697