All of lore.kernel.org
 help / color / mirror / Atom feed
From: Roberto Sassu <roberto.sassu@huawei.com>
To: <zohar@linux.ibm.com>, <gregkh@linuxfoundation.org>,
	<mchehab+huawei@kernel.org>
Cc: <linux-integrity@vger.kernel.org>,
	<linux-security-module@vger.kernel.org>,
	<linux-doc@vger.kernel.org>, <linux-kselftest@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	Roberto Sassu <roberto.sassu@huawei.com>
Subject: [PATCH v3 13/13] diglim: Tests
Date: Tue, 14 Sep 2021 18:34:01 +0200	[thread overview]
Message-ID: <20210914163401.864635-14-roberto.sassu@huawei.com> (raw)
In-Reply-To: <20210914163401.864635-1-roberto.sassu@huawei.com>

Introduce a number of tests to ensure that DIGLIM works as expected:

- digest_list_add_del_test_file_upload;
- digest_list_add_del_test_file_upload_fault;
- digest_list_add_del_test_buffer_upload;
- digest_list_add_del_test_buffer_upload_fault;
- digest_list_fuzzing_test;
- digest_list_add_del_test_file_upload_measured;
- digest_list_add_del_test_file_upload_measured_chown;
- digest_list_check_measurement_list_test_file_upload;
- digest_list_check_measurement_list_test_buffer_upload.

The tests are in tools/testing/selftests/diglim/selftest.c.

A description of the tests can be found in
Documentation/security/diglim/tests.rst.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 Documentation/security/diglim/index.rst   |    1 +
 Documentation/security/diglim/tests.rst   |   70 +
 MAINTAINERS                               |    2 +
 tools/testing/selftests/Makefile          |    1 +
 tools/testing/selftests/diglim/Makefile   |   19 +
 tools/testing/selftests/diglim/common.c   |  135 ++
 tools/testing/selftests/diglim/common.h   |   32 +
 tools/testing/selftests/diglim/config     |    3 +
 tools/testing/selftests/diglim/selftest.c | 1442 +++++++++++++++++++++
 9 files changed, 1705 insertions(+)
 create mode 100644 Documentation/security/diglim/tests.rst
 create mode 100644 tools/testing/selftests/diglim/Makefile
 create mode 100644 tools/testing/selftests/diglim/common.c
 create mode 100644 tools/testing/selftests/diglim/common.h
 create mode 100644 tools/testing/selftests/diglim/config
 create mode 100644 tools/testing/selftests/diglim/selftest.c

diff --git a/Documentation/security/diglim/index.rst b/Documentation/security/diglim/index.rst
index 0f28c5ad71c0..d4ba4ce50a59 100644
--- a/Documentation/security/diglim/index.rst
+++ b/Documentation/security/diglim/index.rst
@@ -11,3 +11,4 @@ Digest Lists Integrity Module (DIGLIM)
    architecture
    implementation
    remote_attestation
+   tests
diff --git a/Documentation/security/diglim/tests.rst b/Documentation/security/diglim/tests.rst
new file mode 100644
index 000000000000..899e7d6683cf
--- /dev/null
+++ b/Documentation/security/diglim/tests.rst
@@ -0,0 +1,70 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Testing
+=======
+
+This section introduces a number of tests to ensure that DIGLIM works as
+expected:
+
+- ``digest_list_add_del_test_file_upload``;
+- ``digest_list_add_del_test_file_upload_fault``;
+- ``digest_list_add_del_test_buffer_upload``;
+- ``digest_list_add_del_test_buffer_upload_fault``;
+- ``digest_list_fuzzing_test``;
+- ``digest_list_add_del_test_file_upload_measured``;
+- ``digest_list_add_del_test_file_upload_measured_chown``;
+- ``digest_list_check_measurement_list_test_file_upload``;
+- ``digest_list_check_measurement_list_test_buffer_upload``.
+
+The tests are in ``tools/testing/selftests/diglim/selftest.c``.
+
+The first four tests randomly perform add, delete and query of digest
+lists. They internally keep track at any time of the digest lists that are
+currently uploaded to the kernel.
+
+Also, digest lists are generated randomly by selecting an arbitrary digest
+algorithm and an arbitrary number of digests. To ensure a good number of
+collisions, digests are a sequence of zeros, except for the first four
+bytes that are set with a random number within a defined range.
+
+When a query operation is selected, a digest is chosen by getting another
+random number within the same range. Then, the tests count how many times
+the digest is found in the internally stored digest lists and in the query
+result obtained from the kernel. The tests are successful if the obtained
+numbers are the same.
+
+The ``file_upload`` variant creates a temporary file from a generated
+digest list and sends its path to the kernel, so that the file is uploaded.
+The ``buffer_upload`` variant directly sends the digest list buffer to the
+kernel (it will be done by the user space parser after it converts a digest
+list not in the compact format).
+
+The ``fault`` variant performs the test by enabling the ad-hoc fault
+injection mechanism in the kernel (accessible through
+``<debugfs>/fail_diglim``). The fault injection mechanism randomly injects
+errors during the addition and deletion of digest lists. When an error
+occurs, the rollback mechanism performs the reverse operation until the
+point the error occurred, so that the kernel is left in the same state as
+when the requested operation began. Since the kernel returns the error to
+user space, the tests also know that the operation didn't succeed and
+behave accordingly (they also revert the internal state).
+
+The fuzzing test simply sends randomly generated digest lists to the
+kernel, to ensure that the parser is robust enough to handle malformed
+data.
+
+The ``measured`` and ``measured_chown`` variants of the
+``digest_list_add_del_test`` series check whether the digest lists actions
+are properly set after adding IMA rules to measure the digest lists. The
+``measured`` variant is expected to match the IMA rule for critical data,
+while the ``measured_chown`` variant is expected to match the IMA rule for
+files with UID 3000.
+
+The ``digest_list_check_measurement_list_test`` tests verify the remote
+attestation functionality. They verify whether IMA creates a measurement
+entry for each addition and deletion of a digest list, and that the
+deletion is forbidden if IMA created a measurement entry only for the
+addition.
+
+The ``file_upload`` variant uploads a file, while the ``buffer_upload``
+variant uploads a buffer.
diff --git a/MAINTAINERS b/MAINTAINERS
index eac82f151d18..033c70014568 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5512,6 +5512,7 @@ F:	Documentation/security/diglim/implementation.rst
 F:	Documentation/security/diglim/index.rst
 F:	Documentation/security/diglim/introduction.rst
 F:	Documentation/security/diglim/remote_attestation.rst
+F:	Documentation/security/diglim/tests.rst
 F:	include/linux/diglim.h
 F:	include/uapi/linux/diglim.h
 F:	security/integrity/diglim/diglim.h
@@ -5519,6 +5520,7 @@ F:	security/integrity/diglim/fs.c
 F:	security/integrity/diglim/ima.c
 F:	security/integrity/diglim/methods.c
 F:	security/integrity/diglim/parser.c
+F:	tools/testing/selftests/diglim/
 
 DIOLAN U2C-12 I2C DRIVER
 M:	Guenter Roeck <linux@roeck-us.net>
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index c852eb40c4f7..667cd738327b 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -8,6 +8,7 @@ TARGETS += clone3
 TARGETS += core
 TARGETS += cpufreq
 TARGETS += cpu-hotplug
