brintos

brintos / linux-shallow public Read only

0
0
Text · 12.8 KiB · 47bad7d Raw
457 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3 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 caps100		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		else284			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}457