mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 15:22:21 -04:00
ima: add critical data measurement for loaded policy
IMA policy can be written multiple times in the securityfs policy file
at runtime if CONFIG_IMA_WRITE_POLICY=y. When IMA_APPRAISE_POLICY is
required, the policy needs to be signed to be loaded, writing the absolute
path of the file containing the new policy:
echo /path/of/custom_ima_policy > /sys/kernel/security/ima/policy
When this is not required, policy can be written directly, rule by rule:
echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \
"audit func=BPRM_CHECK mask=MAY_EXEC\n" \
> /sys/kernel/security/ima/policy
In this case, a new policy can be loaded without being measured or
appraised.
Add a new critical data record to measure the textual policy
representation when it becomes effective. Include in the
architecture-specific policy the new critical data record only when it
is not mandatory to load a signed policy. Additionally, enable the
policy serialization code even when CONFIG_IMA_READ_POLICY=n.
To verify the template data hash value, convert the buffer policy data
to binary:
grep "ima_policy_loaded" \
/sys/kernel/security/integrity/ima/ascii_runtime_measurements | \
tail -1 | cut -d' ' -f 6 | xxd -r -p | sha256sum
Signed-off-by: Enrico Bravi <enrico.bravi@polito.it>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user