390 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			390 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright (c) Wipro Technologies Ltd, 2002.  All Rights Reserved.
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms of version 2 of the GNU General Public License as
 | |
|  * published by the Free Software Foundation.
 | |
|  *
 | |
|  * This program is distributed in the hope that it would be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along
 | |
|  * with this program; if not, write the Free Software Foundation, Inc.,
 | |
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * DESCRIPTION
 | |
|  *	Check for basic mount(2) system call flags.
 | |
|  *
 | |
|  *	Verify that mount(2) syscall passes for each flag setting and validate
 | |
|  *	the flags
 | |
|  *	1) MS_RDONLY - mount read-only.
 | |
|  *	2) MS_NODEV - disallow access to device special files.
 | |
|  *	3) MS_NOEXEC - disallow program execution.
 | |
|  *	4) MS_SYNCHRONOUS - writes are synced at once.
 | |
|  *	5) MS_REMOUNT - alter flags of a mounted FS.
 | |
|  *	6) MS_NOSUID - ignore suid and sgid bits.
 | |
|  *	7) MS_NOATIME - do not update access times.
 | |
|  */
 | |
| 
 | |
| #ifndef _GNU_SOURCE
 | |
| #define _GNU_SOURCE
 | |
| #endif
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/mount.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/wait.h>
 | |
| #include <assert.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <pwd.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include "test.h"
 | |
| #include "safe_macros.h"
 | |
| 
 | |
| static void setup(void);
 | |
| static void cleanup(void);
 | |
| static int test_rwflag(int, int);
 | |
| 
 | |
| char *TCID = "mount03";
 | |
| int TST_TOTAL = 7;
 | |
| 
 | |
| #define TEMP_FILE	"temp_file"
 | |
| #define FILE_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
 | |
| #define DIR_MODE	(S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP| \
 | |
| 			 S_IXGRP|S_IROTH|S_IXOTH)
 | |
| #define SUID_MODE	(S_ISUID|S_IRUSR|S_IXUSR|S_IXGRP|S_IXOTH)
 | |
| 
 | |
| static const char mntpoint[] = "mntpoint";
 | |
| static const char *device;
 | |
| static const char *fs_type;
 | |
| static int fildes;
 | |
| 
 | |
| static char write_buffer[BUFSIZ];
 | |
| static char read_buffer[BUFSIZ];
 | |
| static char path_name[PATH_MAX];
 | |
| static char file[PATH_MAX];
 | |
| 
 | |
| long rwflags[] = {
 | |
| 	MS_RDONLY,
 | |
| 	MS_NODEV,
 | |
| 	MS_NOEXEC,
 | |
| 	MS_SYNCHRONOUS,
 | |
| 	MS_RDONLY,
 | |
| 	MS_NOSUID,
 | |
| 	MS_NOATIME,
 | |
| };
 | |
| 
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
| 	int lc, i;
 | |
| 
 | |
| 	tst_parse_opts(argc, argv, NULL, NULL);
 | |
| 
 | |
| 	setup();
 | |
| 
 | |
