@@ -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.
@@ -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
@@ -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
@@ -268,7 +297,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);
@@ -283,6 +312,20 @@ 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);
+extern const struct seq_operations proc_label_map_seq_operations;
+ssize_t proc_label_map_write(struct task_struct *p, const struct cred *f_cred,
+ void *value, size_t size);
+#endif /* CONFIG_SECURITY_SMACK_NS */
+
+/*
* Shared data.
*/
extern int smack_enabled;
@@ -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);
@@ -742,17 +773,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);
- kfree(cp);
+ if (allocated)
+ kfree(cp);
}
return skp;
@@ -42,6 +42,7 @@
#include <linux/shm.h>
#include <linux/binfmts.h>
#include <linux/parser.h>
+#include <linux/user_namespace.h>
#include "smack.h"
#define TRANS_TRUE "TRUE"
@@ -3397,6 +3398,27 @@ unlockandout:
}
/**
+ * smack_getprocattr_seq - Smack process attribute access through seq
+ * @p: the object task
+ * @name: the name of the attribute in /proc/.../attr/
+ * @ops: out, seq_operations to handle @name
+ *
+ * Returns 0 if @name is to be handled by seq, error otherwise.
+ */
+int smack_getprocattr_seq(struct task_struct *p, const char *name,
+ const struct seq_operations **ops)
+{
+#ifdef CONFIG_SECURITY_SMACK_NS
+ if (strcmp(name, "label_map") == 0) {
+ *ops = &proc_label_map_seq_operations;
+ return 0;
+ }
+#endif
+
+ return -EOPNOTSUPP;
+}
+
+/**
* smack_getprocattr - Smack process attribute access
* @p: the object task
* @name: the name of the attribute in /proc/.../attr
@@ -3428,9 +3450,8 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
}
/**
- * smack_setprocattr - Smack process attribute setting
+ * proc_current_write - Smack "current" process attribute setting
* @p: the object task
- * @name: the name of the attribute in /proc/.../attr
* @value: the value to set
* @size: the size of the value
*
@@ -3439,8 +3460,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
*
* Returns the length of the smack label or an error code
*/
-static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred,
- char *name, void *value, size_t size)
+static int proc_current_write(struct task_struct *p, void *value, size_t size)
{
struct task_smack *tsp;
struct cred *new;
@@ -3459,9 +3479,6 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred,
if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
return -EINVAL;
- if (strcmp(name, "current") != 0)
- return -EINVAL;
-
skp = smk_get_label(value, size, true);
if (IS_ERR(skp))
return PTR_ERR(skp);
@@ -3484,6 +3501,33 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred,
}
/**
+ * smack_setprocattr - Smack process attribute setting
+ * @p: the object task
+ * @cred: the credentials of the file's opener
+ * @name: the name of the attribute in /proc/.../attr
+ * @value: the value to set
+ * @size: the size of the value
+ *
+ * Sets the proc attribute
+ *
+ * Returns the length of the written data or an error code
+ */
+static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred,
+ char *name, void *value, size_t size)
+{
+#ifdef CONFIG_SECURITY_SMACK_NS
+ if (strcmp(name, "label_map") == 0)
+ return proc_label_map_write(p, f_cred, value, size);
+#endif
+
+ if (strcmp(name, "current") == 0)
+ return proc_current_write(p, value, size);
+
+ return -EINVAL;
+
+}
+
+/**
* smack_unix_stream_connect - Smack access on UDS
* @sock: one sock
* @other: the other sock
@@ -4324,6 +4368,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.
@@ -4500,6 +4591,7 @@ struct security_hook_list smack_hooks[] = {
LSM_HOOK_INIT(d_instantiate, smack_d_instantiate),
+ LSM_HOOK_INIT(getprocattr_seq, smack_getprocattr_seq),
LSM_HOOK_INIT(getprocattr, smack_getprocattr),
LSM_HOOK_INIT(setprocattr, smack_setprocattr),
@@ -4537,6 +4629,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),
@@ -4549,6 +4648,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
*/
new file mode 100644
@@ -0,0 +1,404 @@
+/*
+ * 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_label_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_label_map_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ struct smack_known *skp;
+ struct task_struct *task = seq->private;
+ struct user_namespace *ns = ns_of_task_struct(task);
+ 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_label_map_seq_stop(struct seq_file *seq, void *v)
+{
+ rcu_read_unlock();
+}
+
+static void *proc_label_map_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct smack_known *skp = v;
+ struct task_struct *task = seq->private;
+ struct user_namespace *ns = ns_of_task_struct(task);
+
+ 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_label_map_seq_show(struct seq_file *seq, void *v)
+{
+ struct smack_known *skp = v;
+ struct task_struct *task = seq->private;
+ struct user_namespace *ns = ns_of_task_struct(task);
+ struct smack_known_ns *sknp;
+
+ 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_label_map_seq_operations = {
+ .start = proc_label_map_seq_start,
+ .stop = proc_label_map_seq_stop,
+ .next = proc_label_map_seq_next,
+ .show = proc_label_map_seq_show,
+};
+
+static DEFINE_MUTEX(smk_map_mutex);
+
+static bool mapping_permitted(const struct cred *f_cred,
+ 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 would effectively give 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 (security_capable(f_cred, user_ns, CAP_MAC_ADMIN) != 0)
+ 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) &&
+ security_capable(f_cred, &init_user_ns, CAP_MAC_ADMIN) == 0)
+ return true;
+
+ return false;
+}
+
+ssize_t proc_label_map_write(struct task_struct *p, const struct cred *f_cred,
+ void *value, size_t size)
+{
+ struct user_namespace *ns = ns_of_task_struct(p);
+ struct user_namespace *cur_ns = ns_of_current();
+ struct smack_known *skp;
+ struct smack_known_ns *sknp;
+ char *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 (cur_ns != ns->parent)
+ return -EPERM;
+
+ if (!mapping_permitted(f_cred, ns))
+ return -EPERM;
+
+ if (value == NULL || size == 0 || size >= PAGE_SIZE)
+ return -EINVAL;
+
+ mutex_lock(&smk_map_mutex);
+
+ /* Parse the user data */
+ pos = value;
+ pos[size] = '\0';
+
+ 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;
+
+ ret = -EEXIST;
+
+ /* 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)
+ goto out;
+
+ skp = smk_import_entry(tok[0], 0);
+ if (IS_ERR(skp)) {
+ ret = PTR_ERR(skp);
+ goto out;
+ }
+
+ /* do not allow remapping */
+ 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);
+
+ return ret;
+}