xref: /linux/security/landlock/log.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1e761a88cSMickaël Salaün // SPDX-License-Identifier: GPL-2.0-only
2e761a88cSMickaël Salaün /*
3e761a88cSMickaël Salaün  * Landlock - Log helpers
4e761a88cSMickaël Salaün  *
5e761a88cSMickaël Salaün  * Copyright © 2023-2025 Microsoft Corporation
669ca5782SMickaël Salaün  * Copyright © 2026 Cloudflare, Inc.
7e761a88cSMickaël Salaün  */
8e761a88cSMickaël Salaün 
9e761a88cSMickaël Salaün #include <kunit/test.h>
10e761a88cSMickaël Salaün #include <linux/bitops.h>
11e761a88cSMickaël Salaün #include <uapi/linux/landlock.h>
12e761a88cSMickaël Salaün 
13e761a88cSMickaël Salaün #include "access.h"
14e761a88cSMickaël Salaün #include "audit.h"
15e761a88cSMickaël Salaün #include "common.h"
16e761a88cSMickaël Salaün #include "cred.h"
17e761a88cSMickaël Salaün #include "domain.h"
18e761a88cSMickaël Salaün #include "limits.h"
19e761a88cSMickaël Salaün #include "log.h"
20e761a88cSMickaël Salaün #include "ruleset.h"
2167567f03SMickaël Salaün #include "trace.h"
22e761a88cSMickaël Salaün 
23e761a88cSMickaël Salaün static struct landlock_hierarchy *
24e761a88cSMickaël Salaün get_hierarchy(const struct landlock_domain *const domain, const size_t layer)
25e761a88cSMickaël Salaün {
26e761a88cSMickaël Salaün 	struct landlock_hierarchy *hierarchy = domain->hierarchy;
27e761a88cSMickaël Salaün 	ssize_t i;
28e761a88cSMickaël Salaün 
29e761a88cSMickaël Salaün 	if (WARN_ON_ONCE(layer >= domain->num_layers))
30e761a88cSMickaël Salaün 		return hierarchy;
31e761a88cSMickaël Salaün 
32e761a88cSMickaël Salaün 	for (i = domain->num_layers - 1; i > layer; i--) {
33e761a88cSMickaël Salaün 		if (WARN_ON_ONCE(!hierarchy->parent))
34e761a88cSMickaël Salaün 			break;
35e761a88cSMickaël Salaün 
36e761a88cSMickaël Salaün 		hierarchy = hierarchy->parent;
37e761a88cSMickaël Salaün 	}
38e761a88cSMickaël Salaün 
39e761a88cSMickaël Salaün 	return hierarchy;
40e761a88cSMickaël Salaün }
41e761a88cSMickaël Salaün 
42e761a88cSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
43e761a88cSMickaël Salaün 
44e761a88cSMickaël Salaün static void test_get_hierarchy(struct kunit *const test)
45e761a88cSMickaël Salaün {
46e761a88cSMickaël Salaün 	struct landlock_hierarchy dom0_hierarchy = {
47e761a88cSMickaël Salaün 		.id = 10,
48e761a88cSMickaël Salaün 	};
49e761a88cSMickaël Salaün 	struct landlock_hierarchy dom1_hierarchy = {
50e761a88cSMickaël Salaün 		.parent = &dom0_hierarchy,
51e761a88cSMickaël Salaün 		.id = 20,
52e761a88cSMickaël Salaün 	};
53e761a88cSMickaël Salaün 	struct landlock_hierarchy dom2_hierarchy = {
54e761a88cSMickaël Salaün 		.parent = &dom1_hierarchy,
55e761a88cSMickaël Salaün 		.id = 30,
56e761a88cSMickaël Salaün 	};
57e761a88cSMickaël Salaün 	struct landlock_domain dom2 = {
58e761a88cSMickaël Salaün 		.hierarchy = &dom2_hierarchy,
59e761a88cSMickaël Salaün 		.num_layers = 3,
60e761a88cSMickaël Salaün 	};
61e761a88cSMickaël Salaün 
62e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 10, get_hierarchy(&dom2, 0)->id);
63e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 20, get_hierarchy(&dom2, 1)->id);
64e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, 2)->id);
65e761a88cSMickaël Salaün 	/* KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, -1)->id); */
66e761a88cSMickaël Salaün }
67e761a88cSMickaël Salaün 
68e761a88cSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
69e761a88cSMickaël Salaün 
70e761a88cSMickaël Salaün /* Get the youngest layer that denied the access_request. */
71e761a88cSMickaël Salaün static size_t get_denied_layer(const struct landlock_domain *const domain,
72e761a88cSMickaël Salaün 			       access_mask_t *const access_request,
73e761a88cSMickaël Salaün 			       const struct layer_masks *masks)
74e761a88cSMickaël Salaün {
75e761a88cSMickaël Salaün 	for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) {
76e761a88cSMickaël Salaün 		if (masks->layers[i].access & *access_request) {
77e761a88cSMickaël Salaün 			*access_request &= masks->layers[i].access;
78e761a88cSMickaël Salaün 			return i;
79e761a88cSMickaël Salaün 		}
80e761a88cSMickaël Salaün 	}
81e761a88cSMickaël Salaün 
82e761a88cSMickaël Salaün 	/* Not found - fall back to default values */
83e761a88cSMickaël Salaün 	*access_request = 0;
84e761a88cSMickaël Salaün 	return domain->num_layers - 1;
85e761a88cSMickaël Salaün }
86e761a88cSMickaël Salaün 
87e761a88cSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
88e761a88cSMickaël Salaün 
89e761a88cSMickaël Salaün static void test_get_denied_layer(struct kunit *const test)
90e761a88cSMickaël Salaün {
91e761a88cSMickaël Salaün 	const struct landlock_domain dom = {
92e761a88cSMickaël Salaün 		.num_layers = 5,
93e761a88cSMickaël Salaün 	};
94e761a88cSMickaël Salaün 	const struct layer_masks masks = {
95e761a88cSMickaël Salaün 		.layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |
96e761a88cSMickaël Salaün 				    LANDLOCK_ACCESS_FS_READ_DIR,
97e761a88cSMickaël Salaün 		.layers[1].access = LANDLOCK_ACCESS_FS_READ_FILE |
98e761a88cSMickaël Salaün 				    LANDLOCK_ACCESS_FS_READ_DIR,
99e761a88cSMickaël Salaün 		.layers[2].access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
100e761a88cSMickaël Salaün 	};
101e761a88cSMickaël Salaün 	access_mask_t access;
102e761a88cSMickaël Salaün 
103e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_EXECUTE;
104e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 0, get_denied_layer(&dom, &access, &masks));
105e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_EXECUTE);
106e761a88cSMickaël Salaün 
107e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_READ_FILE;
108e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
109e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_FILE);
110e761a88cSMickaël Salaün 
111e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_READ_DIR;
112e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
113e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR);
114e761a88cSMickaël Salaün 
115e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR;
116e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
117e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access,
118e761a88cSMickaël Salaün 			LANDLOCK_ACCESS_FS_READ_FILE |
119e761a88cSMickaël Salaün 				LANDLOCK_ACCESS_FS_READ_DIR);
120e761a88cSMickaël Salaün 
121e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_DIR;
122e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
123e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR);
124e761a88cSMickaël Salaün 
125e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_WRITE_FILE;
126e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 4, get_denied_layer(&dom, &access, &masks));
127e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, 0);
128e761a88cSMickaël Salaün }
129e761a88cSMickaël Salaün 
130e761a88cSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
131e761a88cSMickaël Salaün 
132e761a88cSMickaël Salaün static size_t
133e761a88cSMickaël Salaün get_layer_from_deny_masks(access_mask_t *const access_request,
134e761a88cSMickaël Salaün 			  const access_mask_t all_existing_optional_access,
135e761a88cSMickaël Salaün 			  const deny_masks_t deny_masks,
136e761a88cSMickaël Salaün 			  optional_access_t quiet_optional_accesses,
137e761a88cSMickaël Salaün 			  bool *quiet)
138e761a88cSMickaël Salaün {
139e761a88cSMickaël Salaün 	const unsigned long access_opt = all_existing_optional_access;
140e761a88cSMickaël Salaün 	const unsigned long access_req = *access_request;
141e761a88cSMickaël Salaün 	access_mask_t missing = 0;
142e761a88cSMickaël Salaün 	size_t youngest_layer = 0;
143e761a88cSMickaël Salaün 	size_t access_index = 0;
144e761a88cSMickaël Salaün 	unsigned long access_bit;
145e761a88cSMickaël Salaün 	bool should_quiet = false;
146e761a88cSMickaël Salaün 
147e761a88cSMickaël Salaün 	/* This will require change with new object types. */
148e761a88cSMickaël Salaün 	WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL);
149e761a88cSMickaël Salaün 
150e761a88cSMickaël Salaün 	for_each_set_bit(access_bit, &access_opt,
151e761a88cSMickaël Salaün 			 BITS_PER_TYPE(access_mask_t)) {
152e761a88cSMickaël Salaün 		if (access_req & BIT(access_bit)) {
153e761a88cSMickaël Salaün 			const size_t layer =
154e761a88cSMickaël Salaün 				(deny_masks >>
155e761a88cSMickaël Salaün 				 (access_index *
156e761a88cSMickaël Salaün 				  HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) &
157e761a88cSMickaël Salaün 				(LANDLOCK_MAX_NUM_LAYERS - 1);
158e761a88cSMickaël Salaün 			const bool layer_has_quiet =
159e761a88cSMickaël Salaün 				!!(quiet_optional_accesses & BIT(access_index));
160e761a88cSMickaël Salaün 
161e761a88cSMickaël Salaün 			if (layer > youngest_layer) {
162e761a88cSMickaël Salaün 				youngest_layer = layer;
163e761a88cSMickaël Salaün 				missing = BIT(access_bit);
164e761a88cSMickaël Salaün 				should_quiet = layer_has_quiet;
165e761a88cSMickaël Salaün 			} else if (layer == youngest_layer) {
166e761a88cSMickaël Salaün 				missing |= BIT(access_bit);
167e761a88cSMickaël Salaün 				/*
168e761a88cSMickaël Salaün 				 * Whether the layer has rules with quiet flag
169e761a88cSMickaël Salaün 				 * covering the file accessed does not depend on
170e761a88cSMickaël Salaün 				 * the access, and so the following
171e761a88cSMickaël Salaün 				 * WARN_ON_ONCE() should not fail.
172e761a88cSMickaël Salaün 				 */
173e761a88cSMickaël Salaün 				WARN_ON_ONCE(should_quiet && !layer_has_quiet);
174e761a88cSMickaël Salaün 				should_quiet = layer_has_quiet;
175e761a88cSMickaël Salaün 			}
176e761a88cSMickaël Salaün 		}
177e761a88cSMickaël Salaün 		access_index++;
178e761a88cSMickaël Salaün 	}
179e761a88cSMickaël Salaün 
180e761a88cSMickaël Salaün 	*access_request = missing;
181e761a88cSMickaël Salaün 	*quiet = should_quiet;
182e761a88cSMickaël Salaün 	return youngest_layer;
183e761a88cSMickaël Salaün }
184e761a88cSMickaël Salaün 
185e761a88cSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
186e761a88cSMickaël Salaün 
187e761a88cSMickaël Salaün static void test_get_layer_from_deny_masks(struct kunit *const test)
188e761a88cSMickaël Salaün {
189e761a88cSMickaël Salaün 	deny_masks_t deny_mask;
190e761a88cSMickaël Salaün 	access_mask_t access;
191e761a88cSMickaël Salaün 	optional_access_t quiet_optional_accesses;
192e761a88cSMickaël Salaün 	bool quiet;
193e761a88cSMickaël Salaün 
194e761a88cSMickaël Salaün 	/* truncate:0 ioctl_dev:2 */
195e761a88cSMickaël Salaün 	deny_mask = 0x20;
196e761a88cSMickaël Salaün 	quiet_optional_accesses = 0;
197e761a88cSMickaël Salaün 
198e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE;
199e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 0,
200e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
201e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
202e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
203e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
204e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
205e761a88cSMickaël Salaün 
206e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
207e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
208e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
209e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
210e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
211e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
212e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
213e761a88cSMickaël Salaün 
214e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
215e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
216e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
217e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
218e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
219e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
220e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
221e761a88cSMickaël Salaün 
222e761a88cSMickaël Salaün 	/* layer denying truncate: quiet, ioctl: not quiet */
223e761a88cSMickaël Salaün 	quiet_optional_accesses = 0b01;
224e761a88cSMickaël Salaün 
225e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE;
226e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 0,
227e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
228e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
229e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
230e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
231e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, true);
232e761a88cSMickaël Salaün 
233e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
234e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
235e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
236e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
237e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
238e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
239e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
240e761a88cSMickaël Salaün 
241e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
242e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
243e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
244e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
245e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
246e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
247e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
248e761a88cSMickaël Salaün 
249e761a88cSMickaël Salaün 	/* Reverse order - truncate:2 ioctl_dev:0 */
250e761a88cSMickaël Salaün 	deny_mask = 0x02;
251e761a88cSMickaël Salaün 	quiet_optional_accesses = 0;
252e761a88cSMickaël Salaün 
253e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE;
254e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
255e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
256e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
257e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
258e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
259e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
260e761a88cSMickaël Salaün 
261e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
262e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 0,
263e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
264e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
265e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
266e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
267e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
268e761a88cSMickaël Salaün 
269e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
270e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
271e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
272e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
273e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
274e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
275e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
276e761a88cSMickaël Salaün 
277e761a88cSMickaël Salaün 	/* layer denying truncate: quiet, ioctl: not quiet */
278e761a88cSMickaël Salaün 	quiet_optional_accesses = 0b01;
279e761a88cSMickaël Salaün 
280e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE;
281e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
282e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
283e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
284e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
285e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
286e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, true);
287e761a88cSMickaël Salaün 
288e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
289e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 0,
290e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
291e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
292e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
293e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
294e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
295e761a88cSMickaël Salaün 
296e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
297e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
298e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
299e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
300e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
301e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
302e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, true);
303e761a88cSMickaël Salaün 
304e761a88cSMickaël Salaün 	/* layer denying truncate: not quiet, ioctl: quiet */
305e761a88cSMickaël Salaün 	quiet_optional_accesses = 0b10;
306e761a88cSMickaël Salaün 
307e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE;
308e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
309e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
310e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
311e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
312e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
313e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
314e761a88cSMickaël Salaün 
315e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
316e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 0,
317e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
318e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
319e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
320e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
321e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, true);
322e761a88cSMickaël Salaün 
323e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
324e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 2,
325e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
326e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
327e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
328e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
329e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
330e761a88cSMickaël Salaün 
331e761a88cSMickaël Salaün 	/* truncate:15 ioctl_dev:15 */
332e761a88cSMickaël Salaün 	deny_mask = 0xff;
333e761a88cSMickaël Salaün 	quiet_optional_accesses = 0;
334e761a88cSMickaël Salaün 
335e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE;
336e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 15,
337e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
338e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
339e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
340e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
341e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
342e761a88cSMickaël Salaün 
343e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
344e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 15,
345e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
346e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
347e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
348e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access,
349e761a88cSMickaël Salaün 			LANDLOCK_ACCESS_FS_TRUNCATE |
350e761a88cSMickaël Salaün 				LANDLOCK_ACCESS_FS_IOCTL_DEV);
351e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, false);
352e761a88cSMickaël Salaün 
353e761a88cSMickaël Salaün 	/* Both quiet (same layer so quietness must be the same) */
354e761a88cSMickaël Salaün 	quiet_optional_accesses = 0b11;
355e761a88cSMickaël Salaün 
356e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE;
357e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 15,
358e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
359e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
360e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
361e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
362e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, true);
363e761a88cSMickaël Salaün 
364e761a88cSMickaël Salaün 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
365e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, 15,
366e761a88cSMickaël Salaün 			get_layer_from_deny_masks(
367e761a88cSMickaël Salaün 				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,
368e761a88cSMickaël Salaün 				deny_mask, quiet_optional_accesses, &quiet));
369e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, access,
370e761a88cSMickaël Salaün 			LANDLOCK_ACCESS_FS_TRUNCATE |
371e761a88cSMickaël Salaün 				LANDLOCK_ACCESS_FS_IOCTL_DEV);
372e761a88cSMickaël Salaün 	KUNIT_EXPECT_EQ(test, quiet, true);
373e761a88cSMickaël Salaün }
374e761a88cSMickaël Salaün 
375e761a88cSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
376e761a88cSMickaël Salaün 
377e761a88cSMickaël Salaün static bool is_valid_request(const struct landlock_request *const request)
378e761a88cSMickaël Salaün {
379e761a88cSMickaël Salaün 	if (WARN_ON_ONCE(request->layer_plus_one > LANDLOCK_MAX_NUM_LAYERS))
380e761a88cSMickaël Salaün 		return false;
381e761a88cSMickaël Salaün 
382e761a88cSMickaël Salaün 	if (WARN_ON_ONCE(!(!!request->layer_plus_one ^ !!request->access)))
383e761a88cSMickaël Salaün 		return false;
384e761a88cSMickaël Salaün 
385e761a88cSMickaël Salaün 	if (request->access) {
386e761a88cSMickaël Salaün 		if (WARN_ON_ONCE(!(!!request->layer_masks ^
387e761a88cSMickaël Salaün 				   !!request->all_existing_optional_access)))
388e761a88cSMickaël Salaün 			return false;
389e761a88cSMickaël Salaün 	} else {
390e761a88cSMickaël Salaün 		if (WARN_ON_ONCE(request->layer_masks ||
391e761a88cSMickaël Salaün 				 request->all_existing_optional_access))
392e761a88cSMickaël Salaün 			return false;
393e761a88cSMickaël Salaün 	}
394e761a88cSMickaël Salaün 
395e761a88cSMickaël Salaün 	if (request->deny_masks) {
396e761a88cSMickaël Salaün 		if (WARN_ON_ONCE(!request->all_existing_optional_access))
397e761a88cSMickaël Salaün 			return false;
398e761a88cSMickaël Salaün 		static_assert(sizeof(request->all_existing_optional_access) ==
399e761a88cSMickaël Salaün 			      sizeof(u32));
400e761a88cSMickaël Salaün 		if (WARN_ON_ONCE(
401e761a88cSMickaël Salaün 			    request->quiet_optional_accesses >=
402e761a88cSMickaël Salaün 			    BIT(hweight32(
403e761a88cSMickaël Salaün 				    request->all_existing_optional_access))))
404e761a88cSMickaël Salaün 			return false;
405e761a88cSMickaël Salaün 	}
406e761a88cSMickaël Salaün 
407e761a88cSMickaël Salaün 	return true;
408e761a88cSMickaël Salaün }
409e761a88cSMickaël Salaün 
41069ca5782SMickaël Salaün static access_mask_t
41169ca5782SMickaël Salaün pick_access_mask_for_request_type(const enum landlock_request_type type,
41269ca5782SMickaël Salaün 				  const struct access_masks access_masks)
41369ca5782SMickaël Salaün {
41469ca5782SMickaël Salaün 	switch (type) {
41569ca5782SMickaël Salaün 	case LANDLOCK_REQUEST_FS_ACCESS:
41669ca5782SMickaël Salaün 		return access_masks.fs;
41769ca5782SMickaël Salaün 	case LANDLOCK_REQUEST_NET_ACCESS:
41869ca5782SMickaël Salaün 		return access_masks.net;
41969ca5782SMickaël Salaün 	default:
42069ca5782SMickaël Salaün 		WARN_ONCE(1, "Invalid request type %d passed to %s", type,
42169ca5782SMickaël Salaün 			  __func__);
42269ca5782SMickaël Salaün 		return 0;
42369ca5782SMickaël Salaün 	}
42469ca5782SMickaël Salaün }
42569ca5782SMickaël Salaün 
42669ca5782SMickaël Salaün /*
42769ca5782SMickaël Salaün  * Whether a quiet rule silences the denial: the rule must cover the whole
42869ca5782SMickaël Salaün  * denied access in the layer that denied it (a quiet rule in a non-denying
42969ca5782SMickaël Salaün  * layer does not suppress the denial).
43069ca5782SMickaël Salaün  */
43169ca5782SMickaël Salaün static bool
43269ca5782SMickaël Salaün is_denial_quieted(const struct landlock_request *const request,
43369ca5782SMickaël Salaün 		  const struct landlock_hierarchy *const youngest_denied,
43469ca5782SMickaël Salaün 		  const access_mask_t missing, const bool object_quiet_flag)
43569ca5782SMickaël Salaün {
43669ca5782SMickaël Salaün 	if (object_quiet_flag) {
43769ca5782SMickaël Salaün 		const access_mask_t quiet_mask =
43869ca5782SMickaël Salaün 			pick_access_mask_for_request_type(
43969ca5782SMickaël Salaün 				request->type, youngest_denied->quiet_masks);
44069ca5782SMickaël Salaün 
44169ca5782SMickaël Salaün 		return (quiet_mask & missing) == missing;
44269ca5782SMickaël Salaün 	}
44369ca5782SMickaël Salaün 
44469ca5782SMickaël Salaün 	/*
44569ca5782SMickaël Salaün 	 * Either the object is not quiet, or this is a scope request.  We check
44669ca5782SMickaël Salaün 	 * request->type to distinguish between the two cases.
44769ca5782SMickaël Salaün 	 */
44869ca5782SMickaël Salaün 	switch (request->type) {
44969ca5782SMickaël Salaün 	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
45069ca5782SMickaël Salaün 		return !!(youngest_denied->quiet_masks.scope &
45169ca5782SMickaël Salaün 			  LANDLOCK_SCOPE_SIGNAL);
45269ca5782SMickaël Salaün 	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
45369ca5782SMickaël Salaün 		return !!(youngest_denied->quiet_masks.scope &
45469ca5782SMickaël Salaün 			  LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
45569ca5782SMickaël Salaün 	/*
45669ca5782SMickaël Salaün 	 * Leave LANDLOCK_REQUEST_PTRACE and LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY
45769ca5782SMickaël Salaün 	 * unhandled for now - they are never quiet.
45869ca5782SMickaël Salaün 	 */
45969ca5782SMickaël Salaün 	default:
46069ca5782SMickaël Salaün 		return false;
46169ca5782SMickaël Salaün 	}
46269ca5782SMickaël Salaün }
46369ca5782SMickaël Salaün 
46469ca5782SMickaël Salaün /*
46569ca5782SMickaël Salaün  * Computes whether a denial from youngest_denied is selected for logging by the
46669ca5782SMickaël Salaün  * domain's policy: its logging must not be disabled (by both per-execution
46769ca5782SMickaël Salaün  * flags being off, or by an ancestor's
46869ca5782SMickaël Salaün  * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF), the per-execution flag matching
46969ca5782SMickaël Salaün  * same_exec must be set, and no quiet rule may cover the denied access.
47069ca5782SMickaël Salaün  * landlock_log_denial() computes this once and passes it to
47169ca5782SMickaël Salaün  * landlock_audit_denial(), which additionally requires audit_enabled.
47269ca5782SMickaël Salaün  */
47369ca5782SMickaël Salaün static bool
47469ca5782SMickaël Salaün is_denial_logged(const struct landlock_request *const request,
47569ca5782SMickaël Salaün 		 const struct landlock_hierarchy *const youngest_denied,
47669ca5782SMickaël Salaün 		 const access_mask_t missing, const bool same_exec,
47769ca5782SMickaël Salaün 		 const bool object_quiet_flag)
47869ca5782SMickaël Salaün {
47969ca5782SMickaël Salaün 	if (READ_ONCE(youngest_denied->log_status) == LANDLOCK_LOG_DISABLED)
48069ca5782SMickaël Salaün 		return false;
48169ca5782SMickaël Salaün 
48269ca5782SMickaël Salaün 	if (!(same_exec ? youngest_denied->log_same_exec :
48369ca5782SMickaël Salaün 			  youngest_denied->log_new_exec))
48469ca5782SMickaël Salaün 		return false;
48569ca5782SMickaël Salaün 
48669ca5782SMickaël Salaün 	return !is_denial_quieted(request, youngest_denied, missing,
48769ca5782SMickaël Salaün 				  object_quiet_flag);
48869ca5782SMickaël Salaün }
48969ca5782SMickaël Salaün 
490e761a88cSMickaël Salaün /**
491e761a88cSMickaël Salaün  * landlock_log_denial - Log a denied access
492e761a88cSMickaël Salaün  *
493e761a88cSMickaël Salaün  * @subject: The Landlock subject's credential denying an action.
494e761a88cSMickaël Salaün  * @request: Detail of the user space request.
495e761a88cSMickaël Salaün  */
496e761a88cSMickaël Salaün void landlock_log_denial(const struct landlock_cred_security *const subject,
497e761a88cSMickaël Salaün 			 const struct landlock_request *const request)
498e761a88cSMickaël Salaün {
499e761a88cSMickaël Salaün 	struct landlock_hierarchy *youngest_denied;
500e761a88cSMickaël Salaün 	size_t youngest_layer;
501e761a88cSMickaël Salaün 	access_mask_t missing;
502e761a88cSMickaël Salaün 	bool object_quiet_flag = false;
503e761a88cSMickaël Salaün 
504e761a88cSMickaël Salaün 	if (WARN_ON_ONCE(!subject || !subject->domain ||
505e761a88cSMickaël Salaün 			 !subject->domain->hierarchy || !request))
506e761a88cSMickaël Salaün 		return;
507e761a88cSMickaël Salaün 
508e761a88cSMickaël Salaün 	if (!is_valid_request(request))
509e761a88cSMickaël Salaün 		return;
510e761a88cSMickaël Salaün 
511e761a88cSMickaël Salaün 	missing = request->access;
512e761a88cSMickaël Salaün 	if (missing) {
513e761a88cSMickaël Salaün 		/* Gets the nearest domain that denies the request. */
514e761a88cSMickaël Salaün 		if (request->layer_masks) {
515e761a88cSMickaël Salaün 			youngest_layer = get_denied_layer(subject->domain,
516e761a88cSMickaël Salaün 							  &missing,
517e761a88cSMickaël Salaün 							  request->layer_masks);
518e761a88cSMickaël Salaün 			object_quiet_flag =
519e761a88cSMickaël Salaün 				request->layer_masks->layers[youngest_layer]
520e761a88cSMickaël Salaün 					.quiet;
521e761a88cSMickaël Salaün 		} else {
522e761a88cSMickaël Salaün 			youngest_layer = get_layer_from_deny_masks(
523e761a88cSMickaël Salaün 				&missing, _LANDLOCK_ACCESS_FS_OPTIONAL,
524e761a88cSMickaël Salaün 				request->deny_masks,
525e761a88cSMickaël Salaün 				request->quiet_optional_accesses,
526e761a88cSMickaël Salaün 				&object_quiet_flag);
527e761a88cSMickaël Salaün 		}
528e761a88cSMickaël Salaün 		youngest_denied =
529e761a88cSMickaël Salaün 			get_hierarchy(subject->domain, youngest_layer);
530e761a88cSMickaël Salaün 	} else {
531e761a88cSMickaël Salaün 		youngest_layer = request->layer_plus_one - 1;
532e761a88cSMickaël Salaün 		youngest_denied =
533e761a88cSMickaël Salaün 			get_hierarchy(subject->domain, youngest_layer);
534e761a88cSMickaël Salaün 	}
535e761a88cSMickaël Salaün 
53669ca5782SMickaël Salaün 	const bool same_exec = !!(subject->domain_exec & BIT(youngest_layer));
53769ca5782SMickaël Salaün 	const bool logged = is_denial_logged(request, youngest_denied, missing,
53869ca5782SMickaël Salaün 					     same_exec, object_quiet_flag);
539e761a88cSMickaël Salaün 
540e761a88cSMickaël Salaün 	/*
541e761a88cSMickaël Salaün 	 * Consistently keeps track of the number of denied access requests even
542e761a88cSMickaël Salaün 	 * if audit is currently disabled, or if audit rules currently exclude
543e761a88cSMickaël Salaün 	 * this record type, or if landlock_restrict_self(2)'s flags quiet logs.
544e761a88cSMickaël Salaün 	 */
545e761a88cSMickaël Salaün 	atomic64_inc(&youngest_denied->num_denials);
546e761a88cSMickaël Salaün 
547*01ce260fSMickaël Salaün 	landlock_trace_denial(request, youngest_denied, missing, same_exec,
548*01ce260fSMickaël Salaün 			      logged);
54969ca5782SMickaël Salaün 	landlock_audit_denial(request, youngest_denied, missing, logged);
550e761a88cSMickaël Salaün }
551e761a88cSMickaël Salaün 
552e761a88cSMickaël Salaün /**
553e761a88cSMickaël Salaün  * landlock_log_free_domain - Log domain deallocation
554e761a88cSMickaël Salaün  *
555e761a88cSMickaël Salaün  * @hierarchy: The domain's hierarchy being deallocated.
556e761a88cSMickaël Salaün  *
55767567f03SMickaël Salaün  * Called from landlock_put_domain_deferred() (via a work queue scheduled by
55867567f03SMickaël Salaün  * hook_cred_free()) or directly from landlock_put_domain().
559e761a88cSMickaël Salaün  */
560e761a88cSMickaël Salaün void landlock_log_free_domain(const struct landlock_hierarchy *const hierarchy)
561e761a88cSMickaël Salaün {
562e761a88cSMickaël Salaün 	if (WARN_ON_ONCE(!hierarchy))
563e761a88cSMickaël Salaün 		return;
564e761a88cSMickaël Salaün 
56567567f03SMickaël Salaün 	landlock_trace_free_domain(hierarchy);
566e761a88cSMickaël Salaün 	landlock_audit_free_domain(hierarchy);
567e761a88cSMickaël Salaün }
568e761a88cSMickaël Salaün 
569e761a88cSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
570e761a88cSMickaël Salaün 
571e761a88cSMickaël Salaün static struct kunit_case test_cases[] = {
572e761a88cSMickaël Salaün 	/* clang-format off */
573e761a88cSMickaël Salaün 	KUNIT_CASE(test_get_hierarchy),
574e761a88cSMickaël Salaün 	KUNIT_CASE(test_get_denied_layer),
575e761a88cSMickaël Salaün 	KUNIT_CASE(test_get_layer_from_deny_masks),
576e761a88cSMickaël Salaün 	{}
577e761a88cSMickaël Salaün 	/* clang-format on */
578e761a88cSMickaël Salaün };
579e761a88cSMickaël Salaün 
580e761a88cSMickaël Salaün static struct kunit_suite test_suite = {
581e761a88cSMickaël Salaün 	.name = "landlock_log",
582e761a88cSMickaël Salaün 	.test_cases = test_cases,
583e761a88cSMickaël Salaün };
584e761a88cSMickaël Salaün 
585e761a88cSMickaël Salaün kunit_test_suite(test_suite);
586e761a88cSMickaël Salaün 
587e761a88cSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
588