xref: /aosp_15_r20/external/ltp/testcases/kernel/containers/userns/userns03.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) Huawei Technologies Co., Ltd., 2015
4  * Copyright (C) 2022 SUSE LLC Andrea Cervesato <[email protected]>
5  */
6 
7 /*\
8  * [Description]
9  *
10  * Verify that /proc/PID/uid_map and /proc/PID/gid_map contains three values
11  * separated by white space:
12  *
13  * ID-inside-ns   ID-outside-ns   length
14  *
15  * ID-outside-ns is interpreted according to which process is opening the file.
16  *
17  * If the process opening the file is in the same user namespace as the process
18  * PID, then ID-outside-ns is defined with respect to the parent user namespace.
19  *
20  * If the process opening the file is in a different user namespace, then
21  * ID-outside-ns is defined with respect to the user namespace of the process
22  * opening the file.
23  *
24  * The string "deny" would be written to /proc/self/setgroups before GID
25  * check if setgroups is allowed, see kernel commits:
26  *
27  * - 9cc46516ddf4 ("userns: Add a knob to disable setgroups on a per user namespace basis")
28  * - 66d2f338ee4c ("userns: Allow setting gid_maps without privilege when setgroups is disabled")
29  */
30 
31 #define _GNU_SOURCE
32 
33 #include <stdio.h>
34 #include <stdbool.h>
35 #include "tst_test.h"
36 #include "lapi/sched.h"
37 #include "common.h"
38 
39 #define CHILD1UID 0
40 #define CHILD1GID 0
41 #define CHILD2UID 200
42 #define CHILD2GID 200
43 #define UID_MAP 0
44 #define GID_MAP 1
45 
child_fn1(void)46 static void child_fn1(void)
47 {
48 	TST_CHECKPOINT_WAIT(0);
49 }
50 
child_fn2(int cpid1,int parentuid,int parentgid)51 static void child_fn2(int cpid1, int parentuid, int parentgid)
52 {
53 	int uid, gid;
54 	char cpid1uidpath[BUFSIZ];
55 	char cpid1gidpath[BUFSIZ];
56 	int idinsidens, idoutsidens, length;
57 
58 	TST_CHECKPOINT_WAIT(1);
59 
60 	uid = geteuid();
61 	gid = getegid();
62 
63 	TST_EXP_EQ_LI(uid, CHILD2UID);
64 	TST_EXP_EQ_LI(gid, CHILD2GID);
65 
66 	/* Get the uid parameters of the child_fn2 process */
67 	SAFE_FILE_SCANF("/proc/self/uid_map", "%d %d %d", &idinsidens, &idoutsidens, &length);
68 
69 	/* map file format:ID-inside-ns   ID-outside-ns   length
70 	 * If the process opening the file is in the same user namespace as
71 	 * the process PID, then ID-outside-ns is defined with respect to the
72 	 * parent user namespace
73 	 */
74 	tst_res(TINFO, "child2 checks /proc/cpid2/uid_map");
75 
76 	if (idinsidens != CHILD2UID || idoutsidens != parentuid)
77 		tst_res(TFAIL, "unexpected: namespace ID inside=%d outside=%d", idinsidens, idoutsidens);
78 	else
79 		tst_res(TPASS, "expected namespaces IDs");
80 
81 	sprintf(cpid1uidpath, "/proc/%d/uid_map", cpid1);
82 	SAFE_FILE_SCANF(cpid1uidpath, "%d %d %d", &idinsidens, &idoutsidens, &length);
83 
84 	/* If the process opening the file is in a different user namespace,
85 	 * then ID-outside-ns is defined with respect to the user namespace
86 	 * of the process opening the file
87 	 */
88 	tst_res(TINFO, "child2 checks /proc/cpid1/uid_map");
89 
90 	if (idinsidens != CHILD1UID || idoutsidens != CHILD2UID)
91 		tst_res(TFAIL, "unexpected: namespace ID inside=%d outside=%d", idinsidens, idoutsidens);
92 	else
93 		tst_res(TPASS, "expected namespaces IDs");
94 
95 	sprintf(cpid1gidpath, "/proc/%d/gid_map", cpid1);
96 	SAFE_FILE_SCANF("/proc/self/gid_map", "%d %d %d", &idinsidens, &idoutsidens, &length);
97 
98 	tst_res(TINFO, "child2 checks /proc/cpid2/gid_map");
99 
100 	if (idinsidens != CHILD2GID || idoutsidens != parentgid)
101 		tst_res(TFAIL, "unexpected: namespace ID inside=%d outside=%d", idinsidens, idoutsidens);
102 	else
103 		tst_res(TPASS, "expected namespaces IDs");
104 
105 	SAFE_FILE_SCANF(cpid1gidpath, "%d %d %d", &idinsidens, &idoutsidens, &length);
106 
107 	tst_res(TINFO, "child1 checks /proc/cpid1/gid_map");
108 
109 	if (idinsidens != CHILD1GID || idoutsidens != CHILD2GID)
110 		tst_res(TFAIL, "unexpected: namespace ID inside=%d outside=%d", idinsidens, idoutsidens);
111 	else
112 		tst_res(TPASS, "expected namespaces IDs");
113 
114 	TST_CHECKPOINT_WAKE(0);
115 	TST_CHECKPOINT_WAKE(1);
116 }
117 
run(void)118 static void run(void)
119 {
120 	const struct tst_clone_args args = {
121 		.flags = CLONE_NEWUSER,
122 		.exit_signal = SIGCHLD,
123 	};
124 	pid_t cpid1, cpid2;
125 	uid_t parentuid;
126 	gid_t parentgid;
127 	char path[BUFSIZ];
128 	int fd;
129 
130 	parentuid = geteuid();
131 	parentgid = getegid();
132 
133 	cpid1 = SAFE_CLONE(&args);
134 	if (!cpid1) {
135 		child_fn1();
136 		return;
137 	}
138 
139 	cpid2 = SAFE_CLONE(&args);
140 	if (!cpid2) {
141 		child_fn2(cpid1, parentuid, parentgid);
142 		return;
143 	}
144 
145 	if (access("/proc/self/setgroups", F_OK) == 0) {
146 		sprintf(path, "/proc/%d/setgroups", cpid1);
147 
148 		fd = SAFE_OPEN(path, O_WRONLY, 0644);
149 		SAFE_WRITE(SAFE_WRITE_ALL, fd, "deny", 4);
150 		SAFE_CLOSE(fd);
151 
152 		/* If the setgroups file has the value "deny",
153 		 * then the setgroups(2) system call can't
154 		 * subsequently be reenabled (by writing "allow" to
155 		 * the file) in this user namespace.  (Attempts to
156 		 * do so will fail with the error EPERM.)
157 		 */
158 
159 		tst_res(TINFO, "Check if setgroups can be re-enabled");
160 
161 		fd = SAFE_OPEN(path, O_WRONLY, 0644);
162 		TST_EXP_FAIL2(write(fd, "allow", 5), EPERM);
163 		SAFE_CLOSE(fd);
164 
165 		sprintf(path, "/proc/%d/setgroups", cpid2);
166 
167 		fd = SAFE_OPEN(path, O_WRONLY, 0644);
168 		SAFE_WRITE(SAFE_WRITE_ALL, fd, "deny", 4);
169 		SAFE_CLOSE(fd);
170 	}
171 
172 	updatemap(cpid1, UID_MAP, CHILD1UID, parentuid);
173 	updatemap(cpid2, UID_MAP, CHILD2UID, parentuid);
174 
175 	updatemap(cpid1, GID_MAP, CHILD1GID, parentgid);
176 	updatemap(cpid2, GID_MAP, CHILD2GID, parentgid);
177 
178 	TST_CHECKPOINT_WAKE_AND_WAIT(1);
179 }
180 
181 static struct tst_test test = {
182 	.test_all = run,
183 	.needs_root = 1,
184 	.forks_child = 1,
185 	.needs_checkpoints = 1,
186 	.needs_kconfigs = (const char *[]) {
187 		"CONFIG_USER_NS",
188 		NULL,
189 	},
190 };
191