@@ -11,3 +11,4 @@ Digest Lists Integrity Module (DIGLIM)
architecture
implementation
remote_attestation
+ tests
new file mode 100644
@@ -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.
@@ -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>
@@ -8,6 +8,7 @@ TARGETS += clone3
TARGETS += core
TARGETS += cpufreq
TARGETS += cpu-hotplug
+TARGETS += diglim
TARGETS += drivers/dma-buf
TARGETS += efivarfs
TARGETS += exec
new file mode 100644
@@ -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
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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);
new file mode 100644
@@ -0,0 +1,3 @@
+CONFIG_DIGEST_LISTS=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
new file mode 100644
@@ -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
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