From: Roberto Sassu <[email protected]>

Introduce the new kernel option ima_flush_htable to decide whether or not
the digests of staged measurement entries are flushed from the hash table,
when they are deleted.

When the option is enabled, replace the old hash table with a new one,
by calling ima_alloc_replace_htable(), and completely delete the
measurements entries.

Note: This code derives from the Alt-IMA Huawei project, whose license is
      GPL-2.0 OR MIT.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <[email protected]>
---
 .../admin-guide/kernel-parameters.txt         |  4 +++
 security/integrity/ima/ima.h                  |  1 +
 security/integrity/ima/ima_queue.c            | 36 ++++++++++++++++---
 3 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt 
b/Documentation/admin-guide/kernel-parameters.txt
index 89670c5e7c8e..a651a3864dcf 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2345,6 +2345,10 @@ Kernel parameters
                        Use the canonical format for the binary runtime
                        measurements, instead of host native format.
 
+       ima_flush_htable  [IMA]
+                       Flush the IMA hash table when deleting all the
+                       staged measurement entries.
+
        ima_hash=       [IMA]
                        Format: { md5 | sha1 | rmd160 | sha256 | sha384
                                   | sha512 | ... }
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 65db152a0a24..699b735dec7d 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -340,6 +340,7 @@ extern atomic_long_t ima_num_entries[BINARY__LAST];
 extern atomic_long_t ima_num_violations;
 extern struct hlist_head __rcu *ima_htable;
 extern struct mutex ima_extend_list_mutex;
+extern bool ima_flush_htable;
 
 static inline unsigned int ima_hash_key(u8 *digest)
 {
diff --git a/security/integrity/ima/ima_queue.c 
b/security/integrity/ima/ima_queue.c
index 50519ed837d4..f5c18acfbc43 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -22,6 +22,20 @@
 
 #define AUDIT_CAUSE_LEN_MAX 32
 
+bool ima_flush_htable;
+
+static int __init ima_flush_htable_setup(char *str)
+{
+       if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
+               pr_warn("Hash table not enabled, ignoring request to flush\n");
+               return 1;
+       }
+
+       ima_flush_htable = true;
+       return 1;
+}
+__setup("ima_flush_htable", ima_flush_htable_setup);
+
 /* pre-allocated array of tpm_digest structures to extend a PCR */
 static struct tpm_digest *digests;
 
@@ -317,10 +331,11 @@ int ima_queue_stage(void)
        return ret;
 }
 
-static void ima_queue_delete(struct list_head *head);
+static void ima_queue_delete(struct list_head *head, bool flush_htable);
 
 int ima_queue_staged_delete_all(void)
 {
+       struct hlist_head *old_queue = NULL;
        LIST_HEAD(ima_measurements_trim);
 
        mutex_lock(&ima_extend_list_mutex);
@@ -337,13 +352,26 @@ int ima_queue_staged_delete_all(void)
        if (IS_ENABLED(CONFIG_IMA_KEXEC))
                binary_runtime_size[BINARY_STAGED] = 0;
 
+       if (ima_flush_htable) {
+               old_queue = ima_alloc_replace_htable();
+               if (IS_ERR(old_queue)) {
+                       mutex_unlock(&ima_extend_list_mutex);
+                       return PTR_ERR(old_queue);
+               }
+       }
+
        mutex_unlock(&ima_extend_list_mutex);
 
-       ima_queue_delete(&ima_measurements_trim);
+       if (ima_flush_htable) {
+               synchronize_rcu();
+               kfree(old_queue);
+       }
+
+       ima_queue_delete(&ima_measurements_trim, ima_flush_htable);
        return 0;
 }
 
-static void ima_queue_delete(struct list_head *head)
+static void ima_queue_delete(struct list_head *head, bool flush_htable)
 {
        struct ima_queue_entry *qe, *qe_tmp;
        unsigned int i;
@@ -365,7 +393,7 @@ static void ima_queue_delete(struct list_head *head)
                list_del(&qe->later);
 
                /* No leak if condition is false, referenced by ima_htable. */
-               if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
+               if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE) || flush_htable) {
                        kfree(qe->entry->digests);
                        kfree(qe->entry);
                        kfree(qe);
-- 
2.43.0


Reply via email to