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