[PATCH 6/8] smack: namespace groundwork

Lukasz Pawelczyk l.pawelczyk at samsung.com
Thu May 21 11:53:40 UTC 2015


This commit introduces several changes to Smack to prepare it for
namespace implementation. All the changes are related to namespaces.

Overview of the changes:
- Adds required data structures for mapped labels and functions to
  operate on them.
- Implements the proc interface /proc/$PID/smack_map that can be used for
  remapping of labels for a specific namespace. Also for checking the map.
- Modifies handling of special built-in labels. Detects them on import
  and assigns the same char* pointer regardless whether it's used in a
  normal or a mapped label. This way we can always compare them by ==
  instead of strcmp().
- Adds User namespace hooks implementation

This patch introduces both internal and user-space visible APIs to
handle namespaced labels and Smack namespaces but the behaviour of Smack
should not be changed. The APIs are there, but they have no impact yet.

Signed-off-by: Lukasz Pawelczyk <l.pawelczyk at samsung.com>
---
 fs/proc/base.c                 |  57 ++++++
 include/linux/user_namespace.h |   5 +
 security/smack/Kconfig         |  10 +
 security/smack/Makefile        |   1 +
 security/smack/smack.h         |  42 +++-
 security/smack/smack_access.c  |  46 ++++-
 security/smack/smack_lsm.c     |  76 ++++++++
 security/smack/smack_ns.c      | 432 +++++++++++++++++++++++++++++++++++++++++
 8 files changed, 662 insertions(+), 7 deletions(-)
 create mode 100644 security/smack/smack_ns.c

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 093ca14..22dde1c 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2529,6 +2529,57 @@ static const struct file_operations proc_setgroups_operations = {
 };
 #endif /* CONFIG_USER_NS */
 
+#ifdef CONFIG_SECURITY_SMACK_NS
+static int proc_smack_map_open(struct inode *inode, struct file *file)
+{
+	struct user_namespace *ns = NULL;
+	struct task_struct *task;
+	struct seq_file *seq;
+	int ret = -EINVAL;
+
+	task = get_proc_task(inode);
+	if (task) {
+		rcu_read_lock();
+		ns = get_user_ns(task_cred_xxx(task, user_ns));
+		rcu_read_unlock();
+		put_task_struct(task);
+	}
+	if (!ns)
+		goto err;
+
+	ret = seq_open(file, &proc_smack_map_seq_operations);
+	if (ret)
+		goto err_put_ns;
+
+	seq = file->private_data;
+	seq->private = ns;
+
+	return 0;
+
+err_put_ns:
+	put_user_ns(ns);
+err:
+	return ret;
+}
+
+static int proc_smack_map_release(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq = file->private_data;
+	struct user_namespace *ns = seq->private;
+
+	put_user_ns(ns);
+	return seq_release(inode, file);
+}
+
+static const struct file_operations proc_smack_map_operations = {
+	.open		= proc_smack_map_open,
+	.write		= proc_smack_map_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= proc_smack_map_release,
+};
+#endif /* CONFIG_SECURITY_SMACK_NS */
+
 static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns,
 				struct pid *pid, struct task_struct *task)
 {
@@ -2637,6 +2688,9 @@ static const struct pid_entry tgid_base_stuff[] = {
 	REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
 	REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
 #endif
+#ifdef CONFIG_SECURITY_SMACK_NS
+	REG("smack_map",    S_IRUGO|S_IWUSR, proc_smack_map_operations),
+#endif
 #ifdef CONFIG_CHECKPOINT_RESTORE
 	REG("timers",	  S_IRUGO, proc_timers_operations),
 #endif
@@ -2982,6 +3036,9 @@ static const struct pid_entry tid_base_stuff[] = {
 	REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
 	REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
 #endif
+#ifdef CONFIG_SECURITY_SMACK_NS
+	REG("smack_map",    S_IRUGO|S_IWUSR, proc_smack_map_operations),
+#endif
 };
 
 static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index a9400cc..a8941a5 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -76,6 +76,11 @@ extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t,
 extern ssize_t proc_setgroups_write(struct file *, const char __user *, size_t, loff_t *);
 extern int proc_setgroups_show(struct seq_file *m, void *v);
 extern bool userns_may_setgroups(const struct user_namespace *ns);
+#ifdef CONFIG_SECURITY_SMACK_NS
+extern const struct seq_operations proc_smack_map_seq_operations;
+ssize_t proc_smack_map_write(struct file *file, const char __user *buf,
+			     size_t size, loff_t *ppos);
+#endif /* CONFIG_SECURITY_SMACK_NS */
 #else
 
 static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
diff --git a/security/smack/Kconfig b/security/smack/Kconfig
index 271adae..b19a7fb 100644
--- a/security/smack/Kconfig
+++ b/security/smack/Kconfig
@@ -40,3 +40,13 @@ config SECURITY_SMACK_NETFILTER
 	  This enables security marking of network packets using
 	  Smack labels.
 	  If you are unsure how to answer this question, answer N.
+
+config SECURITY_SMACK_NS
+	bool "Smack namespace"
+	depends on SECURITY_SMACK
+	depends on USER_NS
+	help
+	  This enables Smack namespace that makes it possible to map
+	  specific labels within user namespace (analogously to mapping
+	  UIDs) and to gain MAC capabilities over them.
+	  If you are unsure how to answer this question, answer N.
diff --git a/security/smack/Makefile b/security/smack/Makefile
index ee2ebd5..5faebd7 100644
--- a/security/smack/Makefile
+++ b/security/smack/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_SECURITY_SMACK) := smack.o
 
 smack-y := smack_lsm.o smack_access.o smackfs.o
 smack-$(CONFIG_SECURITY_SMACK_NETFILTER) += smack_netfilter.o
+smack-$(CONFIG_SECURITY_SMACK_NS) += smack_ns.o
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 3818d19..a53623a 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -21,6 +21,7 @@
 #include <linux/list.h>
 #include <linux/rculist.h>
 #include <linux/lsm_audit.h>
+#include <linux/user_namespace.h>
 
 /*
  * Smack labels were limited to 23 characters for a long time.
@@ -59,8 +60,36 @@ struct smack_known {
 	struct netlbl_lsm_secattr	smk_netlabel;	/* on wire labels */
 	struct list_head		smk_rules;	/* access rules */
 	struct mutex			smk_rules_lock;	/* lock for rules */
+#ifdef CONFIG_SECURITY_SMACK_NS
+	struct list_head		smk_mapped;	/* namespaced labels */
+	struct mutex			smk_mapped_lock;
+#endif /* CONFIG_SECURITY_SMACK_NS */
 };
 
