File Index
Files and directories
Sign in
FILE

test_execve.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/capabilities/test_execve.c C
  1// SPDX-License-Identifier: GPL-2.0
  2#define _GNU_SOURCE
  3
  4#include <cap-ng.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#include "kselftest.h"
 22
 23static int nerrs;
 24static pid_t mpid;	/*  main() pid is used to avoid duplicate test counts */
 25
 26static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
 27{
 28	char buf[4096];
 29	int fd;
 30	ssize_t written;
 31	int buf_len;
 32
 33	buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
 34	if (buf_len < 0)
 35		ksft_exit_fail_msg("vsnprintf failed - %s\n", strerror(errno));
 36
 37	if (buf_len >= sizeof(buf))
 38		ksft_exit_fail_msg("vsnprintf output truncated\n");
 39
 40
 41	fd = open(filename, O_WRONLY);
 42	if (fd < 0) {
 43		if ((errno == ENOENT) && enoent_ok)
 44			return;
 45		ksft_exit_fail_msg("open of %s failed - %s\n",
 46					filename, strerror(errno));
 47	}
 48	written = write(fd, buf, buf_len);
 49	if (written != buf_len) {
 50		if (written >= 0) {
 51			ksft_exit_fail_msg("short write to %s\n", filename);
 52		} else {
 53			ksft_exit_fail_msg("write to %s failed - %s\n",
 54						filename, strerror(errno));
 55		}
 56	}
 57	if (close(fd) != 0) {
 58		ksft_exit_fail_msg("close of %s failed - %s\n",
 59					filename, strerror(errno));
 60	}
 61}
 62
 63static void maybe_write_file(char *filename, char *fmt, ...)
 64{
 65	va_list ap;
 66
 67	va_start(ap, fmt);
 68	vmaybe_write_file(true, filename, fmt, ap);
 69	va_end(ap);
 70}
 71
 72static void write_file(char *filename, char *fmt, ...)
 73{
 74	va_list ap;
 75
 76	va_start(ap, fmt);
 77	vmaybe_write_file(false, filename, fmt, ap);
 78	va_end(ap);
 79}
 80
 81static bool create_and_enter_ns(uid_t inner_uid)
 82{
 83	uid_t outer_uid;
 84	gid_t outer_gid;
 85	int i, ret;
 86	bool have_outer_privilege;
 87
 88	outer_uid = getuid();
 89	outer_gid = getgid();
 90
 91	if (outer_uid == 0 && unshare(CLONE_NEWNS) == 0) {
 92		ksft_print_msg("[NOTE]\tUsing global UIDs for tests\n");
 93		if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0)
 94			ksft_exit_fail_msg("PR_SET_KEEPCAPS - %s\n",
 95						strerror(errno));
 96		if (setresuid(inner_uid, inner_uid, -1) != 0)
 97			ksft_exit_fail_msg("setresuid - %s\n", strerror(errno));
 98
 99		// Re-enable effective caps
100		ret = capng_get_caps_process();
101		if (ret == -1)
102			ksft_exit_fail_msg("capng_get_caps_process failed\n");
103
104		for (i = 0; i < CAP_LAST_CAP; i++)
105			if (capng_have_capability(CAPNG_PERMITTED, i))
106				capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, i);
107		if (capng_apply(CAPNG_SELECT_CAPS) != 0)
108			ksft_exit_fail_msg(
109					"capng_apply - %s\n", strerror(errno));
110
111		have_outer_privilege = true;
112	} else if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0) {
113		ksft_print_msg("[NOTE]\tUsing a user namespace for tests\n");
114		maybe_write_file("/proc/self/setgroups", "deny");
115		write_file("/proc/self/uid_map", "%d %d 1", inner_uid, outer_uid);
116		write_file("/proc/self/gid_map", "0 %d 1", outer_gid);
117
118		have_outer_privilege = false;
119	} else {
120		ksft_exit_skip("must be root or be able to create a userns\n");
121	}
122
123	if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
124		ksft_exit_fail_msg("remount everything private - %s\n",
125					strerror(errno));
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		ksft_exit_fail_msg("getcwd - %s\n", strerror(errno));
135
136	if (mount("private_tmp", ".", "tmpfs", 0, "mode=0777") != 0)
137		ksft_exit_fail_msg("mount private tmpfs - %s\n",
138					strerror(errno));
139
140	if (chdir(cwd) != 0)
141		ksft_exit_fail_msg("chdir to private tmpfs - %s\n",
142					strerror(errno));
143}
144
145static void copy_fromat_to(int fromfd, const char *fromname, const char *toname)
146{
147	int from = openat(fromfd, fromname, O_RDONLY);
148	if (from == -1)
149		ksft_exit_fail_msg("open copy source - %s\n", strerror(errno));
150
151	int to = open(toname, O_CREAT | O_WRONLY | O_EXCL, 0700);
152
153	while (true) {
154		char buf[4096];
155		ssize_t sz = read(from, buf, sizeof(buf));
156		if (sz == 0)
157			break;
158		if (sz < 0)
159			ksft_exit_fail_msg("read - %s\n", strerror(errno));
160
161		if (write(to, buf, sz) != sz)
162			/* no short writes on tmpfs */
163			ksft_exit_fail_msg("write - %s\n", strerror(errno));
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			ksft_print_msg("Child died\n");
181			nerrs++;
182		} else if (WEXITSTATUS(status) != 0) {
183			ksft_print_msg("Child failed\n");
184			nerrs++;
185		} else {
186			/* don't print this message for mpid */
187			if (getpid() != mpid)
188				ksft_test_result_pass("Passed\n");
189		}
190		return false;
191	} else {
192		ksft_exit_fail_msg("fork - %s\n", strerror(errno));
193		return false;
194	}
195}
196
197static void exec_other_validate_cap(const char *name,
198				    bool eff, bool perm, bool inh, bool ambient)
199{
200	execl(name, name, (eff ? "1" : "0"),
201	      (perm ? "1" : "0"), (inh ? "1" : "0"), (ambient ? "1" : "0"),
202	      NULL);
203	ksft_exit_fail_msg("execl - %s\n", strerror(errno));
204}
205
206static void exec_validate_cap(bool eff, bool perm, bool inh, bool ambient)
207{
208	exec_other_validate_cap("./validate_cap", eff, perm, inh, ambient);
209}
210
211static int do_tests(int uid, const char *our_path)
212{
213	int ret;
214	bool have_outer_privilege = create_and_enter_ns(uid);
215
216	int ourpath_fd = open(our_path, O_RDONLY | O_DIRECTORY);
217	if (ourpath_fd == -1)
218		ksft_exit_fail_msg("open '%s' - %s\n",
219					our_path, strerror(errno));
220
221	chdir_to_tmpfs();
222
223	copy_fromat_to(ourpath_fd, "validate_cap", "validate_cap");
224
225	if (have_outer_privilege) {
226		uid_t gid = getegid();
227
228		copy_fromat_to(ourpath_fd, "validate_cap",
229			       "validate_cap_suidroot");
230		if (chown("validate_cap_suidroot", 0, -1) != 0)
231			ksft_exit_fail_msg("chown - %s\n", strerror(errno));
232		if (chmod("validate_cap_suidroot", S_ISUID | 0700) != 0)
233			ksft_exit_fail_msg("chmod - %s\n", strerror(errno));
234
235		copy_fromat_to(ourpath_fd, "validate_cap",
236			       "validate_cap_suidnonroot");
237		if (chown("validate_cap_suidnonroot", uid + 1, -1) != 0)
238			ksft_exit_fail_msg("chown - %s\n", strerror(errno));
239		if (chmod("validate_cap_suidnonroot", S_ISUID | 0700) != 0)
240			ksft_exit_fail_msg("chmod - %s\n", strerror(errno));
241
242		copy_fromat_to(ourpath_fd, "validate_cap",
243			       "validate_cap_sgidroot");
244		if (chown("validate_cap_sgidroot", -1, 0) != 0)
245			ksft_exit_fail_msg("chown - %s\n", strerror(errno));
246		if (chmod("validate_cap_sgidroot", S_ISGID | 0710) != 0)
247			ksft_exit_fail_msg("chmod - %s\n", strerror(errno));
248
249		copy_fromat_to(ourpath_fd, "validate_cap",
250			       "validate_cap_sgidnonroot");
251		if (chown("validate_cap_sgidnonroot", -1, gid + 1) != 0)
252			ksft_exit_fail_msg("chown - %s\n", strerror(errno));
253		if (chmod("validate_cap_sgidnonroot", S_ISGID | 0710) != 0)
254			ksft_exit_fail_msg("chmod - %s\n", strerror(errno));
255	}
256
257	ret = capng_get_caps_process();
258	if (ret == -1)
259		ksft_exit_fail_msg("capng_get_caps_process failed\n");
260
261	/* Make sure that i starts out clear */
262	capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
263	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
264		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
265
266	if (uid == 0) {
267		ksft_print_msg("[RUN]\tRoot => ep\n");
268		if (fork_wait())
269			exec_validate_cap(true, true, false, false);
270	} else {
271		ksft_print_msg("[RUN]\tNon-root => no caps\n");
272		if (fork_wait())
273			exec_validate_cap(false, false, false, false);
274	}
275
276	ksft_print_msg("Check cap_ambient manipulation rules\n");
277
278	/* We should not be able to add ambient caps yet. */
279	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != -1 || errno != EPERM) {
280		if (errno == EINVAL)
281			ksft_test_result_fail(
282				"PR_CAP_AMBIENT_RAISE isn't supported\n");
283		else
284			ksft_test_result_fail(
285				"PR_CAP_AMBIENT_RAISE should have failed eith EPERM on a non-inheritable cap\n");
286		return 1;
287	}
288	ksft_test_result_pass(
289		"PR_CAP_AMBIENT_RAISE failed on non-inheritable cap\n");
290
291	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_RAW);
292	capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_NET_RAW);
293	capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_NET_RAW);
294	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
295		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
296	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0, 0) != -1 || errno != EPERM) {
297		ksft_test_result_fail(
298			"PR_CAP_AMBIENT_RAISE should have failed on a non-permitted cap\n");
299		return 1;
300	}
301	ksft_test_result_pass(
302		"PR_CAP_AMBIENT_RAISE failed on non-permitted cap\n");
303
304	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
305	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
306		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
307	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
308		ksft_test_result_fail(
309			"PR_CAP_AMBIENT_RAISE should have succeeded\n");
310		return 1;
311	}
312	ksft_test_result_pass("PR_CAP_AMBIENT_RAISE worked\n");
313
314	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 1) {
315		ksft_test_result_fail("PR_CAP_AMBIENT_IS_SET is broken\n");
316		return 1;
317	}
318
319	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0, 0) != 0)
320		ksft_exit_fail_msg("PR_CAP_AMBIENT_CLEAR_ALL - %s\n",
321					strerror(errno));
322
323	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
324		ksft_test_result_fail(
325			"PR_CAP_AMBIENT_CLEAR_ALL didn't work\n");
326		return 1;
327	}
328
329	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0)
330		ksft_exit_fail_msg("PR_CAP_AMBIENT_RAISE - %s\n",
331					strerror(errno));
332
333	capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
334	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
335		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
336
337	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
338		ksft_test_result_fail("Dropping I should have dropped A\n");
339		return 1;
340	}
341
342	ksft_test_result_pass("Basic manipulation appears to work\n");
343
344	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
345	if (capng_apply(CAPNG_SELECT_CAPS) != 0)
346		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
347	if (uid == 0) {
348		ksft_print_msg("[RUN]\tRoot +i => eip\n");
349		if (fork_wait())
350			exec_validate_cap(true, true, true, false);
351	} else {
352		ksft_print_msg("[RUN]\tNon-root +i => i\n");
353		if (fork_wait())
354			exec_validate_cap(false, false, true, false);
355	}
356
357	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0)
358		ksft_exit_fail_msg("PR_CAP_AMBIENT_RAISE - %s\n",
359					strerror(errno));
360
361	ksft_print_msg("[RUN]\tUID %d +ia => eipa\n", uid);
362	if (fork_wait())
363		exec_validate_cap(true, true, true, true);
364
365	/* The remaining tests need real privilege */
366
367	if (!have_outer_privilege) {
368		ksft_test_result_skip("SUID/SGID tests (needs privilege)\n");
369		goto done;
370	}
371
372	if (uid == 0) {
373		ksft_print_msg("[RUN]\tRoot +ia, suidroot => eipa\n");
374		if (fork_wait())
375			exec_other_validate_cap("./validate_cap_suidroot",
376						true, true, true, true);
377
378		ksft_print_msg("[RUN]\tRoot +ia, suidnonroot => ip\n");
379		if (fork_wait())
380			exec_other_validate_cap("./validate_cap_suidnonroot",
381						false, true, true, false);
382
383		ksft_print_msg("[RUN]\tRoot +ia, sgidroot => eipa\n");
384		if (fork_wait())
385			exec_other_validate_cap("./validate_cap_sgidroot",
386						true, true, true, true);
387
388		if (fork_wait()) {
389			ksft_print_msg(
390				"[RUN]\tRoot, gid != 0, +ia, sgidroot => eip\n");
391			if (setresgid(1, 1, 1) != 0)
392				ksft_exit_fail_msg("setresgid - %s\n",
393							strerror(errno));
394			exec_other_validate_cap("./validate_cap_sgidroot",
395						true, true, true, false);
396		}
397
398		ksft_print_msg("[RUN]\tRoot +ia, sgidnonroot => eip\n");
399		if (fork_wait())
400			exec_other_validate_cap("./validate_cap_sgidnonroot",
401						true, true, true, false);
402	} else {
403		ksft_print_msg("[RUN]\tNon-root +ia, sgidnonroot => i\n");
404		if (fork_wait())
405			exec_other_validate_cap("./validate_cap_sgidnonroot",
406					false, false, true, false);
407
408		if (fork_wait()) {
409			ksft_print_msg("[RUN]\tNon-root +ia, sgidroot => i\n");
410			if (setresgid(1, 1, 1) != 0)
411				ksft_exit_fail_msg("setresgid - %s\n",
412							strerror(errno));
413			exec_other_validate_cap("./validate_cap_sgidroot",
414						false, false, true, false);
415		}
416	}
417
418done:
419	ksft_print_cnts();
420	return nerrs ? 1 : 0;
421}
422
423int main(int argc, char **argv)
424{
425	char *tmp1, *tmp2, *our_path;
426
427	/* Find our path */
428	tmp1 = strdup(argv[0]);
429	if (!tmp1)
430		ksft_exit_fail_msg("strdup - %s\n", strerror(errno));
431	tmp2 = dirname(tmp1);
432	our_path = strdup(tmp2);
433	if (!our_path)
434		ksft_exit_fail_msg("strdup - %s\n", strerror(errno));
435	free(tmp1);
436
437	mpid = getpid();
438
439	if (fork_wait()) {
440		ksft_print_header();
441		ksft_set_plan(12);
442		ksft_print_msg("[RUN]\t+++ Tests with uid == 0 +++\n");
443		return do_tests(0, our_path);
444	}
445
446	ksft_print_msg("==================================================\n");
447
448	if (fork_wait()) {
449		ksft_print_header();
450		ksft_set_plan(9);
451		ksft_print_msg("[RUN]\t+++ Tests with uid != 0 +++\n");
452		return do_tests(1, our_path);
453	}
454
455	return nerrs ? 1 : 0;
456}
~/ Read-only file preview.