[PATCH v2 5/7] smack: namespace groundwork

Lukasz Pawelczyk
2015-05-25T12:33:47Z

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@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@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