Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1#define _GNU_SOURCE
  2
  3#include <cap-ng.h>
  4#include <err.h>
  5#include <linux/capability.h>
  6#include <stdbool.h>
  7#include <string.h>
  8#include <stdio.h>
  9#include <fcntl.h>
 10#include <errno.h>
 11#include <stdarg.h>
 12#include <sched.h>
 13#include <sys/mount.h>
 14#include <limits.h>
 15#include <libgen.h>
 16#include <malloc.h>
 17#include <sys/wait.h>
 18#include <sys/prctl.h>
 19#include <sys/stat.h>
 20
 21#ifndef PR_CAP_AMBIENT
 22#define PR_CAP_AMBIENT			47
 23# define PR_CAP_AMBIENT_IS_SET		1
 24# define PR_CAP_AMBIENT_RAISE		2
 25# define PR_CAP_AMBIENT_LOWER		3
 26# define PR_CAP_AMBIENT_CLEAR_ALL	4
 27#endif
 28
 29static int nerrs;
 30
 31static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
 32{
 33	char buf[4096];
 34	int fd;
 35	ssize_t written;
 36	int buf_len;
 37
 38	buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
 39	if (buf_len < 0) {
 40		err(1, "vsnprintf failed");
 41	}
 42	if (buf_len >= sizeof(buf)) {
 43		errx(1, "vsnprintf output truncated");
 44	}
 45
 46	fd = open(filename, O_WRONLY);
 47	if (fd < 0) {
 48		if ((errno == ENOENT) && enoent_ok)
 49			return;
 50		err(1, "open of %s failed", filename);
 51	}
 52	written = write(fd, buf, buf_len);
 53	if (written != buf_len) {
 54		if (written >= 0) {
 55			errx(1, "short write to %s", filename);
 56		} else {
 57			err(1, "write to %s failed", filename);
 58		}
 59	}
 60	if (close(fd) != 0) {
 61		err(1, "close of %s failed", filename);
 62	}
 63}
 64
 65static void maybe_write_file(char *filename, char *fmt, ...)
 66{
 67	va_list ap;
 68
 69	va_start(ap, fmt);
 70	vmaybe_write_file(true, filename, fmt, ap);
 71	va_end(ap);
 72}
 73
 74static void write_file(char *filename, char *fmt, ...)
 75{
 76	va_list ap;
 77
 78	va_start(ap, fmt);
 79	vmaybe_write_file(false, filename, fmt, ap);
 80	va_end(ap);
 81}
 82
 83static bool create_and_enter_ns(uid_t inner_uid)
 84{
 85	uid_t outer_uid;
 86	gid_t outer_gid;
 87	int i;
 88	bool have_outer_privilege;
 89
 90	outer_uid = getuid();
 91	outer_gid = getgid();
 92
 93	/*
 94	 * TODO: If we're already root, we could skip creating the userns.
 95	 */
 96
 97	if (unshare(CLONE_NEWNS) == 0) {
 98		printf("[NOTE]\tUsing global UIDs for tests\n");
 99		if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0)
100			err(1, "PR_SET_KEEPCAPS");
101		if (setresuid(inner_uid, inner_uid, -1) != 0)
102			err(1, "setresuid");
103
104		// Re-enable effective caps
105		capng_get_caps_process();
106		for (i = 0; i < CAP_LAST_CAP; i++)
107			if (capng_have_capability(CAPNG_PERMITTED, i))
108				capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, i);
109		if (capng_apply(CAPNG_SELECT_CAPS) != 0)
110			err(1, "capng_apply");
111
112		have_outer_privilege = true;
113	} else if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0) {
114		printf("[NOTE]\tUsing a user namespace for tests\n");
115		maybe_write_file("/proc/self/setgroups", "deny");
116		write_file("/proc/self/uid_map", "%d %d 1", inner_uid, outer_uid);
117		write_file("/proc/self/gid_map", "0 %d 1", outer_gid);
118
119		have_outer_privilege = false;
120	} else {
121		errx(1, "must be root or be able to create a userns");
122	}
123
124	if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
125		err(1, "remount everything private");
126
127	return have_outer_privilege;
128}
129
130static void chdir_to_tmpfs(void)
131{
132	char cwd[PATH_MAX];
133	if (getcwd(cwd, sizeof(cwd)) != cwd)
134		err(1, "getcwd");
135
136	if (mount("private_tmp", ".", "tmpfs", 0, "mode=0777") != 0)
137		err(1, "mount private tmpfs");
138
139	if (chdir(cwd) != 0)
140		err(1, "chdir to private tmpfs");
141
142	if (umount2(".", MNT_DETACH) != 0)
143		err(1, "detach private tmpfs");
144}
145
146static void copy_fromat_to(int fromfd, const char *fromname, const char *toname)
147{
148	int from = openat(fromfd, fromname, O_RDONLY);
149	if (from == -1)
150		err(1, "open copy source");
151
152	int to = open(toname, O_CREAT | O_WRONLY | O_EXCL, 0700);
153
154	while (true) {
155		char buf[4096];
156		ssize_t sz = read(from, buf, sizeof(buf));
157		if (sz == 0)
158			break;
159		if (sz < 0)
160			err(1, "read");
161
162		if (write(to, buf, sz) != sz)
163			err(1, "write");	/* no short writes on tmpfs */
164	}
165
166	close(from);
167	close(to);
168}
169
170static bool fork_wait(void)
171{
172	pid_t child = fork();
173	if (child == 0) {
174		nerrs = 0;
175		return true;
176	} else if (child > 0) {
177		int status;
178		if (waitpid(child, &status, 0) != child ||
179		    !WIFEXITED(status)) {
180			printf("[FAIL]\tChild died\n");
181			nerrs++;
182		} else if (WEXITSTATUS(status) != 0) {
183			printf("[FAIL]\tChild failed\n");
184			nerrs++;
185		} else {
186			printf("[OK]\tChild succeeded\n");
187		}
188
189		return false;
190	} else {
191		err(1, "fork");
192	}
193}
194
195static void exec_other_validate_cap(const char *name,
196				    bool eff, bool perm, bool inh, bool ambient)
197{
198	execl(name, name, (eff ? "1" : "0"),
199	      (perm ? "1" : "0"), (inh ? "1" : "0"), (ambient ? "1" : "0"),
200	      NULL);
201	err(1, "execl");
202}
203
204static void exec_validate_cap(bool eff, bool perm, bool inh, bool ambient)
205{
206	exec_other_validate_cap("./validate_cap", eff, perm, inh, ambient);
207}
208
209static int do_tests(int uid, const char *our_path)
210{
211	bool have_outer_privilege = create_and_enter_ns(uid);
212
213	int ourpath_fd = open(our_path, O_RDONLY | O_DIRECTORY);
214	if (ourpath_fd == -1)
215		err(1, "open '%s'", our_path);
216
217	chdir_to_tmpfs();
218
219	copy_fromat_to(ourpath_fd, "validate_cap", "validate_cap");
220
221	if (have_outer_privilege) {
222		uid_t gid = getegid();
223
224		copy_fromat_to(ourpath_fd, "validate_cap",
225			       "validate_cap_suidroot");
226		if (chown("validate_cap_suidroot", 0, -1) != 0)
227			err(1, "chown");
228		if (chmod("validate_cap_suidroot", S_ISUID | 0700) != 0)
229			err(1, "chmod");
230
231		copy_fromat_to(ourpath_fd, "validate_cap",
232			       "validate_cap_suidnonroot");
233		if (chown("validate_cap_suidnonroot", uid + 1, -1) != 0)
234			err(1, "chown");
235		if (chmod("validate_cap_suidnonroot", S_ISUID | 0700) != 0)
236			err(1, "chmod");
237
238		copy_fromat_to(ourpath_fd, "validate_cap",
239			       "validate_cap_sgidroot");
240		if (chown("validate_cap_sgidroot", -1, 0) != 0)
241			err(1, "chown");
242		if (chmod("validate_cap_sgidroot", S_ISGID | 0710) != 0)
243			err(1, "chmod");
244
245		copy_fromat_to(ourpath_fd, "validate_cap",
246			       "validate_cap_sgidnonroot");
247		if (chown("validate_cap_sgidnonroot", -1, gid + 1) != 0)
248			err(1, "chown");
249		if (chmod("validate_cap_sgidnonroot", S_ISGID | 0710) != 0)
250			err(1, "chmod");
251}
252
253	capng_get_caps_process();
254
255	/* Make sure that i starts out clear */
256	capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
257	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
258		err(1, "capng_apply");
259
260	if (uid == 0) {
261		printf("[RUN]\tRoot => ep\n");
262		if (fork_wait())
263			exec_validate_cap(true, true, false, false);
264	} else {
265		printf("[RUN]\tNon-root => no caps\n");
266		if (fork_wait())
267			exec_validate_cap(false, false, false, false);
268	}
269
270	printf("[OK]\tCheck cap_ambient manipulation rules\n");
271
272	/* We should not be able to add ambient caps yet. */
273	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != -1 || errno != EPERM) {
274		if (errno == EINVAL)
275			printf("[FAIL]\tPR_CAP_AMBIENT_RAISE isn't supported\n");
276		else
277			printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have failed eith EPERM on a non-inheritable cap\n");
278		return 1;
279	}
280	printf("[OK]\tPR_CAP_AMBIENT_RAISE failed on non-inheritable cap\n");
281
282	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_RAW);
283	capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_NET_RAW);
284	capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_NET_RAW);
285	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
286		err(1, "capng_apply");
287	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0, 0) != -1 || errno != EPERM) {
288		printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have failed on a non-permitted cap\n");
289		return 1;
290	}
291	printf("[OK]\tPR_CAP_AMBIENT_RAISE failed on non-permitted cap\n");
292
293	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
294	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
295		err(1, "capng_apply");
296	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
297		printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have succeeded\n");
298		return 1;
299	}
300	printf("[OK]\tPR_CAP_AMBIENT_RAISE worked\n");
301
302	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 1) {
303		printf("[FAIL]\tPR_CAP_AMBIENT_IS_SET is broken\n");
304		return 1;
305	}
306
307	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0, 0) != 0)
308		err(1, "PR_CAP_AMBIENT_CLEAR_ALL");
309
310	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
311		printf("[FAIL]\tPR_CAP_AMBIENT_CLEAR_ALL didn't work\n");
312		return 1;
313	}
314
315	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0)
316		err(1, "PR_CAP_AMBIENT_RAISE");
317
318	capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
319	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
320		err(1, "capng_apply");
321
322	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
323		printf("[FAIL]\tDropping I should have dropped A\n");
324		return 1;
325	}
326
327	printf("[OK]\tBasic manipulation appears to work\n");
328
329	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
330	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
331		err(1, "capng_apply");
332	if (uid == 0) {
333		printf("[RUN]\tRoot +i => eip\n");
334		if (fork_wait())
335			exec_validate_cap(true, true, true, false);
336	} else {
337		printf("[RUN]\tNon-root +i => i\n");
338		if (fork_wait())
339			exec_validate_cap(false, false, true, false);
340	}
341
342	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0)
343		err(1, "PR_CAP_AMBIENT_RAISE");
344
345	printf("[RUN]\tUID %d +ia => eipa\n", uid);
346	if (fork_wait())
347		exec_validate_cap(true, true, true, true);
348
349	/* The remaining tests need real privilege */
350
351	if (!have_outer_privilege) {
352		printf("[SKIP]\tSUID/SGID tests (needs privilege)\n");
353		goto done;
354	}
355
356	if (uid == 0) {
357		printf("[RUN]\tRoot +ia, suidroot => eipa\n");
358		if (fork_wait())
359			exec_other_validate_cap("./validate_cap_suidroot",
360						true, true, true, true);
361
362		printf("[RUN]\tRoot +ia, suidnonroot => ip\n");
363		if (fork_wait())
364			exec_other_validate_cap("./validate_cap_suidnonroot",
365						false, true, true, false);
366
367		printf("[RUN]\tRoot +ia, sgidroot => eipa\n");
368		if (fork_wait())
369			exec_other_validate_cap("./validate_cap_sgidroot",
370						true, true, true, true);
371
372		if (fork_wait()) {
373			printf("[RUN]\tRoot, gid != 0, +ia, sgidroot => eip\n");
374			if (setresgid(1, 1, 1) != 0)
375				err(1, "setresgid");
376			exec_other_validate_cap("./validate_cap_sgidroot",
377						true, true, true, false);
378		}
379
380		printf("[RUN]\tRoot +ia, sgidnonroot => eip\n");
381		if (fork_wait())
382			exec_other_validate_cap("./validate_cap_sgidnonroot",
383						true, true, true, false);
384	} else {
385		printf("[RUN]\tNon-root +ia, sgidnonroot => i\n");
386		exec_other_validate_cap("./validate_cap_sgidnonroot",
387						false, false, true, false);
388
389		if (fork_wait()) {
390			printf("[RUN]\tNon-root +ia, sgidroot => i\n");
391			if (setresgid(1, 1, 1) != 0)
392				err(1, "setresgid");
393			exec_other_validate_cap("./validate_cap_sgidroot",
394						false, false, true, false);
395		}
396	}
397
398done:
399	return nerrs ? 1 : 0;
400}
401
402int main(int argc, char **argv)
403{
404	char *tmp1, *tmp2, *our_path;
405
406	/* Find our path */
407	tmp1 = strdup(argv[0]);
408	if (!tmp1)
409		err(1, "strdup");
410	tmp2 = dirname(tmp1);
411	our_path = strdup(tmp2);
412	if (!our_path)
413		err(1, "strdup");
414	free(tmp1);
415
416	if (fork_wait()) {
417		printf("[RUN]\t+++ Tests with uid == 0 +++\n");
418		return do_tests(0, our_path);
419	}
420
421	if (fork_wait()) {
422		printf("[RUN]\t+++ Tests with uid != 0 +++\n");
423		return do_tests(1, our_path);
424	}
425
426	return nerrs ? 1 : 0;
427}