selinux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Richard Haines <richard_c_haines@btinternet.com>
To: Stephen Smalley <sds@tycho.nsa.gov>,
	selinux@vger.kernel.org, Paul Moore <paul@paul-moore.com>
Subject: Re: [RFC V3 PATCH] selinux-testsuite: Add test for restorecon
Date: Sun, 15 Sep 2019 15:35:57 +0100	[thread overview]
Message-ID: <2ada6c60bbac70094b069272772a73add3f3ddc9.camel@btinternet.com> (raw)
In-Reply-To: <9b525cf9-9132-ff68-9d6a-da78c45b8cac@tycho.nsa.gov>

On Fri, 2019-09-13 at 13:09 -0400, Stephen Smalley wrote:
> On 6/3/19 5:56 AM, Richard Haines wrote:
> > This will test the updated libselinux selinux_restorecon(3) using a
> > simple test version of "restorecon", or if the test is run locally,
> > a '-p' option can be used to supply the path of a full version of
> > restorecon(8) (see note in restorecon/test).
> > 
> > As the SELinux testsuite is primarily a set of regression tests for
> > the
> > SELinux kernel, this change also adds support to include the
> > testing of
> > core userspace services such as library functions or utilities. To
> > install and run these type of tests, the following must be run
> > first:
> > 
> > 	# export TEST_USERSPACE=y
> 
> Sorry for the long delay in responding. I would prefer userspace
> tests 
> to remain part of the selinux userspace repository, invoked upon
> make 
> test there.  A test policy if required should be possible via a
> separate 
> standalone cil or te/fc policy module that doesn't depend on the 
> testsuite policy.

That's okay as I didn't expect it to be accepted. I just wanted it to
test the restorecon patches that I submitted as I find it more
convenient to write testsuite tests. Maybe one day I'll rewrite for
libselinux.


