chmod06.c revision 4bb656a129f7507823e9e6d6b98b1a02fd80ef89
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: chmod06
22 *
23 * Test Description:
24 *   Verify that,
25 *   1) chmod(2) returns -1 and sets errno to EPERM if the effective user id
26 *	of process does not match the owner of the file and the process is
27 *	not super user.
28 *   2) chmod(2) returns -1 and sets errno to EACCES if search permission is
29 *	denied on a component of the path prefix.
30 *   3) chmod(2) returns -1 and sets errno to EFAULT if pathname points
31 *	outside user's accessible address space.
32 *   4) chmod(2) returns -1 and sets errno to ENAMETOOLONG if the pathname
33 *	component is too long.
34 *   5) chmod(2) returns -1 and sets errno to ENOTDIR if the directory
35 *	component in pathname is not a directory.
36 *   6) chmod(2) returns -1 and sets errno to ENOENT if the specified file
37 *	does not exists.
38 *
39 * Expected Result:
40 *  chmod() should fail with return value -1 and set expected errno.
41 *
42 * Algorithm:
43 *  Setup:
44 *   Setup signal handling.
45 *   Create temporary directory.
46 *   Pause for SIGUSR1 if option specified.
47 *
48 *  Test:
49 *   Loop if the proper options are given.
50 *   Execute system call
51 *   Check return code, if system call failed (return=-1)
52 *   	if errno set == expected errno
53 *   		Issue sys call fails with expected return value and errno.
54 *   	Otherwise,
55 *		Issue sys call fails with unexpected errno.
56 *   Otherwise,
57 *	Issue sys call returns unexpected value.
58 *
59 *  Cleanup:
60 *   Print errno log and/or timing stats if options given
61 *   Delete the temporary directory(s)/file(s) created.
62 *
63 * Usage:  <for command-line>
64 *  chmod06 [-c n] [-e] [-f] [-i n] [-I x] [-p x] [-t]
65 *     where,  -c n : Run n copies concurrently.
66 *             -e   : Turn on errno logging.
67 *             -f   : Turn off functionality Testing.
68 *	       -i n : Execute test n times.
69 *	       -I x : Execute test for x seconds.
70 *	       -P x : Pause for x seconds between iterations.
71 *	       -t   : Turn on syscall timing.
72 *
73 * HISTORY
74 *	07/2001 Ported by Wayne Boyer
75 *
76 * RESTRICTIONS:
77 */
78
79#ifndef _GNU_SOURCE
80# define _GNU_SOURCE
81#endif
82
83#include <stdio.h>
84#include <stdlib.h>
85#include <unistd.h>
86#include <fcntl.h>
87#include <errno.h>
88#include <string.h>
89#include <signal.h>
90#include <grp.h>
91#include <pwd.h>
92#include <sys/types.h>
93#include <sys/stat.h>
94#include <sys/mman.h>
95
96#include "test.h"
97#include "usctest.h"
98
99#define MODE_RWX	S_IRWXU | S_IRWXG | S_IRWXO
100#define FILE_MODE	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
101#define DIR_TEMP	"testdir_1"
102#define TEST_FILE1	"tfile_1"
103#define TEST_FILE2	"testdir_1/tfile_2"
104#define TEST_FILE3	"t_file/tfile_3"
105
106int no_setup();			/* dummy setup function */
107int setup1();			/* setup function to test chmod for EPERM */
108int setup2();			/* setup function to test chmod for EACCES */
109int setup3();			/* setup function to test chmod for ENOTDIR */
110int longpath_setup();	/* setup function to test chmod for ENAMETOOLONG */
111
112char	*get_high_address();	/* Function from ltp-Lib */
113
114char *test_home;		/* variable to hold TESTHOME env. */
115char Longpathname[PATH_MAX+2];
116char High_address_node[64];
117
118struct test_case_t {		/* test case struct. to hold ref. test cond's*/
119	char *pathname;
120	char *desc;
121	int mode;
122	int exp_errno;
123	int (*setupfunc)();
124} Test_cases[] = {
125	{ TEST_FILE1, "Process is not owner/root", FILE_MODE, EPERM, setup1 },
126	{ TEST_FILE2,  "No Search permissions to process", FILE_MODE, EACCES, setup2 },
127	{ High_address_node, "Address beyond address space", FILE_MODE, EFAULT, no_setup },
128	{ (char *)-1, "Negative address", FILE_MODE, EFAULT, no_setup },
129	{ Longpathname, "Pathname too long", FILE_MODE, ENAMETOOLONG, longpath_setup },
130	{ "", "Pathname is empty", FILE_MODE, ENOENT, no_setup },
131	{ TEST_FILE3, "Path contains regular file", FILE_MODE, ENOTDIR, setup3 },
132	{ NULL, NULL, 0, 0, no_setup }
133};
134
135char *TCID="chmod06";           /* Test program identifier.    */
136int TST_TOTAL = 7;		/* Total number of test cases. */
137extern int Tst_count;           /* Test Case counter for tst_* routines */
138int exp_enos[]={EPERM, EACCES, EFAULT, ENAMETOOLONG, ENOENT, ENOTDIR, 0};
139
140char * bad_addr = 0;
141
142void setup();			/* Main setup function for the tests */
143void cleanup();			/* cleanup function for the test */
144
145int
146main(int ac, char **av)
147{
148	int lc;			/* loop counter */
149	char *msg;		/* message returned from parse_opts */
150	char *file_name;	/* ptr. for file name whose mode is modified*/
151	char *test_desc;	/* test specific error message */
152	int ind;		/* counter to test different test conditions */
153	int mode;		/* creation mode for the node created */
154	char nobody_uid[] = "nobody";
155	struct passwd *ltpuser;
156
157	/* Parse standard options given to run the test. */
158	msg = parse_opts(ac, av, (option_t *) NULL, NULL);
159	if (msg != (char *) NULL) {
160		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
161		tst_exit();
162	}
163
164	/*
165	 * Invoke setup function to call individual test setup functions
166	 * to simulate test conditions.
167	 */
168	setup();
169
170	/* set the expected errnos... */
171	TEST_EXP_ENOS(exp_enos);
172
173
174	/* Check looping state if -c option given */
175	for (lc = 0; TEST_LOOPING(lc); lc++) {
176		/* Reset Tst_count in case we are looping. */
177		Tst_count=0;
178
179		for (ind = 0; Test_cases[ind].desc != NULL; ind++) {
180			file_name = Test_cases[ind].pathname;
181			mode = Test_cases[ind].mode;
182			test_desc = Test_cases[ind].desc;
183
184			if (file_name == High_address_node) {
185				file_name = (char *)get_high_address();
186			}
187			if (ind < 2) {
188			 /* Switch to nobody user for correct error code collection */
189			        ltpuser = getpwnam(nobody_uid);
190         			if (seteuid(ltpuser->pw_uid) == -1) {
191                			tst_resm(TINFO, "seteuid failed to "
192                         			"to set the effective uid to %d",
193                         			ltpuser->pw_uid);
194                			perror("seteuid");
195         			}
196			}
197			if (ind >= 2) {
198				seteuid(0);
199			}
200
201			/*
202			 * Call chmod(2) to test different test conditions.
203			 * verify that it fails with -1 return value and
204			 * sets appropriate errno.
205			 */
206			TEST(chmod(file_name, mode));
207
208			/* Check return code from chmod(2) */
209			if (TEST_RETURN != -1) {
210				tst_resm(TFAIL, "chmod() returned %d, "
211					 "expected -1, errno:%d", TEST_RETURN,
212					 Test_cases[ind].exp_errno);
213				continue;
214			}
215
216			TEST_ERROR_LOG(TEST_ERRNO);
217			if (TEST_ERRNO == Test_cases[ind].exp_errno) {
218				tst_resm(TPASS, "chmod() fails, %s, errno:%d",
219					 test_desc, TEST_ERRNO);
220			} else {
221				tst_resm(TFAIL, "chmod() fails, %s, errno:%d, "
222					 "expected errno:%d", test_desc,
223					 TEST_ERRNO, Test_cases[ind].exp_errno);
224			}
225		}	/* End of TEST CASE LOOPING. */
226
227	}	/* End for TEST_LOOPING */
228
229	/*
230	 * Invoke cleanup() to delete the test directory/file(s) created
231	 * in the setup().
232	 */
233	cleanup();
234
235	return 0;
236	/*NOTREACHED*/
237}	/* End main */
238
239/*
240 * void
241 * setup(void) - performs all ONE TIME setup for this test.
242 * 	Exit the test program on receipt of unexpected signals.
243 *	Create a temporary directory and change directory to it.
244 *	Invoke individual test setup functions according to the order
245 *	set in struct. definition.
246 */
247void
248setup()
249{
250	int ind;			/* counter for setup functions */
251
252	/* Capture unexpected signals */
253	tst_sig(FORK, DEF_HANDLER, cleanup);
254
255	test_home = get_current_dir_name();
256
257	/* Switch to nobody user for correct error code collection */
258        if (geteuid() != 0) {
259                tst_brkm(TBROK, tst_exit, "Test must be run as root");
260         }
261
262
263	/* Pause if that option was specified */
264	TEST_PAUSE;
265
266	/* Make a temp dir and cd to it */
267	tst_tmpdir();
268
269	bad_addr = mmap(0, 1, PROT_NONE,
270			MAP_PRIVATE_EXCEPT_UCLINUX|MAP_ANONYMOUS, 0, 0);
271	if (bad_addr == MAP_FAILED) {
272		tst_brkm(TBROK, cleanup, "mmap failed");
273	}
274	Test_cases[3].pathname = bad_addr;
275
276	/* call individual setup functions */
277	for (ind = 0; Test_cases[ind].desc != NULL; ind++) {
278		Test_cases[ind].setupfunc();
279	}
280}	/* End setup() */
281
282/*
283 * int
284 * no_setup() - Some test conditions for mknod(2) do not any setup.
285 *              Hence, this function just returns 0.
286 *  This function simply returns 0.
287 */
288int
289no_setup()
290{
291        return 0;
292}
293
294/*
295 * int
296 * setup1() - setup function for a test condition for which chmod(2)
297 *	      returns -1 and sets errno to EPERM.
298 *
299 *  Create a testfile under temporary directory and invoke setuid to root
300 *  program to change the ownership of testfile to that of "ltpuser2" user.
301 *
302 */
303int
304setup1()
305{
306	int fd;
307	char Path_name[PATH_MAX];       /* Buffer to hold command string */
308	char Cmd_buffer[BUFSIZ];        /* Buffer to hold command string */
309
310
311	/* open/creat a test file and close it */
312	if ((fd = open(TEST_FILE1, O_RDWR|O_CREAT, 0666)) == -1) {
313		tst_brkm(TBROK, cleanup,
314			 "open(%s, O_RDWR|O_CREAT, 0666) failed, errno=%d : %s",
315			 TEST_FILE1, errno, strerror(errno));
316	}
317	if (close(fd) == -1) {
318		tst_brkm(TBROK, cleanup,
319			 "close(%s) Failed, errno=%d : %s",
320			 TEST_FILE1, errno, strerror(errno));
321	}
322
323	/* Get the current working directory of the process */
324	if (getcwd(Path_name, sizeof(Path_name)) == NULL) {
325                tst_brkm(TBROK, cleanup,
326                         "getcwd(3) fails to get working directory of process");
327        }
328
329	/* Get the path of test file created under temporary directory */
330	strcat(Path_name, "/"TEST_FILE1);
331
332	/* Get the command name to be executed as setuid to root */
333	strcpy((char *)Cmd_buffer, (const char *)test_home);
334	strcat((char *)Cmd_buffer, "/change_owner ");
335	strcat((char *)Cmd_buffer, TCID);
336	strcat((char *)Cmd_buffer, " ");
337	strcat((char *)Cmd_buffer, Path_name);
338
339	/* Change the ownership of testfile */
340	if (system((const char *)Cmd_buffer) != 0) {
341		tst_brkm(TBROK, cleanup,
342			 "Fail to modify %s ownership(s)!", TEST_FILE1);
343	}
344	return 0;
345}
346
347/*
348 * int
349 * setup2() - setup function for a test condition for which mknod(2)
350 *	      returns -1 and sets errno to EACCES.
351 *  Create a test directory under temporary directory and create a test file
352 *  under this directory with mode "0666" permissions.
353 *  Modify the mode permissions on test directory such that process will not
354 *  have search permissions on test directory.
355 *
356 *  The function returns 0.
357 */
358int
359setup2()
360{
361	int fd;			/* file handle for testfile */
362
363	/* Creat a test directory and a file under it */
364	if (mkdir(DIR_TEMP, MODE_RWX) < 0) {
365		tst_brkm(TBROK, cleanup, "mkdir(2) of %s failed", DIR_TEMP);
366	}
367
368	if ((fd = open(TEST_FILE2, O_RDWR|O_CREAT, 0666)) == -1) {
369		tst_brkm(TBROK, cleanup,
370			 "open(%s, O_RDWR|O_CREAT, 0666) failed, errno=%d : %s",
371			 TEST_FILE2, errno, strerror(errno));
372	}
373
374	/* Close the testfile created above */
375	if (close(fd) == -1) {
376		tst_brkm(TBROK, cleanup,
377			 "close(%s) Failed, errno=%d : %s",
378			 TEST_FILE2, errno, strerror(errno));
379	}
380
381	/* Modify mode permissions on test directory */
382	if (chmod(DIR_TEMP, FILE_MODE) < 0) {
383		tst_brkm(TBROK, cleanup, "chmod(2) of %s failed", DIR_TEMP);
384	}
385	return 0;
386}
387
388/*
389 * int
390 * setup3() - setup function for a test condition for which chmod(2)
391 *	     returns -1 and sets errno to ENOTDIR.
392 *
393 *  Create a test file under temporary directory so that test tries to
394 *  change mode of a testfile "tfile_3" under "t_file" which happens to be
395 *  another regular file.
396 */
397int
398setup3()
399{
400	int fd;
401
402	/* Creat a test file under temporary directory and close it */
403	if ((fd = open("t_file", O_RDWR|O_CREAT, MODE_RWX)) == -1) {
404		tst_brkm(TBROK, cleanup,
405			 "open(2) on t_file failed, errno=%d : %s",
406			 errno, strerror(errno));
407	}
408	if (close(fd) == -1) {
409		tst_brkm(TBROK, cleanup,
410			 "close(t_file) Failed, errno=%d : %s",
411			 errno, strerror(errno));
412	}
413	return 0;
414}
415
416/*
417 * int
418 * longpath_setup() - setup to create a node with a name length exceeding
419 *                    the MAX. length of PATH_MAX.
420 *   This function returns 0.
421 */
422int
423longpath_setup()
424{
425        int ind;                /* counter variable */
426
427        for (ind = 0; ind <= (PATH_MAX + 1); ind++) {
428                Longpathname[ind] = 'a';
429        }
430        return 0;
431}
432
433/*
434 * void
435 * cleanup() - Performs all ONE TIME cleanup for this test at
436 *             completion or premature exit.
437 *	Print test timing stats and errno log if test executed with options.
438 *	Remove temporary directory and sub-directories/files under it
439 *	created during setup().
440 *	Exit the test program with normal exit code.
441 */
442void
443cleanup()
444{
445	/*
446	 * print timing stats if that option was specified.
447	 * print errno log if that option was specified.
448	 */
449	TEST_CLEANUP;
450
451	/* Restore mode permissions on test directory created in setup2() */
452	if (chmod(DIR_TEMP, MODE_RWX) < 0) {
453		tst_resm(TBROK, "chmod(2) of %s failed", DIR_TEMP);
454	}
455
456	/* Remove files and temporary directory created */
457	tst_rmdir();
458
459	/* exit with return code appropriate for results */
460	tst_exit();
461}	/* End cleanup() */
462