Re: [PATCH] selinux-testsuite: add tests for fsnotify

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

 



On Tue, Sep 10, 2019 at 08:26:57AM -0400, Stephen Smalley wrote:
> On 9/10/19 1:59 AM, Dominick Grift wrote:
> > On Wed, Jul 10, 2019 at 09:39:17AM -0400, Aaron Goidel wrote:
> > > Added a suite to test permissions for setting inotify and fanotify watches
> > > on filesystem objects. Tests watch, watch_with_perm, and watch_reads permissions.
> > 
> > Ive also "tested" the fsnotify patch. And my tests indicate that this might cause issues:
> > 
> > I added the access vectors to my policy, but on older systems (debian 10) cron fails to start
> > cron needs the "entrypoint" permission on /etc/crontab but it looks like appending the "watch" access vectors to common-file disrupted the ordering
> > The result is that now i have to allow cron to "watch_read" /etc/crontab even though the neither kernel nor selinux user space are aware of the fsnotify access vectors
> > It seems the cron selinux code got confused and now thinks watch_read is entrypoint (its using selinux code to determine whether it can manually transition to cronjob domains on crontabs)
> > 
> > I am hoping this issue with resolve itself on systems with kernels and user spaces that suppose fsnotify.
> > However unless i am overlooking something this is still likely to disrupt compatibility
> 
> So, IIUC, the issue is that:
> a) older cron was directly using the fixed FILE__ENTRYPOINT definition from
> libselinux in a security_compute_av() call rather than dynamically looking
> up entrypoint permission,
> b) the value of the file entrypoint permission in policy has changed because
> you updated your policy and chose to place the watch* permissions in the
> common file definition to match the kernel.
> 
> Note that you could have instead sprinkled copies of the watch* permissions
> across all of the individual file/dir/*_file classes and placed them at the
> end of the classes if you wanted to avoid this compatibility issue.  The
> policy and kernel definitions no longer need to be identical; the kernel
> will dynamically map between them for its own permission checks.
> 
> Also, this should have already been broken for you when map permission was
> added, which would have shown up as execute_no_trans being checked instead
> of entrypoint.  This was already filed as a bug on cron in debian here:
> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=924716
> 
> So, wrt compatibility, IIUC:
> - the kernel change does not break compatibility for old policy + old
> userspace,
> - new policy can choose to add the new permissions in a backward-compatible
> manner if desired, although refpolicy has already chosen not to do this for
> map permission,
> - the offending code in cron produces build warnings that were introduced in
> 2014 in libselinux to update their code to use the dynamic class/perm
> mapping support, which would resolve the issue.

this does not work (cron[8934]: ((null)) ENTRYPOINT FAILED): https://defensec.nl/gitweb/dssp2.git/commitdiff/914b1d5366922f92435cba780519c5d2bb9f4f7d

[kcinimod@brutus dssp2 (master=)]$ seinfo policy.31 -x --common common_file

Commons: 1
   common common_file
{
        append
        lock
        mounton
        unlink
        read
        create
        quotaon
        execute
        setattr
        map
        relabelto
        rename
        link
        ioctl
        getattr
        relabelfrom
}
[kcinimod@brutus dssp2 (master=)]$ seinfo policy.31 -xcfile

Classes: 1
   class file
inherits common_file
{
        execmod
        watch
        write
        watch_reads
        execute_no_trans
        watch_mount
        watch_with_perm
        open
        watch_sb
        audit_access
        entrypoint
}


> 
> > 
> > > 
> > > 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
> > > 
> > 
> 

-- 
Key fingerprint = 5F4D 3CDB D3F8 3652 FBD8 02D5 3B6C 5F1D 2C7B 6B02
https://sks-keyservers.net/pks/lookup?op=get&search=0x3B6C5F1D2C7B6B02
Dominick Grift



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

  Powered by Linux