[PATCH testsuite 2/2] tests/file_contexts: add tests for multiple SELABEL_OPT_PATH

"Thiébaud Weksteen" <[email protected]>
Newsgroups org.kernel.vger.selinux
Message-ID <[email protected]>
Add unit tests in test_multiple.c to exercise opening the file contexts
backend with multiple SELABEL_OPT_PATH options under various validation
and path configuration scenarios.

The following test functions were added:
- Verifies opening multiple file contexts without validation.
- Verifies validation failure when contexts contain undefined types.
- Verifies identical contexts across multiple paths validate successfully.
- Verifies conflicting contexts across multiple paths fail validation.
- Verifies extra files (.subs, .local, .homedirs) are processed only for
  the primary path.
- Verifies context lookups when providing three distinct
  SELABEL_OPT_PATH options.

Signed-off-by: Thiébaud Weksteen <[email protected]>
---
 tests/file_contexts/Makefile        |   2 +-
 tests/file_contexts/f2_conflict.fc  |   1 +
 tests/file_contexts/f2_same.fc      |   1 +
 tests/file_contexts/test            |   5 +-
 tests/file_contexts/test_multiple.c | 241 ++++++++++++++++++++++++++++
 5 files changed, 248 insertions(+), 2 deletions(-)
 create mode 100644 tests/file_contexts/f2_conflict.fc
 create mode 100644 tests/file_contexts/f2_same.fc
 create mode 100644 tests/file_contexts/test_multiple.c

diff --git a/tests/file_contexts/Makefile b/tests/file_contexts/Makefile
index 592a65f..083ef25 100644
--- a/tests/file_contexts/Makefile
+++ b/tests/file_contexts/Makefile
@@ -1,4 +1,4 @@
-TARGETS=test_open test_lookup test_open_base test_validate
+TARGETS=test_open test_lookup test_open_base test_validate test_multiple
 CFLAGS += -O2 -Werror -Wall
 LDLIBS += -lselinux
 
diff --git a/tests/file_contexts/f2_conflict.fc b/tests/file_contexts/f2_conflict.fc
new file mode 100644
index 0000000..8cc388a
--- /dev/null
+++ b/tests/file_contexts/f2_conflict.fc
@@ -0,0 +1 @@
+/base             system_u:object_r:test_file_list:s0
diff --git a/tests/file_contexts/f2_same.fc b/tests/file_contexts/f2_same.fc
new file mode 100644
index 0000000..61a994f
--- /dev/null
+++ b/tests/file_contexts/f2_same.fc
@@ -0,0 +1 @@
+/base             system_u:object_r:test_base_t:s0
diff --git a/tests/file_contexts/test b/tests/file_contexts/test
index 1534925..cd7849b 100755
--- a/tests/file_contexts/test
+++ b/tests/file_contexts/test
@@ -5,7 +5,7 @@
 
 use Test;
 
-BEGIN { plan tests => 4; }
+BEGIN { plan tests => 5; }
 
 $basedir = $0;
 $basedir =~ s|(.*)/[^/]*|$1|;
@@ -22,4 +22,7 @@ ok( $result, 0 );
 $result = system "$basedir/test_validate $basedir 2>&1";
 ok( $result, 0 );
 
+$result = system "$basedir/test_multiple $basedir 2>&1";
+ok( $result, 0 );
+
 exit;
