On Mon Apr 15, 2024 at 5:24 PM EEST, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
Add tests to verify the correctness of the digest_cache LSM, in all_test.c.
Add the kernel module digest_cache_kern.ko, to let all_test call the API of the digest_cache LSM through the newly introduced digest_cache_test file in securityfs.
Test coverage information:
File 'security/digest_cache/notifier.c' Lines executed:100.00% of 31 File 'security/digest_cache/reset.c' Lines executed:98.36% of 61 File 'security/digest_cache/main.c' Lines executed:90.29% of 206 File 'security/digest_cache/modsig.c' Lines executed:42.86% of 21 File 'security/digest_cache/htable.c' Lines executed:93.02% of 86 File 'security/digest_cache/populate.c' Lines executed:92.86% of 56 File 'security/digest_cache/verif.c' Lines executed:89.74% of 39 File 'security/digest_cache/dir.c' Lines executed:90.62% of 96 File 'security/digest_cache/secfs.c' Lines executed:57.14% of 21 File 'security/digest_cache/parsers/tlv.c' Lines executed:79.75% of 79 File 'security/digest_cache/parsers/rpm.c' Lines executed:88.46% of 78
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + .../testing/selftests/digest_cache/.gitignore | 3 + tools/testing/selftests/digest_cache/Makefile | 24 + .../testing/selftests/digest_cache/all_test.c | 815 ++++++++++++++++++ tools/testing/selftests/digest_cache/common.c | 78 ++ tools/testing/selftests/digest_cache/common.h | 135 +++ .../selftests/digest_cache/common_user.c | 47 + .../selftests/digest_cache/common_user.h | 17 + tools/testing/selftests/digest_cache/config | 1 + .../selftests/digest_cache/generators.c | 248 ++++++ .../selftests/digest_cache/generators.h | 19 + .../selftests/digest_cache/testmod/Makefile | 16 + .../selftests/digest_cache/testmod/kern.c | 564 ++++++++++++ 14 files changed, 1969 insertions(+) create mode 100644 tools/testing/selftests/digest_cache/.gitignore create mode 100644 tools/testing/selftests/digest_cache/Makefile create mode 100644 tools/testing/selftests/digest_cache/all_test.c create mode 100644 tools/testing/selftests/digest_cache/common.c create mode 100644 tools/testing/selftests/digest_cache/common.h create mode 100644 tools/testing/selftests/digest_cache/common_user.c create mode 100644 tools/testing/selftests/digest_cache/common_user.h create mode 100644 tools/testing/selftests/digest_cache/config create mode 100644 tools/testing/selftests/digest_cache/generators.c create mode 100644 tools/testing/selftests/digest_cache/generators.h create mode 100644 tools/testing/selftests/digest_cache/testmod/Makefile create mode 100644 tools/testing/selftests/digest_cache/testmod/kern.c
diff --git a/MAINTAINERS b/MAINTAINERS index 72801a88449c..d7f700da009e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6198,6 +6198,7 @@ M: Roberto Sassu roberto.sassu@huawei.com L: linux-security-module@vger.kernel.org S: Maintained F: security/digest_cache/ +F: tools/testing/selftests/digest_cache/
A common convetion is to have one patch with MAINTAINERS update in the tail. This is now sprinkled to multiple patches which is not good.
DIGITEQ AUTOMOTIVE MGB4 V4L2 DRIVER M: Martin Tuma martin.tuma@digiteqautomotive.com diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 15b6a111c3be..3c5965a62d28 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -13,6 +13,7 @@ TARGETS += core TARGETS += cpufreq TARGETS += cpu-hotplug TARGETS += damon +TARGETS += digest_cache TARGETS += dmabuf-heaps TARGETS += drivers/dma-buf TARGETS += drivers/s390x/uvdevice diff --git a/tools/testing/selftests/digest_cache/.gitignore b/tools/testing/selftests/digest_cache/.gitignore new file mode 100644 index 000000000000..392096e18f4e --- /dev/null +++ b/tools/testing/selftests/digest_cache/.gitignore @@ -0,0 +1,3 @@ +/*.mod +/*_test +/*.ko diff --git a/tools/testing/selftests/digest_cache/Makefile b/tools/testing/selftests/digest_cache/Makefile new file mode 100644 index 000000000000..6b1e0d3c08cf --- /dev/null +++ b/tools/testing/selftests/digest_cache/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_GEN_PROGS_EXTENDED = digest_cache_kern.ko +TEST_GEN_PROGS := all_test
+$(OUTPUT)/%.ko: $(wildcard common.[ch]) testmod/Makefile testmod/kern.c
- $(call msg,MOD,,$@)
- $(Q)$(MAKE) -C testmod
- $(Q)cp testmod/digest_cache_kern.ko $@
+LOCAL_HDRS += common.h common_user.h generators.h +CFLAGS += -ggdb -Wall -Wextra $(KHDR_INCLUDES)
+OVERRIDE_TARGETS := 1 +override define CLEAN
- $(call msg,CLEAN)
- $(Q)$(MAKE) -C testmod clean
- rm -Rf $(TEST_GEN_PROGS)
- rm -Rf $(OUTPUT)/common.o $(OUTPUT)/common_user.o $(OUTPUT)/generators.o
- rm -Rf $(OUTPUT)/common.mod
+endef
+include ../lib.mk
+$(OUTPUT)/all_test: common.c common.h common_user.c common_user.h generators.c diff --git a/tools/testing/selftests/digest_cache/all_test.c b/tools/testing/selftests/digest_cache/all_test.c new file mode 100644 index 000000000000..9f45e522c43c --- /dev/null +++ b/tools/testing/selftests/digest_cache/all_test.c @@ -0,0 +1,815 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
- Author: Roberto Sassu roberto.sassu@huawei.com
- Implement the tests of the digest_cache LSM.
- */
+#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include <fts.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/xattr.h> +#include <sys/syscall.h> +#include <linux/module.h>
+#include "generators.h"
+#include "../kselftest_harness.h" +#include "../../../../include/uapi/linux/xattr.h"
+#define BASE_DIR_TEMPLATE "/tmp/digest_cache_test_dirXXXXXX" +#define DIGEST_LISTS_SUBDIR "digest_lists" +#define NUM_DIGEST_LISTS_PREFETCH MAX_WORKS
+FIXTURE(shared_data) {
- char base_dir[sizeof(BASE_DIR_TEMPLATE)];
- char digest_lists_dir[sizeof(BASE_DIR_TEMPLATE) +
sizeof(DIGEST_LISTS_SUBDIR)];
- int base_dirfd, digest_lists_dirfd, kernfd, pathfd, cmd_len;
- int notify_inodesfd;
+};
+FIXTURE_SETUP(shared_data) +{
- char cmd[1024];
- int fd, i, cmd_len;
- /* Create the base directory. */
- snprintf(self->base_dir, sizeof(self->base_dir), BASE_DIR_TEMPLATE);
- ASSERT_NE(NULL, mkdtemp(self->base_dir));
- /* Open base directory. */
- self->base_dirfd = open(self->base_dir, O_RDONLY | O_DIRECTORY);
- ASSERT_NE(-1, self->base_dirfd);
- /* Create the digest_lists subdirectory. */
- snprintf(self->digest_lists_dir, sizeof(self->digest_lists_dir),
"%s/%s", self->base_dir, DIGEST_LISTS_SUBDIR);
- ASSERT_EQ(0, mkdirat(self->base_dirfd, DIGEST_LISTS_SUBDIR, 0600));
- self->digest_lists_dirfd = openat(self->base_dirfd, DIGEST_LISTS_SUBDIR,
O_RDONLY | O_DIRECTORY);
- ASSERT_NE(-1, self->digest_lists_dirfd);
- fd = open("digest_cache_kern.ko", O_RDONLY);
- ASSERT_LT(0, fd);
- ASSERT_EQ(0, syscall(SYS_finit_module, fd, "", 0));
- close(fd);
- /* Open kernel test interface. */
- self->kernfd = open(DIGEST_CACHE_TEST_INTERFACE, O_RDWR, 0600);
- ASSERT_NE(-1, self->kernfd);
- /* Open kernel notify inodes interface. */
- self->notify_inodesfd = open(DIGEST_CACHE_NOTIFY_INODES_INTERFACE,
O_RDWR, 0600);
- ASSERT_NE(-1, self->notify_inodesfd);
- /* Open kernel digest list path interface. */
- self->pathfd = open(DIGEST_CACHE_PATH_INTERFACE, O_RDWR, 0600);
- ASSERT_NE(-1, self->pathfd);
- /* Write the path of the digest lists directory. */
- ASSERT_LT(0, write(self->pathfd, self->digest_lists_dir,
strlen(self->digest_lists_dir)));
- /* Ensure that no verifier is enabled at the beginning of a test. */
- for (i = 0; i < VERIF__LAST; i++) {
cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s",
commands_str[DIGEST_CACHE_DISABLE_VERIF],
verifs_str[i]);
ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- }
+}
+FIXTURE_TEARDOWN(shared_data) +{
- FTS *fts = NULL;
- FTSENT *ftsent;
- int fts_flags = (FTS_PHYSICAL | FTS_COMFOLLOW | FTS_NOCHDIR | FTS_XDEV);
- char *paths[2] = { self->base_dir, NULL };
- char cmd[1024];
- int cmd_len;
- /* Close digest_lists subdirectory. */
- close(self->digest_lists_dirfd);
- /* Close base directory. */
- close(self->base_dirfd);
- /* Delete files and directories. */
- fts = fts_open(paths, fts_flags, NULL);
- if (fts) {
while ((ftsent = fts_read(fts)) != NULL) {
switch (ftsent->fts_info) {
case FTS_DP:
rmdir(ftsent->fts_accpath);
break;
case FTS_F:
case FTS_SL:
case FTS_SLNONE:
case FTS_DEFAULT:
unlink(ftsent->fts_accpath);
break;
default:
break;
}
}
- }
- /* Release digest cache reference, if the test was interrupted. */
- cmd_len = snprintf(cmd, sizeof(cmd), "%s",
commands_str[DIGEST_CACHE_PUT]);
- write(self->kernfd, cmd, cmd_len);
- /* Close kernel notify inodes interface. */
- close(self->notify_inodesfd);
- /* Close kernel test interface. */
- close(self->kernfd);
- /* Close kernel digest list path interface. */
- close(self->pathfd);
- syscall(SYS_delete_module, "digest_cache_kern", 0);
+}
+static int query_test(int kernfd, char *base_dir, char *filename,
enum hash_algo algo, int start_number, int num_digests)
+{
- u8 digest[MAX_DIGEST_SIZE] = { 0 };
- char digest_str[MAX_DIGEST_SIZE * 2 + 1] = { 0 };
- int digest_len = hash_digest_size[algo];
- char cmd[1024];
- int ret, i, cmd_len;
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s/%s",
commands_str[DIGEST_CACHE_GET], base_dir, filename);
- ret = write(kernfd, cmd, cmd_len);
- if (ret != cmd_len)
return -errno;
- ret = 0;
- *(u32 *)digest = start_number;
- for (i = 0; i < num_digests; i++) {
bin2hex(digest_str, digest, digest_len);
cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s/%s|%s:%s",
commands_str[DIGEST_CACHE_LOOKUP], base_dir,
filename, hash_algo_name[algo], digest_str);
ret = write(kernfd, cmd, cmd_len);
if (ret != cmd_len) {
ret = -errno;
goto out;
} else {
ret = 0;
}
(*(u32 *)digest)++;
- }
+out:
- cmd_len = snprintf(cmd, sizeof(cmd), "%s",
commands_str[DIGEST_CACHE_PUT]);
- write(kernfd, cmd, cmd_len);
- return ret;
+}
+static enum pgp_algos get_pgp_algo(enum hash_algo algo) +{
- unsigned long i;
- for (i = DIGEST_ALGO_MD5; i < ARRAY_SIZE(pgp_algo_mapping); i++)
if (pgp_algo_mapping[i] == algo)
return i;
- return DIGEST_ALGO_SHA224 + 1;
+}
+static void test_parser(struct _test_data_shared_data *self,
struct __test_metadata *_metadata,
char *digest_list_filename, char *filename,
enum hash_algo algo, int start_number, int num_digests,
unsigned int failure)
+{
- int expected_ret = (failure) ? -ENOENT : 0;
- if (!strncmp(digest_list_filename, "tlv-", 4)) {
ASSERT_EQ(0, gen_tlv_list(self->digest_lists_dirfd,
digest_list_filename, algo,
start_number, num_digests,
(enum tlv_failures)failure));
- } else if (!strncmp(digest_list_filename, "rpm-", 4)) {
enum pgp_algos pgp_algo = get_pgp_algo(algo);
if (pgp_algo == DIGEST_ALGO_SHA224 + 1)
return;
ASSERT_EQ(0, gen_rpm_list(self->digest_lists_dirfd,
digest_list_filename, algo, pgp_algo,
start_number, num_digests,
(enum rpm_failures)failure));
- }
- ASSERT_EQ(0, create_file(self->base_dirfd, filename,
digest_list_filename));
- ASSERT_EQ(expected_ret, query_test(self->kernfd, self->base_dir,
filename, algo, start_number,
num_digests));
- unlinkat(self->digest_lists_dirfd, digest_list_filename, 0);
- unlinkat(self->base_dirfd, filename, 0);
+}
+/*
- Verify that the tlv digest list parser returns success on well-formatted
- digest lists, for each defined hash algorithm.
- */
+TEST_F(shared_data, tlv_parser_ok) +{
- enum hash_algo algo;
- /* Test every known algorithm. */
- for (algo = 0; algo < HASH_ALGO__LAST; algo++)
test_parser(self, _metadata, "tlv-digest_list", "file", algo,
0, 5, TLV_NO_FAILURE);
+}
+/*
- Verify that the tlv digest list parser returns failure on invalid digest
- lists.
- */
+TEST_F(shared_data, tlv_parser_error) +{
- enum tlv_failures failure;
- /* Test every failure. */
- for (failure = 0; failure < TLV_FAILURE__LAST; failure++)
test_parser(self, _metadata, "tlv-digest_list", "file",
HASH_ALGO_SHA224, 0, 1, failure);
+}
+/*
- Verify that the rpm digest list parser returns success on well-formatted
- digest lists, for each defined hash algorithm.
- */
+TEST_F(shared_data, rpm_parser_ok) +{
- enum hash_algo algo;
- /* Test every known algorithm. */
- for (algo = 0; algo < HASH_ALGO__LAST; algo++)
test_parser(self, _metadata, "rpm-digest_list", "file", algo,
0, 5, RPM_NO_FAILURE);
+}
+/*
- Verify that the rpm digest list parser returns failure on invalid digest
- lists.
- */
+TEST_F(shared_data, rpm_parser_error) +{
- enum rpm_failures failure;
- /* Test every failure. */
- for (failure = 0; failure < RPM_FAILURE__LAST; failure++)
test_parser(self, _metadata, "rpm-digest_list", "file",
HASH_ALGO_SHA224, 0, 1, failure);
+}
+static void test_default_path(struct _test_data_shared_data *self,
struct __test_metadata *_metadata, bool file)
+{
- char path[PATH_MAX];
- size_t path_len;
- if (file) {
path_len = snprintf(path, sizeof(path),
"%s/%s/tlv-digest_list", self->base_dir,
DIGEST_LISTS_SUBDIR);
ASSERT_LT(0, write(self->pathfd, path, path_len));
- }
- ASSERT_EQ(0, gen_tlv_list(self->digest_lists_dirfd, "tlv-digest_list",
HASH_ALGO_SHA1, 0, 1, TLV_NO_FAILURE));
- ASSERT_EQ(0, create_file(self->base_dirfd, "file", NULL));
- ASSERT_EQ(0, query_test(self->kernfd, self->base_dir, "file",
HASH_ALGO_SHA1, 0, 1));
+}
+/*
- Verify that the digest cache created from the default path (regular file)
- can be retrieved and used for lookup.
- */
+TEST_F(shared_data, default_path_file) +{
- test_default_path(self, _metadata, true);
+}
+/*
- Verify that the digest cache created from the default path (directory)
- can be retrieved and used for lookup.
- */
+TEST_F(shared_data, default_path_dir) +{
- test_default_path(self, _metadata, false);
+}
+static void notify_inode_init(struct _test_data_shared_data *self,
struct __test_metadata *_metadata)
+{
- /* Clear buffer. */
- ASSERT_EQ(1, write(self->notify_inodesfd, "1", 1));
+}
+static void notify_inodes_check(struct _test_data_shared_data *self,
struct __test_metadata *_metadata,
char *filenames)
+{
- char notify_inodes_buf[1024] = { 0 };
- char notify_inodes_buf_kernel[1024] = { 0 };
- char *filename, *filenames_copy, *buf_ptr = notify_inodes_buf;
- struct stat st;
- int fd;
- ASSERT_LT(0, read(self->notify_inodesfd, notify_inodes_buf_kernel,
sizeof(notify_inodes_buf_kernel)));
- filenames_copy = strdup(filenames);
- ASSERT_NE(NULL, filenames_copy);
- while ((filename = strsep(&filenames_copy, ","))) {
fd = openat(self->base_dirfd, filename, O_RDONLY);
ASSERT_NE(-1, fd);
ASSERT_EQ(0, fstat(fd, &st));
close(fd);
buf_ptr += snprintf(buf_ptr,
sizeof(notify_inodes_buf) -
(buf_ptr - notify_inodes_buf), "%s%lu",
notify_inodes_buf[0] ? "," : "", st.st_ino);
- }
- free(filenames_copy);
- ASSERT_EQ(0, strcmp(notify_inodes_buf, notify_inodes_buf_kernel));
+}
+static void test_file_changes(struct _test_data_shared_data *self,
struct __test_metadata *_metadata,
enum file_changes change)
+{
- char digest_list_filename[] = "tlv-digest_list";
- char digest_list_filename_new[] = "tlv-digest_list6";
- char digest_list_filename_xattr[] = "tlv-digest_list7";
- char digest_list_path[sizeof(self->digest_lists_dir) +
sizeof(digest_list_filename)];
- int fd;
- ASSERT_EQ(0, gen_tlv_list(self->digest_lists_dirfd,
digest_list_filename, HASH_ALGO_SHA1, 0, 1,
TLV_NO_FAILURE));
- ASSERT_EQ(0, create_file(self->base_dirfd, "file",
digest_list_filename));
- ASSERT_EQ(0, query_test(self->kernfd, self->base_dir, "file",
HASH_ALGO_SHA1, 0, 1));
- notify_inode_init(self, _metadata);
- switch (change) {
- case FILE_WRITE:
fd = openat(self->digest_lists_dirfd, digest_list_filename,
O_WRONLY);
ASSERT_NE(-1, fd);
ASSERT_EQ(4, write(fd, "1234", 4));
close(fd);
break;
- case FILE_TRUNCATE:
snprintf(digest_list_path, sizeof(digest_list_path),
"%s/%s", self->digest_lists_dir, digest_list_filename);
ASSERT_EQ(0, truncate(digest_list_path, 4));
break;
- case FILE_FTRUNCATE:
fd = openat(self->digest_lists_dirfd, digest_list_filename,
O_WRONLY);
ASSERT_NE(-1, fd);
ASSERT_EQ(0, ftruncate(fd, 4));
close(fd);
break;
- case FILE_UNLINK:
ASSERT_EQ(0, unlinkat(self->digest_lists_dirfd,
digest_list_filename, 0));
break;
- case FILE_RENAME:
ASSERT_EQ(0, renameat(self->digest_lists_dirfd,
digest_list_filename,
self->digest_lists_dirfd,
digest_list_filename_new));
break;
- case FILE_SETXATTR:
fd = openat(self->base_dirfd, "file", O_WRONLY);
ASSERT_NE(-1, fd);
ASSERT_EQ(0, fsetxattr(fd, XATTR_NAME_DIGEST_LIST,
digest_list_filename_xattr,
strlen(digest_list_filename_xattr) + 1,
0));
close(fd);
break;
- case FILE_REMOVEXATTR:
fd = openat(self->base_dirfd, "file", O_WRONLY);
ASSERT_NE(-1, fd);
ASSERT_EQ(0, fremovexattr(fd, XATTR_NAME_DIGEST_LIST));
close(fd);
/*
* Removing security.digest_list does not cause a failure,
* the digest can be still retrieved via directory lookup.
*/
ASSERT_EQ(0, query_test(self->kernfd, self->base_dir, "file",
HASH_ALGO_SHA1, 0, 1));
notify_inodes_check(self, _metadata, "file");
return;
- default:
break;
- }
- ASSERT_NE(0, query_test(self->kernfd, self->base_dir, "file",
HASH_ALGO_SHA1, 0, 1));
- notify_inodes_check(self, _metadata, "file");
+}
+/*
- Verify that operations on a digest list cause a reset of the digest cache,
- and that the digest is not found in the invalid/missing digest list.
- */
+TEST_F(shared_data, file_reset) +{
- enum file_changes change;
- /* Test for every file change. */
- for (change = 0; change < FILE_CHANGE__LAST; change++)
test_file_changes(self, _metadata, change);
+}
+static void query_test_with_failures(struct _test_data_shared_data *self,
struct __test_metadata *_metadata,
int start_number, int num_digests,
int *removed, int num_removed)
+{
- int i, j, expected_ret;
- for (i = start_number; i < start_number + num_digests; i++) {
expected_ret = 0;
for (j = 0; j < num_removed; j++) {
if (removed[j] == i) {
expected_ret = -ENOENT;
break;
}
}
ASSERT_EQ(expected_ret, query_test(self->kernfd, self->base_dir,
"file", HASH_ALGO_SHA1, i,
1));
- }
+}
+/*
- Verify that changes in the digest list directory are monitored and that
- a digest cannot be found if the respective digest list file has been moved
- away from the directory, and that a digest can be found if the respective
- digest list has been moved/created in the directory.
- */
+TEST_F(shared_data, dir_reset) +{
- char digest_list_filename[NAME_MAX + 1];
- int i, removed[10];
- for (i = 0; i < 10; i++) {
snprintf(digest_list_filename, sizeof(digest_list_filename),
"tlv-digest_list%d", i);
ASSERT_EQ(0, gen_tlv_list(self->digest_lists_dirfd,
digest_list_filename, HASH_ALGO_SHA1,
i, 1, TLV_NO_FAILURE));
- }
- ASSERT_EQ(0, create_file(self->base_dirfd, "file", NULL));
- /* The second file is to have duplicate notifications (file and dir). */
- ASSERT_EQ(0, create_file(self->base_dirfd, "file2",
"tlv-digest_list7"));
- /* The query adds file2 inode to the file digest cache notif. list. */
- ASSERT_NE(0, query_test(self->kernfd, self->base_dir, "file2",
HASH_ALGO_SHA1, 0, 1));
- query_test_with_failures(self, _metadata, 0, 10, removed, 0);
- notify_inode_init(self, _metadata);
- ASSERT_EQ(0, unlinkat(self->digest_lists_dirfd, "tlv-digest_list7", 0));
- /* File notification comes before directory notification. */
- notify_inodes_check(self, _metadata, "file2,file");
- removed[0] = 7;
- query_test_with_failures(self, _metadata, 0, 10, removed, 1);
- notify_inode_init(self, _metadata);
- ASSERT_EQ(0, renameat(self->digest_lists_dirfd, "tlv-digest_list6",
self->base_dirfd, "tlv-digest_list6"));
- notify_inodes_check(self, _metadata, "file");
- removed[1] = 6;
- query_test_with_failures(self, _metadata, 0, 10, removed, 2);
- notify_inode_init(self, _metadata);
- ASSERT_EQ(0, renameat(self->base_dirfd, "tlv-digest_list6",
self->digest_lists_dirfd, "tlv-digest_list6"));
- notify_inodes_check(self, _metadata, "file");
- query_test_with_failures(self, _metadata, 0, 10, removed, 1);
- notify_inode_init(self, _metadata);
- ASSERT_EQ(0, gen_tlv_list(self->digest_lists_dirfd, "tlv-digest_list10",
HASH_ALGO_SHA1, 10, 1, TLV_NO_FAILURE));
- notify_inodes_check(self, _metadata, "file");
- query_test_with_failures(self, _metadata, 0, 11, removed, 1);
+}
+static void _check_verif_data(struct _test_data_shared_data *self,
struct __test_metadata *_metadata,
char *digest_list_filename, int num,
enum hash_algo algo, bool check_dir)
+{
- char digest_list_filename_kernel[NAME_MAX + 1];
- char cmd[1024], number[20];
- u8 digest[MAX_DIGEST_SIZE] = { 0 };
- char digest_str[MAX_DIGEST_SIZE * 2 + 1] = { 0 };
- int len, cmd_len;
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s/file",
commands_str[DIGEST_CACHE_GET], self->base_dir);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- /*
* If a directory digest cache was requested, we need to do a lookup,
* to make the kernel module retrieve verification data from the digest
* cache of the directory entry.
*/
- if (check_dir) {
*(u32 *)digest = num;
bin2hex(digest_str, digest, hash_digest_size[algo]);
cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s/file|%s:%s",
commands_str[DIGEST_CACHE_LOOKUP],
self->base_dir, hash_algo_name[algo],
digest_str);
ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- }
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s",
commands_str[DIGEST_CACHE_SET_VERIF],
verifs_str[VERIF_FILENAMES]);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- ASSERT_LT(0, read(self->kernfd, digest_list_filename_kernel,
sizeof(digest_list_filename_kernel)));
- ASSERT_EQ(0, strcmp(digest_list_filename, digest_list_filename_kernel));
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s",
commands_str[DIGEST_CACHE_SET_VERIF],
verifs_str[VERIF_NUMBER]);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- len = read(self->kernfd, number, sizeof(number) - 1);
- ASSERT_LT(0, len);
- number[len] = '\0';
- ASSERT_EQ(num, atoi(number));
- cmd_len = snprintf(cmd, sizeof(cmd), "%s",
commands_str[DIGEST_CACHE_PUT]);
- write(self->kernfd, cmd, cmd_len);
+}
+static void check_verif_data(struct _test_data_shared_data *self,
struct __test_metadata *_metadata)
+{
- char digest_list_filename[NAME_MAX + 1];
- char cmd[1024];
- int i, cmd_len;
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s",
commands_str[DIGEST_CACHE_ENABLE_VERIF],
verifs_str[VERIF_FILENAMES]);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s",
commands_str[DIGEST_CACHE_ENABLE_VERIF],
verifs_str[VERIF_NUMBER]);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- /*
* Reverse order is intentional, so that directory entries are created
* in the opposite order as when they are searched (when prefetching is
* requested).
*/
- for (i = 10; i >= 0; i--) {
snprintf(digest_list_filename, sizeof(digest_list_filename),
"%d-tlv-digest_list%d", i, i);
ASSERT_EQ(0, gen_tlv_list(self->digest_lists_dirfd,
digest_list_filename, HASH_ALGO_SHA1,
i, 1, TLV_NO_FAILURE));
ASSERT_EQ(0, create_file(self->base_dirfd, "file",
digest_list_filename));
_check_verif_data(self, _metadata, digest_list_filename, i,
HASH_ALGO_SHA1, false);
ASSERT_EQ(0, unlinkat(self->base_dirfd, "file", 0));
- }
- ASSERT_EQ(0, create_file(self->base_dirfd, "file", NULL));
- for (i = 0; i < 11; i++) {
snprintf(digest_list_filename, sizeof(digest_list_filename),
"%d-tlv-digest_list%d", i, i);
_check_verif_data(self, _metadata, digest_list_filename, i,
HASH_ALGO_SHA1, true);
- }
- ASSERT_EQ(0, unlinkat(self->base_dirfd, "file", 0));
+}
+/*
- Verify that the correct verification data can be retrieved from the digest
- caches (without digest list prefetching).
- */
+TEST_F(shared_data, verif_data_no_prefetch) +{
- check_verif_data(self, _metadata);
+}
+/*
- Verify that the correct verification data can be retrieved from the digest
- caches (with digest list prefetching).
- */
+TEST_F(shared_data, verif_data_prefetch) +{
- ASSERT_EQ(0, lsetxattr(self->base_dir, XATTR_NAME_DIG_PREFETCH,
"1", 1, 0));
- check_verif_data(self, _metadata);
+}
+static void check_prefetch_list(struct _test_data_shared_data *self,
struct __test_metadata *_metadata,
int start_number, int end_number)
+{
- char digest_list_filename[NAME_MAX + 1], filename[NAME_MAX + 1];
- char digest_lists[1024], digest_lists_kernel[1024] = { 0 };
- char cmd[1024];
- int i, cmd_len;
- snprintf(filename, sizeof(filename), "file%d", end_number);
- snprintf(digest_list_filename, sizeof(digest_list_filename),
"%d-tlv-digest_list%d", end_number, end_number);
- ASSERT_EQ(0, create_file(self->base_dirfd, filename,
digest_list_filename));
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s/%s",
commands_str[DIGEST_CACHE_GET], self->base_dir,
filename);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- ASSERT_LT(0, read(self->kernfd, digest_lists, sizeof(digest_lists)));
- for (i = start_number; i <= end_number; i++) {
if (digest_lists_kernel[0])
strcat(digest_lists_kernel, ",");
snprintf(digest_list_filename, sizeof(digest_list_filename),
"%d-tlv-digest_list%d", i, i);
strcat(digest_lists_kernel, digest_list_filename);
- }
- ASSERT_EQ(0, strcmp(digest_lists, digest_lists_kernel));
- ASSERT_EQ(0, unlinkat(self->base_dirfd, filename, 0));
- cmd_len = snprintf(cmd, sizeof(cmd), "%s",
commands_str[DIGEST_CACHE_PUT]);
- write(self->kernfd, cmd, cmd_len);
+}
+static void check_prefetch_list_async(struct _test_data_shared_data *self,
struct __test_metadata *_metadata)
+{
- char digest_list_filename[NAME_MAX + 1], filename[NAME_MAX + 1];
- char digest_lists[1024], digest_lists_kernel[1024] = { 0 };
- char cmd[1024];
- int i, cmd_len;
- for (i = 0; i < NUM_DIGEST_LISTS_PREFETCH; i++) {
snprintf(filename, sizeof(filename), "file%d",
NUM_DIGEST_LISTS_PREFETCH - 1 - i);
snprintf(digest_list_filename, sizeof(digest_list_filename),
"%d-tlv-digest_list%d", i, i);
ASSERT_EQ(0, create_file(self->base_dirfd, filename,
digest_list_filename));
- }
- /* Do batch of get/put to test the kernel for concurrent requests. */
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s/file|%d|%d",
commands_str[DIGEST_CACHE_GET_PUT_ASYNC],
self->base_dir, 0, NUM_DIGEST_LISTS_PREFETCH - 1);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- ASSERT_LT(0, read(self->kernfd, digest_lists, sizeof(digest_lists)));
- for (i = 0; i < NUM_DIGEST_LISTS_PREFETCH; i++) {
if (digest_lists_kernel[0])
strcat(digest_lists_kernel, ",");
snprintf(digest_list_filename, sizeof(digest_list_filename),
"%d-tlv-digest_list%d", i, i);
strcat(digest_lists_kernel, digest_list_filename);
- }
- ASSERT_EQ(0, strcmp(digest_lists, digest_lists_kernel));
+}
+static void prepare_prefetch(struct _test_data_shared_data *self,
struct __test_metadata *_metadata)
+{
- char digest_list_filename[NAME_MAX + 1];
- char cmd[1024];
- int i, cmd_len;
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s",
commands_str[DIGEST_CACHE_ENABLE_VERIF],
verifs_str[VERIF_PREFETCH]);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- cmd_len = snprintf(cmd, sizeof(cmd), "%s|%s",
commands_str[DIGEST_CACHE_SET_VERIF],
verifs_str[VERIF_PREFETCH]);
- ASSERT_EQ(cmd_len, write(self->kernfd, cmd, cmd_len));
- for (i = NUM_DIGEST_LISTS_PREFETCH - 1; i >= 0; i--) {
snprintf(digest_list_filename, sizeof(digest_list_filename),
"%d-tlv-digest_list%d", i, i);
ASSERT_EQ(0, gen_tlv_list(self->digest_lists_dirfd,
digest_list_filename, HASH_ALGO_SHA1,
i, 1, TLV_NO_FAILURE));
- }
- ASSERT_EQ(0, fsetxattr(self->digest_lists_dirfd,
XATTR_NAME_DIG_PREFETCH, "1", 1, 0));
+}
+/*
- Verify that digest lists are prefetched when requested, in the correct order
- (synchronous version).
- */
+TEST_F(shared_data, prefetch_sync) +{
- int i;
- prepare_prefetch(self, _metadata);
- for (i = 2; i < NUM_DIGEST_LISTS_PREFETCH; i += 3)
check_prefetch_list(self, _metadata, i - 2, i);
+}
+/*
- Verify that digest lists are prefetched when requested, in the correct order
- (asynchronous version).
- */
+TEST_F(shared_data, prefetch_async) +{
- prepare_prefetch(self, _metadata);
- check_prefetch_list_async(self, _metadata);
+}
+TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/digest_cache/common.c b/tools/testing/selftests/digest_cache/common.c new file mode 100644 index 000000000000..2123f7d937ce --- /dev/null +++ b/tools/testing/selftests/digest_cache/common.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
- Author: Roberto Sassu roberto.sassu@huawei.com
- Add common code for testing the digest_cache LSM.
- */
+#include "common.h"
+const char *commands_str[DIGEST_CACHE__LAST] = {
- [DIGEST_CACHE_GET] = "get",
- [DIGEST_CACHE_LOOKUP] = "lookup",
- [DIGEST_CACHE_PUT] = "put",
- [DIGEST_CACHE_ENABLE_VERIF] = "enable_verif",
- [DIGEST_CACHE_DISABLE_VERIF] = "disable_verif",
- [DIGEST_CACHE_SET_VERIF] = "set_verif",
- [DIGEST_CACHE_GET_PUT_ASYNC] = "get_put_async",
+};
+const char *const hash_algo_name[HASH_ALGO__LAST] = {
- [HASH_ALGO_MD4] = "md4",
- [HASH_ALGO_MD5] = "md5",
- [HASH_ALGO_SHA1] = "sha1",
- [HASH_ALGO_RIPE_MD_160] = "rmd160",
- [HASH_ALGO_SHA256] = "sha256",
- [HASH_ALGO_SHA384] = "sha384",
- [HASH_ALGO_SHA512] = "sha512",
- [HASH_ALGO_SHA224] = "sha224",
- [HASH_ALGO_RIPE_MD_128] = "rmd128",
- [HASH_ALGO_RIPE_MD_256] = "rmd256",
- [HASH_ALGO_RIPE_MD_320] = "rmd320",
- [HASH_ALGO_WP_256] = "wp256",
- [HASH_ALGO_WP_384] = "wp384",
- [HASH_ALGO_WP_512] = "wp512",
- [HASH_ALGO_TGR_128] = "tgr128",
- [HASH_ALGO_TGR_160] = "tgr160",
- [HASH_ALGO_TGR_192] = "tgr192",
- [HASH_ALGO_SM3_256] = "sm3",
- [HASH_ALGO_STREEBOG_256] = "streebog256",
- [HASH_ALGO_STREEBOG_512] = "streebog512",
- [HASH_ALGO_SHA3_256] = "sha3-256",
- [HASH_ALGO_SHA3_384] = "sha3-384",
- [HASH_ALGO_SHA3_512] = "sha3-512",
+};
+const int hash_digest_size[HASH_ALGO__LAST] = {
- [HASH_ALGO_MD4] = MD5_DIGEST_SIZE,
- [HASH_ALGO_MD5] = MD5_DIGEST_SIZE,
- [HASH_ALGO_SHA1] = SHA1_DIGEST_SIZE,
- [HASH_ALGO_RIPE_MD_160] = RMD160_DIGEST_SIZE,
- [HASH_ALGO_SHA256] = SHA256_DIGEST_SIZE,
- [HASH_ALGO_SHA384] = SHA384_DIGEST_SIZE,
- [HASH_ALGO_SHA512] = SHA512_DIGEST_SIZE,
- [HASH_ALGO_SHA224] = SHA224_DIGEST_SIZE,
- [HASH_ALGO_RIPE_MD_128] = RMD128_DIGEST_SIZE,
- [HASH_ALGO_RIPE_MD_256] = RMD256_DIGEST_SIZE,
- [HASH_ALGO_RIPE_MD_320] = RMD320_DIGEST_SIZE,
- [HASH_ALGO_WP_256] = WP256_DIGEST_SIZE,
- [HASH_ALGO_WP_384] = WP384_DIGEST_SIZE,
- [HASH_ALGO_WP_512] = WP512_DIGEST_SIZE,
- [HASH_ALGO_TGR_128] = TGR128_DIGEST_SIZE,
- [HASH_ALGO_TGR_160] = TGR160_DIGEST_SIZE,
- [HASH_ALGO_TGR_192] = TGR192_DIGEST_SIZE,
- [HASH_ALGO_SM3_256] = SM3256_DIGEST_SIZE,
- [HASH_ALGO_STREEBOG_256] = STREEBOG256_DIGEST_SIZE,
- [HASH_ALGO_STREEBOG_512] = STREEBOG512_DIGEST_SIZE,
- [HASH_ALGO_SHA3_256] = SHA3_256_DIGEST_SIZE,
- [HASH_ALGO_SHA3_384] = SHA3_384_DIGEST_SIZE,
- [HASH_ALGO_SHA3_512] = SHA3_512_DIGEST_SIZE,
+};
+const char *verifs_str[] = {
- [VERIF_FILENAMES] = "filenames",
- [VERIF_NUMBER] = "number",
- [VERIF_PREFETCH] = "prefetch",
+}; diff --git a/tools/testing/selftests/digest_cache/common.h b/tools/testing/selftests/digest_cache/common.h new file mode 100644 index 000000000000..e52e4b137807 --- /dev/null +++ b/tools/testing/selftests/digest_cache/common.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
- Author: Roberto Sassu roberto.sassu@huawei.com
- Header of common.c.
- */
+#ifndef _COMMON_H +#define _COMMON_H +#include <linux/types.h>
+#include "../../../../include/uapi/linux/hash_info.h"
+#define MD5_DIGEST_SIZE 16 +#define SHA1_DIGEST_SIZE 20 +#define RMD160_DIGEST_SIZE 20 +#define SHA256_DIGEST_SIZE 32 +#define SHA384_DIGEST_SIZE 48 +#define SHA512_DIGEST_SIZE 64 +#define SHA224_DIGEST_SIZE 28 +#define RMD128_DIGEST_SIZE 16 +#define RMD256_DIGEST_SIZE 32 +#define RMD320_DIGEST_SIZE 40 +#define WP256_DIGEST_SIZE 32 +#define WP384_DIGEST_SIZE 48 +#define WP512_DIGEST_SIZE 64 +#define TGR128_DIGEST_SIZE 16 +#define TGR160_DIGEST_SIZE 20 +#define TGR192_DIGEST_SIZE 24 +#define SM3256_DIGEST_SIZE 32 +#define STREEBOG256_DIGEST_SIZE 32 +#define STREEBOG512_DIGEST_SIZE 64 +#define SHA3_224_DIGEST_SIZE (224 / 8) +#define SHA3_256_DIGEST_SIZE (256 / 8) +#define SHA3_384_DIGEST_SIZE (384 / 8) +#define SHA3_512_DIGEST_SIZE (512 / 8)
+#define DIGEST_CACHE_TEST_INTERFACE "/sys/kernel/security/digest_cache_test" +#define DIGEST_CACHE_PATH_INTERFACE "/sys/kernel/security/digest_cache_path" +#define DIGEST_CACHE_NOTIFY_INODES_INTERFACE \
- "/sys/kernel/security/digest_cache_notify_inodes"
+#define MAX_DIGEST_SIZE 64
+#define RPMTAG_FILEDIGESTS 1035 +#define RPMTAG_FILEDIGESTALGO 5011
+#define RPM_INT32_TYPE 4 +#define RPM_STRING_ARRAY_TYPE 8
+#define MAX_WORKS 21
+typedef __u8 u8; +typedef __u16 u16; +typedef __u32 u32; +typedef __s32 s32; +typedef __u64 u64;
+enum commands {
- DIGEST_CACHE_GET, // args: <path>
- DIGEST_CACHE_LOOKUP, // args: <algo>|<digest>
- DIGEST_CACHE_PUT, // args:
- DIGEST_CACHE_ENABLE_VERIF, // args: <verif name>
- DIGEST_CACHE_DISABLE_VERIF, // args: <verif name>
- DIGEST_CACHE_SET_VERIF, // args: <verif name>
- DIGEST_CACHE_GET_PUT_ASYNC, // args: <path>|<start#>|<end#>
- DIGEST_CACHE__LAST,
+};
+enum tlv_failures { TLV_NO_FAILURE,
TLV_FAILURE_ALGO_LEN,
TLV_FAILURE_HDR_LEN,
TLV_FAILURE_ALGO_MISMATCH,
TLV_FAILURE_NUM_DIGESTS,
TLV_FAILURE__LAST
+};
+enum rpm_failures { RPM_NO_FAILURE,
RPM_FAILURE_WRONG_MAGIC,
RPM_FAILURE_BAD_DATA_OFFSET,
RPM_FAILURE_WRONG_TAGS,
RPM_FAILURE_WRONG_DIGEST_COUNT,
RPM_FAILURE_DIGEST_WRONG_TYPE,
RPM_FAILURE__LAST
+};
+enum file_changes { FILE_WRITE,
FILE_TRUNCATE,
FILE_FTRUNCATE,
FILE_UNLINK,
FILE_RENAME,
FILE_SETXATTR,
FILE_REMOVEXATTR,
FILE_CHANGE__LAST
+};
+enum VERIFS {
- VERIF_FILENAMES,
- VERIF_NUMBER,
- VERIF_PREFETCH,
- VERIF__LAST
+};
+enum pgp_algos {
- DIGEST_ALGO_MD5 = 1,
- DIGEST_ALGO_SHA1 = 2,
- DIGEST_ALGO_RMD160 = 3,
- /* 4, 5, 6, and 7 are reserved. */
- DIGEST_ALGO_SHA256 = 8,
- DIGEST_ALGO_SHA384 = 9,
- DIGEST_ALGO_SHA512 = 10,
- DIGEST_ALGO_SHA224 = 11,
+};
+struct rpm_hdr {
- u32 magic;
- u32 reserved;
- u32 tags;
- u32 datasize;
+} __attribute__ ((__packed__));
+struct rpm_entryinfo {
- s32 tag;
- u32 type;
- s32 offset;
- u32 count;
+} __attribute__ ((__packed__));
+extern const char *commands_str[DIGEST_CACHE__LAST]; +extern const char *const hash_algo_name[HASH_ALGO__LAST]; +extern const int hash_digest_size[HASH_ALGO__LAST]; +extern const char *verifs_str[VERIF__LAST];
+#endif /* _COMMON_H */ diff --git a/tools/testing/selftests/digest_cache/common_user.c b/tools/testing/selftests/digest_cache/common_user.c new file mode 100644 index 000000000000..1bacadad6b6a --- /dev/null +++ b/tools/testing/selftests/digest_cache/common_user.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
- Author: Roberto Sassu roberto.sassu@huawei.com
- Add common code in user space for testing the digest_cache LSM.
- */
+#include <stddef.h>
+#include "common_user.h"
+static const char hex_asc[] = "0123456789abcdef";
+#define hex_asc_lo(x) hex_asc[((x) & 0x0f)] +#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
+const enum hash_algo pgp_algo_mapping[DIGEST_ALGO_SHA224 + 1] = {
- [DIGEST_ALGO_MD5] = HASH_ALGO_MD5,
- [DIGEST_ALGO_SHA1] = HASH_ALGO_SHA1,
- [DIGEST_ALGO_RMD160] = HASH_ALGO_RIPE_MD_160,
- [4] = HASH_ALGO__LAST,
- [5] = HASH_ALGO__LAST,
- [6] = HASH_ALGO__LAST,
- [7] = HASH_ALGO__LAST,
- [DIGEST_ALGO_SHA256] = HASH_ALGO_SHA256,
- [DIGEST_ALGO_SHA384] = HASH_ALGO_SHA384,
- [DIGEST_ALGO_SHA512] = HASH_ALGO_SHA512,
- [DIGEST_ALGO_SHA224] = HASH_ALGO_SHA224,
+};
+static inline char *hex_byte_pack(char *buf, unsigned char byte) +{
- *buf++ = hex_asc_hi(byte);
- *buf++ = hex_asc_lo(byte);
- return buf;
+}
+char *bin2hex(char *dst, const void *src, size_t count) +{
- const unsigned char *_src = src;
- while (count--)
dst = hex_byte_pack(dst, *_src++);
- return dst;
+} diff --git a/tools/testing/selftests/digest_cache/common_user.h b/tools/testing/selftests/digest_cache/common_user.h new file mode 100644 index 000000000000..4eef52cc5c27 --- /dev/null +++ b/tools/testing/selftests/digest_cache/common_user.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
- Author: Roberto Sassu roberto.sassu@huawei.com
- Header of common_user.c.
- */
+#include <linux/types.h> +#include <stddef.h>
+#include "common.h"
+extern const enum hash_algo pgp_algo_mapping[DIGEST_ALGO_SHA224 + 1];
+char *bin2hex(char *dst, const void *src, size_t count); diff --git a/tools/testing/selftests/digest_cache/config b/tools/testing/selftests/digest_cache/config new file mode 100644 index 000000000000..075a06cc4f8e --- /dev/null +++ b/tools/testing/selftests/digest_cache/config @@ -0,0 +1 @@ +CONFIG_SECURITY_DIGEST_CACHE=y diff --git a/tools/testing/selftests/digest_cache/generators.c b/tools/testing/selftests/digest_cache/generators.c new file mode 100644 index 000000000000..c7791a3589f2 --- /dev/null +++ b/tools/testing/selftests/digest_cache/generators.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
- Author: Roberto Sassu roberto.sassu@huawei.com
- Generate digest lists for testing.
- */
+#include <stddef.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <unistd.h> +#include <sys/xattr.h> +#include <asm/byteorder.h>
+#include "generators.h" +#include "../../../../include/uapi/linux/hash_info.h" +#include "../../../../include/uapi/linux/xattr.h" +#include "../../../../include/uapi/linux/tlv_digest_list.h" +#include "../../../../include/uapi/linux/tlv_parser.h"
+int gen_tlv_list(int temp_dirfd, char *digest_list_filename,
enum hash_algo algo, int start_number, int num_digests,
enum tlv_failures failure)
+{
- u64 _algo = __cpu_to_be64(algo);
- u8 digest[MAX_DIGEST_SIZE] = { 0 };
- int digest_len = hash_digest_size[algo];
- int digest_len_to_copy = digest_len;
- int ret, fd, i;
- struct tlv_data_entry algo_entry = {
.field = __cpu_to_be64(DIGEST_LIST_ALGO),
.length = __cpu_to_be64(sizeof(_algo)),
- };
- struct tlv_data_entry entry_digest = {
.field = __cpu_to_be64(DIGEST_LIST_ENTRY_DIGEST),
.length = __cpu_to_be64(digest_len),
- };
- struct tlv_hdr entry_hdr = {
.data_type = __cpu_to_be64(DIGEST_LIST_ENTRY_DATA),
._reserved = 0,
.num_entries = __cpu_to_be64(1),
.total_len = __cpu_to_be64(sizeof(entry_digest) + digest_len),
- };
- struct tlv_data_entry entry_entry = {
.field = __cpu_to_be64(DIGEST_LIST_ENTRY),
.length = __cpu_to_be64(sizeof(entry_hdr) +
__be64_to_cpu(entry_hdr.total_len)),
- };
- struct tlv_hdr hdr = {
.data_type = __cpu_to_be64(DIGEST_LIST_FILE),
._reserved = 0,
.num_entries = __cpu_to_be64(1 + num_digests),
.total_len = __cpu_to_be64(sizeof(algo_entry) +
__be64_to_cpu(algo_entry.length) +
num_digests * (sizeof(entry_entry) +
__be64_to_cpu(entry_entry.length)))
- };
- switch (failure) {
- case TLV_FAILURE_ALGO_LEN:
algo_entry.length = algo_entry.length / 2;
break;
- case TLV_FAILURE_HDR_LEN:
hdr.total_len--;
break;
- case TLV_FAILURE_ALGO_MISMATCH:
_algo = __cpu_to_be64(algo - 1);
break;
- case TLV_FAILURE_NUM_DIGESTS:
num_digests = 0;
break;
- default:
break;
- }
- fd = openat(temp_dirfd, digest_list_filename,
O_WRONLY | O_CREAT | O_TRUNC, 0600);
- if (fd == -1)
return -errno;
- ret = write(fd, (u8 *)&hdr, sizeof(hdr));
- if (ret != sizeof(hdr))
return -errno;
- ret = write(fd, (u8 *)&algo_entry, sizeof(algo_entry));
- if (ret != sizeof(algo_entry))
return -errno;
- ret = write(fd, (u8 *)&_algo, sizeof(_algo));
- if (ret != sizeof(_algo))
return -errno;
- *(u32 *)digest = start_number;
- for (i = 0; i < num_digests; i++) {
ret = write(fd, (u8 *)&entry_entry, sizeof(entry_entry));
if (ret != sizeof(entry_entry))
return -errno;
ret = write(fd, (u8 *)&entry_hdr, sizeof(entry_hdr));
if (ret != sizeof(entry_hdr))
return -errno;
ret = write(fd, (u8 *)&entry_digest, sizeof(entry_digest));
if (ret != sizeof(entry_digest))
return -errno;
ret = write(fd, digest, digest_len_to_copy);
if (ret != digest_len_to_copy)
return -errno;
(*(u32 *)digest)++;
- }
- close(fd);
- return 0;
+}
+int gen_rpm_list(int temp_dirfd, char *digest_list_filename,
enum hash_algo algo, enum pgp_algos pgp_algo, int start_number,
int num_digests, enum rpm_failures failure)
+{
- u32 _pgp_algo = __cpu_to_be32(pgp_algo);
- u8 digest[MAX_DIGEST_SIZE] = { 0 };
- char digest_str[MAX_DIGEST_SIZE * 2 + 1];
- struct rpm_hdr hdr;
- struct rpm_entryinfo algo_entry, digest_entry;
- int digest_len = hash_digest_size[algo];
- int ret, fd, d_len, i;
- d_len = hash_digest_size[algo] * 2 + 1;
- hdr.magic = __cpu_to_be32(0x8eade801);
- hdr.reserved = 0;
- hdr.tags = __cpu_to_be32(1);
- /*
* Skip the algo section, to ensure that the parser recognizes MD5 as
* the default hash algorithm.
*/
- if (algo != HASH_ALGO_MD5)
hdr.tags = __cpu_to_be32(2);
- hdr.datasize = __cpu_to_be32(d_len * num_digests);
- if (algo != HASH_ALGO_MD5)
hdr.datasize = __cpu_to_be32(sizeof(u32) + d_len * num_digests);
- digest_entry.tag = __cpu_to_be32(RPMTAG_FILEDIGESTS);
- digest_entry.type = __cpu_to_be32(RPM_STRING_ARRAY_TYPE);
- digest_entry.offset = 0;
- digest_entry.count = __cpu_to_be32(num_digests);
- algo_entry.tag = __cpu_to_be32(RPMTAG_FILEDIGESTALGO);
- algo_entry.type = __cpu_to_be32(RPM_INT32_TYPE);
- algo_entry.offset = __cpu_to_be32(d_len * num_digests);
- algo_entry.count = __cpu_to_be32(1);
- switch (failure) {
- case RPM_FAILURE_WRONG_MAGIC:
hdr.magic++;
break;
- case RPM_FAILURE_BAD_DATA_OFFSET:
algo_entry.offset = __cpu_to_be32(UINT_MAX);
break;
- case RPM_FAILURE_WRONG_TAGS:
hdr.tags = __cpu_to_be32(2 + 10);
break;
- case RPM_FAILURE_WRONG_DIGEST_COUNT:
/* We need to go beyond the algorithm, to fail. */
digest_entry.count = __cpu_to_be32(num_digests + 5);
break;
- case RPM_FAILURE_DIGEST_WRONG_TYPE:
digest_entry.type = __cpu_to_be32(RPM_INT32_TYPE);
break;
- default:
break;
- }
- fd = openat(temp_dirfd, digest_list_filename,
O_WRONLY | O_CREAT | O_TRUNC, 0600);
- if (fd == -1)
return -errno;
- ret = write(fd, (u8 *)&hdr, sizeof(hdr));
- if (ret != sizeof(hdr))
return -errno;
- if (algo != HASH_ALGO_MD5) {
ret = write(fd, (u8 *)&algo_entry, sizeof(algo_entry));
if (ret != sizeof(algo_entry))
return -errno;
- }
- ret = write(fd, (u8 *)&digest_entry, sizeof(digest_entry));
- if (ret != sizeof(digest_entry))
return -errno;
- *(u32 *)digest = start_number;
- for (i = 0; i < num_digests; i++) {
bin2hex(digest_str, digest, digest_len);
ret = write(fd, (u8 *)digest_str, d_len);
if (ret != d_len)
return -errno;
(*(u32 *)digest)++;
- }
- if (algo != HASH_ALGO_MD5) {
ret = write(fd, (u8 *)&_pgp_algo, sizeof(_pgp_algo));
if (ret != sizeof(_pgp_algo))
return -errno;
- }
- close(fd);
- return 0;
+}
+int create_file(int temp_dirfd, char *filename, char *digest_list_filename) +{
- int ret = 0, fd;
- fd = openat(temp_dirfd, filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
- if (fd == -1)
return -errno;
- if (!digest_list_filename)
goto out;
- ret = fsetxattr(fd, XATTR_NAME_DIGEST_LIST, digest_list_filename,
strlen(digest_list_filename) + 1, 0);
- if (ret == -1)
ret = -errno;
+out:
- close(fd);
- return ret;
+} diff --git a/tools/testing/selftests/digest_cache/generators.h b/tools/testing/selftests/digest_cache/generators.h new file mode 100644 index 000000000000..1c83e531b799 --- /dev/null +++ b/tools/testing/selftests/digest_cache/generators.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
- Author: Roberto Sassu roberto.sassu@huawei.com
- Header of generators.c.
- */
+#include "common.h" +#include "common_user.h"
+int gen_tlv_list(int temp_dirfd, char *digest_list_filename,
enum hash_algo algo, int start_number, int num_digests,
enum tlv_failures failure);
+int gen_rpm_list(int temp_dirfd, char *digest_list_filename,
enum hash_algo algo, enum pgp_algos pgp_algo, int start_number,
int num_digests, enum rpm_failures failure);
+int create_file(int temp_dirfd, char *filename, char *digest_list_filename); diff --git a/tools/testing/selftests/digest_cache/testmod/Makefile b/tools/testing/selftests/digest_cache/testmod/Makefile new file mode 100644 index 000000000000..1ba1c7f08658 --- /dev/null +++ b/tools/testing/selftests/digest_cache/testmod/Makefile @@ -0,0 +1,16 @@ +KDIR ?= ../../../../..
+MODULES = digest_cache_kern.ko
+obj-m += digest_cache_kern.o
+digest_cache_kern-y := kern.o ../common.o
+all:
- +$(Q)$(MAKE) -C $(KDIR) M=$$PWD modules
+clean:
- +$(Q)$(MAKE) -C $(KDIR) M=$$PWD clean
+install: all
- +$(Q)$(MAKE) -C $(KDIR) M=$$PWD modules_install
diff --git a/tools/testing/selftests/digest_cache/testmod/kern.c b/tools/testing/selftests/digest_cache/testmod/kern.c new file mode 100644 index 000000000000..7215ef638e66 --- /dev/null +++ b/tools/testing/selftests/digest_cache/testmod/kern.c @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH
- Author: Roberto Sassu roberto.sassu@huawei.com
- Implement the kernel module to interact with the digest_cache LSM.
- */
+#define pr_fmt(fmt) "DIGEST CACHE TEST: "fmt +#include <linux/module.h> +#include <linux/namei.h> +#include <linux/security.h> +#include <linux/dynamic_debug.h> +#include <linux/digest_cache.h> +#include <linux/kprobes.h> +#include <linux/cpu.h> +#include <linux/kernel_read_file.h> +#include <crypto/hash_info.h>
+#include "../common.h"
+struct verif {
- int (*update)(struct file *file);
- ssize_t (*read)(struct file *file, char __user *buf, size_t datalen,
loff_t *ppos);
- bool enabled;
+};
+struct read_work {
- struct work_struct work;
- char *path_str;
- int ret;
+};
+static struct dentry *test, *notify_inodes; +static struct digest_cache *digest_cache; +static digest_cache_found_t found; +static int cur_verif_index; +static u8 prefetch_buf[4096]; +static u8 notify_inodes_buf[4096]; +static struct read_work w[MAX_WORKS];
+static int filenames_update(struct file *file) +{
- char *filename = (char *)file->f_path.dentry->d_name.name;
- return digest_cache_verif_set(file, "filenames", filename,
strlen(filename) + 1);
+}
+static int number_update(struct file *file) +{
- const char *filename = file_dentry(file)->d_name.name;
- size_t filename_len = strlen(filename);
- u64 number = U64_MAX;
- int ret;
- while (filename_len) {
if (filename[filename_len - 1] < '0' ||
filename[filename_len - 1] > '9')
break;
filename_len--;
- }
- ret = kstrtoull(filename + filename_len, 10, &number);
- if (ret < 0) {
pr_debug("Failed to convert filename %s into number\n",
file_dentry(file)->d_name.name);
return ret;
- }
- return digest_cache_verif_set(file, "number", &number, sizeof(number));
+}
+static ssize_t filenames_read(struct file *file, char __user *buf,
size_t datalen, loff_t *ppos)
+{
- loff_t _ppos = 0;
- char *filenames_list;
- filenames_list = digest_cache_verif_get(found ?
digest_cache_from_found_t(found) : digest_cache,
verifs_str[VERIF_FILENAMES]);
- if (!filenames_list)
return -ENOENT;
- return simple_read_from_buffer(buf, datalen, &_ppos, filenames_list,
strlen(filenames_list) + 1);
+}
+static ssize_t number_read(struct file *file, char __user *buf, size_t datalen,
loff_t *ppos)
+{
- loff_t _ppos = 0;
- u64 *number;
- char temp[20];
- ssize_t len;
- number = digest_cache_verif_get(found ?
digest_cache_from_found_t(found) :
digest_cache, verifs_str[VERIF_NUMBER]);
- if (!number)
return -ENOENT;
- len = snprintf(temp, sizeof(temp), "%llu", *number);
- return simple_read_from_buffer(buf, datalen, &_ppos, temp, len);
+}
+static int prefetch_update(struct file *file) +{
- char *filename = (char *)file->f_path.dentry->d_name.name;
- char *start_ptr = prefetch_buf, *end_ptr;
- int ret;
- ret = digest_cache_verif_set(file, "probe_digest_cache", "1", 1);
- if (!ret) {
/* Don't include duplicates of requested digest lists. */
while ((end_ptr = strchrnul(start_ptr, ','))) {
if (end_ptr > start_ptr &&
!strncmp(start_ptr, filename, end_ptr - start_ptr))
return 0;
if (!*end_ptr)
break;
start_ptr = end_ptr + 1;
}
- }
- if (prefetch_buf[0])
strlcat(prefetch_buf, ",", sizeof(prefetch_buf));
- strlcat(prefetch_buf, filename, sizeof(prefetch_buf));
- return 0;
+}
+static ssize_t prefetch_read(struct file *file, char __user *buf,
size_t datalen, loff_t *ppos)
+{
- loff_t _ppos = 0;
- ssize_t ret;
- ret = simple_read_from_buffer(buf, datalen, &_ppos, prefetch_buf,
strlen(prefetch_buf) + 1);
- memset(prefetch_buf, 0, sizeof(prefetch_buf));
- return ret;
+}
+static int test_digest_cache_change(struct notifier_block *nb,
unsigned long event, void *data)
+{
- struct digest_cache_event_data *event_data = data;
- char i_ino_str[10];
- if (event != DIGEST_CACHE_RESET)
return NOTIFY_DONE;
- if (notify_inodes_buf[0])
strlcat(notify_inodes_buf, ",", sizeof(notify_inodes_buf));
- snprintf(i_ino_str, sizeof(i_ino_str), "%lu", event_data->inode->i_ino);
- strlcat(notify_inodes_buf, i_ino_str, sizeof(notify_inodes_buf));
- return 0;
+}
+static struct notifier_block digest_cache_notifier = {
- .notifier_call = test_digest_cache_change,
+};
+static ssize_t write_notify_inodes(struct file *file, const char __user *buf,
size_t datalen, loff_t *ppos)
+{
- memset(notify_inodes_buf, 0, sizeof(notify_inodes_buf));
- return datalen;
+}
+static ssize_t read_notify_inodes(struct file *file, char __user *buf,
size_t datalen, loff_t *ppos)
+{
- loff_t _ppos = 0;
- return simple_read_from_buffer(buf, datalen, &_ppos, notify_inodes_buf,
strlen(notify_inodes_buf) + 1);
+}
+static struct verif verifs_methods[] = {
- [VERIF_FILENAMES] = { .update = filenames_update,
.read = filenames_read },
- [VERIF_NUMBER] = { .update = number_update, .read = number_read },
- [VERIF_PREFETCH] = { .update = prefetch_update, .read = prefetch_read },
+};
+static void digest_cache_get_put_work(struct work_struct *work) +{
- struct read_work *w = container_of(work, struct read_work, work);
- struct digest_cache *digest_cache;
- struct path path;
- w->ret = kern_path(w->path_str, 0, &path);
- if (w->ret < 0)
return;
- digest_cache = digest_cache_get(path.dentry);
- path_put(&path);
- if (!digest_cache) {
w->ret = -ENOENT;
return;
- }
- digest_cache_put(digest_cache);
- w->ret = 0;
+}
+static int digest_cache_get_put_async(char *path_str, int start_number,
int end_number)
+{
- int ret = 0, i;
- cpus_read_lock();
- for (i = start_number; i <= end_number; i++) {
w[i].path_str = kasprintf(GFP_KERNEL, "%s%u", path_str, i);
if (!w[i].path_str) {
ret = -ENOMEM;
break;
}
INIT_WORK_ONSTACK(&w[i].work, digest_cache_get_put_work);
schedule_work_on(i % num_online_cpus(), &w[i].work);
- }
- cpus_read_unlock();
- for (i = start_number; i <= end_number; i++) {
if (!w[i].path_str)
continue;
flush_work(&w[i].work);
destroy_work_on_stack(&w[i].work);
kfree(w[i].path_str);
w[i].path_str = NULL;
if (!ret)
ret = w[i].ret;
- }
- return ret;
+}
+static ssize_t write_request(struct file *file, const char __user *buf,
size_t datalen, loff_t *ppos)
+{
- char *data, *data_ptr, *cmd_str, *path_str, *algo_str, *digest_str;
- char *verif_name_str, *start_number_str, *end_number_str;
- u8 digest[64];
- struct path path;
- int ret, cmd, algo, verif_index, start_number, end_number;
- data = memdup_user_nul(buf, datalen);
- if (IS_ERR(data))
return PTR_ERR(data);
- data_ptr = data;
- cmd_str = strsep(&data_ptr, "|");
- if (!cmd_str) {
pr_debug("No command\n");
ret = -EINVAL;
goto out;
- }
- cmd = match_string(commands_str, DIGEST_CACHE__LAST, cmd_str);
- if (cmd < 0) {
pr_err("Unknown command %s\n", cmd_str);
ret = -ENOENT;
goto out;
- }
- switch (cmd) {
- case DIGEST_CACHE_GET:
found = 0UL;
path_str = strsep(&data_ptr, "|");
if (!path_str) {
pr_debug("No path\n");
ret = -EINVAL;
goto out;
}
ret = kern_path(path_str, 0, &path);
if (ret < 0) {
pr_debug("Cannot find file %s\n", path_str);
goto out;
}
if (digest_cache) {
pr_debug("Digest cache exists, doing a put\n");
digest_cache_put(digest_cache);
}
digest_cache = digest_cache_get(path.dentry);
ret = digest_cache ? 0 : -ENOENT;
pr_debug("digest cache get %s, ret: %d\n", path_str, ret);
path_put(&path);
break;
- case DIGEST_CACHE_LOOKUP:
if (!digest_cache) {
pr_debug("No digest cache\n");
ret = -ENOENT;
goto out;
}
path_str = strsep(&data_ptr, "|");
if (!path_str) {
pr_debug("No path\n");
ret = -EINVAL;
goto out;
}
algo_str = strsep(&data_ptr, ":");
digest_str = data_ptr;
if (!algo_str || !digest_str) {
pr_debug("No algo or digest\n");
ret = -EINVAL;
goto out;
}
algo = match_string(hash_algo_name, HASH_ALGO__LAST, algo_str);
if (algo < 0) {
pr_err("Unknown algorithm %s", algo_str);
ret = -ENOENT;
goto out;
}
ret = hex2bin(digest, digest_str, hash_digest_size[algo]);
if (ret < 0) {
pr_debug("Invalid digest %s\n", digest_str);
goto out;
}
ret = kern_path(path_str, 0, &path);
if (ret < 0) {
pr_debug("Cannot find file %s\n", path_str);
goto out;
}
ret = -ENOENT;
found = digest_cache_lookup(path.dentry, digest_cache, digest,
algo);
path_put(&path);
if (found)
ret = 0;
pr_debug("%s:%s lookup %s, ret: %d\n", algo_str, digest_str,
path_str, ret);
break;
- case DIGEST_CACHE_PUT:
if (digest_cache) {
digest_cache_put(digest_cache);
digest_cache = NULL;
}
ret = 0;
pr_debug("digest cache put, ret: %d\n", ret);
break;
- case DIGEST_CACHE_ENABLE_VERIF:
- case DIGEST_CACHE_DISABLE_VERIF:
memset(prefetch_buf, 0, sizeof(prefetch_buf));
fallthrough;
- case DIGEST_CACHE_SET_VERIF:
verif_name_str = strsep(&data_ptr, "|");
if (!verif_name_str) {
pr_debug("No verifier name\n");
ret = -EINVAL;
goto out;
}
verif_index = match_string(verifs_str, ARRAY_SIZE(verifs_str),
verif_name_str);
if (verif_index < 0) {
pr_err("Unknown verifier name %s\n", verif_name_str);
ret = -ENOENT;
goto out;
}
if (cmd == DIGEST_CACHE_ENABLE_VERIF)
verifs_methods[verif_index].enabled = true;
else if (cmd == DIGEST_CACHE_DISABLE_VERIF)
verifs_methods[verif_index].enabled = false;
else
cur_verif_index = verif_index;
ret = 0;
pr_debug("digest cache %s %s, ret: %d\n", cmd_str,
verif_name_str, ret);
break;
- case DIGEST_CACHE_GET_PUT_ASYNC:
path_str = strsep(&data_ptr, "|");
if (!path_str) {
pr_debug("No path\n");
ret = -EINVAL;
goto out;
}
start_number_str = strsep(&data_ptr, "|");
if (!start_number_str) {
pr_debug("No start number\n");
ret = -EINVAL;
goto out;
}
ret = kstrtoint(start_number_str, 10, &start_number);
if (ret < 0) {
pr_debug("Invalid start number %s\n", start_number_str);
ret = -EINVAL;
goto out;
}
end_number_str = strsep(&data_ptr, "|");
if (!end_number_str) {
pr_debug("No end number\n");
ret = -EINVAL;
goto out;
}
ret = kstrtoint(end_number_str, 10, &end_number);
if (ret < 0) {
pr_debug("Invalid end number %s\n", end_number_str);
ret = -EINVAL;
goto out;
}
if (end_number - start_number >= MAX_WORKS) {
pr_debug("Too many works (%d), max %d\n",
end_number - start_number, MAX_WORKS - 1);
ret = -EINVAL;
goto out;
}
ret = digest_cache_get_put_async(path_str, start_number,
end_number);
pr_debug("digest cache %s on %s, start: %d, end: %d, ret: %d\n",
cmd_str, path_str, start_number, end_number, ret);
break;
- default:
ret = -EINVAL;
break;
- }
+out:
- kfree(data);
- return ret ?: datalen;
+}
+static ssize_t read_request(struct file *file, char __user *buf, size_t datalen,
loff_t *ppos)
+{
- return verifs_methods[cur_verif_index].read(file, buf, datalen, ppos);
+}
+static const struct file_operations digest_cache_test_ops = {
- .open = generic_file_open,
- .write = write_request,
- .read = read_request,
- .llseek = generic_file_llseek,
+};
+static const struct file_operations digest_cache_notify_inodes_ops = {
- .open = generic_file_open,
- .write = write_notify_inodes,
- .read = read_notify_inodes,
- .llseek = generic_file_llseek,
+};
+static int __kprobes kernel_post_read_file_hook(struct kprobe *p,
struct pt_regs *regs)
+{ +#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
- struct file *file = (struct file *)regs_get_kernel_argument(regs, 0);
- enum kernel_read_file_id id = regs_get_kernel_argument(regs, 3);
+#else
- struct file *file = NULL;
- enum kernel_read_file_id id = READING_UNKNOWN;
+#endif
- int ret, i;
- if (id != READING_DIGEST_LIST)
return 0;
- for (i = 0; i < ARRAY_SIZE(verifs_methods); i++) {
if (!verifs_methods[i].enabled)
continue;
ret = verifs_methods[i].update(file);
if (ret < 0)
return 0;
- }
- return 0;
+}
+static struct kprobe kp = {
- .symbol_name = "security_kernel_post_read_file",
+};
+static int __init digest_cache_test_init(void) +{
- int ret;
- kp.pre_handler = kernel_post_read_file_hook;
- ret = register_kprobe(&kp);
- if (ret < 0) {
pr_err("register_kprobe failed, returned %d\n", ret);
return ret;
- }
- test = securityfs_create_file("digest_cache_test", 0660, NULL, NULL,
&digest_cache_test_ops);
- if (IS_ERR(test)) {
ret = PTR_ERR(test);
goto out_kprobe;
- }
- notify_inodes = securityfs_create_file("digest_cache_notify_inodes",
0660, NULL, NULL,
&digest_cache_notify_inodes_ops);
- if (IS_ERR(notify_inodes)) {
ret = PTR_ERR(notify_inodes);
goto out_test;
- }
- ret = digest_cache_register_notifier(&digest_cache_notifier);
- if (ret < 0)
goto out_notify_inodes;
- return 0;
+out_notify_inodes:
- securityfs_remove(notify_inodes);
+out_test:
- securityfs_remove(test);
+out_kprobe:
- unregister_kprobe(&kp);
- return ret;
+}
+static void __exit digest_cache_test_fini(void) +{
- if (digest_cache)
digest_cache_put(digest_cache);
- digest_cache_unregister_notifier(&digest_cache_notifier);
- securityfs_remove(notify_inodes);
- securityfs_remove(test);
- unregister_kprobe(&kp);
- pr_debug("kprobe at %p unregistered\n", kp.addr);
+}
+module_init(digest_cache_test_init); +module_exit(digest_cache_test_fini); +MODULE_LICENSE("GPL");
BR, Jarkko