[PATCH v3 04/13] diglim: Methods

From: Roberto Sassu
Date: Tue Sep 14 2021 - 12:34:43 EST


Introduce the methods requires to manage the three objects defined.

- digest_item methods:
- digest_add()
- digest_del()
- __digest_lookup()
- diglim_digest_get_info()

- digest_list_item_ref methods:
- digest_list_ref_add()
- digest_list_ref_del()

- digest_list_item methods:
- digest_list_add()
- digest_list_del()

More information about these functions can be found in
Documentation/security/diglim/implementation.rst.

Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
.../security/diglim/implementation.rst | 9 +
MAINTAINERS | 2 +
include/linux/diglim.h | 28 +
security/integrity/Kconfig | 1 +
security/integrity/Makefile | 1 +
security/integrity/diglim/Kconfig | 11 +
security/integrity/diglim/Makefile | 8 +
security/integrity/diglim/diglim.h | 20 +-
security/integrity/diglim/methods.c | 513 ++++++++++++++++++
9 files changed, 592 insertions(+), 1 deletion(-)
create mode 100644 include/linux/diglim.h
create mode 100644 security/integrity/diglim/Kconfig
create mode 100644 security/integrity/diglim/Makefile
create mode 100644 security/integrity/diglim/methods.c

diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst
index 2089b854e5a5..83342ec12f74 100644
--- a/Documentation/security/diglim/implementation.rst
+++ b/Documentation/security/diglim/implementation.rst
@@ -173,3 +173,12 @@ header the digest belongs to. For example:

.. kernel-doc:: security/integrity/diglim/diglim.h
:identifiers: get_digest
+
+
+Methods
+-------
+
+This section introduces the methods requires to manage the three objects
+defined.
+
+.. kernel-doc:: security/integrity/diglim/methods.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 49e3989677a1..06c6ba0b3f25 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5511,8 +5511,10 @@ F: Documentation/security/diglim/architecture.rst
F: Documentation/security/diglim/implementation.rst
F: Documentation/security/diglim/index.rst
F: Documentation/security/diglim/introduction.rst
+F: include/linux/diglim.h
F: include/uapi/linux/diglim.h
F: security/integrity/diglim/diglim.h
+F: security/integrity/diglim/methods.c

DIOLAN U2C-12 I2C DRIVER
M: Guenter Roeck <linux@xxxxxxxxxxxx>
diff --git a/include/linux/diglim.h b/include/linux/diglim.h
new file mode 100644
index 000000000000..d4b4548a288b
--- /dev/null
+++ b/include/linux/diglim.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
+ *
+ * DIGLIM functions available for use by kernel subsystems.
+ */
+
+#ifndef __DIGLIM_H
+#define __DIGLIM_H
+
+#include <crypto/hash_info.h>
+#include <uapi/linux/diglim.h>
+
+#ifdef CONFIG_DIGLIM
+extern int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
+ enum compact_types type, u16 *modifiers,
+ u8 *actions);
+#else
+static inline int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
+ enum compact_types type,
+ u16 *modifiers, u8 *actions)
+{
+ return -ENOENT;
+}
+#endif /*CONFIG_DIGLIM*/
+#endif /*__DIGLIM_H*/
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index 71f0177e8716..8f94f4dcc052 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -98,5 +98,6 @@ config INTEGRITY_AUDIT

source "security/integrity/ima/Kconfig"
source "security/integrity/evm/Kconfig"
+source "security/integrity/diglim/Kconfig"

