xref: /minix3/minix/tests/test89.c (revision 53d2fa057ee88a709bcc793d3c777acad2c2a2ee)
1 /* Tests for set[ug]id, sete[ug]id, and saved IDs - by D.C. van Moolenbroek */
2 /* This test must be run as root, as it tests privileged operations. */
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include <sys/wait.h>
7 #include <sys/sysctl.h>
8 #include <unistd.h>
9 
10 #include "common.h"
11 
12 #define ITERATIONS	2
13 
14 /* These are in a specific order. */
15 enum {
16 	SUB_REAL,	/* test set[ug]id(2) */
17 	SUB_EFF,	/* test sete[ug]id(2) */
18 	SUB_REAL_E0,	/* test setgid(2) with euid=0 */
19 	SUB_EFF_E0,	/* test setegid(2) with euid=0 */
20 	SUB_RETAIN,	/* test r/e/s preservation across fork(2), exec(2) */
21 };
22 
23 static const char *executable;
24 
25 /*
26  * The table below is exhaustive in terms of different combinations of real,
27  * effective, and saved user IDs (with 0 being a special value, but 1 and 2
28  * being interchangeable), but not all these combinations can actually be
29  * established in practice.  The results for which there is no way to create
30  * the initial condition are set to -1.  If we ever implement setresuid(2),
31  * these results can be filled in and tested as well.
32  */
33 static const struct uid_set {
34 	uid_t ruid;
35 	uid_t euid;
36 	uid_t suid;
37 	uid_t uid;
38 	int res;
39 	int eres;
40 } uid_sets[] = {
41 	{ 0, 0, 0, 0,  1,  1 },
42 	{ 0, 0, 0, 1,  1,  1 },
43 	{ 0, 0, 1, 0,  1,  1 },
44 	{ 0, 0, 1, 1,  1,  1 },
45 	{ 0, 0, 1, 2,  1,  1 },
46 	{ 0, 1, 0, 0,  1,  1 },
47 	{ 0, 1, 0, 1,  0,  0 },
48 	{ 0, 1, 0, 2,  0,  0 },
49 	{ 0, 1, 1, 0,  1,  1 },
50 	{ 0, 1, 1, 1,  0,  1 },
51 	{ 0, 1, 1, 2,  0,  0 },
52 	{ 0, 1, 2, 0, -1, -1 },
53 	{ 0, 1, 2, 1, -1, -1 },
54 	{ 0, 1, 2, 2, -1, -1 },
55 	{ 1, 0, 0, 0,  1,  1 },
56 	{ 1, 0, 0, 1,  1,  1 },
57 	{ 1, 0, 0, 2,  1,  1 },
58 	{ 1, 0, 1, 0, -1, -1 },
59 	{ 1, 0, 1, 1, -1, -1 },
60 	{ 1, 0, 1, 2, -1, -1 },
61 	{ 1, 0, 2, 0, -1, -1 },
62 	{ 1, 0, 2, 1, -1, -1 },
63 	{ 1, 0, 2, 2, -1, -1 },
64 	{ 1, 1, 0, 0,  0,  1 },
65 	{ 1, 1, 0, 1,  1,  1 },
66 	{ 1, 1, 0, 2,  0,  0 },
67 	{ 1, 1, 1, 0,  0,  0 },
68 	{ 1, 1, 1, 1,  1,  1 },
69 	{ 1, 1, 1, 2,  0,  0 },
70 	{ 1, 1, 2, 0,  0,  0 },
71 	{ 1, 1, 2, 1,  1,  1 },
72 	{ 1, 1, 2, 2,  0,  1 },
73 	{ 1, 2, 0, 0,  0,  1 },
74 	{ 1, 2, 0, 1,  1,  1 },
75 	{ 1, 2, 0, 2,  0,  0 },
76 	{ 1, 2, 1, 0, -1, -1 },
77 	{ 1, 2, 1, 1, -1, -1 },
78 	{ 1, 2, 1, 2, -1, -1 },
79 	{ 1, 2, 2, 0,  0,  0 },
80 	{ 1, 2, 2, 1,  1,  1 },
81 	{ 1, 2, 2, 2,  0,  1 },
82 };
83 
84 /*
85  * The same type of table but now for group identifiers.  In this case, all
86  * combinations are possible to establish in practice, because the effective
87  * UID, not the GID, is used for the privilege check.  GID 0 does not have any
88  * special meaning, but we still test it as though it does, in order to ensure
89  * that it in fact does not.
90  */
91 static const struct gid_set {
92 	gid_t rgid;
93 	gid_t egid;
94 	gid_t sgid;
95 	gid_t gid;
96 	int res;
97 	int eres;
98 } gid_sets[] = {
99 	{ 0, 0, 0, 0, 1, 1 },
100 	{ 0, 0, 0, 1, 0, 0 },
101 	{ 0, 0, 1, 0, 1, 1 },
102 	{ 0, 0, 1, 1, 0, 1 },
103 	{ 0, 0, 1, 2, 0, 0 },
104 	{ 0, 1, 0, 0, 1, 1 },
105 	{ 0, 1, 0, 1, 0, 0 },
106 	{ 0, 1, 0, 2, 0, 0 },
107 	{ 0, 1, 1, 0, 1, 1 },
108 	{ 0, 1, 1, 1, 0, 1 },
109 	{ 0, 1, 1, 2, 0, 0 },
110 	{ 0, 1, 2, 0, 1, 1 },
111 	{ 0, 1, 2, 1, 0, 0 },
112 	{ 0, 1, 2, 2, 0, 1 },
113 	{ 1, 0, 0, 0, 0, 1 },
114 	{ 1, 0, 0, 1, 1, 1 },
115 	{ 1, 0, 0, 2, 0, 0 },
116 	{ 1, 0, 1, 0, 0, 0 },
117 	{ 1, 0, 1, 1, 1, 1 },
118 	{ 1, 0, 1, 2, 0, 0 },
119 	{ 1, 0, 2, 0, 0, 0 },
120 	{ 1, 0, 2, 1, 1, 1 },
121 	{ 1, 0, 2, 2, 0, 1 },
122 	{ 1, 1, 0, 0, 0, 1 },
123 	{ 1, 1, 0, 1, 1, 1 },
124 	{ 1, 1, 0, 2, 0, 0 },
125 	{ 1, 1, 1, 0, 0, 0 },
126 	{ 1, 1, 1, 1, 1, 1 },
127 	{ 1, 1, 1, 2, 0, 0 },
128 	{ 1, 1, 2, 0, 0, 0 },
129 	{ 1, 1, 2, 1, 1, 1 },
130 	{ 1, 1, 2, 2, 0, 1 },
131 	{ 1, 2, 0, 0, 0, 1 },
132 	{ 1, 2, 0, 1, 1, 1 },
133 	{ 1, 2, 0, 2, 0, 0 },
134 	{ 1, 2, 1, 0, 0, 0 },
135 	{ 1, 2, 1, 1, 1, 1 },
136 	{ 1, 2, 1, 2, 0, 0 },
137 	{ 1, 2, 2, 0, 0, 0 },
138 	{ 1, 2, 2, 1, 1, 1 },
139 	{ 1, 2, 2, 2, 0, 1 },
140 };
141 
142 /*
143  * Obtain the kinfo_proc2 data for the given process ID.  Return 0 on success,
144  * or -1 with errno set appropriately on failure.
145  */
146 static int
147 get_proc2(pid_t pid, struct kinfo_proc2 * proc2)
148 {
149 	int mib[6];
150 	size_t oldlen;
151 
152 	/*
153 	 * FIXME: for performance reasons, the MIB service updates it process
154 	 * tables only every clock tick.  As a result, we may not be able to
155 	 * obtain accurate process details right away, and we need to wait.
156 	 * Eventually, the MIB service should retrieve more targeted subsets of
157 	 * the process tables, and this problem should go away at least for
158 	 * specific queries such as this one, which queries only a single PID.
159 	 */
160 	usleep((2000000 + sysconf(_SC_CLK_TCK)) / sysconf(_SC_CLK_TCK));
161 
162 	mib[0] = CTL_KERN;
163 	mib[1] = KERN_PROC2;
164 	mib[2] = KERN_PROC_PID;
165 	mib[3] = pid;
166 	mib[4] = sizeof(*proc2);
167 	mib[5] = 1;
168 
169 	oldlen = sizeof(*proc2);
170 	if (sysctl(mib, __arraycount(mib), proc2, &oldlen, NULL, 0) == -1)
171 		return -1;
172 	if (oldlen != sizeof(*proc2)) {
173 		errno = ESRCH;
174 		return -1;
175 	}
176 	return 0;
177 }
178 
179 /*
180  * Verify that the current process's real, effective, and saved user IDs are
181  * set to the given respective value.
182  */
183 static void
184 test_uids(uid_t ruid, uid_t euid, uid_t suid)
185 {
186 	struct kinfo_proc2 proc2;
187 
188 	if (getuid() != ruid) e(0);
189 	if (geteuid() != euid) e(0);
190 
191 	/*
192 	 * There is no system call specifically to retrieve the saved user ID,
193 	 * so we use sysctl(2) to obtain process information.  This allows us
194 	 * to verify the real and effective user IDs once more, too.
195 	 */
196 	if (get_proc2(getpid(), &proc2) != 0) e(0);
197 
198 	if (proc2.p_ruid != ruid) e(0);
199 	if (proc2.p_uid != euid) e(0);
200 	if (proc2.p_svuid != suid) e(0);
201 }
202 
203 /*
204  * Verify that the real and effective user IDs are kept as is after an exec(2)
205  * call on a non-setuid binary, and that the saved user ID is set to the
206  * effective user ID.
207  */
208 static void
209 exec89b(const char * param1, const char * param2 __unused)
210 {
211 	const struct uid_set *set;
212 	int setnum;
213 
214 	setnum = atoi(param1);
215 	if (setnum < 0 || setnum >= __arraycount(uid_sets)) {
216 		e(setnum);
217 		return;
218 	}
219 	set = &uid_sets[setnum];
220 
221 	test_uids(set->ruid, set->euid, set->euid);
222 }
223 
224 /*
225  * The real, effective, and saved user IDs have been set up as indicated by the
226  * current set.  Verify that fork(2) and exec(2) do not change the real and
227  * effective UIDs, and that only exec(2) sets the saved UID to the effective
228  * UID.
229  */
230 static void
231 sub89b(int setnum)
232 {
233 	const struct uid_set *set;
234 	char param1[32];
235 	pid_t pid;
236 	int status;
237 
238 	set = &uid_sets[setnum];
239 
240 	pid = fork();
241 
242 	switch (pid) {
243 	case -1:
244 		e(setnum);
245 		break;
246 
247 	case 0:
248 		/*
249 		 * Verify that all the UIDs were retained across the fork(2)
250 		 * call.
251 		 */
252 		test_uids(set->ruid, set->euid, set->suid);
253 
254 		snprintf(param1, sizeof(param1), "%d", setnum);
255 
256 		(void)execl(executable, executable, "DO CHECK", "b", param1,
257 		    "", NULL);
258 
259 		e(setnum);
260 		break;
261 
262 	default:
263 		if (waitpid(pid, &status, 0) != pid) e(setnum);
264 		if (!WIFEXITED(status)) e(setnum);
265 		if (WEXITSTATUS(status) != 0) e(setnum);
266 	}
267 }
268 
269 /*
270  * The real, effective, and saved user IDs have been set up as indicated by the
271  * current set.  Test one particular case for test A or B, and verify the
272  * result.
273  */
274 static void
275 test_one_uid(int setnum, int sub)
276 {
277 	const struct uid_set *set;
278 	int res, exp;
279 
280 	set = &uid_sets[setnum];
281 
282 	/* Verify that the pre-call process state is as expected. */
283 	test_uids(set->ruid, set->euid, set->suid);
284 
285 	/* Perform the call, and check whether the result is as expected. */
286 	switch (sub) {
287 	case SUB_REAL:
288 		res = setuid(set->uid);
289 		exp = set->res - 1;
290 		break;
291 
292 	case SUB_EFF:
293 		res = seteuid(set->uid);
294 		exp = set->eres - 1;
295 		break;
296 
297 	case SUB_RETAIN:
298 		sub89b(setnum);
299 
300 		return;
301 	}
302 
303 	if (res != 0 && (res != -1 || errno != EPERM)) e(setnum);
304 
305 	if (res != exp) e(setnum);
306 
307 	/* Verify that the post-call process state is as expected as well. */
308 	if (res == 0) {
309 		if (sub == SUB_EFF)
310 			test_uids(set->ruid, set->uid, set->suid);
311 		else
312 			test_uids(set->uid, set->uid, set->uid);
313 	} else
314 		test_uids(set->ruid, set->euid, set->suid);
315 }
316 
317 /*
318  * Test setuid(2) or seteuid(2) after a successful execve(2) call, which should
319  * have set the process's effective and saved user ID.
320  */
321 static void
322 exec89a(const char * param1, const char * param2)
323 {
324 	const struct uid_set *set;
325 	int setnum, sub;
326 
327 	setnum = atoi(param1);
328 	if (setnum < 0 || setnum >= __arraycount(uid_sets)) {
329 		e(setnum);
330 		return;
331 	}
332 	set = &uid_sets[setnum];
333 
334 	sub = atoi(param2);
335 
336 	if (sub == SUB_RETAIN) {
337 		/* Clear the set-uid bit before dropping more privileges. */
338 		if (chmod(executable, S_IXUSR | S_IXGRP | S_IXOTH) != 0)
339 			e(setnum);
340 	}
341 
342 	/* Finish setting up the initial condition. */
343 	if (set->euid != set->suid) {
344 		if (set->euid != set->ruid && set->suid != 0) {
345 			test_uids(set->ruid, set->suid, set->suid);
346 
347 			return; /* skip test */
348 		}
349 
350 		if (seteuid(set->euid) != 0) e(setnum);
351 	}
352 
353 	/* Perform the actual test. */
354 	test_one_uid(setnum, sub);
355 }
356 
357 /*
358  * Test setuid(2) or seteuid(2) with a certain value starting from a certain
359  * initial condition, as identified by the given uid_sets[] array element.  As
360  * a side effect, test that in particular exec(2) properly sets the effective
361  * and saved user ID.
362  */
363 static void
364 sub89a(int setnum, int sub)
365 {
366 	const struct uid_set *set;
367 	char param1[32], param2[32];
368 
369 	set = &uid_sets[setnum];
370 
371 	/*
372 	 * Figure out how to set the real, effective, and saved UIDs to those
373 	 * of the set structure.  Without setresuid(2), not all combinations
374 	 * are possible to achieve.  We silently skip the tests for which we
375 	 * cannot create the requested initial condition.
376 	 */
377 	if (set->ruid != set->suid) {
378 		/*
379 		 * In order to set the saved UID to something other than the
380 		 * real UID, we must exec(2) a set-uid binary.
381 		 */
382 		if (chown(executable, set->suid, 0 /*anything*/) != 0) e(0);
383 		if (chmod(executable,
384 		    S_ISUID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
385 
386 		if (setuid(set->ruid) != 0) e(setnum);
387 
388 		snprintf(param1, sizeof(param1), "%d", setnum);
389 		snprintf(param2, sizeof(param2), "%d", sub);
390 
391 		(void)execl(executable, executable, "DO CHECK", "a", param1,
392 		    param2, NULL);
393 
394 		e(0);
395 	} else {
396 		/*
397 		 * If the real and saved user ID are to be set to the same
398 		 * value, we need not use exec(2).  Still, we cannot achieve
399 		 * all combinations here either.
400 		 */
401 		if (set->ruid != 0 && set->ruid != set->euid)
402 			return; /* skip test */
403 
404 		if (sub == SUB_RETAIN) {
405 			/* Clear the set-uid bit before dropping privileges. */
406 			if (chmod(executable,
407 			    S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(setnum);
408 		}
409 
410 		if (setuid(set->ruid) != 0) e(setnum);
411 		if (seteuid(set->euid) != 0) e(setnum);
412 
413 		/* Perform the actual test. */
414 		test_one_uid(setnum, sub);
415 	}
416 }
417 
418 /*
419  * Test setuid(2) and seteuid(2) calls with various initial conditions, by
420  * setting the real, effective, and saved UIDs to different values before
421  * performing the setuid(2) or seteuid(2) call.
422  */
423 static void
424 test89a(void)
425 {
426 	unsigned int setnum;
427 	int sub, status;
428 	pid_t pid;
429 
430 	subtest = 1;
431 
432 	for (setnum = 0; setnum < __arraycount(uid_sets); setnum++) {
433 		for (sub = SUB_REAL; sub <= SUB_EFF; sub++) {
434 			pid = fork();
435 
436 			switch (pid) {
437 			case -1:
438 				e(setnum);
439 
440 				break;
441 
442 			case 0:
443 				errct = 0;
444 
445 				sub89a((int)setnum, sub);
446 
447 				exit(errct);
448 				/* NOTREACHED */
449 
450 			default:
451 				if (waitpid(pid, &status, 0) != pid) e(setnum);
452 				if (!WIFEXITED(status)) e(setnum);
453 				if (WEXITSTATUS(status) != 0) e(setnum);
454 			}
455 		}
456 	}
457 }
458 
459 /*
460  * Ensure that the real, effective, and saved UIDs are fully preserved across
461  * fork(2) and non-setuid-binary exec(2) calls.
462  */
463 static void
464 test89b(void)
465 {
466 	unsigned int setnum;
467 	int status;
468 	pid_t pid;
469 
470 	subtest = 2;
471 
472 	for (setnum = 0; setnum < __arraycount(uid_sets); setnum++) {
473 		if (uid_sets[setnum].uid != 0)
474 			continue; /* no need to do the same test >1 times */
475 
476 		pid = fork();
477 
478 		switch (pid) {
479 		case -1:
480 			e(setnum);
481 
482 			break;
483 
484 		case 0:
485 			errct = 0;
486 
487 			/*
488 			 * Test B uses some of the A-test code.  While rather
489 			 * ugly, this avoids duplication of some of test A's
490 			 * important UID logic.
491 			 */
492 			sub89a((int)setnum, SUB_RETAIN);
493 
494 			exit(errct);
495 			/* NOTREACHED */
496 
497 		default:
498 			if (waitpid(pid, &status, 0) != pid) e(setnum);
499 			if (!WIFEXITED(status)) e(setnum);
500 			if (WEXITSTATUS(status) != 0) e(setnum);
501 		}
502 	}
503 }
504 
505 /*
506  * Verify that the current process's real, effective, and saved group IDs are
507  * set to the given respective value.
508  */
509 static void
510 test_gids(gid_t rgid, gid_t egid, gid_t sgid)
511 {
512 	struct kinfo_proc2 proc2;
513 
514 	if (getgid() != rgid) e(0);
515 	if (getegid() != egid) e(0);
516 
517 	/* As above. */
518 	if (get_proc2(getpid(), &proc2) != 0) e(0);
519 
520 	if (proc2.p_rgid != rgid) e(0);
521 	if (proc2.p_gid != egid) e(0);
522 	if (proc2.p_svgid != sgid) e(0);
523 }
524 
525 /*
526  * Verify that the real and effective group IDs are kept as is after an exec(2)
527  * call on a non-setgid binary, and that the saved group ID is set to the
528  * effective group ID.
529  */
530 static void
531 exec89d(const char * param1, const char * param2 __unused)
532 {
533 	const struct gid_set *set;
534 	int setnum;
535 
536 	setnum = atoi(param1);
537 	if (setnum < 0 || setnum >= __arraycount(gid_sets)) {
538 		e(setnum);
539 		return;
540 	}
541 	set = &gid_sets[setnum];
542 
543 	test_gids(set->rgid, set->egid, set->egid);
544 }
545 
546 /*
547  * The real, effective, and saved group IDs have been set up as indicated by
548  * the current set.  Verify that fork(2) and exec(2) do not change the real and
549  * effective GID, and that only exec(2) sets the saved GID to the effective
550  * GID.
551  */
552 static void
553 sub89d(int setnum)
554 {
555 	const struct gid_set *set;
556 	char param1[32];
557 	pid_t pid;
558 	int status;
559 
560 	set = &gid_sets[setnum];
561 
562 	pid = fork();
563 
564 	switch (pid) {
565 	case -1:
566 		e(setnum);
567 		break;
568 
569 	case 0:
570 		/*
571 		 * Verify that all the GIDs were retained across the fork(2)
572 		 * call.
573 		 */
574 		test_gids(set->rgid, set->egid, set->sgid);
575 
576 		/* Clear the set-gid bit. */
577 		if (chmod(executable, S_IXUSR | S_IXGRP | S_IXOTH) != 0)
578 			e(setnum);
579 
580 		/* Alternate between preserving and dropping user IDs. */
581 		if (set->gid != 0) {
582 			if (setuid(3) != 0) e(setnum);
583 		}
584 
585 		snprintf(param1, sizeof(param1), "%d", setnum);
586 
587 		(void)execl(executable, executable, "DO CHECK", "d", param1,
588 		    "", NULL);
589 
590 		e(setnum);
591 		break;
592 
593 	default:
594 		if (waitpid(pid, &status, 0) != pid) e(setnum);
595 		if (!WIFEXITED(status)) e(setnum);
596 		if (WEXITSTATUS(status) != 0) e(setnum);
597 	}
598 }
599 
600 /*
601  * The real, effective, and saved group IDs have been set up as indicated by
602  * the current set.  Test one particular case for test C or D, and verify the
603  * result.
604  */
605 static void
606 test_one_gid(int setnum, int sub)
607 {
608 	const struct gid_set *set;
609 	int res, exp;
610 
611 	set = &gid_sets[setnum];
612 
613 	/* Verify that the pre-call process state is as expected. */
614 	test_gids(set->rgid, set->egid, set->sgid);
615 
616 	/* Perform the call, and check whether the result is as expected. */
617 	switch (sub) {
618 	case SUB_REAL:
619 	case SUB_REAL_E0:
620 		if (sub != SUB_REAL_E0 && seteuid(1) != 0) e(0);
621 
622 		res = setgid(set->gid);
623 		exp = (sub != SUB_REAL_E0) ? (set->res - 1) : 0;
624 		break;
625 
626 	case SUB_EFF:
627 	case SUB_EFF_E0:
628 		if (sub != SUB_EFF_E0 && seteuid(1) != 0) e(0);
629 
630 		res = setegid(set->gid);
631 		exp = (sub != SUB_EFF_E0) ? (set->eres - 1) : 0;
632 		break;
633 
634 	case SUB_RETAIN:
635 		sub89d(setnum);
636 
637 		return;
638 	}
639 
640 	if (res != 0 && (res != -1 || errno != EPERM)) e(setnum);
641 
642 	if (res != exp) e(setnum);
643 
644 	/* Verify that the post-call process state is as expected as well. */
645 	if (res == 0) {
646 		if (sub == SUB_EFF || sub == SUB_EFF_E0)
647 			test_gids(set->rgid, set->gid, set->sgid);
648 		else
649 			test_gids(set->gid, set->gid, set->gid);
650 	} else
651 		test_gids(set->rgid, set->egid, set->sgid);
652 }
653 
654 /*
655  * Test setgid(2) or setegid(2) after a successful execve(2) call, which should
656  * have set the process's effective and saved group ID.
657  */
658 static void
659 exec89c(const char * param1, const char * param2)
660 {
661 	const struct gid_set *set;
662 	int setnum, sub;
663 
664 	setnum = atoi(param1);
665 	if (setnum < 0 || setnum >= __arraycount(gid_sets)) {
666 		e(setnum);
667 		return;
668 	}
669 	set = &gid_sets[setnum];
670 
671 	sub = atoi(param2);
672 
673 	/* Finish setting up the initial condition. */
674 	if (set->egid != set->sgid && setegid(set->egid) != 0) e(setnum);
675 
676 	/* Perform the actual test. */
677 	test_one_gid(setnum, sub);
678 }
679 
680 /*
681  * Test setgid(2) or setegid(2) with a certain value starting from a certain
682  * initial condition, as identified by the given gid_sets[] array element.  As
683  * a side effect, test that in particular exec(2) properly sets the effective
684  * and saved group ID.
685  */
686 static void
687 sub89c(int setnum, int sub)
688 {
689 	const struct gid_set *set;
690 	char param1[32], param2[32];
691 
692 	set = &gid_sets[setnum];
693 
694 	/*
695 	 * Figure out how to set the real, effective, and saved GIDs to those
696 	 * of the set structure.  In this case, all combinations are possible.
697 	 */
698 	if (set->rgid != set->sgid) {
699 		/*
700 		 * In order to set the saved GID to something other than the
701 		 * real GID, we must exec(2) a set-gid binary.
702 		 */
703 		if (chown(executable, 0 /*anything*/, set->sgid) != 0) e(0);
704 		if (chmod(executable,
705 		    S_ISGID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
706 
707 		if (setgid(set->rgid) != 0) e(setnum);
708 
709 		snprintf(param1, sizeof(param1), "%d", setnum);
710 		snprintf(param2, sizeof(param2), "%d", sub);
711 
712 		(void)execl(executable, executable, "DO CHECK", "c", param1,
713 		    param2, NULL);
714 
715 		e(0);
716 	} else {
717 		/*
718 		 * If the real and saved group ID are to be set to the same
719 		 * value, we need not use exec(2).
720 		 */
721 		if (setgid(set->rgid) != 0) e(setnum);
722 		if (setegid(set->egid) != 0) e(setnum);
723 
724 		/* Perform the actual test. */
725 		test_one_gid(setnum, sub);
726 	}
727 }
728 
729 /*
730  * Test setgid(2) and setegid(2) calls with various initial conditions, by
731  * setting the real, effective, and saved GIDs to different values before
732  * performing the setgid(2) or setegid(2) call.  At the same time, verify that
733  * if the caller has an effective UID of 0, all set(e)gid calls are allowed.
734  */
735 static void
736 test89c(void)
737 {
738 	unsigned int setnum;
739 	int sub, status;
740 	pid_t pid;
741 
742 	subtest = 3;
743 
744 	for (setnum = 0; setnum < __arraycount(gid_sets); setnum++) {
745 		for (sub = SUB_REAL; sub <= SUB_EFF_E0; sub++) {
746 			pid = fork();
747 
748 			switch (pid) {
749 			case -1:
750 				e(setnum);
751 
752 				break;
753 
754 			case 0:
755 				errct = 0;
756 
757 				sub89c((int)setnum, sub);
758 
759 				exit(errct);
760 				/* NOTREACHED */
761 
762 			default:
763 				if (waitpid(pid, &status, 0) != pid) e(setnum);
764 				if (!WIFEXITED(status)) e(setnum);
765 				if (WEXITSTATUS(status) != 0) e(setnum);
766 			}
767 		}
768 	}
769 }
770 
771 /*
772  * Ensure that the real, effective, and saved GIDs are fully preserved across
773  * fork(2) and non-setgid-binary exec(2) calls.
774  */
775 static void
776 test89d(void)
777 {
778 	unsigned int setnum;
779 	int status;
780 	pid_t pid;
781 
782 	subtest = 4;
783 
784 	for (setnum = 0; setnum < __arraycount(gid_sets); setnum++) {
785 		if (gid_sets[setnum].gid == 2)
786 			continue; /* no need to do the same test >1 times */
787 
788 		pid = fork();
789 
790 		switch (pid) {
791 		case -1:
792 			e(setnum);
793 
794 			break;
795 
796 		case 0:
797 			errct = 0;
798 
799 			/* Similarly, test D uses some of the C-test code. */
800 			sub89c((int)setnum, SUB_RETAIN);
801 
802 			exit(errct);
803 			/* NOTREACHED */
804 
805 		default:
806 			if (waitpid(pid, &status, 0) != pid) e(setnum);
807 			if (!WIFEXITED(status)) e(setnum);
808 			if (WEXITSTATUS(status) != 0) e(setnum);
809 		}
810 	}
811 }
812 
813 /*
814  * Either perform the second step of setting up user and group IDs, or check
815  * whether the user and/or group IDs have indeed been changed appropriately as
816  * the result of the second exec(2).
817  */
818 static void
819 exec89e(const char * param1, const char * param2)
820 {
821 	int mask, step;
822 	mode_t mode;
823 
824 	mask = atoi(param1);
825 	step = atoi(param2);
826 
827 	if (step == 0) {
828 		mode = S_IXUSR | S_IXGRP | S_IXOTH;
829 		if (mask & 1) mode |= S_ISUID;
830 		if (mask & 2) mode |= S_ISGID;
831 
832 		if (chown(executable, 6, 7) != 0) e(0);
833 		if (chmod(executable, mode) != 0) e(0);
834 
835 		if (setegid(4) != 0) e(0);
836 		if (seteuid(2) != 0) e(0);
837 
838 		test_uids(1, 2, 0);
839 		test_gids(3, 4, 5);
840 
841 		(void)execl(executable, executable, "DO CHECK", "e", param1,
842 		    "1", NULL);
843 
844 		e(0);
845 	} else {
846 		if (mask & 1)
847 			test_uids(1, 6, 6);
848 		else
849 			test_uids(1, 2, 2);
850 
851 		if (mask & 2)
852 			test_gids(3, 7, 7);
853 		else
854 			test_gids(3, 4, 4);
855 	}
856 }
857 
858 /*
859  * Set up for the set-uid/set-gid execution test by initializing to different
860  * real and effective user IDs.
861  */
862 static void
863 sub89e(int mask)
864 {
865 	char param1[32];
866 
867 	if (chown(executable, 0, 5) != 0) e(0);
868 	if (chmod(executable,
869 	    S_ISUID | S_ISGID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
870 
871 	if (setgid(3) != 0) e(0);
872 	if (setuid(1) != 0) e(0);
873 
874 	snprintf(param1, sizeof(param1), "%d", mask);
875 	(void)execl(executable, executable, "DO CHECK", "e", param1, "0",
876 	    NULL);
877 }
878 
879 /*
880  * Perform basic verification that the set-uid and set-gid bits on binaries are
881  * fully independent from each other.
882  */
883 static void
884 test89e(void)
885 {
886 	int mask, status;
887 	pid_t pid;
888 
889 	subtest = 5;
890 
891 	for (mask = 0; mask <= 3; mask++) {
892 		pid = fork();
893 
894 		switch (pid) {
895 		case -1:
896 			e(0);
897 
898 			break;
899 
900 		case 0:
901 			errct = 0;
902 
903 			sub89e(mask);
904 
905 			exit(errct);
906 			/* NOTREACHED */
907 
908 		default:
909 			if (waitpid(pid, &status, 0) != pid) e(mask);
910 			if (!WIFEXITED(status)) e(mask);
911 			if (WEXITSTATUS(status) != 0) e(mask);
912 		}
913 	}
914 }
915 
916 /*
917  * Call the right function after having executed myself.
918  */
919 static void
920 exec89(const char * param0, const char * param1, const char * param2)
921 {
922 
923 	switch (param0[0]) {
924 	case 'a':
925 		exec89a(param1, param2);
926 		break;
927 
928 	case 'b':
929 		exec89b(param1, param2);
930 		break;
931 
932 	case 'c':
933 		exec89c(param1, param2);
934 		break;
935 
936 	case 'd':
937 		exec89d(param1, param2);
938 		break;
939 
940 	case 'e':
941 		exec89e(param1, param2);
942 		break;
943 
944 	default:
945 		e(0);
946 	}
947 
948 	exit(errct);
949 }
950 
951 /*
952  * Initialize the test.
953  */
954 static void
955 test89_init(void)
956 {
957 	char cp_cmd[PATH_MAX + 9];
958 	int status;
959 
960 	subtest = 0;
961 
962 	/* Reset all user and group IDs to known values. */
963 	if (setuid(0) != 0) e(0);
964 	if (setgid(0) != 0) e(0);
965 	if (setgroups(0, NULL) != 0) e(0);
966 
967 	test_uids(0, 0, 0);
968 	test_gids(0, 0, 0);
969 
970 	/* Make a copy of the binary, which as of start() is one level up. */
971 	snprintf(cp_cmd, sizeof(cp_cmd), "cp ../%s .", executable);
972 
973 	status = system(cp_cmd);
974 	if (status < 0 || !WIFEXITED(status) ||
975 	    WEXITSTATUS(status) != EXIT_SUCCESS) e(0);
976 }
977 
978 /*
979  * Test program for set[ug]id, sete[ug]id, and saved IDs.
980  */
981 int
982 main(int argc, char ** argv)
983 {
984 	int i, m;
985 
986 	executable = argv[0];
987 
988 	/* This test executes itself.  Handle that case first. */
989 	if (argc == 5 && !strcmp(argv[1], "DO CHECK"))
990 		exec89(argv[2], argv[3], argv[4]);
991 
992 	start(89);
993 
994 	test89_init();
995 
996 	if (argc == 2)
997 		m = atoi(argv[1]);
998 	else
999 		m = 0xFF;
1000 
1001 	for (i = 0; i < ITERATIONS; i++) {
1002 		if (m & 0x01) test89a();
1003 		if (m & 0x02) test89b();
1004 		if (m & 0x04) test89c();
1005 		if (m & 0x08) test89d();
1006 		if (m & 0x10) test89e();
1007 	}
1008 
1009 	quit();
1010 	/* NOTREACHED */
1011 }
1012