Re: [Linux-ima-devel] [PATCH 5/7] ima: add securityfs interface to save a measurements list with kexec header

Mimi Zohar
2017-06-05T06:06:05Z

On Tue, 2017-05-16 at 14:53 +0200, Roberto Sassu wrote:
> 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.

Please correct me if I'm wrong.  Your code walks the measurement list
calculating the total number of measurements and the memory size
needed to store in the kexec header.  Can't there be additional
measurements between the time these values - total number of
measurements and memory needed - were calculated and actually saving
the measurements?  How would that be any different than the problem
you're trying to solve?  In both cases, the number of measurements
might be less than the actual number of measurements.

As long as you query the number of measurements before getting the
memory needed, unless you're trying to verify a TPM quote, having
fewer measurements shouldn't be a problem for testing.

>
> Signed-off-by: Roberto Sassu <roberto.sassu@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;

What is this?

> +
> + if (!khdr)
> + break;

Does this test need to be in the loop?

Mimi

> +
> + 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);