[RFC 07/11] ima: new namespace policy structure to track initial namespace policy data

From: Guilherme Magalhaes
Date: Thu May 11 2017 - 12:33:00 EST


Adding the global ima_initial_namespace_policy which will be used when the
initial namespace IMA policy data must be referred or when
CONFIG_IMA_PER_NAMESPACE is not defined.
New functions which will be used to retrieve the correct namespace IMA
policy data from the radix tree map or from the ima_initial_namespace_policy.
If the given namespace has not yet defined a private IMA policy, the IMA
policy for that namespace falls back to the initial IMA policy by using
ima_initial_namespace_policy.

Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@xxxxxxx>
---
security/integrity/ima/ima.h | 6 ++
security/integrity/ima/ima_fs.c | 112 +++++++++++++++++++++++++++++-------
security/integrity/ima/ima_policy.c | 72 +++++++++++++++++++++++
3 files changed, 170 insertions(+), 20 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 1c5c875..20b927e 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -150,6 +150,7 @@ struct ima_ns_policy {
int ima_appraise;
};

+extern struct ima_ns_policy ima_initial_namespace_policy;
#ifdef CONFIG_IMA_PER_NAMESPACE
extern spinlock_t ima_ns_policy_lock;
extern struct radix_tree_root ima_ns_policy_mapping;
@@ -203,6 +204,11 @@ static inline void ima_namespace_unlock(void) {
}
#endif

+/* IMA namespace function definitions */
+struct ima_ns_policy *ima_get_current_namespace_policy(void);
+struct ima_ns_policy *ima_get_namespace_policy_from_inode(struct inode *inode);
+struct ima_ns_policy *ima_get_policy_from_namespace(unsigned int ns_id);
+
/*
* used to protect h_table and sha_table
*/
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 56ba0ff..61f8da1 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -274,6 +274,22 @@ static const struct file_operations ima_ascii_measurements_ops = {
.release = seq_release,
};

+static struct dentry *ima_dir;
+static struct dentry *binary_runtime_measurements;
+static struct dentry *ascii_runtime_measurements;
+static struct dentry *runtime_measurements_count;
+static struct dentry *violations;
+static struct dentry *ima_policy_initial_ns;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+static struct dentry *ima_namespaces;
+#endif
+
+enum ima_fs_flags {
+ IMA_FS_BUSY,
+};
+
+static unsigned long ima_fs_flags;
+
#ifdef CONFIG_IMA_PER_NAMESPACE
/* used for namespace policy rules initialization */
static LIST_HEAD(empty_policy);
@@ -348,6 +364,76 @@ static int check_mntns(unsigned int ns_id)

return result;
}
+
+/*
+ * ima_find_namespace_id_from_inode
+ * @policy_inode: the inode of the securityfs policy file for a given
+ * namespace
+ *
+ * Return 0 if the namespace id is not found in ima_ns_policy_mapping
+ */
+static unsigned int find_namespace_id_from_inode(struct inode *policy_inode)
+{
+ unsigned int ns_id = 0;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ struct ima_ns_policy *ins;
+ void **slot;
+ struct radix_tree_iter iter;
+
+ rcu_read_lock();
+ radix_tree_for_each_slot(slot, &ima_ns_policy_mapping, &iter, 0) {
+ ins = radix_tree_deref_slot(slot);
+ if (unlikely(!ins))
+ continue;
+ if (radix_tree_deref_retry(ins)) {
+ slot = radix_tree_iter_retry(&iter);
+ continue;
+ }
+
+ if (ins->policy_dentry && ins->policy_dentry->d_inode == policy_inode) {
+ ns_id = iter.index;
+ break;
+ }
+ }
+ rcu_read_unlock();
+#endif
+
+ return ns_id;
+}
+
+/*
+ * get_namespace_policy_from_inode - Finds namespace mapping from
+ * securityfs policy file
+ * It is called to get the namespace policy reference when a seurityfs
+ * file such as the namespace or policy files are read or written.
+ * @inode: inode of the securityfs policy file under a namespace
+ * folder
+ * Expects the ima_ns_policy_lock already held
+ *
+ * Returns NULL if the namespace policy reference is not reliable once it
+ * probably was already released after a concurrent namespace release.
+ * Otherwise, the namespace policy reference is returned.
+ */
+struct ima_ns_policy *ima_get_namespace_policy_from_inode(struct inode *inode)
+{
+ unsigned int ns_id;
+ struct ima_ns_policy *ins;
+
+ ns_id = find_namespace_id_from_inode(inode);
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ if (ns_id == 0 &&
+ (!ima_policy_initial_ns || inode != ima_policy_initial_ns->d_inode)) {
+ /* ns_id == 0 refers to initial namespace, but inode refers to a
+ * namespaced policy file. It might be a race condition with
+ * namespace release, return invalid reference. */
+ return NULL;
+ }
+#endif
+
+ ins = ima_get_policy_from_namespace(ns_id);
+
+ return ins;
+}
#endif

static ssize_t ima_read_policy(char *path)
@@ -439,22 +525,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
return result;
}

