xref: /minix/minix/tests/test89.c (revision 00e393ca)
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 	default:
303 		abort();
304 	}
305 
306 	if (res != 0 && (res != -1 || errno != EPERM)) e(setnum);
307 
308 	if (res != exp) e(setnum);
309 
310 	/* Verify that the post-call process state is as expected as well. */
311 	if (res == 0) {
312 		if (sub == SUB_EFF)
313 			test_uids(set->ruid, set->uid, set->suid);
314 		else
315 			test_uids(set->uid, set->uid, set->uid);
316 	} else
317 		test_uids(set->ruid, set->euid, set->suid);
318 }
319 
320 /*
321  * Test setuid(2) or seteuid(2) after a successful execve(2) call, which should
322  * have set the process's effective and saved user ID.
323  */
324 static void
325 exec89a(const char * param1, const char * param2)
326 {
327 	const struct uid_set *set;
328 	int setnum, sub;
329 
330 	setnum = atoi(param1);
331 	if (setnum < 0 || setnum >= __arraycount(uid_sets)) {
332 		e(setnum);
333 		return;
334 	}
335 	set = &uid_sets[setnum];
336 
337 	sub = atoi(param2);
338 
339 	if (sub == SUB_RETAIN) {
340 		/* Clear the set-uid bit before dropping more privileges. */
341 		if (chmod(executable, S_IXUSR | S_IXGRP | S_IXOTH) != 0)
342 			e(setnum);
343 	}
344 
345 	/* Finish setting up the initial condition. */
346 	if (set->euid != set->suid) {
347 		if (set->euid != set->ruid && set->suid != 0) {
348 			test_uids(set->ruid, set->suid, set->suid);
349 
350 			return; /* skip test */
351 		}
352 
353 		if (seteuid(set->euid) != 0) e(setnum);
354 	}
355 
356 	/* Perform the actual test. */
357 	test_one_uid(setnum, sub);
358 }
359 
360 /*
361  * Test setuid(2) or seteuid(2) with a certain value starting from a certain
362  * initial condition, as identified by the given uid_sets[] array element.  As
363  * a side effect, test that in particular exec(2) properly sets the effective
364  * and saved user ID.
365  */
366 static void
367 sub89a(int setnum, int sub)
368 {
369 	const struct uid_set *set;
370 	char param1[32], param2[32];
371 
372 	set = &uid_sets[setnum];
373 
374 	/*
375 	 * Figure out how to set the real, effective, and saved UIDs to those
376 	 * of the set structure.  Without setresuid(2), not all combinations
377 	 * are possible to achieve.  We silently skip the tests for which we
378 	 * cannot create the requested initial condition.
379 	 */
380 	if (set->ruid != set->suid) {
381 		/*
382 		 * In order to set the saved UID to something other than the
383 		 * real UID, we must exec(2) a set-uid binary.
384 		 */
385 		if (chown(executable, set->suid, 0 /*anything*/) != 0) e(0);
386 		if (chmod(executable,
387 		    S_ISUID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
388 
389 		if (setuid(set->ruid) != 0) e(setnum);
390 
391 		snprintf(param1, sizeof(param1), "%d", setnum);
392 		snprintf(param2, sizeof(param2), "%d", sub);
393 
394 		(void)execl(executable, executable, "DO CHECK", "a", param1,
395 		    param2, NULL);
396 
397 		e(0);
398 	} else {
399 		/*
400 		 * If the real and saved user ID are to be set to the same
401 		 * value, we need not use exec(2).  Still, we cannot achieve
402 		 * all combinations here either.
403 		 */
404 		if (set->ruid != 0 && set->ruid != set->euid)
405 			return; /* skip test */
406 
407 		if (sub == SUB_RETAIN) {
408 			/* Clear the set-uid bit before dropping privileges. */
409 			if (chmod(executable,
410 			    S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(setnum);
411 		}
412 
413 		if (setuid(set->ruid) != 0) e(setnum);
414 		if (seteuid(set->euid) != 0) e(setnum);
415 
416 		/* Perform the actual test. */
417 		test_one_uid(setnum, sub);
418 	}
419 }
420 
421 /*
422  * Test setuid(2) and seteuid(2) calls with various initial conditions, by
423  * setting the real, effective, and saved UIDs to different values before
424  * performing the setuid(2) or seteuid(2) call.
425  */
426 static void
427 test89a(void)
428 {
429 	unsigned int setnum;
430 	int sub, status;
431 	pid_t pid;
432 
433 	subtest = 1;
434 
435 	for (setnum = 0; setnum < __arraycount(uid_sets); setnum++) {
436 		for (sub = SUB_REAL; sub <= SUB_EFF; sub++) {
437 			pid = fork();
438 
439 			switch (pid) {
440 			case -1:
441 				e(setnum);
442 
443 				break;
444 
445 			case 0:
446 				errct = 0;
447 
448 				sub89a((int)setnum, sub);
449 
450 				exit(errct);
451 				/* NOTREACHED */
452 
453 			default:
454 				if (waitpid(pid, &status, 0) != pid) e(setnum);
455 				if (!WIFEXITED(status)) e(setnum);
456 				if (WEXITSTATUS(status) != 0) e(setnum);
457 			}
458 		}
459 	}
460 }
461 
462 /*
463  * Ensure that the real, effective, and saved UIDs are fully preserved across
464  * fork(2) and non-setuid-binary exec(2) calls.
465  */
466 static void
467 test89b(void)
468 {
469 	unsigned int setnum;
470 	int status;
471 	pid_t pid;
472 
473 	subtest = 2;
474 
475 	for (setnum = 0; setnum < __arraycount(uid_sets); setnum++) {
476 		if (uid_sets[setnum].uid != 0)
477 			continue; /* no need to do the same test >1 times */
478 
479 		pid = fork();
480 
481 		switch (pid) {
482 		case -1:
483 			e(setnum);
484 
485 			break;
486 
487 		case 0:
488 			errct = 0;
489 
490 			/*
491 			 * Test B uses some of the A-test code.  While rather
492 			 * ugly, this avoids duplication of some of test A's
493 			 * important UID logic.
494 			 */
495 			sub89a((int)setnum, SUB_RETAIN);
496 
497 			exit(errct);
498 			/* NOTREACHED */
499 
500 		default:
501 			if (waitpid(pid, &status, 0) != pid) e(setnum);
502 			if (!WIFEXITED(status)) e(setnum);
503 			if (WEXITSTATUS(status) != 0) e(setnum);
504 		}
505 	}
506 }
507 
508 /*
509  * Verify that the current process's real, effective, and saved group IDs are
510  * set to the given respective value.
511  */
512 static void
513 test_gids(gid_t rgid, gid_t egid, gid_t sgid)
514 {
515 	struct kinfo_proc2 proc2;
516 
517 	if (getgid() != rgid) e(0);
518 	if (getegid() != egid) e(0);
519 
520 	/* As above. */
521 	if (get_proc2(getpid(), &proc2) != 0) e(0);
522 
523 	if (proc2.p_rgid != rgid) e(0);
524 	if (proc2.p_gid != egid) e(0);
525 	if (proc2.p_svgid != sgid) e(0);
526 }
527 
528 /*
529  * Verify that the real and effective group IDs are kept as is after an exec(2)
530  * call on a non-setgid binary, and that the saved group ID is set to the
531  * effective group ID.
532  */
533 static void
534 exec89d(const char * param1, const char * param2 __unused)
535 {
536 	const struct gid_set *set;
537 	int setnum;
538 
539 	setnum = atoi(param1);
540 	if (setnum < 0 || setnum >= __arraycount(gid_sets)) {
541 		e(setnum);
542 		return;
543 	}
544 	set = &gid_sets[setnum];
545 
546 	test_gids(set->rgid, set->egid, set->egid);
547 }
548 
549 /*
550  * The real, effective, and saved group IDs have been set up as indicated by
551  * the current set.  Verify that fork(2) and exec(2) do not change the real and
552  * effective GID, and that only exec(2) sets the saved GID to the effective
553  * GID.
554  */
555 static void
556 sub89d(int setnum)
557 {
558 	const struct gid_set *set;
559 	char param1[32];
560 	pid_t pid;
561 	int status;
562 
563 	set = &gid_sets[setnum];
564 
565 	pid = fork();
566 
567 	switch (pid) {
568 	case -1:
569 		e(setnum);
570 		break;
571 
572 	case 0:
573 		/*
574 		 * Verify that all the GIDs were retained across the fork(2)
575 		 * call.
576 		 */
577 		test_gids(set->rgid, set->egid, set->sgid);
578 
579 		/* Clear the set-gid bit. */
580 		if (chmod(executable, S_IXUSR | S_IXGRP | S_IXOTH) != 0)
581 			e(setnum);
582 
583 		/* Alternate between preserving and dropping user IDs. */
584 		if (set->gid != 0) {
585 			if (setuid(3) != 0) e(setnum);
586 		}
587 
588 		snprintf(param1, sizeof(param1), "%d", setnum);
589 
590 		(void)execl(executable, executable, "DO CHECK", "d", param1,
591 		    "", NULL);
592 
593 		e(setnum);
594 		break;
595 
596 	default:
597 		if (waitpid(pid, &status, 0) != pid) e(setnum);
598 		if (!WIFEXITED(status)) e(setnum);
599 		if (WEXITSTATUS(status) != 0) e(setnum);
600 	}
601 }
602 
603 /*
604  * The real, effective, and saved group IDs have been set up as indicated by
605  * the current set.  Test one particular case for test C or D, and verify the
606  * result.
607  */
608 static void
609 test_one_gid(int setnum, int sub)
610 {
611 	const struct gid_set *set;
612 	int res, exp;
613 
614 	set = &gid_sets[setnum];
615 
616 	/* Verify that the pre-call process state is as expected. */
617 	test_gids(set->rgid, set->egid, set->sgid);
618 
619 	/* Perform the call, and check whether the result is as expected. */
620 	switch (sub) {
621 	case SUB_REAL:
622 	case SUB_REAL_E0:
623 		if (sub != SUB_REAL_E0 && seteuid(1) != 0) e(0);
624 
625 		res = setgid(set->gid);
626 		exp = (sub != SUB_REAL_E0) ? (set->res - 1) : 0;
627 		break;
628 
629 	case SUB_EFF:
630 	case SUB_EFF_E0:
631 		if (sub != SUB_EFF_E0 && seteuid(1) != 0) e(0);
632 
633 		res = setegid(set->gid);
634 		exp = (sub != SUB_EFF_E0) ? (set->eres - 1) : 0;
635 		break;
636 
637 	case SUB_RETAIN:
638 		sub89d(setnum);
639 
640 		return;
641 
642 	default:
643 		abort();
644 	}
645 
646 	if (res != 0 && (res != -1 || errno != EPERM)) e(setnum);
647 
648 	if (res != exp) e(setnum);
649 
650 	/* Verify that the post-call process state is as expected as well. */
651 	if (res == 0) {
652 		if (sub == SUB_EFF || sub == SUB_EFF_E0)
653 			test_gids(set->rgid, set->gid, set->sgid);
654 		else
655 			test_gids(set->gid, set->gid, set->gid);
656 	} else
657 		test_gids(set->rgid, set->egid, set->sgid);
658 }
659 
660 /*
661  * Test setgid(2) or setegid(2) after a successful execve(2) call, which should
662  * have set the process's effective and saved group ID.
663  */
664 static void
665 exec89c(const char * param1, const char * param2)
666 {
667 	const struct gid_set *set;
668 	int setnum, sub;
669 
670 	setnum = atoi(param1);
671 	if (setnum < 0 || setnum >= __arraycount(gid_sets)) {
672 		e(setnum);
673 		return;
674 	}
675 	set = &gid_sets[setnum];
676 
677 	sub = atoi(param2);
678 
679 	/* Finish setting up the initial condition. */
680 	if (set->egid != set->sgid && setegid(set->egid) != 0) e(setnum);
681 
682 	/* Perform the actual test. */
683 	test_one_gid(setnum, sub);
684 }
685 
686 /*
687  * Test setgid(2) or setegid(2) with a certain value starting from a certain
688  * initial condition, as identified by the given gid_sets[] array element.  As
689  * a side effect, test that in particular exec(2) properly sets the effective
690  * and saved group ID.
691  */
692 static void
693 sub89c(int setnum, int sub)
694 {
695 	const struct gid_set *set;
696 	char param1[32], param2[32];
697 
698 	set = &gid_sets[setnum];
699 
700 	/*
701 	 * Figure out how to set the real, effective, and saved GIDs to those
702 	 * of the set structure.  In this case, all combinations are possible.
703 	 */
704 	if (set->rgid != set->sgid) {
705 		/*
706 		 * In order to set the saved GID to something other than the
707 		 * real GID, we must exec(2) a set-gid binary.
708 		 */
709 		if (chown(executable, 0 /*anything*/, set->sgid) != 0) e(0);
710 		if (chmod(executable,
711 		    S_ISGID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
712 
713 		if (setgid(set->rgid) != 0) e(setnum);
714 
715 		snprintf(param1, sizeof(param1), "%d", setnum);
716 		snprintf(param2, sizeof(param2), "%d", sub);
717 
718 		(void)execl(executable, executable, "DO CHECK", "c", param1,
719 		    param2, NULL);
720 
721 		e(0);
722 	} else {
723 		/*
724 		 * If the real and saved group ID are to be set to the same
725 		 * value, we need not use exec(2).
726 		 */
727 		if (setgid(set->rgid) != 0) e(setnum);
728 		if (setegid(set->egid) != 0) e(setnum);
729 
730 		/* Perform the actual test. */
731 		test_one_gid(setnum, sub);
732 	}
733 }
734 
735 /*
736  * Test setgid(2) and setegid(2) calls with various initial conditions, by
737  * setting the real, effective, and saved GIDs to different values before
738  * performing the setgid(2) or setegid(2) call.  At the same time, verify that
739  * if the caller has an effective UID of 0, all set(e)gid calls are allowed.
740  */
741 static void
742 test89c(void)
743 {
744 	unsigned int setnum;
745 	int sub, status;
746 	pid_t pid;
747 
748 	subtest = 3;
749 
750 	for (setnum = 0; setnum < __arraycount(gid_sets); setnum++) {
751 		for (sub = SUB_REAL; sub <= SUB_EFF_E0; sub++) {
752 			pid = fork();
753 
754 			switch (pid) {
755 			case -1:
756 				e(setnum);
757 
758 				break;
759 
760 			case 0:
761 				errct = 0;
762 
763 				sub89c((int)setnum, sub);
764 
765 				exit(errct);
766 				/* NOTREACHED */
767 
768 			default:
769 				if (waitpid(pid, &status, 0) != pid) e(setnum);
770 				if (!WIFEXITED(status)) e(setnum);
771 				if (WEXITSTATUS(status) != 0) e(setnum);
772 			}
773 		}
774 	}
775 }
776 
777 /*
778  * Ensure that the real, effective, and saved GIDs are fully preserved across
779  * fork(2) and non-setgid-binary exec(2) calls.
780  */
781 static void
782 test89d(void)
783 {
784 	unsigned int setnum;
785 	int status;
786 	pid_t pid;
787 
788 	subtest = 4;
789 
790 	for (setnum = 0; setnum < __arraycount(gid_sets); setnum++) {
791 		if (gid_sets[setnum].gid == 2)
792 			continue; /* no need to do the same test >1 times */
793 
794 		pid = fork();
795 
796 		switch (pid) {
797 		case -1:
798 			e(setnum);
799 
800 			break;
801 
802 		case 0:
803 			errct = 0;
804 
805 			/* Similarly, test D uses some of the C-test code. */
806 			sub89c((int)setnum, SUB_RETAIN);
807 
808 			exit(errct);
809 			/* NOTREACHED */
810 
811 		default:
812 			if (waitpid(pid, &status, 0) != pid) e(setnum);
813 			if (!WIFEXITED(status)) e(setnum);
814 			if (WEXITSTATUS(status) != 0) e(setnum);
815 		}
816 	}
817 }
818 
819 /*
820  * Either perform the second step of setting up user and group IDs, or check
821  * whether the user and/or group IDs have indeed been changed appropriately as
822  * the result of the second exec(2).
823  */
824 static void
825 exec89e(const char * param1, const char * param2)
826 {
827 	int mask, step;
828 	mode_t mode;
829 
830 	mask = atoi(param1);
831 	step = atoi(param2);
832 
833 	if (step == 0) {
834 		mode = S_IXUSR | S_IXGRP | S_IXOTH;
835 		if (mask & 1) mode |= S_ISUID;
836 		if (mask & 2) mode |= S_ISGID;
837 
838 		if (chown(executable, 6, 7) != 0) e(0);
839 		if (chmod(executable, mode) != 0) e(0);
840 
841 		if (setegid(4) != 0) e(0);
842 		if (seteuid(2) != 0) e(0);
843 
844 		test_uids(1, 2, 0);
845 		test_gids(3, 4, 5);
846 
847 		(void)execl(executable, executable, "DO CHECK", "e", param1,
848 		    "1", NULL);
849 
850 		e(0);
851 	} else {
852 		if (mask & 1)
853 			test_uids(1, 6, 6);
854 		else
855 			test_uids(1, 2, 2);
856 
857 		if (mask & 2)
858 			test_gids(3, 7, 7);
859 		else
860 			test_gids(3, 4, 4);
861 	}
862 }
863 
864 /*
865  * Set up for the set-uid/set-gid execution test by initializing to different
866  * real and effective user IDs.
867  */
868 static void
869 sub89e(int mask)
870 {
871 	char param1[32];
872 
873 	if (chown(executable, 0, 5) != 0) e(0);
874 	if (chmod(executable,
875 	    S_ISUID | S_ISGID | S_IXUSR | S_IXGRP | S_IXOTH) != 0) e(0);
876 
877 	if (setgid(3) != 0) e(0);
878 	if (setuid(1) != 0) e(0);
879 
880 	snprintf(param1, sizeof(param1), "%d", mask);
881 	(void)execl(executable, executable, "DO CHECK", "e", param1, "0",
882 	    NULL);
883 }
884 
885 /*
886  * Perform basic verification that the set-uid and set-gid bits on binaries are
887  * fully independent from each other.
888  */
889 static void
890 test89e(void)
891 {
892 	int mask, status;
893 	pid_t pid;
894 
895 	subtest = 5;
896 
897 	for (mask = 0; mask <= 3; mask++) {
898 		pid = fork();
899 
900 		switch (pid) {
901 		case -1:
902 			e(0);
903 
904 			break;
905 
906 		case 0:
907 			errct = 0;
908 
909 			sub89e(mask);
910 
911 			exit(errct);
912 			/* NOTREACHED */
913 
914 		default:
915 			if (waitpid(pid, &status, 0) != pid) e(mask);
916 			if (!WIFEXITED(status)) e(mask);
917 			if (WEXITSTATUS(status) != 0) e(mask);
918 		}
919 	}
920 }
921 
922 /*
923  * Call the right function after having executed myself.
924  */
925 static void
926 exec89(const char * param0, const char * param1, const char * param2)
927 {
928 
929 	switch (param0[0]) {
930 	case 'a':
931 		exec89a(param1, param2);
932 		break;
933 
934 	case 'b':
935 		exec89b(param1, param2);
936 		break;
937 
938 	case 'c':
939 		exec89c(param1, param2);
940 		break;
941 
942 	case 'd':
943 		exec89d(param1, param2);
944 		break;
945 
946 	case 'e':
947 		exec89e(param1, param2);
948 		break;
949 
950 	default:
951 		e(0);
952 	}
953 
954 	exit(errct);
955 }
956 
957 /*
958  * Initialize the test.
959  */
960 static void
961 test89_init(void)
962 {
963 	char cp_cmd[PATH_MAX + 9];
964 	int status;
965 
966 	subtest = 0;
967 
968 	/* Reset all user and group IDs to known values. */
969 	if (setuid(0) != 0) e(0);
970 	if (setgid(0) != 0) e(0);
971 	if (setgroups(0, NULL) != 0) e(0);
972 
973 	test_uids(0, 0, 0);
974 	test_gids(0, 0, 0);
975 
976 	/* Make a copy of the binary, which as of start() is one level up. */
977 	snprintf(cp_cmd, sizeof(cp_cmd), "cp ../%s .", executable);
978 
979 	status = system(cp_cmd);
980 	if (status < 0 || !WIFEXITED(status) ||
981 	    WEXITSTATUS(status) != EXIT_SUCCESS) e(0);
982 }
983 
984 /*
985  * Test program for set[ug]id, sete[ug]id, and saved IDs.
986  */
987 int
988 main(int argc, char ** argv)
989 {
990 	int i, m;
991 
992 	executable = argv[0];
993 
994 	/* This test executes itself.  Handle that case first. */
995 	if (argc == 5 && !strcmp(argv[1], "DO CHECK"))
996 		exec89(argv[2], argv[3], argv[4]);
997 
998 	start(89);
999 
1000 	test89_init();
1001 
1002 	if (argc == 2)
1003 		m = atoi(argv[1]);
1004 	else
1005 		m = 0xFF;
1006 
1007 	for (i = 0; i < ITERATIONS; i++) {
1008 		if (m & 0x01) test89a();
1009 		if (m & 0x02) test89b();
1010 		if (m & 0x04) test89c();
1011 		if (m & 0x08) test89d();
1012 		if (m & 0x10) test89e();
1013 	}
1014 
1015 	quit();
1016 	/* NOTREACHED */
1017 }
1018