+#ifdef CONFIG_SECURITY_SMACK_NS
+
+/*
+ * User namespace security pointer content.
+ */
+struct smack_ns {
+	struct list_head		smk_mapped;	/* namespaced labels */
+	struct mutex			smk_mapped_lock;
+};
+
+/*
+ * A single entry for a namespaced/mapped label.
+ */
+struct smack_known_ns {
+	struct list_head	smk_list_known;
+	struct list_head	smk_list_ns;
+	struct user_namespace	*smk_ns;
+	char			*smk_mapped;
+	struct smack_known	*smk_unmapped;
+	bool			smk_allocated;
+};
+
+#endif /* CONFIG_SECURITY_SMACK_NS */
+
 /*
  * Maximum number of bytes for the levels in a CIPSO IP option.
  * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is
@@ -245,7 +274,7 @@ int smk_tskacc(struct task_struct *, struct smack_known *,
 	       u32, struct smk_audit_info *);
 int smk_curacc(struct smack_known *, u32, struct smk_audit_info *);
 struct smack_known *smack_from_secid(const u32);
-char *smk_parse_smack(const char *string, int len);
+char *smk_parse_smack(const char *string, int len, bool *allocated);
 int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
 struct smack_known *smk_import_entry(const char *, int);
 void smk_insert_entry(struct smack_known *skp);
@@ -254,6 +283,17 @@ char *smk_find_label_name(struct smack_known *skp);
 struct smack_known *smk_get_label(const char *string, int len, bool import);
 
 /*
+ * These functions are in smack_ns.c
+ */
+#ifdef CONFIG_SECURITY_SMACK_NS
+struct user_namespace *smk_find_mapped_ns(struct user_namespace *ns);
+struct smack_known_ns *smk_find_mapped(struct smack_known *skp,
+				       struct user_namespace *ns);
+struct smack_known *smk_find_unmapped(const char *string, int len,
+				      struct user_namespace *ns);
+#endif /* CONFIG_SECURITY_SMACK_NS */
+
+/*
  * Shared data.
  */
 extern int smack_enabled;
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 47a9c92..c4d90d2 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -452,13 +452,16 @@ struct smack_known *smk_find_entry(const char *string)
 /**
  * smk_parse_smack - parse smack label from a text string
  * @string: a text string that might contain a Smack label
- * @len: the maximum size, or zero if it is NULL terminated.
+ * @len: the maximum size, or zero if it is NULL terminated
+ * @allocated: (out) indicates whether the return string has been
+ *             allocated and has to be freed with kfree() later
+ *             (built-in labels returned are not allocated)
  *
  * Returns a pointer to the clean label or an error code.
  */
