xref: /linux/tools/testing/selftests/namespaces/listns_permissions_test.c (revision 2635f93989bf15456ccfd06df6a1f5a4a3928aa7)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <sched.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <linux/nsfs.h>
11 #include <sys/capability.h>
12 #include <sys/ioctl.h>
13 #include <sys/prctl.h>
14 #include <sys/stat.h>
15 #include <sys/syscall.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <unistd.h>
19 #include "../kselftest_harness.h"
20 #include "../filesystems/utils.h"
21 #include "wrappers.h"
22 
23 /*
24  * Test that unprivileged users can only see namespaces they're currently in.
25  * Create a namespace, drop privileges, verify we can only see our own namespaces.
26  */
27 TEST(listns_unprivileged_current_only)
28 {
29 	struct ns_id_req req = {
30 		.size = sizeof(req),
31 		.spare = 0,
32 		.ns_id = 0,
33 		.ns_type = CLONE_NEWNET,
34 		.spare2 = 0,
35 		.user_ns_id = 0,
36 	};
37 	__u64 ns_ids[100];
38 	ssize_t ret;
39 	int pipefd[2];
40 	pid_t pid;
41 	int status;
42 	bool found_ours;
43 	int unexpected_count;
44 
45 	ASSERT_EQ(pipe(pipefd), 0);
46 
47 	pid = fork();
48 	ASSERT_GE(pid, 0);
49 
50 	if (pid == 0) {
51 		int fd;
52 		__u64 our_netns_id;
53 		bool found_ours;
54 		int unexpected_count;
55 
56 		close(pipefd[0]);
57 
58 		/* Create user namespace to be unprivileged */
59 		if (setup_userns() < 0) {
60 			close(pipefd[1]);
61 			exit(1);
62 		}
63 
64 		/* Create a network namespace */
65 		if (unshare(CLONE_NEWNET) < 0) {
66 			close(pipefd[1]);
67 			exit(1);
68 		}
69 
70 		/* Get our network namespace ID */
71 		fd = open("/proc/self/ns/net", O_RDONLY);
72 		if (fd < 0) {
73 			close(pipefd[1]);
74 			exit(1);
75 		}
76 
77 		if (ioctl(fd, NS_GET_ID, &our_netns_id) < 0) {
78 			close(fd);
79 			close(pipefd[1]);
80 			exit(1);
81 		}
82 		close(fd);
83 
84 		/* Now we're unprivileged - list all network namespaces */
85 		ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
86 		if (ret < 0) {
87 			close(pipefd[1]);
88 			exit(1);
89 		}
90 
91 		/* We should only see our own network namespace */
92 		found_ours = false;
93 		unexpected_count = 0;
94 
95 		for (ssize_t i = 0; i < ret; i++) {
96 			if (ns_ids[i] == our_netns_id) {
97 				found_ours = true;
98 			} else {
99 				/* This is either init_net (which we can see) or unexpected */
100 				unexpected_count++;
101 			}
102 		}
103 
104 		/* Send results to parent */
105 		write(pipefd[1], &found_ours, sizeof(found_ours));
106 		write(pipefd[1], &unexpected_count, sizeof(unexpected_count));
107 		close(pipefd[1]);
108 		exit(0);
109 	}
110 
111 	/* Parent */
112 	close(pipefd[1]);
113 
114 	found_ours = false;
115 	unexpected_count = 0;
116 	read(pipefd[0], &found_ours, sizeof(found_ours));
117 	read(pipefd[0], &unexpected_count, sizeof(unexpected_count));
118 	close(pipefd[0]);
119 
120 	waitpid(pid, &status, 0);
121 	ASSERT_TRUE(WIFEXITED(status));
122 	ASSERT_EQ(WEXITSTATUS(status), 0);
123 
124 	/* Child should have seen its own namespace */
125 	ASSERT_TRUE(found_ours);
126 
127 	TH_LOG("Unprivileged child saw its own namespace, plus %d others (likely init_net)",
128 			unexpected_count);
129 }
130 
131 /*
132  * Test that users with CAP_SYS_ADMIN in a user namespace can see
133  * all namespaces owned by that user namespace.
134  */
135 TEST(listns_cap_sys_admin_in_userns)
136 {
137 	struct ns_id_req req = {
138 		.size = sizeof(req),
139 		.spare = 0,
140 		.ns_id = 0,
141 		.ns_type = 0,  /* All types */
142 		.spare2 = 0,
143 		.user_ns_id = 0,  /* Will be set to our created user namespace */
144 	};
145 	__u64 ns_ids[100];
146 	int pipefd[2];
147 	pid_t pid;
148 	int status;
149 	bool success;
150 	ssize_t count;
151 
152 	ASSERT_EQ(pipe(pipefd), 0);
153 
154 	pid = fork();
155 	ASSERT_GE(pid, 0);
156 
157 	if (pid == 0) {
158 		int fd;
159 		__u64 userns_id;
160 		ssize_t ret;
161 		int min_expected;
162 		bool success;
163 
164 		close(pipefd[0]);
165 
166 		/* Create user namespace - we'll have CAP_SYS_ADMIN in it */
167 		if (setup_userns() < 0) {
168 			close(pipefd[1]);
169 			exit(1);
170 		}
171 
172 		/* Get the user namespace ID */
173 		fd = open("/proc/self/ns/user", O_RDONLY);
174 		if (fd < 0) {
175 			close(pipefd[1]);
176 			exit(1);
177 		}
178 
179 		if (ioctl(fd, NS_GET_ID, &userns_id) < 0) {
180 			close(fd);
181 			close(pipefd[1]);
182 			exit(1);
183 		}
184 		close(fd);
185 
186 		/* Create several namespaces owned by this user namespace */
187 		unshare(CLONE_NEWNET);
188 		unshare(CLONE_NEWUTS);
189 		unshare(CLONE_NEWIPC);
190 
191 		/* List namespaces owned by our user namespace */
192 		req.user_ns_id = userns_id;
193 		ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
194 		if (ret < 0) {
195 			close(pipefd[1]);
196 			exit(1);
197 		}
198 
199 		/*
200 		 * We have CAP_SYS_ADMIN in this user namespace,
201 		 * so we should see all namespaces owned by it.
202 		 * That includes: net, uts, ipc, and the user namespace itself.
203 		 */
204 		min_expected = 4;
205 		success = (ret >= min_expected);
206 
207 		write(pipefd[1], &success, sizeof(success));
208 		write(pipefd[1], &ret, sizeof(ret));
209 		close(pipefd[1]);
210 		exit(0);
211 	}
212 
213 	/* Parent */
214 	close(pipefd[1]);
215 
216 	success = false;
217 	count = 0;
218 	read(pipefd[0], &success, sizeof(success));
219 	read(pipefd[0], &count, sizeof(count));
220 	close(pipefd[0]);
221 
222 	waitpid(pid, &status, 0);
223 	ASSERT_TRUE(WIFEXITED(status));
224 	ASSERT_EQ(WEXITSTATUS(status), 0);
225 
226 	ASSERT_TRUE(success);
227 	TH_LOG("User with CAP_SYS_ADMIN saw %zd namespaces owned by their user namespace",
228 			count);
229 }
230 
231 /*
232  * Test that users cannot see namespaces from unrelated user namespaces.
233  * Create two sibling user namespaces, verify they can't see each other's
234  * owned namespaces.
235  */
236 TEST(listns_cannot_see_sibling_userns_namespaces)
237 {
238 	int pipefd[2];
239 	pid_t pid1, pid2;
240 	int status;
241 	__u64 netns_a_id;
242 	int pipefd2[2];
243 	bool found_sibling_netns;
244 
245 	ASSERT_EQ(pipe(pipefd), 0);
246 
247 	/* Fork first child - creates user namespace A */
248 	pid1 = fork();
249 	ASSERT_GE(pid1, 0);
250 
251 	if (pid1 == 0) {
252 		int fd;
253 		__u64 netns_a_id;
254 		char buf;
255 
256 		close(pipefd[0]);
257 
258 		/* Create user namespace A */
259 		if (setup_userns() < 0) {
260 			close(pipefd[1]);
261 			exit(1);
262 		}
263 
264 		/* Create network namespace owned by user namespace A */
265 		if (unshare(CLONE_NEWNET) < 0) {
266 			close(pipefd[1]);
267 			exit(1);
268 		}
269 
270 		/* Get network namespace ID */
271 		fd = open("/proc/self/ns/net", O_RDONLY);
272 		if (fd < 0) {
273 			close(pipefd[1]);
274 			exit(1);
275 		}
276 
277 		if (ioctl(fd, NS_GET_ID, &netns_a_id) < 0) {
278 			close(fd);
279 			close(pipefd[1]);
280 			exit(1);
281 		}
282 		close(fd);
283 
284 		/* Send namespace ID to parent */
285 		write(pipefd[1], &netns_a_id, sizeof(netns_a_id));
286 
287 		/* Keep alive for sibling to check */
288 		read(pipefd[1], &buf, 1);
289 		close(pipefd[1]);
290 		exit(0);
291 	}
292 
293 	/* Parent reads namespace A ID */
294 	close(pipefd[1]);
295 	netns_a_id = 0;
296 	read(pipefd[0], &netns_a_id, sizeof(netns_a_id));
297 
298 	TH_LOG("User namespace A created network namespace with ID %llu",
299 	       (unsigned long long)netns_a_id);
300 
301 	/* Fork second child - creates user namespace B */
302 	ASSERT_EQ(pipe(pipefd2), 0);
303 
304 	pid2 = fork();
305 	ASSERT_GE(pid2, 0);
306 
307 	if (pid2 == 0) {
308 		struct ns_id_req req = {
309 			.size = sizeof(req),
310 			.spare = 0,
311 			.ns_id = 0,
312 			.ns_type = CLONE_NEWNET,
313 			.spare2 = 0,
314 			.user_ns_id = 0,
315 		};
316 		__u64 ns_ids[100];
317 		ssize_t ret;
318 		bool found_sibling_netns;
319 
320 		close(pipefd[0]);
321 		close(pipefd2[0]);
322 
323 		/* Create user namespace B (sibling to A) */
324 		if (setup_userns() < 0) {
325 			close(pipefd2[1]);
326 			exit(1);
327 		}
328 
329 		/* Try to list all network namespaces */
330 		ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
331 
332 		found_sibling_netns = false;
333 		if (ret > 0) {
334 			for (ssize_t i = 0; i < ret; i++) {
335 				if (ns_ids[i] == netns_a_id) {
336 					found_sibling_netns = true;
337 					break;
338 				}
339 			}
340 		}
341 
342 		/* We should NOT see the sibling's network namespace */
343 		write(pipefd2[1], &found_sibling_netns, sizeof(found_sibling_netns));
344 		close(pipefd2[1]);
345 		exit(0);
346 	}
347 
348 	/* Parent reads result from second child */
349 	close(pipefd2[1]);
350 	found_sibling_netns = false;
351 	read(pipefd2[0], &found_sibling_netns, sizeof(found_sibling_netns));
352 	close(pipefd2[0]);
353 
354 	/* Signal first child to exit */
355 	close(pipefd[0]);
356 
357 	/* Wait for both children */
358 	waitpid(pid2, &status, 0);
359 	ASSERT_TRUE(WIFEXITED(status));
360 
361 	waitpid(pid1, &status, 0);
362 	ASSERT_TRUE(WIFEXITED(status));
363 
364 	/* Second child should NOT have seen first child's namespace */
365 	ASSERT_FALSE(found_sibling_netns);
366 	TH_LOG("User namespace B correctly could not see sibling namespace A's network namespace");
367 }
368 
369 TEST_HARNESS_MAIN
370