xref: /linux/tools/testing/selftests/namespaces/listns_permissions_test.c (revision ec382377311b5032bfd9f4844d8a5884d1facbf6)
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 TEST_HARNESS_MAIN
232