diff --git a/tests/file_contexts/test_multiple.c b/tests/file_contexts/test_multiple.c
new file mode 100644
index 0000000..d64c5c4
--- /dev/null
+++ b/tests/file_contexts/test_multiple.c
@@ -0,0 +1,241 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+#include "internal.h"
+
+void test_multiple_no_validation(const char *basedir)
+{
+	struct selabel_handle *hnd;
+
+	/* f1.fc and f2.fc file */
+	char *f1_path, *f2_path;
+	asprintf(&f1_path, "%s/f1.fc", basedir);
+	asprintf(&f2_path, "%s/f2.fc", basedir);
+	struct selinux_opt opts[] = {
+		{ .type = SELABEL_OPT_PATH, .value = f1_path },
+		{ .type = SELABEL_OPT_PATH, .value = f2_path }
+	};
+
+	hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts));
+	free(f1_path);
+	free(f2_path);
+
+	if (!hnd) {
+		log_errno("Unable to open file backend");
+		exit(2);
+	}
+
+	struct test_t tests[] = {
+		{ .path = "/", .context = "system_u:object_r:rootfs:s0" },
+		{ .path = "/base",
+		  .context = "system_u:object_r:test_base_t:s0" },
+	};
+	assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests));
+
+	selabel_close(hnd);
+}
+
+void test_multiple_with_validation(const char *basedir)
+{
+	struct selabel_handle *hnd;
+
+	/* f1.fc and f2.fc file - f1 has undefined type rootfs in test policy */
+	char *f1_path, *f2_path;
+	asprintf(&f1_path, "%s/f1.fc", basedir);
+	asprintf(&f2_path, "%s/f2.fc", basedir);
+	struct selinux_opt opts[] = {
+		{ .type = SELABEL_OPT_PATH, .value = f1_path },
+		{ .type = SELABEL_OPT_PATH, .value = f2_path },
+		{ .type = SELABEL_OPT_VALIDATE, .value = "1" }
+	};
+
+	hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts));
+	free(f1_path);
+	free(f2_path);
+
+	if (hnd) {
+		log_err("Validation of f1 and f2 should have failed");
+		selabel_close(hnd);
+		exit(2);
+	}
+}
+
+void test_multiple_same_spec_validation(const char *basedir)
+{
+	struct selabel_handle *hnd;
+
+	/* f2.fc and f2_same.fc contain identical specs for /base. A warning will be emitted, but no failure is expected */
+	char *f2_path, *same_path;
+	asprintf(&f2_path, "%s/f2.fc", basedir);
+	asprintf(&same_path, "%s/f2_same.fc", basedir);
+	struct selinux_opt opts[] = {
+		{ .type = SELABEL_OPT_PATH, .value = f2_path },
+		{ .type = SELABEL_OPT_PATH, .value = same_path },
+		{ .type = SELABEL_OPT_VALIDATE, .value = "1" }
+	};
+
+	hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts));
+	free(f2_path);
+	free(same_path);
+
+	if (!hnd) {
+		log_errno("Unable to open file backend");
+		exit(2);
+	}
+
+	struct test_t tests[] = {
+		{ .path = "/base",
+		  .context = "system_u:object_r:test_base_t:s0" },
+	};
+	assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests));
+
+	selabel_close(hnd);
+}
+
+void test_multiple_different_spec_validation(const char *basedir)
+{
+	struct selabel_handle *hnd;
+
+	/* f2.fc and f2_conflict.fc contain conflicting contexts for /base */
+	char *f2_path, *conflict_path;
+	asprintf(&f2_path, "%s/f2.fc", basedir);
+	asprintf(&conflict_path, "%s/f2_conflict.fc", basedir);
+	struct selinux_opt opts[] = {
+		{ .type = SELABEL_OPT_PATH, .value = f2_path },
+		{ .type = SELABEL_OPT_PATH, .value = conflict_path },
+		{ .type = SELABEL_OPT_VALIDATE, .value = "1" }
+	};
+
+	hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts));
+	free(f2_path);
+	free(conflict_path);
+
+	if (hnd) {
+		log_err("Validation of f2 and f2_conflict should have failed");
+		selabel_close(hnd);
+		exit(2);
+	}
+}
+
+void test_multiple_with_extras(const char *basedir)
+{
+	struct selabel_handle *hnd;
+	char *f2_path, *f3_path;
+	asprintf(&f2_path, "%s/f2.fc", basedir);
+	asprintf(&f3_path, "%s/f3.fc", basedir);
+
+	/* 1. f3.fc is the first path: extras (.subs, .local, .homedirs) of f3 ARE processed */
+	struct selinux_opt opts_f3_first[] = {
+		{ .type = SELABEL_OPT_PATH, .value = f3_path },
+		{ .type = SELABEL_OPT_PATH, .value = f2_path }
+	};
+	hnd = selabel_open(SELABEL_CTX_FILE, opts_f3_first,
+			   ARRAY_SIZE(opts_f3_first));
+	if (!hnd) {
+		log_errno("Unable to open file backend");
+		exit(2);
+	}
+
+	struct test_t tests_f3_first[] = {
+		{ .path = "/", .context = "system_u:object_r:rootfs:s0" },
+		{ .path = "/sub",
+		  .context = "system_u:object_r:test_subbed:s0" },
+		{ .path = "/local",
+		  .context = "system_u:object_r:test_local:s0" },
+		{ .path = "/homedirs",
+		  .context = "system_u:object_r:test_homedirs:s0" },
+		{ .path = "/base",
+		  .context = "system_u:object_r:test_base_t:s0" },
+	};
+	assertContextsMatch(hnd, __func__, tests_f3_first,
+			    ARRAY_SIZE(tests_f3_first));
+	selabel_close(hnd);
+
+	/* 2. f2.fc is the first path, f3.fc is second: extras from f3 are NOT processed */
+	struct selinux_opt opts_f2_first[] = {
+		{ .type = SELABEL_OPT_PATH, .value = f2_path },
+		{ .type = SELABEL_OPT_PATH, .value = f3_path }
+	};
+	hnd = selabel_open(SELABEL_CTX_FILE, opts_f2_first,
+			   ARRAY_SIZE(opts_f2_first));
+	if (!hnd) {
+		log_errno("Unable to open file backend");
+		exit(2);
+	}
+
+	struct test_t tests_f2_first[] = {
+		{ .path = "/base",
+		  .context = "system_u:object_r:test_base_t:s0" },
+		{ .path = "/", .context = "system_u:object_r:rootfs:s0" },
+		{ .path = "/subbed",
+		  .context = "system_u:object_r:test_subbed:s0" },
+		/* /sub, /local, /homedirs should NOT match the f3 extra contexts */
+		{ .path = "/sub", .context = NULL },
+		{ .path = "/local", .context = NULL },
+		{ .path = "/homedirs", .context = NULL },
+	};
+	assertContextsMatch(hnd, __func__, tests_f2_first,
+			    ARRAY_SIZE(tests_f2_first));
+	selabel_close(hnd);
+
+	free(f2_path);
+	free(f3_path);
+}
+
+void test_multiple_three_paths(const char *basedir)
+{
+	struct selabel_handle *hnd;
+	char *f1_path, *f2_path, *f3_path;
+	asprintf(&f1_path, "%s/f1.fc", basedir);
+	asprintf(&f2_path, "%s/f2.fc", basedir);
+	asprintf(&f3_path, "%s/f3.fc", basedir);
+
+	struct selinux_opt opts[] = {
+		{ .type = SELABEL_OPT_PATH, .value = f1_path },
+		{ .type = SELABEL_OPT_PATH, .value = f2_path },
+		{ .type = SELABEL_OPT_PATH, .value = f3_path }
+	};
+
+	hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts));
+	free(f1_path);
+	free(f2_path);
+	free(f3_path);
+
+	if (!hnd) {
+		log_errno("Unable to open file backend");
+		exit(2);
+	}
+
+	struct test_t tests[] = {
+		{ .path = "/", .context = "system_u:object_r:rootfs:s0" },
+		{ .path = "/base",
+		  .context = "system_u:object_r:test_base_t:s0" },
+		{ .path = "/subbed",
+		  .context = "system_u:object_r:test_subbed:s0" },
+	};
+	assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests));
+	selabel_close(hnd);
+}
+
+int main(int argc, char **argv)
+{
+	if (argc != 2) {
+		log_err("basedir not provided");
+		exit(1);
+	}
+
+	test_multiple_no_validation(argv[1]);
+	test_multiple_with_validation(argv[1]);
+	test_multiple_same_spec_validation(argv[1]);
+	test_multiple_different_spec_validation(argv[1]);
+	test_multiple_with_extras(argv[1]);
+	test_multiple_three_paths(argv[1]);
+
+	return 0;
+}
-- 
2.55.0.229.g6434b31f56-goog
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.