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 <[email protected]>
---
security/integrity/ima/ima.h | 3 ++
security/integrity/ima/ima_efi.c | 2 +
security/integrity/ima/ima_fs.c | 6 ++-
security/integrity/ima/ima_policy.c | 70 ++++++++++++++++++++++++++++-
4 files changed, 78 insertions(+), 3 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 69e9bf0b82c6..befa221716e5 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -46,6 +46,8 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
/* 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;
@@ -452,6 +454,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 ca4931a95098..65e7812c702f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -24,7 +24,7 @@
#include "ima.h"
-static DEFINE_MUTEX(ima_write_mutex);
+DEFINE_MUTEX(ima_write_mutex);
bool ima_canonical_fmt;
static int __init default_canonical_fmt_setup(char *str)
@@ -478,6 +478,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 f7f940a76922..0a70d10da70a 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/rculist.h>
#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
#include <linux/ima.h>
#include "ima.h"
@@ -2020,7 +2021,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
};
@@ -2322,7 +2322,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)
/*
@@ -2379,3 +2378,70 @@ 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";
+ struct ima_rule_entry *rule_entry;
+ struct list_head *ima_rules_tmp;
+ struct seq_file file;
+ int result = -ENOMEM;
+ size_t file_len = 0;
+ char rule[512];
+
+ /* calculate IMA policy rules memory size */
+ file.buf = rule;
+ file.read_pos = 0;
+ file.size = 512;
+ file.count = 0;
+
+ lockdep_assert_held(&ima_write_mutex);
+
+ 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, 1);
+ return;
+ }
+
+ file_len += file.count;
+ file.count = 0;
+ }
+ rcu_read_unlock();
+
+ /* copy IMA policy rules to a buffer for measuring */
+ file.buf = vmalloc(file_len);
+ if (!file.buf) {
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, event_name,
+ op, "ENOMEM", result, 1);
+ return;
+ }
+
+ 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);
+
+ vfree(file.buf);
+}
--
2.52.0