This patch adds a set of simple tests that test if the functions for reading and writing filename transitions in avtab work properly. Reviewed-by: Ondrej Mosnacek <omosnace@xxxxxxxxxx> Signed-off-by: Juraj Marcin <juraj@xxxxxxxxxxxxxxx> --- security/selinux/Kconfig | 17 + security/selinux/Makefile | 2 + security/selinux/ss/avtab.c | 20 + security/selinux/ss/avtab_test.c | 729 +++++++++++++++++++++++++++++++ security/selinux/ss/symtab.c | 3 + 5 files changed, 771 insertions(+) create mode 100644 security/selinux/ss/avtab_test.c diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index 95a186ec0fcb..78cdc8d4232d 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -68,3 +68,20 @@ config SECURITY_SELINUX_SID2STR_CACHE_SIZE conversion. Setting this option to 0 disables the cache completely. If unsure, keep the default value. + +config SECURITY_SELINUX_KUNIT_TEST + tristate "Tests for SELinux" if !KUNIT_ALL_TESTS + depends on KUNIT && SECURITY_SELINUX + default KUNIT_ALL_TESTS + help + This builds the SELinux KUnit tests. + + KUnit tests run during boot and output the results to the debug log + in TAP format (https://testanything.org/). Only useful for kernel devs + running KUnit test harness and are not for inclusion into a + production build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. diff --git a/security/selinux/Makefile b/security/selinux/Makefile index ab8c3093d5fd..d3d63b28edcf 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -27,3 +27,5 @@ quiet_cmd_genhdrs = GEN $(addprefix $(obj)/,$(genhdrs)) targets += $(genhdrs) $(addprefix $(obj)/,$(genhdrs)) &: scripts/selinux/genheaders/genheaders FORCE $(call if_changed,genhdrs) + +obj-$(CONFIG_SECURITY_SELINUX_KUNIT_TEST) += ss/avtab_test.o diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 2cd9eabf796c..db7123670ef8 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -223,6 +223,9 @@ struct avtab_datum *avtab_search(struct avtab *h, const struct avtab_key *key) return NULL; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_search); + /* This search function returns a node pointer, and can be used in * conjunction with avtab_search_next_node() */ @@ -331,6 +334,9 @@ void avtab_destroy(struct avtab *h) h->mask = 0; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_destroy); + void avtab_init(struct avtab *h) { h->htable = NULL; @@ -378,6 +384,9 @@ int avtab_alloc(struct avtab *h, u32 nrules) return 0; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_alloc); + int avtab_alloc_dup(struct avtab *new, const struct avtab *orig) { return avtab_alloc_common(new, orig->nslot); @@ -766,6 +775,9 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) goto out; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_read); + static int avtab_trans_write_helper(void *k, void *d, void *fp) { char *name = k; @@ -900,6 +912,9 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp) return rc; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_write); + void __init avtab_cache_init(void) { avtab_node_cachep = kmem_cache_create("avtab_node", @@ -1105,6 +1120,8 @@ int avtab_filename_trans_read(struct avtab *a, void *fp, struct policydb *p) return 0; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_filename_trans_read); struct filenametr_write_args { void *fp; @@ -1406,3 +1423,6 @@ int avtab_filename_trans_write(struct policydb *p, struct avtab *a, void *fp) return rc; } + +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_filename_trans_write); diff --git a/security/selinux/ss/avtab_test.c b/security/selinux/ss/avtab_test.c new file mode 100644 index 000000000000..daa8e4cfaeb2 --- /dev/null +++ b/security/selinux/ss/avtab_test.c @@ -0,0 +1,729 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for access vector table type Implementation + * + * Author: Juraj Marcin <juraj@xxxxxxxxxxxxxxx> + */ + +#include <kunit/test.h> +#include "policydb.h" +#include "security.h" +#include "avtab.h" + +static void filename_trans_read__pre_filename_trans(struct kunit *test) +{ + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS - 1, + }; + struct policy_file fp = { + .data = NULL, + .len = 0, + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_EXPECT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 0, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); +} + +static void filename_trans_read__empty(struct kunit *test) +{ + char data[] = {0, 0, 0, 0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_EXPECT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 0, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); +} + +static void filename_trans_read__comp_empty(struct kunit *test) +{ + char data[] = {0, 0, 0, 0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_EXPECT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 0, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); +} + +static void filename_trans_read__simple(struct kunit *test) +{ + char data[] = { + 3, 0, 0, 0, /* count */ + + 5, 0, 0, 0, /* entry 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 name */ + 42, 0, 0, 0, /* entry 1 stype */ + 43, 0, 0, 0, /* entry 1 ttype */ + 44, 0, 0, 0, /* entry 1 tclass */ + 45, 0, 0, 0, /* entry 1 otype */ + + 5, 0, 0, 0, /* entry 2 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 name */ + 46, 0, 0, 0, /* entry 2 stype */ + 47, 0, 0, 0, /* entry 2 ttype */ + 48, 0, 0, 0, /* entry 2 tclass */ + 49, 0, 0, 0, /* entry 2 otype */ + + 5, 0, 0, 0, /* entry 3 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 3 name */ + 46, 0, 0, 0, /* entry 3 stype */ + 47, 0, 0, 0, /* entry 3 ttype */ + 48, 0, 0, 0, /* entry 3 tclass */ + 53, 0, 0, 0, /* entry 3 otype */ + }; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + struct avtab_key key; + struct avtab_datum *node; + u32 *otype; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + KUNIT_ASSERT_EQ(test, 0, avtab_alloc(&p.te_avtab, 3)); + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 2, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); + + key = (struct avtab_key){42, 43, 44, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file1"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 45, *otype); + + key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file2"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 49, *otype); + + otype = symtab_search(&node->u.trans->name_trans, "file3"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 53, *otype); + + avtab_destroy(&p.te_avtab); +} + +static void filename_trans_read__comp_simple(struct kunit *test) +{ + char data[] = { + 3, 0, 0, 0, /* count */ + + 5, 0, 0, 0, /* entry 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 name */ + 43, 0, 0, 0, /* entry 1 ttype */ + 44, 0, 0, 0, /* entry 1 tclass */ + 1, 0, 0, 0, /* entry 1 ndatum */ + 64, 0, 0, 0, /* entry 1 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 45, 0, 0, 0, /* entry 1 otype */ + + 5, 0, 0, 0, /* entry 2 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 name */ + 47, 0, 0, 0, /* entry 2 ttype */ + 48, 0, 0, 0, /* entry 2 tclass */ + 1, 0, 0, 0, /* entry 2 ndatum */ + 64, 0, 0, 0, /* entry 2 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 49, 0, 0, 0, /* entry 2 otype */ + + 5, 0, 0, 0, /* entry 3 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 3 name */ + 47, 0, 0, 0, /* entry 3 ttype */ + 48, 0, 0, 0, /* entry 3 tclass */ + 1, 0, 0, 0, /* entry 2 ndatum */ + 64, 0, 0, 0, /* entry 2 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 53, 0, 0, 0, /* entry 3 otype */ + }; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + struct avtab_key key; + struct avtab_datum *node; + u32 *otype; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + KUNIT_ASSERT_EQ(test, 0, avtab_alloc(&p.te_avtab, 3)); + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 2, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); + + key = (struct avtab_key){42, 43, 44, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file1"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 45, *otype); + + key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file2"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 49, *otype); + + otype = symtab_search(&node->u.trans->name_trans, "file3"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 53, *otype); + + avtab_destroy(&p.te_avtab); +} + +static void filename_trans_write__pre_filename_trans(struct kunit *test) +{ + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS - 1, + }; + struct policy_file fp = { + .data = NULL, + .len = 0, + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_EXPECT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + KUNIT_EXPECT_EQ(test, 0, fp.len); +} + +static void filename_trans_write__empty(struct kunit *test) +{ + char expected_data[] = {0, 0, 0, 0}; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + + KUNIT_EXPECT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void filename_trans_write__comp_empty(struct kunit *test) +{ + char expected_data[] = {0, 0, 0, 0}; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + + KUNIT_EXPECT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void filename_trans_write__simple(struct kunit *test) +{ + char expected_data[] = { + 3, 0, 0, 0, /* count */ + + 5, 0, 0, 0, /* entry 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 name */ + 42, 0, 0, 0, /* entry 1 stype */ + 43, 0, 0, 0, /* entry 1 ttype */ + 44, 0, 0, 0, /* entry 1 tclass */ + 45, 0, 0, 0, /* entry 1 otype */ + + 5, 0, 0, 0, /* entry 2 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 name */ + 46, 0, 0, 0, /* entry 2 stype */ + 47, 0, 0, 0, /* entry 2 ttype */ + 48, 0, 0, 0, /* entry 2 tclass */ + 49, 0, 0, 0, /* entry 2 otype */ + + 5, 0, 0, 0, /* entry 3 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 3 name */ + 46, 0, 0, 0, /* entry 3 stype */ + 47, 0, 0, 0, /* entry 3 ttype */ + 48, 0, 0, 0, /* entry 3 tclass */ + 53, 0, 0, 0, /* entry 3 otype */ + }; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + u32 otypes[] = {45, 49, 53}; + struct hashtab_node nhnodes[] = { + {"file1", &otypes[0], NULL}, + {"file2", &otypes[1], &nhnodes[2]}, + {"file3", &otypes[2], NULL}, + }; + struct hashtab_node *nhtable[] = {&nhnodes[0], &nhnodes[1]}; + struct avtab_trans transs[] = { + {0, {{&nhtable[0], 1, 1}, 0}}, + {0, {{&nhtable[1], 1, 2}, 0}}, + }; + struct avtab_node nodes[] = { + {{42, 43, 44, AVTAB_TRANSITION}, + {.u.trans = &transs[0]}, NULL}, + {{46, 47, 48, AVTAB_TRANSITION}, + {.u.trans = &transs[1]}, NULL}, + }; + struct avtab_node *htable[] = {&nodes[0], &nodes[1]}; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + p.te_avtab.htable = htable; + p.te_avtab.nslot = 2; + p.te_avtab.nel = 2; + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + + KUNIT_ASSERT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void filename_trans_write__comp_simple(struct kunit *test) +{ + char expected_data[] = { + 3, 0, 0, 0, /* count */ + + 5, 0, 0, 0, /* entry 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 name */ + 43, 0, 0, 0, /* entry 1 ttype */ + 44, 0, 0, 0, /* entry 1 tclass */ + 1, 0, 0, 0, /* entry 1 ndatum */ + 64, 0, 0, 0, /* entry 1 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 45, 0, 0, 0, /* entry 1 otype */ + + 5, 0, 0, 0, /* entry 2 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 name */ + 47, 0, 0, 0, /* entry 2 ttype */ + 48, 0, 0, 0, /* entry 2 tclass */ + 1, 0, 0, 0, /* entry 2 ndatum */ + 64, 0, 0, 0, /* entry 2 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 49, 0, 0, 0, /* entry 2 otype */ + + 5, 0, 0, 0, /* entry 3 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 3 name */ + 47, 0, 0, 0, /* entry 3 ttype */ + 48, 0, 0, 0, /* entry 3 tclass */ + 1, 0, 0, 0, /* entry 2 ndatum */ + 64, 0, 0, 0, /* entry 2 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 53, 0, 0, 0, /* entry 3 otype */ + }; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + u32 otypes[] = {45, 49, 53}; + struct hashtab_node nhnodes[] = { + {"file1", &otypes[0], NULL}, + {"file2", &otypes[1], &nhnodes[2]}, + {"file3", &otypes[2], NULL}, + }; + struct hashtab_node *nhtable[] = {&nhnodes[0], &nhnodes[1]}; + struct avtab_trans transs[] = { + {0, {{&nhtable[0], 1, 1}, 0}}, + {0, {{&nhtable[1], 1, 2}, 0}}, + }; + struct avtab_node nodes[] = { + {{42, 43, 44, AVTAB_TRANSITION}, + {.u.trans = &transs[0]}, NULL}, + {{46, 47, 48, AVTAB_TRANSITION}, + {.u.trans = &transs[1]}, NULL}, + }; + struct avtab_node *htable[] = {&nodes[0], &nodes[1]}; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + p.te_avtab.htable = htable; + p.te_avtab.nslot = 2; + p.te_avtab.nel = 2; + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + + KUNIT_ASSERT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void read__pre_avtab_ftrans(struct kunit *test) +{ + char data[] = { + 2, 0, 0, 0, /* nel */ + + 42, 0, /* entry 1 source type */ + 43, 0, /* entry 1 target type */ + 44, 0, /* entry 1 target class */ + AVTAB_TRANSITION, 0, /* entry 1 specified */ + 45, 0, 0, 0, /* entry 1 otype */ + + 46, 0, /* entry 2 source type */ + 47, 0, /* entry 2 target type */ + 48, 0, /* entry 2 target class */ + AVTAB_TRANSITION, 0, /* entry 2 specified */ + 49, 0, 0, 0, /* entry 2 otype */ + }; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + struct avtab_key key; + struct avtab_datum *node; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + KUNIT_ASSERT_EQ(test, 0, + avtab_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 2, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); + + key = (struct avtab_key){42, 43, 44, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 45, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->name_trans.table.nel); + + key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 49, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->name_trans.table.nel); + + avtab_destroy(&p.te_avtab); +} + +static void read__simple(struct kunit *test) +{ + char data[] = { + 2, 0, 0, 0, /* nel */ + + 42, 0, /* entry 1 source type */ + 43, 0, /* entry 1 target type */ + 44, 0, /* entry 1 target class */ + AVTAB_TRANSITION, 0, /* entry 1 specified */ + 41, 0, 0, 0, /* entry 1 otype */ + 1, 0, 0, 0, /* entry 1 nfnts */ + 45, 0, 0, 0, /* entry 1 fnt 1 otype */ + 5, 0, 0, 0, /* entry 1 fnt 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 fnt 1 name */ + + 46, 0, /* entry 2 source type */ + 47, 0, /* entry 2 target type */ + 48, 0, /* entry 2 target class */ + AVTAB_TRANSITION, 0, /* entry 2 specified */ + 40, 0, 0, 0, /* entry 2 otype */ + 2, 0, 0, 0, /* entry 2 nfnts */ + 49, 0, 0, 0, /* entry 2 fnt 1 otype */ + 5, 0, 0, 0, /* entry 2 fnt 1 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 fnt 1 name */ + 50, 0, 0, 0, /* entry 2 fnt 2 otype */ + 5, 0, 0, 0, /* entry 2 fnt 2 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 2 fnt 2 name */ + }; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_AVTAB_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + struct avtab_key key; + struct avtab_datum *node; + u32 *otype; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + KUNIT_ASSERT_EQ(test, 0, + avtab_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 2, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); + + key = (struct avtab_key){42, 43, 44, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 41, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file1"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 45, *otype); + + key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 40, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file2"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 49, *otype); + + otype = symtab_search(&node->u.trans->name_trans, "file3"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 50, *otype); + + avtab_destroy(&p.te_avtab); +} + +static void write__pre_avtab_ftrans(struct kunit *test) +{ + char expected_data[] = { + 1, 0, 0, 0, /* nel */ + + 46, 0, /* entry 2 source type */ + 47, 0, /* entry 2 target type */ + 48, 0, /* entry 2 target class */ + AVTAB_TRANSITION, 0, /* entry 2 specified */ + 40, 0, 0, 0, /* entry 2 otype */ + }; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + u32 otypes[] = {45, 49, 53}; + struct hashtab_node nhnodes[] = { + {"file1", &otypes[0], NULL}, + {"file2", &otypes[1], &nhnodes[2]}, + {"file3", &otypes[2], NULL}, + }; + struct hashtab_node *nhtable[] = {&nhnodes[0], &nhnodes[1]}; + struct avtab_trans transs[] = { + {0, {{&nhtable[0], 1, 1}, 0}}, + {40, {{&nhtable[1], 1, 2}, 0}}, + }; + struct avtab_node nodes[] = { + {{42, 43, 44, AVTAB_TRANSITION}, + {.u.trans = &transs[0]}, NULL}, + {{46, 47, 48, AVTAB_TRANSITION}, + {.u.trans = &transs[1]}, NULL}, + }; + struct avtab_node *htable[] = {&nodes[0], &nodes[1]}; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + p.te_avtab.htable = htable; + p.te_avtab.nslot = 2; + p.te_avtab.nel = 2; + + KUNIT_ASSERT_EQ(test, 0, avtab_write(&p, &p.te_avtab, &fp)); + + KUNIT_ASSERT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void write__simple(struct kunit *test) +{ + char expected_data[] = { + 2, 0, 0, 0, /* nel */ + + 42, 0, /* entry 1 source type */ + 43, 0, /* entry 1 target type */ + 44, 0, /* entry 1 target class */ + AVTAB_TRANSITION, 0, /* entry 1 specified */ + 41, 0, 0, 0, /* entry 1 otype */ + 1, 0, 0, 0, /* entry 1 nfnts */ + 45, 0, 0, 0, /* entry 1 fnt 1 otype */ + 5, 0, 0, 0, /* entry 1 fnt 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 fnt 1 name */ + + 46, 0, /* entry 2 source type */ + 47, 0, /* entry 2 target type */ + 48, 0, /* entry 2 target class */ + AVTAB_TRANSITION, 0, /* entry 2 specified */ + 40, 0, 0, 0, /* entry 2 otype */ + 2, 0, 0, 0, /* entry 2 nfnts */ + 49, 0, 0, 0, /* entry 1 fnt 1 otype */ + 5, 0, 0, 0, /* entry 1 fnt 1 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 1 fnt 1 name */ + 50, 0, 0, 0, /* entry 1 fnt 1 otype */ + 5, 0, 0, 0, /* entry 1 fnt 1 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 1 fnt 1 name */ + }; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_AVTAB_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + u32 otypes[] = {45, 49, 50}; + struct hashtab_node nhnodes[] = { + {"file1", &otypes[0], NULL}, + {"file2", &otypes[1], &nhnodes[2]}, + {"file3", &otypes[2], NULL}, + }; + struct hashtab_node *nhtable[] = {&nhnodes[0], &nhnodes[1]}; + struct avtab_trans transs[] = { + {41, {{&nhtable[0], 1, 1}, 0}}, + {40, {{&nhtable[1], 1, 2}, 0}}, + }; + struct avtab_node nodes[] = { + {{42, 43, 44, AVTAB_TRANSITION}, + {.u.trans = &transs[0]}, NULL}, + {{46, 47, 48, AVTAB_TRANSITION}, + {.u.trans = &transs[1]}, NULL}, + }; + struct avtab_node *htable[] = {&nodes[0], &nodes[1]}; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + p.te_avtab.htable = htable; + p.te_avtab.nslot = 2; + p.te_avtab.nel = 2; + + KUNIT_ASSERT_EQ(test, 0, avtab_write(&p, &p.te_avtab, &fp)); + + KUNIT_ASSERT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static struct kunit_case avtab_test_cases[] = { + KUNIT_CASE(filename_trans_read__pre_filename_trans), + KUNIT_CASE(filename_trans_read__empty), + KUNIT_CASE(filename_trans_read__comp_empty), + KUNIT_CASE(filename_trans_read__simple), + KUNIT_CASE(filename_trans_read__comp_simple), + + KUNIT_CASE(filename_trans_write__pre_filename_trans), + KUNIT_CASE(filename_trans_write__empty), + KUNIT_CASE(filename_trans_write__comp_empty), + KUNIT_CASE(filename_trans_write__simple), + KUNIT_CASE(filename_trans_write__comp_simple), + + KUNIT_CASE(read__pre_avtab_ftrans), + KUNIT_CASE(read__simple), + + KUNIT_CASE(write__pre_avtab_ftrans), + KUNIT_CASE(write__simple), + {0}, +}; + +static struct kunit_suite avtab_test_suite = { + .name = "security-selinux-avtab", + .test_cases = avtab_test_cases, +}; + +kunit_test_suite(avtab_test_suite); + +MODULE_LICENSE("GPL"); diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index c42a6648a07d..c53863d5ed40 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -52,3 +52,6 @@ void *symtab_search(struct symtab *s, const char *name) { return hashtab_search(&s->table, name, symtab_key_params); } + +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(symtab_search); -- 2.40.0