Added a suite to test permissions for setting inotify and fanotify watches on filesystem objects. Tests watch, watch_with_perm, and watch_reads permissions. Signed-off-by: Aaron Goidel <acgoide@xxxxxxxxxxxxx> --- policy/Makefile | 4 ++ policy/test_notify.te | 74 ++++++++++++++++++++++++ tests/Makefile | 4 ++ tests/notify/Makefile | 5 ++ tests/notify/test | 101 +++++++++++++++++++++++++++++++++ tests/notify/test_fanotify.c | 105 +++++++++++++++++++++++++++++++++++ tests/notify/test_inotify.c | 43 ++++++++++++++ 7 files changed, 336 insertions(+) create mode 100644 policy/test_notify.te create mode 100644 tests/notify/Makefile create mode 100755 tests/notify/test create mode 100644 tests/notify/test_fanotify.c create mode 100644 tests/notify/test_inotify.c diff --git a/policy/Makefile b/policy/Makefile index 305b572..65f88c5 100644 --- a/policy/Makefile +++ b/policy/Makefile @@ -71,6 +71,10 @@ ifeq ($(shell grep -q corenet_sctp_bind_all_nodes $(POLDEV)/include/kernel/coren TARGETS += test_sctp.te endif +ifeq ($(shell grep -q all_file_perms.*watch $(POLDEV)/include/support/all_perms.spt && echo true),true) +TARGETS+=test_notify.te +endif + ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6)) TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te, $(TARGETS)) endif diff --git a/policy/test_notify.te b/policy/test_notify.te new file mode 100644 index 0000000..8ba6f1a --- /dev/null +++ b/policy/test_notify.te @@ -0,0 +1,74 @@ +#################################################### +# Policy for testing inoftify and fanotify watches # +#################################################### + +attribute test_notify_domain; + +# Type for the file on which we want to set a watch +type test_notify_file_t; +files_type(test_notify_file_t); + +# Domain for the process which CAN set a non-permission watch +type test_watch_t; +domain_type(test_watch_t); +unconfined_runs_test(test_watch_t); + +typeattribute test_watch_t test_notify_domain; +typeattribute test_watch_t testdomain; + +allow test_notify_domain self:capability sys_admin; + +allow test_watch_t test_notify_file_t:file { read write open watch }; + +# Domain for the process which CAN set a NON-access watch on a file +type test_perm_watch_t; +domain_type(test_perm_watch_t); +unconfined_runs_test(test_perm_watch_t); + +typeattribute test_perm_watch_t test_notify_domain; +typeattribute test_perm_watch_t testdomain; + +allow test_perm_watch_t test_notify_file_t:file { read write open watch watch_with_perm }; + +# Domain which CAN set a NON-perm watch which MAY read accesses +type test_read_watch_t; +domain_type(test_read_watch_t); +unconfined_runs_test(test_read_watch_t); + +typeattribute test_read_watch_t test_notify_domain; +typeattribute test_read_watch_t testdomain; + +allow test_read_watch_t test_notify_file_t:file { read write open watch watch_reads }; + +# Domain which CAN set any watch which CAN read accesses +type test_perm_read_watch_t; +domain_type(test_perm_read_watch_t); +unconfined_runs_test(test_perm_read_watch_t); + +typeattribute test_perm_read_watch_t test_notify_domain; +typeattribute test_perm_read_watch_t testdomain; + +allow test_perm_read_watch_t test_notify_file_t:file { read write open watch watch_with_perm watch_reads }; + +# Domain which CANNOT set any watches +type test_no_watch_t; +domain_type(test_no_watch_t); +unconfined_runs_test(test_no_watch_t); + +typeattribute test_no_watch_t test_notify_domain; +typeattribute test_no_watch_t testdomain; + +allow test_no_watch_t test_notify_file_t:file { read write open }; + +# Domain which has no write access but can watch +type test_rdonly_t; +domain_type(test_rdonly_t); +unconfined_runs_test(test_rdonly_t); + +typeattribute test_rdonly_t test_notify_domain; +typeattribute test_rdonly_t testdomain; + +allow test_rdonly_t test_notify_file_t:file { read open watch }; + +miscfiles_domain_entry_test_files(test_notify_domain); +userdom_sysadm_entry_spec_domtrans_to(test_notify_domain); diff --git a/tests/Makefile b/tests/Makefile index 63aa325..b99c96e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -50,6 +50,10 @@ ifeq ($(shell grep "^SELINUX_INFINIBAND_PKEY_TEST=" infiniband_pkey/ibpkey_test. SUBDIRS += infiniband_pkey endif +ifeq ($(shell grep -q all_file_perms.*watch $(POLDEV)/include/support/all_perms.spt && echo true),true) +SUBDIRS+=notify +endif + ifeq ($(DISTRO),RHEL4) SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp_nosuid overlay unix_socket, $(SUBDIRS)) endif diff --git a/tests/notify/Makefile b/tests/notify/Makefile new file mode 100644 index 0000000..78c4b3b --- /dev/null +++ b/tests/notify/Makefile @@ -0,0 +1,5 @@ +TARGETS=test_inotify test_fanotify + +all: $(TARGETS) +clean: + rm -f $(TARGETS) diff --git a/tests/notify/test b/tests/notify/test new file mode 100755 index 0000000..21f03de --- /dev/null +++ b/tests/notify/test @@ -0,0 +1,101 @@ +#!/usr/bin/perl + +use Test; +BEGIN { plan tests => 14 } # number of tests to run + +# help the test script locate itself +$basedir = $0; +$basedir =~ s|(.*)/[^/]*|$1|; + +# Get rid of a testfile from last run if it's there (just in case) +system("rm -f $basedir/watch_me"); + +# Create a new test file +system("touch $basedir/watch_me"); +system("chcon -t test_notify_file_t $basedir/watch_me"); + +## TESTS + +## TEST BASIC WATCH PERMISSION +# Should be able to set inotify watch +$exit_val = + system("runcon -t test_watch_t $basedir/test_inotify $basedir/watch_me 2>&1"); +ok( $exit_val, 0 ); + +# Should be able to set non-permissions based fanotify watch +$exit_val = system( + "runcon -t test_watch_t $basedir/test_fanotify $basedir/watch_me 2>&1"); +ok( $exit_val, 0 ); + +# Should NOT be able to set permission based fanotify watch +$exit_val = system( + "runcon -t test_watch_t $basedir/test_fanotify -p $basedir/watch_me 2>&1"); +ok($exit_val); # this should fail + +# Should NOT be able to set read based fanotify watch +$exit_val = system( + "runcon -t test_watch_t $basedir/test_fanotify -r $basedir/watch_me 2>&1"); +ok($exit_val); # this should fail + +# Should NOT be able to set read based inotify watch +$exit_val = system( + "runcon -t test_watch_t $basedir/test_inotify -r $basedir/watch_me 2>&1"); +ok($exit_val); # this should fail + +## TEST PERM WATCH +# Should be able to set permission based fanotify watch +$exit_val = system( +"runcon -t test_perm_watch_t $basedir/test_fanotify -p $basedir/watch_me 2>&1" +); +ok( $exit_val, 0 ); + +# Should NOT be able to set watch of accesses +$exit_val = system( +"runcon -t test_perm_watch_t $basedir/test_fanotify -r $basedir/watch_me 2>&1" +); +ok($exit_val); # this should fail + +## TEST READ NO PERM WATCH PERMSISSIONS +# Should NOT be able to set read and perm watch +$exit_val = system( +"runcon -t test_read_watch_t $basedir/test_fanotify -p -r $basedir/watch_me 2>&1" +); +ok($exit_val); # should fail + +# Should be able to set read inotify watch +$exit_val = system( +"runcon -t test_read_watch_t $basedir/test_inotify -r $basedir/watch_me 2>&1" +); +ok( $exit_val, 0 ); + +## TEST READ WITH PERM WATCH PERMSISSIONS +# Should be able to set read and perm watch +$exit_val = system( +"runcon -t test_perm_read_watch_t $basedir/test_fanotify -p -r $basedir/watch_me 2>&1" +); +ok( $exit_val, 0 ); + +## TEST NO WATCH PERMSISSIONS +# Should NOT be able to set inotify watch +$exit_val = system( + "runcon -t test_no_watch_t $basedir/test_inotify $basedir/watch_me 2>&1"); +ok($exit_val); # this should fail + +# Should NOT be able to set any fanotify watch +$exit_val = system( + "runcon -t test_no_watch_t $basedir/test_fanotify $basedir/watch_me 2>&1"); +ok($exit_val); # this should fail + +## TEST READ ONLY +# Should NOT be able to get read-write descriptor +$exit_val = system( + "runcon -t test_rdonly_t $basedir/test_fanotify -l $basedir/watch_me 2>&1"); +ok($exit_val); # this should fail + +# Should be able to get read-write descriptor +$exit_val = system( + "runcon -t test_watch_t $basedir/test_fanotify -l $basedir/watch_me 2>&1"); +ok( $exit_val, 0 ); + +# Clean up test file +system("rm -f $basedir/watch_me"); diff --git a/tests/notify/test_fanotify.c b/tests/notify/test_fanotify.c new file mode 100644 index 0000000..fff773f --- /dev/null +++ b/tests/notify/test_fanotify.c @@ -0,0 +1,105 @@ +#define _GNU_SOURCE 1 + +#include <stdio.h> +#include <stdlib.h> + +#include <getopt.h> + +#include <fcntl.h> +#include <poll.h> +#include <sys/fanotify.h> +#include <unistd.h> + +void printUsage() +{ + fprintf(stderr, "Usage: test_fanotify [-p] [-r] [-l] file_name\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + printUsage(); + } + + int fd, ret, arg; + int mask = FAN_OPEN; // default mask + int listening = 0; + + // the -p flag will test for watch_with_perm + // the mask used at mark will contain FAN_OPEN_PERM + // + // the -r flag will test for watching accesses to files for reads + // the mask will contain FAN_ACCESS + while ((arg = getopt(argc, argv, "prl")) != -1) { + switch (arg) { + case 'p': + mask |= FAN_OPEN_PERM; + break; + case 'r': + mask |= FAN_ACCESS; + break; + case 'l': + listening = 1; + break; + default: + printUsage(); + } + } + + // get file descriptor for new fanotify event queue + fd = fanotify_init(FAN_CLASS_CONTENT, O_RDWR); + if (fd < 0) { + perror("fanotify_init:bad file descriptor"); + exit(1); + } + + // mark a filesystem object and add mark to event queue + // get notifications on file opens, accesses, and closes + // use current working directory as base dir + ret = fanotify_mark(fd, FAN_MARK_ADD, mask, AT_FDCWD, argv[optind]); + + if (ret < 0) { + perror("test_fanotify:watch denied"); + exit(1); + } + + // logic to actually listen for an event if the -l flag is passed + // this is used to test if an app with read-only access can get a read/write + // handle to the watched file + if (listening) { + if (fork() == 0) { // fork a child process to cause an event on the file + FILE *f; + + f = fopen(argv[optind], "r"); // open file for reading + fgetc(f); // read char from file + + fclose(f); + } else { // logic to watch for events and try to access file read/write + struct pollfd fds; + fds.fd = fd; + fds.events = POLLIN; + + while (listening) { + int polled = poll(&fds, 1, 1); + if (polled > 0) { + if (fds.revents & POLLIN) { + struct fanotify_event_metadata buff[200]; + + size_t len = read(fd, (void *)&buff, sizeof(buff)); + if (len == -1) { + perror("test_fanotify:can't open file"); + exit(1); + } else { + listening = 0; + break; + } + } + } else if (polled == -1) { + listening = 0; + } + } + } + } + exit(0); +} diff --git a/tests/notify/test_inotify.c b/tests/notify/test_inotify.c new file mode 100644 index 0000000..17c3565 --- /dev/null +++ b/tests/notify/test_inotify.c @@ -0,0 +1,43 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/inotify.h> +#include <getopt.h> + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: test_inotify [-r] file_name\n"); + exit(1); + } + + int fd, wd, arg; + int mask = IN_MODIFY; + + while ((arg = getopt(argc, argv, "pr")) != -1) { + switch (arg) { + case 'r': + mask |= IN_ACCESS; + break; + default: + fprintf(stderr, "Usage: test_inotify [-r] file_name\n"); + exit(1); + } + } + + // get new file descriptor for inotify access + fd = inotify_init(); + if (fd < 0) { + perror("inotify_init:bad file descriptor"); + exit(1); + } + + // set watch on file and get watch descriptor for accessing events on it + wd = inotify_add_watch(fd, argv[optind], mask); + + if (wd < 0) { + perror("test_inotify:watch denied"); + exit(1); + } + + exit(0); +} -- 2.21.0