xref: /minix3/minix/tests/test89.c (revision 3083d603ba9dea68befc9da05a714884e10ec7e8)
11122b286SDavid van Moolenbroek /* Tests for set[ug]id, sete[ug]id, and saved IDs - by D.C. van Moolenbroek */
21122b286SDavid van Moolenbroek /* This test must be run as root, as it tests privileged operations. */
31122b286SDavid van Moolenbroek #include <stdlib.h>
41122b286SDavid van Moolenbroek #include <unistd.h>
51122b286SDavid van Moolenbroek #include <sys/stat.h>
61122b286SDavid van Moolenbroek #include <sys/wait.h>
71122b286SDavid van Moolenbroek #include <sys/sysctl.h>
81122b286SDavid van Moolenbroek #include <unistd.h>
91122b286SDavid van Moolenbroek 
101122b286SDavid van Moolenbroek #include "common.h"
111122b286SDavid van Moolenbroek 
121122b286SDavid van Moolenbroek #define ITERATIONS	2
131122b286SDavid van Moolenbroek 
141122b286SDavid van Moolenbroek /* These are in a specific order. */
151122b286SDavid van Moolenbroek enum {
161122b286SDavid van Moolenbroek 	SUB_REAL,	/* test set[ug]id(2) */
171122b286SDavid van Moolenbroek 	SUB_EFF,	/* test sete[ug]id(2) */
181122b286SDavid van Moolenbroek 	SUB_REAL_E0,	/* test setgid(2) with euid=0 */
191122b286SDavid van Moolenbroek 	SUB_EFF_E0,	/* test setegid(2) with euid=0 */
201122b286SDavid van Moolenbroek 	SUB_RETAIN,	/* test r/e/s preservation across fork(2), exec(2) */
211122b286SDavid van Moolenbroek };
221122b286SDavid van Moolenbroek 
231122b286SDavid van Moolenbroek static const char *executable;
241122b286SDavid van Moolenbroek 
251122b286SDavid van Moolenbroek /*
261122b286SDavid van Moolenbroek  * The table below is exhaustive in terms of different combinations of real,
271122b286SDavid van Moolenbroek  * effective, and saved user IDs (with 0 being a special value, but 1 and 2
281122b286SDavid van Moolenbroek  * being interchangeable), but not all these combinations can actually be
291122b286SDavid van Moolenbroek  * established in practice.  The results for which there is no way to create
301122b286SDavid van Moolenbroek  * the initial condition are set to -1.  If we ever implement setresuid(2),
311122b286SDavid van Moolenbroek  * these results can be filled in and tested as well.
321122b286SDavid van Moolenbroek  */
331122b286SDavid van Moolenbroek static const struct uid_set {
341122b286SDavid van Moolenbroek 	uid_t ruid;
351122b286SDavid van Moolenbroek 	uid_t euid;
361122b286SDavid van Moolenbroek 	uid_t suid;
371122b286SDavid van Moolenbroek 	uid_t uid;
381122b286SDavid van Moolenbroek 	int res;
391122b286SDavid van Moolenbroek 	int eres;
401122b286SDavid van Moolenbroek } uid_sets[] = {
411122b286SDavid van Moolenbroek 	{ 0, 0, 0, 0,  1,  1 },
421122b286SDavid van Moolenbroek 	{ 0, 0, 0, 1,  1,  1 },
431122b286SDavid van Moolenbroek 	{ 0, 0, 1, 0,  1,  1 },
441122b286SDavid van Moolenbroek 	{ 0, 0, 1, 1,  1,  1 },
451122b286SDavid van Moolenbroek 	{ 0, 0, 1, 2,  1,  1 },
461122b286SDavid van Moolenbroek 	{ 0, 1, 0, 0,  1,  1 },
471122b286SDavid van Moolenbroek 	{ 0, 1, 0, 1,  0,  0 },
481122b286SDavid van Moolenbroek 	{ 0, 1, 0, 2,  0,  0 },
491122b286SDavid van Moolenbroek 	{ 0, 1, 1, 0,  1,  1 },
501122b286SDavid van Moolenbroek 	{ 0, 1, 1, 1,  0,  1 },
511122b286SDavid van Moolenbroek 	{ 0, 1, 1, 2,  0,  0 },
521122b286SDavid van Moolenbroek 	{ 0, 1, 2, 0, -1, -1 },
531122b286SDavid van Moolenbroek 	{ 0, 1, 2, 1, -1, -1 },
541122b286SDavid van Moolenbroek 	{ 0, 1, 2, 2, -1, -1 },
551122b286SDavid van Moolenbroek 	{ 1, 0, 0, 0,  1,  1 },
561122b286SDavid van Moolenbroek 	{ 1, 0, 0, 1,  1,  1 },
571122b286SDavid van Moolenbroek 	{ 1, 0, 0, 2,  1,  1 },
581122b286SDavid van Moolenbroek 	{ 1, 0, 1, 0, -1, -1 },
591122b286SDavid van Moolenbroek 	{ 1, 0, 1, 1, -1, -1 },
601122b286SDavid van Moolenbroek 	{ 1, 0, 1, 2, -1, -1 },
611122b286SDavid van Moolenbroek 	{ 1, 0, 2, 0, -1, -1 },
621122b286SDavid van Moolenbroek 	{ 1, 0, 2, 1, -1, -1 },
631122b286SDavid van Moolenbroek 	{ 1, 0, 2, 2, -1, -1 },
641122b286SDavid van Moolenbroek 	{ 1, 1, 0, 0,  0,  1 },
651122b286SDavid van Moolenbroek 	{ 1, 1, 0, 1,  1,  1 },
661122b286SDavid van Moolenbroek 	{ 1, 1, 0, 2,  0,  0 },
671122b286SDavid van Moolenbroek 	{ 1, 1, 1, 0,  0,  0 },
681122b286SDavid van Moolenbroek 	{ 1, 1, 1, 1,  1,  1 },
691122b286SDavid van Moolenbroek 	{ 1, 1, 1, 2,  0,  0 },
701122b286SDavid van Moolenbroek 	{ 1, 1, 2, 0,  0,  0 },
711122b286SDavid van Moolenbroek 	{ 1, 1, 2, 1,  1,  1 },
721122b286SDavid van Moolenbroek 	{ 1, 1, 2, 2,  0,  1 },
731122b286SDavid van Moolenbroek 	{ 1, 2, 0, 0,  0,  1 },
741122b286SDavid van Moolenbroek 	{ 1, 2, 0, 1,  1,  1 },
751122b286SDavid van Moolenbroek 	{ 1, 2, 0, 2,  0,  0 },
761122b286SDavid van Moolenbroek 	{ 1, 2, 1, 0, -1, -1 },
771122b286SDavid van Moolenbroek 	{ 1, 2, 1, 1, -1, -1 },
781122b286SDavid van Moolenbroek 	{ 1, 2, 1, 2, -1, -1 },
791122b286SDavid van Moolenbroek 	{ 1, 2, 2, 0,  0,  0 },
801122b286SDavid van Moolenbroek 	{ 1, 2, 2, 1,  1,  1 },
811122b286SDavid van Moolenbroek 	{ 1, 2, 2, 2,  0,  1 },
821122b286SDavid van Moolenbroek };
831122b286SDavid van Moolenbroek 
841122b286SDavid van Moolenbroek /*
851122b286SDavid van Moolenbroek  * The same type of table but now for group identifiers.  In this case, all
861122b286SDavid van Moolenbroek  * combinations are possible to establish in practice, because the effective
871122b286SDavid van Moolenbroek  * UID, not the GID, is used for the privilege check.  GID 0 does not have any
881122b286SDavid van Moolenbroek  * special meaning, but we still test it as though it does, in order to ensure
891122b286SDavid van Moolenbroek  * that it in fact does not.
901122b286SDavid van Moolenbroek  */
911122b286SDavid van Moolenbroek static const struct gid_set {
921122b286SDavid van Moolenbroek 	gid_t rgid;
931122b286SDavid van Moolenbroek 	gid_t egid;
941122b286SDavid van Moolenbroek 	gid_t sgid;
951122b286SDavid van Moolenbroek 	gid_t gid;
961122b286SDavid van Moolenbroek 	int res;
971122b286SDavid van Moolenbroek 	int eres;
981122b286SDavid van Moolenbroek } gid_sets[] = {
991122b286SDavid van Moolenbroek 	{ 0, 0, 0, 0, 1, 1 },
1001122b286SDavid van Moolenbroek 	{ 0, 0, 0, 1, 0, 0 },
1011122b286SDavid van Moolenbroek 	{ 0, 0, 1, 0, 1, 1 },
1021122b286SDavid van Moolenbroek 	{ 0, 0, 1, 1, 0, 1 },
1031122b286SDavid van Moolenbroek 	{ 0, 0, 1, 2, 0, 0 },
1041122b286SDavid van Moolenbroek 	{ 0, 1, 0, 0, 1, 1 },
1051122b286SDavid van Moolenbroek 	{ 0, 1, 0, 1, 0, 0 },
1061122b286SDavid van Moolenbroek 	{ 0, 1, 0, 2, 0, 0 },
1071122b286SDavid van Moolenbroek 	{ 0, 1, 1, 0, 1, 1 },
1081122b286SDavid van Moolenbroek 	{ 0, 1, 1, 1, 0, 1 },
1091122b286SDavid van Moolenbroek 	{ 0, 1, 1, 2, 0, 0 },
1101122b286SDavid van Moolenbroek 	{ 0, 1, 2, 0, 1, 1 },
1111122b286SDavid van Moolenbroek 	{ 0, 1, 2, 1, 0, 0 },
1121122b286SDavid van Moolenbroek 	{ 0, 1, 2, 2, 0, 1 },
1131122b286SDavid van Moolenbroek 	{ 1, 0, 0, 0, 0, 1 },
1141122b286SDavid van Moolenbroek 	{ 1, 0, 0, 1, 1, 1 },
1151122b286SDavid van Moolenbroek 	{ 1, 0, 0, 2, 0, 0 },
1161122b286SDavid van Moolenbroek 	{ 1, 0, 1, 0, 0, 0 },
1171122b286SDavid van Moolenbroek 	{ 1, 0, 1, 1, 1, 1 },
1181122b286SDavid van Moolenbroek 	{ 1, 0, 1, 2, 0, 0 },
1191122b286SDavid van Moolenbroek 	{ 1, 0, 2, 0, 0, 0 },
1201122b286SDavid van Moolenbroek 	{ 1, 0, 2, 1, 1, 1 },
1211122b286SDavid van Moolenbroek 	{ 1, 0, 2, 2, 0, 1 },
1221122b286SDavid van Moolenbroek 	{ 1, 1, 0, 0, 0, 1 },
1231122b286SDavid van Moolenbroek 	{ 1, 1, 0, 1, 1, 1 },
1241122b286SDavid van Moolenbroek 	{ 1, 1, 0, 2, 0, 0 },
1251122b286SDavid van Moolenbroek 	{ 1, 1, 1, 0, 0, 0 },
1261122b286SDavid van Moolenbroek 	{ 1, 1, 1, 1, 1, 1 },
1271122b286SDavid van Moolenbroek 	{ 1, 1, 1, 2, 0, 0 },
1281122b286SDavid van Moolenbroek 	{ 1, 1, 2, 0, 0, 0 },
1291122b286SDavid van Moolenbroek 	{ 1, 1, 2, 1, 1, 1 },
1301122b286SDavid van Moolenbroek 	{ 1, 1, 2, 2, 0, 1 },
1311122b286SDavid van Moolenbroek 	{ 1, 2, 0, 0, 0, 1 },
1321122b286SDavid van Moolenbroek 	{ 1, 2, 0, 1, 1, 1 },
1331122b286SDavid van Moolenbroek 	{ 1, 2, 0, 2, 0, 0 },
1341122b286SDavid van Moolenbroek 	{ 1, 2, 1, 0, 0, 0 },
1351122b286SDavid van Moolenbroek 	{ 1, 2, 1, 1, 1, 1 },
1361122b286SDavid van Moolenbroek 	{ 1, 2, 1, 2, 0, 0 },
1371122b286SDavid van Moolenbroek 	{ 1, 2, 2, 0, 0, 0 },
1381122b286SDavid van Moolenbroek 	{ 1, 2, 2, 1, 1, 1 },
1391122b286SDavid van Moolenbroek 	{ 1, 2, 2, 2, 0, 1 },
1401122b286SDavid van Moolenbroek };
1411122b286SDavid van Moolenbroek 
1421122b286SDavid van Moolenbroek /*
1431122b286SDavid van Moolenbroek  * Obtain the kinfo_proc2 data for the given process ID.  Return 0 on success,
1441122b286SDavid van Moolenbroek  * or -1 with errno set appropriately on failure.
1451122b286SDavid van Moolenbroek  */
1461122b286SDavid van Moolenbroek static int
get_proc2(pid_t pid,struct kinfo_proc2 * proc2)1471122b286SDavid van Moolenbroek get_proc2(pid_t pid, struct kinfo_proc2 * proc2)
1481122b286SDavid van Moolenbroek {
1491122b286SDavid van Moolenbroek 	int mib[6];
1501122b286SDavid van Moolenbroek 	size_t oldlen;
1511122b286SDavid van Moolenbroek 
1521122b286SDavid van Moolenbroek 	/*
1531122b286SDavid van Moolenbroek 	 * FIXME: for performance reasons, the MIB service updates it process
1541122b286SDavid van Moolenbroek 	 * tables only every clock tick.  As a result, we may not be able to
1551122b286SDavid van Moolenbroek 	 * obtain accurate process details right away, and we need to wait.
1561122b286SDavid van Moolenbroek 	 * Eventually, the MIB service should retrieve more targeted subsets of
1571122b286SDavid van Moolenbroek 	 * the process tables, and this problem should go away at least for
1581122b286SDavid van Moolenbroek 	 * specific queries such as this one, which queries only a single PID.
1591122b286SDavid van Moolenbroek 	 */
1601122b286SDavid van Moolenbroek 	usleep((2000000 + sysconf(_SC_CLK_TCK)) / sysconf(_SC_CLK_TCK));
1611122b286SDavid van Moolenbroek 
1621122b286SDavid van Moolenbroek 	mib[0] = CTL_KERN;
1631122b286SDavid van Moolenbroek 	mib[1] = KERN_PROC2;
1641122b286SDavid van Moolenbroek 	mib[2] = KERN_PROC_PID;
1651122b286SDavid van Moolenbroek 	mib[3] = pid;
1661122b286SDavid van Moolenbroek 	mib[4] = sizeof(*proc2);
1671122b286SDavid van Moolenbroek 	mib[5] = 1;
1681122b286SDavid van Moolenbroek 
1691122b286SDavid van Moolenbroek 	oldlen = sizeof(*proc2);
1701122b286SDavid van Moolenbroek 	if (sysctl(mib, __arraycount(mib), proc2, &oldlen, NULL, 0) == -1)
1711122b286SDavid van Moolenbroek 		return -1;
1721122b286SDavid van Moolenbroek 	if (oldlen != sizeof(*proc2)) {
1731122b286SDavid van Moolenbroek 		errno = ESRCH;
1741122b286SDavid van Moolenbroek 		return -1;
1751122b286SDavid van Moolenbroek 	}
1761122b286SDavid van Moolenbroek 	return 0;
1771122b286SDavid van Moolenbroek }
1781122b286SDavid van Moolenbroek 
1791122b286SDavid van Moolenbroek /*
1801122b286SDavid van Moolenbroek  * Verify that the current process's real, effective, and saved user IDs are
1811122b286SDavid van Moolenbroek  * set to the given respective value.
1821122b286SDavid van Moolenbroek  */
1831122b286SDavid van Moolenbroek static void
test_uids(uid_t ruid,uid_t euid,uid_t suid)1841122b286SDavid van Moolenbroek test_uids(uid_t ruid, uid_t euid, uid_t suid)
1851122b286SDavid van Moolenbroek {
1861122b286SDavid van Moolenbroek 	struct kinfo_proc2 proc2;
1871122b286SDavid van Moolenbroek 
1881122b286SDavid van Moolenbroek 	if (getuid() != ruid) e(0);
1891122b286SDavid van Moolenbroek 	if (geteuid() != euid) e(0);
1901122b286SDavid van Moolenbroek 
1911122b286SDavid van Moolenbroek 	/*
1921122b286SDavid van Moolenbroek 	 * There is no system call specifically to retrieve the saved user ID,
1931122b286SDavid van Moolenbroek 	 * so we use sysctl(2) to obtain process information.  This allows us
1941122b286SDavid van Moolenbroek 	 * to verify the real and effective user IDs once more, too.
1951122b286SDavid van Moolenbroek 	 */
1961122b286SDavid van Moolenbroek 	if (get_proc2(getpid(), &proc2) != 0) e(0);
1971122b286SDavid van Moolenbroek 
1981122b286SDavid van Moolenbroek 	if (proc2.p_ruid != ruid) e(0);
1991122b286SDavid van Moolenbroek 	if (proc2.p_uid != euid) e(0);
2001122b286SDavid van Moolenbroek 	if (proc2.p_svuid != suid) e(0);
2011122b286SDavid van Moolenbroek }
2021122b286SDavid van Moolenbroek 
2031122b286SDavid van Moolenbroek /*
2041122b286SDavid van Moolenbroek  * Verify that the real and effective user IDs are kept as is after an exec(2)
2051122b286SDavid van Moolenbroek  * call on a non-setuid binary, and that the saved user ID is set to the
2061122b286SDavid van Moolenbroek  * effective user ID.
2071122b286SDavid van Moolenbroek  */
2081122b286SDavid van Moolenbroek static void
exec89b(const char * param1,const char * param2 __unused)2091122b286SDavid van Moolenbroek exec89b(const char * param1, const char * param2 __unused)
2101122b286SDavid van Moolenbroek {
2111122b286SDavid van Moolenbroek 	const struct uid_set *set;
2121122b286SDavid van Moolenbroek 	int setnum;
2131122b286SDavid van Moolenbroek 
2141122b286SDavid van Moolenbroek 	setnum = atoi(param1);
2151122b286SDavid van Moolenbroek 	if (setnum < 0 || setnum >= __arraycount(uid_sets)) {
2161122b286SDavid van Moolenbroek 		e(setnum);
2171122b286SDavid van Moolenbroek 		return;
2181122b286SDavid van Moolenbroek 	}
2191122b286SDavid van Moolenbroek 	set = &uid_sets[setnum];
2201122b286SDavid van Moolenbroek 
2211122b286SDavid van Moolenbroek 	test_uids(set->ruid, set->euid, set->euid);
2221122b286SDavid van Moolenbroek }
2231122b286SDavid van Moolenbroek 
2241122b286SDavid van Moolenbroek /*
2251122b286SDavid van Moolenbroek  * The real, effective, and saved user IDs have been set up as indicated by the
2261122b286SDavid van Moolenbroek  * current set.  Verify that fork(2) and exec(2) do not change the real and
2271122b286SDavid van Moolenbroek  * effective UIDs, and that only exec(2) sets the saved UID to the effective
2281122b286SDavid van Moolenbroek  * UID.
2291122b286SDavid van Moolenbroek  */
2301122b286SDavid van Moolenbroek static void
sub89b(int setnum)2311122b286SDavid van Moolenbroek sub89b(int setnum)
2321122b286SDavid van Moolenbroek {
2331122b286SDavid van Moolenbroek 	const struct uid_set *set;
2341122b286SDavid van Moolenbroek 	char param1[32];
2351122b286SDavid van Moolenbroek 	pid_t pid;
2361122b286SDavid van Moolenbroek 	int status;
2371122b286SDavid van Moolenbroek 
2381122b286SDavid van Moolenbroek 	set = &uid_sets[setnum];
2391122b286SDavid van Moolenbroek 
2401122b286SDavid van Moolenbroek 	pid = fork();
2411122b286SDavid van Moolenbroek 
2421122b286SDavid van Moolenbroek 	switch (pid) {
2431122b286SDavid van Moolenbroek 	case -1:
2441122b286SDavid van Moolenbroek 		e(setnum);
2451122b286SDavid van Moolenbroek 		break;
2461122b286SDavid van Moolenbroek 
2471122b286SDavid van Moolenbroek 	case 0:
2481122b286SDavid van Moolenbroek 		/*
2491122b286SDavid van Moolenbroek 		 * Verify that all the UIDs were retained across the fork(2)
2501122b286SDavid van Moolenbroek 		 * call.
2511122b286SDavid van Moolenbroek 		 */
2521122b286SDavid van Moolenbroek 		test_uids(set->ruid, set->euid, set->suid);
2531122b286SDavid van Moolenbroek 
2541122b286SDavid van Moolenbroek 		snprintf(param1, sizeof(param1), "%d", setnum);
2551122b286SDavid van Moolenbroek 
2561122b286SDavid van Moolenbroek 		(void)execl(executable, executable, "DO CHECK", "b", param1,
2571122b286SDavid van Moolenbroek 		    "", NULL);
2581122b286SDavid van Moolenbroek 
2591122b286SDavid van Moolenbroek 		e(setnum);
2601122b286SDavid van Moolenbroek 		break;
2611122b286SDavid van Moolenbroek 
2621122b286SDavid van Moolenbroek 	default:
2631122b286SDavid van Moolenbroek 		if (waitpid(pid, &status, 0) != pid) e(setnum);
2641122b286SDavid van Moolenbroek 		if (!WIFEXITED(status)) e(setnum);
2651122b286SDavid van Moolenbroek 		if (WEXITSTATUS(status) != 0) e(setnum);
2661122b286SDavid van Moolenbroek 	}
2671122b286SDavid van Moolenbroek }
2681122b286SDavid van Moolenbroek 
2691122b286SDavid van Moolenbroek /*
2701122b286SDavid van Moolenbroek  * The real, effective, and saved user IDs have been set up as indicated by the
2711122b286SDavid van Moolenbroek  * current set.  Test one particular case for test A or B, and verify the
2721122b286SDavid van Moolenbroek  * result.
2731122b286SDavid van Moolenbroek  */
2741122b286SDavid van Moolenbroek static void
test_one_uid(int setnum,int sub)2751122b286SDavid van Moolenbroek test_one_uid(int setnum, int sub)
2761122b286SDavid van Moolenbroek {
2771122b286SDavid van Moolenbroek 	const struct uid_set *set;
2781122b286SDavid van Moolenbroek 	int res, exp;
2791122b286SDavid van Moolenbroek 
2801122b286SDavid van Moolenbroek 	set = &uid_sets[setnum];
2811122b286SDavid van Moolenbroek 
2821122b286SDavid van Moolenbroek 	/* Verify that the pre-call process state is as expected. */
2831122b286SDavid van Moolenbroek 	test_uids(set->ruid, set->euid, set->suid);
2841122b286SDavid van Moolenbroek 
2851122b286SDavid van Moolenbroek 	/* Perform the call, and check whether the result is as expected. */
2861122b286SDavid van Moolenbroek 	switch (sub) {
2871122b286SDavid van Moolenbroek 	case SUB_REAL:
2881122b286SDavid van Moolenbroek 		res = setuid(set->uid);
2891122b286SDavid van Moolenbroek 		exp = set->res - 1;
2901122b286SDavid van Moolenbroek 		break;
2911122b286SDavid van Moolenbroek 
2921122b286SDavid van Moolenbroek 	case SUB_EFF:
2931122b286SDavid van Moolenbroek 		res = seteuid(set->uid);
2941122b286SDavid van Moolenbroek 		exp = set->eres - 1;
2951122b286SDavid van Moolenbroek 		break;
2961122b286SDavid van Moolenbroek 
2971122b286SDavid van Moolenbroek 	case SUB_RETAIN:
2981122b286SDavid van Moolenbroek 		sub89b(setnum);
2991122b286SDavid van Moolenbroek 
3001122b286SDavid van Moolenbroek 		return;
301*3083d603SDavid van Moolenbroek 
302*3083d603SDavid van Moolenbroek 	default:
303*3083d603SDavid van Moolenbroek 		abort();
3041122b286SDavid van Moolenbroek 	}
3051122b286SDavid van Moolenbroek 
3061122b286SDavid van Moolenbroek 	if (res != 0 && (res != -1 || errno != EPERM)) e(setnum);
3071122b286SDavid van Moolenbroek 
3081122b286SDavid van Moolenbroek 	if (res != exp) e(setnum);
3091122b286SDavid van Moolenbroek 
3101122b286SDavid van Moolenbroek 	/* Verify that the post-call process state is as expected as well. */
3111122b286SDavid van Moolenbroek 	if (res == 0) {
3121122b286SDavid van Moolenbroek 		if (sub == SUB_EFF)
3131122b286SDavid van Moolenbroek 			test_uids(set->ruid, set->uid, set->suid);
3141122b286SDavid van Moolenbroek 		else
3151122b286SDavid van Moolenbroek 			test_uids(set->uid, set->uid, set->uid);
3161122b286SDavid van Moolenbroek 	} else
3171122b286SDavid van Moolenbroek 		test_uids(set->ruid, set->euid, set->suid);
3181122b286SDavid van Moolenbroek }
3191122b286SDavid van Moolenbroek 
3201122b286SDavid van Moolenbroek /*
3211122b286SDavid van Moolenbroek  * Test setuid(2) or seteuid(2) after a successful execve(2) call, which should
3221122b286SDavid van Moolenbroek  * have set the process's effective and saved user ID.
3231122b286SDavid van Moolenbroek  */
3241122b286SDavid van Moolenbroek static void
exec89a(const char * param1,const char * param2)3251122b286SDavid van Moolenbroek exec89a(const char * param1, const char * param2)
3261122b286SDavid van Moolenbroek {
3271122b286SDavid van Moolenbroek 	const struct uid_set *set;
3281122b286SDavid van Moolenbroek 	int setnum, sub;
3291122b286SDavid van Moolenbroek 
3301122b286SDavid van Moolenbroek 	setnum = atoi(param1);
3311122b286SDavid van Moolenbroek 	if (setnum < 0 || setnum >= __arraycount(uid_sets)) {
3321122b286SDavid van Moolenbroek 		e(setnum);
3331122b286SDavid van Moolenbroek 		return;
3341122b286SDavid van Moolenbroek 	}
3351122b286SDavid van Moolenbroek 	set = &uid_sets[setnum];
3361122b286SDavid van Moolenbroek 
3371122b286SDavid van Moolenbroek 	sub = atoi(param2);
3381122b286SDavid van Moolenbroek 
3391122b286SDavid van Moolenbroek 	if (sub == SUB_RETAIN) {
3401122b286SDavid van Moolenbroek 		/* Clear the set-uid bit before dropping more privileges. */
3411122b286SDavid van Moolenbroek 		if (chmod(executable, S_IXUSR | S_IXGRP | S_IXOTH) != 0)
3421122b286SDavid van Moolenbroek 			e(setnum);
3431122b286SDavid van Moolenbroek 	}
3441122b286SDavid van Moolenbroek 
3451122b286SDavid van Moolenbroek 	/* Finish setting up the initial condition. */
3461122b286SDavid van Moolenbroek 	if (set->euid != set->suid) {
3471122b286SDavid van Moolenbroek 		if (set->euid != set->ruid && set->suid != 0) {
3481122b286SDavid van Moolenbroek 			test_uids(set->ruid, set->suid, set->suid);
3491122b286SDavid van Moolenbroek 
3501122b286SDavid van Moolenbroek 			return; /* skip test */
3511122b286SDavid van Moolenbroek 		}
3521122b286SDavid van Moolenbroek 
3531122b286SDavid van Moolenbroek 		if (seteuid(set->euid) != 0) e(setnum);
3541122b286SDavid van Moolenbroek 	}
3551122b286SDavid van Moolenbroek 
3561122b286SDavid van Moolenbroek 	/* Perform the actual test. */
3571122b286SDavid van Moolenbroek 	test_one_uid(setnum, sub);
3581122b286SDavid van Moolenbroek }
3591122b286SDavid van Moolenbroek 
3601122b286SDavid van Moolenbroek /*
3611122b286SDavid van Moolenbroek  * Test setuid(2) or seteuid(2) with a certain value starting from a certain
3621122b286SDavid van Moolenbroek  * initial condition, as identified by the given uid_sets[] array element.  As
3631122b286SDavid van Moolenbroek  * a side effect, test that in particular exec(2) properly sets the effective
3641122b286SDavid van Moolenbroek  * and saved user ID.
3651122b286SDavid van Moolenbroek  */
3661122b286SDavid van Moolenbroek static void
sub89a(int setnum,int sub)3671122b286SDavid van Moolenbroek sub89a(int setnum, int sub)
3681122b286SDavid van Moolenbroek {
3691122b286SDavid van Moolenbroek 	const struct uid_set *set;
3701122b286SDavid van Moolenbroek 	char param1[32], param2[32];
3711122b286SDavid van Moolenbroek 
3721122b286SDavid van Moolenbroek 	set = &uid_sets[setnum];
3731122b286SDavid van Moolenbroek 
3741122b286SDavid van Moolenbroek 	/*
3751122b286SDavid van Moolenbroek 	 * Figure out how to set the real, effective, and saved UIDs to those
3761122b286SDavid van Moolenbroek 	 * of the set structure.  Without setresuid(2), not all combinations
3771122b286SDavid van Moolenbroek 	 * are possible to achieve.  We silently skip the tests for which we
3781122b286SDavid van Moolenbroek 	 * cannot create the requested initial condition.
3791122b286SDavid van Moolenbroek 	 */
3801122b286SDavid van Moolenbroek 	if (set->ruid != set->suid) {
3811122b286SDavid van Moolenbroek 		/*
3821122b286SDavid van Moolenbroek 		 * In order to set the saved UID to something other than the
3831122b286SDavid van Moolenbroek 		 * real UID, we must exec(2) a set-uid binary.
3841122b286SDavid van Moolenbroek 		 */
3851122b286SDavid van Moolenbroek 		if (chown(executable, set->suid, 0 /*anything*/) != 0) e(0);
3861122b286SDavid van Moolenbroek 		if (chmod(executable,
3871122b286SDavid van Moolenbroek 		    S_ISUID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
3881122b286SDavid van Moolenbroek 
3891122b286SDavid van Moolenbroek 		if (setuid(set->ruid) != 0) e(setnum);
3901122b286SDavid van Moolenbroek 
3911122b286SDavid van Moolenbroek 		snprintf(param1, sizeof(param1), "%d", setnum);
3921122b286SDavid van Moolenbroek 		snprintf(param2, sizeof(param2), "%d", sub);
3931122b286SDavid van Moolenbroek 
3941122b286SDavid van Moolenbroek 		(void)execl(executable, executable, "DO CHECK", "a", param1,
3951122b286SDavid van Moolenbroek 		    param2, NULL);
3961122b286SDavid van Moolenbroek 
3971122b286SDavid van Moolenbroek 		e(0);
3981122b286SDavid van Moolenbroek 	} else {
3991122b286SDavid van Moolenbroek 		/*
4001122b286SDavid van Moolenbroek 		 * If the real and saved user ID are to be set to the same
4011122b286SDavid van Moolenbroek 		 * value, we need not use exec(2).  Still, we cannot achieve
4021122b286SDavid van Moolenbroek 		 * all combinations here either.
4031122b286SDavid van Moolenbroek 		 */
4041122b286SDavid van Moolenbroek 		if (set->ruid != 0 && set->ruid != set->euid)
4051122b286SDavid van Moolenbroek 			return; /* skip test */
4061122b286SDavid van Moolenbroek 
4071122b286SDavid van Moolenbroek 		if (sub == SUB_RETAIN) {
4081122b286SDavid van Moolenbroek 			/* Clear the set-uid bit before dropping privileges. */
4091122b286SDavid van Moolenbroek 			if (chmod(executable,
4101122b286SDavid van Moolenbroek 			    S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(setnum);
4111122b286SDavid van Moolenbroek 		}
4121122b286SDavid van Moolenbroek 
4131122b286SDavid van Moolenbroek 		if (setuid(set->ruid) != 0) e(setnum);
4141122b286SDavid van Moolenbroek 		if (seteuid(set->euid) != 0) e(setnum);
4151122b286SDavid van Moolenbroek 
4161122b286SDavid van Moolenbroek 		/* Perform the actual test. */
4171122b286SDavid van Moolenbroek 		test_one_uid(setnum, sub);
4181122b286SDavid van Moolenbroek 	}
4191122b286SDavid van Moolenbroek }
4201122b286SDavid van Moolenbroek 
4211122b286SDavid van Moolenbroek /*
4221122b286SDavid van Moolenbroek  * Test setuid(2) and seteuid(2) calls with various initial conditions, by
4231122b286SDavid van Moolenbroek  * setting the real, effective, and saved UIDs to different values before
4241122b286SDavid van Moolenbroek  * performing the setuid(2) or seteuid(2) call.
4251122b286SDavid van Moolenbroek  */
4261122b286SDavid van Moolenbroek static void
test89a(void)4271122b286SDavid van Moolenbroek test89a(void)
4281122b286SDavid van Moolenbroek {
4291122b286SDavid van Moolenbroek 	unsigned int setnum;
4301122b286SDavid van Moolenbroek 	int sub, status;
4311122b286SDavid van Moolenbroek 	pid_t pid;
4321122b286SDavid van Moolenbroek 
4331122b286SDavid van Moolenbroek 	subtest = 1;
4341122b286SDavid van Moolenbroek 
4351122b286SDavid van Moolenbroek 	for (setnum = 0; setnum < __arraycount(uid_sets); setnum++) {
4361122b286SDavid van Moolenbroek 		for (sub = SUB_REAL; sub <= SUB_EFF; sub++) {
4371122b286SDavid van Moolenbroek 			pid = fork();
4381122b286SDavid van Moolenbroek 
4391122b286SDavid van Moolenbroek 			switch (pid) {
4401122b286SDavid van Moolenbroek 			case -1:
4411122b286SDavid van Moolenbroek 				e(setnum);
4421122b286SDavid van Moolenbroek 
4431122b286SDavid van Moolenbroek 				break;
4441122b286SDavid van Moolenbroek 
4451122b286SDavid van Moolenbroek 			case 0:
4461122b286SDavid van Moolenbroek 				errct = 0;
4471122b286SDavid van Moolenbroek 
4481122b286SDavid van Moolenbroek 				sub89a((int)setnum, sub);
4491122b286SDavid van Moolenbroek 
4501122b286SDavid van Moolenbroek 				exit(errct);
4511122b286SDavid van Moolenbroek 				/* NOTREACHED */
4521122b286SDavid van Moolenbroek 
4531122b286SDavid van Moolenbroek 			default:
4541122b286SDavid van Moolenbroek 				if (waitpid(pid, &status, 0) != pid) e(setnum);
4551122b286SDavid van Moolenbroek 				if (!WIFEXITED(status)) e(setnum);
4561122b286SDavid van Moolenbroek 				if (WEXITSTATUS(status) != 0) e(setnum);
4571122b286SDavid van Moolenbroek 			}
4581122b286SDavid van Moolenbroek 		}
4591122b286SDavid van Moolenbroek 	}
4601122b286SDavid van Moolenbroek }
4611122b286SDavid van Moolenbroek 
4621122b286SDavid van Moolenbroek /*
4631122b286SDavid van Moolenbroek  * Ensure that the real, effective, and saved UIDs are fully preserved across
4641122b286SDavid van Moolenbroek  * fork(2) and non-setuid-binary exec(2) calls.
4651122b286SDavid van Moolenbroek  */
4661122b286SDavid van Moolenbroek static void
test89b(void)4671122b286SDavid van Moolenbroek test89b(void)
4681122b286SDavid van Moolenbroek {
4691122b286SDavid van Moolenbroek 	unsigned int setnum;
4701122b286SDavid van Moolenbroek 	int status;
4711122b286SDavid van Moolenbroek 	pid_t pid;
4721122b286SDavid van Moolenbroek 
4731122b286SDavid van Moolenbroek 	subtest = 2;
4741122b286SDavid van Moolenbroek 
4751122b286SDavid van Moolenbroek 	for (setnum = 0; setnum < __arraycount(uid_sets); setnum++) {
4761122b286SDavid van Moolenbroek 		if (uid_sets[setnum].uid != 0)
4771122b286SDavid van Moolenbroek 			continue; /* no need to do the same test >1 times */
4781122b286SDavid van Moolenbroek 
4791122b286SDavid van Moolenbroek 		pid = fork();
4801122b286SDavid van Moolenbroek 
4811122b286SDavid van Moolenbroek 		switch (pid) {
4821122b286SDavid van Moolenbroek 		case -1:
4831122b286SDavid van Moolenbroek 			e(setnum);
4841122b286SDavid van Moolenbroek 
4851122b286SDavid van Moolenbroek 			break;
4861122b286SDavid van Moolenbroek 
4871122b286SDavid van Moolenbroek 		case 0:
4881122b286SDavid van Moolenbroek 			errct = 0;
4891122b286SDavid van Moolenbroek 
4901122b286SDavid van Moolenbroek 			/*
4911122b286SDavid van Moolenbroek 			 * Test B uses some of the A-test code.  While rather
4921122b286SDavid van Moolenbroek 			 * ugly, this avoids duplication of some of test A's
4931122b286SDavid van Moolenbroek 			 * important UID logic.
4941122b286SDavid van Moolenbroek 			 */
4951122b286SDavid van Moolenbroek 			sub89a((int)setnum, SUB_RETAIN);
4961122b286SDavid van Moolenbroek 
4971122b286SDavid van Moolenbroek 			exit(errct);
4981122b286SDavid van Moolenbroek 			/* NOTREACHED */
4991122b286SDavid van Moolenbroek 
5001122b286SDavid van Moolenbroek 		default:
5011122b286SDavid van Moolenbroek 			if (waitpid(pid, &status, 0) != pid) e(setnum);
5021122b286SDavid van Moolenbroek 			if (!WIFEXITED(status)) e(setnum);
5031122b286SDavid van Moolenbroek 			if (WEXITSTATUS(status) != 0) e(setnum);
5041122b286SDavid van Moolenbroek 		}
5051122b286SDavid van Moolenbroek 	}
5061122b286SDavid van Moolenbroek }
5071122b286SDavid van Moolenbroek 
5081122b286SDavid van Moolenbroek /*
5091122b286SDavid van Moolenbroek  * Verify that the current process's real, effective, and saved group IDs are
5101122b286SDavid van Moolenbroek  * set to the given respective value.
5111122b286SDavid van Moolenbroek  */
5121122b286SDavid van Moolenbroek static void
test_gids(gid_t rgid,gid_t egid,gid_t sgid)5131122b286SDavid van Moolenbroek test_gids(gid_t rgid, gid_t egid, gid_t sgid)
5141122b286SDavid van Moolenbroek {
5151122b286SDavid van Moolenbroek 	struct kinfo_proc2 proc2;
5161122b286SDavid van Moolenbroek 
5171122b286SDavid van Moolenbroek 	if (getgid() != rgid) e(0);
5181122b286SDavid van Moolenbroek 	if (getegid() != egid) e(0);
5191122b286SDavid van Moolenbroek 
5201122b286SDavid van Moolenbroek 	/* As above. */
5211122b286SDavid van Moolenbroek 	if (get_proc2(getpid(), &proc2) != 0) e(0);
5221122b286SDavid van Moolenbroek 
5231122b286SDavid van Moolenbroek 	if (proc2.p_rgid != rgid) e(0);
5241122b286SDavid van Moolenbroek 	if (proc2.p_gid != egid) e(0);
5251122b286SDavid van Moolenbroek 	if (proc2.p_svgid != sgid) e(0);
5261122b286SDavid van Moolenbroek }
5271122b286SDavid van Moolenbroek 
5281122b286SDavid van Moolenbroek /*
5291122b286SDavid van Moolenbroek  * Verify that the real and effective group IDs are kept as is after an exec(2)
5301122b286SDavid van Moolenbroek  * call on a non-setgid binary, and that the saved group ID is set to the
5311122b286SDavid van Moolenbroek  * effective group ID.
5321122b286SDavid van Moolenbroek  */
5331122b286SDavid van Moolenbroek static void
exec89d(const char * param1,const char * param2 __unused)5341122b286SDavid van Moolenbroek exec89d(const char * param1, const char * param2 __unused)
5351122b286SDavid van Moolenbroek {
5361122b286SDavid van Moolenbroek 	const struct gid_set *set;
5371122b286SDavid van Moolenbroek 	int setnum;
5381122b286SDavid van Moolenbroek 
5391122b286SDavid van Moolenbroek 	setnum = atoi(param1);
5401122b286SDavid van Moolenbroek 	if (setnum < 0 || setnum >= __arraycount(gid_sets)) {
5411122b286SDavid van Moolenbroek 		e(setnum);
5421122b286SDavid van Moolenbroek 		return;
5431122b286SDavid van Moolenbroek 	}
5441122b286SDavid van Moolenbroek 	set = &gid_sets[setnum];
5451122b286SDavid van Moolenbroek 
5461122b286SDavid van Moolenbroek 	test_gids(set->rgid, set->egid, set->egid);
5471122b286SDavid van Moolenbroek }
5481122b286SDavid van Moolenbroek 
5491122b286SDavid van Moolenbroek /*
5501122b286SDavid van Moolenbroek  * The real, effective, and saved group IDs have been set up as indicated by
5511122b286SDavid van Moolenbroek  * the current set.  Verify that fork(2) and exec(2) do not change the real and
5521122b286SDavid van Moolenbroek  * effective GID, and that only exec(2) sets the saved GID to the effective
5531122b286SDavid van Moolenbroek  * GID.
5541122b286SDavid van Moolenbroek  */
5551122b286SDavid van Moolenbroek static void
sub89d(int setnum)5561122b286SDavid van Moolenbroek sub89d(int setnum)
5571122b286SDavid van Moolenbroek {
5581122b286SDavid van Moolenbroek 	const struct gid_set *set;
5591122b286SDavid van Moolenbroek 	char param1[32];
5601122b286SDavid van Moolenbroek 	pid_t pid;
5611122b286SDavid van Moolenbroek 	int status;
5621122b286SDavid van Moolenbroek 
5631122b286SDavid van Moolenbroek 	set = &gid_sets[setnum];
5641122b286SDavid van Moolenbroek 
5651122b286SDavid van Moolenbroek 	pid = fork();
5661122b286SDavid van Moolenbroek 
5671122b286SDavid van Moolenbroek 	switch (pid) {
5681122b286SDavid van Moolenbroek 	case -1:
5691122b286SDavid van Moolenbroek 		e(setnum);
5701122b286SDavid van Moolenbroek 		break;
5711122b286SDavid van Moolenbroek 
5721122b286SDavid van Moolenbroek 	case 0:
5731122b286SDavid van Moolenbroek 		/*
5741122b286SDavid van Moolenbroek 		 * Verify that all the GIDs were retained across the fork(2)
5751122b286SDavid van Moolenbroek 		 * call.
5761122b286SDavid van Moolenbroek 		 */
5771122b286SDavid van Moolenbroek 		test_gids(set->rgid, set->egid, set->sgid);
5781122b286SDavid van Moolenbroek 
5791122b286SDavid van Moolenbroek 		/* Clear the set-gid bit. */
5801122b286SDavid van Moolenbroek 		if (chmod(executable, S_IXUSR | S_IXGRP | S_IXOTH) != 0)
5811122b286SDavid van Moolenbroek 			e(setnum);
5821122b286SDavid van Moolenbroek 
5831122b286SDavid van Moolenbroek 		/* Alternate between preserving and dropping user IDs. */
5841122b286SDavid van Moolenbroek 		if (set->gid != 0) {
5851122b286SDavid van Moolenbroek 			if (setuid(3) != 0) e(setnum);
5861122b286SDavid van Moolenbroek 		}
5871122b286SDavid van Moolenbroek 
5881122b286SDavid van Moolenbroek 		snprintf(param1, sizeof(param1), "%d", setnum);
5891122b286SDavid van Moolenbroek 
5901122b286SDavid van Moolenbroek 		(void)execl(executable, executable, "DO CHECK", "d", param1,
5911122b286SDavid van Moolenbroek 		    "", NULL);
5921122b286SDavid van Moolenbroek 
5931122b286SDavid van Moolenbroek 		e(setnum);
5941122b286SDavid van Moolenbroek 		break;
5951122b286SDavid van Moolenbroek 
5961122b286SDavid van Moolenbroek 	default:
5971122b286SDavid van Moolenbroek 		if (waitpid(pid, &status, 0) != pid) e(setnum);
5981122b286SDavid van Moolenbroek 		if (!WIFEXITED(status)) e(setnum);
5991122b286SDavid van Moolenbroek 		if (WEXITSTATUS(status) != 0) e(setnum);
6001122b286SDavid van Moolenbroek 	}
6011122b286SDavid van Moolenbroek }
6021122b286SDavid van Moolenbroek 
6031122b286SDavid van Moolenbroek /*
6041122b286SDavid van Moolenbroek  * The real, effective, and saved group IDs have been set up as indicated by
6051122b286SDavid van Moolenbroek  * the current set.  Test one particular case for test C or D, and verify the
6061122b286SDavid van Moolenbroek  * result.
6071122b286SDavid van Moolenbroek  */
6081122b286SDavid van Moolenbroek static void
test_one_gid(int setnum,int sub)6091122b286SDavid van Moolenbroek test_one_gid(int setnum, int sub)
6101122b286SDavid van Moolenbroek {
6111122b286SDavid van Moolenbroek 	const struct gid_set *set;
6121122b286SDavid van Moolenbroek 	int res, exp;
6131122b286SDavid van Moolenbroek 
6141122b286SDavid van Moolenbroek 	set = &gid_sets[setnum];
6151122b286SDavid van Moolenbroek 
6161122b286SDavid van Moolenbroek 	/* Verify that the pre-call process state is as expected. */
6171122b286SDavid van Moolenbroek 	test_gids(set->rgid, set->egid, set->sgid);
6181122b286SDavid van Moolenbroek 
6191122b286SDavid van Moolenbroek 	/* Perform the call, and check whether the result is as expected. */
6201122b286SDavid van Moolenbroek 	switch (sub) {
6211122b286SDavid van Moolenbroek 	case SUB_REAL:
6221122b286SDavid van Moolenbroek 	case SUB_REAL_E0:
6231122b286SDavid van Moolenbroek 		if (sub != SUB_REAL_E0 && seteuid(1) != 0) e(0);
6241122b286SDavid van Moolenbroek 
6251122b286SDavid van Moolenbroek 		res = setgid(set->gid);
6261122b286SDavid van Moolenbroek 		exp = (sub != SUB_REAL_E0) ? (set->res - 1) : 0;
6271122b286SDavid van Moolenbroek 		break;
6281122b286SDavid van Moolenbroek 
6291122b286SDavid van Moolenbroek 	case SUB_EFF:
6301122b286SDavid van Moolenbroek 	case SUB_EFF_E0:
6311122b286SDavid van Moolenbroek 		if (sub != SUB_EFF_E0 && seteuid(1) != 0) e(0);
6321122b286SDavid van Moolenbroek 
6331122b286SDavid van Moolenbroek 		res = setegid(set->gid);
6341122b286SDavid van Moolenbroek 		exp = (sub != SUB_EFF_E0) ? (set->eres - 1) : 0;
6351122b286SDavid van Moolenbroek 		break;
6361122b286SDavid van Moolenbroek 
6371122b286SDavid van Moolenbroek 	case SUB_RETAIN:
6381122b286SDavid van Moolenbroek 		sub89d(setnum);
6391122b286SDavid van Moolenbroek 
6401122b286SDavid van Moolenbroek 		return;
641*3083d603SDavid van Moolenbroek 
642*3083d603SDavid van Moolenbroek 	default:
643*3083d603SDavid van Moolenbroek 		abort();
6441122b286SDavid van Moolenbroek 	}
6451122b286SDavid van Moolenbroek 
6461122b286SDavid van Moolenbroek 	if (res != 0 && (res != -1 || errno != EPERM)) e(setnum);
6471122b286SDavid van Moolenbroek 
6481122b286SDavid van Moolenbroek 	if (res != exp) e(setnum);
6491122b286SDavid van Moolenbroek 
6501122b286SDavid van Moolenbroek 	/* Verify that the post-call process state is as expected as well. */
6511122b286SDavid van Moolenbroek 	if (res == 0) {
6521122b286SDavid van Moolenbroek 		if (sub == SUB_EFF || sub == SUB_EFF_E0)
6531122b286SDavid van Moolenbroek 			test_gids(set->rgid, set->gid, set->sgid);
6541122b286SDavid van Moolenbroek 		else
6551122b286SDavid van Moolenbroek 			test_gids(set->gid, set->gid, set->gid);
6561122b286SDavid van Moolenbroek 	} else
6571122b286SDavid van Moolenbroek 		test_gids(set->rgid, set->egid, set->sgid);
6581122b286SDavid van Moolenbroek }
6591122b286SDavid van Moolenbroek 
6601122b286SDavid van Moolenbroek /*
6611122b286SDavid van Moolenbroek  * Test setgid(2) or setegid(2) after a successful execve(2) call, which should
6621122b286SDavid van Moolenbroek  * have set the process's effective and saved group ID.
6631122b286SDavid van Moolenbroek  */
6641122b286SDavid van Moolenbroek static void
exec89c(const char * param1,const char * param2)6651122b286SDavid van Moolenbroek exec89c(const char * param1, const char * param2)
6661122b286SDavid van Moolenbroek {
6671122b286SDavid van Moolenbroek 	const struct gid_set *set;
6681122b286SDavid van Moolenbroek 	int setnum, sub;
6691122b286SDavid van Moolenbroek 
6701122b286SDavid van Moolenbroek 	setnum = atoi(param1);
6711122b286SDavid van Moolenbroek 	if (setnum < 0 || setnum >= __arraycount(gid_sets)) {
6721122b286SDavid van Moolenbroek 		e(setnum);
6731122b286SDavid van Moolenbroek 		return;
6741122b286SDavid van Moolenbroek 	}
6751122b286SDavid van Moolenbroek 	set = &gid_sets[setnum];
6761122b286SDavid van Moolenbroek 
6771122b286SDavid van Moolenbroek 	sub = atoi(param2);
6781122b286SDavid van Moolenbroek 
6791122b286SDavid van Moolenbroek 	/* Finish setting up the initial condition. */
6801122b286SDavid van Moolenbroek 	if (set->egid != set->sgid && setegid(set->egid) != 0) e(setnum);
6811122b286SDavid van Moolenbroek 
6821122b286SDavid van Moolenbroek 	/* Perform the actual test. */
6831122b286SDavid van Moolenbroek 	test_one_gid(setnum, sub);
6841122b286SDavid van Moolenbroek }
6851122b286SDavid van Moolenbroek 
6861122b286SDavid van Moolenbroek /*
6871122b286SDavid van Moolenbroek  * Test setgid(2) or setegid(2) with a certain value starting from a certain
6881122b286SDavid van Moolenbroek  * initial condition, as identified by the given gid_sets[] array element.  As
6891122b286SDavid van Moolenbroek  * a side effect, test that in particular exec(2) properly sets the effective
6901122b286SDavid van Moolenbroek  * and saved group ID.
6911122b286SDavid van Moolenbroek  */
6921122b286SDavid van Moolenbroek static void
sub89c(int setnum,int sub)6931122b286SDavid van Moolenbroek sub89c(int setnum, int sub)
6941122b286SDavid van Moolenbroek {
6951122b286SDavid van Moolenbroek 	const struct gid_set *set;
6961122b286SDavid van Moolenbroek 	char param1[32], param2[32];
6971122b286SDavid van Moolenbroek 
6981122b286SDavid van Moolenbroek 	set = &gid_sets[setnum];
6991122b286SDavid van Moolenbroek 
7001122b286SDavid van Moolenbroek 	/*
7011122b286SDavid van Moolenbroek 	 * Figure out how to set the real, effective, and saved GIDs to those
7021122b286SDavid van Moolenbroek 	 * of the set structure.  In this case, all combinations are possible.
7031122b286SDavid van Moolenbroek 	 */
7041122b286SDavid van Moolenbroek 	if (set->rgid != set->sgid) {
7051122b286SDavid van Moolenbroek 		/*
7061122b286SDavid van Moolenbroek 		 * In order to set the saved GID to something other than the
7071122b286SDavid van Moolenbroek 		 * real GID, we must exec(2) a set-gid binary.
7081122b286SDavid van Moolenbroek 		 */
7091122b286SDavid van Moolenbroek 		if (chown(executable, 0 /*anything*/, set->sgid) != 0) e(0);
7101122b286SDavid van Moolenbroek 		if (chmod(executable,
7111122b286SDavid van Moolenbroek 		    S_ISGID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
7121122b286SDavid van Moolenbroek 
7131122b286SDavid van Moolenbroek 		if (setgid(set->rgid) != 0) e(setnum);
7141122b286SDavid van Moolenbroek 
7151122b286SDavid van Moolenbroek 		snprintf(param1, sizeof(param1), "%d", setnum);
7161122b286SDavid van Moolenbroek 		snprintf(param2, sizeof(param2), "%d", sub);
7171122b286SDavid van Moolenbroek 
7181122b286SDavid van Moolenbroek 		(void)execl(executable, executable, "DO CHECK", "c", param1,
7191122b286SDavid van Moolenbroek 		    param2, NULL);
7201122b286SDavid van Moolenbroek 
7211122b286SDavid van Moolenbroek 		e(0);
7221122b286SDavid van Moolenbroek 	} else {
7231122b286SDavid van Moolenbroek 		/*
7241122b286SDavid van Moolenbroek 		 * If the real and saved group ID are to be set to the same
7251122b286SDavid van Moolenbroek 		 * value, we need not use exec(2).
7261122b286SDavid van Moolenbroek 		 */
7271122b286SDavid van Moolenbroek 		if (setgid(set->rgid) != 0) e(setnum);
7281122b286SDavid van Moolenbroek 		if (setegid(set->egid) != 0) e(setnum);
7291122b286SDavid van Moolenbroek 
7301122b286SDavid van Moolenbroek 		/* Perform the actual test. */
7311122b286SDavid van Moolenbroek 		test_one_gid(setnum, sub);
7321122b286SDavid van Moolenbroek 	}
7331122b286SDavid van Moolenbroek }
7341122b286SDavid van Moolenbroek 
7351122b286SDavid van Moolenbroek /*
7361122b286SDavid van Moolenbroek  * Test setgid(2) and setegid(2) calls with various initial conditions, by
7371122b286SDavid van Moolenbroek  * setting the real, effective, and saved GIDs to different values before
7381122b286SDavid van Moolenbroek  * performing the setgid(2) or setegid(2) call.  At the same time, verify that
7391122b286SDavid van Moolenbroek  * if the caller has an effective UID of 0, all set(e)gid calls are allowed.
7401122b286SDavid van Moolenbroek  */
7411122b286SDavid van Moolenbroek static void
test89c(void)7421122b286SDavid van Moolenbroek test89c(void)
7431122b286SDavid van Moolenbroek {
7441122b286SDavid van Moolenbroek 	unsigned int setnum;
7451122b286SDavid van Moolenbroek 	int sub, status;
7461122b286SDavid van Moolenbroek 	pid_t pid;
7471122b286SDavid van Moolenbroek 
7481122b286SDavid van Moolenbroek 	subtest = 3;
7491122b286SDavid van Moolenbroek 
7501122b286SDavid van Moolenbroek 	for (setnum = 0; setnum < __arraycount(gid_sets); setnum++) {
7511122b286SDavid van Moolenbroek 		for (sub = SUB_REAL; sub <= SUB_EFF_E0; sub++) {
7521122b286SDavid van Moolenbroek 			pid = fork();
7531122b286SDavid van Moolenbroek 
7541122b286SDavid van Moolenbroek 			switch (pid) {
7551122b286SDavid van Moolenbroek 			case -1:
7561122b286SDavid van Moolenbroek 				e(setnum);
7571122b286SDavid van Moolenbroek 
7581122b286SDavid van Moolenbroek 				break;
7591122b286SDavid van Moolenbroek 
7601122b286SDavid van Moolenbroek 			case 0:
7611122b286SDavid van Moolenbroek 				errct = 0;
7621122b286SDavid van Moolenbroek 
7631122b286SDavid van Moolenbroek 				sub89c((int)setnum, sub);
7641122b286SDavid van Moolenbroek 
7651122b286SDavid van Moolenbroek 				exit(errct);
7661122b286SDavid van Moolenbroek 				/* NOTREACHED */
7671122b286SDavid van Moolenbroek 
7681122b286SDavid van Moolenbroek 			default:
7691122b286SDavid van Moolenbroek 				if (waitpid(pid, &status, 0) != pid) e(setnum);
7701122b286SDavid van Moolenbroek 				if (!WIFEXITED(status)) e(setnum);
7711122b286SDavid van Moolenbroek 				if (WEXITSTATUS(status) != 0) e(setnum);
7721122b286SDavid van Moolenbroek 			}
7731122b286SDavid van Moolenbroek 		}
7741122b286SDavid van Moolenbroek 	}
7751122b286SDavid van Moolenbroek }
7761122b286SDavid van Moolenbroek 
7771122b286SDavid van Moolenbroek /*
7781122b286SDavid van Moolenbroek  * Ensure that the real, effective, and saved GIDs are fully preserved across
7791122b286SDavid van Moolenbroek  * fork(2) and non-setgid-binary exec(2) calls.
7801122b286SDavid van Moolenbroek  */
7811122b286SDavid van Moolenbroek static void
test89d(void)7821122b286SDavid van Moolenbroek test89d(void)
7831122b286SDavid van Moolenbroek {
7841122b286SDavid van Moolenbroek 	unsigned int setnum;
7851122b286SDavid van Moolenbroek 	int status;
7861122b286SDavid van Moolenbroek 	pid_t pid;
7871122b286SDavid van Moolenbroek 
7881122b286SDavid van Moolenbroek 	subtest = 4;
7891122b286SDavid van Moolenbroek 
7901122b286SDavid van Moolenbroek 	for (setnum = 0; setnum < __arraycount(gid_sets); setnum++) {
7911122b286SDavid van Moolenbroek 		if (gid_sets[setnum].gid == 2)
7921122b286SDavid van Moolenbroek 			continue; /* no need to do the same test >1 times */
7931122b286SDavid van Moolenbroek 
7941122b286SDavid van Moolenbroek 		pid = fork();
7951122b286SDavid van Moolenbroek 
7961122b286SDavid van Moolenbroek 		switch (pid) {
7971122b286SDavid van Moolenbroek 		case -1:
7981122b286SDavid van Moolenbroek 			e(setnum);
7991122b286SDavid van Moolenbroek 
8001122b286SDavid van Moolenbroek 			break;
8011122b286SDavid van Moolenbroek 
8021122b286SDavid van Moolenbroek 		case 0:
8031122b286SDavid van Moolenbroek 			errct = 0;
8041122b286SDavid van Moolenbroek 
8051122b286SDavid van Moolenbroek 			/* Similarly, test D uses some of the C-test code. */
8061122b286SDavid van Moolenbroek 			sub89c((int)setnum, SUB_RETAIN);
8071122b286SDavid van Moolenbroek 
8081122b286SDavid van Moolenbroek 			exit(errct);
8091122b286SDavid van Moolenbroek 			/* NOTREACHED */
8101122b286SDavid van Moolenbroek 
8111122b286SDavid van Moolenbroek 		default:
8121122b286SDavid van Moolenbroek 			if (waitpid(pid, &status, 0) != pid) e(setnum);
8131122b286SDavid van Moolenbroek 			if (!WIFEXITED(status)) e(setnum);
8141122b286SDavid van Moolenbroek 			if (WEXITSTATUS(status) != 0) e(setnum);
8151122b286SDavid van Moolenbroek 		}
8161122b286SDavid van Moolenbroek 	}
8171122b286SDavid van Moolenbroek }
8181122b286SDavid van Moolenbroek 
8191122b286SDavid van Moolenbroek /*
8201122b286SDavid van Moolenbroek  * Either perform the second step of setting up user and group IDs, or check
8211122b286SDavid van Moolenbroek  * whether the user and/or group IDs have indeed been changed appropriately as
8221122b286SDavid van Moolenbroek  * the result of the second exec(2).
8231122b286SDavid van Moolenbroek  */
8241122b286SDavid van Moolenbroek static void
exec89e(const char * param1,const char * param2)8251122b286SDavid van Moolenbroek exec89e(const char * param1, const char * param2)
8261122b286SDavid van Moolenbroek {
8271122b286SDavid van Moolenbroek 	int mask, step;
8281122b286SDavid van Moolenbroek 	mode_t mode;
8291122b286SDavid van Moolenbroek 
8301122b286SDavid van Moolenbroek 	mask = atoi(param1);
8311122b286SDavid van Moolenbroek 	step = atoi(param2);
8321122b286SDavid van Moolenbroek 
8331122b286SDavid van Moolenbroek 	if (step == 0) {
8341122b286SDavid van Moolenbroek 		mode = S_IXUSR | S_IXGRP | S_IXOTH;
8351122b286SDavid van Moolenbroek 		if (mask & 1) mode |= S_ISUID;
8361122b286SDavid van Moolenbroek 		if (mask & 2) mode |= S_ISGID;
8371122b286SDavid van Moolenbroek 
8381122b286SDavid van Moolenbroek 		if (chown(executable, 6, 7) != 0) e(0);
8391122b286SDavid van Moolenbroek 		if (chmod(executable, mode) != 0) e(0);
8401122b286SDavid van Moolenbroek 
8411122b286SDavid van Moolenbroek 		if (setegid(4) != 0) e(0);
8421122b286SDavid van Moolenbroek 		if (seteuid(2) != 0) e(0);
8431122b286SDavid van Moolenbroek 
8441122b286SDavid van Moolenbroek 		test_uids(1, 2, 0);
8451122b286SDavid van Moolenbroek 		test_gids(3, 4, 5);
8461122b286SDavid van Moolenbroek 
8471122b286SDavid van Moolenbroek 		(void)execl(executable, executable, "DO CHECK", "e", param1,
8481122b286SDavid van Moolenbroek 		    "1", NULL);
8491122b286SDavid van Moolenbroek 
8501122b286SDavid van Moolenbroek 		e(0);
8511122b286SDavid van Moolenbroek 	} else {
8521122b286SDavid van Moolenbroek 		if (mask & 1)
8531122b286SDavid van Moolenbroek 			test_uids(1, 6, 6);
8541122b286SDavid van Moolenbroek 		else
8551122b286SDavid van Moolenbroek 			test_uids(1, 2, 2);
8561122b286SDavid van Moolenbroek 
8571122b286SDavid van Moolenbroek 		if (mask & 2)
8581122b286SDavid van Moolenbroek 			test_gids(3, 7, 7);
8591122b286SDavid van Moolenbroek 		else
8601122b286SDavid van Moolenbroek 			test_gids(3, 4, 4);
8611122b286SDavid van Moolenbroek 	}
8621122b286SDavid van Moolenbroek }
8631122b286SDavid van Moolenbroek 
8641122b286SDavid van Moolenbroek /*
8651122b286SDavid van Moolenbroek  * Set up for the set-uid/set-gid execution test by initializing to different
8661122b286SDavid van Moolenbroek  * real and effective user IDs.
8671122b286SDavid van Moolenbroek  */
8681122b286SDavid van Moolenbroek static void
sub89e(int mask)8691122b286SDavid van Moolenbroek sub89e(int mask)
8701122b286SDavid van Moolenbroek {
8711122b286SDavid van Moolenbroek 	char param1[32];
8721122b286SDavid van Moolenbroek 
8731122b286SDavid van Moolenbroek 	if (chown(executable, 0, 5) != 0) e(0);
8741122b286SDavid van Moolenbroek 	if (chmod(executable,
8751122b286SDavid van Moolenbroek 	    S_ISUID | S_ISGID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
8761122b286SDavid van Moolenbroek 
8771122b286SDavid van Moolenbroek 	if (setgid(3) != 0) e(0);
8781122b286SDavid van Moolenbroek 	if (setuid(1) != 0) e(0);
8791122b286SDavid van Moolenbroek 
8801122b286SDavid van Moolenbroek 	snprintf(param1, sizeof(param1), "%d", mask);
8811122b286SDavid van Moolenbroek 	(void)execl(executable, executable, "DO CHECK", "e", param1, "0",
8821122b286SDavid van Moolenbroek 	    NULL);
8831122b286SDavid van Moolenbroek }
8841122b286SDavid van Moolenbroek 
8851122b286SDavid van Moolenbroek /*
8861122b286SDavid van Moolenbroek  * Perform basic verification that the set-uid and set-gid bits on binaries are
8871122b286SDavid van Moolenbroek  * fully independent from each other.
8881122b286SDavid van Moolenbroek  */
8891122b286SDavid van Moolenbroek static void
test89e(void)8901122b286SDavid van Moolenbroek test89e(void)
8911122b286SDavid van Moolenbroek {
8921122b286SDavid van Moolenbroek 	int mask, status;
8931122b286SDavid van Moolenbroek 	pid_t pid;
8941122b286SDavid van Moolenbroek 
8951122b286SDavid van Moolenbroek 	subtest = 5;
8961122b286SDavid van Moolenbroek 
8971122b286SDavid van Moolenbroek 	for (mask = 0; mask <= 3; mask++) {
8981122b286SDavid van Moolenbroek 		pid = fork();
8991122b286SDavid van Moolenbroek 
9001122b286SDavid van Moolenbroek 		switch (pid) {
9011122b286SDavid van Moolenbroek 		case -1:
9021122b286SDavid van Moolenbroek 			e(0);
9031122b286SDavid van Moolenbroek 
9041122b286SDavid van Moolenbroek 			break;
9051122b286SDavid van Moolenbroek 
9061122b286SDavid van Moolenbroek 		case 0:
9071122b286SDavid van Moolenbroek 			errct = 0;
9081122b286SDavid van Moolenbroek 
9091122b286SDavid van Moolenbroek 			sub89e(mask);
9101122b286SDavid van Moolenbroek 
9111122b286SDavid van Moolenbroek 			exit(errct);
9121122b286SDavid van Moolenbroek 			/* NOTREACHED */
9131122b286SDavid van Moolenbroek 
9141122b286SDavid van Moolenbroek 		default:
9151122b286SDavid van Moolenbroek 			if (waitpid(pid, &status, 0) != pid) e(mask);
9161122b286SDavid van Moolenbroek 			if (!WIFEXITED(status)) e(mask);
9171122b286SDavid van Moolenbroek 			if (WEXITSTATUS(status) != 0) e(mask);
9181122b286SDavid van Moolenbroek 		}
9191122b286SDavid van Moolenbroek 	}
9201122b286SDavid van Moolenbroek }
9211122b286SDavid van Moolenbroek 
9221122b286SDavid van Moolenbroek /*
9231122b286SDavid van Moolenbroek  * Call the right function after having executed myself.
9241122b286SDavid van Moolenbroek  */
9251122b286SDavid van Moolenbroek static void
exec89(const char * param0,const char * param1,const char * param2)9261122b286SDavid van Moolenbroek exec89(const char * param0, const char * param1, const char * param2)
9271122b286SDavid van Moolenbroek {
9281122b286SDavid van Moolenbroek 
9291122b286SDavid van Moolenbroek 	switch (param0[0]) {
9301122b286SDavid van Moolenbroek 	case 'a':
9311122b286SDavid van Moolenbroek 		exec89a(param1, param2);
9321122b286SDavid van Moolenbroek 		break;
9331122b286SDavid van Moolenbroek 
9341122b286SDavid van Moolenbroek 	case 'b':
9351122b286SDavid van Moolenbroek 		exec89b(param1, param2);
9361122b286SDavid van Moolenbroek 		break;
9371122b286SDavid van Moolenbroek 
9381122b286SDavid van Moolenbroek 	case 'c':
9391122b286SDavid van Moolenbroek 		exec89c(param1, param2);
9401122b286SDavid van Moolenbroek 		break;
9411122b286SDavid van Moolenbroek 
9421122b286SDavid van Moolenbroek 	case 'd':
9431122b286SDavid van Moolenbroek 		exec89d(param1, param2);
9441122b286SDavid van Moolenbroek 		break;
9451122b286SDavid van Moolenbroek 
9461122b286SDavid van Moolenbroek 	case 'e':
9471122b286SDavid van Moolenbroek 		exec89e(param1, param2);
9481122b286SDavid van Moolenbroek 		break;
9491122b286SDavid van Moolenbroek 
9501122b286SDavid van Moolenbroek 	default:
9511122b286SDavid van Moolenbroek 		e(0);
9521122b286SDavid van Moolenbroek 	}
9531122b286SDavid van Moolenbroek 
9541122b286SDavid van Moolenbroek 	exit(errct);
9551122b286SDavid van Moolenbroek }
9561122b286SDavid van Moolenbroek 
9571122b286SDavid van Moolenbroek /*
9581122b286SDavid van Moolenbroek  * Initialize the test.
9591122b286SDavid van Moolenbroek  */
9601122b286SDavid van Moolenbroek static void
test89_init(void)9611122b286SDavid van Moolenbroek test89_init(void)
9621122b286SDavid van Moolenbroek {
9631122b286SDavid van Moolenbroek 	char cp_cmd[PATH_MAX + 9];
9641122b286SDavid van Moolenbroek 	int status;
9651122b286SDavid van Moolenbroek 
9661122b286SDavid van Moolenbroek 	subtest = 0;
9671122b286SDavid van Moolenbroek 
9681122b286SDavid van Moolenbroek 	/* Reset all user and group IDs to known values. */
9691122b286SDavid van Moolenbroek 	if (setuid(0) != 0) e(0);
9701122b286SDavid van Moolenbroek 	if (setgid(0) != 0) e(0);
9711122b286SDavid van Moolenbroek 	if (setgroups(0, NULL) != 0) e(0);
9721122b286SDavid van Moolenbroek 
9731122b286SDavid van Moolenbroek 	test_uids(0, 0, 0);
9741122b286SDavid van Moolenbroek 	test_gids(0, 0, 0);
9751122b286SDavid van Moolenbroek 
9761122b286SDavid van Moolenbroek 	/* Make a copy of the binary, which as of start() is one level up. */
9771122b286SDavid van Moolenbroek 	snprintf(cp_cmd, sizeof(cp_cmd), "cp ../%s .", executable);
9781122b286SDavid van Moolenbroek 
9791122b286SDavid van Moolenbroek 	status = system(cp_cmd);
9801122b286SDavid van Moolenbroek 	if (status < 0 || !WIFEXITED(status) ||
9811122b286SDavid van Moolenbroek 	    WEXITSTATUS(status) != EXIT_SUCCESS) e(0);
9821122b286SDavid van Moolenbroek }
9831122b286SDavid van Moolenbroek 
9841122b286SDavid van Moolenbroek /*
9851122b286SDavid van Moolenbroek  * Test program for set[ug]id, sete[ug]id, and saved IDs.
9861122b286SDavid van Moolenbroek  */
9871122b286SDavid van Moolenbroek int
main(int argc,char ** argv)9881122b286SDavid van Moolenbroek main(int argc, char ** argv)
9891122b286SDavid van Moolenbroek {
9901122b286SDavid van Moolenbroek 	int i, m;
9911122b286SDavid van Moolenbroek 
9921122b286SDavid van Moolenbroek 	executable = argv[0];
9931122b286SDavid van Moolenbroek 
9941122b286SDavid van Moolenbroek 	/* This test executes itself.  Handle that case first. */
9951122b286SDavid van Moolenbroek 	if (argc == 5 && !strcmp(argv[1], "DO CHECK"))
9961122b286SDavid van Moolenbroek 		exec89(argv[2], argv[3], argv[4]);
9971122b286SDavid van Moolenbroek 
9981122b286SDavid van Moolenbroek 	start(89);
9991122b286SDavid van Moolenbroek 
10001122b286SDavid van Moolenbroek 	test89_init();
10011122b286SDavid van Moolenbroek 
10021122b286SDavid van Moolenbroek 	if (argc == 2)
10031122b286SDavid van Moolenbroek 		m = atoi(argv[1]);
10041122b286SDavid van Moolenbroek 	else
10051122b286SDavid van Moolenbroek 		m = 0xFF;
10061122b286SDavid van Moolenbroek 
10071122b286SDavid van Moolenbroek 	for (i = 0; i < ITERATIONS; i++) {
10081122b286SDavid van Moolenbroek 		if (m & 0x01) test89a();
10091122b286SDavid van Moolenbroek 		if (m & 0x02) test89b();
10101122b286SDavid van Moolenbroek 		if (m & 0x04) test89c();
10111122b286SDavid van Moolenbroek 		if (m & 0x08) test89d();
10121122b286SDavid van Moolenbroek 		if (m & 0x10) test89e();
10131122b286SDavid van Moolenbroek 	}
10141122b286SDavid van Moolenbroek 
10151122b286SDavid van Moolenbroek 	quit();
10161122b286SDavid van Moolenbroek 	/* NOTREACHED */
10171122b286SDavid van Moolenbroek }
1018