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