-static struct dentry *ima_dir;
-static struct dentry *binary_runtime_measurements;
-static struct dentry *ascii_runtime_measurements;
-static struct dentry *runtime_measurements_count;
-static struct dentry *violations;
-static struct dentry *ima_policy;
-#ifdef CONFIG_IMA_PER_NAMESPACE
-static struct dentry *ima_namespaces;
-#endif
-
-enum ima_fs_flags {
- IMA_FS_BUSY,
-};
-
-static unsigned long ima_fs_flags;
-
#ifdef CONFIG_IMA_READ_POLICY
static const struct seq_operations ima_policy_seqops = {
.start = ima_policy_start,
@@ -517,7 +587,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)

ima_update_policy();
#ifndef CONFIG_IMA_WRITE_POLICY
- securityfs_remove(ima_policy);
+ securityfs_remove(ima_policy_initial_ns);
ima_policy = NULL;
#endif

@@ -539,6 +609,8 @@ static const struct file_operations ima_measure_policy_ops = {
/*
* Assumes namespace id is in use by some process and this mapping
* does not exist in the map table.
+ * @ns_id namespace id
+ * Expects ima_ns_policy_lock already held
*/
static int create_mnt_ns_directory(unsigned int ns_id)
{
@@ -751,10 +823,10 @@ int __init ima_fs_init(void)
if (IS_ERR(violations))
goto out;

- ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
+ ima_policy_initial_ns = securityfs_create_file("policy", POLICY_FILE_FLAGS,
ima_dir, NULL,
&ima_measure_policy_ops);
- if (IS_ERR(ima_policy))
+ if (IS_ERR(ima_policy_initial_ns))
goto out;

#ifdef CONFIG_IMA_PER_NAMESPACE
@@ -772,7 +844,7 @@ int __init ima_fs_init(void)
securityfs_remove(ascii_runtime_measurements);
securityfs_remove(binary_runtime_measurements);
securityfs_remove(ima_dir);
- securityfs_remove(ima_policy);
+ securityfs_remove(ima_policy_initial_ns);
#ifdef CONFIG_IMA_PER_NAMESPACE
securityfs_remove(ima_namespaces);
#endif
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 2e8c3b7..8c0d4c9 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -20,6 +20,8 @@
#include <linux/rculist.h>
#include <linux/genhd.h>
#include <linux/seq_file.h>
+#include <linux/radix-tree.h>
+#include <linux/proc_ns.h>

#include "ima.h"

@@ -53,6 +55,17 @@ RADIX_TREE(ima_ns_policy_mapping, GFP_ATOMIC);
spinlock_t ima_ns_policy_lock;
#endif

+/* initial namespace map entry, not added to the ima_ns_policy_mapping
+ * Used as policy fallback for namespaces without policy settings */
+struct ima_ns_policy ima_initial_namespace_policy = {
+ .policy_dentry = NULL,
+ .ns_dentry = NULL,
+ .ima_rules = NULL,
+ .ima_policy_rules = LIST_HEAD_INIT(ima_initial_namespace_policy.ima_policy_rules),
+ .ima_policy_flag = 0,
+ .ima_appraise = 0
+ };
+
#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
@@ -191,6 +204,65 @@ static int __init default_appraise_policy_setup(char *str)
__setup("ima_appraise_tcb", default_appraise_policy_setup);

/*
+ * ima_get_policy_from_namespace - Finds the ns_id mapping to namespace
+ * policy structure
+ * @ns_id: mount namespace id to look for in the policy mapping tree
+ *
+ * Returns either the given namespace policy data if mapped or the initial
+ * namespace data instead.
+ *
+ * Note that if a namespace has not a specific policy defined, it will
+ * fall back to the initial namespace policy.
+ */
+struct ima_ns_policy *ima_get_policy_from_namespace(unsigned int ns_id)
+{
+ struct ima_ns_policy *ins;
+
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ rcu_read_lock();
+ ins = radix_tree_lookup(&ima_ns_policy_mapping, ns_id);
+ rcu_read_unlock();
+
+ if (!ins) {
+ ins = &ima_initial_namespace_policy;
+ }
+#else
+ ins = &ima_initial_namespace_policy;
+#endif
+
+ return ins;
+}
+
+/*
+ * ima_get_current_namespace_policy - Finds the namespace policy mapping
+ * for the current task
+ * This function is called on the context of a syscall and then the namespace
+ * in use will not be released during this context.
+ */
+struct ima_ns_policy *ima_get_current_namespace_policy(void)
+{
+ struct ima_ns_policy *ins = NULL;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ struct ns_common *ns;
+
+ ns = mntns_operations.get(current);
+ if (ns) {
+ ins = ima_get_policy_from_namespace(ns->inum);
+ mntns_operations.put(ns);
+ }
+ if (!ins || (ins->ima_rules != &ins->ima_policy_rules)) {
+ /* if current namespace has no IMA policy, get the
+ * initial namespace policy */
+ ins = &ima_initial_namespace_policy;
+ }
+#else
+ ins = &ima_initial_namespace_policy;
+#endif
+
+ return ins;
+}
+
+/*
* The LSM policy can be reloaded, leaving the IMA LSM based rules referring
* to the old, stale LSM policy. Update the IMA LSM based rules to reflect
* the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if
--
2.7.4