1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2002
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 * Description:
22 *	Verifies that the group ID and setgid bit are
23 *	set correctly when a new file is created using open.
24 *
25 * ALGORITHM
26 *	Create two directories, one with the group ID of this process
27 *	and the setgid bit not set, and the other with a group ID
28 *	other than that of this process and with the setgid bit set.
29 *	In each directory, create a file with and without the setgid
30 *	bit set in the creation modes. Verify that the modes and group
31 *	ID are correct on each of the 4 files.
32 *	As root, create a file with the setgid bit on in the
33 *	directory with the setgid bit.
34 *	This tests the SVID3 create group semantics.
35 */
36
37#include <stdio.h>
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <errno.h>
42#include <grp.h>
43#include <pwd.h>
44#include "test.h"
45
46char *TCID = "open10";
47int TST_TOTAL = 1;
48static int local_flag;
49
50#define PASSED 1
51#define FAILED 0
52
53#define MODE_RWX        (S_IRWXU | S_IRWXG | S_IRWXO)
54#define MODE_SGID       (S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO)
55#define DIR_A_TEMP	"open10.testdir.A.%d"
56#define DIR_B_TEMP	"open10.testdir.B.%d"
57#define SETGID		"setgid"
58#define NOSETGID	"nosetgid"
59#define ROOT_SETGID	"root_setgid"
60#define	MSGSIZE		150
61
62static void setup(void);
63static void cleanup(void);
64
65int main(int ac, char *av[])
66{
67	int ret;
68	struct stat buf;
69	struct group *group;
70	struct passwd *user1;
71	char DIR_A[MSGSIZE], DIR_B[MSGSIZE];
72	char setgid_A[MSGSIZE], nosetgid_A[MSGSIZE];
73	char setgid_B[MSGSIZE], nosetgid_B[MSGSIZE], root_setgid_B[MSGSIZE];
74	gid_t group1_gid, group2_gid, mygid;
75	uid_t save_myuid, user1_uid;
76	pid_t mypid;
77
78	int lc;
79	int fail_count = 0;
80
81	tst_parse_opts(ac, av, NULL, NULL);
82
83	setup();
84
85	for (lc = 0; TEST_LOOPING(lc); lc++) {
86		local_flag = PASSED;
87
88		save_myuid = getuid();
89		mypid = getpid();
90		sprintf(DIR_A, DIR_A_TEMP, mypid);
91		sprintf(DIR_B, DIR_B_TEMP, mypid);
92		sprintf(setgid_A, "%s/%s", DIR_A, SETGID);
93		sprintf(nosetgid_A, "%s/%s", DIR_A, NOSETGID);
94		sprintf(setgid_B, "%s/%s", DIR_B, SETGID);
95		sprintf(nosetgid_B, "%s/%s", DIR_B, NOSETGID);
96		sprintf(root_setgid_B, "%s/%s", DIR_B, ROOT_SETGID);
97
98		/* Get the uid of user1 */
99		user1 = getpwnam("nobody");
100		if (user1 == NULL)
101			tst_brkm(TBROK, cleanup, "nobody not in /etc/passwd");
102
103		user1_uid = user1->pw_uid;
104
105		/*
106		 * Get the group IDs of group1 and group2.
107		 */
108		group = getgrnam("nobody");
109		if (group == NULL) {
110			group = getgrnam("nogroup");
111			if (group == NULL) {
112				tst_brkm(TBROK, cleanup,
113					 "nobody/nogroup not in /etc/group");
114			}
115		}
116		group1_gid = group->gr_gid;
117#ifdef ANDROID
118		group = getgrnam("everybody");
119		if (group == NULL)
120			tst_brkm(TBROK, cleanup, "everybody not in /etc/group");
121#else
122		group = getgrnam("bin");
123		if (group == NULL)
124			tst_brkm(TBROK, cleanup, "bin not in /etc/group");
125#endif
126
127		group2_gid = group->gr_gid;
128
129		/*
130		 * Create a directory with group id the same as this process
131		 * and with no setgid bit.
132		 */
133		if (mkdir(DIR_A, MODE_RWX) < 0) {
134			tst_resm(TFAIL | TERRNO, "mkdir(%s) failed", DIR_A);
135			local_flag = FAILED;
136		}
137
138		if (chown(DIR_A, user1_uid, group2_gid) < 0) {
139			tst_resm(TFAIL | TERRNO, "chown(%s) failed", DIR_A);
140			local_flag = FAILED;
141		}
142
143		if (stat(DIR_A, &buf) < 0) {
144			tst_resm(TFAIL | TERRNO, "stat(%s) failed", DIR_A);
145			local_flag = FAILED;
146		}
147
148		/* Verify modes */
149		if (buf.st_mode & S_ISGID) {
150			tst_resm(TFAIL, "%s: Incorrect modes, setgid bit set",
151				 DIR_A);
152			local_flag = FAILED;
153		}
154
155		/* Verify group ID */
156		if (buf.st_gid != group2_gid) {
157			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
158				 DIR_A, buf.st_gid, group2_gid);
159			local_flag = FAILED;
160		}
161
162		/*
163		 * Create a directory with group id different from that of
164		 * this process and with the setgid bit set.
165		 */
166		if (mkdir(DIR_B, MODE_RWX) < 0) {
167			tst_resm(TFAIL | TERRNO, "mkdir(%s) failed", DIR_B);
168			local_flag = FAILED;
169		}
170
171		if (chown(DIR_B, user1_uid, group2_gid) < 0) {
172			tst_resm(TFAIL | TERRNO, "chown(%s) failed", DIR_B);
173			local_flag = FAILED;
174		}
175
176		if (chmod(DIR_B, MODE_SGID) < 0) {
177			tst_resm(TFAIL | TERRNO, "chmod(%s) failed", DIR_B);
178			local_flag = FAILED;
179		}
180
181		if (stat(DIR_B, &buf) < 0) {
182			tst_resm(TFAIL | TERRNO, "stat(%s) failed", DIR_B);
183			local_flag = FAILED;
184		}
185
186		/* Verify modes */
187		if (!(buf.st_mode & S_ISGID)) {
188			tst_resm(TFAIL,
189				 "%s: Incorrect modes, setgid bit not set",
190				 DIR_B);
191			local_flag = FAILED;
192		}
193
194		/* Verify group ID */
195		if (buf.st_gid != group2_gid) {
196			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
197				 DIR_B, buf.st_gid, group2_gid);
198			local_flag = FAILED;
199		}
200
201		if (local_flag == PASSED) {
202			tst_resm(TPASS, "Test passed in block0.");
203		} else {
204			tst_resm(TFAIL, "Test failed in block0.");
205			fail_count++;
206		}
207
208		local_flag = PASSED;
209
210		/*
211		 * Create two files in testdir.A, one with the setgid
212		 * bit set in the creation modes and the other without.
213		 * Both should inherit the group ID of the process and
214		 * maintain the setgid bit as specified in the creation
215		 * modes.
216		 */
217		if (setgid(group1_gid) < 0) {
218			tst_resm(TINFO,
219				 "Unable to set process group ID to group1");
220		}
221
222		if (setreuid(-1, user1_uid) < 0)
223			tst_resm(TINFO, "Unable to set process uid to user1");
224
225		mygid = getgid();
226
227		/*
228		 * Create the file with setgid not set
229		 */
230		ret = open(nosetgid_A, O_CREAT | O_EXCL | O_RDWR, MODE_RWX);
231		if (ret < 0) {
232			tst_resm(TFAIL | TERRNO, "open(%s) failed", nosetgid_A);
233			local_flag = FAILED;
234		}
235		close(ret);
236
237		if (stat(nosetgid_A, &buf) < 0) {
238			tst_resm(TFAIL | TERRNO, "stat(%s) failed", nosetgid_A);
239			local_flag = FAILED;
240		}
241
242		/* Verify modes */
243		if (buf.st_mode & S_ISGID) {
244			tst_resm(TFAIL, "%s: Incorrect modes, setgid bit set",
245				 nosetgid_A);
246			local_flag = FAILED;
247		}
248
249		/* Verify group ID */
250		if (buf.st_gid != mygid) {
251			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
252				 nosetgid_A, buf.st_gid, mygid);
253			local_flag = FAILED;
254		}
255
256		/*
257		 * Create the file with setgid set
258		 */
259		ret = open(setgid_A, O_CREAT | O_EXCL | O_RDWR, MODE_SGID);
260		if (ret < 0) {
261			tst_resm(TFAIL | TERRNO, "open(%s) failed", setgid_A);
262			local_flag = FAILED;
263		}
264		close(ret);
265
266		if (stat(setgid_A, &buf) < 0) {
267			tst_resm(TFAIL | TERRNO, "stat(%s) failed", setgid_A);
268			local_flag = FAILED;
269		}
270
271		/* Verify modes */
272		if (!(buf.st_mode & S_ISGID)) {
273			tst_resm(TFAIL,
274				 "%s: Incorrect modes, setgid bit not set",
275				 setgid_A);
276			local_flag = FAILED;
277		}
278
279		/* Verify group ID */
280		if (buf.st_gid != mygid) {
281			tst_resm(TFAIL, "%s: Incorrect group (%d and %d)",
282				 setgid_A, buf.st_gid, mygid);
283			local_flag = FAILED;
284		}
285
286		if (local_flag == PASSED) {
287			tst_resm(TPASS, "Test passed in block1.");
288		} else {
289			tst_resm(TFAIL, "Test failed in block1.");
290			fail_count++;
291		}
292
293		local_flag = PASSED;
294
295		/*
296		 * Create two files in testdir.B, one with the setgid
297		 * bit set in the creation modes and the other without.
298		 * Both should inherit the group ID of the parent
299		 * directory, group2. Either file should have the segid
300		 * bit set in the modes.
301		 */
302		/*
303		 * Create the file with setgid not set
304		 */
305		ret = open(nosetgid_B, O_CREAT | O_EXCL | O_RDWR, MODE_RWX);
306		if (ret < 0) {
307			tst_resm(TFAIL | TERRNO, "open(%s) failed", nosetgid_B);
308			local_flag = FAILED;
309		}
310		close(ret);
311
312		if (stat(nosetgid_B, &buf) < 0) {
313			tst_resm(TFAIL | TERRNO, "stat(%s) failed", nosetgid_B);
314			local_flag = FAILED;
315		}
316
317		/* Verify modes */
318		if (buf.st_mode & S_ISGID) {
319			tst_resm(TFAIL,
320				 "%s: Incorrect modes, setgid bit should be set",
321				 nosetgid_B);
322			local_flag = FAILED;
323		}
324
325		/* Verify group ID */
326		if (buf.st_gid != group2_gid) {
327			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
328				 nosetgid_B, buf.st_gid, group2_gid);
329			local_flag = FAILED;
330		}
331
332		/*
333		 * Create the file with setgid set
334		 */
335		ret = open(setgid_B, O_CREAT | O_EXCL | O_RDWR, MODE_SGID);
336		if (ret < 0) {
337			tst_resm(TFAIL | TERRNO, "open(%s) failed", setgid_B);
338			local_flag = FAILED;
339		}
340		close(ret);
341
342		if (stat(setgid_B, &buf) < 0) {
343			tst_resm(TFAIL | TERRNO, "stat(%s) failed", setgid_B);
344			local_flag = FAILED;
345		}
346
347		/* Verify group ID */
348		if (buf.st_gid != group2_gid) {
349			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
350				 setgid_B, buf.st_gid, group2_gid);
351			local_flag = FAILED;
352		}
353
354		/* Verify modes */
355		if (!(buf.st_mode & S_ISGID)) {
356			tst_resm(TFAIL,
357				 "%s: Incorrect modes, setgid bit not set",
358				 setgid_B);
359			local_flag = FAILED;
360		}
361
362		if (local_flag == PASSED) {
363			tst_resm(TPASS, "Test passed in block2.");
364		} else {
365			tst_resm(TFAIL, "Test failed in block2.");
366			fail_count++;
367		}
368
369		local_flag = PASSED;
370
371		/*
372		 * Create a file in testdir.B, with the setgid bit set
373		 * in the creation modes and do so as root. The file
374		 * should inherit the group ID of the parent directory,
375		 * group2 and should have the setgid bit set.
376		 */
377
378		/* Become root again */
379		if (setreuid(-1, save_myuid) < 0) {
380			tst_resm(TFAIL | TERRNO,
381				 "Changing back to root failed");
382			local_flag = FAILED;
383		}
384
385		/* Create the file with setgid set */
386		ret = open(root_setgid_B, O_CREAT | O_EXCL | O_RDWR, MODE_SGID);
387		if (ret < 0) {
388			tst_resm(TFAIL | TERRNO, "open(%s) failed",
389				 root_setgid_B);
390			local_flag = FAILED;
391		}
392		close(ret);
393
394		if (stat(root_setgid_B, &buf) < 0) {
395			tst_resm(TFAIL | TERRNO, "stat(%s) failed",
396				 root_setgid_B);
397			local_flag = FAILED;
398		}
399
400		/* Verify modes */
401		if (!(buf.st_mode & S_ISGID)) {
402			tst_resm(TFAIL,
403				 "%s: Incorrect modes, setgid bit not set",
404				 root_setgid_B);
405			local_flag = FAILED;
406		}
407
408		/* Verify group ID */
409		if (buf.st_gid != group2_gid) {
410			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
411				 root_setgid_B, buf.st_gid, group2_gid);
412			local_flag = FAILED;
413		}
414
415		if (local_flag == PASSED) {
416			tst_resm(TPASS, "Test passed in block3.");
417		} else {
418			tst_resm(TFAIL, "Test failed in block3.");
419			fail_count++;
420		}
421
422		/*
423		 * Clean up any files created by test before call to anyfail.
424		 * Remove the directories.
425		 */
426		if (unlink(setgid_A) < 0)
427			tst_resm(TWARN | TERRNO, "unlink(%s) failed", setgid_A);
428		if (unlink(nosetgid_A) < 0)
429			tst_resm(TWARN | TERRNO, "unlink(%s) failed",
430				 nosetgid_A);
431		if (rmdir(DIR_A) < 0)
432			tst_resm(TWARN | TERRNO, "rmdir(%s) failed", DIR_A);
433
434		if (unlink(setgid_B) < 0)
435			tst_resm(TWARN | TERRNO, "unlink(%s) failed", setgid_B);
436		if (unlink(root_setgid_B) < 0)
437			tst_resm(TWARN | TERRNO, "unlink(%s) failed",
438				 root_setgid_B);
439		if (unlink(nosetgid_B) < 0)
440			tst_resm(TWARN | TERRNO, "unlink(%s) failed",
441				 nosetgid_B);
442		if (rmdir(DIR_B) < 0)
443			tst_resm(TWARN | TERRNO, "rmdir(%s) failed", DIR_B);
444
445		if (fail_count == 0) {
446			tst_resm(TPASS, "Test passed.");
447		} else {
448			tst_resm(TFAIL,
449				 "Test failed because of above failures.");
450		}
451
452	}
453
454	cleanup();
455	tst_exit();
456}
457
458static void setup(void)
459{
460	tst_require_root();
461	tst_sig(NOFORK, DEF_HANDLER, cleanup);
462	TEST_PAUSE;
463	tst_tmpdir();
464}
465
466static void cleanup(void)
467{
468	tst_rmdir();
469}
470