endif # if INTEGRITY
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index 7ee39d66cf16..d6166550a6b8 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -19,3 +19,4 @@ integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
platform_certs/keyring_handler.o
obj-$(CONFIG_IMA) += ima/
obj-$(CONFIG_EVM) += evm/
+obj-$(CONFIG_DIGLIM) += diglim/
diff --git a/security/integrity/diglim/Kconfig b/security/integrity/diglim/Kconfig
new file mode 100644
index 000000000000..436a76a14337
--- /dev/null
+++ b/security/integrity/diglim/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Digest Lists Integrity Module (DIGLIM)
+#
+config DIGLIM
+ bool "Digest Lists Integrity Module (DIGLIM)"
+ select SECURITYFS
+ select CRYPTO
+ select CRYPTO_HASH_INFO
+ help
+ DIGLIM provides reference values for file content and metadata,
+ that can be used for measurement and appraisal with IMA.
diff --git a/security/integrity/diglim/Makefile b/security/integrity/diglim/Makefile
new file mode 100644
index 000000000000..b761ed8cfb3e
--- /dev/null
+++ b/security/integrity/diglim/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for building Digest Lists Integrity Module (DIGLIM).
+#
+
+obj-$(CONFIG_DIGLIM) += diglim.o
+
+diglim-y := methods.o
diff --git a/security/integrity/diglim/diglim.h b/security/integrity/diglim/diglim.h
index 1c1f513060c7..75359f9cd3dd 100644
--- a/security/integrity/diglim/diglim.h
+++ b/security/integrity/diglim/diglim.h
@@ -20,7 +20,7 @@
#include <linux/audit.h>
#include <crypto/hash_info.h>
#include <linux/hash_info.h>
-#include <uapi/linux/diglim.h>
+#include <linux/diglim.h>

#define MAX_DIGEST_SIZE 64
#define HASH_BITS 10
@@ -87,6 +87,8 @@ static inline unsigned int hash_key(u8 *digest)
return (digest[0] | digest[1] << 8) % DIGLIM_HTABLE_SIZE;
}