> 
> > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> > ---
> > V2 Changes:
> > Allow option to run other restorecon binaries.
> > Only run if userspace tests enabled.
> > Add tests for "Ignore the stem..." patch
> > V3 Changes:
> > Add tests for No CAP_SYS_ADMIN permission and SKIP_DIGEST
> > 
> >   README.md                                  |   7 +
> >   policy/Makefile                            |   8 +
> >   policy/test_restorecon.te                  |  74 ++++++++
> >   tests/Makefile                             |   7 +
> >   tests/file/test                            |   2 +-
> >   tests/restorecon/.gitignore                |   5 +
> >   tests/restorecon/Makefile                  |   7 +
> >   tests/restorecon/check_fs.c                |  69 ++++++++
> >   tests/restorecon/get_all_digests.c         | 176
> > +++++++++++++++++++
> >   tests/restorecon/restorecon.c              |  80 +++++++++
> >   tests/restorecon/restorecon_xattr.c        | 116 +++++++++++++
> >   tests/restorecon/selinux_restorecon_skip.c |  66 ++++++++
> >   tests/restorecon/test                      | 188
> > +++++++++++++++++++++
> >   13 files changed, 804 insertions(+), 1 deletion(-)
> >   create mode 100644 policy/test_restorecon.te
> >   create mode 100644 tests/restorecon/.gitignore
> >   create mode 100644 tests/restorecon/Makefile
> >   create mode 100644 tests/restorecon/check_fs.c
> >   create mode 100644 tests/restorecon/get_all_digests.c
> >   create mode 100644 tests/restorecon/restorecon.c
> >   create mode 100644 tests/restorecon/restorecon_xattr.c
> >   create mode 100644 tests/restorecon/selinux_restorecon_skip.c
> >   create mode 100755 tests/restorecon/test
> > 
> > diff --git a/README.md b/README.md
> > index 26784f8..329a495 100644
> > --- a/README.md
> > +++ b/README.md
> > @@ -114,6 +114,13 @@ the tests:
> >   
> >   ## Running the Tests
> >   
> > +The SELinux testsuite is primarily a set of regression tests for
> > the SELinux
> > +kernel, however it is possible to include the testing of core
> > userspace
> > +services such as library functions or utilities. To install any
> > relevant
> > +tests, the following must be run first:
> > +
> > +	# export TEST_USERSPACE=y
> > +
> >   Create a shell with the `unconfined_r` or `sysadm_r` role and the
> > Linux
> >   superuser identity, e.g.:
> >   
> > diff --git a/policy/Makefile b/policy/Makefile
> > index 305b572..9c7d173 100644
> > --- a/policy/Makefile
> > +++ b/policy/Makefile
> > @@ -3,6 +3,7 @@ POLDEV ?= /usr/share/selinux/devel
> >   SEMODULE = /usr/sbin/semodule
> >   CHECKPOLICY = /usr/bin/checkpolicy
> >   CHECKMODULE = /usr/bin/checkmodule
> > +INCLUDEDIR ?= /usr/include
> >   
> >   DISTRO=$(shell ../tests/os_detect)
> >   RHEL_VERS=$(shell echo $(DISTRO) | sed 's/RHEL//')
> > @@ -79,6 +80,13 @@ ifeq (x$(DISTRO),$(filter x$(DISTRO), xRHEL6))
> >   TARGETS:=$(filter-out test_ibpkey.te, $(TARGETS))
> >   endif
> >   
> > +# Add any userspace test policy
> > +ifeq ($(TEST_USERSPACE),y)
> > +    ifeq ($(shell grep -q selabel_get_digests_all_partial_matches
> > $(INCLUDEDIR)/selinux/label.h && echo true),true)
> > +        TARGETS += test_restorecon.te
> > +    endif
> > +endif
> > +
> >   ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5))
> >   	BUILD_TARGET := build_rhel
> >   	LOAD_TARGET := load_rhel
> > diff --git a/policy/test_restorecon.te b/policy/test_restorecon.te
> > new file mode 100644
> > index 0000000..57699bb
> > --- /dev/null
> > +++ b/policy/test_restorecon.te
> > @@ -0,0 +1,74 @@
> > +#################################
> > +#
> > +# Policy for testing restorecon
> > +#
> > +
> > +require {
> > +	attribute file_type;
> > +	type setfiles_exec_t;
> > +}
> > +
> > +attribute restorecon_domain;
> > +
> > +type test_restorecon_file_t;
> > +files_type(test_restorecon_file_t)
> > +type in_dir_t;
> > +files_type(in_dir_t)
> > +type out_dir_t;
> > +files_type(out_dir_t)
> > +type in_file_t;
> > +files_type(in_file_t)
> > +type out_file_t;
> > +files_type(out_file_t)
> > +
> > +# Domain for restorecon.
> > +type test_restorecon_t;
> > +files_type(test_restorecon_t)
> > +
> > +domain_type(test_restorecon_t)
> > +unconfined_runs_test(test_restorecon_t)
> > +typeattribute test_restorecon_t testdomain;
> > +typeattribute test_restorecon_t restorecon_domain;
> > +
> > +allow test_restorecon_t self:capability sys_admin;
> > +allow test_restorecon_t test_file_t:file relabelfrom;
> > +allow test_restorecon_t file_type:dir { relabel_dir_perms
> > manage_dir_perms };
> > +allow test_restorecon_t file_type:file { rw_file_perms execute
> > relabelto relabelfrom };
> > +allow_map(test_restorecon_t, file_type, file)
> > +corecmd_bin_entry_type(test_restorecon_t)
> > +
> > +# Allow these for statfs(2) if /tmp = TMPFS_MAGIC test
> > +allow test_restorecon_t tmpfs_t:filesystem getattr;
> > +allow test_restorecon_t user_tmp_t:sock_file getattr;
> > +# and this to add the root test directory
> > +allow test_restorecon_t fs_t:filesystem getattr;
> > +
> > +# Allow restorecon(8) to be used instead of the test program
> > +allow test_restorecon_t setfiles_exec_t:file entrypoint;
> > +
> > +######### No CAP_SYS_ADMIN permission ################
> > +
> > +type test_no_admin_restorecon_t;
> > +files_type(test_no_admin_restorecon_t)
> > +
> > +domain_type(test_no_admin_restorecon_t)
> > +unconfined_runs_test(test_no_admin_restorecon_t)
> > +typeattribute test_no_admin_restorecon_t testdomain;
> > +typeattribute test_no_admin_restorecon_t restorecon_domain;
> > +
> > +allow test_no_admin_restorecon_t test_file_t:file relabelfrom;
> > +allow test_no_admin_restorecon_t file_type:dir { relabel_dir_perms
> > manage_dir_perms };
> > +allow test_no_admin_restorecon_t file_type:file { rw_file_perms
> > execute relabelto relabelfrom };
> > +allow_map(test_no_admin_restorecon_t, file_type, file)
> > +corecmd_bin_entry_type(test_no_admin_restorecon_t)
> > +
> > +# and this to add the root test directory
> > +allow test_no_admin_restorecon_t fs_t:filesystem getattr;
> > +
> > +# Allow restorecon(8) to be used instead of the test program
> > +allow test_no_admin_restorecon_t setfiles_exec_t:file entrypoint;
> > +
> > +######################################################
> > +# Allow all of these domains to be entered from sysadm domain
> > +miscfiles_domain_entry_test_files(restorecon_domain)
> > +userdom_sysadm_entry_spec_domtrans_to(restorecon_domain)
> > diff --git a/tests/Makefile b/tests/Makefile
> > index 63aa325..37ed6af 100644
> > --- a/tests/Makefile
> > +++ b/tests/Makefile
> > @@ -50,6 +50,13 @@ ifeq ($(shell grep
> > "^SELINUX_INFINIBAND_PKEY_TEST=" infiniband_pkey/ibpkey_test.
> >   SUBDIRS += infiniband_pkey
> >   endif
> >   
> > +# Keep userspace tests last
> > +ifeq ($(TEST_USERSPACE),y)
> > +    ifeq ($(shell grep -q selabel_get_digests_all_partial_matches
> > $(INCLUDEDIR)/selinux/label.h && echo true),true)
> > +        SUBDIRS += restorecon
> > +    endif
> > +endif
> > +
> >   ifeq ($(DISTRO),RHEL4)
> >       SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket
> > mmap nnp_nosuid overlay unix_socket, $(SUBDIRS))
> >   endif
> > diff --git a/tests/file/test b/tests/file/test
> > index 32dd2bd..cb86f41 100755
> > --- a/tests/file/test
> > +++ b/tests/file/test
> > @@ -50,7 +50,7 @@ system "chcon -t fileop_exec_t $basedir/wait_io
> > 2>&1 > /dev/null";
> >   # Get the SID of the good file.
> >   #
> >   $output = `ls -Z $basedir/temp_file`;
> > -@arr = split( ' ', $output );
> > +@arr    = split( ' ', $output );
> >   if ( index( $arr[0], ":" ) != -1 ) {
> >       $good_file_sid = $arr[0];
> >   }
> > diff --git a/tests/restorecon/.gitignore
> > b/tests/restorecon/.gitignore
> > new file mode 100644
> > index 0000000..98a33e8
> > --- /dev/null
> > +++ b/tests/restorecon/.gitignore
> > @@ -0,0 +1,5 @@
> > +restorecon
> > +selinux_restorecon_skip
> > +restorecon_xattr
> > +get_all_digests
> > +check_fs
> > diff --git a/tests/restorecon/Makefile b/tests/restorecon/Makefile
> > new file mode 100644
> > index 0000000..477f8d1
> > --- /dev/null
> > +++ b/tests/restorecon/Makefile
> > @@ -0,0 +1,7 @@
> > +TARGETS = restorecon selinux_restorecon_skip restorecon_xattr
> > get_all_digests check_fs
> > +LDLIBS += -lselinux
> > +
> > +all: $(TARGETS)
> > +
> > +clean:
> > +	rm -f $(TARGETS)
> > diff --git a/tests/restorecon/check_fs.c
> > b/tests/restorecon/check_fs.c
> > new file mode 100644
> > index 0000000..f18910f
> > --- /dev/null
> > +++ b/tests/restorecon/check_fs.c
> > @@ -0,0 +1,69 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <getopt.h>
> > +#include <errno.h>
> > +#include <stdbool.h>
> > +#include <sys/statfs.h>
> > +#include <linux/magic.h>
> > +
> > +static void usage(char *progname)
> > +{
> > +	fprintf(stderr,
> > +		"usage:  %s [-v] <path>\n"
> > +		"Where:\n\t"
> > +		"-v  Display filesystem f_type magic number.\n\t"
> > +		"path  Path to check fs type.\n\n"
> > +		"Returns 1=RAMFS_MAGIC, 2=TMPFS_MAGIC,
> > 3=SYSFS_MAGIC\n", progname);
> > +	exit(-1);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +	int opt, rc;
> > +	bool verbose = false;
> > +	struct statfs sfsb;
> > +
> > +	while ((opt = getopt(argc, argv, "v")) > 0) {
> > +		switch (opt) {
> > +		case 'v':
> > +			verbose = true;
> > +			break;
> > +		default:
> > +			usage(argv[0]);
> > +		}
> > +	}
> > +
> > +	if (optind >= argc) {
> > +		fprintf(stderr, "No pathname specified\n");
> > +		return -1;
> > +	}
> > +
> > +	rc = statfs(argv[optind], &sfsb);
> > +	if (rc < 0) {
> > +		fprintf(stderr, "Get filesystem statistics ERROR:
> > %s\n",
> > +			strerror(errno));
> > +		return rc;
> > +	}
> > +
> > +	switch (sfsb.f_type) {
> > +	case RAMFS_MAGIC:
> > +		if (verbose)
> > +			printf("RAMFS_MAGIC\n");
> > +		return 1;
> > +	case TMPFS_MAGIC:
> > +		if (verbose)
> > +			printf("TMPFS_MAGIC\n");
> > +		return 2;
> > +	case SYSFS_MAGIC:
> > +		if (verbose)
> > +			printf("SYSFS_MAGIC\n");
> > +		return 3;
> > +	default:
> > +		if (verbose)
> > +			printf("sfsb.f_type: 0x%lx\n", sfsb.f_type);
> > +		return 0;
> > +	}
> > +
> > +	return rc;
> > +}
> > diff --git a/tests/restorecon/get_all_digests.c
> > b/tests/restorecon/get_all_digests.c
> > new file mode 100644
> > index 0000000..59f0ed8
> > --- /dev/null
> > +++ b/tests/restorecon/get_all_digests.c
> > @@ -0,0 +1,176 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <stdint.h>
> > +#include <getopt.h>
> > +#include <errno.h>
> > +#include <stdbool.h>
> > +#include <fts.h>
> > +#include <selinux/selinux.h>
> > +#include <selinux/label.h>
> > +
> > +#define RESTORECON_PARTIAL_MATCH_DIGEST  "security.sehash"
> > +
> > +static void usage(char *progname)
> > +{
> > +	fprintf(stderr,
> > +		"usage:  %s [-vr] <path>\n\n"
> > +		"Where:\n\t"
> > +		"-v  Display information.\n\t"
> > +		"-r  Recursively descend directories.\n\t"
> > +		"path  Path to check current SHA1 digest against
> > file_contexts entries.\n\n"
> > +		"This will check the directory selinux.sehash SHA1
> > digest for "
> > +		"<path> against\na newly generated digest based on the
> > "
> > +		"file_context entries for that node\n(using the regx,
> > mode "
> > +		"and path entries).\n", progname);
> > +	exit(1);
> > +}
> > +
> > +static int get_digests(struct selabel_handle *hnd, bool verbose,
> > char *path)
> > +{
> > +	int rc = 0;
> > +	size_t i, digest_len = 0;
> > +	bool status;
> > +	uint8_t *xattr_digest = NULL;
> > +	uint8_t *calculated_digest = NULL;
> > +	char *sha1_buf = NULL;
> > +
> > +	status = selabel_get_digests_all_partial_matches(hnd, path,
> > +							 &calculated_di
> > gest,
> > +							 &xattr_digest,
> > +							 &digest_len);
> > +
> > +	sha1_buf = calloc(1, digest_len * 2 + 1);
> > +	if (!sha1_buf) {
> > +		fprintf(stderr, "Could not calloc buffer ERROR: %s\n",
> > +			strerror(errno));
> > +		rc = -1;
> > +		goto out;
> > +	}
> > +
> > +	/* rc = 0 NO MATCH, rc = 1 MATCH, rc = 2 NO calculated_digest
> > +	 * rc = 4 NO xattr_digest, rc = 6 NO digests
> > +	 */
> > +	if (status) { /* They match */
> > +		if (verbose) {
> > +			printf("xattr and file_contexts SHA1 digests
> > match for: %s\n",
> > +			       path);
> > +
> > +			if (calculated_digest) {
> > +				for (i = 0; i < digest_len; i++)
> > +					sprintf((&sha1_buf[i * 2]),
> > "%02x",
> > +						calculated_digest[i]);
> > +				printf("SHA1 digest: %s\n", sha1_buf);
> > +			}
> > +		}
> > +
> > +		rc = 1;
> > +		goto out;
> > +	} else {
> > +		if (!calculated_digest) {
> > +			rc = 2;
> > +			if (verbose) {
> > +				printf("No SHA1 digest available for:
> > %s\n", path);
> > +				printf("as file_context entry is
> > \"<<none>>\"\n");
> > +			}
> > +		}
> > +
> > +		if (calculated_digest && verbose) {
> > +			printf("The file_context entries for: %s\n",
> > path);
> > +
> > +			for (i = 0; i < digest_len; i++)
> > +				sprintf((&sha1_buf[i * 2]), "%02x",
> > calculated_digest[i]);
> > +			printf("generated SHA1 digest: %s\n",
> > sha1_buf);
> > +		}
> > +		if (!xattr_digest) {
> > +			rc = rc | 4;
> > +			if (verbose)
> > +				printf("however there is no
> > selinux.sehash xattr entry.\n");
> > +			else
> > +				goto out;
> > +
> > +		} else if (verbose) {
> > +			printf("however it does NOT match the current
> > entry of:\n");
> > +			for (i = 0; i < digest_len; i++)
> > +				sprintf((&sha1_buf[i * 2]), "%02x",
> > xattr_digest[i]);
> > +			printf("%s\n", sha1_buf);
> > +		}
> > +	}
> > +
> > +	free(sha1_buf);
> > +out:
> > +	free(xattr_digest);
> > +	free(calculated_digest);
> > +	return rc;
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +	int opt, fts_flags, status;
> > +	bool verbose = false, recurse = false;
> > +	FTS *fts;
> > +	FTSENT *ftsent;
> > +	char *paths[2] = { NULL, NULL };
> > +	struct selabel_handle *hnd;
> > +
> > +	if (argc < 2)
> > +		usage(argv[0]);
> > +
> > +	while ((opt = getopt(argc, argv, "vr")) > 0) {
> > +		switch (opt) {
> > +		case 'v':
> > +			verbose = true;
> > +			break;
> > +		case 'r':
> > +			recurse = true;
> > +			break;
> > +		default:
> > +			usage(argv[0]);
> > +		}
> > +	}
> > +
> > +	if (optind >= argc) {
> > +		fprintf(stderr, "No pathname specified\n");
> > +		exit(-1);
> > +	}
> > +	paths[0] = argv[optind];
> > +
> > +	hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
> > +	if (!hnd) {
> > +		fprintf(stderr, "ERROR: selabel_open - Could not obtain
> > handle.\n");
> > +		return -1;
> > +	}
> > +
> > +	fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
> > +	fts = fts_open(paths, fts_flags, NULL);
> > +	if (!fts) {
> > +		printf("fts error on %s: %s\n",
> > +		       paths[0], strerror(errno));
> > +		return -1;
> > +	}
> > +
> > +	while ((ftsent = fts_read(fts)) != NULL) {
> > +		switch (ftsent->fts_info) {
> > +		case FTS_D:
> > +			/* If recurse = TRUE, then 'status' will
> > reflect the
> > +			 * last path match with 0 = NO MATCH, 1 =
> > MATCH,
> > +			 * 2 = NO calculated_digest, 4 = NO
> > xattr_digest and
> > +			 * 6 = NO digests.
> > +			 */
> > +			status = get_digests(hnd, verbose, ftsent-
> > >fts_path);
> > +			if (status < 0)
> > +				goto out;
> > +			break;
> > +		default:
> > +			break;
> > +		}
> > +
> > +		if (!recurse)
> > +			break;
> > +	}
> > +
> > +out:
> > +	(void) fts_close(fts);
> > +	(void) selabel_close(hnd);
> > +	return status;
> > +}
> > diff --git a/tests/restorecon/restorecon.c
> > b/tests/restorecon/restorecon.c
> > new file mode 100644
> > index 0000000..9daa19a
> > --- /dev/null
> > +++ b/tests/restorecon/restorecon.c
> > @@ -0,0 +1,80 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <getopt.h>
> > +#include <errno.h>
> > +#include <stdbool.h>
> > +#include <selinux/selinux.h>
> > +#include <selinux/label.h>
> > +#include <selinux/restorecon.h>
> > +
> > +static void usage(char *progname)
> > +{
> > +	fprintf(stderr,
> > +		"usage:  %s [-IDrv] <path>\n\n"
> > +		"Where:\n\t"
> > +		"-I  Set SELINUX_RESTORECON_IGNORE_DIGEST\n\t"
> > +		"-D  Enable digests\n\t"
> > +		"-r  Recursively descend directories.\n\t"
> > +		"-v  Display information.\n\t"
> > +		"path  Path of file or directory to check.\n\n"
> > +		"The parameters must follow those of restorecon(8)\n",
> > progname);
> > +	exit(-1);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +	int opt, rc, flags = 0;
> > +	bool request_digest = false;
> > +	struct selabel_handle *hnd = NULL;
> > +
> > +	if (argc < 2)
> > +		usage(argv[0]);
> > +
> > +	while ((opt = getopt(argc, argv, "IDrv")) > 0) {
> > +		switch (opt) {
> > +		case 'I':
> > +			flags |= SELINUX_RESTORECON_IGNORE_DIGEST;
> > +			request_digest = true;
> > +			break;
> > +		case 'D':
> > +			request_digest = true;
> > +			break;
> > +		case 'r':
> > +			flags |= SELINUX_RESTORECON_RECURSE;
> > +			break;
> > +		case 'v':
> > +			flags |= SELINUX_RESTORECON_VERBOSE;
> > +			break;
> > +		default:
> > +			usage(argv[0]);
> > +		}
> > +	}
> > +
> > +	if (optind >= argc) {
> > +		fprintf(stderr, "No pathname specified\n");
> > +		return -1;
> > +	}
> > +
> > +	struct selinux_opt fc_opts[] = {
> > +		{ SELABEL_OPT_DIGEST, (request_digest ? (char *)1 :
> > NULL) }
> > +	};
> > +
> > +	hnd = selabel_open(SELABEL_CTX_FILE, fc_opts, 1);
> > +	if (!hnd) {
> > +		fprintf(stderr, "ERROR: selabel_open - Could not obtain
> > handle.\n");
> > +		return -1;
> > +	}
> > +
> > +	/* Use own handle */
> > +	selinux_restorecon_set_sehandle(hnd);
> > +
> > +	rc = selinux_restorecon(argv[optind], flags);
> > +	if (rc < 0)
> > +		fprintf(stderr, "selinux_restorecon ERROR: %s\n",
> > +			strerror(errno));
> > +
> > +	selabel_close(hnd);
> > +	return rc;
> > +}
> > +
> > diff --git a/tests/restorecon/restorecon_xattr.c
> > b/tests/restorecon/restorecon_xattr.c
> > new file mode 100644
> > index 0000000..12e89b3
> > --- /dev/null
> > +++ b/tests/restorecon/restorecon_xattr.c
> > @@ -0,0 +1,116 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +#include <getopt.h>
> > +#include <errno.h>
> > +#include <stdbool.h>
> > +#include <selinux/selinux.h>
> > +#include <selinux/label.h>
> > +#include <selinux/restorecon.h>
> > +
> > +static void usage(char *progname)
> > +{
> > +	fprintf(stderr,
> > +		"\nusage: %s [-vrdD] <path>\n"
> > +		"\nWhere:\n\t"
> > +		"-n  Do not append \"Match\" or \"No Match\" to
> > displayed digests.\n\t"
> > +		"-r  Recursively descend directories.\n\t"
> > +		"-m  Do not read /proc/mounts for entries to be
> > excluded.\n\t"
> > +		"-d  Delete non-matching digest entries.\n\t"
> > +		"-D  Delete all digest entries.\n\t"
> > +		"path  Path to search for xattr \"security.sehash\"
> > entries.\n\n",
> > +		progname);
> > +	exit(-1);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +	int opt, rc;
> > +	unsigned int xattr_flags = 0, delete_digest = 0, recurse = 0;
> > +	unsigned int delete_all_digests = 0;
> > +	struct dir_xattr *current, *next, **xattr_list = NULL;
> > +	bool verbose = false;
> > +
> > +	if (argc < 2)
> > +		usage(argv[0]);
> > +
> > +	while ((opt = getopt(argc, argv, "vrdD")) > 0) {
> > +		switch (opt) {
> > +		case 'v':
> > +			verbose = true;
> > +			break;
> > +		case 'r':
> > +			recurse = SELINUX_RESTORECON_XATTR_RECURSE;
> > +			break;
> > +		case 'd':
> > +			delete_digest =
> > +				SELINUX_RESTORECON_XATTR_DELETE_NONMATC
> > H_DIGESTS;
> > +			break;
> > +		case 'D':
> > +			delete_all_digests =
> > +				SELINUX_RESTORECON_XATTR_DELETE_ALL_DIG
> > ESTS;
> > +			break;
> > +		default:
> > +			usage(argv[0]);
> > +		}
> > +	}
> > +
> > +	if (optind >= argc) {
> > +		fprintf(stderr, "No pathname specified\n");
> > +		exit(-1);
> > +	}
> > +
> > +	xattr_flags = delete_digest | delete_all_digests | recurse;
> > +
> > +	if (selinux_restorecon_xattr(argv[optind], xattr_flags,
> > &xattr_list)) {
> > +		fprintf(stderr, "Error selinux_restorecon_xattr: %s\n",
> > +			strerror(errno));
> > +		rc = -1;
> > +		goto out;
> > +	}
> > +
> > +	if (xattr_list && verbose) {
> > +		current = *xattr_list;
> > +		while (current) {
> > +			next = current->next;
> > +			printf("%s ", current->directory);
> > +
> > +			switch (current->result) {
> > +			case MATCH:
> > +				printf("Digest: %s Match\n", current-
> > >digest);
> > +				break;
> > +			case NOMATCH:
> > +				printf("Digest: %s No Match\n",
> > current->digest);
> > +				break;
> > +			case DELETED_MATCH:
> > +				printf("Deleted Digest: %s Match\n",
> > current->digest);
> > +				break;
> > +			case DELETED_NOMATCH:
> > +				printf("Deleted Digest: %s No Match\n",
> > +				       current->digest);
> > +				break;
> > +			case ERROR:
> > +				printf("Digest: %s Error removing
> > xattr\n",
> > +				       current->digest);
> > +				break;
> > +			}
> > +			current = next;
> > +		}
> > +		/* Free memory */
> > +		current = *xattr_list;
> > +		while (current) {
> > +			next = current->next;
> > +			free(current->directory);
> > +			free(current->digest);
> > +			free(current);
> > +			current = next;
> > +		}
> > +	} else if (verbose) {
> > +		printf("No digests available\n");
> > +	}
> > +
> > +	rc = 0;
> > +out:
> > +	return rc;
> > +}
> > diff --git a/tests/restorecon/selinux_restorecon_skip.c
> > b/tests/restorecon/selinux_restorecon_skip.c
> > new file mode 100644
> > index 0000000..a09b658
> > --- /dev/null
> > +++ b/tests/restorecon/selinux_restorecon_skip.c
> > @@ -0,0 +1,66 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <getopt.h>
> > +#include <errno.h>
> > +#include <stdbool.h>
> > +#include <selinux/selinux.h>
> > +#include <selinux/label.h>
> > +#include <selinux/restorecon.h>
> > +
> > +static void usage(char *progname)
> > +{
> > +	fprintf(stderr,
> > +		"usage:  %s [-SIrv] <path>\n\n"
> > +		"Where:\n\t"
> > +		"-S  set SELINUX_RESTORECON_SKIP_DIGEST\n\t"
> > +		"-I  Set SELINUX_RESTORECON_IGNORE_DIGEST\n\t"
> > +		"-r  Recursively descend directories.\n\t"
> > +		"-v  Display information.\n\t"
> > +		"path  Path of file or directory to check.\n",
> > progname);
> > +	exit(-1);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +	int opt, rc, flags = 0;
> > +
> > +	if (argc < 2)
> > +		usage(argv[0]);
> > +
> > +	while ((opt = getopt(argc, argv, "SIrv")) > 0) {
> > +		switch (opt) {
> > +		case 'I':
> > +			flags |= SELINUX_RESTORECON_IGNORE_DIGEST;
> > +			break;
> > +		case 'S':
> > +			flags |= SELINUX_RESTORECON_SKIP_DIGEST;
> > +			break;
> > +		case 'r':
> > +			flags |= SELINUX_RESTORECON_RECURSE;
> > +			break;
> > +		case 'v':
> > +			flags |= SELINUX_RESTORECON_VERBOSE;
> > +			break;
> > +		default:
> > +			usage(argv[0]);
> > +		}
> > +	}
> > +
> > +	if (optind >= argc) {
> > +		fprintf(stderr, "No pathname specified\n");
> > +		return -1;
> > +	}
> > +
> > +	/*
> > +	 * selinux_restorecon() calls selabel_open(3) and by default
> > enables
> > +	 * digests.
> > +	 */
> > +	rc = selinux_restorecon(argv[optind], flags);
> > +	if (rc < 0)
> > +		fprintf(stderr, "selinux_restorecon ERROR: %s\n",
> > +			strerror(errno));
> > +
> > +	return rc;
> > +}
> > +
> > diff --git a/tests/restorecon/test b/tests/restorecon/test
> > new file mode 100755
> > index 0000000..a21765e
> > --- /dev/null
> > +++ b/tests/restorecon/test
> > @@ -0,0 +1,188 @@
> > +#!/usr/bin/perl
> > +use Test::More;
> > +
> > +# Options: -v = Verbose, -p <path_to_restorecon>
> > +# NOTE: If using the -p option to use a different version of
> > restorecon,
> > +# ensure they are labeled correctly before use. This can be
> > achieved by:
> > +#    chcon -h -t bin_t .../sbin/restorecon
> > +#    chcon -h -t setfiles_exec_t .../sbin/setfiles
> > +# The test_restorecon.te policy has rules to support this
> > labeling.
> > +
> > +BEGIN {
> > +    $basedir = $0;
> > +    $basedir =~ s|(.*)/[^/]*|$1|;
> > +
> > +    $v      = " ";
> > +    $bindir = $basedir;
> > +    $i      = 0;
> > +    foreach $arg (@ARGV) {
> > +        if ( $arg eq "-v" ) {
> > +            $v = $arg;
> > +        }
> > +        elsif ( $arg eq "-p" ) {
> > +            $bindir = $ARGV[ $i + 1 ];
> > +            if ( not -e "$bindir/restorecon" ) {
> > +                plan skip_all => "$bindir/restorecon not found";
> > +            }
> > +        }
> > +        $i++;
> > +    }
> > +
> > +    # check if /tmp is really type tmpfs (TMPFS_MAGIC).
> > +    $test_tmpfs = 0;
> > +    $result     = system("$basedir/check_fs $v /tmp 2>/dev/null");
> > +    if ( $result >> 8 eq 2 ) {
> > +        $test_tmpfs = 1;
> > +        plan tests => 12;
> > +    }
> > +    else {
> > +        plan tests => 11;
> > +    }
> > +}
> > +
> > +print "Using \"restorecon\" from $bindir\n";
> > +
> > +# Make sure test directory removed then generate new. Using a root
> > dir to test
> > +# libselinux: Ignore the stem when looking up all matches in file
> > context
> > +print "Generating test directories\n";
> > +system("rm -rf /restore_test");
> > +system("mkdir -p /restore_test/in_dir");
> > +system("mkdir -p /restore_test/out_dir");
> > +
> > +# Using semanage is much quicker than using semodule to build fc
> > entries.
> > +print "semanage adding file context entries\n";
> > +system("semanage fcontext -a -t test_file_t -f d /restore_test");
> > +system("semanage fcontext -a -t in_dir_t -f d
> > /restore_test/in_dir");
> > +system("semanage fcontext -a -t out_dir_t -f d
> > /restore_test/out_dir");
> > +
> > +print "Add files to the directories\n";
> > +system("touch /restore_test/out_dir/out_file1");
> > +system("touch /restore_test/in_dir/in_file1");
> > +
> > +print "Test no CAP_SYS_ADMIN (setxattr failed)\n";
> > +system(
> > +    "runcon -t test_restorecon_t $basedir/restorecon_xattr -rD $v
> > /restore_test"
> > +);
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +if ( $result >> 8 eq 4 ) {
> > +    print "Run selinux_restorecon with digests enabled and no
> > CAP_SYS_ADMIN\n";
> > +    system(
> > +"runcon -t test_no_admin_restorecon_t $bindir/restorecon -rD $v
> > /restore_test 2>&1"
> > +    );
> > +    print "Check there are no xattr digest entries\n";
> > +    $result = system(
> > +"runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test"
> > +    );
> > +    ok( $result >> 8 eq 4 );
> > +}
> > +else {
> > +    print "Failed no CAP_SYS_ADMIN test\n";
> > +    ok(0);
> > +}
> > +
> > +print "Run restorecon to add digest entries, then check they
> > match\n";
> > +system("runcon -t test_restorecon_t $bindir/restorecon -rD $v
> > /restore_test");
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +ok( $result >> 8 eq 1 );
> > +
> > +print "Add new file context entries, then check digests do not
> > match\n";
> > +system("semanage fcontext -a -t in_file_t -f f
> > \"/restore_test/in_dir(/.*)?\"");
> > +system(
> > +    "semanage fcontext -a -t out_file_t -f f
> > \"/restore_test/out_dir(/.*)?\"");
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +ok( $result eq 0 );
> > +
> > +print "Now fix with restorecon and check digests match\n";
> > +system("runcon -t test_restorecon_t $bindir/restorecon -rD $v
> > /restore_test");
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +ok( $result >> 8 eq 1 );
> > +
> > +print "Remove sehash entry on /restore_test/out_dir then check if
> > removed\n";
> > +system(
> > +"runcon -t test_restorecon_t setfattr -x security.sehash
> > /restore_test/out_dir"
> > +);
> > +$result = system(
> > +"runcon -t test_restorecon_t $basedir/get_all_digests $v
> > /restore_test/out_dir"
> > +);
> > +ok( $result >> 8 eq 4 );
> > +
> > +print
> > +  "Run restorecon with SELINUX_RESTORECON_IGNORE_DIGEST = TRUE.
> > This will\n";
> > +print "rewrite the missing digest, then check they match\n";
> > +system("runcon -t test_restorecon_t $bindir/restorecon -Ir $v
> > /restore_test");
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +ok( $result >> 8 eq 1 );
> > +
> > +print "Remove some file context entries, then check digests do not
> > match\n";
> > +system("semanage fcontext -d -t in_dir_t -f d
> > /restore_test/in_dir");
> > +system("semanage fcontext -d -t out_dir_t -f d
> > /restore_test/out_dir");
> > +system("semanage fcontext -d -t in_file_t -f f
> > \"/restore_test/in_dir(/.*)?\"");
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +ok( $result eq 0 );
> > +
> > +print "Run restorecon with digests disabled, then check digests
> > still do\n";
> > +print "not match as they were not updated\n";
> > +system("runcon -t test_restorecon_t $bindir/restorecon -r $v
> > /restore_test");
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +ok( $result eq 0 );
> > +
> > +print "Run restorecon with digests enabled, then check they
> > match\n";
> > +system("runcon -t test_restorecon_t $bindir/restorecon -rD $v
> > /restore_test");
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +ok( $result >> 8 eq 1 );
> > +
> > +print "Test SELINUX_RESTORECON_SKIP_DIGEST\n";
> > +system(
> > +    "runcon -t test_restorecon_t $basedir/restorecon_xattr -rD $v
> > /restore_test"
> > +);
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test");
> > +if ( $result >> 8 eq 4 ) {
> > +    print
> > +"Run selinux_restorecon with digests enabled and
> > SELINUX_RESTORECON_SKIP_DIGEST = TRUE\n";
> > +    system(
> > +"runcon -t test_restorecon_t $basedir/selinux_restorecon_skip -rS
> > $v /restore_test"
> > +    );
> > +
> > +    $result = system(
> > +"runcon -t test_restorecon_t $basedir/get_all_digests -r $v
> > /restore_test"
> > +    );
> > +    ok( $result >> 8 eq 4 );
> > +}
> > +else {
> > +    print "Failed SELINUX_RESTORECON_SKIP_DIGEST test\n";
> > +    ok(0);
> > +}
> > +
> > +system(
> > +    "semanage fcontext -d -t in_file_t -f f
> > \"/restore_test/out_dir(/.*)?\"");
> > +system("semanage fcontext -d -t test_file_t -f d /restore_test");
> > +system("rm -rf /restore_test");
> > +
> > +print
> > +  "Run restorecon on /sys with digests enabled, then check digests
> > are not\n";
> > +print "written as /sys is SYSFS_MAGIC.\n";
> > +system("runcon -t test_restorecon_t $bindir/restorecon -rD $v
> > /sys/fs/selinux");
> > +$result = system(
> > +    "runcon -t test_restorecon_t $basedir/get_all_digests $v
> > /sys/fs/selinux");
> > +ok( $result >> 8 eq 4 );
> > +
> > +if ($test_tmpfs) {
> > +    print
> > +"Run restorecon on /tmp with digests enabled, then check digests
> > are not\n";
> > +    print "written as /tmp is TMPFS_MAGIC\n";
> > +    system("runcon -t test_restorecon_t $bindir/restorecon -rD $v
> > /tmp");
> > +    $result =
> > +      system("runcon -t test_restorecon_t $basedir/get_all_digests
> > $v /tmp");
> > +    ok( $result >> 8 eq 4 );
> > +}
> > +
> > +exit;
> > 


      reply	other threads:[~2019-09-15 14:36 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-06-03  9:56 [RFC V3 PATCH] selinux-testsuite: Add test for restorecon Richard Haines
2019-09-13 17:09 ` Stephen Smalley
2019-09-15 14:35   ` Richard Haines [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2ada6c60bbac70094b069272772a73add3f3ddc9.camel@btinternet.com \
    --to=richard_c_haines@btinternet.com \
    --cc=paul@paul-moore.com \
    --cc=sds@tycho.nsa.gov \
    --cc=selinux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).