-char *smk_parse_smack(const char *string, int len)
+char *smk_parse_smack(const char *string, int len, bool *allocated)
 {
-	char *smack;
+	char *smack = NULL;
 	int i;
 
 	if (len <= 0)
@@ -480,11 +483,33 @@ char *smk_parse_smack(const char *string, int len)
 	if (i == 0 || i >= SMK_LONGLABEL)
 		return ERR_PTR(-EINVAL);
 
+	/*
+	 * Look for special labels. This way we guarantee that we can compare
+	 * special labels in mapped entries by ==, without strcmp().
+	 */
+	if (len == 1 && !strcmp(string, smack_known_huh.smk_known))
+		smack = smack_known_huh.smk_known;
+	else if (len == 1 && !strcmp(string, smack_known_hat.smk_known))
+		smack = smack_known_hat.smk_known;
+	else if (len == 1 && !strcmp(string, smack_known_star.smk_known))
+		smack = smack_known_star.smk_known;
+	else if (len == 1 && !strcmp(string, smack_known_floor.smk_known))
+		smack = smack_known_floor.smk_known;
+	else if (len == 1 && !strcmp(string, smack_known_web.smk_known))
+		smack = smack_known_web.smk_known;
+
+	if (smack) {
+		*allocated = false;
+
+		return smack;
+	}
+
 	smack = kzalloc(i + 1, GFP_KERNEL);
 	if (smack == NULL)
 		return ERR_PTR(-ENOMEM);
 
 	strncpy(smack, string, i);
+	*allocated = true;
 
 	return smack;
 }
@@ -540,8 +565,9 @@ struct smack_known *smk_import_entry(const char *string, int len)
 	char *smack;
 	int slen;
 	int rc;
+	bool allocated;
 
-	smack = smk_parse_smack(string, len);
+	smack = smk_parse_smack(string, len, &allocated);
 	if (IS_ERR(smack))
 		return ERR_CAST(smack);
 
@@ -577,6 +603,10 @@ struct smack_known *smk_import_entry(const char *string, int len)
 	if (rc >= 0) {
 		INIT_LIST_HEAD(&skp->smk_rules);
 		mutex_init(&skp->smk_rules_lock);
+#ifdef CONFIG_SECURITY_SMACK_NS
+		INIT_LIST_HEAD(&skp->smk_mapped);
+		mutex_init(&skp->smk_mapped_lock);
+#endif /* CONFIG_SECURITY_SMACK_NS */
 		/*
 		 * Make sure that the entry is actually
 		 * filled before putting it on the list.
@@ -590,7 +620,8 @@ struct smack_known *smk_import_entry(const char *string, int len)
 	kfree(skp);
 	skp = ERR_PTR(rc);
 freeout:
-	kfree(smack);
+	if (allocated)
+		kfree(smack);
 unlockout:
 	mutex_unlock(&smack_known_lock);
 
@@ -649,16 +680,19 @@ char *smk_find_label_name(struct smack_known *skp)
 struct smack_known *smk_get_label(const char *string, int len, bool import)
 {
 	struct smack_known *skp;
+	bool allocated;
 	char *cp;
 
 	if (import) {
 		skp = smk_import_entry(string, len);
 	} else {
-		cp = smk_parse_smack(string, len);
+		cp = smk_parse_smack(string, len, &allocated);
 		if (IS_ERR(cp))
 			return ERR_CAST(cp);
 
 		skp = smk_find_entry(cp);
+		if (allocated)
+			kfree(cp);
 	}
 
 	return skp;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index bb74ca9..4ae9a9a 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -41,6 +41,7 @@
 #include <linux/msg.h>
 #include <linux/shm.h>
 #include <linux/binfmts.h>
+#include <linux/user_namespace.h>
 #include "smack.h"
 
 #define TRANS_TRUE	"TRUE"
@@ -4165,6 +4166,53 @@ static void smack_audit_rule_free(void *vrule)
 
 #endif /* CONFIG_AUDIT */
 
+#ifdef CONFIG_SECURITY_SMACK_NS
+
+static inline int smack_userns_create(struct user_namespace *ns)
+{
+	struct smack_ns *snsp;
+
+	snsp = kzalloc(sizeof(*snsp), GFP_KERNEL);
+	if (snsp == NULL)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&snsp->smk_mapped);
+	mutex_init(&snsp->smk_mapped_lock);
+
+	ns->security = snsp;
+	return 0;
+}
+
+static inline void smack_userns_free(struct user_namespace *ns)
+{
+	struct smack_ns *snsp = ns->security;
+	struct smack_known *skp;
+	struct smack_known_ns *sknp, *n;
+
+	list_for_each_entry_safe(sknp, n, &snsp->smk_mapped, smk_list_ns) {
+		skp = sknp->smk_unmapped;
+
+		mutex_lock(&skp->smk_mapped_lock);
+		list_del_rcu(&sknp->smk_list_known);
+		if (sknp->smk_allocated)
+			kfree(sknp->smk_mapped);
+		kfree(sknp);
+		mutex_unlock(&skp->smk_mapped_lock);
+
+		list_del(&sknp->smk_list_ns);
+	}
+
+	kfree(snsp);
+}
+
+static inline int smack_userns_setns(struct nsproxy *nsproxy,
+				     struct user_namespace *ns)
+{
+	return 0;
+}
+
+#endif /* CONFIG_SECURITY_SMACK_NS */
+
 /**
  * smack_ismaclabel - check if xattr @name references a smack MAC label
  * @name: Full xattr name to check.
@@ -4376,6 +4424,13 @@ struct security_hook_list smack_hooks[] = {
 	LSM_HOOK_INIT(audit_rule_free, smack_audit_rule_free),
 #endif /* CONFIG_AUDIT */
 