| 	for (lc = 0; TEST_LOOPING(lc); lc++) {
 | |
| 
 | |
| 		tst_count = 0;
 | |
| 
 | |
| 		for (i = 0; i < TST_TOTAL; ++i) {
 | |
| 
 | |
| 			TEST(mount(device, mntpoint, fs_type, rwflags[i],
 | |
| 				   NULL));
 | |
| 
 | |
| 			if (TEST_RETURN != 0) {
 | |
| 				tst_resm(TFAIL | TTERRNO, "mount(2) failed");
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			/* Validate the rwflag */
 | |
| 			if (test_rwflag(i, lc) == 1)
 | |
| 				tst_resm(TFAIL, "mount(2) failed while"
 | |
| 					 " validating %ld", rwflags[i]);
 | |
| 			else
 | |
| 				tst_resm(TPASS, "mount(2) passed with "
 | |
| 					 "rwflag = %ld", rwflags[i]);
 | |
| 
 | |
| 			TEST(tst_umount(mntpoint));
 | |
| 			if (TEST_RETURN != 0)
 | |
| 				tst_brkm(TBROK | TTERRNO, cleanup,
 | |
| 					 "umount(2) failed for %s", mntpoint);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	cleanup();
 | |
| 	tst_exit();
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * test_rwflag(int i, int cnt)
 | |
|  * Validate the mount system call for rwflags.
 | |
|  */
 | |
| int test_rwflag(int i, int cnt)
 | |
| {
 | |
| 	int ret, fd, pid, status;
 | |
| 	char nobody_uid[] = "nobody";
 | |
| 	time_t atime;
 | |
| 	struct passwd *ltpuser;
 | |
| 	struct stat file_stat;
 | |
| 	char readbuf[20];
 | |
| 
 | |
| 	switch (i) {
 | |
| 	case 0:
 | |
| 		/* Validate MS_RDONLY flag of mount call */
 | |
| 
 | |
| 		snprintf(file, PATH_MAX, "%stmp", path_name);
 | |
| 		fd = open(file, O_CREAT | O_RDWR, S_IRWXU);
 | |
| 		if (fd == -1) {
 | |
| 			if (errno == EROFS) {
 | |
| 				return 0;
 | |
| 			} else {
 | |
| 				tst_resm(TWARN | TERRNO,
 | |
| 					 "open didn't fail with EROFS");
 | |
| 				return 1;
 | |
| 			}
 | |
| 		}
 | |
| 		close(fd);
 | |
| 		return 1;
 | |
| 	case 1:
 | |
| 		/* Validate MS_NODEV flag of mount call */
 | |
| 
 | |
| 		snprintf(file, PATH_MAX, "%smynod_%d_%d", path_name, getpid(),
 | |
| 			 cnt);
 | |
| 		if (mknod(file, S_IFBLK | 0777, 0) == 0) {
 | |
| 			fd = open(file, O_RDWR, S_IRWXU);
 | |
| 			if (fd == -1) {
 | |
| 				if (errno == EACCES) {
 | |
| 					return 0;
 | |
| 				} else {
 | |
| 					tst_resm(TWARN | TERRNO,
 | |
| 						 "open didn't fail with EACCES");
 | |
| 					return 1;
 | |
| 				}
 | |
| 			}
 | |
| 			close(fd);
 | |
| 		} else {
 | |
| 			tst_resm(TWARN | TERRNO, "mknod(2) failed to create %s",
 | |
| 				 file);
 | |
| 			return 1;
 | |
| 		}
 | |
| 		return 1;
 | |
| 	case 2:
 | |
| 		/* Validate MS_NOEXEC flag of mount call */
 | |
| 
 | |
| 		snprintf(file, PATH_MAX, "%stmp1", path_name);
 | |
| 		fd = open(file, O_CREAT | O_RDWR, S_IRWXU);
 | |
| 		if (fd == -1) {
 | |
| 			tst_resm(TWARN | TERRNO, "opening %s failed", file);
 | |
| 		} else {
 | |
| 			close(fd);
 | |
| 			ret = execlp(file, basename(file), NULL);
 | |
| 			if ((ret == -1) && (errno == EACCES))
 | |
| 				return 0;
 | |
| 		}
 | |
| 		return 1;
 | |
| 	case 3:
 | |
| 		/*
 | |
| 		 * Validate MS_SYNCHRONOUS flag of mount call.
 | |
| 		 * Copy some data into data buffer.
 | |
| 		 */
 | |
| 
 | |
| 		strcpy(write_buffer, "abcdefghijklmnopqrstuvwxyz");
 | |
| 
 | |
| 		/* Creat a temporary file under above directory */
 | |
| 		snprintf(file, PATH_MAX, "%s%s", path_name, TEMP_FILE);
 | |
| 		fildes = open(file, O_RDWR | O_CREAT, FILE_MODE);
 | |
| 		if (fildes == -1) {
 | |
| 			tst_resm(TWARN | TERRNO,
 | |
| 				 "open(%s, O_RDWR|O_CREAT, %#o) failed",
 | |
| 				 file, FILE_MODE);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		/* Write the buffer data into file */
 | |
| 		if (write(fildes, write_buffer, strlen(write_buffer)) !=
 | |
| 		    (long)strlen(write_buffer)) {
 | |
| 			tst_resm(TWARN | TERRNO, "writing to %s failed", file);
 | |
| 			close(fildes);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		/* Set the file ptr to b'nning of file */
 | |
| 		if (lseek(fildes, 0, SEEK_SET) < 0) {
 | |
| 			tst_resm(TWARN, "lseek() failed on %s, error="
 | |
| 				 " %d", file, errno);
 | |
| 			close(fildes);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		/* Read the contents of file */
 | |
| 		if (read(fildes, read_buffer, sizeof(read_buffer)) > 0) {
 | |
| 			if (strcmp(read_buffer, write_buffer)) {
 | |
| 				tst_resm(TWARN, "Data read from %s and written "
 | |
| 					 "mismatch", file);
 | |
| 				close(fildes);
 | |
| 				return 1;
 | |
| 			} else {
 | |
| 				close(fildes);
 | |
| 				return 0;
 | |
| 			}
 | |
| 		} else {
 | |
| 			tst_resm(TWARN | TERRNO, "read() Fails on %s", file);
 | |
| 			close(fildes);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 	case 4:
 | |
| 		/* Validate MS_REMOUNT flag of mount call */
 | |
| 
 | |
| 		TEST(mount(device, mntpoint, fs_type, MS_REMOUNT, NULL));
 | |
| 		if (TEST_RETURN != 0) {
 | |
| 			tst_resm(TWARN | TTERRNO, "mount(2) failed to remount");
 | |
| 			return 1;
 | |
| 		} else {
 | |
| 			snprintf(file, PATH_MAX, "%stmp2", path_name);
 | |
| 			fd = open(file, O_CREAT | O_RDWR, S_IRWXU);
 | |
| 			if (fd == -1) {
 | |
| 				tst_resm(TWARN, "open(%s) on readonly "
 | |
| 					 "filesystem passed", file);
 | |
| 				return 1;
 | |
| 			} else {
 | |
| 				close(fd);
 | |
| 				return 0;
 | |
| 			}
 | |
| 		}
 | |
| 	case 5:
 | |
| 		/* Validate MS_NOSUID flag of mount call */
 | |
| 
 | |
| 		snprintf(file, PATH_MAX, "%smount03_setuid_test", path_name);
 | |
| 
 | |
| 		pid = fork();
 | |
| 		switch (pid) {
 | |
| 		case -1:
 | |
| 			tst_resm(TBROK | TERRNO, "fork failed");
 | |
| 			return 1;
 | |
| 		case 0:
 | |
| 			ltpuser = getpwnam(nobody_uid);
 | |
| 			if (setreuid(ltpuser->pw_uid, ltpuser->pw_uid) == -1)
 | |
| 				tst_resm(TWARN | TERRNO,
 | |
| 					 "seteuid() failed to change euid to %d",
 | |
| 					 ltpuser->pw_uid);
 | |
| 
 | |
| 			execlp(file, basename(file), NULL);
 | |
| 			exit(1);
 | |
| 		default:
 | |
| 			waitpid(pid, &status, 0);
 | |
| 			if (WIFEXITED(status)) {
 | |
| 				/* reset the setup_uid */
 | |
| 				if (status)
 | |
| 					return 0;
 | |
| 			}
 | |
| 			return 1;
 | |
| 		}
 | |
| 	case 6:
 | |
| 		/* Validate MS_NOATIME flag of mount call */
 | |
| 
 | |
| 		snprintf(file, PATH_MAX, "%satime", path_name);
 | |
| 		fd = open(file, O_CREAT | O_RDWR, S_IRWXU);
 | |
| 		if (fd == -1) {
 | |
| 			tst_resm(TWARN | TERRNO, "opening %s failed", file);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		if (write(fd, "TEST_MS_NOATIME", 15) != 15) {
 | |
| 			tst_resm(TWARN | TERRNO, "write %s failed", file);
 | |
| 			close(fd);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		if (fstat(fd, &file_stat) == -1) {
 | |
| 			tst_resm(TWARN | TERRNO, "stat %s failed #1", file);
 | |
| 			close(fd);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		atime = file_stat.st_atime;
 | |
| 
 | |
| 		sleep(1);
 | |
| 
 | |
| 		if (read(fd, readbuf, sizeof(readbuf)) == -1) {
 | |
| 			tst_resm(TWARN | TERRNO, "read %s failed", file);
 | |
| 			close(fd);
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		if (fstat(fd, &file_stat) == -1) {
 | |
| 			tst_resm(TWARN | TERRNO, "stat %s failed #2", file);
 | |
| 			close(fd);
 | |
| 			return 1;
 | |
| 		}
 | |
| 		close(fd);
 | |
| 
 | |
| 		if (file_stat.st_atime != atime) {
 | |
| 			tst_resm(TWARN, "access time is updated");
 | |
| 			return 1;
 | |
| 		}
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void setup(void)
 | |
| {
 | |
| 	char path[PATH_MAX];
 | |
| 	struct stat file_stat;
 | |
| 
 | |
| 	tst_sig(FORK, DEF_HANDLER, cleanup);
 | |
| 
 | |
| 	tst_require_root();
 | |
| 
 | |
| 	tst_tmpdir();
 | |
| 
 | |
| 	fs_type = tst_dev_fs_type();
 | |
| 	device = tst_acquire_device(cleanup);
 | |
| 
 | |
| 	if (!device)
 | |
| 		tst_brkm(TCONF, cleanup, "Failed to obtain block device");
 | |
| 
 | |
| 	tst_mkfs(cleanup, device, fs_type, NULL, NULL);
 | |
| 
 | |
| 	SAFE_MKDIR(cleanup, mntpoint, DIR_MODE);
 | |
| 
 | |
| 	if (getcwd(path_name, sizeof(path_name)) == NULL)
 | |
| 		tst_brkm(TBROK, cleanup, "getcwd failed");
 | |
| 
 | |
| 	if (chmod(path_name, DIR_MODE) != 0)
 | |
| 		tst_brkm(TBROK, cleanup, "chmod(%s, %#o) failed",
 | |
| 			 path_name, DIR_MODE);
 | |
| 
 | |
| 	strncpy(path, path_name, PATH_MAX);
 | |
| 	snprintf(path_name, PATH_MAX, "%s/%s/", path, mntpoint);
 | |
| 
 | |
| 	SAFE_MOUNT(cleanup, device, mntpoint, fs_type, 0, NULL);
 | |
| 	TST_RESOURCE_COPY(cleanup, "mount03_setuid_test", path_name);
 | |
| 
 | |
| 	snprintf(file, PATH_MAX, "%smount03_setuid_test", path_name);
 | |
| 	SAFE_STAT(cleanup, file, &file_stat);
 | |
| 
 | |
| 	if (file_stat.st_mode != SUID_MODE &&
 | |
| 	    chmod(file, SUID_MODE) < 0)
 | |
| 		tst_brkm(TBROK, cleanup,
 | |
| 			 "setuid for setuid_test failed");
 | |
| 	SAFE_UMOUNT(cleanup, mntpoint);
 | |
| 
 | |
| 	TEST_PAUSE;
 | |
| }
 | |
| 
 | |
| static void cleanup(void)
 | |
| {
 | |
| 	if (device)
 | |
| 		tst_release_device(device);
 | |
| 
 | |
| 	tst_rmdir();
 | |
| }
 |