+TARGETS += diglim
 TARGETS += drivers/dma-buf
 TARGETS += efivarfs
 TARGETS += exec
diff --git a/tools/testing/selftests/diglim/Makefile b/tools/testing/selftests/diglim/Makefile
new file mode 100644
index 000000000000..100c219955d7
--- /dev/null
+++ b/tools/testing/selftests/diglim/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+LDFLAGS += -lcrypto
+
+CFLAGS += -O2 -Wall -Wl,-no-as-needed -g -I./ -I../../../../usr/include/ \
+	  -L$(OUTPUT) -Wl,-rpath=./ -ggdb
+LDLIBS += -lpthread
+
+OVERRIDE_TARGETS = 1
+
+TEST_GEN_PROGS = selftest
+TEST_GEN_PROGS_EXTENDED = libcommon.so
+
+include ../lib.mk
+
+$(OUTPUT)/libcommon.so: common.c
+	$(CC) $(CFLAGS) -shared -fPIC $< $(LDLIBS) -o $@
+
+$(OUTPUT)/selftest: selftest.c $(TEST_GEN_PROGS_EXTENDED)
+	$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) -lcommon
diff --git a/tools/testing/selftests/diglim/common.c b/tools/testing/selftests/diglim/common.c
new file mode 100644
index 000000000000..20d693a4fc26
--- /dev/null
+++ b/tools/testing/selftests/diglim/common.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Common functions.
+ */
+
+#include <sys/random.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/hash_info.h>
+
+#include "common.h"
+
+int write_buffer(char *path, char *buffer, size_t buffer_len, int uid)
+{
+	ssize_t to_write = buffer_len, written = 0;
+	int ret = 0, ret_seteuid, fd, cur_uid = geteuid();
+	int open_flags = O_WRONLY;
+	struct stat st;
+
+	if (stat(path, &st) == -1)
+		open_flags |= O_CREAT;
+
+	fd = open(path, open_flags, 0644);
+	if (fd < 0)
+		return -errno;
+
+	if (uid >= 0) {
+		ret_seteuid = seteuid(uid);
+		if (ret_seteuid < 0)
+			return ret_seteuid;
+	}
+
+	while (to_write) {
+		written = write(fd, buffer + buffer_len - to_write, to_write);
+		if (written <= 0) {
+			ret = -errno;
+			break;
+		}
+
+		to_write -= written;
+	}
+
+	if (uid >= 0) {
+		ret_seteuid = seteuid(cur_uid);
+		if (ret_seteuid < 0)
+			return ret_seteuid;
+	}
+
+	close(fd);
+	return ret;
+}
+
+int read_buffer(char *path, char **buffer, size_t *buffer_len, bool alloc,
+		bool is_char)
+{
+	ssize_t len = 0, read_len;
+	int ret = 0, fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -errno;
+
+	if (alloc) {
+		*buffer = NULL;
+		*buffer_len = 0;
+	}
+
+	while (1) {
+		if (alloc) {
+			if (*buffer_len == len) {
+				*buffer_len += BUFFER_SIZE;
+				*buffer = realloc(*buffer, *buffer_len + 1);
+				if (!*buffer) {
+					ret = -ENOMEM;
+					goto out;
+				}
+			}
+		}
+
+		read_len = read(fd, *buffer + len, *buffer_len - len);
+		if (read_len < 0) {
+			ret = -errno;
+			goto out;
+		}
+
+		if (!read_len)
+			break;
+
+		len += read_len;
+	}
+
+	*buffer_len = len;
+	if (is_char)
+		(*buffer)[(*buffer_len)++] = '\0';
+out:
+	close(fd);
+	if (ret < 0) {
+		if (alloc) {
+			free(*buffer);
+			*buffer = NULL;
+		}
+	}
+
+	return ret;
+}
+
+int copy_file(char *src_path, char *dst_path)
+{
+	char *buffer;
+	size_t buffer_len;
+	int ret;
+
+	ret = read_buffer(src_path, &buffer, &buffer_len, true, false);
+	if (!ret) {
+		ret = write_buffer(dst_path, buffer, buffer_len, -1);
+		free(buffer);
+	}
+
+	return ret;
+}
diff --git a/tools/testing/selftests/diglim/common.h b/tools/testing/selftests/diglim/common.h
new file mode 100644
index 000000000000..6c7979f4182e
--- /dev/null
+++ b/tools/testing/selftests/diglim/common.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Header of common.c
+ */
+
+#include <sys/random.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/hash_info.h>
+
+#define BUFFER_SIZE 1024
+
+int write_buffer(char *path, char *buffer, size_t buffer_len, int uid);
+int read_buffer(char *path, char **buffer, size_t *buffer_len, bool alloc,
+		bool is_char);
+int copy_file(char *src_path, char *dst_path);
diff --git a/tools/testing/selftests/diglim/config b/tools/testing/selftests/diglim/config
new file mode 100644
index 000000000000..faafc742974c
--- /dev/null
+++ b/tools/testing/selftests/diglim/config
@@ -0,0 +1,3 @@
+CONFIG_DIGEST_LISTS=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
diff --git a/tools/testing/selftests/diglim/selftest.c b/tools/testing/selftests/diglim/selftest.c
new file mode 100644
index 000000000000..273ba80c43fd
--- /dev/null
+++ b/tools/testing/selftests/diglim/selftest.c
@@ -0,0 +1,1442 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Functions to test DIGLIM.
+ */
+
+#include <sys/random.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/hash_info.h>
+#include <linux/diglim.h>
+#include <bits/endianness.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#include <linux/byteorder/big_endian.h>
+#else
+#include <linux/byteorder/little_endian.h>
+#endif
+
+#include <openssl/evp.h>
+
+#include "common.h"
+#include "../kselftest_harness.h"
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+#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 DIGEST_LIST_PATH_TEMPLATE "/tmp/digest_list.XXXXXX"
+
+#define INTEGRITY_DIR "/sys/kernel/security/integrity"
+
+#define DIGEST_LIST_DIR INTEGRITY_DIR "/diglim"
+#define DIGEST_QUERY_PATH DIGEST_LIST_DIR "/digest_query"
+#define DIGEST_LABEL_PATH DIGEST_LIST_DIR "/digest_list_label"
+#define DIGEST_LIST_ADD_PATH DIGEST_LIST_DIR "/digest_list_add"
+#define DIGEST_LIST_DEL_PATH DIGEST_LIST_DIR "/digest_list_del"
+#define DIGEST_LISTS_LOADED_PATH DIGEST_LIST_DIR "/digest_lists_loaded"
+#define DIGESTS_COUNT DIGEST_LIST_DIR "/digests_count"
+
+#define IMA_POLICY_PATH INTEGRITY_DIR "/ima/policy"
+#define IMA_MEASUREMENTS_PATH INTEGRITY_DIR "/ima/ascii_runtime_measurements"
+
+#define DIGEST_LIST_DEBUGFS_DIR "/sys/kernel/debug/fail_diglim"
+#define DIGEST_LIST_DEBUGFS_TASK_FILTER DIGEST_LIST_DEBUGFS_DIR "/task-filter"
+#define DIGEST_LIST_DEBUGFS_PROBABILITY DIGEST_LIST_DEBUGFS_DIR "/probability"
+#define DIGEST_LIST_DEBUGFS_TIMES DIGEST_LIST_DEBUGFS_DIR "/times"
+#define DIGEST_LIST_DEBUGFS_VERBOSE DIGEST_LIST_DEBUGFS_DIR "/verbose"
+#define PROCFS_SELF_FAULT "/proc/self/make-it-fail"
+
+#define MAX_LINE_LENGTH 512
+#define LABEL_LEN 32
+#define MAX_DIGEST_COUNT 100
+#define MAX_DIGEST_LISTS 100
+#define MAX_DIGEST_BLOCKS 10
+#define MAX_DIGEST_VALUE 10
+#define MAX_SEARCH_ATTEMPTS 10
+#define NUM_QUERIES 1000
+#define MAX_DIGEST_LIST_SIZE 10000
+#define NUM_ITERATIONS 100000
+
+enum upload_types { UPLOAD_FILE, UPLOAD_FILE_CHOWN, UPLOAD_BUFFER };
+
+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",
+};
+
+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,
+};
+
+struct digest_list_item {
+	unsigned long long size;
+	u8 *buf;
+	u8 actions;
+	char digest_str[64 * 2 + 1];
+	enum hash_algo algo;
+	char filename_suffix[6 + 1];
+};
+
+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]
+
+static inline char *hex_byte_pack(char *buf, unsigned char byte)
+{
+	*buf++ = hex_asc_hi(byte);
+	*buf++ = hex_asc_lo(byte);
+	return buf;
+}
+
+/* from lib/hexdump.c (Linux kernel) */
+static int hex_to_bin(char ch)
+{
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	ch = tolower(ch);
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	return -1;
+}
+
+int _hex2bin(unsigned char *dst, const char *src, size_t count)
+{
+	while (count--) {
+		int hi = hex_to_bin(*src++);
+		int lo = hex_to_bin(*src++);
+
+		if ((hi < 0) || (lo < 0))
+			return -1;
+
+		*dst++ = (hi << 4) | lo;
+	}
+	return 0;
+}
+
+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;
+}
+
+static void set_hdr(u8 *buf, struct compact_list_hdr *hdr)
+{
+	memcpy(hdr, buf, sizeof(*hdr));
+	hdr->type = __le16_to_cpu(hdr->type);
+	hdr->modifiers = __le16_to_cpu(hdr->modifiers);
+	hdr->algo = __le16_to_cpu(hdr->algo);
+	hdr->count = __le32_to_cpu(hdr->count);
+	hdr->datalen = __le32_to_cpu(hdr->datalen);
+}
+
+u32 num_max_digest_lists = MAX_DIGEST_LISTS;
+u32 digest_lists_pos;
+struct digest_list_item *digest_lists[MAX_DIGEST_LISTS];
+
+enum hash_algo ima_hash_algo = HASH_ALGO__LAST;
+
+static enum hash_algo get_ima_hash_algo(void)
+{
+	char *measurement_list, *measurement_list_ptr;
+	size_t measurement_list_len;
+	int ret, i = 0;
+
+	if (ima_hash_algo != HASH_ALGO__LAST)
+		return ima_hash_algo;
+
+	ret = read_buffer(IMA_MEASUREMENTS_PATH, &measurement_list,
+			  &measurement_list_len, true, true);
+	if (ret < 0)
+		return HASH_ALGO_SHA256;
+
+	measurement_list_ptr = measurement_list;
+	while ((strsep(&measurement_list_ptr, " ")) && i++ < 2)
+		;
+
+	for (i = 0; i < HASH_ALGO__LAST; i++) {
+		if (!strncmp(hash_algo_name[i], measurement_list_ptr,
+			     strlen(hash_algo_name[i]))) {
+			ima_hash_algo = i;
+			break;
+		}
+	}
+
+	free(measurement_list);
+	return ima_hash_algo;
+}
+
+int calc_digest(u8 *digest, void *data, u64 len, enum hash_algo algo)
+{
+	EVP_MD_CTX *mdctx;
+	const EVP_MD *md;
+	int ret = -EINVAL;
+
+	OpenSSL_add_all_algorithms();
+
+	md = EVP_get_digestbyname(hash_algo_name[algo]);
+	if (!md)
+		goto out;
+
+	mdctx = EVP_MD_CTX_create();
+	if (!mdctx)
+		goto out;
+
+	if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
+		goto out_mdctx;
+
+	if (EVP_DigestUpdate(mdctx, data, len) != 1)
+		goto out_mdctx;
+
+	if (EVP_DigestFinal_ex(mdctx, digest, NULL) != 1)
+		goto out_mdctx;
+
+	ret = 0;
+out_mdctx:
+	EVP_MD_CTX_destroy(mdctx);
+out:
+	EVP_cleanup();
+	return ret;
+}
+
+int calc_file_digest(u8 *digest, char *path, enum hash_algo algo)
+{
+	void *data = MAP_FAILED;
+	struct stat st;
+	int fd, ret = 0;
+
+	if (stat(path, &st) == -1)
+		return -EACCES;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -errno;
+
+	if (st.st_size) {
+		data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+		if (data == MAP_FAILED) {
+			ret = -ENOMEM;
+			goto out;
+		}
+	}
+
+	ret = calc_digest(digest, data, st.st_size, algo);
+out:
+	if (data != MAP_FAILED)
+		munmap(data, st.st_size);
+
+	close(fd);
+	return ret;
+}
+
+static struct digest_list_item *digest_list_generate(void)
+{
+	struct digest_list_item *digest_list;
+	struct compact_list_hdr *hdr_array = NULL, *hdr;
+	u8 *buf_ptr;
+	u32 num_digest_blocks = 0;
+	u8 digest[64];
+	int ret, i, j;
+
+	digest_list = calloc(1, sizeof(*digest_list));
+	if (!digest_list)
+		return NULL;
+
+	digest_list->buf = NULL;
+
+	while (!num_digest_blocks) {
+		ret = getrandom(&num_digest_blocks,
+				sizeof(num_digest_blocks), 0);
+		if (ret < 0)
+			goto out;
+
+		num_digest_blocks = num_digest_blocks % MAX_DIGEST_BLOCKS;
+	}
+
+	hdr_array = calloc(num_digest_blocks, sizeof(*hdr_array));
+	if (!hdr_array)
+		goto out;
+
+	for (i = 0; i < num_digest_blocks; i++) {
+		ret = getrandom(&hdr_array[i], sizeof(hdr_array[i]), 0);
+		if (ret < 0)
+			goto out;
+
+		hdr_array[i].version = 1;
+		hdr_array[i]._reserved = 0;
+		/* COMPACT_DIGEST_LIST type is not allowed. */
+		hdr_array[i].type = hdr_array[i].type % (COMPACT__LAST - 1);
+		hdr_array[i].modifiers =
+		    hdr_array[i].modifiers % (1 << COMPACT_MOD_IMMUTABLE) + 1;
+		hdr_array[i].algo = hdr_array[i].algo % HASH_ALGO_RIPE_MD_128;
+		hdr_array[i].count = hdr_array[i].count % MAX_DIGEST_COUNT;
+
+		while (!hdr_array[i].count) {
+			ret = getrandom(&hdr_array[i].count,
+					sizeof(hdr_array[i].count), 0);
+			if (ret < 0)
+				goto out;
+
+			hdr_array[i].count =
+				hdr_array[i].count % MAX_DIGEST_COUNT;
+		}
+
+		hdr_array[i].datalen =
+		    hdr_array[i].count * hash_digest_size[hdr_array[i].algo];
+
+		digest_list->size += sizeof(*hdr_array) + hdr_array[i].datalen;
+	}
+
+	digest_list->buf = calloc(digest_list->size, sizeof(unsigned char));
+	if (!digest_list->buf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	buf_ptr = digest_list->buf;
+
+	for (i = 0; i < num_digest_blocks; i++) {
+		memcpy(buf_ptr, &hdr_array[i], sizeof(*hdr_array));
+		hdr = (struct compact_list_hdr *)buf_ptr;
+		hdr->type = __cpu_to_le16(hdr->type);
+		hdr->modifiers = __cpu_to_le16(hdr->modifiers);
+		hdr->algo = __cpu_to_le16(hdr->algo);
+		hdr->count = __cpu_to_le32(hdr->count);
+		hdr->datalen = __cpu_to_le32(hdr->datalen);
+
+		buf_ptr += sizeof(*hdr_array);
+
+		for (j = 0; j < hdr_array[i].count; j++) {
+			ret = getrandom(buf_ptr, sizeof(u32), 0);
+			if (ret < 0)
+				goto out;
+
+			*(u32 *)buf_ptr = *(u32 *)buf_ptr % MAX_DIGEST_VALUE;
+			buf_ptr += hash_digest_size[hdr_array[i].algo];
+		}
+	}
+
+	digest_list->algo = get_ima_hash_algo();
+	if (digest_list->algo == HASH_ALGO__LAST) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	ret = calc_digest(digest, digest_list->buf, digest_list->size,
+			  digest_list->algo);
+	if (ret < 0)
+		goto out;
+
+	_bin2hex(digest_list->digest_str, digest,
+		 hash_digest_size[digest_list->algo]);
+
+	ret = 0;
+out:
+	if (ret < 0) {
+		free(digest_list->buf);
+		free(digest_list);
+	}
+
+	free(hdr_array);
+	return !ret ? digest_list : NULL;
+}
+
+static struct digest_list_item *digest_list_generate_random(void)
+{
+	struct digest_list_item *digest_list;
+	struct compact_list_hdr *hdr;
+	u32 size = 0;
+	u8 digest[64];
+	int ret;
+
+	digest_list = calloc(1, sizeof(*digest_list));
+	if (!digest_list)
+		return NULL;
+
+	while (!size) {
+		ret = getrandom(&size, sizeof(size), 0);
+		if (ret < 0)
+			goto out;
+
+		size = size % MAX_DIGEST_LIST_SIZE;
+	}
+
+	digest_list->size = size;
+	digest_list->buf = calloc(digest_list->size, sizeof(unsigned char));
+	if (!digest_list->buf) {
+		free(digest_list);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = getrandom(digest_list->buf, digest_list->size, 0);
+	if (ret < 0)
+		goto out;
+
+	hdr = (struct compact_list_hdr *)digest_list->buf;
+	hdr->version = 1;
+	hdr->_reserved = 0;
+	hdr->type = hdr->type % (COMPACT__LAST - 1);
+	hdr->algo = hdr->algo % HASH_ALGO__LAST;
+
+	hdr->type = __cpu_to_le16(hdr->type);
+	hdr->modifiers = __cpu_to_le16(hdr->modifiers);
+	hdr->algo = __cpu_to_le16(hdr->algo);
+	hdr->count = __cpu_to_le32(hdr->count);
+	hdr->datalen = __cpu_to_le32(hdr->datalen);
+
+	digest_list->algo = get_ima_hash_algo();
+	if (digest_list->algo == HASH_ALGO__LAST) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	ret = calc_digest(digest, digest_list->buf, digest_list->size,
+			  digest_list->algo);
+	if (ret < 0)
+		goto out;
+
+	_bin2hex(digest_list->digest_str, digest,
+		 hash_digest_size[digest_list->algo]);
+
+	ret = 0;
+out:
+	if (ret < 0) {
+		free(digest_list->buf);
+		free(digest_list);
+	}
+
+	return !ret ? digest_list : NULL;
+}
+
+static int digest_list_upload(struct digest_list_item *digest_list, enum ops op,
+			      enum upload_types upload_type, int uid)
+{
+	char path_template[] = DIGEST_LIST_PATH_TEMPLATE;
+	char *path_upload = DIGEST_LIST_ADD_PATH, *basename;
+	unsigned char *buffer = digest_list->buf;
+	size_t buffer_len = digest_list->size;
+	unsigned char rnd[3];
+	int ret = 0, fd;
+
+	if (op == DIGEST_LIST_ADD) {
+		if (upload_type == UPLOAD_FILE ||
+		    upload_type == UPLOAD_FILE_CHOWN) {
+			fd = mkstemp(path_template);
+			if (fd < 0)
+				return -EPERM;
+
+			if (upload_type == UPLOAD_FILE_CHOWN)
+				ret = fchown(fd, 3000, -1);
+
+			fchmod(fd, 0644);
+			close(fd);
+
+			if (ret < 0)
+				goto out;
+
+			ret = write_buffer(path_template,
+					   (char *)digest_list->buf,
+					   digest_list->size, -1);
+			if (ret < 0)
+				goto out;
+
+			buffer = (unsigned char *)path_template;
+			buffer_len = strlen(path_template);
+		} else {
+			ret = getrandom(rnd, sizeof(rnd), 0);
+			if (ret < 0)
+				goto out;
+
+			_bin2hex(path_template +
+				 sizeof(DIGEST_LIST_PATH_TEMPLATE) - 7, rnd,
+				 sizeof(rnd));
+		}
+
+		memcpy(digest_list->filename_suffix,
+		       path_template + sizeof(DIGEST_LIST_PATH_TEMPLATE) - 7,
+		       6);
+	} else {
+		memcpy(path_template + sizeof(DIGEST_LIST_PATH_TEMPLATE) - 7,
+		       digest_list->filename_suffix, 6);
+		path_upload = DIGEST_LIST_DEL_PATH;
+		if (upload_type == UPLOAD_FILE ||
+		    upload_type == UPLOAD_FILE_CHOWN) {
+			buffer = (unsigned char *)path_template;
+			buffer_len = strlen(path_template);
+		}
+	}
+
+	if (upload_type == UPLOAD_BUFFER) {
+		basename = strrchr(path_template, '/') + 1;
+		ret = write_buffer(DIGEST_LABEL_PATH, basename,
+				   strlen(basename), -1);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = write_buffer(path_upload, (char *)buffer, buffer_len, uid);
+out:
+	if ((op == DIGEST_LIST_ADD && ret < 0) ||
+	    (op == DIGEST_LIST_DEL && !ret))
+		unlink(path_template);
+
+	return ret;
+}
+
+static int digest_list_check(struct digest_list_item *digest_list, enum ops op)
+{
+	char path[PATH_MAX];
+	u8 digest_list_buf[MAX_LINE_LENGTH];
+	char digest_list_info[MAX_LINE_LENGTH];
+	ssize_t size = digest_list->size;
+	struct compact_list_hdr hdr;
+	struct stat st;
+	int ret = 0, i, fd, path_len, len, read_len;
+
+	path_len = snprintf(path, sizeof(path), "%s/%s-%s-digest_list.%s.ascii",
+			    DIGEST_LISTS_LOADED_PATH,
+			    hash_algo_name[digest_list->algo],
+			    digest_list->digest_str,
+			    digest_list->filename_suffix);
+
+	path[path_len - 6] = '\0';
+
+	if (op == DIGEST_LIST_DEL) {
+		if (stat(path, &st) != -1)
+			return -EEXIST;
+
+		path[path_len - 6] = '.';
+
+		if (stat(path, &st) != -1)
+			return -EEXIST;
+
+		return 0;
+	}
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -errno;
+
+	while (size) {
+		len = read(fd, digest_list_buf, sizeof(digest_list_buf));
+		if (len <= 0) {
+			ret = -errno;
+			goto out;
+		}
+
+		if (memcmp(digest_list_buf,
+			   digest_list->buf + digest_list->size - size, len)) {
+			ret = -EIO;
+			goto out;
+		}
+
+		size -= len;
+	}
+
+	close(fd);
+
+	path[path_len - 6] = '.';
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -errno;
+
+	size = digest_list->size;
+	while (size) {
+		set_hdr(digest_list->buf + digest_list->size - size, &hdr);
+
+		/* From digest_list_show_common(). */
+		len = snprintf(digest_list_info, sizeof(digest_list_info),
+			"actions: %d, version: %d, algo: %s, type: %d, modifiers: %d, count: %d, datalen: %d\n",
+			digest_list->actions, hdr.version,
+			hash_algo_name[hdr.algo], hdr.type, hdr.modifiers,
+			hdr.count, hdr.datalen);
+
+		read_len = read(fd, digest_list_buf, len);
+
+		if (read_len != len ||
+		    memcmp(digest_list_info, digest_list_buf, len)) {
+			ret = -EIO;
+			goto out;
+		}
+
+		size -= sizeof(hdr);
+
+		for (i = 0; i < hdr.count; i++) {
+			_bin2hex(digest_list_info,
+				 digest_list->buf + digest_list->size - size,
+				 hash_digest_size[hdr.algo]);
+
+			read_len = read(fd, digest_list_buf,
+					hash_digest_size[hdr.algo] * 2 + 1);
+
+			if (read_len != hash_digest_size[hdr.algo] * 2 + 1 ||
+			    memcmp(digest_list_info, digest_list_buf,
+				   read_len - 1) ||
+				   digest_list_buf[read_len - 1] != '\n') {
+				ret = -EIO;
+				goto out;
+			}
+
+			size -= hash_digest_size[hdr.algo];
+		}
+	}
+out:
+	close(fd);
+	return ret;
+}
+
+static int digest_list_query(u8 *digest, enum hash_algo algo,
+			     char **query_result)
+{
+	ssize_t len, to_write, written;
+	char query[256] = { 0 };
+	size_t query_result_len;
+	int ret = 0, fd;
+
+	len = snprintf(query, sizeof(query), "%s-", hash_algo_name[algo]);
+
+	_bin2hex(query + len, digest, hash_digest_size[algo]);
+	len += hash_digest_size[algo] * 2 + 1;
+
+	fd = open(DIGEST_QUERY_PATH, O_WRONLY);
+	if (fd < 0)
+		return -errno;
+
+	to_write = len;
+
+	while (to_write) {
+		written = write(fd, query + len - to_write, to_write);
+		if (written <= 0) {
+			ret = -errno;
+			break;
+		}
+
+		to_write -= written;
+	}
+
+	close(fd);
+	if (ret < 0)
+		return ret;
+
+	return read_buffer(DIGEST_QUERY_PATH, query_result, &query_result_len,
+			   true, true);
+}
+
+static int *get_count_gen_lists(u8 *digest, enum hash_algo algo,
+				bool is_digest_list)
+{
+	struct compact_list_hdr hdr;
+	u8 *buf_ptr;
+	unsigned long long size;
+	struct digest_list_item *digest_list;
+	u8 digest_list_digest[64];
+	int i, j, *count;
+
+	count = calloc(num_max_digest_lists, sizeof(*count));
+	if (!count)
+		return count;
+
+	for (i = 0; i < num_max_digest_lists; i++) {
+		if (!digest_lists[i])
+			continue;
+
+		digest_list = digest_lists[i];
+		size = digest_lists[i]->size;
+		buf_ptr = digest_lists[i]->buf;
+
+		if (is_digest_list) {
+			_hex2bin(digest_list_digest, digest_list->digest_str,
+				 hash_digest_size[digest_list->algo]);
+			if (!memcmp(digest_list_digest, digest,
+				    hash_digest_size[digest_list->algo]))
+				count[i]++;
+
+			continue;
+		}
+
+		while (size) {
+			set_hdr(buf_ptr, &hdr);
+
+			if (hdr.algo != algo) {
+				buf_ptr += sizeof(hdr) + hdr.datalen;
+				size -= sizeof(hdr) + hdr.datalen;
+				continue;
+			}
+
+			buf_ptr += sizeof(hdr);
+			size -= sizeof(hdr);
+
+			for (j = 0; j < hdr.count; j++) {
+				if (!memcmp(digest, buf_ptr,
+				    hash_digest_size[algo]))
+					count[i]++;
+				buf_ptr += hash_digest_size[algo];
+				size -= hash_digest_size[algo];
+			}
+		}
+	}
+
+	return count;
+}
+
+static int *get_count_kernel_query(u8 *digest, enum hash_algo algo,
+				   bool is_digest_list)
+{
+	char *query_result = NULL, *query_result_ptr, *line;
+	char digest_list_info[MAX_LINE_LENGTH];
+	char label[256];
+	struct compact_list_hdr hdr;
+	struct digest_list_item *digest_list;
+	unsigned long long size, size_info;
+	int ret, i, *count = NULL;
+
+	count = calloc(num_max_digest_lists, sizeof(*count));
+	if (!count)
+		return count;
+
+	ret = digest_list_query(digest, algo, &query_result);
+	if (ret < 0)
+		goto out;
+
+	query_result_ptr = query_result;
+
+	while ((line = strsep(&query_result_ptr, "\n"))) {
+		if (!strlen(line))
+			continue;
+
+		for (i = 0; i < num_max_digest_lists; i++) {
+			if (!digest_lists[i])
+				continue;
+
+			digest_list = digest_lists[i];
+			size = digest_list->size;
+
+			if (is_digest_list) {
+				snprintf(label, sizeof(label),
+					 "%s-%s-digest_list.%s",
+					 hash_algo_name[digest_list->algo],
+					 digest_list->digest_str,
+					 digest_list->filename_suffix);
+
+				/* From digest_query_show(). */
+				size_info = snprintf(digest_list_info,
+					 sizeof(digest_list_info),
+					 "%s (actions: %d): type: %d, size: %lld\n",
+					 label, digest_list->actions,
+					 COMPACT_DIGEST_LIST, size);
+
+				/* strsep() replaced '\n' with '\0' in line. */
+				digest_list_info[size_info - 1] = '\0';
+
+				if (!strcmp(digest_list_info, line))
+					count[i]++;
+
+				continue;
+			}
+
+			while (size) {
+				set_hdr(digest_list->buf + digest_list->size -
+					size, &hdr);
+				size -= sizeof(hdr) + hdr.datalen;
+
+				snprintf(label, sizeof(label),
+					 "%s-%s-digest_list.%s",
+					 hash_algo_name[digest_list->algo],
+					 digest_list->digest_str,
+					 digest_list->filename_suffix);
+
+				/* From digest_query_show(). */
+				size_info = snprintf(digest_list_info,
+					 sizeof(digest_list_info),
+					 "%s (actions: %d): version: %d, algo: %s, type: %d, modifiers: %d, count: %d, datalen: %d\n",
+					 label, digest_list->actions,
+					 hdr.version,
+					 hash_algo_name[hdr.algo], hdr.type,
+					 hdr.modifiers, hdr.count,
+					 hdr.datalen);
+
+				/* strsep() replaced '\n' with '\0' in line. */
+				digest_list_info[size_info - 1] = '\0';
+
+				if (!strcmp(digest_list_info, line)) {
+					count[i]++;
+					break;
+				}
+			}
+		}
+	}
+out:
+	free(query_result);
+	if (ret < 0)
+		free(count);
+
+	return (!ret) ? count : NULL;
+}
+
+static int compare_count(u8 *digest, enum hash_algo algo,
+			 bool is_digest_list, struct __test_metadata *_metadata)
+{
+	int *count_gen_list_array, *count_kernel_query_array;
+	int count_gen_list = 0, count_kernel_query = 0;
+	char digest_str[64 * 2 + 1] = { 0 };
+	int i;
+
+	count_gen_list_array = get_count_gen_lists(digest, algo,
+						   is_digest_list);
+	if (!count_gen_list_array)
+		return -EINVAL;
+
+	count_kernel_query_array = get_count_kernel_query(digest, algo,
+							  is_digest_list);
+	if (!count_kernel_query_array) {
+		free(count_gen_list_array);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_max_digest_lists; i++) {
+		count_gen_list += count_gen_list_array[i];
+		count_kernel_query += count_kernel_query_array[i];
+	}
+
+	_bin2hex(digest_str, digest, hash_digest_size[algo]);
+
+	TH_LOG("digest: %s, algo: %s, gen list digests: %d, kernel digests: %d",
+	       digest_str, hash_algo_name[algo], count_gen_list,
+	       count_kernel_query);
+	free(count_gen_list_array);
+	free(count_kernel_query_array);
+	return (count_gen_list == count_kernel_query) ? 0 : -EINVAL;
+}
+
+static void digest_list_delete_all(struct __test_metadata *_metadata,
+				   enum upload_types upload_type)
+{
+	int ret, i;
+
+	for (i = 0; i < MAX_DIGEST_LISTS; i++) {
+		if (!digest_lists[i])
+			continue;
+
+		ret = digest_list_upload(digest_lists[i], DIGEST_LIST_DEL,
+					 upload_type, -1);
+		ASSERT_EQ(0, ret) {
+			TH_LOG("digest_list_upload() failed\n");
+		}
+
+		free(digest_lists[i]->buf);
+		free(digest_lists[i]);
+		digest_lists[i] = NULL;
+	}
+}
+
+FIXTURE(test)
+{
+	enum upload_types upload_type;
+};
+
+FIXTURE_SETUP(test)
+{
+}
+
+FIXTURE_TEARDOWN(test)
+{
+	digest_list_delete_all(_metadata, self->upload_type);
+}
+
+static int enable_fault_injection(void)
+{
+	int ret;
+
+	ret = write_buffer(DIGEST_LIST_DEBUGFS_TASK_FILTER, "Y", 1, -1);
+	if (ret < 0)
+		return ret;
+
+	ret = write_buffer(DIGEST_LIST_DEBUGFS_PROBABILITY, "1", 1, -1);
+	if (ret < 0)
+		return ret;
+
+	ret = write_buffer(DIGEST_LIST_DEBUGFS_TIMES, "10000", 5, -1);
+	if (ret < 0)
+		return ret;
+
+	ret = write_buffer(DIGEST_LIST_DEBUGFS_VERBOSE, "1", 1, -1);
+	if (ret < 0)
+		return ret;
+
+	ret = write_buffer(PROCFS_SELF_FAULT, "1", 1, -1);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void digest_list_add_del_test(struct __test_metadata *_metadata,
+				     int fault_injection,
+				     enum upload_types upload_type)
+{
+	u32 value;
+	enum ops op;
+	enum hash_algo algo;
+	u8 digest[64];
+	int ret, i, cur_queries = 1;
+
+	while (cur_queries <= NUM_QUERIES) {
+		ret = getrandom(&op, sizeof(op), 0);
+		ASSERT_EQ(sizeof(op), ret) {
+			TH_LOG("getrandom() failed\n");
+		}
+
+		op = op % 2;
+
+		switch (op) {
+		case DIGEST_LIST_ADD:
+			TH_LOG("add digest list...");
+			for (digest_lists_pos = 0;
+			     digest_lists_pos < num_max_digest_lists;
+			     digest_lists_pos++)
+				if (!digest_lists[digest_lists_pos])
+					break;
+
+			if (digest_lists_pos == num_max_digest_lists)
+				continue;
+
+			digest_lists[digest_lists_pos] = digest_list_generate();
+			ASSERT_NE(NULL, digest_lists[digest_lists_pos]) {
+				TH_LOG("digest_list_generate() failed");
+			}
+
+			ret = digest_list_upload(digest_lists[digest_lists_pos],
+						 op, upload_type, -1);
+			/* Handle failures from fault injection. */
+			if (fault_injection && ret < 0) {
+				TH_LOG("handle failure...");
+				ret = digest_list_check(
+						digest_lists[digest_lists_pos],
+						DIGEST_LIST_DEL);
+				ASSERT_EQ(0, ret) {
+					TH_LOG("digest_list_check() failed");
+				}
+
+				free(digest_lists[digest_lists_pos]->buf);
+				free(digest_lists[digest_lists_pos]);
+				digest_lists[digest_lists_pos] = NULL;
+				break;
+			}
+
+			ASSERT_EQ(0, ret) {
+				TH_LOG("digest_list_upload() failed");
+			}
+
+			ret = digest_list_check(digest_lists[digest_lists_pos],
+						op);
+			ASSERT_EQ(0, ret) {
+				TH_LOG("digest_list_check() failed");
+			}
+
+			break;
+		case DIGEST_LIST_DEL:
+			TH_LOG("delete digest list...");
+			for (digest_lists_pos = 0;
+			     digest_lists_pos < num_max_digest_lists;
+			     digest_lists_pos++)
+				if (digest_lists[digest_lists_pos])
+					break;
+
+			if (digest_lists_pos == num_max_digest_lists)
+				continue;
+
+			for (i = 0; i < MAX_SEARCH_ATTEMPTS; i++) {
+				ret = getrandom(&digest_lists_pos,
+						sizeof(digest_lists_pos), 0);
+				ASSERT_EQ(sizeof(digest_lists_pos), ret) {
+					TH_LOG("getrandom() failed");
+				}
+
+				digest_lists_pos =
+					digest_lists_pos % num_max_digest_lists;
+
+				if (digest_lists[digest_lists_pos])
+					break;
+			}
+
+			if (i == MAX_SEARCH_ATTEMPTS) {
+				for (digest_lists_pos = 0;
+				     digest_lists_pos < num_max_digest_lists;
+				     digest_lists_pos++)
+					if (digest_lists[digest_lists_pos])
+						break;
+
+				if (digest_lists_pos == num_max_digest_lists)
+					continue;
+			}
+
+			ret = digest_list_upload(digest_lists[digest_lists_pos],
+						 op, upload_type, -1);
+			ASSERT_EQ(0, ret) {
+				TH_LOG("digest_list_upload() failed");
+			}
+
+			ret = digest_list_check(digest_lists[digest_lists_pos],
+						op);
+			ASSERT_EQ(0, ret) {
+				TH_LOG("digest_list_check() failed");
+			}
+
+			free(digest_lists[digest_lists_pos]->buf);
+			free(digest_lists[digest_lists_pos]);
+			digest_lists[digest_lists_pos] = NULL;
+			break;
+		default:
+			break;
+		}
+
+		ret = getrandom(&value, sizeof(value), 0);
+		ASSERT_EQ(sizeof(value), ret) {
+			TH_LOG("getrandom() failed");
+		}
+
+		value = value % 10;
+
+		if (value != 1)
+			continue;
+
+		ret = getrandom(&value, sizeof(value), 0);
+		ASSERT_EQ(sizeof(value), ret) {
+			TH_LOG("getrandom() failed");
+		}
+
+		value = value % MAX_DIGEST_VALUE;
+
+		ret = getrandom(&algo, sizeof(algo), 0);
+		ASSERT_EQ(sizeof(algo), ret) {
+			TH_LOG("getrandom() failed");
+		}
+
+		algo = algo % HASH_ALGO_RIPE_MD_128;
+
+		memset(digest, 0, sizeof(digest));
+		*(u32 *)digest = value;
+
+		ret = compare_count(digest, algo, false, _metadata);
+		ASSERT_EQ(0, ret) {
+			TH_LOG("count mismatch");
+		}
+
+		ret = getrandom(&value, sizeof(value), 0);
+		ASSERT_EQ(sizeof(value), ret) {
+			TH_LOG("getrandom() failed");
+		}
+
+		value = value % MAX_DIGEST_LISTS;
+
+		if (digest_lists[value] != NULL) {
+			_hex2bin(digest, digest_lists[value]->digest_str,
+				 hash_digest_size[digest_lists[value]->algo]);
+
+			ret = compare_count(digest, digest_lists[value]->algo,
+					    true, _metadata);
+			ASSERT_EQ(0, ret) {
+				TH_LOG("count mismatch");
+			}
+		}
+
+		TH_LOG("query digest lists (%d/%d)...", cur_queries,
+		       NUM_QUERIES);
+
+		cur_queries++;
+	}
+}
+
+TEST_F_TIMEOUT(test, digest_list_add_del_test_file_upload, UINT_MAX)
+{
+	self->upload_type = UPLOAD_FILE;
+	digest_list_add_del_test(_metadata, 0, self->upload_type);
+}
+
+TEST_F_TIMEOUT(test, digest_list_add_del_test_file_upload_fault, UINT_MAX)
+{
+	int ret;
+
+	self->upload_type = UPLOAD_FILE;
+
+	ret = enable_fault_injection();
+	ASSERT_EQ(0, ret) {
+		TH_LOG("enable_fault_injection() failed");
+	}
+
+	digest_list_add_del_test(_metadata, 1, self->upload_type);
+}
+
+TEST_F_TIMEOUT(test, digest_list_add_del_test_buffer_upload, UINT_MAX)
+{
+	self->upload_type = UPLOAD_BUFFER;
+	digest_list_add_del_test(_metadata, 0, self->upload_type);
+}
+
+TEST_F_TIMEOUT(test, digest_list_add_del_test_buffer_upload_fault, UINT_MAX)
+{
+	int ret;
+
+	self->upload_type = UPLOAD_BUFFER;
+
+	ret = enable_fault_injection();
+	ASSERT_EQ(0, ret) {
+		TH_LOG("enable_fault_injection() failed");
+	}
+
+	digest_list_add_del_test(_metadata, 1, self->upload_type);
+}
+
+FIXTURE(test_fuzzing)
+{
+};
+
+FIXTURE_SETUP(test_fuzzing)
+{
+}
+
+FIXTURE_TEARDOWN(test_fuzzing)
+{
+}
+
+TEST_F_TIMEOUT(test_fuzzing, digest_list_fuzzing_test, UINT_MAX)
+{
+	char digests_count_before[256] = { 0 };
+	char *digests_count_before_ptr = digests_count_before;
+	char digests_count_after[256] = { 0 };
+	char *digests_count_after_ptr = digests_count_after;
+	size_t len = sizeof(digests_count_before) - 1;
+	struct digest_list_item *digest_list;
+	int ret, i;
+
+	ret = read_buffer(DIGESTS_COUNT, &digests_count_before_ptr, &len,
+			  false, true);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("read_buffer() failed");
+	}
+
+	for (i = 1; i <= NUM_ITERATIONS; i++) {
+		TH_LOG("add digest list (%d/%d)...", i, NUM_ITERATIONS);
+
+		digest_list = digest_list_generate_random();
+		ASSERT_NE(NULL, digest_list) {
+			TH_LOG("digest_list_generate() failed");
+		}
+
+		ret = digest_list_upload(digest_list, DIGEST_LIST_ADD,
+					 UPLOAD_FILE, -1);
+		if (!ret) {
+			ret = digest_list_check(digest_list, DIGEST_LIST_ADD);
+			ASSERT_EQ(0, ret) {
+				TH_LOG("digest_list_check() failed");
+			}
+
+			ret = digest_list_upload(digest_list,
+						 DIGEST_LIST_DEL, UPLOAD_FILE,
+						 -1);
+			ASSERT_EQ(0, ret) {
+				TH_LOG("digest_list_upload() failed");
+			}
+
+			ret = digest_list_check(digest_list, DIGEST_LIST_DEL);
+			ASSERT_EQ(0, ret) {
+				TH_LOG("digest_list_check() failed");
+			}
+		}
+
+		free(digest_list->buf);
+		free(digest_list);
+	}
+
+	ret = read_buffer(DIGESTS_COUNT, &digests_count_after_ptr, &len, false,
+			  true);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("read_buffer() failed");
+	}
+
+	ASSERT_STREQ(digests_count_before, digests_count_after);
+}
+
+#define IMA_MEASURE_RULES "measure func=CRITICAL_DATA label=diglim euid=1000 \nmeasure func=FILE_CHECK fowner=3000 \n"
+
+static int load_ima_policy(char *policy)
+{
+	char *cur_ima_policy = NULL;
+	size_t cur_ima_policy_len = 0;
+	bool rule_found = false;
+	int ret;
+
+	ret = read_buffer(IMA_POLICY_PATH, &cur_ima_policy, &cur_ima_policy_len,
+			  true, true);
+	if (ret < 0)
+		return ret;
+
+	rule_found = (strstr(cur_ima_policy, policy) != NULL);
+	free(cur_ima_policy);
+
+	if (!rule_found) {
+		ret = write_buffer(IMA_POLICY_PATH, policy, strlen(policy), -1);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+FIXTURE(test_measure)
+{
+};
+
+FIXTURE_SETUP(test_measure)
+{
+	int ret;
+
+	ret = load_ima_policy(IMA_MEASURE_RULES);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("load_ima_policy() failed");
+	}
+}
+
+FIXTURE_TEARDOWN(test_measure)
+{
+}
+
+static void digest_list_add_del_test_file_upload_measured_common(
+				struct __test_metadata *_metadata,
+				enum upload_types upload_type, uid_t uid)
+{
+	struct digest_list_item *digest_list;
+	int ret;
+
+	digest_list = digest_list_generate();
+	ASSERT_NE(NULL, digest_list) {
+		TH_LOG("digest_list_generate() failed");
+	}
+
+	digest_list->actions |= (1 << COMPACT_ACTION_IMA_MEASURED);
+
+	ret = digest_list_upload(digest_list, DIGEST_LIST_ADD, upload_type,
+				 uid);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("digest_list_upload() failed");
+	}
+
+	ret = digest_list_check(digest_list, DIGEST_LIST_ADD);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("digest_list_check() failed");
+	}
+
+	ret = digest_list_upload(digest_list, DIGEST_LIST_DEL,
+				 upload_type, uid);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("digest_list_upload() failed");
+	}
+
+	ret = digest_list_check(digest_list, DIGEST_LIST_DEL);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("digest_list_check() failed");
+	}
+
+	free(digest_list->buf);
+	free(digest_list);
+}
+
+TEST_F_TIMEOUT(test_measure, digest_list_add_del_test_file_upload_measured,
+	       UINT_MAX)
+{
+	digest_list_add_del_test_file_upload_measured_common(_metadata,
+							     UPLOAD_FILE, 1000);
+}
+
+TEST_F_TIMEOUT(test_measure,
+	       digest_list_add_del_test_file_upload_measured_chown, UINT_MAX)
+{
+	digest_list_add_del_test_file_upload_measured_common(_metadata,
+							     UPLOAD_FILE_CHOWN,
+							     -1);
+}
+
+void digest_list_check_measurement_list_test_common(
+					struct __test_metadata *_metadata,
+					enum upload_types upload_type)
+{
+	struct digest_list_item *digest_list;
+	char *measurement_list = NULL;
+	size_t measurement_list_len;
+	char event_digest_name[512];
+	bool entry_found;
+	int ret;
+
+	digest_list = digest_list_generate();
+	ASSERT_NE(NULL, digest_list) {
+		TH_LOG("digest_list_generate() failed");
+	}
+
+	digest_list->actions |= (1 << COMPACT_ACTION_IMA_MEASURED);
+
+	ret = digest_list_upload(digest_list, DIGEST_LIST_ADD, upload_type,
+				 1000);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("digest_list_upload() failed");
+	}
+
+	ret = digest_list_check(digest_list, DIGEST_LIST_ADD);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("digest_list_check() failed");
+	}
+
+	ret = read_buffer(IMA_MEASUREMENTS_PATH, &measurement_list,
+			  &measurement_list_len, true, true);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("read_buffer() failed");
+	}
+
+	snprintf(event_digest_name, sizeof(event_digest_name),
+		 "%s:%s add_%s_digest_list.%s",
+		 hash_algo_name[digest_list->algo],
+		 digest_list->digest_str,
+		 upload_type == UPLOAD_FILE ? "file" : "buffer",
+		 digest_list->filename_suffix);
+
+	entry_found = (strstr(measurement_list, event_digest_name) != NULL);
+	free(measurement_list);
+
+	ASSERT_EQ(true, entry_found) {
+		TH_LOG("digest list not found in measurement list");
+	}
+
+	ret = digest_list_upload(digest_list, DIGEST_LIST_DEL, upload_type, -1);
+	ASSERT_NE(0, ret) {
+		TH_LOG("digest_list_upload() success unexpected");
+	}
+
+	ret = digest_list_upload(digest_list, DIGEST_LIST_DEL, upload_type,
+				 1000);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("digest_list_upload() failed");
+	}
+
+	ret = digest_list_check(digest_list, DIGEST_LIST_DEL);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("digest_list_check() failed");
+	}
+
+	measurement_list = NULL;
+
+	ret = read_buffer(IMA_MEASUREMENTS_PATH, &measurement_list,
+			  &measurement_list_len, true, true);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("read_buffer() failed");
+	}
+
+	snprintf(event_digest_name, sizeof(event_digest_name),
+		 "%s:%s del_%s_digest_list.%s",
+		 hash_algo_name[digest_list->algo],
+		 digest_list->digest_str,
+		 upload_type == UPLOAD_FILE ? "file" : "buffer",
+		 digest_list->filename_suffix);
+
+	entry_found = (strstr(measurement_list, event_digest_name) != NULL);
+	free(measurement_list);
+
+	ASSERT_EQ(true, entry_found) {
+		TH_LOG("digest list not found in measurement list");
+	}
+
+	free(digest_list->buf);
+	free(digest_list);
+}
+
+TEST_F_TIMEOUT(test_measure,
+	       digest_list_check_measurement_list_test_file_upload, UINT_MAX)
+{
+	digest_list_check_measurement_list_test_common(_metadata, UPLOAD_FILE);
+}
+
+TEST_F_TIMEOUT(test_measure,
+	       digest_list_check_measurement_list_test_buffer_upload, UINT_MAX)
+{
+	digest_list_check_measurement_list_test_common(_metadata,
+						       UPLOAD_BUFFER);
+}
+
+TEST_HARNESS_MAIN
-- 
2.25.1


  parent reply	other threads:[~2021-09-14 16:37 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-14 16:33 [PATCH v3 00/13] integrity: Introduce DIGLIM Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 01/13] diglim: Overview Roberto Sassu
2021-09-14 17:00   ` Jarkko Sakkinen
2021-09-15  6:54     ` Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 02/13] diglim: Basic definitions Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 03/13] diglim: Objects Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 04/13] diglim: Methods Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 05/13] diglim: Parser Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 06/13] diglim: IMA info Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 07/13] diglim: Interfaces - digest_list_add, digest_list_del Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 08/13] diglim: Interfaces - digest_lists_loaded Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 09/13] diglim: Interfaces - digest_list_label Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 10/13] diglim: Interfaces - digest_query Roberto Sassu
2021-09-14 16:33 ` [PATCH v3 11/13] diglim: Interfaces - digests_count Roberto Sassu
2021-09-14 16:34 ` [PATCH v3 12/13] diglim: Remote Attestation Roberto Sassu
2021-09-14 16:34 ` Roberto Sassu [this message]
2021-10-28  9:08 ` [PATCH v3 00/13] integrity: Introduce DIGLIM Roberto Sassu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210914163401.864635-14-roberto.sassu@huawei.com \
    --to=roberto.sassu@huawei.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=mchehab+huawei@kernel.org \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.