This will test the updated selinux_restorecon(3) that is currently in Android. Only use this to test the "libselinux: Save digest of all partial matches for directory" patch as it will probably change. Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx> --- policy/Makefile | 2 +- policy/test_restorecon.te | 40 +++++++++ tests/Makefile | 4 + tests/restorecon/.gitignore | 2 + tests/restorecon/Makefile | 8 ++ tests/restorecon/get_digests.c | 125 ++++++++++++++++++++++++++ tests/restorecon/selinux_restorecon.c | 68 ++++++++++++++ tests/restorecon/test | 101 +++++++++++++++++++++ 8 files changed, 349 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/get_digests.c create mode 100644 tests/restorecon/selinux_restorecon.c create mode 100755 tests/restorecon/test diff --git a/policy/Makefile b/policy/Makefile index cc70d33..a8c8b8a 100644 --- a/policy/Makefile +++ b/policy/Makefile @@ -25,7 +25,7 @@ TARGETS = \ test_task_getsid.te test_task_setpgid.te test_task_setsched.te \ test_transition.te test_inet_socket.te test_unix_socket.te \ test_mmap.te test_overlayfs.te test_mqueue.te test_mac_admin.te \ - test_ibpkey.te test_atsecure.te + test_ibpkey.te test_atsecure.te test_restorecon.te ifeq ($(shell [ $(POL_VERS) -ge 24 ] && echo true),true) TARGETS += test_bounds.te diff --git a/policy/test_restorecon.te b/policy/test_restorecon.te new file mode 100644 index 0000000..91cc6ea --- /dev/null +++ b/policy/test_restorecon.te @@ -0,0 +1,40 @@ +################################# +# +# Policy for testing restorecon +# + +require { + attribute file_type; +} + +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 process that can restorecon the test file. +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) + +# 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 fb6de5c..94eca9f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -47,6 +47,10 @@ ifeq ($(shell grep "^SELINUX_INFINIBAND_PKEY_TEST=" infiniband_pkey/ibpkey_test. SUBDIRS += infiniband_pkey endif +ifeq ($(shell grep -q selabel_get_digests_all_partial_matches $(INCLUDEDIR)/selinux/label.h && echo true),true) +SUBDIRS += restorecon +endif + ifeq ($(DISTRO),RHEL4) SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp_nosuid overlay unix_socket, $(SUBDIRS)) endif diff --git a/tests/restorecon/.gitignore b/tests/restorecon/.gitignore new file mode 100644 index 0000000..c37f923 --- /dev/null +++ b/tests/restorecon/.gitignore @@ -0,0 +1,2 @@ +selinux_restorecon +get_digests diff --git a/tests/restorecon/Makefile b/tests/restorecon/Makefile new file mode 100644 index 0000000..5c0d6e2 --- /dev/null +++ b/tests/restorecon/Makefile @@ -0,0 +1,8 @@ +TARGETS = selinux_restorecon get_digests + +LDLIBS += -lselinux + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) diff --git a/tests/restorecon/get_digests.c b/tests/restorecon/get_digests.c new file mode 100644 index 0000000..875ba3b --- /dev/null +++ b/tests/restorecon/get_digests.c @@ -0,0 +1,125 @@ +#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> + +#define RESTORECON_PARTIAL_MATCH_DIGEST "security.sehash" + +static void usage(char *progname) +{ + fprintf(stderr, + "usage: %s [-v] path\n\n" + "Where:\n\t" + "-v Display information.\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); +} + +int main(int argc, char **argv) +{ + int opt, rc = 0; /* The hashes do NOT match */ + size_t i, digest_len = 0; + bool status, verbose = false; + uint8_t *xattr_digest = NULL; + uint8_t *calculated_digest = NULL; + char *sha1_buf = NULL; + + struct selabel_handle *hnd; + + if (argc < 2) + usage(argv[0]); + + 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"); + exit(-1); + } + + hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (!hnd) { + fprintf(stderr, "ERROR: selabel_open - Could not obtain handle.\n"); + return -1; + } + + status = selabel_get_digests_all_partial_matches(hnd, argv[optind], + &calculated_digest, + &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; + } + + if (status) { /* They match */ + if (verbose) { + printf("xattr and file_contexts SHA1 digests match for: %s\n", + argv[optind]); + + 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", argv[optind]); + printf("as file_context entry is \"<<none>>\"\n"); + } + } + + if (calculated_digest && verbose) { + printf("The file_context entries for: %s\n", argv[optind]); + + 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); + } + } +out: + selabel_close(hnd); + free(xattr_digest); + free(calculated_digest); + free(sha1_buf); + return rc; +} diff --git a/tests/restorecon/selinux_restorecon.c b/tests/restorecon/selinux_restorecon.c new file mode 100644 index 0000000..7ea9387 --- /dev/null +++ b/tests/restorecon/selinux_restorecon.c @@ -0,0 +1,68 @@ +#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 [-vr]\n" + "Where:\n\t" + "-v Display information.\n\t" + "-r Recursively descend directories.\n", progname); + exit(-1); +} + +int main(int argc, char **argv) +{ + int opt, rc, flags = 0; + bool verbose = false; + + struct selabel_handle *hnd; + + if (argc < 2) + usage(argv[0]); + + while ((opt = getopt(argc, argv, "rv")) > 0) { + switch (opt) { + case 'r': + flags = SELINUX_RESTORECON_RECURSE; + break; + case 'v': + verbose = true; + break; + default: + usage(argv[0]); + } + } + + if (optind >= argc) { + fprintf(stderr, "No pathname specified\n"); + exit(-1); + } + + hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (!hnd) { + fprintf(stderr, "ERROR: selabel_open - Could not obtain handle.\n"); + return -1; + } + + /* Use own handle */ + selinux_restorecon_set_sehandle(hnd); + + if (verbose) + flags |= SELINUX_RESTORECON_VERBOSE; + + 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..208b6c4 --- /dev/null +++ b/tests/restorecon/test @@ -0,0 +1,101 @@ +#!/usr/bin/perl +use Test::More; + +BEGIN { + $basedir = $0; + $basedir =~ s|(.*)/[^/]*|$1|; + + # allow info to be shown + $v = $ARGV[0]; + if ($v) { + if ( $v ne "-v" ) { + plan skip_all => "Invalid option (use -v)"; + } + } + else { + $v = " "; + } + + plan tests => 6; +} + +# Need to get full path for semanage and tests progs +use Cwd qw(cwd); +$cwd = cwd; +if ( $basedir ne "." ) { + $path = "$cwd/$basedir"; +} +else { + $path = $cwd; +} + +# Make sure removed then generate new +system("rm -rf $basedir/restore_test"); +system("mkdir -p $basedir/restore_test/in_dir"); +system("mkdir -p $basedir/restore_test/out_dir"); + +system("semanage fcontext -a -t test_file_t -f d $path/restore_test"); +system("semanage fcontext -a -t in_dir_t -f d $path/restore_test/in_dir"); +system("semanage fcontext -a -t out_dir_t -f d $path/restore_test/out_dir"); + +# Add some files +system("touch $path/restore_test/out_dir/out_file1"); +system("touch $path/restore_test/in_dir/in_file1"); + +# There is no selinux.sehash xattr entry +$result = + system( + "runcon -t test_restorecon_t $basedir/get_digests $v $path/restore_test"); +ok( $result >> 8 eq 4 ); + +$result = + system( +"runcon -t test_restorecon_t $basedir/selinux_restorecon -r $v $path/restore_test" + ); +ok( $result eq 0 ); + +# After restorecon they match +$result = + system( + "runcon -t test_restorecon_t $basedir/get_digests $v $path/restore_test"); +ok( $result >> 8 eq 1 ); + +# Add new file_context enties to get the files relabeled: +system( + "semanage fcontext -a -t in_file_t -f f \"$path/restore_test/in_dir(/.*)?\"" +); +system( +"semanage fcontext -a -t out_file_t -f f \"$path/restore_test/out_dir(/.*)?\"" +); + +# Now the digests do NOT match +$result = + system( + "runcon -t test_restorecon_t $basedir/get_digests $v $path/restore_test"); +ok( $result eq 0 ); + +$result = + system( +"runcon -t test_restorecon_t $basedir/selinux_restorecon -r $v $path/restore_test" + ); +ok( $result eq 0 ); + +# After restorecon they match again +$result = + system( + "runcon -t test_restorecon_t $basedir/get_digests $v $path/restore_test"); +ok( $result >> 8 eq 1 ); + +# semanage x 10 takes 50s. Using semodule loading 2 x *.cil + 1 delete is slower 1m:40s +system("semanage fcontext -d -t in_dir_t -f d $path/restore_test/in_dir"); +system("semanage fcontext -d -t out_dir_t -f d $path/restore_test/out_dir"); +system( + "semanage fcontext -d -t in_file_t -f f \"$path/restore_test/in_dir(/.*)?\"" +); +system( +"semanage fcontext -d -t in_file_t -f f \"$path/restore_test/out_dir(/.*)?\"" +); +system("semanage fcontext -d -t test_file_t -f d $path/restore_test"); +system("rm -rf $basedir/restore_test"); + +exit; -- 2.20.1