Message ID | 20191215170620.73506-2-richard_c_haines@btinternet.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | selinux-testsuite: Add filesystem tests | expand |
On 12/15/19 12:06 PM, Richard Haines wrote: > 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@btinternet.com> This didn't pass travis-ci, looks like a combination of failing check-syntax and FAN_MARK_FILESYSTEM not being defined (maybe the kernel headers are too old in the base distro?). > --- > 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))) >
On 12/16/19 9:09 AM, Stephen Smalley wrote: > On 12/15/19 12:06 PM, Richard Haines wrote: >> 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@btinternet.com> > > This didn't pass travis-ci, looks like a combination of failing > check-syntax and FAN_MARK_FILESYSTEM not being defined (maybe the kernel > headers are too old in the base distro?). Possibly we need to install our own kernel headers for the testsuite? It looks like you are exercising the various *context= mount options with respect to permission checking, but not explicitly checking that the resulting object is labeled correctly. For fscontext, this can be done indirectly by e.g. mounting tmpfs or some other fs_use_trans filesystem with fscontext= and then creating a file within it and checking its context. For rootcontext=, you can just check that the context of the root directory of the mount matches (need to make sure that its xattr was not in fact set to that value in the first place - it must either be unset or something different in order to truly test rootcontext=). For context=, you could create a filesystem that actually has xattrs set to a different value, then mount with context= and confirm that the files have that context as well as any newly created files (even if fscreate was set to something else), and that setfilecon/setxattr() on files within the mount fails with errno EOPNOTSUPP. For defcontext=, you could create a filesystem that has files w/o xattrs and then confirm that they are mapped to the specified defcontext upon mount, where defcontext must differ from the policy default to truly test. We'll have to work out how to handle the watch policy / tests cleanly; we should just skip the test if filesystem watch permission isn't defined in /usr/share/selinux/devel/include/support/all_perms.spt. > >> --- >>  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))) >> >
On Tue, 2019-12-17 at 10:36 -0500, Stephen Smalley wrote: > On 12/16/19 9:09 AM, Stephen Smalley wrote: > > On 12/15/19 12:06 PM, Richard Haines wrote: > > > 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@btinternet.com> > > > > This didn't pass travis-ci, looks like a combination of failing > > check-syntax and FAN_MARK_FILESYSTEM not being defined (maybe the > > kernel > > headers are too old in the base distro?). > > Possibly we need to install our own kernel headers for the testsuite? I assume this is on the travis system (that I don't use). > > It looks like you are exercising the various *context= mount options > with respect to permission checking, but not explicitly checking > that > the resulting object is labeled correctly. For fscontext, this can > be > done indirectly by e.g. mounting tmpfs or some other fs_use_trans > filesystem with fscontext= and then creating a file within it and > checking its context. For rootcontext=, you can just check that the > context of the root directory of the mount matches (need to make > sure > that its xattr was not in fact set to that value in the first place - > it > must either be unset or something different in order to truly test > rootcontext=). For context=, you could create a filesystem that > actually has xattrs set to a different value, then mount with > context= > and confirm that the files have that context as well as any newly > created files (even if fscreate was set to something else), and that > setfilecon/setxattr() on files within the mount fails with errno > EOPNOTSUPP. For defcontext=, you could create a filesystem that has > files w/o xattrs and then confirm that they are mapped to the > specified > defcontext upon mount, where defcontext must differ from the policy > default to truly test. Thanks for the info, just what I needed > > We'll have to work out how to handle the watch policy / tests > cleanly; > we should just skip the test if filesystem watch permission isn't > defined in /usr/share/selinux/devel/include/support/all_perms.spt. I'll add this check. I tested on 5.5 with the notify policy changes and it required a further allow rule in watch.cil: (allow test_mount_t self (dir (watch_sb))) > > > > --- > > > 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,fsc > > > ontext=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,fscon > > > text=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))) > > >
On 12/17/19 11:38 AM, Richard Haines wrote: > On Tue, 2019-12-17 at 10:36 -0500, Stephen Smalley wrote: >> On 12/16/19 9:09 AM, Stephen Smalley wrote: >>> On 12/15/19 12:06 PM, Richard Haines wrote: >>>> 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@btinternet.com> >>> >>> This didn't pass travis-ci, looks like a combination of failing >>> check-syntax and FAN_MARK_FILESYSTEM not being defined (maybe the >>> kernel >>> headers are too old in the base distro?). >> >> Possibly we need to install our own kernel headers for the testsuite? > I assume this is on the travis system (that I don't use). Yes, the build/test environment is specified by the .travis.yml file in the selinux-testsuite repo. Currently uses a bionic Ubuntu distro as the base (sadly Fedora isn't an option). In other situations where we have had dependency problems, we have explicitly had it download the desired upstream sources and install them, e.g. for perl tidy, libbpf, keytuils, etc. Should be possible to do the same with the kernel headers instead of just using the distro-provided ones. Or your test code could have #ifndef FAN_MARK_FILESYSTEM...#define FAN_MARK_FILESYSTEM...#endif; we have some instances of that already for SO_PEERSEC, SCM_SECURITY, MAP_HUGETLB, AF_KCM, etc. That's probably the easiest solution.
On Tue, 2019-12-17 at 11:49 -0500, Stephen Smalley wrote: > On 12/17/19 11:38 AM, Richard Haines wrote: > > On Tue, 2019-12-17 at 10:36 -0500, Stephen Smalley wrote: > > > On 12/16/19 9:09 AM, Stephen Smalley wrote: > > > > On 12/15/19 12:06 PM, Richard Haines wrote: > > > > > 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@btinternet.com> > > > > > > > > This didn't pass travis-ci, looks like a combination of failing > > > > check-syntax and FAN_MARK_FILESYSTEM not being defined (maybe > > > > the > > > > kernel > > > > headers are too old in the base distro?). > > > > > > Possibly we need to install our own kernel headers for the > > > testsuite? > > I assume this is on the travis system (that I don't use). > > Yes, the build/test environment is specified by the .travis.yml file > in > the selinux-testsuite repo. Currently uses a bionic Ubuntu distro > as > the base (sadly Fedora isn't an option). In other situations where > we > have had dependency problems, we have explicitly had it download the > desired upstream sources and install them, e.g. for perl tidy, > libbpf, > keytuils, etc. Should be possible to do the same with the kernel > headers instead of just using the distro-provided ones. Or your > test > code could have #ifndef FAN_MARK_FILESYSTEM...#define > FAN_MARK_FILESYSTEM...#endif; we have some instances of that already > for > SO_PEERSEC, SCM_SECURITY, MAP_HUGETLB, AF_KCM, etc. That's probably > the > easiest solution. Thanks. I'll add the ifndef etc. > >
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)))
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@btinternet.com> --- 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