From 9f1840db5abfabeaeb7835bc277a75ac23c4b188 Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 16:24:05 -0300
Subject: [PATCH 05/11] ima: store new namespace policy structure in a radix
 tree

New ima_ns_policy structure to describe IMA policy data per namespace.
Using a radix tree to map namespace ids to a respective ima_ns_policy
structure.
When it is needed to retrieve IMA policy rules/flags, the target
ima_ns_policy structure is retrieved from the radix tree by getting the
namespace id from the current context.

Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
 security/integrity/ima/ima.h        | 37 +++++++++++++++++
 security/integrity/ima/ima_fs.c     | 79 ++++++++++++++++++++++++++++++++++---
 security/integrity/ima/ima_init.c   |  2 +
 security/integrity/ima/ima_policy.c | 29 +++++++++-----
 4 files changed, 133 insertions(+), 14 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 6e8ca8e..1c5c875 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -140,6 +140,21 @@ static inline void ima_load_kexec_buffer(void) {}
  */
 extern bool ima_canonical_fmt;
 
+/* Namespace policy globals */
+struct ima_ns_policy {
+	struct dentry *policy_dentry;
+	struct dentry *ns_dentry;
+	struct list_head *ima_rules;
+	struct list_head ima_policy_rules;
+	int ima_policy_flag;
+	int ima_appraise;
+};
+
+#ifdef CONFIG_IMA_PER_NAMESPACE
+extern spinlock_t ima_ns_policy_lock;
+extern struct radix_tree_root ima_ns_policy_mapping;
+#endif
+
 /* Internal IMA function definitions */
 int ima_init(void);
 int ima_fs_init(void);
@@ -166,6 +181,27 @@ int ima_measurements_show(struct seq_file *m, void *v);
 unsigned long ima_get_binary_runtime_size(void);
 int ima_init_template(void);
 void ima_init_template_list(void);
+#ifdef CONFIG_IMA_PER_NAMESPACE
+static inline void ima_namespace_lock_init(void) {
+	spin_lock_init(&ima_ns_policy_lock);
+}
+static inline void ima_namespace_lock(void) {
+	spin_lock(&ima_ns_policy_lock);
+}
+static inline void ima_namespace_unlock(void) {
+	spin_unlock(&ima_ns_policy_lock);
+}
+#else
+static inline void ima_namespace_lock_init(void) {
+	return;
+}
+static inline void ima_namespace_lock(void) {
+	return;
+}
+static inline void ima_namespace_unlock(void) {
+	return;
+}
+#endif
 
 /*
  * used to protect h_table and sha_table
@@ -226,6 +262,7 @@ void ima_update_policy(void);
 void ima_update_policy_flag(void);
 ssize_t ima_parse_add_rule(char *);
 void ima_delete_rules(void);
+void ima_free_policy_rules(struct list_head *policy_rules);
 int ima_check_policy(void);
 void *ima_policy_start(struct seq_file *m, loff_t *pos);
 void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 6456407..ce6dcdf 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -275,6 +275,48 @@ static const struct file_operations ima_ascii_measurements_ops = {
 };
 
 #ifdef CONFIG_IMA_PER_NAMESPACE
+/* used for namespace policy rules initialization */
+static LIST_HEAD(empty_policy);
+
+static int allocate_namespace_policy(struct ima_ns_policy **ins,
+		struct dentry *policy_dentry, struct dentry *ns_dentry)
+{
+	int result;
+	struct ima_ns_policy *p;
+
+	p = kmalloc(sizeof(struct ima_ns_policy), GFP_KERNEL);
+	if (!p) {
+		result = -ENOMEM;
+		goto out;
+	}
+
+	p->policy_dentry = policy_dentry;
+	p->ns_dentry = ns_dentry;
+	p->ima_appraise = 0;
+	p->ima_policy_flag = 0;
+	INIT_LIST_HEAD(&p->ima_policy_rules);
+	/* namespace starts with empty rules and not pointing to
+	 * ima_policy_rules */
+	p->ima_rules = &empty_policy;
+
+	result = 0;
+	*ins = p;
+
+out:
+	return result;
+}
+
+static void free_namespace_policy(struct ima_ns_policy *ins)
+{
+	if (ins->policy_dentry)
+		securityfs_remove(ins->policy_dentry);
+	securityfs_remove(ins->ns_dentry);
+
+	ima_free_policy_rules(&ins->ima_policy_rules);
+
+	kfree(ins);
+}
+
 /*
  * check_mntns: check a mount namespace is valid
  *
@@ -476,9 +518,11 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 #ifndef	CONFIG_IMA_WRITE_POLICY
 	securityfs_remove(ima_policy);
 	ima_policy = NULL;
-#else
-	clear_bit(IMA_FS_BUSY, &ima_fs_flags);
 #endif
+
+	/* always clear the busy flag so other namespaces can use it */
+	clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+
 	return 0;
 }
 
