diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index cebaeb620b89..6817ee53ed9e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -55,6 +55,8 @@ enum binary_lists { /* current content of the policy */ extern int ima_policy_flag; +extern struct mutex ima_write_mutex; + /* bitset of digests algorithms allowed in the setxattr hook */ extern atomic_t ima_setxattr_allowed_hash_algorithms; @@ -467,6 +469,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos); void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos); void ima_policy_stop(struct seq_file *m, void *v); int ima_policy_show(struct seq_file *m, void *v); +void ima_measure_loaded_policy(void); /* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c index bca57d836cb9..be7911009454 100644 --- a/security/integrity/ima/ima_efi.c +++ b/security/integrity/ima/ima_efi.c @@ -17,6 +17,8 @@ static const char * const sb_arch_rules[] = { #endif #if IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) && IS_ENABLED(CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY) "appraise func=POLICY_CHECK appraise_type=imasig", +#else + "measure func=CRITICAL_DATA label=ima_policy", #endif "measure func=MODULE_CHECK", NULL diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 174a94740da1..92ca4d62c94d 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -32,7 +32,9 @@ */ #define STAGED_REQ_LENGTH 21 -static DEFINE_MUTEX(ima_write_mutex); +/* lock for protecting concurrent IMA policy updates */ +DEFINE_MUTEX(ima_write_mutex); + static DEFINE_MUTEX(ima_measure_mutex); static long ima_measure_users; static struct task_struct *measure_writer; @@ -752,6 +754,10 @@ static int ima_release_policy(struct inode *inode, struct file *file) } ima_update_policy(); + + mutex_lock(&ima_write_mutex); + ima_measure_loaded_policy(); + mutex_unlock(&ima_write_mutex); #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY) securityfs_remove(file->f_path.dentry); #elif defined(CONFIG_IMA_WRITE_POLICY) diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index b1c010e8eb13..09da140de67c 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -53,6 +53,8 @@ #define INVALID_PCR(a) (((a) < 0) || \ (a) >= (sizeof_field(struct ima_iint_cache, measured_pcrs) * 8)) +static size_t max_rule_len; + int ima_policy_flag; static int temp_ima_appraise; static int build_ima_appraise __ro_after_init; @@ -955,6 +957,8 @@ void __init ima_init_policy(void) { int build_appraise_entries, arch_entries; + max_rule_len = 255; + /* if !ima_policy, we load NO default rules */ if (ima_policy) add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules), @@ -1994,6 +1998,9 @@ ssize_t ima_parse_add_rule(char *rule) list_add_tail(&entry->list, &ima_temp_rules); + if (len > max_rule_len) + max_rule_len = len; + return len; } @@ -2021,7 +2028,6 @@ const char *const func_tokens[] = { __ima_hooks(__ima_hook_stringify) }; -#ifdef CONFIG_IMA_READ_POLICY enum { mask_exec = 0, mask_write, mask_read, mask_append }; @@ -2323,7 +2329,6 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, "\n"); return 0; } -#endif /* CONFIG_IMA_READ_POLICY */ #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING) /* @@ -2380,3 +2385,83 @@ bool ima_appraise_signature(enum kernel_read_file_id id) return found; } #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */ + +/** + * ima_measure_loaded_policy - measure the active IMA policy ruleset + * + * Must be called with ima_write_mutex held, as it performs two + * separate RCU read passes over ima_rules and relies on the mutex + * to prevent concurrent policy updates between them. + */ +void ima_measure_loaded_policy(void) +{ + const char *event_name = "ima_policy_loaded"; + const char *op = "measure_loaded_ima_policy"; + size_t rule_len = max_rule_len + 2; + struct ima_rule_entry *rule_entry; + struct list_head *ima_rules_tmp; + struct seq_file file = { 0 }; + int result = -ENOMEM; + size_t file_len = 0; + char *rule; + + lockdep_assert_held(&ima_write_mutex); + + rule = kmalloc(rule_len, GFP_KERNEL); + if (!rule) { + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, event_name, + op, "ENOMEM", result, 0); + return; + } + + /* calculate IMA policy rules memory size */ + file.buf = rule; + file.read_pos = 0; + file.size = rule_len; + file.count = 0; + + rcu_read_lock(); + ima_rules_tmp = rcu_dereference(ima_rules); + list_for_each_entry_rcu(rule_entry, ima_rules_tmp, list) { + ima_policy_show(&file, rule_entry); + + if (seq_has_overflowed(&file)) { + result = -E2BIG; + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, + event_name, op, "rule_length", + result, 0); + rcu_read_unlock(); + goto free_rule; + } + + file_len += file.count; + file.count = 0; + } + rcu_read_unlock(); + + /* copy IMA policy rules to a buffer for measuring */ + file.buf = kmalloc(file_len, GFP_KERNEL); + if (!file.buf) { + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, event_name, + op, "ENOMEM", result, 0); + goto free_rule; + } + + file.read_pos = 0; + file.size = file_len; + file.count = 0; + + rcu_read_lock(); + ima_rules_tmp = rcu_dereference(ima_rules); + list_for_each_entry_rcu(rule_entry, ima_rules_tmp, list) { + ima_policy_show(&file, rule_entry); + } + rcu_read_unlock(); + + ima_measure_critical_data("ima_policy", event_name, file.buf, + file.count, false, NULL, 0); + + kfree(file.buf); +free_rule: + kfree(rule); +}