1c67e8ec0SMicah Morton // SPDX-License-Identifier: GPL-2.0
2c67e8ec0SMicah Morton #define _GNU_SOURCE
3c67e8ec0SMicah Morton #include <stdio.h>
4c67e8ec0SMicah Morton #include <errno.h>
5c67e8ec0SMicah Morton #include <pwd.h>
6a1732d68SMicah Morton #include <grp.h>
7c67e8ec0SMicah Morton #include <string.h>
8c67e8ec0SMicah Morton #include <syscall.h>
9c67e8ec0SMicah Morton #include <sys/capability.h>
10c67e8ec0SMicah Morton #include <sys/types.h>
11c67e8ec0SMicah Morton #include <sys/mount.h>
12c67e8ec0SMicah Morton #include <sys/prctl.h>
13c67e8ec0SMicah Morton #include <sys/wait.h>
14c67e8ec0SMicah Morton #include <stdlib.h>
15c67e8ec0SMicah Morton #include <unistd.h>
16c67e8ec0SMicah Morton #include <fcntl.h>
17c67e8ec0SMicah Morton #include <stdbool.h>
18c67e8ec0SMicah Morton #include <stdarg.h>
19c67e8ec0SMicah Morton
20b2927170SMicah Morton /*
21b2927170SMicah Morton * NOTES about this test:
22b2927170SMicah Morton * - requries libcap-dev to be installed on test system
23b2927170SMicah Morton * - requires securityfs to me mounted at /sys/kernel/security, e.g.:
24b2927170SMicah Morton * mount -n -t securityfs -o nodev,noexec,nosuid securityfs /sys/kernel/security
25b2927170SMicah Morton * - needs CONFIG_SECURITYFS and CONFIG_SAFESETID to be enabled
26b2927170SMicah Morton */
27b2927170SMicah Morton
28c67e8ec0SMicah Morton #ifndef CLONE_NEWUSER
29c67e8ec0SMicah Morton # define CLONE_NEWUSER 0x10000000
30c67e8ec0SMicah Morton #endif
31c67e8ec0SMicah Morton
32b2927170SMicah Morton #define ROOT_UGID 0
33b2927170SMicah Morton #define RESTRICTED_PARENT_UGID 1
34b2927170SMicah Morton #define ALLOWED_CHILD1_UGID 2
35b2927170SMicah Morton #define ALLOWED_CHILD2_UGID 3
36b2927170SMicah Morton #define NO_POLICY_UGID 4
37c67e8ec0SMicah Morton
38b2927170SMicah Morton #define UGID_POLICY_STRING "1:2\n1:3\n2:2\n3:3\n"
39b2927170SMicah Morton
40b2927170SMicah Morton char* add_uid_whitelist_policy_file = "/sys/kernel/security/safesetid/uid_allowlist_policy";
41b2927170SMicah Morton char* add_gid_whitelist_policy_file = "/sys/kernel/security/safesetid/gid_allowlist_policy";
42c67e8ec0SMicah Morton
die(char * fmt,...)43c67e8ec0SMicah Morton static void die(char *fmt, ...)
44c67e8ec0SMicah Morton {
45c67e8ec0SMicah Morton va_list ap;
46c67e8ec0SMicah Morton va_start(ap, fmt);
47c67e8ec0SMicah Morton vfprintf(stderr, fmt, ap);
48c67e8ec0SMicah Morton va_end(ap);
49c67e8ec0SMicah Morton exit(EXIT_FAILURE);
50c67e8ec0SMicah Morton }
51c67e8ec0SMicah Morton
vmaybe_write_file(bool enoent_ok,char * filename,char * fmt,va_list ap)52c67e8ec0SMicah Morton static bool vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
53c67e8ec0SMicah Morton {
54c67e8ec0SMicah Morton char buf[4096];
55c67e8ec0SMicah Morton int fd;
56c67e8ec0SMicah Morton ssize_t written;
57c67e8ec0SMicah Morton int buf_len;
58c67e8ec0SMicah Morton
59c67e8ec0SMicah Morton buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
60c67e8ec0SMicah Morton if (buf_len < 0) {
61c67e8ec0SMicah Morton printf("vsnprintf failed: %s\n",
62c67e8ec0SMicah Morton strerror(errno));
63c67e8ec0SMicah Morton return false;
64c67e8ec0SMicah Morton }
65c67e8ec0SMicah Morton if (buf_len >= sizeof(buf)) {
66c67e8ec0SMicah Morton printf("vsnprintf output truncated\n");
67c67e8ec0SMicah Morton return false;
68c67e8ec0SMicah Morton }
69c67e8ec0SMicah Morton
70c67e8ec0SMicah Morton fd = open(filename, O_WRONLY);
71c67e8ec0SMicah Morton if (fd < 0) {
72c67e8ec0SMicah Morton if ((errno == ENOENT) && enoent_ok)
73c67e8ec0SMicah Morton return true;
74c67e8ec0SMicah Morton return false;
75c67e8ec0SMicah Morton }
76c67e8ec0SMicah Morton written = write(fd, buf, buf_len);
77c67e8ec0SMicah Morton if (written != buf_len) {
78c67e8ec0SMicah Morton if (written >= 0) {
79c67e8ec0SMicah Morton printf("short write to %s\n", filename);
80c67e8ec0SMicah Morton return false;
81c67e8ec0SMicah Morton } else {
82c67e8ec0SMicah Morton printf("write to %s failed: %s\n",
83c67e8ec0SMicah Morton filename, strerror(errno));
84c67e8ec0SMicah Morton return false;
85c67e8ec0SMicah Morton }
86c67e8ec0SMicah Morton }
87c67e8ec0SMicah Morton if (close(fd) != 0) {
88c67e8ec0SMicah Morton printf("close of %s failed: %s\n",
89c67e8ec0SMicah Morton filename, strerror(errno));
90c67e8ec0SMicah Morton return false;
91c67e8ec0SMicah Morton }
92c67e8ec0SMicah Morton return true;
93c67e8ec0SMicah Morton }
94c67e8ec0SMicah Morton
write_file(char * filename,char * fmt,...)95c67e8ec0SMicah Morton static bool write_file(char *filename, char *fmt, ...)
96c67e8ec0SMicah Morton {
97c67e8ec0SMicah Morton va_list ap;
98c67e8ec0SMicah Morton bool ret;
99c67e8ec0SMicah Morton
100c67e8ec0SMicah Morton va_start(ap, fmt);
101c67e8ec0SMicah Morton ret = vmaybe_write_file(false, filename, fmt, ap);
102c67e8ec0SMicah Morton va_end(ap);
103c67e8ec0SMicah Morton
104c67e8ec0SMicah Morton return ret;
105c67e8ec0SMicah Morton }
106c67e8ec0SMicah Morton
ensure_user_exists(uid_t uid)107c67e8ec0SMicah Morton static void ensure_user_exists(uid_t uid)
108c67e8ec0SMicah Morton {
109c67e8ec0SMicah Morton struct passwd p;
110c67e8ec0SMicah Morton
111c67e8ec0SMicah Morton FILE *fd;
112c67e8ec0SMicah Morton char name_str[10];
113c67e8ec0SMicah Morton
114c67e8ec0SMicah Morton if (getpwuid(uid) == NULL) {
115c67e8ec0SMicah Morton memset(&p,0x00,sizeof(p));
116c67e8ec0SMicah Morton fd=fopen("/etc/passwd","a");
117c67e8ec0SMicah Morton if (fd == NULL)
118c67e8ec0SMicah Morton die("couldn't open file\n");
119c67e8ec0SMicah Morton if (fseek(fd, 0, SEEK_END))
120c67e8ec0SMicah Morton die("couldn't fseek\n");
121b2927170SMicah Morton snprintf(name_str, 10, "user %d", uid);
122c67e8ec0SMicah Morton p.pw_name=name_str;
123c67e8ec0SMicah Morton p.pw_uid=uid;
124a1732d68SMicah Morton p.pw_gid=uid;
125c67e8ec0SMicah Morton p.pw_gecos="Test account";
126c67e8ec0SMicah Morton p.pw_dir="/dev/null";
127c67e8ec0SMicah Morton p.pw_shell="/bin/false";
128c67e8ec0SMicah Morton int value = putpwent(&p,fd);
129c67e8ec0SMicah Morton if (value != 0)
130c67e8ec0SMicah Morton die("putpwent failed\n");
131c67e8ec0SMicah Morton if (fclose(fd))
132c67e8ec0SMicah Morton die("fclose failed\n");
133c67e8ec0SMicah Morton }
134c67e8ec0SMicah Morton }
135c67e8ec0SMicah Morton
ensure_group_exists(gid_t gid)136a1732d68SMicah Morton static void ensure_group_exists(gid_t gid)
137a1732d68SMicah Morton {
138a1732d68SMicah Morton struct group g;
139a1732d68SMicah Morton
140a1732d68SMicah Morton FILE *fd;
141a1732d68SMicah Morton char name_str[10];
142a1732d68SMicah Morton
143a1732d68SMicah Morton if (getgrgid(gid) == NULL) {
144a1732d68SMicah Morton memset(&g,0x00,sizeof(g));
145a1732d68SMicah Morton fd=fopen("/etc/group","a");
146a1732d68SMicah Morton if (fd == NULL)
147a1732d68SMicah Morton die("couldn't open group file\n");
148a1732d68SMicah Morton if (fseek(fd, 0, SEEK_END))
149a1732d68SMicah Morton die("couldn't fseek group file\n");
150a1732d68SMicah Morton snprintf(name_str, 10, "group %d", gid);
151a1732d68SMicah Morton g.gr_name=name_str;
152a1732d68SMicah Morton g.gr_gid=gid;
153a1732d68SMicah Morton g.gr_passwd=NULL;
154a1732d68SMicah Morton g.gr_mem=NULL;
155a1732d68SMicah Morton int value = putgrent(&g,fd);
156a1732d68SMicah Morton if (value != 0)
157a1732d68SMicah Morton die("putgrent failed\n");
158a1732d68SMicah Morton if (fclose(fd))
159a1732d68SMicah Morton die("fclose failed\n");
160a1732d68SMicah Morton }
161a1732d68SMicah Morton }
162a1732d68SMicah Morton
ensure_securityfs_mounted(void)163c67e8ec0SMicah Morton static void ensure_securityfs_mounted(void)
164c67e8ec0SMicah Morton {
165b2927170SMicah Morton int fd = open(add_uid_whitelist_policy_file, O_WRONLY);
166c67e8ec0SMicah Morton if (fd < 0) {
167c67e8ec0SMicah Morton if (errno == ENOENT) {
168c67e8ec0SMicah Morton // Need to mount securityfs
169c67e8ec0SMicah Morton if (mount("securityfs", "/sys/kernel/security",
170c67e8ec0SMicah Morton "securityfs", 0, NULL) < 0)
171c67e8ec0SMicah Morton die("mounting securityfs failed\n");
172c67e8ec0SMicah Morton } else {
173c67e8ec0SMicah Morton die("couldn't find securityfs for unknown reason\n");
174c67e8ec0SMicah Morton }
175c67e8ec0SMicah Morton } else {
176c67e8ec0SMicah Morton if (close(fd) != 0) {
177c67e8ec0SMicah Morton die("close of %s failed: %s\n",
178b2927170SMicah Morton add_uid_whitelist_policy_file, strerror(errno));
179c67e8ec0SMicah Morton }
180c67e8ec0SMicah Morton }
181c67e8ec0SMicah Morton }
182c67e8ec0SMicah Morton
write_uid_policies()183b2927170SMicah Morton static void write_uid_policies()
184c67e8ec0SMicah Morton {
185b2927170SMicah Morton static char *policy_str = UGID_POLICY_STRING;
186c67e8ec0SMicah Morton ssize_t written;
187c67e8ec0SMicah Morton int fd;
188c67e8ec0SMicah Morton
189b2927170SMicah Morton fd = open(add_uid_whitelist_policy_file, O_WRONLY);
190c67e8ec0SMicah Morton if (fd < 0)
191b2927170SMicah Morton die("can't open add_uid_whitelist_policy file\n");
19203638e62SJann Horn written = write(fd, policy_str, strlen(policy_str));
19303638e62SJann Horn if (written != strlen(policy_str)) {
194c67e8ec0SMicah Morton if (written >= 0) {
195b2927170SMicah Morton die("short write to %s\n", add_uid_whitelist_policy_file);
196c67e8ec0SMicah Morton } else {
197c67e8ec0SMicah Morton die("write to %s failed: %s\n",
198b2927170SMicah Morton add_uid_whitelist_policy_file, strerror(errno));
199c67e8ec0SMicah Morton }
200c67e8ec0SMicah Morton }
201c67e8ec0SMicah Morton if (close(fd) != 0) {
202c67e8ec0SMicah Morton die("close of %s failed: %s\n",
203b2927170SMicah Morton add_uid_whitelist_policy_file, strerror(errno));
204c67e8ec0SMicah Morton }
205c67e8ec0SMicah Morton }
206c67e8ec0SMicah Morton
write_gid_policies()207a1732d68SMicah Morton static void write_gid_policies()
208a1732d68SMicah Morton {
209a1732d68SMicah Morton static char *policy_str = UGID_POLICY_STRING;
210a1732d68SMicah Morton ssize_t written;
211a1732d68SMicah Morton int fd;
212a1732d68SMicah Morton
213a1732d68SMicah Morton fd = open(add_gid_whitelist_policy_file, O_WRONLY);
214a1732d68SMicah Morton if (fd < 0)
215a1732d68SMicah Morton die("can't open add_gid_whitelist_policy file\n");
216a1732d68SMicah Morton written = write(fd, policy_str, strlen(policy_str));
217a1732d68SMicah Morton if (written != strlen(policy_str)) {
218a1732d68SMicah Morton if (written >= 0) {
219a1732d68SMicah Morton die("short write to %s\n", add_gid_whitelist_policy_file);
220a1732d68SMicah Morton } else {
221a1732d68SMicah Morton die("write to %s failed: %s\n",
222a1732d68SMicah Morton add_gid_whitelist_policy_file, strerror(errno));
223a1732d68SMicah Morton }
224a1732d68SMicah Morton }
225a1732d68SMicah Morton if (close(fd) != 0) {
226a1732d68SMicah Morton die("close of %s failed: %s\n",
227a1732d68SMicah Morton add_gid_whitelist_policy_file, strerror(errno));
228a1732d68SMicah Morton }
229a1732d68SMicah Morton }
230a1732d68SMicah Morton
231a1732d68SMicah Morton
test_userns(bool expect_success)232c67e8ec0SMicah Morton static bool test_userns(bool expect_success)
233c67e8ec0SMicah Morton {
234c67e8ec0SMicah Morton uid_t uid;
235c67e8ec0SMicah Morton char map_file_name[32];
236c67e8ec0SMicah Morton size_t sz = sizeof(map_file_name);
237c67e8ec0SMicah Morton pid_t cpid;
238c67e8ec0SMicah Morton bool success;
239c67e8ec0SMicah Morton
240c67e8ec0SMicah Morton uid = getuid();
241c67e8ec0SMicah Morton
242c67e8ec0SMicah Morton int clone_flags = CLONE_NEWUSER;
243c67e8ec0SMicah Morton cpid = syscall(SYS_clone, clone_flags, NULL);
244c67e8ec0SMicah Morton if (cpid == -1) {
245c67e8ec0SMicah Morton printf("clone failed");
246c67e8ec0SMicah Morton return false;
247c67e8ec0SMicah Morton }
248c67e8ec0SMicah Morton
249c67e8ec0SMicah Morton if (cpid == 0) { /* Code executed by child */
250c67e8ec0SMicah Morton // Give parent 1 second to write map file
251c67e8ec0SMicah Morton sleep(1);
252c67e8ec0SMicah Morton exit(EXIT_SUCCESS);
253c67e8ec0SMicah Morton } else { /* Code executed by parent */
254c67e8ec0SMicah Morton if(snprintf(map_file_name, sz, "/proc/%d/uid_map", cpid) < 0) {
255c67e8ec0SMicah Morton printf("preparing file name string failed");
256c67e8ec0SMicah Morton return false;
257c67e8ec0SMicah Morton }
25892c005a1SMicah Morton success = write_file(map_file_name, "0 %d 1", uid);
259c67e8ec0SMicah Morton return success == expect_success;
260c67e8ec0SMicah Morton }
261c67e8ec0SMicah Morton
262c67e8ec0SMicah Morton printf("should not reach here");
263c67e8ec0SMicah Morton return false;
264c67e8ec0SMicah Morton }
265c67e8ec0SMicah Morton
test_setuid(uid_t child_uid,bool expect_success)266c67e8ec0SMicah Morton static void test_setuid(uid_t child_uid, bool expect_success)
267c67e8ec0SMicah Morton {
268c67e8ec0SMicah Morton pid_t cpid, w;
269c67e8ec0SMicah Morton int wstatus;
270c67e8ec0SMicah Morton
271c67e8ec0SMicah Morton cpid = fork();
272c67e8ec0SMicah Morton if (cpid == -1) {
273c67e8ec0SMicah Morton die("fork\n");
274c67e8ec0SMicah Morton }
275c67e8ec0SMicah Morton
276c67e8ec0SMicah Morton if (cpid == 0) { /* Code executed by child */
277295c4e21SMasami Hiramatsu if (setuid(child_uid) < 0)
278295c4e21SMasami Hiramatsu exit(EXIT_FAILURE);
279c67e8ec0SMicah Morton if (getuid() == child_uid)
280c67e8ec0SMicah Morton exit(EXIT_SUCCESS);
281c67e8ec0SMicah Morton else
282c67e8ec0SMicah Morton exit(EXIT_FAILURE);
283c67e8ec0SMicah Morton } else { /* Code executed by parent */
284c67e8ec0SMicah Morton do {
285c67e8ec0SMicah Morton w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
286c67e8ec0SMicah Morton if (w == -1) {
287c67e8ec0SMicah Morton die("waitpid\n");
288c67e8ec0SMicah Morton }
289c67e8ec0SMicah Morton
290c67e8ec0SMicah Morton if (WIFEXITED(wstatus)) {
291c67e8ec0SMicah Morton if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
292c67e8ec0SMicah Morton if (expect_success) {
293c67e8ec0SMicah Morton return;
294c67e8ec0SMicah Morton } else {
295c67e8ec0SMicah Morton die("unexpected success\n");
296c67e8ec0SMicah Morton }
297c67e8ec0SMicah Morton } else {
298c67e8ec0SMicah Morton if (expect_success) {
299c67e8ec0SMicah Morton die("unexpected failure\n");
300c67e8ec0SMicah Morton } else {
301c67e8ec0SMicah Morton return;
302c67e8ec0SMicah Morton }
303c67e8ec0SMicah Morton }
304c67e8ec0SMicah Morton } else if (WIFSIGNALED(wstatus)) {
305c67e8ec0SMicah Morton if (WTERMSIG(wstatus) == 9) {
306c67e8ec0SMicah Morton if (expect_success)
307c67e8ec0SMicah Morton die("killed unexpectedly\n");
308c67e8ec0SMicah Morton else
309c67e8ec0SMicah Morton return;
310c67e8ec0SMicah Morton } else {
311c67e8ec0SMicah Morton die("unexpected signal: %d\n", wstatus);
312c67e8ec0SMicah Morton }
313c67e8ec0SMicah Morton } else {
314c67e8ec0SMicah Morton die("unexpected status: %d\n", wstatus);
315c67e8ec0SMicah Morton }
316c67e8ec0SMicah Morton } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
317c67e8ec0SMicah Morton }
318c67e8ec0SMicah Morton
319c67e8ec0SMicah Morton die("should not reach here\n");
320c67e8ec0SMicah Morton }
321c67e8ec0SMicah Morton
test_setgid(gid_t child_gid,bool expect_success)322a1732d68SMicah Morton static void test_setgid(gid_t child_gid, bool expect_success)
323a1732d68SMicah Morton {
324a1732d68SMicah Morton pid_t cpid, w;
325a1732d68SMicah Morton int wstatus;
326a1732d68SMicah Morton
327a1732d68SMicah Morton cpid = fork();
328a1732d68SMicah Morton if (cpid == -1) {
329a1732d68SMicah Morton die("fork\n");
330a1732d68SMicah Morton }
331a1732d68SMicah Morton
332a1732d68SMicah Morton if (cpid == 0) { /* Code executed by child */
333a1732d68SMicah Morton if (setgid(child_gid) < 0)
334a1732d68SMicah Morton exit(EXIT_FAILURE);
335a1732d68SMicah Morton if (getgid() == child_gid)
336a1732d68SMicah Morton exit(EXIT_SUCCESS);
337a1732d68SMicah Morton else
338a1732d68SMicah Morton exit(EXIT_FAILURE);
339a1732d68SMicah Morton } else { /* Code executed by parent */
340a1732d68SMicah Morton do {
341a1732d68SMicah Morton w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
342a1732d68SMicah Morton if (w == -1) {
343a1732d68SMicah Morton die("waitpid\n");
344a1732d68SMicah Morton }
345a1732d68SMicah Morton
346a1732d68SMicah Morton if (WIFEXITED(wstatus)) {
347a1732d68SMicah Morton if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
348a1732d68SMicah Morton if (expect_success) {
349a1732d68SMicah Morton return;
350a1732d68SMicah Morton } else {
351a1732d68SMicah Morton die("unexpected success\n");
352a1732d68SMicah Morton }
353a1732d68SMicah Morton } else {
354a1732d68SMicah Morton if (expect_success) {
355a1732d68SMicah Morton die("unexpected failure\n");
356a1732d68SMicah Morton } else {
357a1732d68SMicah Morton return;
358a1732d68SMicah Morton }
359a1732d68SMicah Morton }
360a1732d68SMicah Morton } else if (WIFSIGNALED(wstatus)) {
361a1732d68SMicah Morton if (WTERMSIG(wstatus) == 9) {
362a1732d68SMicah Morton if (expect_success)
363a1732d68SMicah Morton die("killed unexpectedly\n");
364a1732d68SMicah Morton else
365a1732d68SMicah Morton return;
366a1732d68SMicah Morton } else {
367a1732d68SMicah Morton die("unexpected signal: %d\n", wstatus);
368a1732d68SMicah Morton }
369a1732d68SMicah Morton } else {
370a1732d68SMicah Morton die("unexpected status: %d\n", wstatus);
371a1732d68SMicah Morton }
372a1732d68SMicah Morton } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
373a1732d68SMicah Morton }
374a1732d68SMicah Morton
375a1732d68SMicah Morton die("should not reach here\n");
376a1732d68SMicah Morton }
377a1732d68SMicah Morton
test_setgroups(gid_t * child_groups,size_t len,bool expect_success)378*64b63483SMicah Morton static void test_setgroups(gid_t* child_groups, size_t len, bool expect_success)
379*64b63483SMicah Morton {
380*64b63483SMicah Morton pid_t cpid, w;
381*64b63483SMicah Morton int wstatus;
382*64b63483SMicah Morton gid_t groupset[len];
383*64b63483SMicah Morton int i, j;
384*64b63483SMicah Morton
385*64b63483SMicah Morton cpid = fork();
386*64b63483SMicah Morton if (cpid == -1) {
387*64b63483SMicah Morton die("fork\n");
388*64b63483SMicah Morton }
389*64b63483SMicah Morton
390*64b63483SMicah Morton if (cpid == 0) { /* Code executed by child */
391*64b63483SMicah Morton if (setgroups(len, child_groups) != 0)
392*64b63483SMicah Morton exit(EXIT_FAILURE);
393*64b63483SMicah Morton if (getgroups(len, groupset) != len)
394*64b63483SMicah Morton exit(EXIT_FAILURE);
395*64b63483SMicah Morton for (i = 0; i < len; i++) {
396*64b63483SMicah Morton for (j = 0; j < len; j++) {
397*64b63483SMicah Morton if (child_groups[i] == groupset[j])
398*64b63483SMicah Morton break;
399*64b63483SMicah Morton if (j == len - 1)
400*64b63483SMicah Morton exit(EXIT_FAILURE);
401*64b63483SMicah Morton }
402*64b63483SMicah Morton }
403*64b63483SMicah Morton exit(EXIT_SUCCESS);
404*64b63483SMicah Morton } else { /* Code executed by parent */
405*64b63483SMicah Morton do {
406*64b63483SMicah Morton w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
407*64b63483SMicah Morton if (w == -1) {
408*64b63483SMicah Morton die("waitpid\n");
409*64b63483SMicah Morton }
410*64b63483SMicah Morton
411*64b63483SMicah Morton if (WIFEXITED(wstatus)) {
412*64b63483SMicah Morton if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
413*64b63483SMicah Morton if (expect_success) {
414*64b63483SMicah Morton return;
415*64b63483SMicah Morton } else {
416*64b63483SMicah Morton die("unexpected success\n");
417*64b63483SMicah Morton }
418*64b63483SMicah Morton } else {
419*64b63483SMicah Morton if (expect_success) {
420*64b63483SMicah Morton die("unexpected failure\n");
421*64b63483SMicah Morton } else {
422*64b63483SMicah Morton return;
423*64b63483SMicah Morton }
424*64b63483SMicah Morton }
425*64b63483SMicah Morton } else if (WIFSIGNALED(wstatus)) {
426*64b63483SMicah Morton if (WTERMSIG(wstatus) == 9) {
427*64b63483SMicah Morton if (expect_success)
428*64b63483SMicah Morton die("killed unexpectedly\n");
429*64b63483SMicah Morton else
430*64b63483SMicah Morton return;
431*64b63483SMicah Morton } else {
432*64b63483SMicah Morton die("unexpected signal: %d\n", wstatus);
433*64b63483SMicah Morton }
434*64b63483SMicah Morton } else {
435*64b63483SMicah Morton die("unexpected status: %d\n", wstatus);
436*64b63483SMicah Morton }
437*64b63483SMicah Morton } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
438*64b63483SMicah Morton }
439*64b63483SMicah Morton
440*64b63483SMicah Morton die("should not reach here\n");
441*64b63483SMicah Morton }
442*64b63483SMicah Morton
443a1732d68SMicah Morton
ensure_users_exist(void)444c67e8ec0SMicah Morton static void ensure_users_exist(void)
445c67e8ec0SMicah Morton {
446b2927170SMicah Morton ensure_user_exists(ROOT_UGID);
447b2927170SMicah Morton ensure_user_exists(RESTRICTED_PARENT_UGID);
448b2927170SMicah Morton ensure_user_exists(ALLOWED_CHILD1_UGID);
449b2927170SMicah Morton ensure_user_exists(ALLOWED_CHILD2_UGID);
450b2927170SMicah Morton ensure_user_exists(NO_POLICY_UGID);
451c67e8ec0SMicah Morton }
452c67e8ec0SMicah Morton
ensure_groups_exist(void)453a1732d68SMicah Morton static void ensure_groups_exist(void)
454a1732d68SMicah Morton {
455a1732d68SMicah Morton ensure_group_exists(ROOT_UGID);
456a1732d68SMicah Morton ensure_group_exists(RESTRICTED_PARENT_UGID);
457a1732d68SMicah Morton ensure_group_exists(ALLOWED_CHILD1_UGID);
458a1732d68SMicah Morton ensure_group_exists(ALLOWED_CHILD2_UGID);
459a1732d68SMicah Morton ensure_group_exists(NO_POLICY_UGID);
460a1732d68SMicah Morton }
461a1732d68SMicah Morton
drop_caps(bool setid_retained)462c67e8ec0SMicah Morton static void drop_caps(bool setid_retained)
463c67e8ec0SMicah Morton {
464c67e8ec0SMicah Morton cap_value_t cap_values[] = {CAP_SETUID, CAP_SETGID};
465c67e8ec0SMicah Morton cap_t caps;
466c67e8ec0SMicah Morton
467c67e8ec0SMicah Morton caps = cap_get_proc();
468c67e8ec0SMicah Morton if (setid_retained)
469c67e8ec0SMicah Morton cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_values, CAP_SET);
470c67e8ec0SMicah Morton else
471c67e8ec0SMicah Morton cap_clear(caps);
472c67e8ec0SMicah Morton cap_set_proc(caps);
473c67e8ec0SMicah Morton cap_free(caps);
474c67e8ec0SMicah Morton }
475c67e8ec0SMicah Morton
main(int argc,char ** argv)476c67e8ec0SMicah Morton int main(int argc, char **argv)
477c67e8ec0SMicah Morton {
478a1732d68SMicah Morton ensure_groups_exist();
479c67e8ec0SMicah Morton ensure_users_exist();
480c67e8ec0SMicah Morton ensure_securityfs_mounted();
481b2927170SMicah Morton write_uid_policies();
482a1732d68SMicah Morton write_gid_policies();
483c67e8ec0SMicah Morton
484c67e8ec0SMicah Morton if (prctl(PR_SET_KEEPCAPS, 1L))
485c67e8ec0SMicah Morton die("Error with set keepcaps\n");
486c67e8ec0SMicah Morton
487b2927170SMicah Morton // First test to make sure we can write userns mappings from a non-root
488b2927170SMicah Morton // user that doesn't have any restrictions (as long as it has
489b2927170SMicah Morton // CAP_SETUID);
490b2927170SMicah Morton if (setgid(NO_POLICY_UGID) < 0)
491b2927170SMicah Morton die("Error with set gid(%d)\n", NO_POLICY_UGID);
492b2927170SMicah Morton if (setuid(NO_POLICY_UGID) < 0)
493b2927170SMicah Morton die("Error with set uid(%d)\n", NO_POLICY_UGID);
494c67e8ec0SMicah Morton // Take away all but setid caps
495c67e8ec0SMicah Morton drop_caps(true);
496c67e8ec0SMicah Morton // Need PR_SET_DUMPABLE flag set so we can write /proc/[pid]/uid_map
497c67e8ec0SMicah Morton // from non-root parent process.
498c67e8ec0SMicah Morton if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0))
499c67e8ec0SMicah Morton die("Error with set dumpable\n");
500c67e8ec0SMicah Morton if (!test_userns(true)) {
501c67e8ec0SMicah Morton die("test_userns failed when it should work\n");
502c67e8ec0SMicah Morton }
503c67e8ec0SMicah Morton
504b2927170SMicah Morton // Now switch to a user/group with restrictions
505b2927170SMicah Morton if (setgid(RESTRICTED_PARENT_UGID) < 0)
506b2927170SMicah Morton die("Error with set gid(%d)\n", RESTRICTED_PARENT_UGID);
507b2927170SMicah Morton if (setuid(RESTRICTED_PARENT_UGID) < 0)
508b2927170SMicah Morton die("Error with set uid(%d)\n", RESTRICTED_PARENT_UGID);
509c67e8ec0SMicah Morton
510b2927170SMicah Morton test_setuid(ROOT_UGID, false);
511b2927170SMicah Morton test_setuid(ALLOWED_CHILD1_UGID, true);
512b2927170SMicah Morton test_setuid(ALLOWED_CHILD2_UGID, true);
513b2927170SMicah Morton test_setuid(NO_POLICY_UGID, false);
514c67e8ec0SMicah Morton
515a1732d68SMicah Morton test_setgid(ROOT_UGID, false);
516a1732d68SMicah Morton test_setgid(ALLOWED_CHILD1_UGID, true);
517a1732d68SMicah Morton test_setgid(ALLOWED_CHILD2_UGID, true);
518a1732d68SMicah Morton test_setgid(NO_POLICY_UGID, false);
519a1732d68SMicah Morton
520*64b63483SMicah Morton gid_t allowed_supp_groups[2] = {ALLOWED_CHILD1_UGID, ALLOWED_CHILD2_UGID};
521*64b63483SMicah Morton gid_t disallowed_supp_groups[2] = {ROOT_UGID, NO_POLICY_UGID};
522*64b63483SMicah Morton test_setgroups(allowed_supp_groups, 2, true);
523*64b63483SMicah Morton test_setgroups(disallowed_supp_groups, 2, false);
524a1732d68SMicah Morton
525c67e8ec0SMicah Morton if (!test_userns(false)) {
526c67e8ec0SMicah Morton die("test_userns worked when it should fail\n");
527c67e8ec0SMicah Morton }
528c67e8ec0SMicah Morton
529c67e8ec0SMicah Morton // Now take away all caps
530c67e8ec0SMicah Morton drop_caps(false);
531c67e8ec0SMicah Morton test_setuid(2, false);
532c67e8ec0SMicah Morton test_setuid(3, false);
533c67e8ec0SMicah Morton test_setuid(4, false);
534a1732d68SMicah Morton test_setgid(2, false);
535a1732d68SMicah Morton test_setgid(3, false);
536a1732d68SMicah Morton test_setgid(4, false);
537c67e8ec0SMicah Morton
538c67e8ec0SMicah Morton // NOTE: this test doesn't clean up users that were created in
539c67e8ec0SMicah Morton // /etc/passwd or flush policies that were added to the LSM.
540b2927170SMicah Morton printf("test successful!\n");
541c67e8ec0SMicah Morton return EXIT_SUCCESS;
542c67e8ec0SMicah Morton }
543