+extern struct h_table diglim_htable[COMPACT__LAST];
+
/**
* get_hdr - get a compact header from a digest list
* @digest_list: digest list the header is obtained from
@@ -200,4 +202,20 @@ static inline u8 *get_digest_ref(struct digest_list_item_ref *ref)

return ref->digest_list->buf + ref->digest_offset;
}
+
+struct digest_item *__digest_lookup(u8 *digest, enum hash_algo algo,
+ enum compact_types type, u16 *modifiers,
+ u8 *actions);
+struct digest_item *digest_add(u8 *digest, enum hash_algo algo,
+ enum compact_types type,
+ struct digest_list_item *digest_list,
+ loff_t digest_offset, loff_t hdr_offset);
+void digest_del(u8 *digest, enum hash_algo algo, enum compact_types type,
+ struct digest_list_item *digest_list, loff_t digest_offset,
+ loff_t hdr_offset);
+struct digest_item *digest_list_add(u8 *digest, enum hash_algo algo,
+ loff_t size, u8 *buf, u8 actions,
+ const char *label);
+void digest_list_del(u8 *digest, enum hash_algo algo, u8 actions,
+ struct digest_list_item *digest_list);
#endif /*__DIGLIM_INTERNAL_H*/
diff --git a/security/integrity/diglim/methods.c b/security/integrity/diglim/methods.c
new file mode 100644
index 000000000000..b90c8c9dbd1a
--- /dev/null
+++ b/security/integrity/diglim/methods.c
@@ -0,0 +1,513 @@
+// 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@xxxxxxxxxx>
+ *
+ * Functions to manage digest lists.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/fault-inject.h>
+
+#include "diglim.h"
+#include "../integrity.h"
+
+/* Define a cache for each object type. */
+static struct kmem_cache *digest_list_item_cache __read_mostly;
+static struct kmem_cache *digest_list_item_ref_cache __read_mostly;
+static struct kmem_cache *digest_item_cache __read_mostly;
+
+/* Define a hash table for each digest type. */
+struct h_table diglim_htable[COMPACT__LAST] = {{
+ .queue[0 ... DIGLIM_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
+}};
+
+#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
+static DECLARE_FAULT_ATTR(fail_diglim);
+
+static int __init fail_diglim_debugfs(void)
+{
+ struct dentry *dir = fault_create_debugfs_attr("fail_diglim", NULL,
+ &fail_diglim);
+
+ return PTR_ERR_OR_ZERO(dir);
+}
+
+static inline bool should_fail_diglim(void)
+{
+ return should_fail(&fail_diglim, 1);
+}
+
+late_initcall(fail_diglim_debugfs);
+#else
+static inline bool should_fail_diglim(void)
+{
+ return false;
+}
+#endif
+
+/**
+ * __digest_lookup - lookup digest and return associated modifiers and actions
+ * @digest: digest to lookup
+ * @algo: digest algorithm
+ * @type: type of digest to lookup (e.g. file, metadata)
+ * @modifiers: modifiers (attributes) associated to the found digest
+ * @actions: actions performed by IMA on the digest list containing the digest
+ *
+ * This function searches the given digest in the hash table depending on the
+ * passed type and sets the modifiers and actions associated to the digest, if
+ * the pointers are not NULL.
+ *
+ * This function is not intended for external use, as the returned digest item
+ * could be freed at any time after it has been returned.
+ * diglim_digest_get_info() should be used instead by external callers, as it
+ * only returns the modifiers and the actions associated to the digest at the
+ * time the digest is searched.
+ *
+ * RCU protects both the hash table and the linked list of references to the
+ * digest lists containing the found digest.
+ *
+ * Return: a digest_item structure if the digest is found, NULL otherwise.
+ */
+struct digest_item *__digest_lookup(u8 *digest, enum hash_algo algo,
+ enum compact_types type, u16 *modifiers,
+ u8 *actions)
+{
+ struct digest_item *d = NULL;
+ struct digest_list_item_ref *ref;
+ int digest_len = hash_digest_size[algo];
+ unsigned int key = hash_key(digest);
+ bool found = false;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, &diglim_htable[type].queue[key], hnext) {
+ list_for_each_entry_rcu(ref, &d->refs, list) {
+ if (get_algo_ref(ref) != algo ||
+ memcmp(get_digest_ref(ref), digest, digest_len))
+ break;
+
+ found = true;
+
+ /* There is no need to scan all digest list refs. */
+ if (!modifiers || !actions)
+ break;
+
+ /*
+ * The resulting modifiers and actions are the OR of the
+ * modifiers and actions for each digest list.
+ */
+ *modifiers |= get_hdr_ref(ref)->modifiers;
+ *actions |= ref->digest_list->actions;
+ }
+
+ if (found)
+ break;
+ }
+
+ rcu_read_unlock();
+ return d;
+}
+
+/**
+ * diglim_digest_get_info - lookup digest and return modifiers and actions
+ * @digest: digest to lookup
+ * @algo: digest algorithm
+ * @type: type of digest to lookup (e.g. file, metadata)
+ * @modifiers: modifiers (attributes) associated to the found digest
+ * @actions: actions performed by IMA on the digest lists containing the digest
+ *
+ * This function searches the given digest in the hash table depending on the
+ * passed type and sets the modifiers and actions associated to the digest, if
+ * the pointers are not NULL.
+ *
+ * This function is safe for external use, as it does not return pointers of
+ * objects that can be freed without the caller notices it.
+ *
+ * Return: 0 if the digest is found, -ENOENT otherwise.
+ */
+int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
+ enum compact_types type, u16 *modifiers, u8 *actions)
+{
+ struct digest_item *d;
+
+ d = __digest_lookup(digest, algo, type, modifiers, actions);
+ if (!d)
+ return -ENOENT;
+
+ return 0;
+}
+
+/**
+ * digest_list_ref_add - add reference to a digest list
+ * @d: digest a new reference is added to
+ * @digest_list: digest list whose reference is being added
+ * @digest_offset: offset of the digest in the buffer of the digest list
+ * @hdr_offset: offset of the header within the digest list the digest refers to
+ *
+ * This function adds a new reference to an existing digest list for a given
+ * digest. The reference is described by the digest_list_item_ref structure and
+ * consists of a pointer of the digest list, the offset of the digest to the
+ * beginning of the digest list buffer and the offset of the header the digest
+ * refers to (each digest list might be composed of several digest blocks, each
+ * prefixed by a header describing the attributes of those digests).
+ *
+ * Return: 0 if a new digest list reference was successfully added, a negative
+ * value otherwise.
+ */
+static int digest_list_ref_add(struct digest_item *d,
+ struct digest_list_item *digest_list,
+ loff_t digest_offset, loff_t hdr_offset)
+{
+ struct digest_list_item_ref *new_ref = NULL;
+ u8 *digest = get_digest(digest_list, digest_offset, hdr_offset);
+ enum hash_algo algo = get_algo(digest_list, digest_offset, hdr_offset);
+ int digest_len = hash_digest_size[algo];
+
+ /* Allocate a new reference. */
+ if (!should_fail_diglim())
+ new_ref = kmem_cache_alloc(digest_list_item_ref_cache,
+ GFP_KERNEL);
+ if (!new_ref) {
+ print_hex_dump(KERN_ERR, "digest list ref allocation failed: ",
+ DUMP_PREFIX_NONE, digest_len, 1, digest,
+ digest_len, true);
+ return -ENOMEM;
+ }
+
+ /* Set the new reference. */
+ new_ref->digest_list = digest_list;
+ /* Converting loff_t -> u32 is fine as long as the digest list < 4G. */
+ new_ref->digest_offset = digest_offset;
+ new_ref->hdr_offset = hdr_offset;
+
+ list_add_tail_rcu(&new_ref->list, &d->refs);
+
+ print_hex_dump_debug("add digest list ref: ", DUMP_PREFIX_NONE,
+ digest_len, 1, digest, digest_len, true);
+ return 0;
+}
+
+/**
+ * digest_list_ref_del - del reference to a digest list
+ * @d: digest a reference is deleted from
+ * @digest_list: digest list whose reference is being deleted
+ * @digest_offset: offset of the digest in the buffer of the digest list
+ * @hdr_offset: offset of the header within the digest list the digest refers to
+ *
+ * This function searches the reference to an already loaded digest list in the
+ * linked list of references stored for each digest item. If the reference is
+ * found (if not, it is a bug), the function deletes it from the linked list.
+ */
+static void digest_list_ref_del(struct digest_item *d,
+ struct digest_list_item *digest_list,
+ loff_t digest_offset, loff_t hdr_offset)
+{
+ struct digest_list_item_ref *ref;
+ u8 *digest = get_digest(digest_list, digest_offset, hdr_offset);
+ enum hash_algo algo = get_algo(digest_list, digest_offset, hdr_offset);
+ int digest_len = hash_digest_size[algo];
+
+ /* Search for a digest list reference. */
+ list_for_each_entry(ref, &d->refs, list)
+ if (ref->digest_list == digest_list)
+ break;
+
+ if (!ref) {
+ print_hex_dump(KERN_ERR, "digest list ref not found: ",
+ DUMP_PREFIX_NONE, digest_len, 1, digest,
+ digest_len, true);
+ return;
+ }
+
+ list_del_rcu(&ref->list);
+ kmem_cache_free(digest_list_item_ref_cache, ref);
+
+ print_hex_dump_debug("del digest list ref: ", DUMP_PREFIX_NONE,
+ digest_len, 1, digest, digest_len, true);
+}
+
+/**
+ * digest_add - add a new digest
+ * @digest: digest in binary form
+ * @algo: digest algorithm
+ * @type: digest type
+ * @digest_list: digest list the new digest belongs to
+ * @digest_offset: offset of the digest in the buffer of the digest list
+ * @hdr_offset: offset of the header within the digest list the digest refers to
+ *
+ * This function first searches if the digest is already in the hash table for
+ * the given type. The digest is searched by comparing the passed digest and
+ * algorithm with the digest obtained from the first digest list reference
+ * (buffer + digest_offset), or from the digest field of a digest list item,
+ * for a digest list.
+ *
+ * If the digest exists, only a new reference is added (there might be multiple
+ * references to the same digest list).
+ *
+ * If the digest is not found, a new digest item is allocated and a reference to
+ * the passed digest list is added to that item. The digest item is finally
+ * added to the hash table for the given type.
+ *
+ * Proper locking must be provided by the caller.
+ *
+ * Return: a new or the found digest item on success, an error pointer
+ * otherwise.
+ */
+struct digest_item *digest_add(u8 *digest, enum hash_algo algo,
+ enum compact_types type,
+ struct digest_list_item *digest_list,
+ loff_t digest_offset, loff_t hdr_offset)
+{
+ int digest_len = hash_digest_size[algo];
+ struct digest_item *d;
+ int ret;
+
+ /* Search the digest. */
+ d = __digest_lookup(digest, algo, type, NULL, NULL);
+ if (d) {
+ /*
+ * Add a new digest list reference to the existing digest item.
+ */
+ ret = digest_list_ref_add(d, digest_list, digest_offset,
+ hdr_offset);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ print_hex_dump_debug("digest add duplicate: ", DUMP_PREFIX_NONE,
+ digest_len, 1, digest, digest_len, true);
+ return d;
+ }
+
+ /* Allocate a new digest item. */
+ if (!should_fail_diglim())
+ d = kmem_cache_alloc(digest_item_cache, GFP_KERNEL);
+ if (!d) {
+ print_hex_dump_debug("digest allocation failed: ",
+ DUMP_PREFIX_NONE, digest_len, 1, digest,
+ digest_len, true);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ INIT_LIST_HEAD(&d->refs);
+
+ /* Add a new digest list reference to the new digest item. */
+ ret = digest_list_ref_add(d, digest_list, digest_offset, hdr_offset);
+ if (ret < 0) {
+ kmem_cache_free(digest_item_cache, d);
+ return ERR_PTR(ret);
+ }
+
+ /* Add the new digest item to the hash table for the given type. */
+ hlist_add_head_rcu(&d->hnext,
+ &diglim_htable[type].queue[hash_key(digest)]);
+ diglim_htable[type].len++;
+
+ print_hex_dump_debug("digest add: ", DUMP_PREFIX_NONE, digest_len, 1,
+ digest, digest_len, true);
+ return d;
+}
+
+/**
+ * digest_del - delete a digest with one reference, or just a reference
+ * @digest: digest in binary form
+ * @algo: digest algorithm
+ * @type: digest type
+ * @digest_list: digest list the digest belongs to
+ * @digest_offset: offset of the digest in the buffer of the digest list
+ * @hdr_offset: offset of the header within the digest list the digest refers to
+ *
+ * This function is called when a digest list is being removed. The digest is
+ * first searched in the hash table for the given type. If it is found (if not,
+ * it is a bug, because digest lists can be deleted only if they were added
+ * previously), a reference of the passed digest list is deleted from the linked
+ * list of references of the digest item.
+ *
+ * If the last reference was deleted, the digest item is also deleted and
+ * removed from the hash table.
+ *
+ * Proper locking must be provided by the caller.
+ */
+void digest_del(u8 *digest, enum hash_algo algo, enum compact_types type,
+ struct digest_list_item *digest_list, loff_t digest_offset,
+ loff_t hdr_offset)
+{
+ struct digest_item *d;
+ int digest_len = hash_digest_size[algo];
+
+ /* Search the digest. */
+ d = __digest_lookup(digest, algo, type, NULL, NULL);
+ if (!d) {
+ print_hex_dump(KERN_ERR, "digest not found: ", DUMP_PREFIX_NONE,
+ digest_len, 1, digest, digest_len, true);
+ return;
+ }
+
+ /* Delete a reference of the passed digest list. */
+ digest_list_ref_del(d, digest_list, digest_offset, hdr_offset);
+
+ print_hex_dump_debug(!list_empty(&d->refs) ?
+ "digest del duplicate: " : "digest del: ",
+ DUMP_PREFIX_NONE, digest_len, 1, digest,
+ digest_len, true);
+
+ /* Return if there are still references. */
+ if (!list_empty(&d->refs))
+ return;
+
+ /*
+ * Remove the digest item from the hash table and free it if there are
+ * no more references left.
+ */
+ hlist_del_rcu(&d->hnext);
+ diglim_htable[type].len--;
+ kmem_cache_free(digest_item_cache, d);
+}
+
+static void digest_list_free(struct digest_list_item *digest_list)
+{
+ kfree(digest_list->label);
+ kfree(digest_list->buf);
+ kmem_cache_free(digest_list_item_cache, digest_list);
+}
+
+/**
+ * digest_list_add - add a new digest list
+ * @digest: digest of the digest list in binary form
+ * @algo: digest algorithm
+ * @size: digest list size
+ * @buf: digest list buffer
+ * @actions: actions (measure/appraise) performed by IMA on the digest list
+ * @label: label to be used to identify the digest list
+ *
+ * This function allocates a new digest list item, which contains the buffer,
+ * size, actions performed by IMA and a label. Each digest list item is
+ * associated to a digest item representing the digest of the digest list.
+ *
+ * This function prevents the same digest list to be added multiple times by
+ * searching its digest in the hash table for the COMPACT_DIGEST_LIST type.
+ *
+ * The passed buffer is copied in a new memory area, to avoid to reference
+ * memory that could be freed by the caller.
+ *
+ * If allocation of a new digest list and the associated buffer was successful,
+ * its digest is added to the hash table for the COMPACT_DIGEST_LIST type.
+ *
+ * Proper locking must be provided by the caller.
+ *
+ * Return: the digest item associated to the digest list item on success, an
+ * error pointer otherwise.
+ */
+struct digest_item *digest_list_add(u8 *digest, enum hash_algo algo,
+ loff_t size, u8 *buf, u8 actions,
+ const char *label)
+{
+ struct digest_item *d;
+ struct digest_list_item *digest_list = NULL;
+ int digest_len = hash_digest_size[algo];
+
+ /* Search the digest of the digest list. */
+ d = __digest_lookup(digest, algo, COMPACT_DIGEST_LIST, NULL, NULL);
+ if (d) {
+ print_hex_dump_debug("digest list already uploaded: ",
+ DUMP_PREFIX_NONE, digest_len, 1, digest,
+ digest_len, true);
+ return ERR_PTR(-EEXIST);
+ }
+
+ /* Allocate a new digest list. */
+ if (!should_fail_diglim())
+ digest_list = kmem_cache_alloc(digest_list_item_cache,
+ GFP_KERNEL);
+ if (!digest_list) {
+ print_hex_dump(KERN_ERR, "digest list allocation failed: ",
+ DUMP_PREFIX_NONE, digest_len, 1, digest,
+ digest_len, true);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ digest_list->buf = NULL;
+ digest_list->size = size;
+ digest_list->label = NULL;
+
+ if (!should_fail_diglim())
+ digest_list->buf = kmemdup(buf, size, GFP_KERNEL);
+ if (!digest_list->buf) {
+ print_hex_dump(KERN_ERR, "digest list allocation failed: ",
+ DUMP_PREFIX_NONE, digest_len, 1, digest,
+ digest_len, true);
+ digest_list_free(digest_list);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ digest_list->actions = actions;
+ memcpy(digest_list->digest, digest, hash_digest_size[algo]);
+ digest_list->algo = algo;
+ if (!should_fail_diglim())
+ digest_list->label = kstrdup(label, GFP_KERNEL);
+ if (!digest_list->label) {
+ print_hex_dump(KERN_ERR, "digest list allocation failed: ",
+ DUMP_PREFIX_NONE, digest_len, 1, digest,
+ digest_len, true);
+ digest_list_free(digest_list);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Add the digest of the digest list to the hash table. */
+ d = digest_add(digest, algo, COMPACT_DIGEST_LIST, digest_list, 0, 0);
+ if (IS_ERR(d))
+ digest_list_free(digest_list);
+
+ return d;
+}
+
+/**
+ * digest_list_del - delete an existing digest list
+ * @digest: digest of the digest list in binary form
+ * @algo: digest algorithm
+ * @actions: actions (measure/appraise) performed by IMA on the digest list
+ * @digest_list: digest list to delete
+ *
+ * This function searches the digest of the digest list in the hash table for
+ * the COMPACT_DIGEST_LIST type. If it is found, this function frees the buffer
+ * and the digest list item allocated in digest_list_add().
+ *
+ * This function will be executed only for digest lists that were previously
+ * added.
+ *
+ * Proper locking must be provided by the caller.
+ */
+void digest_list_del(u8 *digest, enum hash_algo algo, u8 actions,
+ struct digest_list_item *digest_list)
+{
+ /* Delete the digest item associated to the digest list. */
+ digest_del(digest, algo, COMPACT_DIGEST_LIST, digest_list, 0, 0);
+
+ /*
+ * Free the buffer, the label and the digest list item allocated when
+ * the digest list was added.
+ */
+ digest_list_free(digest_list);
+}
+
+static int __init digest_list_cache_init(void)
+{
+ digest_list_item_cache = kmem_cache_create("digest_list_item_cache",
+ sizeof(struct digest_list_item),
+ 0, SLAB_PANIC, NULL);
+
+ digest_list_item_ref_cache = kmem_cache_create(
+ "digest_list_item_ref_cache",
+ sizeof(struct digest_list_item_ref), 0,
+ SLAB_PANIC, NULL);
+
+ digest_item_cache = kmem_cache_create("digest_item_cache",
+ sizeof(struct digest_item), 0,
+ SLAB_PANIC, NULL);
+
+ return 0;
+}
+
+late_initcall(digest_list_cache_init)
--
2.25.1