[PATCH v4 09/11] smack: namespace groundwork

Casey Schaufler casey at schaufler-ca.com
Thu Oct 29 22:51:55 UTC 2015


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 <l.pawelczyk at samsung.com>
> Reviewed-by: Casey Schaufler <casey at schaufler-ca.com>

Acked-by: Casey Schaufler <casey at schaufler-ca.com>


> ---
>  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 <linux/list.h>
>  #include <linux/rculist.h>
>  #include <linux/lsm_audit.h>
> +#include <linux/user_namespace.h>
>  
>  /*
>   * 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 <linux/shm.h>
>  #include <linux/binfmts.h>
>  #include <linux/parser.h>
> +#include <linux/user_namespace.h>
>  #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 <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_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;
> +}



More information about the Containers mailing list