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 <sys/fcntl.h>
41 #include <errno.h>
42 #include <grp.h>
43 #include <pwd.h>
44 #include "test.h"
45 
46 char *TCID = "open10";
47 int TST_TOTAL = 1;
48 static 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 
62 static void setup(void);
63 static void cleanup(void);
64 
main(int ac,char * av[])65 int 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 		group = getgrnam("bin");
118 		if (group == NULL)
119 			tst_brkm(TBROK, cleanup, "bin not in /etc/group");
120 
121 		group2_gid = group->gr_gid;
122 
123 		/*
124 		 * Create a directory with group id the same as this process
125 		 * and with no setgid bit.
126 		 */
127 		if (mkdir(DIR_A, MODE_RWX) < 0) {
128 			tst_resm(TFAIL | TERRNO, "mkdir(%s) failed", DIR_A);
129 			local_flag = FAILED;
130 		}
131 
132 		if (chown(DIR_A, user1_uid, group2_gid) < 0) {
133 			tst_resm(TFAIL | TERRNO, "chown(%s) failed", DIR_A);
134 			local_flag = FAILED;
135 		}
136 
137 		if (stat(DIR_A, &buf) < 0) {
138 			tst_resm(TFAIL | TERRNO, "stat(%s) failed", DIR_A);
139 			local_flag = FAILED;
140 		}
141 
142 		/* Verify modes */
143 		if (buf.st_mode & S_ISGID) {
144 			tst_resm(TFAIL, "%s: Incorrect modes, setgid bit set",
145 				 DIR_A);
146 			local_flag = FAILED;
147 		}
148 
149 		/* Verify group ID */
150 		if (buf.st_gid != group2_gid) {
151 			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
152 				 DIR_A, buf.st_gid, group2_gid);
153 			local_flag = FAILED;
154 		}
155 
156 		/*
157 		 * Create a directory with group id different from that of
158 		 * this process and with the setgid bit set.
159 		 */
160 		if (mkdir(DIR_B, MODE_RWX) < 0) {
161 			tst_resm(TFAIL | TERRNO, "mkdir(%s) failed", DIR_B);
162 			local_flag = FAILED;
163 		}
164 
165 		if (chown(DIR_B, user1_uid, group2_gid) < 0) {
166 			tst_resm(TFAIL | TERRNO, "chown(%s) failed", DIR_B);
167 			local_flag = FAILED;
168 		}
169 
170 		if (chmod(DIR_B, MODE_SGID) < 0) {
171 			tst_resm(TFAIL | TERRNO, "chmod(%s) failed", DIR_B);
172 			local_flag = FAILED;
173 		}
174 
175 		if (stat(DIR_B, &buf) < 0) {
176 			tst_resm(TFAIL | TERRNO, "stat(%s) failed", DIR_B);
177 			local_flag = FAILED;
178 		}
179 
180 		/* Verify modes */
181 		if (!(buf.st_mode & S_ISGID)) {
182 			tst_resm(TFAIL,
183 				 "%s: Incorrect modes, setgid bit not set",
184 				 DIR_B);
185 			local_flag = FAILED;
186 		}
187 
188 		/* Verify group ID */
189 		if (buf.st_gid != group2_gid) {
190 			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
191 				 DIR_B, buf.st_gid, group2_gid);
192 			local_flag = FAILED;
193 		}
194 
195 		if (local_flag == PASSED) {
196 			tst_resm(TPASS, "Test passed in block0.");
197 		} else {
198 			tst_resm(TFAIL, "Test failed in block0.");
199 			fail_count++;
200 		}
201 
202 		local_flag = PASSED;
203 
204 		/*
205 		 * Create two files in testdir.A, one with the setgid
206 		 * bit set in the creation modes and the other without.
207 		 * Both should inherit the group ID of the process and
208 		 * maintain the setgid bit as specified in the creation
209 		 * modes.
210 		 */
211 		if (setgid(group1_gid) < 0) {
212 			tst_resm(TINFO,
213 				 "Unable to set process group ID to group1");
214 		}
215 
216 		if (setreuid(-1, user1_uid) < 0)
217 			tst_resm(TINFO, "Unable to set process uid to user1");
218 
219 		mygid = getgid();
220 
221 		/*
222 		 * Create the file with setgid not set
223 		 */
224 		ret = open(nosetgid_A, O_CREAT | O_EXCL | O_RDWR, MODE_RWX);
225 		if (ret < 0) {
226 			tst_resm(TFAIL | TERRNO, "open(%s) failed", nosetgid_A);
227 			local_flag = FAILED;
228 		}
229 		close(ret);
230 
231 		if (stat(nosetgid_A, &buf) < 0) {
232 			tst_resm(TFAIL | TERRNO, "stat(%s) failed", nosetgid_A);
233 			local_flag = FAILED;
234 		}
235 
236 		/* Verify modes */
237 		if (buf.st_mode & S_ISGID) {
238 			tst_resm(TFAIL, "%s: Incorrect modes, setgid bit set",
239 				 nosetgid_A);
240 			local_flag = FAILED;
241 		}
242 
243 		/* Verify group ID */
244 		if (buf.st_gid != mygid) {
245 			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
246 				 nosetgid_A, buf.st_gid, mygid);
247 			local_flag = FAILED;
248 		}
249 
250 		/*
251 		 * Create the file with setgid set
252 		 */
253 		ret = open(setgid_A, O_CREAT | O_EXCL | O_RDWR, MODE_SGID);
254 		if (ret < 0) {
255 			tst_resm(TFAIL | TERRNO, "open(%s) failed", setgid_A);
256 			local_flag = FAILED;
257 		}
258 		close(ret);
259 
260 		if (stat(setgid_A, &buf) < 0) {
261 			tst_resm(TFAIL | TERRNO, "stat(%s) failed", setgid_A);
262 			local_flag = FAILED;
263 		}
264 
265 		/* Verify modes */
266 		if (!(buf.st_mode & S_ISGID)) {
267 			tst_resm(TFAIL,
268 				 "%s: Incorrect modes, setgid bit not set",
269 				 setgid_A);
270 			local_flag = FAILED;
271 		}
272 
273 		/* Verify group ID */
274 		if (buf.st_gid != mygid) {
275 			tst_resm(TFAIL, "%s: Incorrect group (%d and %d)",
276 				 setgid_A, buf.st_gid, mygid);
277 			local_flag = FAILED;
278 		}
279 
280 		if (local_flag == PASSED) {
281 			tst_resm(TPASS, "Test passed in block1.");
282 		} else {
283 			tst_resm(TFAIL, "Test failed in block1.");
284 			fail_count++;
285 		}
286 
287 		local_flag = PASSED;
288 
289 		/*
290 		 * Create two files in testdir.B, one with the setgid
291 		 * bit set in the creation modes and the other without.
292 		 * Both should inherit the group ID of the parent
293 		 * directory, group2. Either file should have the segid
294 		 * bit set in the modes.
295 		 */
296 		/*
297 		 * Create the file with setgid not set
298 		 */
299 		ret = open(nosetgid_B, O_CREAT | O_EXCL | O_RDWR, MODE_RWX);
300 		if (ret < 0) {
301 			tst_resm(TFAIL | TERRNO, "open(%s) failed", nosetgid_B);
302 			local_flag = FAILED;
303 		}
304 		close(ret);
305 
306 		if (stat(nosetgid_B, &buf) < 0) {
307 			tst_resm(TFAIL | TERRNO, "stat(%s) failed", nosetgid_B);
308 			local_flag = FAILED;
309 		}
310 
311 		/* Verify modes */
312 		if (buf.st_mode & S_ISGID) {
313 			tst_resm(TFAIL,
314 				 "%s: Incorrect modes, setgid bit should be set",
315 				 nosetgid_B);
316 			local_flag = FAILED;
317 		}
318 
319 		/* Verify group ID */
320 		if (buf.st_gid != group2_gid) {
321 			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
322 				 nosetgid_B, buf.st_gid, group2_gid);
323 			local_flag = FAILED;
324 		}
325 
326 		/*
327 		 * Create the file with setgid set
328 		 */
329 		ret = open(setgid_B, O_CREAT | O_EXCL | O_RDWR, MODE_SGID);
330 		if (ret < 0) {
331 			tst_resm(TFAIL | TERRNO, "open(%s) failed", setgid_B);
332 			local_flag = FAILED;
333 		}
334 		close(ret);
335 
336 		if (stat(setgid_B, &buf) < 0) {
337 			tst_resm(TFAIL | TERRNO, "stat(%s) failed", setgid_B);
338 			local_flag = FAILED;
339 		}
340 
341 		/* Verify group ID */
342 		if (buf.st_gid != group2_gid) {
343 			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
344 				 setgid_B, buf.st_gid, group2_gid);
345 			local_flag = FAILED;
346 		}
347 
348 		/* Verify modes */
349 		if (!(buf.st_mode & S_ISGID)) {
350 			tst_resm(TFAIL,
351 				 "%s: Incorrect modes, setgid bit not set",
352 				 setgid_B);
353 			local_flag = FAILED;
354 		}
355 
356 		if (local_flag == PASSED) {
357 			tst_resm(TPASS, "Test passed in block2.");
358 		} else {
359 			tst_resm(TFAIL, "Test failed in block2.");
360 			fail_count++;
361 		}
362 
363 		local_flag = PASSED;
364 
365 		/*
366 		 * Create a file in testdir.B, with the setgid bit set
367 		 * in the creation modes and do so as root. The file
368 		 * should inherit the group ID of the parent directory,
369 		 * group2 and should have the setgid bit set.
370 		 */
371 
372 		/* Become root again */
373 		if (setreuid(-1, save_myuid) < 0) {
374 			tst_resm(TFAIL | TERRNO,
375 				 "Changing back to root failed");
376 			local_flag = FAILED;
377 		}
378 
379 		/* Create the file with setgid set */
380 		ret = open(root_setgid_B, O_CREAT | O_EXCL | O_RDWR, MODE_SGID);
381 		if (ret < 0) {
382 			tst_resm(TFAIL | TERRNO, "open(%s) failed",
383 				 root_setgid_B);
384 			local_flag = FAILED;
385 		}
386 		close(ret);
387 
388 		if (stat(root_setgid_B, &buf) < 0) {
389 			tst_resm(TFAIL | TERRNO, "stat(%s) failed",
390 				 root_setgid_B);
391 			local_flag = FAILED;
392 		}
393 
394 		/* Verify modes */
395 		if (!(buf.st_mode & S_ISGID)) {
396 			tst_resm(TFAIL,
397 				 "%s: Incorrect modes, setgid bit not set",
398 				 root_setgid_B);
399 			local_flag = FAILED;
400 		}
401 
402 		/* Verify group ID */
403 		if (buf.st_gid != group2_gid) {
404 			tst_resm(TFAIL, "%s: Incorrect group (got %d and %d)",
405 				 root_setgid_B, buf.st_gid, group2_gid);
406 			local_flag = FAILED;
407 		}
408 
409 		if (local_flag == PASSED) {
410 			tst_resm(TPASS, "Test passed in block3.");
411 		} else {
412 			tst_resm(TFAIL, "Test failed in block3.");
413 			fail_count++;
414 		}
415 
416 		/*
417 		 * Clean up any files created by test before call to anyfail.
418 		 * Remove the directories.
419 		 */
420 		if (unlink(setgid_A) < 0)
421 			tst_resm(TWARN | TERRNO, "unlink(%s) failed", setgid_A);
422 		if (unlink(nosetgid_A) < 0)
423 			tst_resm(TWARN | TERRNO, "unlink(%s) failed",
424 				 nosetgid_A);
425 		if (rmdir(DIR_A) < 0)
426 			tst_resm(TWARN | TERRNO, "rmdir(%s) failed", DIR_A);
427 
428 		if (unlink(setgid_B) < 0)
429 			tst_resm(TWARN | TERRNO, "unlink(%s) failed", setgid_B);
430 		if (unlink(root_setgid_B) < 0)
431 			tst_resm(TWARN | TERRNO, "unlink(%s) failed",
432 				 root_setgid_B);
433 		if (unlink(nosetgid_B) < 0)
434 			tst_resm(TWARN | TERRNO, "unlink(%s) failed",
435 				 nosetgid_B);
436 		if (rmdir(DIR_B) < 0)
437 			tst_resm(TWARN | TERRNO, "rmdir(%s) failed", DIR_B);
438 
439 		if (fail_count == 0) {
440 			tst_resm(TPASS, "Test passed.");
441 		} else {
442 			tst_resm(TFAIL,
443 				 "Test failed because of above failures.");
444 		}
445 
446 	}
447 
448 	cleanup();
449 	tst_exit();
450 }
451 
setup(void)452 static void setup(void)
453 {
454 	tst_require_root();
455 	tst_sig(NOFORK, DEF_HANDLER, cleanup);
456 	TEST_PAUSE;
457 	tst_tmpdir();
458 }
459 
cleanup(void)460 static void cleanup(void)
461 {
462 	tst_rmdir();
463 }
464