+ /* Namespace hooks */
+#ifdef CONFIG_SECURITY_SMACK_NS
+	LSM_HOOK_INIT(userns_create, smack_userns_create),
+	LSM_HOOK_INIT(userns_free, smack_userns_free),
+	LSM_HOOK_INIT(userns_setns, smack_userns_setns),
+#endif /* CONFIG_SECURITY_SMACK_NS */
+
 	LSM_HOOK_INIT(ismaclabel, smack_ismaclabel),
 	LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx),
 	LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid),
@@ -4388,6 +4443,27 @@ struct security_hook_list smack_hooks[] = {
 
 static __init void init_smack_known_list(void)
 {
+#ifdef CONFIG_SECURITY_SMACK_NS
+	/*
+	 * Initialize mapped list locks
+	 */
+	mutex_init(&smack_known_huh.smk_mapped_lock);
+	mutex_init(&smack_known_hat.smk_mapped_lock);
+	mutex_init(&smack_known_floor.smk_mapped_lock);
+	mutex_init(&smack_known_star.smk_mapped_lock);
+	mutex_init(&smack_known_invalid.smk_mapped_lock);
+	mutex_init(&smack_known_web.smk_mapped_lock);
+	/*
+	 * Initialize mapped lists
+	 */
+	INIT_LIST_HEAD(&smack_known_huh.smk_mapped);
+	INIT_LIST_HEAD(&smack_known_hat.smk_mapped);
+	INIT_LIST_HEAD(&smack_known_star.smk_mapped);
+	INIT_LIST_HEAD(&smack_known_floor.smk_mapped);
+	INIT_LIST_HEAD(&smack_known_invalid.smk_mapped);
+	INIT_LIST_HEAD(&smack_known_web.smk_mapped);
+#endif /* CONFIG_SECURITY_SMACK_NS */
+
 	/*
 	 * Initialize rule list locks
 	 */
diff --git a/security/smack/smack_ns.c b/security/smack/smack_ns.c
new file mode 100644
index 0000000..141a836
--- /dev/null
+++ b/security/smack/smack_ns.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics.
+ *
+ * Smack namespaces
+ *
+ * Author(s):
+ *    Lukasz Pawelczyk <l.pawelczyk at samsung.com>
+ *
+ * This program is free software, you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/file.h>
+#include <linux/ctype.h>
+#include <linux/rculist.h>
+#include <linux/seq_file.h>
+#include <linux/user_namespace.h>
+#include "smack.h"
+
+/**
+ * smk_find_mapped_ns - Finds a first namespace from this one through
+ * its parrents that has a map. This map is the effective map in this
+ * namespace.
+ * @ns: a user namespace for which we search for a mapped ns
+ *
+ * Returns a namespace that has a non-NULL map, or NULL if there is
+ * no mapped namespace.
+ *
+ * Can be effectively used to answer a question: "is there a Smack
+ * map for this namespace?"
+ */
+struct user_namespace *smk_find_mapped_ns(struct user_namespace *ns)
+{
+	struct user_namespace *user_ns = ns;
+
+	do {
+		struct smack_ns *sns = user_ns->security;
+
+		if (sns && !list_empty(&sns->smk_mapped))
+			break;
+
+		user_ns = user_ns->parent;
+	} while (user_ns);
+
+	return user_ns;
+}
+
+/**
+ * __smk_find_mapped - an internal version of smk_find_mapped
+ *                     that doesn't use smk_find_mapped_ns, but
+ *                     operates directly on the passed one.
+ */
+static struct smack_known_ns *__smk_find_mapped(struct smack_known *skp,
+						struct user_namespace *ns)
+{
+	struct smack_known_ns *sknp;
+
+	if (ns == NULL)
+		return NULL;
+
+	list_for_each_entry_rcu(sknp, &skp->smk_mapped, smk_list_known)
+		if (sknp->smk_ns == ns)
+			return sknp;
+
+	return NULL;
+}
+
+/**
+ * smk_find_mapped - Finds a mapped label on the smack_known's mapped list
+ * @skp: a label which mapped label we look for
+ * @ns: a user namespace the label we search for is assigned to
+ *
+ * Returns a pointer to the mapped label if one exists that is
+ * assigned to the specified user namespace or NULL if not found.
+ */
+struct smack_known_ns *smk_find_mapped(struct smack_known *skp,
+				       struct user_namespace *ns)
+{
+	struct user_namespace *user_ns = smk_find_mapped_ns(ns);
+
+	return __smk_find_mapped(skp, user_ns);
+}
+
+/**
+ * __smk_find_unmapped - an internal version of smk_find_unmapped
+ *                       that doesn't use smk_find_mapped_ns, but
+ *                       operates directly on the passed one.
+ */
+static struct smack_known *__smk_find_unmapped(const char *string, int len,
+					       struct user_namespace *ns)
+{
+	struct smack_ns *snsp;
+	struct smack_known *skp = NULL;
+	struct smack_known_ns *sknp;
+	char *smack;
+	bool allocated = false;
+
+	if (ns == NULL)
+		return NULL;
+
+	snsp = ns->security;
+
+	smack = smk_parse_smack(string, len, &allocated);
+	if (IS_ERR(smack))
+		return ERR_CAST(smack);
+
+	list_for_each_entry_rcu(sknp, &snsp->smk_mapped, smk_list_ns) {
+		if (strcmp(smack, sknp->smk_mapped) == 0) {
+			skp = sknp->smk_unmapped;
+			break;
+		}
+	}
+
+	if (allocated)
+		kfree(smack);
+	return skp;
+}
+
+/**
+ * smk_find_unmapped - Finds an original label by a mapped label string
+ *                     and the namespace it could be mapped in
+ * @string: a name of a mapped label we look for
+ * @len: the string size, or zero if it is NULL terminated.
+ * @ns: a namespace the looked for label should be mapped in
+ *
+ * Returns a smack_known label that is mapped as 'string' in 'ns',
+ * NULL if not found or an error code.
+ */
+struct smack_known *smk_find_unmapped(const char *string, int len,
+				      struct user_namespace *ns)
+{
+	struct user_namespace *user_ns = smk_find_mapped_ns(ns);
+
+	return __smk_find_unmapped(string, len, user_ns);
+}
+
+/**
+ * smk_import_mapped - Imports a mapped label effectively creating a mapping.
+ * @skp: a label we map
+ * @ns: a user namespace this label will be mapped in
+ * @string: a text string of the mapped label
+ * @len: the maximum size, or zero if it is NULL terminanted
+ *
+ * Returns a pointer to the mapped label entry or an error code.
+ *
+ * The mapped label will be added to 2 lists:
+ *   - a list of mapped labels of skp
+ *   - a list of labels mapped in ns
+ */
+static struct smack_known_ns *smk_import_mapped(struct smack_known *skp,
+						struct user_namespace *ns,
+						const char *string, int len)
+{
+	struct smack_ns *snsp = ns->security;
+	struct smack_known_ns *sknp;
+	char *mapped;
+	bool allocated;
+
+	/* Mapping init_user_ns is against the design and pointless */
+	if (ns == &init_user_ns)
+		return ERR_PTR(-EBADR);
+
+	mapped = smk_parse_smack(string, len, &allocated);
+	if (IS_ERR(mapped))
+		return ERR_CAST(mapped);
+
+	mutex_lock(&skp->smk_mapped_lock);
+
+	/*
+	 * Don't allow one<->many mappings in namespace, rename.
+	 * This code won't get triggered for now as trying to assign
+	 * a duplicate is forbidden in proc_smack_map_write().
+	 * Leaving this as this function might be also used elsewhere.
+	 */
+	sknp = smk_find_mapped(skp, ns);
+	if (sknp != NULL) {
+		if (sknp->smk_allocated)
+			kfree(sknp->smk_mapped);
+		sknp->smk_mapped = mapped;
+		sknp->smk_allocated = allocated;
+		goto unlockout;
+	}
+
+	sknp = kzalloc(sizeof(*sknp), GFP_KERNEL);
+	if (sknp == NULL) {
+		sknp = ERR_PTR(-ENOMEM);
+		if (allocated)
+			kfree(mapped);
+		goto unlockout;
+	}
+
+	sknp->smk_ns = ns;
+	sknp->smk_mapped = mapped;
+	sknp->smk_allocated = allocated;
+	sknp->smk_unmapped = skp;
+	list_add_rcu(&sknp->smk_list_known, &skp->smk_mapped);
+
+	mutex_lock(&snsp->smk_mapped_lock);
+	list_add_rcu(&sknp->smk_list_ns, &snsp->smk_mapped);
+	mutex_unlock(&snsp->smk_mapped_lock);
+
+unlockout:
+	mutex_unlock(&skp->smk_mapped_lock);
+
+	return sknp;
+}
+
+static void *proc_smack_map_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct smack_known *skp;
+	struct user_namespace *ns = seq->private;
+	loff_t counter = *pos;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(skp, &smack_known_list, list)
+		if (smk_find_mapped(skp, ns) && counter-- == 0)
+			return skp;
+
+	return NULL;
+}
+
+static void proc_smack_map_seq_stop(struct seq_file *seq, void *v)
+{
+	rcu_read_unlock();
+}
+
+static void *proc_smack_map_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct smack_known *skp = v;
+	struct user_namespace *ns = seq->private;
+
+	list_for_each_entry_continue_rcu(skp, &smack_known_list, list) {
+		if (smk_find_mapped(skp, ns)) {
+			(*pos)++;
+			return skp;
+		}
+	}
+
+	return NULL;
+}
+
+static int proc_smack_map_seq_show(struct seq_file *seq, void *v)
+{
+	struct smack_known *skp = v;
+	struct user_namespace *ns = seq->private;
+	struct smack_known_ns *sknp;
+
+	/*
+	 * QUESTION: linux-api
+	 * What to print when in init_map_ns where the map is empty
+	 * effectively meaning identity? Unfortunately it's impossible
+	 * to show identity in this syntax without printing all the labels.
+	 */
+	if (smk_find_mapped_ns(ns) == NULL) {
+		seq_puts("This namespace is not mapped.\n");
+	} else {
+		sknp = smk_find_mapped(skp, ns);
+		if (sknp)
+			seq_printf(seq, "%s -> %s\n",
+				   skp->smk_known, sknp->smk_mapped);
+	}
+
+	return 0;
+}
+
+const struct seq_operations proc_smack_map_seq_operations = {
+	.start = proc_smack_map_seq_start,
+	.stop = proc_smack_map_seq_stop,
+	.next = proc_smack_map_seq_next,
+	.show = proc_smack_map_seq_show,
+};
+
+static DEFINE_MUTEX(smk_map_mutex);
+
+static bool mapping_permitted(const struct file *file,
+			      struct user_namespace *user_ns)
+{
+	/*
+	 * Do not allow mapping own label. This is in contrast to user
+	 * namespace where you can always map your own UID. In Smack having
+	 * administrative privileges over your own label (which Smack
+	 * namespace effectively gives you) is not equivalent to user
+	 * namespace. E.g. things like setting exec/transmute labels that
+	 * otherwise would be denied. Hence no own_label param here.
+	 */
+
+	/*
+	 * Adjusting namespace settings requires capabilities on the target.
+	 */
+	if (!file_ns_capable(file, user_ns, CAP_MAC_ADMIN))
+		return false;
+
+	/*
+	 * And it requires capabilities in the parent.
+	 *
+	 * If the Smack namespace was properly hierarchical the user_ns to
+	 * check against could be 'user_ns->parent'. Right now because of
+	 * security concerns only privileged initial namespace is allowed
+	 * to fill the map. For a hierarchical namespaces one would
+	 * implement mapping (in the child namespaces) of only mapped labels
+	 * (in parent namespace) and change '&init_user_ns' to
+	 * 'user_ns->parent'. This will be added in the future.
+	 */
+	if (smack_ns_privileged(&init_user_ns, CAP_MAC_ADMIN) &&
+	    file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
+		return true;
+
+	return false;
+}
+
+ssize_t proc_smack_map_write(struct file *file, const char __user *buf,
+			     size_t size, loff_t *ppos)
+{
+	struct seq_file *seq = file->private_data;
+	struct user_namespace *ns = seq->private;
+	struct user_namespace *seq_ns = seq_user_ns(seq);
+	struct smack_known *skp;
+	struct smack_known_ns *sknp;
+	unsigned long page = 0;
+	char *kbuf, *pos, *next_line, *tok[2];
+	ssize_t ret;
+	int i;
+
+	/* Mapping labels for the init ns makes no sense */
+	if (ns == &init_user_ns)
+		return -EBADR;
+
+	if ((seq_ns != ns) && (seq_ns != ns->parent))
+		return -EPERM;
+
+	mutex_lock(&smk_map_mutex);
+
+	ret = -EPERM;
+	if (!mapping_permitted(file, ns))
+		goto out;
+
+	/* Get a buffer */
+	ret = ENOMEM;
+	page = __get_free_page(GFP_TEMPORARY);
+	if (!page)
+		goto out;
+	kbuf = (char *)page;
+
+	/* Only allow <= page size writes at the beginning of the file */
+	ret = -EINVAL;
+	if ((*ppos != 0) || (size >= PAGE_SIZE))
+		goto out;
+
+	/* Slurp in the user data */
+	ret = -EFAULT;
+	if (copy_from_user(kbuf, buf, size))
+		goto out;
+	kbuf[size] = '\0';
+
+	/* Parse the user data */
+	pos = kbuf;
+
+	for (; pos; pos = next_line) {
+		ret = -EINVAL;
+
+		/* Find the end of line and ensure I don't look past it */
+		next_line = strchr(pos, '\n');
+		if (next_line) {
+			*next_line = '\0';
+			next_line++;
+			if (*next_line == '\0')
+				next_line = NULL;
+		}
+
+		/* Find tokens in line */
+		for (i = 0; i < 2; ++i) {
+			while (isspace(*pos))
+				*(pos++) = '\0';
+
+			/* unexpected end of file */
+			if (*pos == '\0')
+				goto out;
+
+			tok[i] = pos;
+
+			/* find the end of the token */
+			while (*pos != '\0' && !isspace(*pos))
+				++pos;
+		}
+
+		/* NUL terminate the last token if not EOL */
+		while (isspace(*pos))
+			*(pos++) = '\0';
+
+		/* there should not be any trailing data */
+		if (*pos != '\0')
+			goto out;
+
+		/* do not allow to map 2 different labels to one name */
+		skp = __smk_find_unmapped(tok[1], 0, ns);
+		if (IS_ERR(skp)) {
+			ret = PTR_ERR(skp);
+			goto out;
+		}
+		if (skp != NULL) {
+			ret = -EEXIST;
+			goto out;
+		}
+
+		skp = smk_import_entry(tok[0], 0);
+		if (IS_ERR(skp)) {
+			ret = PTR_ERR(skp);
+			goto out;
+		}
+
+		/* do not allow remapping */
+		ret = -EEXIST;
+		if (__smk_find_mapped(skp, ns))
+			goto out;
+
+		sknp = smk_import_mapped(skp, ns, tok[1], 0);
+		if (IS_ERR(sknp)) {
+			ret = PTR_ERR(sknp);
+			goto out;
+		}
+	}
+
+	ret = size;
+
+out:
+	mutex_unlock(&smk_map_mutex);
+	if (page)
+		free_page(page);
+
+	return ret;
+}
-- 
2.1.0



More information about the Containers mailing list