@@ -500,11 +544,14 @@ static int create_mnt_ns_directory(unsigned int ns_id)
 	int result;
 	struct dentry *ns_dir, *ns_policy;
 	char dir_name[64];
+	struct ima_ns_policy *ins;
 
 	snprintf(dir_name, sizeof(dir_name), "%u", ns_id);
 
 	ns_dir = securityfs_create_dir(dir_name, ima_dir);
 	if (IS_ERR(ns_dir)) {
+		/* TODO: handle EEXIST error, remove the folder and
+		continue the procedure */
 		result = PTR_ERR(ns_dir);
 		goto out;
 	}
@@ -518,7 +565,15 @@ static int create_mnt_ns_directory(unsigned int ns_id)
 		goto out;
 	}
 
-	result = 0;
+	result = allocate_namespace_policy(&ins, ns_policy, ns_dir);
+	if (!result) {
+		result = radix_tree_insert(&ima_ns_policy_mapping, ns_id, ins);
+		if (result)
+			free_namespace_policy(ins);
+	} else {
+		securityfs_remove(ns_policy);
+		securityfs_remove(ns_dir);
+	}
 
 out:
 	return result;
@@ -528,6 +583,7 @@ static ssize_t handle_new_namespace_policy(const char *data, size_t datalen)
 {
 	unsigned int ns_id;
 	ssize_t result;
+	struct ima_ns_policy *ins;
 
 	result = -EINVAL;
 
@@ -536,21 +592,34 @@ static ssize_t handle_new_namespace_policy(const char *data, size_t datalen)
 		goto out;
 	}
 
+	rcu_read_lock();
+	ins = radix_tree_lookup(&ima_ns_policy_mapping, ns_id);
+	rcu_read_unlock();
+	if (ins) {
+		pr_info("IMA: directory for namespace id %u already created\n", ns_id);
+		result = datalen;
+		goto out;
+	}
+
+	ima_namespace_lock();
 	if (check_mntns(ns_id)) {
 		result = -ENOENT;
 		pr_err("IMA: unused namespace id %u\n", ns_id);
-		goto out;
+		goto out_unlock;
 	}
 
 	result = create_mnt_ns_directory(ns_id);
 	if (result != 0) {
 		pr_err("IMA: namespace id %u directory creation failed\n", ns_id);
-		goto out;
+		goto out_unlock;
 	}
 
 	result = datalen;
 	pr_info("IMA: directory created for namespace id %u\n", ns_id);
 
+out_unlock:
+	ima_namespace_unlock();
+
 out:
 	return result;
 }
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 2967d49..b557ee3 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -135,6 +135,8 @@ int __init ima_init(void)
 	if (rc != 0)
 		return rc;
 
+	ima_namespace_lock_init();
+
 	ima_init_policy();
 
 	return ima_fs_init();
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index aed47b7..2e8c3b7 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -47,6 +47,12 @@
 int ima_policy_flag;
 static int temp_ima_appraise;
 
+#ifdef CONFIG_IMA_PER_NAMESPACE
+/* policy namespace map entries except the initial namespace policy */
+RADIX_TREE(ima_ns_policy_mapping, GFP_ATOMIC);
+spinlock_t ima_ns_policy_lock;
+#endif
+
 #define MAX_LSM_RULES 6
 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
 	LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
@@ -863,19 +869,12 @@ ssize_t ima_parse_add_rule(char *rule)
 	return len;
 }
 
-/**
- * ima_delete_rules() called to cleanup invalid in-flight policy.
- * We don't need locking as we operate on the temp list, which is
- * different from the active one.  There is also only one user of
- * ima_delete_rules() at a time.
- */
-void ima_delete_rules(void)
+void ima_free_policy_rules(struct list_head *policy_rules)
 {
 	struct ima_rule_entry *entry, *tmp;
 	int i;
 
-	temp_ima_appraise = 0;
-	list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
+	list_for_each_entry_safe(entry, tmp, policy_rules, list) {
 		for (i = 0; i < MAX_LSM_RULES; i++)
 			kfree(entry->lsm[i].args_p);
 
@@ -884,6 +883,18 @@ void ima_delete_rules(void)
 	}
 }
 
+/**
+ * ima_delete_rules() called to cleanup invalid in-flight policy.
+ * We don't need locking as we operate on the temp list, which is
+ * different from the active one.  There is also only one user of
+ * ima_delete_rules() at a time.
+ */
+void ima_delete_rules(void)
+{
+	temp_ima_appraise = 0;
+	ima_free_policy_rules(&ima_temp_rules);
+}
+
 #ifdef	CONFIG_IMA_READ_POLICY
 enum {
 	mask_exec = 0, mask_write, mask_read, mask_append
-- 
2.9.3

