Through the new interface binary_kexec_runtime_measurements, it will be
possible to read the same content returned by binary_runtime_measurements,
with the kexec header prepended.

The new interface has been added for testing ima_restore_measurement_list()
which, at the moment, works only on PPC systems. The interface for reading
the binary list with the kexec header will be provided in a separate patch.

The patch reuses ima_measurements_start() and ima_measurements_next()
to send the measurements list to userspace. Their behavior changes
depending on the current dentry.

To provide the correct information in the kexec header,
ima_measurements_start() has to iterate over the whole list and calculate
the number of entries and the total size. It is not possible to read
the value of the global variable binary_runtime_size and ima_htable.len
without taking ima_extend_list_mutex, because there might have been a list
add between the two read operations.

Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com>
---
 security/integrity/ima/Kconfig            |  8 ++++++
 security/integrity/ima/ima.h              |  2 ++
 security/integrity/ima/ima_fs.c           | 42 ++++++++++++++++++++++++++++---
 security/integrity/ima/ima_kexec.c        |  2 +-
 security/integrity/ima/ima_template.c     |  2 +-
 security/integrity/ima/ima_template_lib.c | 19 ++++++++++++++
 security/integrity/ima/ima_template_lib.h |  2 ++
 7 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 370eb2f..0f60c04 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -39,6 +39,14 @@ config IMA_KEXEC
           Depending on the IMA policy, the measurement list can grow to
           be very large.
 
+config IMA_KEXEC_TESTING
+       bool "Enable securityfs interfaces to save/restore measurement list"
+       depends on IMA
+       default n
+       help
+          Use binary_kexec_runtime_measurements to save the binary list
+          with the kexec header; use restore_kexec_list to restore a list.
+
 config IMA_MEASURE_PCR_IDX
        int
        depends on IMA
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 10ef9c8..416497b 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -49,6 +49,8 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 #define IMA_TEMPLATE_IMA_NAME "ima"
 #define IMA_TEMPLATE_IMA_FMT "d|n"
 
+#define IMA_KEXEC_HDR_VERSION 1
+
 /* current content of the policy */
 extern int ima_policy_flag;
 
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index ca303e5..a93f941 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -25,6 +25,7 @@
 #include <linux/vmalloc.h>
 
 #include "ima.h"
+#include "ima_template_lib.h"
 
 static DEFINE_MUTEX(ima_write_mutex);
 
@@ -75,28 +76,52 @@ static const struct file_operations 
ima_measurements_count_ops = {
        .llseek = generic_file_llseek,
 };
 
+static struct dentry *binary_kexec_runtime_measurements;
+
 /* returns pointer to hlist_node */
 static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 {
        loff_t l = *pos;
        struct ima_queue_entry *qe;
+       struct ima_queue_entry *qe_found = NULL;
+       unsigned long size = 0, count = 0;
+       bool khdr = m->file->f_path.dentry == binary_kexec_runtime_measurements;
 
        /* we need a lock since pos could point beyond last element */
        rcu_read_lock();
        list_for_each_entry_rcu(qe, &ima_measurements, later) {
-               if (!l--) {
-                       rcu_read_unlock();
-                       return qe;
+               if (!l) {
+                       qe_found = qe_found ? qe_found : qe;
+
+                       if (!khdr)
+                               break;
+
+                       if (*pos)
+                               break;
+
+                       size += ima_get_template_entry_size(qe->entry);
+                       count++;
+                       m->private = qe;
+                       continue;
                }
+               l--;
        }
        rcu_read_unlock();
-       return NULL;
+
+       if (khdr && size)
+               ima_show_kexec_hdr(m, count, size);
+
+       return qe_found;
 }
 
 static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
 {
+       bool khdr = m->file->f_path.dentry == binary_kexec_runtime_measurements;
        struct ima_queue_entry *qe = v;
 
+       if (khdr && qe == m->private)
+               return NULL;
+
        /* lock protects when reading beyond last element
         * against concurrent list-extension
         */
@@ -490,8 +515,17 @@ int __init ima_fs_init(void)
        if (IS_ERR(ima_policy))
                goto out;
 
+#ifdef CONFIG_IMA_KEXEC_TESTING
+       binary_kexec_runtime_measurements =
+           securityfs_create_file("binary_kexec_runtime_measurements",
+                                  S_IRUSR | S_IRGRP, ima_dir, NULL,
+                                  &ima_measurements_ops);
+       if (IS_ERR(binary_kexec_runtime_measurements))
+               goto out;
+#endif
        return 0;
 out:
+       securityfs_remove(binary_kexec_runtime_measurements);
        securityfs_remove(violations);
        securityfs_remove(runtime_measurements_count);
        securityfs_remove(ascii_runtime_measurements);
diff --git a/security/integrity/ima/ima_kexec.c 
b/security/integrity/ima/ima_kexec.c
index e473eee..b0b8ed2 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -36,7 +36,7 @@ static int ima_dump_measurement_list(unsigned long 
*buffer_size, void **buffer,
        file.count = sizeof(khdr);      /* reserved space */
 
        memset(&khdr, 0, sizeof(khdr));
-       khdr.version = 1;
+       khdr.version = IMA_KEXEC_HDR_VERSION;
        list_for_each_entry_rcu(qe, &ima_measurements, later) {
                if (file.count < file.size) {
                        khdr.count++;
diff --git a/security/integrity/ima/ima_template.c 
b/security/integrity/ima/ima_template.c
index 7412d02..f86456c 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -347,7 +347,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
                khdr->buffer_size = le64_to_cpu(khdr->buffer_size);
        }
 
-       if (khdr->version != 1) {
+       if (khdr->version != IMA_KEXEC_HDR_VERSION) {
                pr_err("attempting to restore a incompatible measurement list");
                return -EINVAL;
        }
diff --git a/security/integrity/ima/ima_template_lib.c 
b/security/integrity/ima/ima_template_lib.c
index 28af43f..de2b064 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -159,6 +159,25 @@ void ima_show_template_sig(struct seq_file *m, enum 
ima_show_type show,
        ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
 }
 
+void ima_show_kexec_hdr(struct seq_file *m, unsigned long count,
+                       unsigned long size)
+{
+       struct ima_kexec_hdr khdr;
+
+       memset(&khdr, 0, sizeof(khdr));
+       khdr.version = IMA_KEXEC_HDR_VERSION;
+       khdr.count = count;
+       khdr.buffer_size = sizeof(khdr) + size;
+
+       if (ima_canonical_fmt) {
+               khdr.version = cpu_to_le16(khdr.version);
+               khdr.count = cpu_to_le64(khdr.count);
+               khdr.buffer_size = cpu_to_le64(khdr.buffer_size);
+       }
+
+       ima_putc(m, &khdr, sizeof(khdr));
+}
+
 /**
  * ima_parse_buf() - Parses lengths and data from an input buffer
  * @bufstartp:       Buffer start address.
diff --git a/security/integrity/ima/ima_template_lib.h 
b/security/integrity/ima/ima_template_lib.h
index 6a3d8b8..069e4ba 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -29,6 +29,8 @@ void ima_show_template_string(struct seq_file *m, enum 
ima_show_type show,
                              struct ima_field_data *field_data);
 void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
                           struct ima_field_data *field_data);
+void ima_show_kexec_hdr(struct seq_file *m, unsigned long count,
+                       unsigned long size);
 int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
                  int maxfields, struct ima_field_data *fields, int *curfields,
                  unsigned long *len_mask, int enforce_mask, char *bufname);
-- 
2.9.3

Reply via email to