xref: /linux/tools/testing/selftests/namespaces/uid_map_range_test.c (revision e86d265826b8dc54178e16c4027d3bd5d0299f12)
1*8b7b7739SNatasha Klaus // SPDX-License-Identifier: GPL-2.0
2*8b7b7739SNatasha Klaus #define _GNU_SOURCE
3*8b7b7739SNatasha Klaus #include <errno.h>
4*8b7b7739SNatasha Klaus #include <fcntl.h>
5*8b7b7739SNatasha Klaus #include <sched.h>
6*8b7b7739SNatasha Klaus #include <stdio.h>
7*8b7b7739SNatasha Klaus #include <string.h>
8*8b7b7739SNatasha Klaus #include <sys/wait.h>
9*8b7b7739SNatasha Klaus #include <unistd.h>
10*8b7b7739SNatasha Klaus #include "../kselftest_harness.h"
11*8b7b7739SNatasha Klaus 
12*8b7b7739SNatasha Klaus /*
13*8b7b7739SNatasha Klaus  * The fields of a uid_map/gid_map line are stored in u32. Values that
14*8b7b7739SNatasha Klaus  * do not fit, and negative values, must be rejected with EINVAL instead
15*8b7b7739SNatasha Klaus  * of being silently truncated, and in-range values must still be
16*8b7b7739SNatasha Klaus  * accepted.
17*8b7b7739SNatasha Klaus  */
18*8b7b7739SNatasha Klaus 
write_file(const char * path,const char * buf)19*8b7b7739SNatasha Klaus static int write_file(const char *path, const char *buf)
20*8b7b7739SNatasha Klaus {
21*8b7b7739SNatasha Klaus 	ssize_t len = strlen(buf);
22*8b7b7739SNatasha Klaus 	ssize_t ret;
23*8b7b7739SNatasha Klaus 	int fd;
24*8b7b7739SNatasha Klaus 
25*8b7b7739SNatasha Klaus 	fd = open(path, O_WRONLY | O_CLOEXEC);
26*8b7b7739SNatasha Klaus 	if (fd < 0)
27*8b7b7739SNatasha Klaus 		return -errno;
28*8b7b7739SNatasha Klaus 	ret = write(fd, buf, len);
29*8b7b7739SNatasha Klaus 	if (ret < 0)
30*8b7b7739SNatasha Klaus 		ret = -errno;
31*8b7b7739SNatasha Klaus 	close(fd);
32*8b7b7739SNatasha Klaus 	if (ret < 0)
33*8b7b7739SNatasha Klaus 		return ret;
34*8b7b7739SNatasha Klaus 	return ret == len ? 0 : -EIO;
35*8b7b7739SNatasha Klaus }
36*8b7b7739SNatasha Klaus 
read_file(const char * path,char * buf,size_t size)37*8b7b7739SNatasha Klaus static ssize_t read_file(const char *path, char *buf, size_t size)
38*8b7b7739SNatasha Klaus {
39*8b7b7739SNatasha Klaus 	ssize_t ret;
40*8b7b7739SNatasha Klaus 	int fd;
41*8b7b7739SNatasha Klaus 
42*8b7b7739SNatasha Klaus 	fd = open(path, O_RDONLY | O_CLOEXEC);
43*8b7b7739SNatasha Klaus 	if (fd < 0)
44*8b7b7739SNatasha Klaus 		return -errno;
45*8b7b7739SNatasha Klaus 	ret = read(fd, buf, size - 1);
46*8b7b7739SNatasha Klaus 	if (ret < 0)
47*8b7b7739SNatasha Klaus 		ret = -errno;
48*8b7b7739SNatasha Klaus 	else
49*8b7b7739SNatasha Klaus 		buf[ret] = '\0';
50*8b7b7739SNatasha Klaus 	close(fd);
51*8b7b7739SNatasha Klaus 	return ret;
52*8b7b7739SNatasha Klaus }
53*8b7b7739SNatasha Klaus 
54*8b7b7739SNatasha Klaus /* Enter a fresh user namespace, remembering the outer ids. */
enter_userns(uid_t * uid,gid_t * gid)55*8b7b7739SNatasha Klaus static int enter_userns(uid_t *uid, gid_t *gid)
56*8b7b7739SNatasha Klaus {
57*8b7b7739SNatasha Klaus 	*uid = geteuid();
58*8b7b7739SNatasha Klaus 	*gid = getegid();
59*8b7b7739SNatasha Klaus 	if (unshare(CLONE_NEWUSER) < 0)
60*8b7b7739SNatasha Klaus 		return -errno;
61*8b7b7739SNatasha Klaus 	/* Required before an unprivileged gid_map write. */
62*8b7b7739SNatasha Klaus 	return write_file("/proc/self/setgroups", "deny");
63*8b7b7739SNatasha Klaus }
64*8b7b7739SNatasha Klaus 
expect_rejected(struct __test_metadata * _metadata,const char * path,const char * line)65*8b7b7739SNatasha Klaus static void expect_rejected(struct __test_metadata *_metadata,
66*8b7b7739SNatasha Klaus 			    const char *path, const char *line)
67*8b7b7739SNatasha Klaus {
68*8b7b7739SNatasha Klaus 	char buf[256];
69*8b7b7739SNatasha Klaus 
70*8b7b7739SNatasha Klaus 	TH_LOG("writing \"%s\" to %s", line, path);
71*8b7b7739SNatasha Klaus 	EXPECT_EQ(write_file(path, line), -EINVAL);
72*8b7b7739SNatasha Klaus 	/* Nothing may have been installed. */
73*8b7b7739SNatasha Klaus 	EXPECT_EQ(read_file(path, buf, sizeof(buf)), 0) {
74*8b7b7739SNatasha Klaus 		TH_LOG("installed: %s", buf);
75*8b7b7739SNatasha Klaus 	}
76*8b7b7739SNatasha Klaus }
77*8b7b7739SNatasha Klaus 
expect_accepted(struct __test_metadata * _metadata,const char * path,const char * line,unsigned int first,unsigned int lower_first,unsigned int count)78*8b7b7739SNatasha Klaus static void expect_accepted(struct __test_metadata *_metadata,
79*8b7b7739SNatasha Klaus 			    const char *path, const char *line,
80*8b7b7739SNatasha Klaus 			    unsigned int first, unsigned int lower_first,
81*8b7b7739SNatasha Klaus 			    unsigned int count)
82*8b7b7739SNatasha Klaus {
83*8b7b7739SNatasha Klaus 	unsigned int f, l, c;
84*8b7b7739SNatasha Klaus 	char buf[256];
85*8b7b7739SNatasha Klaus 
86*8b7b7739SNatasha Klaus 	TH_LOG("writing \"%s\" to %s", line, path);
87*8b7b7739SNatasha Klaus 	ASSERT_EQ(write_file(path, line), 0);
88*8b7b7739SNatasha Klaus 
89*8b7b7739SNatasha Klaus 	ASSERT_GT(read_file(path, buf, sizeof(buf)), 0);
90*8b7b7739SNatasha Klaus 	ASSERT_EQ(sscanf(buf, "%u %u %u", &f, &l, &c), 3);
91*8b7b7739SNatasha Klaus 	EXPECT_EQ(f, first);
92*8b7b7739SNatasha Klaus 	EXPECT_EQ(l, lower_first);
93*8b7b7739SNatasha Klaus 	EXPECT_EQ(c, count);
94*8b7b7739SNatasha Klaus 	/* Exactly one extent. */
95*8b7b7739SNatasha Klaus 	EXPECT_EQ(strchr(buf, '\n'), buf + strlen(buf) - 1);
96*8b7b7739SNatasha Klaus }
97*8b7b7739SNatasha Klaus 
TEST(uid_map_first_above_u32)98*8b7b7739SNatasha Klaus TEST(uid_map_first_above_u32)
99*8b7b7739SNatasha Klaus {
100*8b7b7739SNatasha Klaus 	char line[64];
101*8b7b7739SNatasha Klaus 	uid_t uid;
102*8b7b7739SNatasha Klaus 	gid_t gid;
103*8b7b7739SNatasha Klaus 	int ret;
104*8b7b7739SNatasha Klaus 
105*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
106*8b7b7739SNatasha Klaus 	if (ret)
107*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
108*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "4294967301 %u 1", uid);
109*8b7b7739SNatasha Klaus 	expect_rejected(_metadata, "/proc/self/uid_map", line);
110*8b7b7739SNatasha Klaus }
111*8b7b7739SNatasha Klaus 
TEST(uid_map_lower_first_above_u32)112*8b7b7739SNatasha Klaus TEST(uid_map_lower_first_above_u32)
113*8b7b7739SNatasha Klaus {
114*8b7b7739SNatasha Klaus 	char line[64];
115*8b7b7739SNatasha Klaus 	uid_t uid;
116*8b7b7739SNatasha Klaus 	gid_t gid;
117*8b7b7739SNatasha Klaus 	int ret;
118*8b7b7739SNatasha Klaus 
119*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
120*8b7b7739SNatasha Klaus 	if (ret)
121*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
122*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "0 %llu 1", (1ULL << 32) + uid);
123*8b7b7739SNatasha Klaus 	expect_rejected(_metadata, "/proc/self/uid_map", line);
124*8b7b7739SNatasha Klaus }
125*8b7b7739SNatasha Klaus 
TEST(uid_map_count_above_u32)126*8b7b7739SNatasha Klaus TEST(uid_map_count_above_u32)
127*8b7b7739SNatasha Klaus {
128*8b7b7739SNatasha Klaus 	char line[64];
129*8b7b7739SNatasha Klaus 	uid_t uid;
130*8b7b7739SNatasha Klaus 	gid_t gid;
131*8b7b7739SNatasha Klaus 	int ret;
132*8b7b7739SNatasha Klaus 
133*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
134*8b7b7739SNatasha Klaus 	if (ret)
135*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
136*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "0 %u 4294967297", uid);
137*8b7b7739SNatasha Klaus 	expect_rejected(_metadata, "/proc/self/uid_map", line);
138*8b7b7739SNatasha Klaus }
139*8b7b7739SNatasha Klaus 
TEST(gid_map_first_above_u32)140*8b7b7739SNatasha Klaus TEST(gid_map_first_above_u32)
141*8b7b7739SNatasha Klaus {
142*8b7b7739SNatasha Klaus 	char line[64];
143*8b7b7739SNatasha Klaus 	uid_t uid;
144*8b7b7739SNatasha Klaus 	gid_t gid;
145*8b7b7739SNatasha Klaus 	int ret;
146*8b7b7739SNatasha Klaus 
147*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
148*8b7b7739SNatasha Klaus 	if (ret)
149*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
150*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "4294967301 %u 1", gid);
151*8b7b7739SNatasha Klaus 	expect_rejected(_metadata, "/proc/self/gid_map", line);
152*8b7b7739SNatasha Klaus }
153*8b7b7739SNatasha Klaus 
TEST(uid_map_first_negative)154*8b7b7739SNatasha Klaus TEST(uid_map_first_negative)
155*8b7b7739SNatasha Klaus {
156*8b7b7739SNatasha Klaus 	char line[64];
157*8b7b7739SNatasha Klaus 	uid_t uid;
158*8b7b7739SNatasha Klaus 	gid_t gid;
159*8b7b7739SNatasha Klaus 	int ret;
160*8b7b7739SNatasha Klaus 
161*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
162*8b7b7739SNatasha Klaus 	if (ret)
163*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
164*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "-1 %u 1", uid);
165*8b7b7739SNatasha Klaus 	expect_rejected(_metadata, "/proc/self/uid_map", line);
166*8b7b7739SNatasha Klaus }
167*8b7b7739SNatasha Klaus 
TEST(uid_map_lower_first_negative)168*8b7b7739SNatasha Klaus TEST(uid_map_lower_first_negative)
169*8b7b7739SNatasha Klaus {
170*8b7b7739SNatasha Klaus 	uid_t uid;
171*8b7b7739SNatasha Klaus 	gid_t gid;
172*8b7b7739SNatasha Klaus 	int ret;
173*8b7b7739SNatasha Klaus 
174*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
175*8b7b7739SNatasha Klaus 	if (ret)
176*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
177*8b7b7739SNatasha Klaus 	expect_rejected(_metadata, "/proc/self/uid_map", "0 -1 1");
178*8b7b7739SNatasha Klaus }
179*8b7b7739SNatasha Klaus 
TEST(uid_map_count_negative)180*8b7b7739SNatasha Klaus TEST(uid_map_count_negative)
181*8b7b7739SNatasha Klaus {
182*8b7b7739SNatasha Klaus 	char line[64];
183*8b7b7739SNatasha Klaus 	uid_t uid;
184*8b7b7739SNatasha Klaus 	gid_t gid;
185*8b7b7739SNatasha Klaus 	int ret;
186*8b7b7739SNatasha Klaus 
187*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
188*8b7b7739SNatasha Klaus 	if (ret)
189*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
190*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "0 %u -1", uid);
191*8b7b7739SNatasha Klaus 	expect_rejected(_metadata, "/proc/self/uid_map", line);
192*8b7b7739SNatasha Klaus }
193*8b7b7739SNatasha Klaus 
TEST(uid_map_in_range)194*8b7b7739SNatasha Klaus TEST(uid_map_in_range)
195*8b7b7739SNatasha Klaus {
196*8b7b7739SNatasha Klaus 	char line[64];
197*8b7b7739SNatasha Klaus 	uid_t uid;
198*8b7b7739SNatasha Klaus 	gid_t gid;
199*8b7b7739SNatasha Klaus 	int ret;
200*8b7b7739SNatasha Klaus 
201*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
202*8b7b7739SNatasha Klaus 	if (ret)
203*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
204*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "5 %u 1", uid);
205*8b7b7739SNatasha Klaus 	expect_accepted(_metadata, "/proc/self/uid_map", line, 5, uid, 1);
206*8b7b7739SNatasha Klaus }
207*8b7b7739SNatasha Klaus 
TEST(uid_map_zero)208*8b7b7739SNatasha Klaus TEST(uid_map_zero)
209*8b7b7739SNatasha Klaus {
210*8b7b7739SNatasha Klaus 	char line[64];
211*8b7b7739SNatasha Klaus 	uid_t uid;
212*8b7b7739SNatasha Klaus 	gid_t gid;
213*8b7b7739SNatasha Klaus 	int ret;
214*8b7b7739SNatasha Klaus 
215*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
216*8b7b7739SNatasha Klaus 	if (ret)
217*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
218*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "0 %u 1", uid);
219*8b7b7739SNatasha Klaus 	expect_accepted(_metadata, "/proc/self/uid_map", line, 0, uid, 1);
220*8b7b7739SNatasha Klaus }
221*8b7b7739SNatasha Klaus 
222*8b7b7739SNatasha Klaus /* 4294967295 is (u32)-1, which map_write() refuses as a starting id. */
TEST(uid_map_largest_first)223*8b7b7739SNatasha Klaus TEST(uid_map_largest_first)
224*8b7b7739SNatasha Klaus {
225*8b7b7739SNatasha Klaus 	char line[64];
226*8b7b7739SNatasha Klaus 	uid_t uid;
227*8b7b7739SNatasha Klaus 	gid_t gid;
228*8b7b7739SNatasha Klaus 	int ret;
229*8b7b7739SNatasha Klaus 
230*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
231*8b7b7739SNatasha Klaus 	if (ret)
232*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
233*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "4294967294 %u 1", uid);
234*8b7b7739SNatasha Klaus 	expect_accepted(_metadata, "/proc/self/uid_map", line,
235*8b7b7739SNatasha Klaus 			4294967294U, uid, 1);
236*8b7b7739SNatasha Klaus }
237*8b7b7739SNatasha Klaus 
TEST(uid_map_extra_spaces)238*8b7b7739SNatasha Klaus TEST(uid_map_extra_spaces)
239*8b7b7739SNatasha Klaus {
240*8b7b7739SNatasha Klaus 	char line[64];
241*8b7b7739SNatasha Klaus 	uid_t uid;
242*8b7b7739SNatasha Klaus 	gid_t gid;
243*8b7b7739SNatasha Klaus 	int ret;
244*8b7b7739SNatasha Klaus 
245*8b7b7739SNatasha Klaus 	ret = enter_userns(&uid, &gid);
246*8b7b7739SNatasha Klaus 	if (ret)
247*8b7b7739SNatasha Klaus 		SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
248*8b7b7739SNatasha Klaus 	snprintf(line, sizeof(line), "0   %u  1", uid);
249*8b7b7739SNatasha Klaus 	expect_accepted(_metadata, "/proc/self/uid_map", line, 0, uid, 1);
250*8b7b7739SNatasha Klaus }
251*8b7b7739SNatasha Klaus 
252*8b7b7739SNatasha Klaus /*
253*8b7b7739SNatasha Klaus  * An unprivileged writer may only map a single id, so the largest count
254*8b7b7739SNatasha Klaus  * needs a privileged parent writing the map of a child namespace. With
255*8b7b7739SNatasha Klaus  * first and lower_first 0, the no-wrap rule allows up to 4294967295.
256*8b7b7739SNatasha Klaus  */
TEST(uid_map_largest_count)257*8b7b7739SNatasha Klaus TEST(uid_map_largest_count)
258*8b7b7739SNatasha Klaus {
259*8b7b7739SNatasha Klaus 	unsigned int first, lower_first, count;
260*8b7b7739SNatasha Klaus 	int to_child[2], from_child[2];
261*8b7b7739SNatasha Klaus 	char path[64], buf[256];
262*8b7b7739SNatasha Klaus 	pid_t pid;
263*8b7b7739SNatasha Klaus 	char c;
264*8b7b7739SNatasha Klaus 
265*8b7b7739SNatasha Klaus 	if (geteuid() != 0)
266*8b7b7739SNatasha Klaus 		SKIP(return, "needs root in the initial user namespace");
267*8b7b7739SNatasha Klaus 	ASSERT_GT(read_file("/proc/self/uid_map", buf, sizeof(buf)), 0);
268*8b7b7739SNatasha Klaus 	ASSERT_EQ(sscanf(buf, "%u %u %u", &first, &lower_first, &count), 3);
269*8b7b7739SNatasha Klaus 	if (first != 0 || lower_first != 0 || count != 4294967295U)
270*8b7b7739SNatasha Klaus 		SKIP(return, "not in the initial user namespace");
271*8b7b7739SNatasha Klaus 
272*8b7b7739SNatasha Klaus 	ASSERT_EQ(pipe(to_child), 0);
273*8b7b7739SNatasha Klaus 	ASSERT_EQ(pipe(from_child), 0);
274*8b7b7739SNatasha Klaus 	pid = fork();
275*8b7b7739SNatasha Klaus 	ASSERT_GE(pid, 0);
276*8b7b7739SNatasha Klaus 	if (pid == 0) {
277*8b7b7739SNatasha Klaus 		close(to_child[1]);
278*8b7b7739SNatasha Klaus 		close(from_child[0]);
279*8b7b7739SNatasha Klaus 		c = unshare(CLONE_NEWUSER) ? 'F' : 'K';
280*8b7b7739SNatasha Klaus 		if (write(from_child[1], &c, 1) != 1)
281*8b7b7739SNatasha Klaus 			_exit(1);
282*8b7b7739SNatasha Klaus 		/* Stay alive until the parent is done with our uid_map. */
283*8b7b7739SNatasha Klaus 		if (read(to_child[0], &c, 1) < 0)
284*8b7b7739SNatasha Klaus 			_exit(1);
285*8b7b7739SNatasha Klaus 		_exit(0);
286*8b7b7739SNatasha Klaus 	}
287*8b7b7739SNatasha Klaus 	close(to_child[0]);
288*8b7b7739SNatasha Klaus 	close(from_child[1]);
289*8b7b7739SNatasha Klaus 
290*8b7b7739SNatasha Klaus 	ASSERT_EQ(read(from_child[0], &c, 1), 1);
291*8b7b7739SNatasha Klaus 	ASSERT_EQ(c, 'K');
292*8b7b7739SNatasha Klaus 	snprintf(path, sizeof(path), "/proc/%d/uid_map", pid);
293*8b7b7739SNatasha Klaus 	expect_accepted(_metadata, path, "0 0 4294967295", 0, 0, 4294967295U);
294*8b7b7739SNatasha Klaus 
295*8b7b7739SNatasha Klaus 	close(to_child[1]);
296*8b7b7739SNatasha Klaus 	EXPECT_EQ(waitpid(pid, NULL, 0), pid);
297*8b7b7739SNatasha Klaus }
298*8b7b7739SNatasha Klaus 
299*8b7b7739SNatasha Klaus TEST_HARNESS_MAIN
300