From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965994AbbJ2WwK (ORCPT ); Thu, 29 Oct 2015 18:52:10 -0400 Received: from smtp103.biz.mail.bf1.yahoo.com ([98.139.221.62]:32098 "EHLO smtp103.biz.mail.bf1.yahoo.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965936AbbJ2WwE (ORCPT ); Thu, 29 Oct 2015 18:52:04 -0400 X-Yahoo-Newman-Property: ymail-3 X-YMail-OSG: 1nEdu.kVM1lpKjjoqM5dKJsBhlSb_J_kd_sRqrRKirN200x 6_DbV19rLE2TTsn7Fsl6gOu2eCMZUK7fcHVDEtV6_d05dsrSlA8TEnM7lkN4 d_cAxjvep9Dd2LPykIPtz3dQRQV2FNVyTkrfCVPwGiiUPmvbtobObcN16O_I b9221yzP3QNh.ek9_2KNaFOXrTWE14O1RQSmdWEqwizlFI8jj13WFj.116oL OSCZnPvK6zlNnXjr1IkIuDxxVcgyLaCSeCxPMOifAHXiC7ahOnlrelS6T0FP wVUOWAwoO6Wewr83vK0dYKBO6mHbuIbFgA0NUHndWFNC6z5Z9UdF.PBthGh2 nxDURadFwnqRG0gQrK11AU3C7F2ToXi8Vuyntq_XYCACcxfUemNZGAT8aJfN cEVdmnH2Y5t2kjpQnM5IiZnSKQaZpJTXs99r0ybTZ6qjl9.csthAPkd02FPo gQx.DQ6Z8N6W9H3TMwNxL.knMRLRO3726O8KzNSfTtRP4F7TBOPvicsp5rNi 5brA4NxQvKo96wo_6q6la7jXhAg-- X-Yahoo-SMTP: OIJXglSswBDfgLtXluJ6wiAYv6_cnw-- Subject: Re: [PATCH v4 09/11] smack: namespace groundwork To: Lukasz Pawelczyk , "David S. Miller" , "Eric W. Biederman" , "Serge E. Hallyn" , Al Viro , Alexey Dobriyan , Andrew Morton , Andy Lutomirski , Calvin Owens , David Howells , Eric Dumazet , Eric Paris , Greg Kroah-Hartman , James Morris , Jann Horn , Jiri Slaby , Joe Perches , John Johansen , Jonathan Corbet , Kees Cook , Mauro Carvalho Chehab , NeilBrown , Paul Moore , Serge Hallyn , Stephen Smalley , Tejun Heo , Tetsuo Handa , containers@lists.linuxfoundation.org, linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, selinux@tycho.nsa.gov References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-10-git-send-email-l.pawelczyk@samsung.com> Cc: Lukasz Pawelczyk From: Casey Schaufler Message-ID: <5632A30B.9030703@schaufler-ca.com> Date: Thu, 29 Oct 2015 15:51:55 -0700 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.3.0 MIME-Version: 1.0 In-Reply-To: <1444826525-9758-10-git-send-email-l.pawelczyk@samsung.com> Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 10/14/2015 5:42 AM, Lukasz Pawelczyk wrote: > 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/attr/label_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 > Reviewed-by: Casey Schaufler Acked-by: Casey Schaufler > --- > security/smack/Kconfig | 10 ++ > security/smack/Makefile | 1 + > security/smack/smack.h | 45 ++++- > security/smack/smack_access.c | 47 ++++- > security/smack/smack_lsm.c | 134 +++++++++++++- > security/smack/smack_ns.c | 404 ++++++++++++++++++++++++++++++++++++++++++ > 6 files changed, 626 insertions(+), 15 deletions(-) > create mode 100644 security/smack/smack_ns.c > > 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 98bb676..4b7489f 100644 > --- a/security/smack/smack.h > +++ b/security/smack/smack.h > @@ -24,6 +24,7 @@ > #include > #include > #include > +#include > > /* > * Use IPv6 port labeling if IPv6 is enabled and secmarks > @@ -74,8 +75,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 > @@ -295,7 +324,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); > @@ -310,6 +339,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; > diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c > index 750aa9c..17b7e2c 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); > > @@ -748,17 +779,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; > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index 5a59836..206e0ce 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -42,6 +42,7 @@ > #include > #include > #include > +#include > #include "smack.h" > > #define TRANS_TRUE "TRUE" > @@ -3496,6 +3497,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 > @@ -3527,9 +3549,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 > * > @@ -3538,8 +3559,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; > @@ -3558,9 +3578,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); > @@ -3583,6 +3600,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 > @@ -4434,6 +4478,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. > @@ -4610,6 +4701,7 @@ static 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), > > @@ -4647,6 +4739,13 @@ static 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), > @@ -4659,6 +4758,27 @@ static 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..49223c4 > --- /dev/null > +++ b/security/smack/smack_ns.c > @@ -0,0 +1,404 @@ > +/* > + * Copyright (C) 2014 Samsung Electronics. > + * > + * Smack namespaces > + * > + * Author(s): > + * Lukasz Pawelczyk > + * > + * 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 > +#include > +#include > +#include > +#include > +#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; > +}