xref: /linux/security/commoncap_test.c (revision 6179d7a630c2747e2a9329149de2a7aa67a95978)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * KUnit tests for commoncap.c security functions
4  *
5  * Tests for security-critical functions in the capability subsystem,
6  * particularly namespace-related capability checks.
7  */
8 
9 #include <kunit/test.h>
10 #include <linux/user_namespace.h>
11 #include <linux/uidgid.h>
12 #include <linux/cred.h>
13 #include <linux/mnt_idmapping.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/refcount.h>
17 
18 #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
19 
20 /* Functions are static in commoncap.c, but we can call them since we're
21  * included in the same compilation unit when tests are enabled.
22  */
23 
24 /**
25  * test_vfsuid_root_in_currentns_init_ns - Test vfsuid_root_in_currentns with init ns
26  *
27  * Verifies that UID 0 in the init namespace correctly owns the current
28  * namespace when running in init_user_ns.
29  *
30  * @test: KUnit test context
31  */
32 static void test_vfsuid_root_in_currentns_init_ns(struct kunit *test)
33 {
34 	vfsuid_t vfsuid;
35 	kuid_t kuid;
36 
37 	/* Create UID 0 in init namespace */
38 	kuid = KUIDT_INIT(0);
39 	vfsuid = VFSUIDT_INIT(kuid);
40 
41 	/* In init namespace, UID 0 should own current namespace */
42 	KUNIT_EXPECT_TRUE(test, vfsuid_root_in_currentns(vfsuid));
43 }
44 
45 /**
46  * test_vfsuid_root_in_currentns_invalid - Test vfsuid_root_in_currentns with invalid vfsuid
47  *
48  * Verifies that an invalid vfsuid correctly returns false.
49  *
50  * @test: KUnit test context
51  */
52 static void test_vfsuid_root_in_currentns_invalid(struct kunit *test)
53 {
54 	vfsuid_t invalid_vfsuid;
55 
56 	/* Use the predefined invalid vfsuid */
57 	invalid_vfsuid = INVALID_VFSUID;
58 
59 	/* Invalid vfsuid should return false */
60 	KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(invalid_vfsuid));
61 }
62 
63 /**
64  * test_vfsuid_root_in_currentns_nonzero - Test vfsuid_root_in_currentns with non-zero UID
65  *
66  * Verifies that a non-zero UID correctly returns false.
67  *
68  * @test: KUnit test context
69  */
70 static void test_vfsuid_root_in_currentns_nonzero(struct kunit *test)
71 {
72 	vfsuid_t vfsuid;
73 	kuid_t kuid;
74 
75 	/* Create a non-zero UID */
76 	kuid = KUIDT_INIT(1000);
77 	vfsuid = VFSUIDT_INIT(kuid);
78 
79 	/* Non-zero UID should return false */
80 	KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(vfsuid));
81 }
82 
83 /**
84  * test_kuid_root_in_ns_init_ns_uid0 - Test kuid_root_in_ns with init namespace and UID 0
85  *
86  * Verifies that kuid_root_in_ns correctly identifies UID 0 in init namespace.
87  * This tests the core namespace traversal logic. In init namespace, UID 0
88  * maps to itself, so it should own the namespace.
89  *
90  * @test: KUnit test context
91  */
92 static void test_kuid_root_in_ns_init_ns_uid0(struct kunit *test)
93 {
94 	kuid_t kuid;
95 	struct user_namespace *init_ns;
96 
97 	kuid = KUIDT_INIT(0);
98 	init_ns = &init_user_ns;
99 
100 	/* UID 0 should own init namespace */
101 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(kuid, init_ns));
102 }
103 
104 /**
105  * test_kuid_root_in_ns_init_ns_nonzero - Test kuid_root_in_ns with init namespace and non-zero UID
106  *
107  * Verifies that kuid_root_in_ns correctly rejects non-zero UIDs in init namespace.
108  * Only UID 0 should own a namespace.
109  *
110  * @test: KUnit test context
111  */
112 static void test_kuid_root_in_ns_init_ns_nonzero(struct kunit *test)
113 {
114 	kuid_t kuid;
115 	struct user_namespace *init_ns;
116 
117 	kuid = KUIDT_INIT(1000);
118 	init_ns = &init_user_ns;
119 
120 	/* Non-zero UID should not own namespace */
121 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(kuid, init_ns));
122 }
123 
124 /**
125  * create_test_user_ns_with_mapping - Create a mock user namespace with UID mapping
126  *
127  * Creates a minimal user namespace structure for testing where uid 0 in the
128  * namespace maps to a specific kuid in the parent namespace.
129  *
130  * @test: KUnit test context
131  * @parent_ns: Parent namespace (typically init_user_ns)
132  * @mapped_kuid: The kuid that uid 0 in this namespace maps to in parent
133  *
134  * Returns: Pointer to allocated namespace, or NULL on failure
135  */
136 static struct user_namespace *create_test_user_ns_with_mapping(struct kunit *test,
137 								 struct user_namespace *parent_ns,
138 								 kuid_t mapped_kuid)
139 {
140 	struct user_namespace *ns;
141 	struct uid_gid_extent extent;
142 
143 	/* Allocate a test namespace - use kzalloc to zero all fields */
144 	ns = kunit_kzalloc(test, sizeof(*ns), GFP_KERNEL);
145 	if (!ns)
146 		return NULL;
147 
148 	/* Initialize basic namespace structure fields */
149 	ns->parent = parent_ns;
150 	ns->level = parent_ns ? parent_ns->level + 1 : 0;
151 	ns->owner = mapped_kuid;
152 	ns->group = KGIDT_INIT(0);
153 
154 	/* Initialize ns_common structure */
155 	refcount_set(&ns->ns.__ns_ref, 1);
156 	ns->ns.inum = 0; /* Mock inum */
157 
158 	/* Set up uid mapping: uid 0 in this namespace maps to mapped_kuid in parent
159 	 * Format: first (uid in ns) : lower_first (kuid in parent) : count
160 	 * So: uid 0 in ns -> kuid mapped_kuid in parent
161 	 * This means from_kuid(ns, mapped_kuid) returns 0
162 	 */
163 	extent.first = 0;                              /* uid 0 in this namespace */
164 	extent.lower_first = __kuid_val(mapped_kuid);  /* maps to this kuid in parent */
165 	extent.count = 1;
166 
167 	ns->uid_map.extent[0] = extent;
168 	ns->uid_map.nr_extents = 1;
169 
170 	/* Set up gid mapping: gid 0 maps to gid 0 in parent (simplified) */
171 	extent.first = 0;
172 	extent.lower_first = 0;
173 	extent.count = 1;
174 
175 	ns->gid_map.extent[0] = extent;
176 	ns->gid_map.nr_extents = 1;
177 
178 	return ns;
179 }
180 
181 /**
182  * test_kuid_root_in_ns_with_mapping - Test kuid_root_in_ns with namespace where uid 0
183  *				       maps to different kuid
184  *
185  * Creates a user namespace where uid 0 maps to kuid 1000 in the parent namespace.
186  * Verifies that kuid_root_in_ns correctly identifies kuid 1000 as owning the namespace.
187  *
188  * Note: kuid_root_in_ns walks up the namespace hierarchy, so it checks the current
189  * namespace first, then parent, then parent's parent, etc. So:
190  * - kuid 1000 owns test_ns because from_kuid(test_ns, 1000) == 0
191  * - kuid 0 also owns test_ns because from_kuid(init_user_ns, 0) == 0
192  *   (checked in parent)
193  *
194  * This tests the actual functionality as requested: creating namespaces with
195  * different values for the namespace's uid 0.
196  *
197  * @test: KUnit test context
198  */
199 static void test_kuid_root_in_ns_with_mapping(struct kunit *test)
200 {
201 	struct user_namespace *test_ns;
202 	struct user_namespace *parent_ns;
203 	kuid_t mapped_kuid, other_kuid;
204 
205 	parent_ns = &init_user_ns;
206 	mapped_kuid = KUIDT_INIT(1000);
207 	other_kuid = KUIDT_INIT(2000);
208 
209 	test_ns = create_test_user_ns_with_mapping(test, parent_ns, mapped_kuid);
210 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_ns);
211 
212 	/* kuid 1000 should own test_ns because it maps to uid 0 in test_ns */
213 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(mapped_kuid, test_ns));
214 
215 	/* kuid 0 should also own test_ns (checked via parent init_user_ns) */
216 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), test_ns));
217 
218 	/* Other kuids should not own test_ns */
219 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(other_kuid, test_ns));
220 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(500), test_ns));
221 }
222 
223 /**
224  * test_kuid_root_in_ns_with_different_mappings - Test with multiple namespaces
225  *
226  * Creates multiple user namespaces with different UID mappings to verify
227  * that kuid_root_in_ns correctly distinguishes between namespaces.
228  *
229  * Each namespace maps uid 0 to a different kuid, and we verify that each
230  * kuid only owns its corresponding namespace (plus kuid 0 owns all via
231  * init_user_ns parent).
232  *
233  * @test: KUnit test context
234  */
235 static void test_kuid_root_in_ns_with_different_mappings(struct kunit *test)
236 {
237 	struct user_namespace *ns1, *ns2, *ns3;
238 
239 	/* Create three independent namespaces, each mapping uid 0 to different kuids */
240 	ns1 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(1000));
241 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1);
242 
243 	ns2 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(2000));
244 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2);
245 
246 	ns3 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(3000));
247 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3);
248 
249 	/* Test ns1: kuid 1000 owns it, kuid 0 owns it (via parent), others do not */
250 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns1));
251 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns1));
252 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns1));
253 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns1));
254 
255 	/* Test ns2: kuid 2000 owns it, kuid 0 owns it (via parent), others do not */
256 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns2));
257 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns2));
258 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns2));
259 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns2));
260 
261 	/* Test ns3: kuid 3000 owns it, kuid 0 owns it (via parent), others do not */
262 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns3));
263 	KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns3));
264 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns3));
265 	KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns3));
266 }
267 
268 static struct kunit_case commoncap_test_cases[] = {
269 	KUNIT_CASE(test_vfsuid_root_in_currentns_init_ns),
270 	KUNIT_CASE(test_vfsuid_root_in_currentns_invalid),
271 	KUNIT_CASE(test_vfsuid_root_in_currentns_nonzero),
272 	KUNIT_CASE(test_kuid_root_in_ns_init_ns_uid0),
273 	KUNIT_CASE(test_kuid_root_in_ns_init_ns_nonzero),
274 	KUNIT_CASE(test_kuid_root_in_ns_with_mapping),
275 	KUNIT_CASE(test_kuid_root_in_ns_with_different_mappings),
276 	{}
277 };
278 
279 static struct kunit_suite commoncap_test_suite = {
280 	.name = "commoncap",
281 	.test_cases = commoncap_test_cases,
282 };
283 
284 kunit_test_suite(commoncap_test_suite);
285 
286 MODULE_LICENSE("GPL");
287 
288 #endif /* CONFIG_SECURITY_COMMONCAP_KUNIT_TEST */
289