chown02.c revision 7d0a4a57fbcd47f72b67c08df532e8ef47f6fdae
1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20/*
21 * Test Name: chown02
22 *
23 * Test Description:
24 *  Verify that, when chown(2) invoked by super-user to change the owner and
25 *  group of a file specified by path to any numeric owner(uid)/group(gid)
26 *  values,
27 *	- clears setuid and setgid bits set on an executable file.
28 *	- preserves setgid bit set on a non-group-executable file.
29 *
30 * Expected Result:
31 *  chown(2) should return 0 and the ownership set on the file should match
32 *  the numeric values contained in owner and group respectively.
33 *
34 * Algorithm:
35 *  Setup:
36 *   Setup signal handling.
37 *   Create temporary directory.
38 *   Pause for SIGUSR1 if option specified.
39 *
40 *  Test:
41 *   Loop if the proper options are given.
42 *   Execute system call
43 *   Check return code, if system call failed (return=-1)
44 *   	Log the errno and Issue a FAIL message.
45 *   Otherwise,
46 *   	Verify the Functionality of system call
47 *      if successful,
48 *      	Issue Functionality-Pass message.
49 *      Otherwise,
50 *		Issue Functionality-Fail message.
51 *  Cleanup:
52 *   Print errno log and/or timing stats if options given
53 *   Delete the temporary directory created.
54 *
55 * Usage:  <for command-line>
56 *  chown02 [-c n] [-f] [-i n] [-I x] [-P x] [-t]
57 *     where,  -c n : Run n copies concurrently.
58 *             -f   : Turn off functionality Testing.
59 *	       -i n : Execute test n times.
60 *	       -I x : Execute test for x seconds.
61 *	       -P x : Pause for x seconds between iterations.
62 *	       -t   : Turn on syscall timing.
63 *
64 * HISTORY
65 *	07/2001 Ported by Wayne Boyer
66 *
67 * RESTRICTIONS:
68 *  This test should be run by 'super-user' (root) only.
69 *
70 */
71
72#include <stdio.h>
73#include <sys/types.h>
74#include <sys/stat.h>
75#include <sys/fcntl.h>
76#include <errno.h>
77#include <string.h>
78#include <signal.h>
79
80#include "test.h"
81#include "usctest.h"
82
83#define FILE_MODE	S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
84#define NEW_PERMS1	S_IFREG | S_IRWXU | S_IRWXG | S_ISUID | S_ISGID
85#define NEW_PERMS2	S_IFREG | S_IRWXU | S_ISGID
86#define EXP_PERMS	S_IFREG | S_IRWXU | S_IRWXG
87#define TESTFILE1	"testfile1"
88#define TESTFILE2	"testfile2"
89
90char *TCID = "chown02";		/* Test program identifier.    */
91int TST_TOTAL = 1;		/* Total number of test conditions */
92extern int Tst_count;		/* Test Case counter for tst_* routines */
93
94int no_setup();
95int setup1();			/* Test specific setup functions */
96int setup2();
97
98struct test_case_t {
99	char *pathname;
100	char *desc;
101	uid_t user_id;
102	gid_t group_id;
103	int test_flag;
104	int (*setupfunc) ();
105} Test_cases[] = {
106	{
107	TESTFILE1, "Setuid/Setgid bits cleared", 700, 701, 1, setup1}, {
108	TESTFILE2, "Setgid bit not cleared", 700, 701, 2, setup2}, {
109NULL, NULL, 0, 0, 0, no_setup},};
110
111void setup();			/* setup function for the test */
112void cleanup();			/* cleanup function for the test */
113
114int main(int ac, char **av)
115{
116	struct stat stat_buf;	/* stat(2) struct contents */
117	int lc;			/* loop counter */
118	char *msg;		/* message returned from parse_opts */
119	int ind;		/* counter variable for chmod(2) tests */
120	uid_t user_id;		/* user id of the user set for testfile */
121	gid_t group_id;		/* group id of the user set for testfile */
122	int test_flag;		/* test condition specific flag variable */
123	char *file_name;	/* ptr. for test file name */
124	char *test_desc;	/* test specific message */
125
126	/* Parse standard options given to run the test. */
127	msg = parse_opts(ac, av, NULL, NULL);
128	if (msg != NULL) {
129		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
130
131	}
132
133	setup();
134
135	for (lc = 0; TEST_LOOPING(lc); lc++) {
136
137		Tst_count = 0;
138
139		for (ind = 0; Test_cases[ind].desc != NULL; ind++) {
140
141			file_name = Test_cases[ind].pathname;
142			test_desc = Test_cases[ind].desc;
143			user_id = Test_cases[ind].user_id;
144			group_id = Test_cases[ind].group_id;
145			test_flag = Test_cases[ind].test_flag;
146
147			/*
148			 * Call chown(2) with different user id and
149			 * group id (numeric values) to set it on testfile.
150			 */
151			TEST(chown(file_name, user_id, group_id));
152
153			if (TEST_RETURN == -1) {
154				tst_resm(TFAIL,
155					 "chown() Fails on %s, errno=%d",
156					 file_name, TEST_ERRNO);
157				continue;
158			}
159
160			/*
161			 * Perform functional verification if test
162			 * executed without (-f) option.
163			 */
164			if (STD_FUNCTIONAL_TEST) {
165				/*
166				 * Get the testfile information using stat(2).
167				 */
168				if (stat(file_name, &stat_buf) < 0) {
169					tst_brkm(TFAIL, cleanup, "stat(2) of "
170						 "%s failed, errno:%d",
171						 file_name, TEST_ERRNO);
172				}
173
174				/*
175				 * Check for expected Ownership ids
176				 * set on testfile.
177				 */
178				if ((stat_buf.st_uid != user_id) ||
179				    (stat_buf.st_gid != group_id)) {
180					tst_brkm(TFAIL, cleanup, "%s: Incorrect"
181						 " ownership set, Expected %d "
182						 "%d", file_name,
183						 user_id, group_id);
184				}
185
186				/*
187				 * Verify that S_ISUID/S_ISGID bits set on the
188				 * testfile(s) in setup()s are cleared by
189				 * chown().
190				 */
191				if ((test_flag == 1) &&
192				    ((stat_buf.st_mode &
193				      (S_ISUID | S_ISGID)))) {
194					tst_resm(TFAIL, "%s: Incorrect mode "
195						 "permissions %#o, Expected "
196						 "%#o", file_name, NEW_PERMS1,
197						 EXP_PERMS);
198				} else if ((test_flag == 2) &&
199					   (!(stat_buf.st_mode & S_ISGID))) {
200					tst_resm(TFAIL, "%s: Incorrect mode "
201						 "permissions %#o, Expected "
202						 "%#o", file_name,
203						 stat_buf.st_mode, NEW_PERMS2);
204				} else {
205					tst_resm(TPASS,
206						 "chown() on %s succeeds : %s",
207						 file_name, test_desc);
208				}
209			} else {
210				tst_resm(TPASS, "call succeeded");
211			}
212		}
213	}
214
215	cleanup();
216
217 }
218
219/*
220 * void
221 * setup() - performs all ONE TIME setup for this test.
222 *  Create a temporary directory and change directory to it.
223 *  Create a test file under temporary directory and close it
224 */
225void setup()
226{
227	int ind;
228
229	tst_sig(NOFORK, DEF_HANDLER, cleanup);
230
231	/* Check that the test process id is super/root  */
232	if (geteuid() != 0) {
233		tst_brkm(TBROK, NULL, "Must be super/root for this test!");
234
235	}
236
237	TEST_PAUSE;
238
239	tst_tmpdir();
240
241	/* call individual setup functions */
242	for (ind = 0; Test_cases[ind].desc != NULL; ind++) {
243		Test_cases[ind].setupfunc();
244	}
245}
246
247/*
248 * int
249 * setup1() - Setup function for chown(2) to verify setuid/setgid bits
250 *	      set on an executable file will not be cleared.
251 *  Creat a testfile and set setuid/setgid bits on the mode of file.$
252 */
253int setup1()
254{
255	int fd;			/* File descriptor for testfile1 */
256
257	/* Creat a testfile and close it */
258	if ((fd = open(TESTFILE1, O_RDWR | O_CREAT, FILE_MODE)) == -1) {
259		tst_brkm(TBROK|TERRNO, cleanup,
260			 "open(%s, O_RDWR|O_CREAT, %o) failed",
261			 TESTFILE1, FILE_MODE);
262	}
263	if (close(fd) == -1) {
264		tst_brkm(TBROK|TERRNO, cleanup,
265			 "close(%s) failed",
266			 TESTFILE1);
267	}
268
269	/* Set setuid/setgid bits on the test file created */
270	if (chmod(TESTFILE1, NEW_PERMS1) != 0) {
271		tst_brkm(TBROK|TERRNO, cleanup,
272			 "chmod(%s) failed",
273			 TESTFILE1);
274	}
275	return 0;
276}
277
278/*
279 * int
280 * setup2() - Setup function for chown(2) to verify setgid bit set
281 *	      set on non-group executable file will not be cleared.
282 *  Creat a testfile and set setgid bit on the mode of file.
283 */
284int setup2()
285{
286	int fd;			/* File descriptor for testfile2 */
287
288	/* Creat a testfile and close it */
289	if ((fd = open(TESTFILE2, O_RDWR | O_CREAT, FILE_MODE)) == -1) {
290		tst_brkm(TBROK|TERRNO, cleanup,
291			 "open(%s, O_RDWR|O_CREAT, %o) failed",
292			 TESTFILE2, FILE_MODE);
293	}
294	if (close(fd) == -1) {
295		tst_brkm(TBROK|TERRNO, cleanup,
296			 "close(%s) failed",
297			 TESTFILE2);
298	}
299
300	/* Set setgid bit on the test file created */
301	if (chmod(TESTFILE2, NEW_PERMS2) != 0) {
302		tst_brkm(TBROK|TERRNO, cleanup,
303			 "chmod(%s) failed",
304			 TESTFILE2);
305	}
306	return 0;
307}
308
309/*
310 * int
311 * no_setup() - Some test conditions for mknod(2) do not any setup.
312 * 		Hence, this function just returns 0.
313 *  This function simply returns 0.
314 */
315int no_setup()
316{
317	return 0;
318}
319
320/*
321 * void
322 * cleanup() - performs all ONE TIME cleanup for this test at
323 *	       completion or premature exit.
324 *  Remove the test directory and testfile created in the setup.
325 */
326void cleanup()
327{
328	/*
329	 * print timing stats if that option was specified.
330	 */
331	TEST_CLEANUP;
332
333	tst_rmdir();
334
335}