xref: /linux/security/landlock/fs.c (revision 06a1c40a09a8dded4bf0e7e3ccbda6bddcccd7c8)
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