This patch defines a new IMA hook named ima_add_measurement_check() for including pre-calculated measurements in the IMA measurement list. Signed-off-by: Mimi Zohar <zohar at linux.vnet.ibm.com> --- Documentation/ABI/testing/ima_policy | 2 +- include/linux/ima.h | 12 ++++ security/integrity/ima/Kconfig | 8 +++ security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_buffer.c | 116 +++++++++++++++++++++++++++++------ security/integrity/ima/ima_policy.c | 18 +++++- 6 files changed, 136 insertions(+), 21 deletions(-) diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 5a99c6f..e5a137e 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -28,7 +28,7 @@ Description: base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] [FIRMWARE_CHECK] [KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK] - [KEXEC_CMDLINE_CHECK] + [KEXEC_CMDLINE_CHECK] [PRECALC_CHECK] mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND] [[^]MAY_EXEC] fsmagic:= hex value diff --git a/include/linux/ima.h b/include/linux/ima.h index 88203f9..797de51 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -15,6 +15,7 @@ struct linux_binprm; enum ima_buffer_id { MEASURING_KEXEC_CMDLINE, + MEASURING_PRECALC_DATA, MEASURING_MAX_BUFFER_ID }; @@ -29,6 +30,9 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size, extern void ima_post_path_mknod(struct dentry *dentry); extern void ima_buffer_check(void *buf, loff_t size, enum ima_buffer_id buffer_id); +extern void ima_add_measurement_check(const char *hashname, u8 *digest, + loff_t size, enum ima_buffer_id buffer_id, + char *hint); #else static inline int ima_bprm_check(struct linux_binprm *bprm) @@ -72,6 +76,14 @@ static inline void ima_buffer_check(void *buf, loff_t size, { return; } + +static inline void ima_add_measurement_check(const char *hashname, u8 *digest, + loff_t size, + enum ima_buffer_id buffer_id, + char *hint) +{ + return; +} #endif /* CONFIG_IMA */ #ifdef CONFIG_IMA_APPRAISE diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 5487827..0fb54d3 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -44,6 +44,14 @@ config IMA_LSM_RULES help Disabling this option will disregard LSM based policy rules. +config IMA_PRECALC_RULES + bool "Permit pre-calculated measurements (EXPERIMENTAL)" + depends on IMA + default n + help + Enabling this option will permit pre-calculated measurements + to be added to the IMA measurement list. + choice prompt "Default template" default IMA_NG_TEMPLATE diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 5f21a9a..ccad21d 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -152,6 +152,7 @@ enum ima_hooks { KEXEC_INITRAMFS_CHECK, KEXEC_CMDLINE_CHECK, POLICY_CHECK, + PRECALC_CHECK, MAX_CHECK }; diff --git a/security/integrity/ima/ima_buffer.c b/security/integrity/ima/ima_buffer.c index e74131b..ad49f6c 100644 --- a/security/integrity/ima/ima_buffer.c +++ b/security/integrity/ima/ima_buffer.c @@ -11,6 +11,8 @@ #include <linux/uaccess.h> #include <linux/module.h> #include <linux/ima.h> +#include <crypto/hash_info.h> +#include <linux/string_helpers.h> #include "ima.h" @@ -21,9 +23,37 @@ struct buffer_idmap { static struct buffer_idmap _idmap[MEASURING_MAX_BUFFER_ID] = { [MEASURING_KEXEC_CMDLINE].func = KEXEC_CMDLINE_CHECK, - [MEASURING_KEXEC_CMDLINE].buf = "boot-cmdline", + [MEASURING_KEXEC_CMDLINE].buf = "kexec-boot-cmdline", + [MEASURING_PRECALC_DATA].func = PRECALC_CHECK, + [MEASURING_PRECALC_DATA].buf = "precalc", }; +#define IMA_MAX_BUFFER_HINT_SIZE 255 + +static int store_buffer_measurement(struct ima_digest_data *hash, int pcr, + char *buffer_hint) +{ + struct ima_template_entry *entry; + struct integrity_iint_cache tmp_iint, *iint = &tmp_iint; + struct ima_event_data event_data = {iint, NULL, NULL, NULL, 0, NULL}; + int violation = 0; + int result; + + iint->ima_hash = hash; + event_data.filename = buffer_hint; + + result = ima_alloc_init_template(&event_data, &entry); + if (result < 0) + return result; + + result = ima_store_template(entry, violation, NULL, + event_data.filename, pcr); + if (result < 0) + ima_free_template_entry(entry); + + return result; +} + static void process_buffer_measurement(void *buf, loff_t size, enum ima_buffer_id buffer_id, int pcr) { @@ -31,10 +61,6 @@ static void process_buffer_measurement(void *buf, loff_t size, struct ima_digest_data hdr; char digest[IMA_MAX_DIGEST_SIZE]; } hash; - struct ima_template_entry *entry; - struct integrity_iint_cache tmp_iint, *iint = &tmp_iint; - struct ima_event_data event_data = {iint, NULL, NULL, NULL, 0, NULL}; - int violation = 0; int result; memset(&hash, 0, sizeof(hash)); @@ -45,20 +71,10 @@ static void process_buffer_measurement(void *buf, loff_t size, return; } - iint->ima_hash = &hash.hdr; - event_data.filename = _idmap[buffer_id].buf; - result = ima_alloc_init_template(&event_data, &entry); - if (result < 0) { - pr_debug("failed allocating template\n"); - return; - } - - result = ima_store_template(entry, violation, NULL, - event_data.filename, pcr); - if (result < 0) { + result = store_buffer_measurement(&hash.hdr, pcr, + _idmap[buffer_id].buf); + if (result < 0) pr_debug("failed storing buffer measurement\n"); - ima_free_template_entry(entry); - } } /** @@ -82,3 +98,67 @@ void ima_buffer_check(void *buf, loff_t size, enum ima_buffer_id buffer_id) process_buffer_measurement(buf, size, buffer_id, pcr); } EXPORT_SYMBOL_GPL(ima_buffer_check); + +/** + * ima_add_measurement_check - add pre-calculated hash measurement + * @hashname: pointer to hash algorithm name + * @digest: pointer to hash digest + * @size: hash digest size + * @buffer_id: caller identifier + * @hint: measurement identifier + * + * Include pre-calculated hash measurements in the IMA measurement list. + */ +void ima_add_measurement_check(const char *hashname, u8 *digest, loff_t size, + enum ima_buffer_id buffer_id, char *hint) +{ + struct { + struct ima_digest_data hdr; + char digest[IMA_MAX_DIGEST_SIZE]; + } hash; + int pcr = CONFIG_IMA_MEASURE_PCR_IDX; + char buffer_hint[IMA_MAX_BUFFER_HINT_SIZE]; + char *buf; + int result, i; + + if (buffer_id > MEASURING_MAX_BUFFER_ID) + return; + + if (!ima_match_buffer_id(_idmap[buffer_id].func, &pcr)) + return; + + if (!hint) { + pr_debug("missing buffer hint\n"); + return; + } + + buf = kstrdup_quotable(hint, GFP_KERNEL); + if (!buf) { + pr_debug("failed quoting buffer hint\n"); + return; + } + + /* Limit the total measurement hint to IMA_MAX_BUFFER_HINT_SIZE. */ + snprintf(buffer_hint, sizeof(buffer_hint), "(%s) %s", + _idmap[buffer_id].buf, buf); + kfree(buf); + + memset(&hash, 0, sizeof(hash)); + for (i = 1; i < HASH_ALGO__LAST; i++) { + if (strcmp(hashname, hash_algo_name[i]) != 0) + continue; + hash.hdr.algo = i; + break; + } + if (hash.hdr.algo == 0) { + pr_debug("invalid hash algorithm (%d)\n", hash.hdr.algo); + return; + } + + hash.hdr.length = size; + memcpy(&hash.hdr.digest, digest, size); + result = store_buffer_measurement(&hash.hdr, pcr, buffer_hint); + if (result < 0) + pr_debug("failed to store buffer measurement\n"); +} +EXPORT_SYMBOL_GPL(ima_add_measurement_check); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 8e53f84..4094dd7 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -20,6 +20,7 @@ #include <linux/rculist.h> #include <linux/genhd.h> #include <linux/seq_file.h> +#include <linux/ima.h> #include "ima.h" @@ -54,6 +55,12 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; +#ifdef CONFIG_MEASURING_PRECALC_RULES +static int permit_measuring_precalc_rules = 1; +#else +static int permit_measuring_precalc_rules; +#endif + struct ima_rule_entry { struct list_head list; int action; @@ -668,6 +675,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = KEXEC_CMDLINE_CHECK; else if (strcmp(args[0].from, "POLICY_CHECK") == 0) entry->func = POLICY_CHECK; + else if ((strcmp(args[0].from, "PRECALC_CHECK") == 0) + && permit_measuring_precalc_rules) + entry->func = PRECALC_CHECK; else result = -EINVAL; if (!result) @@ -929,7 +939,7 @@ enum { func_file = 0, func_mmap, func_bprm, func_module, func_firmware, func_post, func_kexec_kernel, func_kexec_initramfs, - func_kexec_cmdline, func_policy + func_kexec_cmdline, func_policy, func_precalc }; static char *func_tokens[] = { @@ -942,7 +952,8 @@ static char *func_tokens[] = { "KEXEC_KERNEL_CHECK", "KEXEC_INITRAMFS_CHECK", "KEXEC_CMDLINE_CHECK", - "POLICY_CHECK" + "POLICY_CHECK", + "PRECALC_CHECK" }; void *ima_policy_start(struct seq_file *m, loff_t *pos) @@ -1019,6 +1030,9 @@ static void policy_func_show(struct seq_file *m, enum ima_hooks func) case POLICY_CHECK: seq_printf(m, pt(Opt_func), ft(func_policy)); break; + case PRECALC_CHECK: + seq_printf(m, pt(Opt_func), ft(func_precalc)); + break; default: snprintf(tbuf, sizeof(tbuf), "%d", func); seq_printf(m, pt(Opt_func), tbuf); -- 2.1.0