[RFC PATCH 1/1] selinux-testsuite: Add filesystem tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Test filesystem permissions using mount(2)/umount(2).

>From kernels 5.5 filesystem { watch } is also tested.

Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx>
---
 defconfig                     |   6 +
 policy/Makefile               |   4 +
 policy/test_mount.te          | 235 ++++++++++++++
 tests/Makefile                |   4 +
 tests/mount/.gitignore        |   7 +
 tests/mount/Makefile          |   7 +
 tests/mount/fanotify_test.c   |  77 +++++
 tests/mount/grim_reaper.c     |  63 ++++
 tests/mount/may_create_test.c | 121 +++++++
 tests/mount/mount.c           | 130 ++++++++
 tests/mount/quotas_test.c     | 134 ++++++++
 tests/mount/statfs_test.c     |  65 ++++
 tests/mount/test              | 579 ++++++++++++++++++++++++++++++++++
 tests/mount/umount.c          |  85 +++++
 tests/mount/watch.cil         |   7 +
 15 files changed, 1524 insertions(+)
 create mode 100644 policy/test_mount.te
 create mode 100644 tests/mount/.gitignore
 create mode 100644 tests/mount/Makefile
 create mode 100644 tests/mount/fanotify_test.c
 create mode 100644 tests/mount/grim_reaper.c
 create mode 100644 tests/mount/may_create_test.c
 create mode 100644 tests/mount/mount.c
 create mode 100644 tests/mount/quotas_test.c
 create mode 100644 tests/mount/statfs_test.c
 create mode 100755 tests/mount/test
 create mode 100644 tests/mount/umount.c
 create mode 100644 tests/mount/watch.cil

diff --git a/defconfig b/defconfig
index 3bea332..c8d4762 100644
--- a/defconfig
+++ b/defconfig
@@ -88,3 +88,9 @@ CONFIG_TUN=m
 CONFIG_HAVE_PERF_EVENTS=y
 CONFIG_PERF_EVENTS=y
 CONFIG_TRACEPOINTS=y
+
+# Test mounting filesystems and their quotas.
+# This is not required for SELinux operation itself.
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=0
+CONFIG_QFMT_V2=y
diff --git a/policy/Makefile b/policy/Makefile
index f0de669..932909f 100644
--- a/policy/Makefile
+++ b/policy/Makefile
@@ -109,6 +109,10 @@ ifeq ($(shell grep -q perf_event $(POLDEV)/include/support/all_perms.spt && echo
 TARGETS += test_perf_event.te
 endif
 
+ifeq ($(shell grep -q filesystem $(POLDEV)/include/support/all_perms.spt && echo true),true)
+TARGETS += test_mount.te
+endif
+
 ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6))
 TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS))
 endif
