[RFC][PATCH] ima: add measurement for first unverified write on ima policy file

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The first write on the ima policy file permits to override the default
policy defined with the ima_policy= boot parameter. This can be done
by adding the /etc/ima/ima-policy which allows loading the custom policy
during boot. It is also possible to load custom policy at runtime through
file operations:

cp custom_ima_policy /sys/kernel/security/ima/policy
cat custom_ima_policy > /sys/kernel/security/ima/policy

or by writing the absolute path of the file containing the custom policy:

echo /path/of/custom_ima_policy > /sys/kernel/security/ima/policy

In these cases, file signature can be necessary to load the policy
(func=POLICY_CHECK). Custom policy can also be set at runtime by directly
writing the policy stream on the ima policy file:

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, there is no mechanism to verify the integrity of the new
policy.

Add a new entry in the ima measurements list containing the ascii custom
ima policy buffer when not verified at load time.

Signed-off-by: Enrico Bravi <enrico.bravi@xxxxxxxxx>
---
 security/integrity/ima/ima.h        |  3 ++
 security/integrity/ima/ima_fs.c     | 11 ++++
 security/integrity/ima/ima_policy.c | 81 ++++++++++++++++++++++++++++-
 3 files changed, 93 insertions(+), 2 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index c0d3b716d11f..27cba2e612a5 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 bool override_ima_policy;
+
 /* bitset of digests algorithms allowed in the setxattr hook */
 extern atomic_t ima_setxattr_allowed_hash_algorithms;
 
@@ -414,6 +416,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_override_policy(size_t file_len);
 
 /* Appraise integrity measurements */
 #define IMA_APPRAISE_ENFORCE	0x01
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index e4a79a9b2d58..8c516de4aebe 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -309,6 +309,9 @@ static const struct file_operations ima_ascii_measurements_ops = {
 	.release = seq_release,
 };
 
+static size_t text_policy_len;
+bool override_ima_policy;
+
 static ssize_t ima_read_policy(char *path)
 {
 	void *data = NULL;
@@ -383,6 +386,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 		result = -EACCES;
 	} else {
 		result = ima_parse_add_rule(data);
+		text_policy_len += (result + 1);
 	}
 	mutex_unlock(&ima_write_mutex);
 out_free:
@@ -532,6 +536,10 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 	}
 
 	ima_update_policy();
+	if (unlikely(override_ima_policy && text_policy_len)) {
+		ima_measure_override_policy(text_policy_len);
+		override_ima_policy = false;
+	}
 #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
 	securityfs_remove(ima_policy);
 	ima_policy = NULL;
@@ -558,6 +566,9 @@ int __init ima_fs_init(void)
 	ascii_securityfs_measurement_lists = NULL;
 	binary_securityfs_measurement_lists = NULL;
 
+	text_policy_len = 0;
+	override_ima_policy = false;
+
 	ima_dir = securityfs_create_dir("ima", integrity_dir);
 	if (IS_ERR(ima_dir))
 		return PTR_ERR(ima_dir);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 21a8e54c383f..34753bce4668 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"
@@ -1044,6 +1045,8 @@ void ima_update_policy(void)
 	if (ima_rules != (struct list_head __rcu *)policy) {
 		ima_policy_flag = 0;
 
+		override_ima_policy = true;
+
 		rcu_assign_pointer(ima_rules, policy);
 		/*
 		 * IMA architecture specific policy rules are specified
@@ -1982,7 +1985,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
 };
@@ -1994,6 +1996,7 @@ static const char *const mask_tokens[] = {
 	"^MAY_APPEND"
 };
 
+#ifdef	CONFIG_IMA_READ_POLICY
 void *ima_policy_start(struct seq_file *m, loff_t *pos)
 {
 	loff_t l = *pos;
@@ -2028,6 +2031,7 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
 void ima_policy_stop(struct seq_file *m, void *v)
 {
 }
+#endif	/* CONFIG_IMA_READ_POLICY */
 
 #define pt(token)	policy_tokens[token].pattern
 #define mt(token)	mask_tokens[token]
@@ -2276,7 +2280,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)
 /*
@@ -2333,3 +2336,77 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
 	return found;
 }
 #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
+
+void ima_measure_override_policy(size_t file_len)
+{
+	struct ima_iint_cache iint = {};
+	const char event_name[] = "ima-policy-override";
+	struct ima_event_data event_data = {.iint = &iint,
+					    .filename = event_name};
+	struct ima_max_digest_data hash;
+	struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
+						struct ima_digest_data, hdr);
+	static const char op[] = "measure_ima_policy_override";
+	struct ima_template_entry *entry = NULL;
+	static char *audit_cause = "ENOMEM";
+	struct ima_template_desc *template;
+	struct ima_rule_entry *rule_entry;
+	struct list_head *ima_rules_tmp;
+	struct seq_file file;
+	int result = -ENOMEM;
+	int violation = 0;
+
+	file.buf = vmalloc(file_len);
+	if (!file.buf)
+		goto out;
+
+	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();
+
+	event_data.buf = file.buf;
+	event_data.buf_len = file.count;
+
+	template = ima_template_desc_buf();
+	if (!template) {
+		audit_cause = "ima_template_desc_buf";
+		goto out_free;
+	}
+
+	iint.ima_hash = hash_hdr;
+	iint.ima_hash->algo = ima_hash_algo;
+	iint.ima_hash->length = hash_digest_size[ima_hash_algo];
+
+	result = ima_calc_buffer_hash(file.buf, file.count, iint.ima_hash);
+	if (result < 0) {
+		audit_cause = "hashing_error";
+		goto out_free;
+	}
+
+	result = ima_alloc_init_template(&event_data, &entry, template);
+	if (result < 0) {
+		audit_cause = "alloc_entry";
+		goto out_free;
+	}
+
+	result = ima_store_template(entry, violation, NULL, event_data.buf,
+				    CONFIG_IMA_MEASURE_PCR_IDX);
+	if (result < 0) {
+		audit_cause = "store_entry";
+		ima_free_template_entry(entry);
+	}
+
+out_free:
+	kvfree(file.buf);
+out:
+	if (result < 0)
+		integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, event_name,
+				    op, audit_cause, result, 1);
+}

base-commit: ffd294d346d185b70e28b1a28abe367bbfe53c04
-- 
2.47.1





[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux Kernel Hardening]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux