ima: measure kexec boot command line In addition to files, other things (eg. boot command line) need to be measured to attest to the integrity of a running system. A new IMA hook named ima_buffer_check() calculates and includes the buffer hash in the measurement list. Callers of this hook provide the buffer, buffer length and a policy identifier. To measure the boot command line, this patch defines a new policy identifier named BOOT_CHECK. Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index e80f767..227df54 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy
@@ -27,6 +27,7 @@ base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] [FIRMWARE_CHECK] [KEXEC_CHECK] [INITRAMFS_CHECK] + [BOOT_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 969552b..6c2b80a 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h
@@ -22,12 +22,19 @@ IMA_MAX_READ_CHECK }; +enum ima_buffer_id { + BOOT_CHECK = IMA_MAX_READ_CHECK, + IMA_MAX_BUFFER_ID +}; + #ifdef CONFIG_IMA extern int ima_bprm_check(struct linux_binprm *bprm); extern int ima_file_check(struct file *file, int mask, int opened); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); extern int ima_module_check(struct file *file); +extern void ima_buffer_check(void *buf, loff_t size, + enum ima_buffer_id buffer_id); extern int ima_hash_and_process_file(struct file *file, void *buf, loff_t size, enum ima_policy_id policy_id); @@ -58,13 +65,18 @@ return 0; } +static inline void ima_buffer_check(void *buf, loff_t size, + enum ima_buffer_id buffer_id) +{ + return; +} + static inline int ima_hash_and_process_file(struct file *file, void *buf, loff_t size, enum ima_policy_id policy_id) { return 0; } - #endif /* CONFIG_IMA */ #ifdef CONFIG_IMA_APPRAISE
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index f7c3ce4..c98dfb1 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c
@@ -175,6 +175,9 @@ ret = -EINVAL; goto out; } + + ima_buffer_check(image->cmdline_buf, cmdline_len - 1, + BOOT_CHECK); } /* Call arch image load handlers */
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e8f111b..09ccb4e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h
@@ -122,6 +122,7 @@ void ima_print_digest(struct seq_file *m, u8 *digest, u32 size); struct ima_template_desc *ima_template_desc_current(void); int ima_init_template(void); +int ima_match_buffer_id(enum ima_buffer_id buffer_id); /* * used to protect h_table and sha_table @@ -161,7 +162,7 @@ /* IMA policy related functions */ enum ima_hooks { - FILE_CHECK = IMA_MAX_READ_CHECK, + FILE_CHECK = IMA_MAX_BUFFER_ID, MMAP_CHECK, BPRM_CHECK, POST_SETATTR
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 2955118..b6e9bf2 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c
@@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/xattr.h> #include <linux/ima.h> +#include <crypto/hash_info.h> #include "ima.h" @@ -337,6 +338,72 @@ return 0; } +static void process_buffer_measurement(void *buf, loff_t size, + enum ima_buffer_id buffer_id) +{ + 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}; + struct ima_digest_data *hash; + char *name; + int length = hash_digest_size[ima_hash_algo]; + int violation = 0; + int result; + + hash = kzalloc(sizeof(*hash) + length, GFP_KERNEL); + if (!hash) + return; + + hash->algo = ima_hash_algo; + result = ima_calc_buffer_hash(buf, size, hash); + if (result < 0) + goto out; + + iint->ima_hash = hash; + iint->ima_hash->algo = ima_hash_algo; + iint->ima_hash->length = length; + + switch (buffer_id) { + case BOOT_CHECK: + name = "boot-cmdline"; + break; + default: + name = "unknown"; + } + + event_data.filename = name; + result = ima_alloc_init_template(&event_data, &entry); + if (result < 0) + goto out; + + result = ima_store_template(entry, violation, NULL, name); + if (result < 0) { + ima_free_template_entry(entry); + goto out; + } + return; + +out: + kfree(hash); +} + +/** + * ima_buffer_check - based on policy, collect & store buffer measurement + * @buf: pointer to buffer + * @size: size of buffer + * @buffer_id: caller identifier + * + * Buffers can only be measured, not appraised. The buffer identifier + * is used as the measurement list entry name (eg. boot_cmdline). + */ +void ima_buffer_check(void *buf, loff_t size, enum ima_buffer_id buffer_id) +{ + if (!ima_match_buffer_id(buffer_id)) + return; + + process_buffer_measurement(buf, size, buffer_id); +} + /** * ima_hash_and_process_file - in memory collect/appraise/audit measurement * @file: pointer to the file to be measured/appraised/audit
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index bf30acf..7645616 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c
@@ -56,6 +56,7 @@ union { enum ima_hooks func; enum ima_policy_id policy_id; + enum ima_buffer_id buffer_id; } hooks; int mask; unsigned long fsmagic; @@ -363,6 +364,29 @@ return action; } +/** + * ima_match_buffer_id - decision based on policy containing buffer id + * @buffer_id: IMA buffer identifier + * + * Measure decision based the buffer identifier's existence in policy. + * (Without an inode, buffers can only be measured, not appraised.) + */ +int ima_match_buffer_id(enum ima_buffer_id buffer_id) +{ + struct ima_rule_entry *entry; + int result = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, ima_rules, list) { + if ((entry->action == MEASURE) && (entry->flags & IMA_FUNC) && + (entry->hooks.buffer_id == buffer_id)) + result = 1; + } + rcu_read_unlock(); + + return result; +} + /* * Initialize the ima_policy_flag variable based on the currently * loaded policy. Based on this flag, the decision to short circuit @@ -628,6 +652,8 @@ entry->hooks.policy_id = POLICY_CHECK; else if (strcmp(args[0].from, "MODULE_CHECK") == 0) entry->hooks.policy_id = MODULE_CHECK; + else if (strcmp(args[0].from, "BOOT_CHECK") == 0) + entry->hooks.buffer_id = BOOT_CHECK; else result = -EINVAL; if (!result) @@ -875,7 +901,7 @@ func_file = 0, func_mmap, func_bprm, func_module, func_post, func_kexec, func_initramfs, func_firmware, - func_policy + func_policy, func_cmdline }; static char *func_tokens[] = { @@ -887,7 +913,8 @@ "KEXEC_CHECK", "INITRAMFS_CHECK", "FIRMWARE_CHECK", - "POLICY_CHECK" + "POLICY_CHECK", + "BOOT_CHECK" }; void *ima_policy_start(struct seq_file *m, loff_t *pos) @@ -978,6 +1005,9 @@ case MODULE_CHECK: seq_printf(m, pt(Opt_func), ft(func_module)); break; + case BOOT_CHECK: + seq_printf(m, pt(Opt_func), ft(func_cmdline)); + break; default: snprintf(tbuf, sizeof(tbuf), "%d", entry->hooks.func);