diff --git a/policy/test_mount.te b/policy/test_mount.te
new file mode 100644
index 0000000..affaf00
--- /dev/null
+++ b/policy/test_mount.te
@@ -0,0 +1,235 @@
+#
+######### Test mount filesystem policy module ##########
+#
+attribute mountdomain;
+
+################# Test all functions ##########################
+type test_mount_t;
+domain_type(test_mount_t)
+unconfined_runs_test(test_mount_t)
+typeattribute test_mount_t testdomain;
+typeattribute test_mount_t mountdomain;
+
+allow test_mount_t self:capability { sys_admin };
+allow test_mount_t self:filesystem { mount remount quotamod relabelfrom relabelto unmount quotaget };
+allow test_mount_t self:dir { mounton add_name write };
+allow test_mount_t test_file_t:dir { mounton write remove_name rmdir };
+# Create test file
+allow test_mount_t self:dir { add_name write };
+allow test_mount_t self:file { create relabelfrom relabelto };
+
+fs_mount_all_fs(test_mount_t)
+fs_remount_all_fs(test_mount_t)
+fs_unmount_all_fs(test_mount_t)
+fs_relabelfrom_all_fs(test_mount_t)
+fs_get_xattr_fs_quotas(test_mount_t)
+files_search_all(test_mount_t)
+# Required for mount opts "rootcontext=system_u:object_r:test_mount_t:s0";
+fs_associate(test_mount_t)
+fs_getattr_xattr_fs(test_mount_t)
+
+# For running quotacheck(8)
+files_type(test_mount_t)
+# Update quotas
+fs_set_all_quotas(test_mount_t)
+allow test_mount_t self:file { quotaon };
+# Create test file and change context:
+allow test_mount_t test_mount_no_getattr_t:file { open read relabelto write };
+dontaudit test_mount_t kernel_t:process { setsched };
+
+#################### Deny filesystem { getattr } ######################
+type test_mount_no_getattr_t;
+domain_type(test_mount_no_getattr_t)
+unconfined_runs_test(test_mount_no_getattr_t)
+typeattribute test_mount_no_getattr_t testdomain;
+typeattribute test_mount_no_getattr_t mountdomain;
+
+allow test_mount_no_getattr_t self:capability { sys_admin };
+fs_mount_all_fs(test_mount_no_getattr_t)
+fs_unmount_all_fs(test_mount_no_getattr_t)
+fs_relabelfrom_all_fs(test_mount_no_getattr_t)
+fs_associate(test_mount_no_getattr_t)
+allow test_mount_no_getattr_t self:dir { mounton };
+allow test_mount_no_getattr_t test_file_t:dir { mounton write remove_name rmdir };
+dontaudit test_mount_no_getattr_t kernel_t:process { setsched };
+
+#################### Deny filesystem { remount } ######################
+type test_mount_no_remount_t;
+domain_type(test_mount_no_remount_t)
+unconfined_runs_test(test_mount_no_remount_t)
+typeattribute test_mount_no_remount_t testdomain;
+typeattribute test_mount_no_remount_t mountdomain;
+
+allow test_mount_no_remount_t self:capability { sys_admin };
+fs_mount_all_fs(test_mount_no_remount_t)
+fs_unmount_all_fs(test_mount_no_remount_t)
+fs_relabelfrom_all_fs(test_mount_no_remount_t)
+fs_associate(test_mount_no_remount_t)
+allow test_mount_no_remount_t self:dir { mounton };
+allow test_mount_no_remount_t test_file_t:dir { mounton write remove_name rmdir };
+dontaudit test_mount_no_remount_t kernel_t:process { setsched };
+
+#################### Deny filesystem { mount } ######################
+type test_mount_no_mount_t;
+domain_type(test_mount_no_mount_t)
+unconfined_runs_test(test_mount_no_mount_t)
+typeattribute test_mount_no_mount_t testdomain;
+typeattribute test_mount_no_mount_t mountdomain;
+
+allow test_mount_no_mount_t self:capability { sys_admin };
+fs_relabelfrom_all_fs(test_mount_no_mount_t)
+fs_associate(test_mount_no_mount_t)
+allow test_mount_no_mount_t self:dir { mounton };
+allow test_mount_no_mount_t test_file_t:dir { mounton write remove_name rmdir };
+dontaudit test_mount_no_mount_t kernel_t:process { setsched };
+
+#################### Deny filesystem { unmount } ######################
+type test_mount_no_unmount_t;
+domain_type(test_mount_no_unmount_t)
+unconfined_runs_test(test_mount_no_unmount_t)
+typeattribute test_mount_no_unmount_t testdomain;
+typeattribute test_mount_no_unmount_t mountdomain;
+
+allow test_mount_no_unmount_t self:capability { sys_admin };
+fs_mount_all_fs(test_mount_no_unmount_t)
+fs_relabelfrom_all_fs(test_mount_no_unmount_t)
+fs_associate(test_mount_no_unmount_t)
+allow test_mount_no_unmount_t self:dir { mounton };
+allow test_mount_no_unmount_t test_file_t:dir { mounton write remove_name rmdir };
+dontaudit test_mount_no_unmount_t kernel_t:process { setsched };
+
+#################### Deny filesystem { relabelfrom } ######################
+type test_mount_no_relabelfrom_t;
+domain_type(test_mount_no_relabelfrom_t)
+unconfined_runs_test(test_mount_no_relabelfrom_t)
+typeattribute test_mount_no_relabelfrom_t testdomain;
+typeattribute test_mount_no_relabelfrom_t mountdomain;
+
+allow test_mount_no_relabelfrom_t self:capability { sys_admin };
+fs_associate(test_mount_no_relabelfrom_t)
+allow test_mount_no_relabelfrom_t self:dir { mounton };
+allow test_mount_no_relabelfrom_t test_file_t:dir { mounton write remove_name rmdir };
+dontaudit test_mount_no_relabelfrom_t kernel_t:process { setsched };
+
+#################### Deny filesystem { relabelto } ######################
+type test_mount_no_relabelto_t;
+domain_type(test_mount_no_relabelto_t)
+unconfined_runs_test(test_mount_no_relabelto_t)
+typeattribute test_mount_no_relabelto_t testdomain;
+typeattribute test_mount_no_relabelto_t mountdomain;
+
+allow test_mount_no_relabelto_t self:capability { sys_admin };
+fs_mount_all_fs(test_mount_no_relabelto_t)
+fs_relabelfrom_all_fs(test_mount_no_relabelto_t)
+fs_associate(test_mount_no_relabelto_t)
+allow test_mount_no_relabelto_t self:dir { mounton };
+allow test_mount_no_relabelto_t test_file_t:dir { mounton write remove_name rmdir };
+dontaudit test_mount_no_relabelto_t kernel_t:process { setsched };
+
+#################### Deny filesystem { associate } ######################
+type test_mount_no_associate_t;
+type test_mount_no_associate1_t;
+domain_type(test_mount_no_associate_t)
+unconfined_runs_test(test_mount_no_associate_t)
+typeattribute test_mount_no_associate_t testdomain;
+typeattribute test_mount_no_associate_t mountdomain;
+
+allow test_mount_no_associate_t self:capability { sys_admin };
+allow test_mount_no_associate_t self:filesystem { relabelto mount relabelfrom };
+fs_mount_all_fs(test_mount_no_associate_t)
+fs_relabelfrom_all_fs(test_mount_no_associate_t)
+allow test_mount_no_associate_t self:dir { mounton };
+allow test_mount_no_associate_t test_file_t:dir { mounton write remove_name rmdir };
+dontaudit test_mount_no_associate_t kernel_t:process { setsched };
+
+########## Deny filesystem { associate } for create file ################
+type test_mount_no_associate_file_t;
+domain_type(test_mount_no_associate_file_t)
+unconfined_runs_test(test_mount_no_associate_file_t)
+typeattribute test_mount_no_associate_file_t testdomain;
+typeattribute test_mount_no_associate_file_t mountdomain;
+
+allow test_mount_no_associate_file_t self:capability { sys_admin };
+allow test_mount_no_associate_file_t self:filesystem { mount relabelfrom relabelto unmount associate };
+allow test_mount_no_associate_file_t self:dir { mounton add_name write };
+allow test_mount_no_associate_file_t test_file_t:dir { mounton write remove_name rmdir };
+
+fs_mount_all_fs(test_mount_no_associate_file_t)
+fs_unmount_all_fs(test_mount_no_associate_file_t)
+fs_relabelfrom_all_fs(test_mount_no_associate_file_t)
+fs_associate(test_mount_no_associate_file_t)
+fs_getattr_xattr_fs(test_mount_no_associate_file_t)
+dontaudit test_mount_no_associate_file_t kernel_t:process { setsched };
+
+# Create test file
+allow test_mount_no_associate_file_t self:file { create relabelfrom relabelto };
+############ hooks.c may_create() FILESYSTEM__ASSOCIATE #############
+# FOR: neverallow unlabeled_t test_mount_no_associate_file_t:filesystem { associate };
+allow test_mount_no_associate_file_t unconfined_t:file { open read write };
+allow test_mount_no_associate_file_t unlabeled_t:dir { add_name search write };
+allow test_mount_no_associate_file_t unlabeled_t:file { create open relabelfrom write };
+############ hooks.c selinux_inode_setxattr() FILESYSTEM__ASSOCIATE ##########
+# FOR: neverallow unconfined_t test_mount_no_associate_file_t:filesystem { associate };
+allow test_mount_no_associate_file_t unconfined_t:dir { add_name write };
+allow test_mount_no_associate_file_t unconfined_t:file { create relabelfrom relabelto };
+
+#################### Deny filesystem { quotamod } ######################
+type test_mount_no_quotamod_t;
+domain_type(test_mount_no_quotamod_t)
+unconfined_runs_test(test_mount_no_quotamod_t)
+typeattribute test_mount_no_quotamod_t testdomain;
+typeattribute test_mount_no_quotamod_t mountdomain;
+
+allow test_mount_no_quotamod_t self:capability { sys_admin };
+allow test_mount_no_quotamod_t self:filesystem { quotaget relabelto mount unmount};
+fs_mount_all_fs(test_mount_no_quotamod_t)
+fs_relabelfrom_all_fs(test_mount_no_quotamod_t)
+fs_associate(test_mount_no_quotamod_t)
+# Required as $private_path to quota files
+files_search_all(test_mount_no_quotamod_t)
+allow test_mount_no_quotamod_t self:dir { mounton };
+allow test_mount_no_quotamod_t test_file_t:dir { mounton write remove_name rmdir };
+dontaudit test_mount_no_quotamod_t kernel_t:process { setsched };
+
+#################### Deny filesystem { quotaget } ######################
+type test_mount_no_quotaget_t;
+domain_type(test_mount_no_quotaget_t)
+unconfined_runs_test(test_mount_no_quotaget_t)
+typeattribute test_mount_no_quotaget_t testdomain;
+typeattribute test_mount_no_quotaget_t mountdomain;
+
+allow test_mount_no_quotaget_t self:capability { sys_admin };
+allow test_mount_no_quotaget_t self:filesystem { quotamod relabelto mount unmount relabelfrom };
+allow test_mount_no_quotaget_t self:dir { mounton };
+allow test_mount_no_quotaget_t test_file_t:dir { mounton write remove_name rmdir };
+allow test_mount_no_quotaget_t self:file { quotaon };
+fs_mount_all_fs(test_mount_no_quotaget_t)
+fs_relabelfrom_all_fs(test_mount_no_quotaget_t)
+fs_associate(test_mount_no_quotaget_t)
+# Required as $private_path to quota files
+files_search_all(test_mount_no_quotaget_t)
+# For running quotacheck(8)
+files_type(test_mount_no_quotaget_t)
+dontaudit test_mount_no_quotaget_t kernel_t:process { setsched };
+
+#################### Deny filesystem { watch } ######################
+type test_mount_no_watch_t;
+domain_type(test_mount_no_watch_t)
+unconfined_runs_test(test_mount_no_watch_t)
+typeattribute test_mount_no_watch_t testdomain;
+typeattribute test_mount_no_watch_t mountdomain;
+
+allow test_mount_no_watch_t self:capability { sys_admin };
+allow test_mount_no_watch_t self:filesystem { associate relabelto mount unmount relabelfrom };
+allow test_mount_no_watch_t self:dir { mounton };
+allow test_mount_no_watch_t test_file_t:dir { mounton write remove_name rmdir };
+fs_mount_all_fs(test_mount_no_watch_t)
+fs_relabelfrom_all_fs(test_mount_no_watch_t)
+fs_associate(test_mount_no_watch_t)
+dontaudit test_mount_no_watch_t kernel_t:process { setsched };
+
+#
+########### Allow these domains to be entered from sysadm domain ############
+#
+miscfiles_domain_entry_test_files(mountdomain)
+userdom_sysadm_entry_spec_domtrans_to(mountdomain)
diff --git a/tests/Makefile b/tests/Makefile
index 9a890be..3b968d1 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -87,6 +87,10 @@ ifeq ($(shell grep -q perf_event $(POLDEV)/include/support/all_perms.spt && echo
 SUBDIRS += perf_event
 endif
 
+ifeq ($(shell grep -q filesystem $(POLDEV)/include/support/all_perms.spt && echo true),true)
+SUBDIRS += mount
+endif
+
 ifeq ($(DISTRO),RHEL4)
     SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp_nosuid overlay unix_socket, $(SUBDIRS))
 endif
diff --git a/tests/mount/.gitignore b/tests/mount/.gitignore
new file mode 100644
index 0000000..d8f0df7
--- /dev/null
+++ b/tests/mount/.gitignore
@@ -0,0 +1,7 @@
+mount
+umount
+quotas_test
+statfs_test
+fanotify_test
+may_create_test
+grim_reaper
diff --git a/tests/mount/Makefile b/tests/mount/Makefile
new file mode 100644
index 0000000..1f74425
--- /dev/null
+++ b/tests/mount/Makefile
@@ -0,0 +1,7 @@
+TARGETS = mount umount quotas_test statfs_test fanotify_test may_create_test grim_reaper
+LDLIBS += -lselinux
+
+all: $(TARGETS)
+
+clean:
+	rm -f $(TARGETS)
diff --git a/tests/mount/fanotify_test.c b/tests/mount/fanotify_test.c
new file mode 100644
index 0000000..0dd60bf
--- /dev/null
+++ b/tests/mount/fanotify_test.c
@@ -0,0 +1,77 @@
+#define _GNU_SOURCE 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/fanotify.h>
+#include <selinux/selinux.h>
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-v] -t\n"
+		"Where:\n\t"
+		"-t  Target path\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	int mask = FAN_OPEN, flags = FAN_MARK_ADD | FAN_MARK_FILESYSTEM;
+	int fd, result, opt, save_err;
+	char *context, *tgt = NULL;
+	bool verbose = false;
+
+	while ((opt = getopt(argc, argv, "t:v")) != -1) {
+		switch (opt) {
+		case 't':
+			tgt = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!tgt)
+		print_usage(argv[0]);
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			exit(-1);
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	fd = fanotify_init(FAN_CLASS_CONTENT, O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "fanotify_init(2) Failed: %s\n",
+			strerror(errno));
+		exit(-1);
+	}
+
+	result = fanotify_mark(fd, flags, mask, AT_FDCWD, tgt);
+	save_err = errno;
+	if (result < 0) {
+		fprintf(stderr, "fanotify_mark(2) Failed: %s\n",
+			strerror(errno));
+		close(fd);
+		return save_err;
+	}
+
+	if (verbose)
+		printf("Set fanotify_mark(2) on filesystem: %s\n", tgt);
+
+	close(fd);
+	return 0;
+}
diff --git a/tests/mount/grim_reaper.c b/tests/mount/grim_reaper.c
new file mode 100644
index 0000000..0105ab6
--- /dev/null
+++ b/tests/mount/grim_reaper.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/mount.h>
+#include <selinux/selinux.h>
+
+#define WAIT_COUNT 60
+#define USLEEP_TIME 10000
+
+/* Remove any mounts associated with the loop device in argv[1] */
+int main(int argc, char *argv[])
+{
+	FILE *fp;
+	size_t len;
+	ssize_t num;
+	int index = 0, i, result = 0;
+	char *mount_info[2];
+	char *buf = NULL, *item;
+
+	if (!argv[1])
+		return -1;
+
+	fp = fopen("/proc/mounts", "re");
+	if (!fp) {
+		fprintf(stderr, "Failed to open /proc/mounts: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	while ((num = getline(&buf, &len, fp)) != -1) {
+		index = 0;
+		item = strtok(buf, " ");
+		while (item != NULL) {
+			mount_info[index] = item;
+			index++;
+			if (index == 2)
+				break;
+			item = strtok(NULL, " ");
+		}
+
+		if (strcmp(mount_info[0], argv[1]) == 0) {
+			for (i = 0; i < WAIT_COUNT; i++) {
+				result = umount(mount_info[1]);
+				if (!result)
+					break;
+
+				if (errno != EBUSY) {
+					fprintf(stderr, "Failed umount(2): %s\n",
+						strerror(errno));
+					break;
+				}
+				usleep(USLEEP_TIME);
+			}
+		}
+	}
+
+	free(buf);
+	fclose(fp);
+	return result;
+}
diff --git a/tests/mount/may_create_test.c b/tests/mount/may_create_test.c
new file mode 100644
index 0000000..9a99d8d
--- /dev/null
+++ b/tests/mount/may_create_test.c
@@ -0,0 +1,121 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <linux/unistd.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-v] -t -f\n"
+		"Where:\n\t"
+		"-t  Type for context of created file\n\t"
+		"-f  File to create\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char **argv)
+{
+	int opt, result, fd, save_err;
+	char *context, *newcon, *orgfcon, *type = NULL, *file = NULL;
+	bool verbose = false;
+	context_t con_t;
+
+	while ((opt = getopt(argc, argv, "t:f:v")) != -1) {
+		switch (opt) {
+		case 't':
+			type = optarg;
+			break;
+		case 'f':
+			file = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!type || !file)
+		print_usage(argv[0]);
+
+	result = getcon(&context);
+	if (result < 0) {
+		fprintf(stderr, "Failed to obtain process context\n");
+		exit(-1);
+	}
+
+	/* Build new file context */
+	con_t = context_new(context);
+	if (!con_t) {
+		fprintf(stderr, "Unable to create context structure\n");
+		exit(-1);
+	}
+
+	if (context_type_set(con_t, type)) {
+		fprintf(stderr, "Unable to set new type\n");
+		exit(-1);
+	}
+
+	newcon = context_str(con_t);
+	if (!newcon) {
+		fprintf(stderr, "Unable to obtain new context string\n");
+		exit(-1);
+	}
+
+	if (verbose) {
+		printf("Process context:\n\t%s\n", context);
+		printf("is creating test file:\n\t%s\n", file);
+		printf("and changing its context to:\n\t%s\n", newcon);
+	}
+
+	/* hooks.c may_create() FILESYSTEM__ASSOCIATE */
+	fd = creat(file, O_RDWR);
+	save_err = errno;
+	if (fd < 0) {
+		fprintf(stderr, "creat(2) Failed: %s\n", strerror(errno));
+		result = save_err;
+		goto err;
+	}
+
+	result = fgetfilecon(fd, &orgfcon);
+	if (result < 0) {
+		fprintf(stderr, "fgetfilecon(3) Failed: %s\n", strerror(errno));
+		result = -1;
+		goto err;
+	}
+
+	if (verbose)
+		printf("Current test file context is: %s\n", orgfcon);
+
+	free(orgfcon);
+
+	/* hooks.c selinux_inode_setxattr() FILESYSTEM__ASSOCIATE */
+	result = fsetfilecon(fd, newcon);
+	save_err = errno;
+	if (result < 0) {
+		fprintf(stderr, "fsetfilecon(3) Failed: %s\n", strerror(errno));
+		result = save_err;
+		goto err;
+	}
+
+	fd = open(file, O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "open(2) Failed: %s\n", strerror(errno));
+		result = -1;
+	}
+
+err:
+	free(context);
+	free(newcon);
+	close(fd);
+	return result;
+
+}
diff --git a/tests/mount/mount.c b/tests/mount/mount.c
new file mode 100644
index 0000000..034f0ec
--- /dev/null
+++ b/tests/mount/mount.c
@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/mount.h>
+#include <selinux/selinux.h>
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-s src] -t tgt [-f fs_type] [-o options] [-bmprv]\n"
+		"Where:\n\t"
+		"-s  Source path\n\t"
+		"-t  Target path\n\t"
+		"-f  Filesystem type\n\t"
+		"-o  Options list (comma separated list)\n\t"
+		"Zero or one of the following flags:\n\t"
+		"\t-b  MS_BIND\n\t"
+		"\t-m  MS_MOVE\n\t"
+		"\t-p  MS_PRIVATE\n\t"
+		"\t-r  MS_REMOUNT\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+static int ck_mount(char *mntpoint)
+{
+	int result = 0;
+	FILE *fp;
+	size_t len;
+	ssize_t num;
+	char *buf = NULL;
+
+	fp = fopen("/proc/mounts", "re");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open /proc/mounts: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	while ((num = getline(&buf, &len, fp)) != -1) {
+		if (strstr(buf, mntpoint) != NULL) {
+			result = 1;
+			break;
+		}
+	}
+
+	free(buf);
+	fclose(fp);
+	return result;
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, result, save_err, flags = 0;
+	char *context, *src = NULL, *tgt = NULL, *fs_type = NULL, *opts = NULL;
+	bool verbose = false;
+
+	while ((opt = getopt(argc, argv, "s:t:f:o:pbmrv")) != -1) {
+		switch (opt) {
+		case 's':
+			src = optarg;
+			break;
+		case 't':
+			tgt = optarg;
+			break;
+		case 'f':
+			fs_type = optarg;
+			break;
+		case 'o':
+			opts = optarg;
+			break;
+		case 'b':
+			flags = MS_BIND;
+			break;
+		case 'p':
+			flags = MS_PRIVATE;
+			break;
+		case 'm':
+			flags = MS_MOVE;
+			break;
+		case 'r':
+			flags = MS_REMOUNT;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!tgt)
+		print_usage(argv[0]);
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			return -1;
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	if (verbose)
+		printf("Mounting\n\tsrc: %s\n\ttgt: %s\n\tfs_type: %s flags: 0x%x\n\topts: %s\n",
+		       src, tgt, fs_type, flags, opts);
+
+	result = mount(src, tgt, fs_type, flags, opts);
+	save_err = errno;
+	if (result < 0) {
+		fprintf(stderr, "Failed mount(2): %s\n", strerror(errno));
+		return save_err;
+	}
+
+	if (flags == MS_MOVE) {
+		if (!ck_mount(src) && ck_mount(tgt)) {
+			if (verbose)
+				printf("MS_MOVE: Moved mountpoint\n");
+		} else {
+			fprintf(stderr, "MS_MOVE: Move mountpoint failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
diff --git a/tests/mount/quotas_test.c b/tests/mount/quotas_test.c
new file mode 100644
index 0000000..34aaca9
--- /dev/null
+++ b/tests/mount/quotas_test.c
@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/quota.h>
+#include <selinux/selinux.h>
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s -s src -t tgt\n"
+		"Where:\n\t"
+		"-s  Source path (e.g. /dev/loop0)\n\t"
+		"-t  Target quota file (Full path with either 'aquota.user'\n\t"
+		"    or 'aquota.group' appended)\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, result, qcmd, save_err, test_id = geteuid();
+	char *context, *src = NULL, *tgt = NULL;
+	bool verbose = false;
+	char fmt_buf[2];
+
+	while ((opt = getopt(argc, argv, "s:t:v")) != -1) {
+		switch (opt) {
+		case 's':
+			src = optarg;
+			break;
+		case 't':
+			tgt = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!src || !tgt)
+		print_usage(argv[0]);
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			return -1;
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	if (strstr(tgt, "aquota.user") != NULL) {
+		qcmd = QCMD(Q_QUOTAON, USRQUOTA);
+		result = quotactl(qcmd, src, QFMT_VFS_V0, tgt);
+		save_err = errno;
+		if (result < 0) {
+			fprintf(stderr, "quotactl(Q_QUOTAON, USRQUOTA) Failed: %s\n",
+				strerror(errno));
+			return save_err;
+		}
+		if (verbose)
+			printf("User Quota - ON\n");
+
+		qcmd = QCMD(Q_GETFMT, USRQUOTA);
+		result = quotactl(qcmd, src, test_id, fmt_buf);
+		save_err = errno;
+		if (result < 0) {
+			fprintf(stderr, "quotactl(Q_GETFMT, USRQUOTA) Failed: %s\n",
+				strerror(errno));
+			return save_err;
+		}
+		if (verbose)
+			printf("User Format: 0x%x\n", fmt_buf[0]);
+
+		qcmd = QCMD(Q_QUOTAOFF, USRQUOTA);
+		result = quotactl(qcmd, src, QFMT_VFS_V0, tgt);
+		save_err = errno;
+		if (result < 0) {
+			fprintf(stderr, "quotactl(Q_QUOTAOFF, USRQUOTA) Failed: %s\n",
+				strerror(errno));
+			return save_err;
+		}
+		if (verbose)
+			printf("User Quota - OFF\n");
+
+		return 0;
+
+	} else if (strstr(tgt, "aquota.group") != NULL) {
+		qcmd = QCMD(Q_QUOTAON, GRPQUOTA);
+		result = quotactl(qcmd, src, QFMT_VFS_V0, tgt);
+		save_err = errno;
+		if (result < 0) {
+			fprintf(stderr, "quotactl(Q_QUOTAON, GRPQUOTA) Failed: %s\n",
+				strerror(errno));
+			return save_err;
+		}
+		if (verbose)
+			printf("Group Quota - ON\n");
+
+		qcmd = QCMD(Q_GETFMT, GRPQUOTA);
+		result = quotactl(qcmd, src, test_id, fmt_buf);
+		save_err = errno;
+		if (result < 0) {
+			fprintf(stderr, "quotactl(Q_GETFMT, GRPQUOTA) Failed: %s\n",
+				strerror(errno));
+			return save_err;
+		}
+		if (verbose)
+			printf("Group Format: 0x%x\n", fmt_buf[0]);
+
+		qcmd = QCMD(Q_QUOTAOFF, GRPQUOTA);
+		result = quotactl(qcmd, src, QFMT_VFS_V0, tgt);
+		save_err = errno;
+		if (result < 0) {
+			fprintf(stderr, "quotactl(Q_QUOTAOFF, GRPQUOTA) Failed: %s\n",
+				strerror(errno));
+			return save_err;
+		}
+		if (verbose)
+			printf("Group Quota - OFF\n");
+
+		return 0;
+	}
+
+	fprintf(stderr, "Required %s to specify 'aquota.user' or 'aquota.group' file\n",
+		tgt);
+	return -1;
+}
diff --git a/tests/mount/statfs_test.c b/tests/mount/statfs_test.c
new file mode 100644
index 0000000..5de49b1
--- /dev/null
+++ b/tests/mount/statfs_test.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/statfs.h>
+#include <selinux/selinux.h>
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-v] -t\n"
+		"Where:\n\t"
+		"-t  Target path\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, result, save_err;
+	char *context, *tgt = NULL;
+	bool verbose = false;
+	struct statfs statfs_t;
+
+	while ((opt = getopt(argc, argv, "t:v")) != -1) {
+		switch (opt) {
+		case 't':
+			tgt = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!tgt)
+		print_usage(argv[0]);
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			return -1;
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	result = statfs(tgt, &statfs_t);
+	save_err = errno;
+	if (result < 0) {
+		fprintf(stderr, "statfs(2) Failed: %s\n", strerror(errno));
+		return save_err;
+	}
+
+	if (verbose)
+		printf("statfs(2) returned magic filesystem: 0x%lx\n",
+		       statfs_t.f_type);
+
+	return 0;
+}
diff --git a/tests/mount/test b/tests/mount/test
new file mode 100755
index 0000000..90c5a7c
--- /dev/null
+++ b/tests/mount/test
@@ -0,0 +1,579 @@
+#!/usr/bin/perl
+use Test::More;
+
+BEGIN {
+    $basedir = $0;
+    $basedir =~ s|(.*)/[^/]*|$1|;
+
+    $test_count = 41;
+
+    # Allow info to be shown.
+    $v = $ARGV[0];
+    if ($v) {
+        if ( $v ne "-v" ) {
+            plan skip_all => "Invalid option (use -v)";
+        }
+    }
+    else {
+        $v = " ";
+    }
+
+    # From kernel 5.5 support for fanotify(7) with filesystem { watch }
+    $kvercur = `uname -r`;
+    chomp($kvercur);
+    $kverminstream = "5.5";
+
+    $result = `$basedir/../kvercmp $kvercur $kverminstream`;
+    if ( $result > 0 ) {
+        $test_watch = 1;
+        $test_count += 4;
+    }
+
+    plan tests => $test_count;
+}
+
+# context     - Useful when mounting filesystems that do not support extended
+#               attributes
+# fscontext   - Sets the overarching filesystem label to a specific security
+#               context. This filesystem label is separate from the individual
+#               labels on the files.
+# defcontext  - Set default security context for unlabeled files.
+#               This overrides the value set for unlabeled files in policy
+#               and requires a filesystem that supports xattr labeling.
+# rootcontext - Explicitly label the root inode of the filesystem being
+#               mounted before that filesystem or inode becomes visible
+#               to userspace.
+
+# mount(2) MS_BIND | MS_PRIVATE requires an absolute path to a private mount
+# point before MS_MOVE
+$cwd = `pwd 2>/dev/null`;
+chomp($cwd);
+$private_path = "$cwd";
+if ( $basedir eq "." ) {
+    $private_path = "$cwd/mntpoint";
+}
+else {
+    $private_path = "$cwd/$basedir/mntpoint";
+}
+
+# Set filesystem type
+$fs_type = "ext4";
+
+# For list of devices used
+$device_count = 0;
+
+sub make_fs {
+    print "Create $basedir/fstest with dd\n";
+    $result = system(
+        "dd if=/dev/zero of=$basedir/fstest bs=1024 count=10240 2>/dev/null");
+    if ( $result != 0 ) {
+        print "dd failed to create fstest\n";
+        exit -1;
+    }
+
+    print "Finding free /dev/loop entry\n";
+    $dev = `losetup -f 2>/dev/null`;
+    chomp($dev);
+    if ( $dev eq "" ) {
+        print "losetup failed to obtain /dev/loop entry\n";
+        cleanup();
+        exit -1;
+    }
+
+    # Keep list of devices for cleanup later
+    if ( $device_count eq 0 ) {
+        $device_list[$device_count] = $dev;
+        $device_count += 1;
+    }
+    elsif ( $dev ne $device_list[ $device_count - 1 ] ) {
+            $device_list[$device_count] = $dev;
+            $device_count += 1;
+    }
+
+    print "Attaching $basedir/fstest to $dev\n";
+    $result = system("losetup $dev $basedir/fstest 2>/dev/null");
+    if ( $result != 0 ) {
+        print "Failed to attach $basedir/fstest to $dev\n";
+        cleanup();
+        exit -1;
+    }
+
+    print "Make $fs_type filesystem on $dev\n";
+    $result = system("mkfs.$fs_type $dev >& /dev/null");
+    if ( $result != 0 ) {
+        system("losetup -d $dev 2>/dev/null");
+        cleanup();
+        print "mkfs.$fs_type failed to create filesystem on $dev\n";
+        exit -1;
+    }
+}
+
+sub mk_mntpoint_1 {
+    system("rm -rf $private_path/mp1 2>/dev/null");
+    system("mkdir -p $private_path/mp1 2>/dev/null");
+}
+
+sub mk_mntpoint_2 {
+    system("rm -rf $private_path/mp2 2>/dev/null");
+    system("mkdir -p $private_path/mp2 2>/dev/null");
+}
+
+sub cleanup {
+    system("rm -rf $basedir/fstest 2>/dev/null");
+    system("rm -rf $basedir/mntpoint 2>/dev/null");
+}
+
+sub cleanup1 {
+    system("losetup -d $dev 2>/dev/null");
+    system("rm -rf $basedir/fstest 2>/dev/null");
+    system("rm -rf $basedir/mntpoint 2>/dev/null");
+}
+
+############### Test Basic Mount/Unmount ##########################
+cleanup();
+mk_mntpoint_1();
+make_fs();
+$mount_opts1 =
+  "quota,usrquota,grpquota,defcontext=system_u:object_r:test_mount_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$mount_opts1\n";
+$result = system(
+"runcon -t test_mount_t $basedir/mount -s $dev -t $private_path/mp1 -f $fs_type -o $mount_opts1 $v"
+);
+ok( $result eq 0 );
+
+print "Then remount\n";
+$result = system(
+"runcon -t test_mount_t $basedir/mount -r -s $dev -t $private_path/mp1 -f $fs_type -o $mount_opts1 $v"
+);
+ok( $result eq 0 );
+
+print "Running quotacheck(8) to init user/group quota files\n";
+$result = system("quotacheck -ugF vfsv0 $private_path/mp1");
+ok( $result eq 0 );
+
+print "Toggle User & Group quotas on/off\n";
+$result = system(
+"runcon -t test_mount_t $basedir/quotas_test -s $dev -t $private_path/mp1/aquota.user $v"
+);
+ok( $result eq 0 );
+$result = system(
+"runcon -t test_mount_t $basedir/quotas_test -s $dev -t $private_path/mp1/aquota.group $v"
+);
+ok( $result eq 0 );
+
+print "Get statfs(2)\n";
+$result =
+  system("runcon -t test_mount_t $basedir/statfs_test -t $basedir/mntpoint $v");
+ok( $result eq 0 );
+
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_mount_t $basedir/may_create_test -t test_mount_no_getattr_t -f $private_path/mp1/test_file $v"
+  );
+ok( $result eq 0 );
+
+if ($test_watch) {
+    print "fanotify(7) test\n";
+    $result = system(
+"runcon -t test_mount_t $basedir/fanotify_test $v -t $basedir/mntpoint/mp1"
+    );
+    ok( $result eq 0 );
+}
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system("runcon -t test_mount_t $basedir/umount -t $private_path/mp1 $v");
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Test Move Mount ##########################
+make_fs();
+$mount_opts2 =
+  "quota,usrquota,grpquota,rootcontext=system_u:object_r:test_mount_t:s0";
+system("mkdir -p $private_path 2>/dev/null");
+
+print "Set mount MS_BIND on filesystem\n";
+$result = system(
+"runcon -t test_mount_t $basedir/mount -s $private_path -t $private_path -b $v"
+);
+ok( $result eq 0 );
+
+print "Set mount MS_PRIVATE on filesystem\n";
+$result =
+  system("runcon -t test_mount_t $basedir/mount -t $private_path -p $v");
+ok( $result eq 0 );
+
+mk_mntpoint_1();
+mk_mntpoint_2();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$mount_opts2\n";
+$result = system(
+"runcon -t test_mount_t $basedir/mount -s $dev -t $private_path/mp1 -f $fs_type -o $mount_opts2 $v"
+);
+ok( $result eq 0 );
+
+print "Set mount MS_MOVE on filesystem\n";
+$result = system(
+"runcon -t test_mount_t $basedir/mount -s $private_path/mp1 -t $private_path/mp2 -m  $v"
+);
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp2\n";
+$result =
+  system("runcon -t test_mount_t $basedir/umount -t $private_path/mp2 $v");
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint\n";
+$result = system("runcon -t test_mount_t $basedir/umount -t $private_path $v");
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { relabelfrom } ##########################
+# hooks.c may_context_mount_sb_relabel() FILESYSTEM__RELABELFROM
+
+$opts_no_relabelfrom =
+"defcontext=system_u:object_r:test_mount_no_relabelfrom_t:s0,fscontext=system_u:object_r:test_mount_no_relabelfrom_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_relabelfrom\n";
+$result = system(
+"runcon -t test_mount_no_relabelfrom_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_relabelfrom $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { relabelto } ##########################
+# hooks.c may_context_mount_sb_relabel() FILESYSTEM__RELABELTO
+
+$opts_no_relabelto = "fscontext=system_u:object_r:test_mount_no_relabelto_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_relabelto\n";
+$result = system(
+"runcon -t test_mount_no_relabelto_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_relabelto $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { relabelfrom } ##########################
+# hooks.c may_context_mount_inode_relabel() FILESYSTEM__RELABELFROM
+
+$opts_no_relabelfrom =
+  "rootcontext=system_u:object_r:test_mount_no_relabelfrom_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_relabelfrom\n";
+$result = system(
+"runcon -t test_mount_no_relabelfrom_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_relabelfrom $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { associate } ##########################
+# hooks.c may_context_mount_inode_relabel() FILESYSTEM__ASSOCIATE
+
+$opts_no_associate =
+"defcontext=system_u:object_r:test_mount_no_associate_t:s0,fscontext=system_u:object_r:test_mount_no_associate_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_associate\n";
+$result = system(
+"runcon -t test_mount_no_associate_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_associate $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { associate } ##########################
+# hooks.c may_create() FILESYSTEM__ASSOCIATE
+cleanup();
+mk_mntpoint_1();
+make_fs();
+$opts_no_associate_file =
+  "fscontext=system_u:object_r:test_mount_no_associate_file_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_associate_file\n";
+$result = system(
+"runcon -t test_mount_no_associate_file_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_associate_file $v"
+);
+ok( $result eq 0 );
+
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_mount_no_associate_file_t $basedir/may_create_test -t unconfined_t -f $basedir/mntpoint/mp1/test_file $v 2>&1"
+  );
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_mount_no_associate_file_t $basedir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { quotamod } ##########################
+# hooks.c selinux_quotactl() FILESYSTEM__QUOTAMOD
+
+$opts_no_quotamod =
+"quota,usrquota,grpquota,fscontext=system_u:object_r:test_mount_no_quotamod_t:s0";
+mk_mntpoint_1();
+make_fs();
+system("mkdir -p $private_path 2>/dev/null");
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_quotamod\n";
+$result = system(
+"runcon -t test_mount_no_quotamod_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_quotamod $v 2>&1"
+);
+ok( $result eq 0 );
+
+# No need to run quotacheck(8) as never gets far enough to read quota file
+print "Toggle User & Group quotas on/off\n";    # Must have full path
+$result = system(
+"runcon -t test_mount_no_quotamod_t $basedir/quotas_test -s $dev -t $private_path/mp1/aquota.user $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_mount_no_quotamod_t $basedir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { quotaget } ##########################
+# hooks.c selinux_quotactl() FILESYSTEM__QUOTAGET
+
+$opts_no_quotaget =
+"quota,usrquota,grpquota,context=system_u:object_r:test_mount_no_quotaget_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_quotaget\n";
+$result = system(
+"runcon -t test_mount_no_quotaget_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_quotaget $v"
+);
+ok( $result eq 0 );
+
+print "Running quotacheck(8) to init user/group quota files\n";
+$result = system("quotacheck -ugF vfsv0 $private_path/mp1");
+ok( $result eq 0 );
+
+print "Toggle User & Group quotas on/off\n";    # Must have full path
+$result = system(
+"runcon -t test_mount_no_quotaget_t $basedir/quotas_test -s $dev -t $private_path/mp1/aquota.user $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_mount_no_quotaget_t $basedir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { mount } ##########################
+# hooks.c selinux_sb_kern_mount() FILESYSTEM__MOUNT
+
+$opts_no_mount = "rootcontext=system_u:object_r:test_mount_no_mount_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_mount\n";
+$result = system(
+"runcon -t test_mount_no_mount_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_mount $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { getattr } ##########################
+# hooks.c selinux_sb_statfs() FILESYSTEM__GETATTR
+
+$opts_no_getattr = "rootcontext=system_u:object_r:test_mount_no_getattr_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_getattr\n";
+$result = system(
+"runcon -t test_mount_no_getattr_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_getattr $v"
+);
+ok( $result eq 0 );
+
+$result = system(
+"runcon -t test_mount_no_getattr_t $basedir/statfs_test -t $basedir/mntpoint $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_mount_no_getattr_t $basedir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { remount } ##########################
+# hooks.c selinux_mount() FILESYSTEM__REMOUNT
+
+$opts_no_remount = "rootcontext=system_u:object_r:test_mount_no_remount_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_remount\n";
+$result = system(
+"runcon -t test_mount_no_remount_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_remount $v"
+);
+ok( $result eq 0 );
+
+print "Then remount\n";
+$result = system(
+"runcon -t test_mount_no_remount_t $basedir/mount -r -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_remount $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_mount_no_remount_t $basedir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { unmount } ##########################
+# hooks.c selinux_umount() FILESYSTEM__UNMOUNT
+
+$opts_no_unmount = "rootcontext=system_u:object_r:test_mount_no_unmount_t:s0";
+mk_mntpoint_1();
+make_fs();
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_unmount\n";
+$result = system(
+"runcon -t test_mount_no_unmount_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_unmount $v"
+);
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_mount_no_unmount_t $basedir/umount -t $basedir/mntpoint/mp1 $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+# Make sure it does get unmounted
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system("runcon -t test_mount_t $basedir/umount -t $basedir/mntpoint/mp1 $v");
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { associate }  ##########################
+# hooks.c selinux_inode_setxattr() FILESYSTEM__ASSOCIATE
+cleanup();
+mk_mntpoint_1();
+make_fs();
+$opts_no_associate_file =
+"defcontext=system_u:object_r:test_mount_no_associate_file_t:s0,fscontext=system_u:object_r:test_mount_no_associate_file_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_associate_file\n";
+$result = system(
+"runcon -t test_mount_no_associate_file_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_associate_file $v"
+);
+ok( $result eq 0 );
+
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_mount_no_associate_file_t $basedir/may_create_test -t unconfined_t -f $basedir/mntpoint/mp1/test_file $v 2>&1"
+  );
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_mount_no_associate_file_t $basedir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1();
+
+############### Deny filesystem { watch }  ##########################
+# hooks.c selinux_path_notify() FILESYSTEM__WATCH
+if ($test_watch) {
+    cleanup();
+    mk_mntpoint_1();
+    make_fs();
+    $opts_no_watch = "context=system_u:object_r:test_mount_no_watch_t:s0";
+
+    print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+    print "Using mount options:\n\t$opts_no_watch\n";
+    $result = system(
+"runcon -t test_mount_no_watch_t $basedir/mount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_watch $v"
+    );
+    ok( $result eq 0 );
+
+    print "test_fanotify\n";
+    $result = system(
+"runcon -t test_mount_no_watch_t $basedir/fanotify_test $v -t $basedir/mntpoint/mp1 2>&1"
+    );
+    ok( $result >> 8 eq 13 );
+
+    print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+    $result = system(
+"runcon -t test_mount_no_watch_t $basedir/umount -t $basedir/mntpoint/mp1 $v"
+    );
+    ok( $result eq 0 );
+
+    print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+    cleanup1();
+}
+
+# Cleanup any attached /dev/loop entries
+foreach my $n (@device_list) {
+    system("$basedir/grim_reaper $n 2>/dev/null");
+}
+
+exit;
diff --git a/tests/mount/umount.c b/tests/mount/umount.c
new file mode 100644
index 0000000..50bb3fc
--- /dev/null
+++ b/tests/mount/umount.c
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/mount.h>
+#include <selinux/selinux.h>
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-v] [-t]\n"
+		"Where:\n\t"
+		"-t  Target path\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+#define WAIT_COUNT 60
+#define USLEEP_TIME 100000
+
+int main(int argc, char *argv[])
+{
+	char *context, *tgt = NULL;
+	int opt, result, i, save_err;
+	bool verbose = false;
+
+	while ((opt = getopt(argc, argv, "t:v")) != -1) {
+		switch (opt) {
+		case 't':
+			tgt = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!tgt)
+		print_usage(argv[0]);
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			exit(-1);
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	/*
+	 * umount(2) will sometimes return EBUSY when other tasks are
+	 * checking mounts so wait around before bailing out.
+	 */
+	for (i = 0; i < WAIT_COUNT; i++) {
+		result = umount(tgt);
+		save_err = errno;
+		if (!result) {
+			if (verbose)
+				printf("Unmounted: %s\n", tgt);
+
+			return 0;
+		}
+
+		if (verbose && save_err == EBUSY)
+			printf("umount(2) returned EBUSY %d times\n",
+			       i + 1);
+
+		if (save_err != EBUSY) {
+			fprintf(stderr, "Failed umount(2): %s\n",
+				strerror(save_err));
+			return save_err;
+		}
+		usleep(USLEEP_TIME);
+	}
+
+	fprintf(stderr, "Failed to umount(2) after %d retries with: %s\n",
+		WAIT_COUNT, strerror(save_err));
+
+	return save_err;
+}
diff --git a/tests/mount/watch.cil b/tests/mount/watch.cil
new file mode 100644
index 0000000..14d41ab
--- /dev/null
+++ b/tests/mount/watch.cil
@@ -0,0 +1,7 @@
+(common filesystem (watch))
+(classcommon filesystem filesystem)
+
+(allow test_mount_t self(filesystem (watch)))
+
+; Until 'fs_watch_all_fs(test_mount_t)' in Policy use:
+(allow test_mount_t fs_t (filesystem (watch)))
-- 
2.23.0




[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux