@@ -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
new file mode 100644
@@ -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)
@@ -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
new file mode 100644
@@ -0,0 +1,2 @@
+selinux_restorecon
+get_digests
new file mode 100644
@@ -0,0 +1,8 @@
+TARGETS = selinux_restorecon get_digests
+
+LDLIBS += -lselinux
+
+all: $(TARGETS)
+
+clean:
+ rm -f $(TARGETS)
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
+
new file mode 100755
@@ -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;
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@btinternet.com> --- 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