All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] namespaces: add transparent user namespaces
@ 2016-06-23 11:11 Jann Horn
  2016-06-23 18:50 ` Eric W. Biederman
  0 siblings, 1 reply; 14+ messages in thread
From: Jann Horn @ 2016-06-23 11:11 UTC (permalink / raw)
  To: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Eric W. Biederman,
	Andy Lutomirski, linux-kernel
  Cc: Jann Horn

This allows the admin of a user namespace to mark the namespace as
transparent. All other namespaces, by default, are opaque.

While the current behavior of user namespaces is appropriate for use in
containers, there are many programs that only use user namespaces because
doing so enables them to do other things (e.g. unsharing the mount or
network namespace) that require namespaced capabilities. For them, the
inability to see the real UIDs and GIDs of things from inside the user
namespace can be very annoying.

In a transparent namespace, all UIDs and GIDs that are mapped into its
first opaque ancestor are visible and are not remapped. This means that if
a process e.g. stat()s the real root directory in a namespace, it will
still see it as owned by UID 0.

Traditionally, any UID or GID that was visible in a user namespace was also
mapped into the namespace, giving the namespace admin full access to it.
This patch introduces a distinction: In a transparent namespace, UIDs and
GIDs can be visible without being mapped. Non-mapped, visible UIDs can be
passed from the kernel to userspace, but userspace can't send them back to
the kernel. In order to be able to fully use specific UIDs/GIDs and gain
privileges over them, mappings need to be set up in the usual way -
however, to avoid aliasing problems, only identity mappings are permitted.

I have gone through all callers of from_kuid() and from_kgid(), and as far
as I can tell, kuid_has_mapping() and kgid_has_mapping() were the only
functions that used them for privilege checks. (The keys subsystem uses
them in an insecure way, and that issue has been known for a while, but my
patch doesn't make that any more vulnerable than it already is.)

Signed-off-by: Jann Horn <jannh@google.com>
---
 fs/proc/base.c                 |  28 ++++++--
 include/linux/uidgid.h         |  16 ++++-
 include/linux/user_namespace.h |   4 ++
 kernel/user.c                  |   1 +
 kernel/user_namespace.c        | 152 +++++++++++++++++++++++++++++++++++++++--
 5 files changed, 191 insertions(+), 10 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index a11eb71..c521c51 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2744,7 +2744,8 @@ static const struct file_operations proc_projid_map_operations = {
 	.release	= proc_id_map_release,
 };
 
-static int proc_setgroups_open(struct inode *inode, struct file *file)
+static int proc_nsadmin_open(struct inode *inode, struct file *file,
+	int (*show)(struct seq_file *, void *))
 {
 	struct user_namespace *ns = NULL;
 	struct task_struct *task;
@@ -2767,7 +2768,7 @@ static int proc_setgroups_open(struct inode *inode, struct file *file)
 			goto err_put_ns;
 	}
 
-	ret = single_open(file, &proc_setgroups_show, ns);
+	ret = single_open(file, show, ns);
 	if (ret)
 		goto err_put_ns;
 
@@ -2778,7 +2779,7 @@ err:
 	return ret;
 }
 
-static int proc_setgroups_release(struct inode *inode, struct file *file)
+static int proc_nsadmin_release(struct inode *inode, struct file *file)
 {
 	struct seq_file *seq = file->private_data;
 	struct user_namespace *ns = seq->private;
@@ -2787,12 +2788,30 @@ static int proc_setgroups_release(struct inode *inode, struct file *file)
 	return ret;
 }
 
+static int proc_setgroups_open(struct inode *inode, struct file *file)
+{
+	return proc_nsadmin_open(inode, file, &proc_setgroups_show);
+}
+
 static const struct file_operations proc_setgroups_operations = {
 	.open		= proc_setgroups_open,
 	.write		= proc_setgroups_write,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
-	.release	= proc_setgroups_release,
+	.release	= proc_nsadmin_release,
+};
+
+static int proc_transparent_open(struct inode *inode, struct file *file)
+{
+	return proc_nsadmin_open(inode, file, &proc_transparent_show);
+}
+
+static const struct file_operations proc_transparent_operations = {
+	.open		= proc_transparent_open,
+	.write		= proc_transparent_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= proc_nsadmin_release,
 };
 #endif /* CONFIG_USER_NS */
 
@@ -2901,6 +2920,7 @@ static const struct pid_entry tgid_base_stuff[] = {
 	REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
 	REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
 	REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
+	REG("transparent", S_IRUGO|S_IWUSR, proc_transparent_operations),
 #endif
 #ifdef CONFIG_CHECKPOINT_RESTORE
 	REG("timers",	  S_IRUGO, proc_timers_operations),
diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
index 0383552..2908d40 100644
--- a/include/linux/uidgid.h
+++ b/include/linux/uidgid.h
@@ -124,17 +124,19 @@ extern kgid_t make_kgid(struct user_namespace *from, gid_t gid);
 
 extern uid_t from_kuid(struct user_namespace *to, kuid_t uid);
 extern gid_t from_kgid(struct user_namespace *to, kgid_t gid);
+extern uid_t from_kuid_opaque(struct user_namespace *to, kuid_t uid);
+extern gid_t from_kgid_opaque(struct user_namespace *to, kgid_t gid);
 extern uid_t from_kuid_munged(struct user_namespace *to, kuid_t uid);
 extern gid_t from_kgid_munged(struct user_namespace *to, kgid_t gid);
 
 static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid)
 {
-	return from_kuid(ns, uid) != (uid_t) -1;
+	return from_kuid_opaque(ns, uid) != (uid_t) -1;
 }
 
 static inline bool kgid_has_mapping(struct user_namespace *ns, kgid_t gid)
 {
-	return from_kgid(ns, gid) != (gid_t) -1;
+	return from_kgid_opaque(ns, gid) != (gid_t) -1;
 }
 
 #else
@@ -159,6 +161,16 @@ static inline gid_t from_kgid(struct user_namespace *to, kgid_t kgid)
 	return __kgid_val(kgid);
 }
 
+static inline uid_t from_kuid_opaque(struct user_namespace *to, kuid_t kuid)
+{
+	return __kuid_val(kuid);
+}
+
+static inline gid_t from_kgid_opaque(struct user_namespace *to, kgid_t kgid)
+{
+	return __kgid_val(kgid);
+}
+
 static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
 {
 	uid_t uid = from_kuid(to, kuid);
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 8297e5b..18291ac 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -28,6 +28,8 @@ struct user_namespace {
 	struct uid_gid_map	projid_map;
 	atomic_t		count;
 	struct user_namespace	*parent;
+	/* self for normal ns; first opaque parent for transparent ns */
+	struct user_namespace	*opaque;
 	int			level;
 	kuid_t			owner;
 	kgid_t			group;
@@ -71,6 +73,8 @@ extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, lo
 extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_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 ssize_t proc_transparent_write(struct file *, const char __user *, size_t, loff_t *);
+extern int proc_transparent_show(struct seq_file *m, void *v);
 extern bool userns_may_setgroups(const struct user_namespace *ns);
 #else
 
diff --git a/kernel/user.c b/kernel/user.c
index b069ccb..e1fd9e5 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -48,6 +48,7 @@ struct user_namespace init_user_ns = {
 		},
 	},
 	.count = ATOMIC_INIT(3),
+	.opaque = &init_user_ns,
 	.owner = GLOBAL_ROOT_UID,
 	.group = GLOBAL_ROOT_GID,
 	.ns.inum = PROC_USER_INIT_INO,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 9bafc21..da329a1 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -98,6 +98,7 @@ int create_user_ns(struct cred *new)
 	atomic_set(&ns->count, 1);
 	/* Leave the new->user_ns reference with the new user namespace. */
 	ns->parent = parent_ns;
+	ns->opaque = ns;
 	ns->level = parent_ns->level + 1;
 	ns->owner = owner;
 	ns->group = group;
@@ -251,18 +252,46 @@ EXPORT_SYMBOL(make_kuid);
  *	Map @kuid into the user-namespace specified by @targ and
  *	return the resulting uid.
  *
+ *	This function is *not* appropriate for security checks because
+ *	if @targ is transparent, the mappings of an ancestor namespace
+ *	are used. If @targ isn't &init_user_ns and you intend to do
+ *	anything with the result apart from returning it to a process
+ *	in @targ, you might want to use from_kuid_opaque() instead.
+ *
  *	There is always a mapping into the initial user_namespace.
  *
- *	If @kuid has no mapping in @targ (uid_t)-1 is returned.
+ *	If @kuid is not visible in @targ (uid_t)-1 is returned.
  */
 uid_t from_kuid(struct user_namespace *targ, kuid_t kuid)
 {
 	/* Map the uid from a global kernel uid */
-	return map_id_up(&targ->uid_map, __kuid_val(kuid));
+	struct user_namespace *opaque = READ_ONCE(targ->opaque);
+
+	return map_id_up(&opaque->uid_map, __kuid_val(kuid));
 }
 EXPORT_SYMBOL(from_kuid);
 
 /**
+ *	from_kuid_opaque - Create a uid from a kuid user-namespace pair.
+ *	@targ: The user namespace we want a uid in.
+ *	@kuid: The kernel internal uid to start with.
+ *
+ *	Map @kuid into the user-namespace specified by @targ and
+ *	return the resulting uid. This ignores transparent user
+ *	namespaces and is therefore appropriate for security checks.
+ *
+ *	There is always a mapping into the initial user_namespace.
+ *
+ *	If @kuid has no mapping in @targ (uid_t)-1 is returned.
+ */
+uid_t from_kuid_opaque(struct user_namespace *targ, kuid_t kuid)
+{
+	/* Map the uid from a global kernel uid */
+	return map_id_up(&targ->uid_map, __kuid_val(kuid));
+}
+EXPORT_SYMBOL(from_kuid_opaque);
+
+/**
  *	from_kuid_munged - Create a uid from a kuid user-namespace pair.
  *	@targ: The user namespace we want a uid in.
  *	@kuid: The kernel internal uid to start with.
@@ -319,18 +348,46 @@ EXPORT_SYMBOL(make_kgid);
  *	Map @kgid into the user-namespace specified by @targ and
  *	return the resulting gid.
  *
+ *	This function is *not* appropriate for security checks because
+ *	if @targ is transparent, the mappings of an ancestor namespace
+ *	are used. If @targ isn't &init_user_ns and you intend to do
+ *	anything with the result apart from returning it to a process
+ *	in @targ, you might want to use from_kgid_opaque() instead.
+ *
  *	There is always a mapping into the initial user_namespace.
  *
- *	If @kgid has no mapping in @targ (gid_t)-1 is returned.
+ *	If @kgid is not visible in @targ (gid_t)-1 is returned.
  */
 gid_t from_kgid(struct user_namespace *targ, kgid_t kgid)
 {
 	/* Map the gid from a global kernel gid */
-	return map_id_up(&targ->gid_map, __kgid_val(kgid));
+	struct user_namespace *opaque = READ_ONCE(targ->opaque);
+
+	return map_id_up(&opaque->gid_map, __kgid_val(kgid));
 }
 EXPORT_SYMBOL(from_kgid);
 
 /**
+ *	from_kgid_opaque - Create a gid from a kgid user-namespace pair.
+ *	@targ: The user namespace we want a gid in.
+ *	@kgid: The kernel internal gid to start with.
+ *
+ *	Map @kgid into the user-namespace specified by @targ and
+ *	return the resulting gid. This ignores transparent user
+ *	namespaces and is therefore appropriate for security checks.
+ *
+ *	There is always a mapping into the initial user_namespace.
+ *
+ *	If @kgid has no mapping in @targ (gid_t)-1 is returned.
+ */
+gid_t from_kgid_opaque(struct user_namespace *targ, kgid_t kgid)
+{
+	/* Map the gid from a global kernel gid */
+	return map_id_up(&targ->gid_map, __kgid_val(kgid));
+}
+EXPORT_SYMBOL(from_kgid_opaque);
+
+/**
  *	from_kgid_munged - Create a gid from a kgid user-namespace pair.
  *	@targ: The user namespace we want a gid in.
  *	@kgid: The kernel internal gid to start with.
@@ -811,6 +868,18 @@ static bool new_idmap_permitted(const struct file *file,
 				struct uid_gid_map *new_map)
 {
 	const struct cred *cred = file->f_cred;
+	unsigned int idx;
+
+	/* Don't allow non-identity mappings in transparent namespaces. */
+	if (ns != ns->opaque) {
+		for (idx = 0; idx < new_map->nr_extents; idx++) {
+			struct uid_gid_extent *ext = &new_map->extent[idx];
+
+			if (ext->first != ext->lower_first)
+				return false;
+		}
+	}
+
 	/* Don't allow mappings that would allow anything that wouldn't
 	 * be allowed without the establishment of unprivileged mappings.
 	 */
@@ -922,6 +991,81 @@ out_unlock:
 	goto out;
 }
 
+int proc_transparent_show(struct seq_file *seq, void *v)
+{
+	struct user_namespace *ns = seq->private;
+	struct user_namespace *opaque = READ_ONCE(ns->opaque);
+
+	seq_printf(seq, "%d\n", (ns == opaque) ? 0 : 1);
+	return 0;
+}
+
+ssize_t proc_transparent_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct seq_file *seq = file->private_data;
+	struct user_namespace *ns = seq->private;
+	char kbuf[8], *pos;
+	bool transparent;
+	ssize_t ret;
+
+	/* Only allow a very narrow range of strings to be written */
+	ret = -EINVAL;
+	if ((*ppos != 0) || (count >= sizeof(kbuf)))
+		goto out;
+
+	/* What was written? */
+	ret = -EFAULT;
+	if (copy_from_user(kbuf, buf, count))
+		goto out;
+	kbuf[count] = '\0';
+	pos = kbuf;
+
+	/* What is being requested? */
+	ret = -EINVAL;
+	if (pos[0] == '1') {
+		pos += 1;
+		transparent = true;
+	} else if (pos[0] == '0') {
+		pos += 1;
+		transparent = false;
+	} else
+		goto out;
+
+	/* Verify there is not trailing junk on the line */
+	pos = skip_spaces(pos);
+	if (*pos != '\0')
+		goto out;
+
+	ret = -EPERM;
+	mutex_lock(&userns_state_mutex);
+	/* Is the requested state different from the current one? */
+	if (transparent != (ns->opaque != ns)) {
+		/* You can't turn off transparent mode. */
+		if (!transparent)
+			goto out_unlock;
+		/* If there are existing mappings, they might be
+		 * non-identity mappings. Therefore, block transparent
+		 * mode. This also prevents making the init namespace
+		 * transparent (which wouldn't work).
+		 */
+		if (ns->uid_map.nr_extents != 0 || ns->gid_map.nr_extents != 0)
+			goto out_unlock;
+		/* Okay! Make the namespace transparent. */
+		ns->opaque = ns->parent->opaque;
+	}
+	mutex_unlock(&userns_state_mutex);
+
+	/* Report a successful write */
+	*ppos = count;
+	ret = count;
+out:
+	return ret;
+out_unlock:
+	mutex_unlock(&userns_state_mutex);
+	goto out;
+}
+
 bool userns_may_setgroups(const struct user_namespace *ns)
 {
 	bool allowed;
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH] namespaces: add transparent user namespaces
  2016-06-23 11:11 [PATCH] namespaces: add transparent user namespaces Jann Horn
@ 2016-06-23 18:50 ` Eric W. Biederman
  2016-06-23 20:02   ` Jann Horn
  0 siblings, 1 reply; 14+ messages in thread
From: Eric W. Biederman @ 2016-06-23 18:50 UTC (permalink / raw)
  To: Jann Horn
  Cc: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Andy Lutomirski,
	linux-kernel

Jann Horn <jannh@google.com> writes:

> This allows the admin of a user namespace to mark the namespace as
> transparent. All other namespaces, by default, are opaque.
>
> While the current behavior of user namespaces is appropriate for use in
> containers, there are many programs that only use user namespaces because
> doing so enables them to do other things (e.g. unsharing the mount or
> network namespace) that require namespaced capabilities. For them, the
> inability to see the real UIDs and GIDs of things from inside the user
> namespace can be very annoying.
>
> In a transparent namespace, all UIDs and GIDs that are mapped into its
> first opaque ancestor are visible and are not remapped. This means that if
> a process e.g. stat()s the real root directory in a namespace, it will
> still see it as owned by UID 0.
>
> Traditionally, any UID or GID that was visible in a user namespace was also
> mapped into the namespace, giving the namespace admin full access to it.
> This patch introduces a distinction: In a transparent namespace, UIDs and
> GIDs can be visible without being mapped. Non-mapped, visible UIDs can be
> passed from the kernel to userspace, but userspace can't send them back to
> the kernel. In order to be able to fully use specific UIDs/GIDs and gain
> privileges over them, mappings need to be set up in the usual way -
> however, to avoid aliasing problems, only identity mappings are permitted.
>
> I have gone through all callers of from_kuid() and from_kgid(), and as far
> as I can tell, kuid_has_mapping() and kgid_has_mapping() were the only
> functions that used them for privilege checks. (The keys subsystem uses
> them in an insecure way, and that issue has been known for a while, but my
> patch doesn't make that any more vulnerable than it already is.

Perhaps it has been known for a while but no one has stopped and
mentioned it to me.  What questionable thing is the keys subsystem
doing?

>)

This is a bigish change in semantics and I am going to have to digest
this before I can give this an ok.

Quite frankly at the base it scares me.


If this is just about presentation and allowing some information from
the parent user namespace I would be much happier if it was not
from_kuid but that you modified, but if you instead you had a function
say from_kuid_transparent, that performed the transformation you need
and was only used in those places it is safe.

I think I could reason about that.

As your patchset sits I can not reason about the change in semantics,
because without a large grep of the source I don't know what you are
changing.

And you are dramatically changing the semantics the semantics of
from_kuid to the point I do believe we need to inspepect all of the call
sites.  As such I really don't think it makes sense to reuse the
existing name for your new semantics.

Eric

> Signed-off-by: Jann Horn <jannh@google.com>
> ---
>  fs/proc/base.c                 |  28 ++++++--
>  include/linux/uidgid.h         |  16 ++++-
>  include/linux/user_namespace.h |   4 ++
>  kernel/user.c                  |   1 +
>  kernel/user_namespace.c        | 152 +++++++++++++++++++++++++++++++++++++++--
>  5 files changed, 191 insertions(+), 10 deletions(-)
>
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index a11eb71..c521c51 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -2744,7 +2744,8 @@ static const struct file_operations proc_projid_map_operations = {
>  	.release	= proc_id_map_release,
>  };
>  
> -static int proc_setgroups_open(struct inode *inode, struct file *file)
> +static int proc_nsadmin_open(struct inode *inode, struct file *file,
> +	int (*show)(struct seq_file *, void *))
>  {
>  	struct user_namespace *ns = NULL;
>  	struct task_struct *task;
> @@ -2767,7 +2768,7 @@ static int proc_setgroups_open(struct inode *inode, struct file *file)
>  			goto err_put_ns;
>  	}
>  
> -	ret = single_open(file, &proc_setgroups_show, ns);
> +	ret = single_open(file, show, ns);
>  	if (ret)
>  		goto err_put_ns;
>  
> @@ -2778,7 +2779,7 @@ err:
>  	return ret;
>  }
>  
> -static int proc_setgroups_release(struct inode *inode, struct file *file)
> +static int proc_nsadmin_release(struct inode *inode, struct file *file)
>  {
>  	struct seq_file *seq = file->private_data;
>  	struct user_namespace *ns = seq->private;
> @@ -2787,12 +2788,30 @@ static int proc_setgroups_release(struct inode *inode, struct file *file)
>  	return ret;
>  }
>  
> +static int proc_setgroups_open(struct inode *inode, struct file *file)
> +{
> +	return proc_nsadmin_open(inode, file, &proc_setgroups_show);
> +}
> +
>  static const struct file_operations proc_setgroups_operations = {
>  	.open		= proc_setgroups_open,
>  	.write		= proc_setgroups_write,
>  	.read		= seq_read,
>  	.llseek		= seq_lseek,
> -	.release	= proc_setgroups_release,
> +	.release	= proc_nsadmin_release,
> +};
> +
> +static int proc_transparent_open(struct inode *inode, struct file *file)
> +{
> +	return proc_nsadmin_open(inode, file, &proc_transparent_show);
> +}
> +
> +static const struct file_operations proc_transparent_operations = {
> +	.open		= proc_transparent_open,
> +	.write		= proc_transparent_write,
> +	.read		= seq_read,
> +	.llseek		= seq_lseek,
> +	.release	= proc_nsadmin_release,
>  };
>  #endif /* CONFIG_USER_NS */
>  
> @@ -2901,6 +2920,7 @@ static const struct pid_entry tgid_base_stuff[] = {
>  	REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
>  	REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
>  	REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
> +	REG("transparent", S_IRUGO|S_IWUSR, proc_transparent_operations),
>  #endif
>  #ifdef CONFIG_CHECKPOINT_RESTORE
>  	REG("timers",	  S_IRUGO, proc_timers_operations),
> diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
> index 0383552..2908d40 100644
> --- a/include/linux/uidgid.h
> +++ b/include/linux/uidgid.h
> @@ -124,17 +124,19 @@ extern kgid_t make_kgid(struct user_namespace *from, gid_t gid);
>  
>  extern uid_t from_kuid(struct user_namespace *to, kuid_t uid);
>  extern gid_t from_kgid(struct user_namespace *to, kgid_t gid);
> +extern uid_t from_kuid_opaque(struct user_namespace *to, kuid_t uid);
> +extern gid_t from_kgid_opaque(struct user_namespace *to, kgid_t gid);
>  extern uid_t from_kuid_munged(struct user_namespace *to, kuid_t uid);
>  extern gid_t from_kgid_munged(struct user_namespace *to, kgid_t gid);
>  
>  static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid)
>  {
> -	return from_kuid(ns, uid) != (uid_t) -1;
> +	return from_kuid_opaque(ns, uid) != (uid_t) -1;
>  }
>  
>  static inline bool kgid_has_mapping(struct user_namespace *ns, kgid_t gid)
>  {
> -	return from_kgid(ns, gid) != (gid_t) -1;
> +	return from_kgid_opaque(ns, gid) != (gid_t) -1;
>  }
>  
>  #else
> @@ -159,6 +161,16 @@ static inline gid_t from_kgid(struct user_namespace *to, kgid_t kgid)
>  	return __kgid_val(kgid);
>  }
>  
> +static inline uid_t from_kuid_opaque(struct user_namespace *to, kuid_t kuid)
> +{
> +	return __kuid_val(kuid);
> +}
> +
> +static inline gid_t from_kgid_opaque(struct user_namespace *to, kgid_t kgid)
> +{
> +	return __kgid_val(kgid);
> +}
> +
>  static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
>  {
>  	uid_t uid = from_kuid(to, kuid);
> diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
> index 8297e5b..18291ac 100644
> --- a/include/linux/user_namespace.h
> +++ b/include/linux/user_namespace.h
> @@ -28,6 +28,8 @@ struct user_namespace {
>  	struct uid_gid_map	projid_map;
>  	atomic_t		count;
>  	struct user_namespace	*parent;
> +	/* self for normal ns; first opaque parent for transparent ns */
> +	struct user_namespace	*opaque;
>  	int			level;
>  	kuid_t			owner;
>  	kgid_t			group;
> @@ -71,6 +73,8 @@ extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, lo
>  extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_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 ssize_t proc_transparent_write(struct file *, const char __user *, size_t, loff_t *);
> +extern int proc_transparent_show(struct seq_file *m, void *v);
>  extern bool userns_may_setgroups(const struct user_namespace *ns);
>  #else
>  
> diff --git a/kernel/user.c b/kernel/user.c
> index b069ccb..e1fd9e5 100644
> --- a/kernel/user.c
> +++ b/kernel/user.c
> @@ -48,6 +48,7 @@ struct user_namespace init_user_ns = {
>  		},
>  	},
>  	.count = ATOMIC_INIT(3),
> +	.opaque = &init_user_ns,
>  	.owner = GLOBAL_ROOT_UID,
>  	.group = GLOBAL_ROOT_GID,
>  	.ns.inum = PROC_USER_INIT_INO,
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 9bafc21..da329a1 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -98,6 +98,7 @@ int create_user_ns(struct cred *new)
>  	atomic_set(&ns->count, 1);
>  	/* Leave the new->user_ns reference with the new user namespace. */
>  	ns->parent = parent_ns;
> +	ns->opaque = ns;
>  	ns->level = parent_ns->level + 1;
>  	ns->owner = owner;
>  	ns->group = group;
> @@ -251,18 +252,46 @@ EXPORT_SYMBOL(make_kuid);
>   *	Map @kuid into the user-namespace specified by @targ and
>   *	return the resulting uid.
>   *
> + *	This function is *not* appropriate for security checks because
> + *	if @targ is transparent, the mappings of an ancestor namespace
> + *	are used. If @targ isn't &init_user_ns and you intend to do
> + *	anything with the result apart from returning it to a process
> + *	in @targ, you might want to use from_kuid_opaque() instead.
> + *
>   *	There is always a mapping into the initial user_namespace.
>   *
> - *	If @kuid has no mapping in @targ (uid_t)-1 is returned.
> + *	If @kuid is not visible in @targ (uid_t)-1 is returned.
>   */
>  uid_t from_kuid(struct user_namespace *targ, kuid_t kuid)
>  {
>  	/* Map the uid from a global kernel uid */
> -	return map_id_up(&targ->uid_map, __kuid_val(kuid));
> +	struct user_namespace *opaque = READ_ONCE(targ->opaque);
> +
> +	return map_id_up(&opaque->uid_map, __kuid_val(kuid));
>  }
>  EXPORT_SYMBOL(from_kuid);
>  
>  /**
> + *	from_kuid_opaque - Create a uid from a kuid user-namespace pair.
> + *	@targ: The user namespace we want a uid in.
> + *	@kuid: The kernel internal uid to start with.
> + *
> + *	Map @kuid into the user-namespace specified by @targ and
> + *	return the resulting uid. This ignores transparent user
> + *	namespaces and is therefore appropriate for security checks.
> + *
> + *	There is always a mapping into the initial user_namespace.
> + *
> + *	If @kuid has no mapping in @targ (uid_t)-1 is returned.
> + */
> +uid_t from_kuid_opaque(struct user_namespace *targ, kuid_t kuid)
> +{
> +	/* Map the uid from a global kernel uid */
> +	return map_id_up(&targ->uid_map, __kuid_val(kuid));
> +}
> +EXPORT_SYMBOL(from_kuid_opaque);
> +
> +/**
>   *	from_kuid_munged - Create a uid from a kuid user-namespace pair.
>   *	@targ: The user namespace we want a uid in.
>   *	@kuid: The kernel internal uid to start with.
> @@ -319,18 +348,46 @@ EXPORT_SYMBOL(make_kgid);
>   *	Map @kgid into the user-namespace specified by @targ and
>   *	return the resulting gid.
>   *
> + *	This function is *not* appropriate for security checks because
> + *	if @targ is transparent, the mappings of an ancestor namespace
> + *	are used. If @targ isn't &init_user_ns and you intend to do
> + *	anything with the result apart from returning it to a process
> + *	in @targ, you might want to use from_kgid_opaque() instead.
> + *
>   *	There is always a mapping into the initial user_namespace.
>   *
> - *	If @kgid has no mapping in @targ (gid_t)-1 is returned.
> + *	If @kgid is not visible in @targ (gid_t)-1 is returned.
>   */
>  gid_t from_kgid(struct user_namespace *targ, kgid_t kgid)
>  {
>  	/* Map the gid from a global kernel gid */
> -	return map_id_up(&targ->gid_map, __kgid_val(kgid));
> +	struct user_namespace *opaque = READ_ONCE(targ->opaque);
> +
> +	return map_id_up(&opaque->gid_map, __kgid_val(kgid));
>  }
>  EXPORT_SYMBOL(from_kgid);
>  
>  /**
> + *	from_kgid_opaque - Create a gid from a kgid user-namespace pair.
> + *	@targ: The user namespace we want a gid in.
> + *	@kgid: The kernel internal gid to start with.
> + *
> + *	Map @kgid into the user-namespace specified by @targ and
> + *	return the resulting gid. This ignores transparent user
> + *	namespaces and is therefore appropriate for security checks.
> + *
> + *	There is always a mapping into the initial user_namespace.
> + *
> + *	If @kgid has no mapping in @targ (gid_t)-1 is returned.
> + */
> +gid_t from_kgid_opaque(struct user_namespace *targ, kgid_t kgid)
> +{
> +	/* Map the gid from a global kernel gid */
> +	return map_id_up(&targ->gid_map, __kgid_val(kgid));
> +}
> +EXPORT_SYMBOL(from_kgid_opaque);
> +
> +/**
>   *	from_kgid_munged - Create a gid from a kgid user-namespace pair.
>   *	@targ: The user namespace we want a gid in.
>   *	@kgid: The kernel internal gid to start with.
> @@ -811,6 +868,18 @@ static bool new_idmap_permitted(const struct file *file,
>  				struct uid_gid_map *new_map)
>  {
>  	const struct cred *cred = file->f_cred;
> +	unsigned int idx;
> +
> +	/* Don't allow non-identity mappings in transparent namespaces. */
> +	if (ns != ns->opaque) {
> +		for (idx = 0; idx < new_map->nr_extents; idx++) {
> +			struct uid_gid_extent *ext = &new_map->extent[idx];
> +
> +			if (ext->first != ext->lower_first)
> +				return false;
> +		}
> +	}
> +
>  	/* Don't allow mappings that would allow anything that wouldn't
>  	 * be allowed without the establishment of unprivileged mappings.
>  	 */
> @@ -922,6 +991,81 @@ out_unlock:
>  	goto out;
>  }
>  
> +int proc_transparent_show(struct seq_file *seq, void *v)
> +{
> +	struct user_namespace *ns = seq->private;
> +	struct user_namespace *opaque = READ_ONCE(ns->opaque);
> +
> +	seq_printf(seq, "%d\n", (ns == opaque) ? 0 : 1);
> +	return 0;
> +}
> +
> +ssize_t proc_transparent_write(struct file *file, const char __user *buf,
> +			     size_t count, loff_t *ppos)
> +{
> +	struct seq_file *seq = file->private_data;
> +	struct user_namespace *ns = seq->private;
> +	char kbuf[8], *pos;
> +	bool transparent;
> +	ssize_t ret;
> +
> +	/* Only allow a very narrow range of strings to be written */
> +	ret = -EINVAL;
> +	if ((*ppos != 0) || (count >= sizeof(kbuf)))
> +		goto out;
> +
> +	/* What was written? */
> +	ret = -EFAULT;
> +	if (copy_from_user(kbuf, buf, count))
> +		goto out;
> +	kbuf[count] = '\0';
> +	pos = kbuf;
> +
> +	/* What is being requested? */
> +	ret = -EINVAL;
> +	if (pos[0] == '1') {
> +		pos += 1;
> +		transparent = true;
> +	} else if (pos[0] == '0') {
> +		pos += 1;
> +		transparent = false;
> +	} else
> +		goto out;
> +
> +	/* Verify there is not trailing junk on the line */
> +	pos = skip_spaces(pos);
> +	if (*pos != '\0')
> +		goto out;
> +
> +	ret = -EPERM;
> +	mutex_lock(&userns_state_mutex);
> +	/* Is the requested state different from the current one? */
> +	if (transparent != (ns->opaque != ns)) {
> +		/* You can't turn off transparent mode. */
> +		if (!transparent)
> +			goto out_unlock;
> +		/* If there are existing mappings, they might be
> +		 * non-identity mappings. Therefore, block transparent
> +		 * mode. This also prevents making the init namespace
> +		 * transparent (which wouldn't work).
> +		 */
> +		if (ns->uid_map.nr_extents != 0 || ns->gid_map.nr_extents != 0)
> +			goto out_unlock;
> +		/* Okay! Make the namespace transparent. */
> +		ns->opaque = ns->parent->opaque;
> +	}
> +	mutex_unlock(&userns_state_mutex);
> +
> +	/* Report a successful write */
> +	*ppos = count;
> +	ret = count;
> +out:
> +	return ret;
> +out_unlock:
> +	mutex_unlock(&userns_state_mutex);
> +	goto out;
> +}
> +
>  bool userns_may_setgroups(const struct user_namespace *ns)
>  {
>  	bool allowed;

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] namespaces: add transparent user namespaces
  2016-06-23 18:50 ` Eric W. Biederman
@ 2016-06-23 20:02   ` Jann Horn
  2016-06-23 20:04     ` Eric W. Biederman
  2016-06-25  0:23     ` [PATCH v2 1/2] namespaces: don't use from_k*id_munged() with init_user_ns Jann Horn
  0 siblings, 2 replies; 14+ messages in thread
From: Jann Horn @ 2016-06-23 20:02 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Andy Lutomirski,
	linux-kernel

On Thu, Jun 23, 2016 at 8:50 PM, Eric W. Biederman
<ebiederm@xmission.com> wrote:
> Jann Horn <jannh@google.com> writes:
>
>> This allows the admin of a user namespace to mark the namespace as
>> transparent. All other namespaces, by default, are opaque.
>>
>> While the current behavior of user namespaces is appropriate for use in
>> containers, there are many programs that only use user namespaces because
>> doing so enables them to do other things (e.g. unsharing the mount or
>> network namespace) that require namespaced capabilities. For them, the
>> inability to see the real UIDs and GIDs of things from inside the user
>> namespace can be very annoying.
>>
>> In a transparent namespace, all UIDs and GIDs that are mapped into its
>> first opaque ancestor are visible and are not remapped. This means that if
>> a process e.g. stat()s the real root directory in a namespace, it will
>> still see it as owned by UID 0.
>>
>> Traditionally, any UID or GID that was visible in a user namespace was also
>> mapped into the namespace, giving the namespace admin full access to it.
>> This patch introduces a distinction: In a transparent namespace, UIDs and
>> GIDs can be visible without being mapped. Non-mapped, visible UIDs can be
>> passed from the kernel to userspace, but userspace can't send them back to
>> the kernel. In order to be able to fully use specific UIDs/GIDs and gain
>> privileges over them, mappings need to be set up in the usual way -
>> however, to avoid aliasing problems, only identity mappings are permitted.
>>
>> I have gone through all callers of from_kuid() and from_kgid(), and as far
>> as I can tell, kuid_has_mapping() and kgid_has_mapping() were the only
>> functions that used them for privilege checks. (The keys subsystem uses
>> them in an insecure way, and that issue has been known for a while, but my
>> patch doesn't make that any more vulnerable than it already is.
>
> Perhaps it has been known for a while but no one has stopped and
> mentioned it to me.  What questionable thing is the keys subsystem
> doing?

Not just questionable, completely wrong. The gist is that there is a
*global* name -> key mapping for accessing keys by name, and user
keyrings are stored in there under the name "_uid.%u", where %u
refers to the *namespaced* UID. (See install_user_keyrings().)
The result is that, if e.g. the user with UID 1000 has no running
processes, a local attacker can enter a new user namespace, map UID
1000 in the namespace to some KUID he controls, do
setresuid(1000, 1000, 1000), and now he owns user 1000's keyring.
This ends up permitting the attacker to dump the contents of KUID
1000's keys after KUID 1000 signs in. I discovered this while going
through the kuid->uid conversions when I thought about writing this
feature the first time.

(I think you have security list access, right? If so, you can see my PoC
and the discussion in the "namespace handling in security/keys is
broken" thread from 5. January 2016.)

> This is a bigish change in semantics and I am going to have to digest
> this before I can give this an ok.
>
> Quite frankly at the base it scares me.
>
> If this is just about presentation and allowing some information from
> the parent user namespace

Yes, that's exactly my intent.

> I would be much happier if it was not
> from_kuid but that you modified, but if you instead you had a function
> say from_kuid_transparent, that performed the transformation you need
> and was only used in those places it is safe.
>
> I think I could reason about that.
>
> As your patchset sits I can not reason about the change in semantics,
> because without a large grep of the source I don't know what you are
> changing.
>
> And you are dramatically changing the semantics the semantics of
> from_kuid to the point I do believe we need to inspepect all of the call
> sites.  As such I really don't think it makes sense to reuse the
> existing name for your new semantics.

Sure, that makes sense. I'll make a v2 with from_*uid_transparent() or
so.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] namespaces: add transparent user namespaces
  2016-06-23 20:02   ` Jann Horn
@ 2016-06-23 20:04     ` Eric W. Biederman
  2016-06-25  0:23     ` [PATCH v2 1/2] namespaces: don't use from_k*id_munged() with init_user_ns Jann Horn
  1 sibling, 0 replies; 14+ messages in thread
From: Eric W. Biederman @ 2016-06-23 20:04 UTC (permalink / raw)
  To: Jann Horn
  Cc: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Andy Lutomirski,
	linux-kernel

Jann Horn <jannh@google.com> writes:

> On Thu, Jun 23, 2016 at 8:50 PM, Eric W. Biederman
> <ebiederm@xmission.com> wrote:
>> Jann Horn <jannh@google.com> writes:
>>
>>> This allows the admin of a user namespace to mark the namespace as
>>> transparent. All other namespaces, by default, are opaque.
>>>
>>> While the current behavior of user namespaces is appropriate for use in
>>> containers, there are many programs that only use user namespaces because
>>> doing so enables them to do other things (e.g. unsharing the mount or
>>> network namespace) that require namespaced capabilities. For them, the
>>> inability to see the real UIDs and GIDs of things from inside the user
>>> namespace can be very annoying.
>>>
>>> In a transparent namespace, all UIDs and GIDs that are mapped into its
>>> first opaque ancestor are visible and are not remapped. This means that if
>>> a process e.g. stat()s the real root directory in a namespace, it will
>>> still see it as owned by UID 0.
>>>
>>> Traditionally, any UID or GID that was visible in a user namespace was also
>>> mapped into the namespace, giving the namespace admin full access to it.
>>> This patch introduces a distinction: In a transparent namespace, UIDs and
>>> GIDs can be visible without being mapped. Non-mapped, visible UIDs can be
>>> passed from the kernel to userspace, but userspace can't send them back to
>>> the kernel. In order to be able to fully use specific UIDs/GIDs and gain
>>> privileges over them, mappings need to be set up in the usual way -
>>> however, to avoid aliasing problems, only identity mappings are permitted.
>>>
>>> I have gone through all callers of from_kuid() and from_kgid(), and as far
>>> as I can tell, kuid_has_mapping() and kgid_has_mapping() were the only
>>> functions that used them for privilege checks. (The keys subsystem uses
>>> them in an insecure way, and that issue has been known for a while, but my
>>> patch doesn't make that any more vulnerable than it already is.
>>
>> Perhaps it has been known for a while but no one has stopped and
>> mentioned it to me.  What questionable thing is the keys subsystem
>> doing?
>
> Not just questionable, completely wrong. The gist is that there is a
> *global* name -> key mapping for accessing keys by name, and user
> keyrings are stored in there under the name "_uid.%u", where %u
> refers to the *namespaced* UID. (See install_user_keyrings().)
> The result is that, if e.g. the user with UID 1000 has no running
> processes, a local attacker can enter a new user namespace, map UID
> 1000 in the namespace to some KUID he controls, do
> setresuid(1000, 1000, 1000), and now he owns user 1000's keyring.
> This ends up permitting the attacker to dump the contents of KUID
> 1000's keys after KUID 1000 signs in. I discovered this while going
> through the kuid->uid conversions when I thought about writing this
> feature the first time.

Ugh.  That definitely sounds like something worth addressing, and is
unfortunately how these kinds of things are found.

> (I think you have security list access, right? If so, you can see my PoC
> and the discussion in the "namespace handling in security/keys is
> broken" thread from 5. January 2016.)

I actually don't.  I have been Cc'd frequently so it would probably
smart if I did subscribe to the security list.  The rules of how a
person subscribes to that list have changed since last I understood what
was going on.  It used to be a vger list with a two week delay for
ordinary mortals to see the emails.

Currently I don't know what the rules for the list are, or how to
subscribe.  Any chance you might put me in contact with right people.
Not having been notified of that issue in January rather scares me.

>> This is a bigish change in semantics and I am going to have to digest
>> this before I can give this an ok.
>>
>> Quite frankly at the base it scares me.
>>
>> If this is just about presentation and allowing some information from
>> the parent user namespace
>
> Yes, that's exactly my intent.
>
>> I would be much happier if it was not
>> from_kuid but that you modified, but if you instead you had a function
>> say from_kuid_transparent, that performed the transformation you need
>> and was only used in those places it is safe.
>>
>> I think I could reason about that.
>>
>> As your patchset sits I can not reason about the change in semantics,
>> because without a large grep of the source I don't know what you are
>> changing.
>>
>> And you are dramatically changing the semantics the semantics of
>> from_kuid to the point I do believe we need to inspepect all of the call
>> sites.  As such I really don't think it makes sense to reuse the
>> existing name for your new semantics.
>
> Sure, that makes sense. I'll make a v2 with from_*uid_transparent() or
> so.

Thanks.  

Eric

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 1/2] namespaces: don't use from_k*id_munged() with init_user_ns
  2016-06-23 20:02   ` Jann Horn
  2016-06-23 20:04     ` Eric W. Biederman
@ 2016-06-25  0:23     ` Jann Horn
  2016-06-25  0:23       ` [PATCH v2 2/2] namespaces: add transparent user namespaces Jann Horn
  1 sibling, 1 reply; 14+ messages in thread
From: Jann Horn @ 2016-06-25  0:23 UTC (permalink / raw)
  To: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Eric W. Biederman,
	Andy Lutomirski, linux-kernel
  Cc: Jann Horn

For init_user_ns, from_kuid_munged() and from_kgid_munged() are
guaranteed to be identical to from_kuid() and from_kgid(). In
preparation for another patch, change all usages of the _munged
variants that explicitly use init_user_ns to the non-munged ones.

This patch by itself should not have any effect.

Signed-off-by: Jann Horn <jannh@google.com>
---
 arch/s390/hypfs/inode.c         |  4 ++--
 arch/x86/kernel/vm86_32.c       |  2 +-
 drivers/connector/cn_proc.c     |  8 ++++----
 fs/adfs/super.c                 |  4 ++--
 fs/autofs4/inode.c              |  4 ++--
 fs/cifs/cifs_spnego.c           |  4 ++--
 fs/cifs/cifsfs.c                |  8 ++++----
 fs/debugfs/inode.c              |  4 ++--
 fs/devpts/inode.c               |  4 ++--
 fs/ext2/super.c                 |  4 ++--
 fs/ext4/super.c                 |  4 ++--
 fs/fat/inode.c                  |  4 ++--
 fs/fuse/dev.c                   |  4 ++--
 fs/fuse/inode.c                 |  4 ++--
 fs/hfs/super.c                  |  4 ++--
 fs/hfsplus/options.c            |  4 ++--
 fs/hpfs/super.c                 |  4 ++--
 fs/ncpfs/inode.c                |  6 +++---
 fs/ntfs/inode.c                 |  4 ++--
 fs/proc/inode.c                 |  2 +-
 fs/quota/netlink.c              |  2 +-
 fs/tracefs/inode.c              |  4 ++--
 kernel/cred.c                   | 16 ++++++++--------
 mm/shmem.c                      |  4 ++--
 net/netfilter/nf_log_common.c   |  4 ++--
 net/netfilter/nfnetlink_queue.c |  4 ++--
 net/netfilter/nft_meta.c        |  4 ++--
 27 files changed, 62 insertions(+), 62 deletions(-)

diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index 255c7ee..df0e2f3 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -259,8 +259,8 @@ static int hypfs_show_options(struct seq_file *s, struct dentry *root)
 {
 	struct hypfs_sb_info *hypfs_info = root->d_sb->s_fs_info;
 
-	seq_printf(s, ",uid=%u", from_kuid_munged(&init_user_ns, hypfs_info->uid));
-	seq_printf(s, ",gid=%u", from_kgid_munged(&init_user_ns, hypfs_info->gid));
+	seq_printf(s, ",uid=%u", from_kuid(&init_user_ns, hypfs_info->uid));
+	seq_printf(s, ",gid=%u", from_kgid(&init_user_ns, hypfs_info->gid));
 	return 0;
 }
 
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 3dce1ca..23d8541 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -259,7 +259,7 @@ static long do_sys_vm86(struct vm86plus_struct __user *user_vm86, bool plus)
 		 */
 		pr_info_once("Denied a call to vm86(old) from %s[%d] (uid: %d).  Set the vm.mmap_min_addr sysctl to 0 and/or adjust LSM mmap_min_addr policy to enable vm86 if you are using a vm86-based DOS emulator.\n",
 			     current->comm, task_pid_nr(current),
-			     from_kuid_munged(&init_user_ns, current_uid()));
+			     from_kuid(&init_user_ns, current_uid()));
 		return -EPERM;
 	}
 
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 15d06fc..89c3b6f 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -140,11 +140,11 @@ void proc_id_connector(struct task_struct *task, int which_id)
 	rcu_read_lock();
 	cred = __task_cred(task);
 	if (which_id == PROC_EVENT_UID) {
-		ev->event_data.id.r.ruid = from_kuid_munged(&init_user_ns, cred->uid);
-		ev->event_data.id.e.euid = from_kuid_munged(&init_user_ns, cred->euid);
+		ev->event_data.id.r.ruid = from_kuid(&init_user_ns, cred->uid);
+		ev->event_data.id.e.euid = from_kuid(&init_user_ns, cred->euid);
 	} else if (which_id == PROC_EVENT_GID) {
-		ev->event_data.id.r.rgid = from_kgid_munged(&init_user_ns, cred->gid);
-		ev->event_data.id.e.egid = from_kgid_munged(&init_user_ns, cred->egid);
+		ev->event_data.id.r.rgid = from_kgid(&init_user_ns, cred->gid);
+		ev->event_data.id.e.egid = from_kgid(&init_user_ns, cred->egid);
 	} else {
 		rcu_read_unlock();
 		return;
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index c9fdfb1..48b9195 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -131,9 +131,9 @@ static int adfs_show_options(struct seq_file *seq, struct dentry *root)
 	struct adfs_sb_info *asb = ADFS_SB(root->d_sb);
 
 	if (!uid_eq(asb->s_uid, GLOBAL_ROOT_UID))
-		seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, asb->s_uid));
+		seq_printf(seq, ",uid=%u", from_kuid(&init_user_ns, asb->s_uid));
 	if (!gid_eq(asb->s_gid, GLOBAL_ROOT_GID))
-		seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, asb->s_gid));
+		seq_printf(seq, ",gid=%u", from_kgid(&init_user_ns, asb->s_gid));
 	if (asb->s_owner_mask != ADFS_DEFAULT_OWNER_MASK)
 		seq_printf(seq, ",ownmask=%o", asb->s_owner_mask);
 	if (asb->s_other_mask != ADFS_DEFAULT_OTHER_MASK)
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
index 61b2105..85a306f 100644
--- a/fs/autofs4/inode.c
+++ b/fs/autofs4/inode.c
@@ -77,10 +77,10 @@ static int autofs4_show_options(struct seq_file *m, struct dentry *root)
 	seq_printf(m, ",fd=%d", sbi->pipefd);
 	if (!uid_eq(root_inode->i_uid, GLOBAL_ROOT_UID))
 		seq_printf(m, ",uid=%u",
-			from_kuid_munged(&init_user_ns, root_inode->i_uid));
+			from_kuid(&init_user_ns, root_inode->i_uid));
 	if (!gid_eq(root_inode->i_gid, GLOBAL_ROOT_GID))
 		seq_printf(m, ",gid=%u",
-			from_kgid_munged(&init_user_ns, root_inode->i_gid));
+			from_kgid(&init_user_ns, root_inode->i_gid));
 	seq_printf(m, ",pgrp=%d", pid_vnr(sbi->oz_pgrp));
 	seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ);
 	seq_printf(m, ",minproto=%d", sbi->min_proto);
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index b611fc2..3784b8f 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -152,11 +152,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo)
 
 	dp = description + strlen(description);
 	sprintf(dp, ";uid=0x%x",
-		from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
+		from_kuid(&init_user_ns, sesInfo->linux_uid));
 
 	dp = description + strlen(description);
 	sprintf(dp, ";creduid=0x%x",
-		from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
+		from_kuid(&init_user_ns, sesInfo->cred_uid));
 
 	if (sesInfo->user_name) {
 		dp = description + strlen(description);
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 5d8b7ed..36af22a 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -428,14 +428,14 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
 	}
 
 	seq_printf(s, ",uid=%u",
-		   from_kuid_munged(&init_user_ns, cifs_sb->mnt_uid));
+		   from_kuid(&init_user_ns, cifs_sb->mnt_uid));
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
 		seq_puts(s, ",forceuid");
 	else
 		seq_puts(s, ",noforceuid");
 
 	seq_printf(s, ",gid=%u",
-		   from_kgid_munged(&init_user_ns, cifs_sb->mnt_gid));
+		   from_kgid(&init_user_ns, cifs_sb->mnt_gid));
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
 		seq_puts(s, ",forcegid");
 	else
@@ -500,11 +500,11 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
 		seq_puts(s, ",noperm");
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID)
 		seq_printf(s, ",backupuid=%u",
-			   from_kuid_munged(&init_user_ns,
+			   from_kuid(&init_user_ns,
 					    cifs_sb->mnt_backupuid));
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID)
 		seq_printf(s, ",backupgid=%u",
-			   from_kgid_munged(&init_user_ns,
+			   from_kgid(&init_user_ns,
 					    cifs_sb->mnt_backupgid));
 
 	seq_printf(s, ",rsize=%u", cifs_sb->rsize);
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 4bc1f68..ff4cffe 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -160,10 +160,10 @@ static int debugfs_show_options(struct seq_file *m, struct dentry *root)
 
 	if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
 		seq_printf(m, ",uid=%u",
-			   from_kuid_munged(&init_user_ns, opts->uid));
+			   from_kuid(&init_user_ns, opts->uid));
 	if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
 		seq_printf(m, ",gid=%u",
-			   from_kgid_munged(&init_user_ns, opts->gid));
+			   from_kgid(&init_user_ns, opts->gid));
 	if (opts->mode != DEBUGFS_DEFAULT_MODE)
 		seq_printf(m, ",mode=%o", opts->mode);
 
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 37c134a..2809cb9 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -357,10 +357,10 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root)
 
 	if (opts->setuid)
 		seq_printf(seq, ",uid=%u",
-			   from_kuid_munged(&init_user_ns, opts->uid));
+			   from_kuid(&init_user_ns, opts->uid));
 	if (opts->setgid)
 		seq_printf(seq, ",gid=%u",
-			   from_kgid_munged(&init_user_ns, opts->gid));
+			   from_kgid(&init_user_ns, opts->gid));
 	seq_printf(seq, ",mode=%03o", opts->mode);
 	seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode);
 	if (opts->max < NR_UNIX98_PTY_MAX)
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 1d93795..0d66d5d 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -244,12 +244,12 @@ static int ext2_show_options(struct seq_file *seq, struct dentry *root)
 	if (!uid_eq(sbi->s_resuid, make_kuid(&init_user_ns, EXT2_DEF_RESUID)) ||
 	    le16_to_cpu(es->s_def_resuid) != EXT2_DEF_RESUID) {
 		seq_printf(seq, ",resuid=%u",
-				from_kuid_munged(&init_user_ns, sbi->s_resuid));
+				from_kuid(&init_user_ns, sbi->s_resuid));
 	}
 	if (!gid_eq(sbi->s_resgid, make_kgid(&init_user_ns, EXT2_DEF_RESGID)) ||
 	    le16_to_cpu(es->s_def_resgid) != EXT2_DEF_RESGID) {
 		seq_printf(seq, ",resgid=%u",
-				from_kgid_munged(&init_user_ns, sbi->s_resgid));
+				from_kgid(&init_user_ns, sbi->s_resgid));
 	}
 	if (test_opt(sb, ERRORS_RO)) {
 		int def_errors = le16_to_cpu(es->s_errors);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 3822a5a..4c5664b 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1879,11 +1879,11 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
 	if (nodefs || !uid_eq(sbi->s_resuid, make_kuid(&init_user_ns, EXT4_DEF_RESUID)) ||
 	    le16_to_cpu(es->s_def_resuid) != EXT4_DEF_RESUID)
 		SEQ_OPTS_PRINT("resuid=%u",
-				from_kuid_munged(&init_user_ns, sbi->s_resuid));
+				from_kuid(&init_user_ns, sbi->s_resuid));
 	if (nodefs || !gid_eq(sbi->s_resgid, make_kgid(&init_user_ns, EXT4_DEF_RESGID)) ||
 	    le16_to_cpu(es->s_def_resgid) != EXT4_DEF_RESGID)
 		SEQ_OPTS_PRINT("resgid=%u",
-				from_kgid_munged(&init_user_ns, sbi->s_resgid));
+				from_kgid(&init_user_ns, sbi->s_resgid));
 	def_errors = nodefs ? -1 : le16_to_cpu(es->s_errors);
 	if (test_opt(sb, ERRORS_RO) && def_errors != EXT4_ERRORS_RO)
 		SEQ_OPTS_PUTS("errors=remount-ro");
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 3bcf579..e56472e 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -923,10 +923,10 @@ static int fat_show_options(struct seq_file *m, struct dentry *root)
 
 	if (!uid_eq(opts->fs_uid, GLOBAL_ROOT_UID))
 		seq_printf(m, ",uid=%u",
-				from_kuid_munged(&init_user_ns, opts->fs_uid));
+				from_kuid(&init_user_ns, opts->fs_uid));
 	if (!gid_eq(opts->fs_gid, GLOBAL_ROOT_GID))
 		seq_printf(m, ",gid=%u",
-				from_kgid_munged(&init_user_ns, opts->fs_gid));
+				from_kgid(&init_user_ns, opts->fs_gid));
 	seq_printf(m, ",fmask=%04o", opts->fs_fmask);
 	seq_printf(m, ",dmask=%04o", opts->fs_dmask);
 	if (opts->allow_utime)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index cbece12..811c96c 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -126,8 +126,8 @@ static void __fuse_put_request(struct fuse_req *req)
 
 static void fuse_req_init_context(struct fuse_req *req)
 {
-	req->in.h.uid = from_kuid_munged(&init_user_ns, current_fsuid());
-	req->in.h.gid = from_kgid_munged(&init_user_ns, current_fsgid());
+	req->in.h.uid = from_kuid(&init_user_ns, current_fsuid());
+	req->in.h.gid = from_kgid(&init_user_ns, current_fsgid());
 	req->in.h.pid = current->pid;
 }
 
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 1ce6766..fb5b216 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -554,8 +554,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
 	struct super_block *sb = root->d_sb;
 	struct fuse_conn *fc = get_fuse_conn_super(sb);
 
-	seq_printf(m, ",user_id=%u", from_kuid_munged(&init_user_ns, fc->user_id));
-	seq_printf(m, ",group_id=%u", from_kgid_munged(&init_user_ns, fc->group_id));
+	seq_printf(m, ",user_id=%u", from_kuid(&init_user_ns, fc->user_id));
+	seq_printf(m, ",group_id=%u", from_kgid(&init_user_ns, fc->group_id));
 	if (fc->flags & FUSE_DEFAULT_PERMISSIONS)
 		seq_puts(m, ",default_permissions");
 	if (fc->flags & FUSE_ALLOW_OTHER)
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 1ca95c2..30c952a 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -140,8 +140,8 @@ static int hfs_show_options(struct seq_file *seq, struct dentry *root)
 	if (sbi->s_type != cpu_to_be32(0x3f3f3f3f))
 		seq_show_option_n(seq, "type", (char *)&sbi->s_type, 4);
 	seq_printf(seq, ",uid=%u,gid=%u",
-			from_kuid_munged(&init_user_ns, sbi->s_uid),
-			from_kgid_munged(&init_user_ns, sbi->s_gid));
+			from_kuid(&init_user_ns, sbi->s_uid),
+			from_kgid(&init_user_ns, sbi->s_gid));
 	if (sbi->s_file_umask != 0133)
 		seq_printf(seq, ",file_umask=%o", sbi->s_file_umask);
 	if (sbi->s_dir_umask != 0022)
diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c
index bb806e5..6897f9f 100644
--- a/fs/hfsplus/options.c
+++ b/fs/hfsplus/options.c
@@ -222,8 +222,8 @@ int hfsplus_show_options(struct seq_file *seq, struct dentry *root)
 	if (sbi->type != HFSPLUS_DEF_CR_TYPE)
 		seq_show_option_n(seq, "type", (char *)&sbi->type, 4);
 	seq_printf(seq, ",umask=%o,uid=%u,gid=%u", sbi->umask,
-			from_kuid_munged(&init_user_ns, sbi->uid),
-			from_kgid_munged(&init_user_ns, sbi->gid));
+			from_kuid(&init_user_ns, sbi->uid),
+			from_kgid(&init_user_ns, sbi->gid));
 	if (sbi->part >= 0)
 		seq_printf(seq, ",part=%u", sbi->part);
 	if (sbi->session >= 0)
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index 82067ca..98d632b 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -502,8 +502,8 @@ static int hpfs_show_options(struct seq_file *seq, struct dentry *root)
 {
 	struct hpfs_sb_info *sbi = hpfs_sb(root->d_sb);
 
-	seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, sbi->sb_uid));
-	seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, sbi->sb_gid));
+	seq_printf(seq, ",uid=%u", from_kuid(&init_user_ns, sbi->sb_uid));
+	seq_printf(seq, ",gid=%u", from_kgid(&init_user_ns, sbi->sb_gid));
 	seq_printf(seq, ",umask=%03o", (~sbi->sb_mode & 0777));
 	if (sbi->sb_lowercase)
 		seq_printf(seq, ",case=lower");
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 1af15fc..43770d3 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -334,13 +334,13 @@ static int  ncp_show_options(struct seq_file *seq, struct dentry *root)
 
 	if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID))
 		seq_printf(seq, ",uid=%u",
-			   from_kuid_munged(&init_user_ns, server->m.uid));
+			   from_kuid(&init_user_ns, server->m.uid));
 	if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID))
 		seq_printf(seq, ",gid=%u",
-			   from_kgid_munged(&init_user_ns, server->m.gid));
+			   from_kgid(&init_user_ns, server->m.gid));
 	if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID))
 		seq_printf(seq, ",owner=%u",
-			   from_kuid_munged(&init_user_ns, server->m.mounted_uid));
+			   from_kuid(&init_user_ns, server->m.mounted_uid));
 	tmp = server->m.file_mode & S_IALLUGO;
 	if (tmp != NCP_DEFAULT_FILE_MODE)
 		seq_printf(seq, ",mode=0%o", tmp);
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index f40972d..dc9dd5d 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -2314,8 +2314,8 @@ int ntfs_show_options(struct seq_file *sf, struct dentry *root)
 	ntfs_volume *vol = NTFS_SB(root->d_sb);
 	int i;
 
-	seq_printf(sf, ",uid=%i", from_kuid_munged(&init_user_ns, vol->uid));
-	seq_printf(sf, ",gid=%i", from_kgid_munged(&init_user_ns, vol->gid));
+	seq_printf(sf, ",uid=%i", from_kuid(&init_user_ns, vol->uid));
+	seq_printf(sf, ",gid=%i", from_kgid(&init_user_ns, vol->gid));
 	if (vol->fmask == vol->dmask)
 		seq_printf(sf, ",umask=0%o", vol->fmask);
 	else {
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 42305dd..4cba964 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -106,7 +106,7 @@ static int proc_show_options(struct seq_file *seq, struct dentry *root)
 	struct pid_namespace *pid = sb->s_fs_info;
 
 	if (!gid_eq(pid->pid_gid, GLOBAL_ROOT_GID))
-		seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, pid->pid_gid));
+		seq_printf(seq, ",gid=%u", from_kgid(&init_user_ns, pid->pid_gid));
 	if (pid->hide_pid != 0)
 		seq_printf(seq, ",hidepid=%u", pid->hide_pid);
 
diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c
index 8b25267..042c799 100644
--- a/fs/quota/netlink.c
+++ b/fs/quota/netlink.c
@@ -83,7 +83,7 @@ void quota_send_warning(struct kqid qid, dev_t dev,
 	if (ret)
 		goto attr_err_out;
 	ret = nla_put_u64_64bit(skb, QUOTA_NL_A_CAUSED_ID,
-				from_kuid_munged(&init_user_ns, current_uid()),
+				from_kuid(&init_user_ns, current_uid()),
 				QUOTA_NL_A_PAD);
 	if (ret)
 		goto attr_err_out;
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
index 4a0e48f..74b6645 100644
--- a/fs/tracefs/inode.c
+++ b/fs/tracefs/inode.c
@@ -248,10 +248,10 @@ static int tracefs_show_options(struct seq_file *m, struct dentry *root)
 
 	if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
 		seq_printf(m, ",uid=%u",
-			   from_kuid_munged(&init_user_ns, opts->uid));
+			   from_kuid(&init_user_ns, opts->uid));
 	if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
 		seq_printf(m, ",gid=%u",
-			   from_kgid_munged(&init_user_ns, opts->gid));
+			   from_kgid(&init_user_ns, opts->gid));
 	if (opts->mode != TRACEFS_DEFAULT_MODE)
 		seq_printf(m, ",mode=%o", opts->mode);
 
diff --git a/kernel/cred.c b/kernel/cred.c
index 0c0cd8a..da39a3d 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -735,15 +735,15 @@ static void dump_invalid_creds(const struct cred *cred, const char *label,
 	       atomic_read(&cred->usage),
 	       read_cred_subscribers(cred));
 	printk(KERN_ERR "CRED: ->*uid = { %d,%d,%d,%d }\n",
-		from_kuid_munged(&init_user_ns, cred->uid),
-		from_kuid_munged(&init_user_ns, cred->euid),
-		from_kuid_munged(&init_user_ns, cred->suid),
-		from_kuid_munged(&init_user_ns, cred->fsuid));
+		from_kuid(&init_user_ns, cred->uid),
+		from_kuid(&init_user_ns, cred->euid),
+		from_kuid(&init_user_ns, cred->suid),
+		from_kuid(&init_user_ns, cred->fsuid));
 	printk(KERN_ERR "CRED: ->*gid = { %d,%d,%d,%d }\n",
-		from_kgid_munged(&init_user_ns, cred->gid),
-		from_kgid_munged(&init_user_ns, cred->egid),
-		from_kgid_munged(&init_user_ns, cred->sgid),
-		from_kgid_munged(&init_user_ns, cred->fsgid));
+		from_kgid(&init_user_ns, cred->gid),
+		from_kgid(&init_user_ns, cred->egid),
+		from_kgid(&init_user_ns, cred->sgid),
+		from_kgid(&init_user_ns, cred->fsgid));
 #ifdef CONFIG_SECURITY
 	printk(KERN_ERR "CRED: ->security is %p\n", cred->security);
 	if ((unsigned long) cred->security >= PAGE_SIZE &&
diff --git a/mm/shmem.c b/mm/shmem.c
index a361449..144b3d7 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2937,10 +2937,10 @@ static int shmem_show_options(struct seq_file *seq, struct dentry *root)
 		seq_printf(seq, ",mode=%03ho", sbinfo->mode);
 	if (!uid_eq(sbinfo->uid, GLOBAL_ROOT_UID))
 		seq_printf(seq, ",uid=%u",
-				from_kuid_munged(&init_user_ns, sbinfo->uid));
+				from_kuid(&init_user_ns, sbinfo->uid));
 	if (!gid_eq(sbinfo->gid, GLOBAL_ROOT_GID))
 		seq_printf(seq, ",gid=%u",
-				from_kgid_munged(&init_user_ns, sbinfo->gid));
+				from_kgid(&init_user_ns, sbinfo->gid));
 	shmem_show_mpol(seq, sbinfo->mpol);
 	return 0;
 }
diff --git a/net/netfilter/nf_log_common.c b/net/netfilter/nf_log_common.c
index a5aa596..b115895 100644
--- a/net/netfilter/nf_log_common.c
+++ b/net/netfilter/nf_log_common.c
@@ -141,8 +141,8 @@ void nf_log_dump_sk_uid_gid(struct nf_log_buf *m, struct sock *sk)
 	if (sk->sk_socket && sk->sk_socket->file) {
 		const struct cred *cred = sk->sk_socket->file->f_cred;
 		nf_log_buf_add(m, "UID=%u GID=%u ",
-			from_kuid_munged(&init_user_ns, cred->fsuid),
-			from_kgid_munged(&init_user_ns, cred->fsgid));
+			from_kuid(&init_user_ns, cred->fsuid),
+			from_kgid(&init_user_ns, cred->fsgid));
 	}
 	read_unlock_bh(&sk->sk_callback_lock);
 }
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 5d36a09..71e57bf 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -264,10 +264,10 @@ static int nfqnl_put_sk_uidgid(struct sk_buff *skb, struct sock *sk)
 	if (sk->sk_socket && sk->sk_socket->file) {
 		cred = sk->sk_socket->file->f_cred;
 		if (nla_put_be32(skb, NFQA_UID,
-		    htonl(from_kuid_munged(&init_user_ns, cred->fsuid))))
+		    htonl(from_kuid(&init_user_ns, cred->fsuid))))
 			goto nla_put_failure;
 		if (nla_put_be32(skb, NFQA_GID,
-		    htonl(from_kgid_munged(&init_user_ns, cred->fsgid))))
+		    htonl(from_kgid(&init_user_ns, cred->fsgid))))
 			goto nla_put_failure;
 	}
 	read_unlock_bh(&sk->sk_callback_lock);
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 16c50b0..fbdbb52 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -104,7 +104,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
 			goto err;
 		}
 
-		*dest =	from_kuid_munged(&init_user_ns,
+		*dest =	from_kuid(&init_user_ns,
 				sk->sk_socket->file->f_cred->fsuid);
 		read_unlock_bh(&sk->sk_callback_lock);
 		break;
@@ -119,7 +119,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
 			read_unlock_bh(&sk->sk_callback_lock);
 			goto err;
 		}
-		*dest =	from_kgid_munged(&init_user_ns,
+		*dest =	from_kgid(&init_user_ns,
 				 sk->sk_socket->file->f_cred->fsgid);
 		read_unlock_bh(&sk->sk_callback_lock);
 		break;
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 2/2] namespaces: add transparent user namespaces
  2016-06-25  0:23     ` [PATCH v2 1/2] namespaces: don't use from_k*id_munged() with init_user_ns Jann Horn
@ 2016-06-25  0:23       ` Jann Horn
  2016-06-27  3:34           ` Michael Kerrisk
       [not found]         ` <1466814210-3778-2-git-send-email-jannh-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
  0 siblings, 2 replies; 14+ messages in thread
From: Jann Horn @ 2016-06-25  0:23 UTC (permalink / raw)
  To: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Eric W. Biederman,
	Andy Lutomirski, linux-kernel
  Cc: Jann Horn

This allows the admin of a user namespace to mark the namespace as
transparent. All other namespaces, by default, are opaque.

While the current behavior of user namespaces is appropriate for use in
containers, there are many programs that only use user namespaces because
doing so enables them to do other things (e.g. unsharing the mount or
network namespace) that require namespaced capabilities. For them, the
inability to see the real UIDs and GIDs of things from inside the user
namespace can be very annoying.

In a transparent namespace, all UIDs and GIDs that are mapped into its
first opaque ancestor are visible and are not remapped. This means that if
a process e.g. stat()s the real root directory in a namespace, it will
still see it as owned by UID 0.

Traditionally, any UID or GID that was visible in a user namespace was also
mapped into the namespace, giving the namespace admin full access to it.
This patch introduces a distinction: In a transparent namespace, UIDs and
GIDs can be visible without being mapped. Non-mapped, visible UIDs can be
passed from the kernel to userspace, but userspace can't send them back to
the kernel. In order to be able to fully use specific UIDs/GIDs and gain
privileges over them, mappings need to be set up in the usual way -
however, to avoid aliasing problems, only identity mappings are permitted.

v2:
Ensure that all relevant from_k[ug]id callers show up in the patch.
_transparent would be more verbose than _tp, but considering the line
length rule, that's just too long.

Yes, this makes the patch rather large.

Behavior should be the same as in v1, except that I'm not touching orangefs
in this patch because every single use of from_k[ug]id in it is wrong in
some way. (Thanks for making me reread all that stuff, Eric.) I'll write a
separate patch or at least report the issue with more detail later.

(Also, the handling of user namespaces when dealing with signals is
super-ugly and kind of incorrect. That should probably be cleaned up.)

posix_acl_to_xattr would have changed behavior in the v1 patch, but isn't
changed here. Because it's only used with init_user_ns, that won't change
user-visible behavior relative to v1.

This patch was compile-tested with allyesconfig. I also ran a VM with this
patch applied and checked that it still works, but that probably doesn't
mean much.

Signed-off-by: Jann Horn <jannh@google.com>
---
 arch/alpha/kernel/osf_sys.c       |   4 +-
 arch/arm/kernel/sys_oabi-compat.c |   4 +-
 arch/ia64/kernel/signal.c         |   4 +-
 arch/s390/kernel/compat_linux.c   |  26 +++---
 arch/sparc/kernel/sys_sparc32.c   |   4 +-
 arch/x86/ia32/sys_ia32.c          |   4 +-
 drivers/android/binder.c          |   2 +-
 drivers/gpu/drm/drm_info.c        |   2 +-
 drivers/gpu/drm/drm_ioctl.c       |   2 +-
 drivers/net/tun.c                 |   4 +-
 fs/autofs4/dev-ioctl.c            |   4 +-
 fs/autofs4/waitq.c                |   4 +-
 fs/binfmt_elf.c                   |  12 +--
 fs/binfmt_elf_fdpic.c             |  12 +--
 fs/compat.c                       |   4 +-
 fs/fcntl.c                        |   4 +-
 fs/ncpfs/ioctl.c                  |  12 +--
 fs/posix_acl.c                    |  11 ++-
 fs/proc/array.c                   |  18 ++--
 fs/proc/base.c                    |  30 +++++--
 fs/quota/kqid.c                   |  12 ++-
 fs/stat.c                         |  12 +--
 include/linux/uidgid.h            |  24 +++--
 include/linux/user_namespace.h    |   4 +
 include/net/scm.h                 |   4 +-
 ipc/mqueue.c                      |   2 +-
 ipc/msg.c                         |   8 +-
 ipc/sem.c                         |   8 +-
 ipc/shm.c                         |   8 +-
 ipc/util.c                        |   8 +-
 kernel/acct.c                     |   4 +-
 kernel/exit.c                     |   6 +-
 kernel/groups.c                   |   2 +-
 kernel/signal.c                   |  16 ++--
 kernel/sys.c                      |  24 ++---
 kernel/trace/trace.c              |   2 +-
 kernel/tsacct.c                   |   4 +-
 kernel/uid16.c                    |  22 ++---
 kernel/user.c                     |   1 +
 kernel/user_namespace.c           | 178 +++++++++++++++++++++++++++++++++++---
 net/appletalk/atalk_proc.c        |   2 +-
 net/ax25/ax25_uid.c               |   4 +-
 net/bluetooth/af_bluetooth.c      |   2 +-
 net/core/sock.c                   |   4 +-
 net/ipv4/inet_diag.c              |   2 +-
 net/ipv4/ping.c                   |   2 +-
 net/ipv4/raw.c                    |   2 +-
 net/ipv4/sysctl_net_ipv4.c        |   4 +-
 net/ipv4/tcp_ipv4.c               |   6 +-
 net/ipv4/udp.c                    |   2 +-
 net/ipv6/datagram.c               |   2 +-
 net/ipv6/ip6_flowlabel.c          |   2 +-
 net/ipv6/tcp_ipv6.c               |   6 +-
 net/ipx/ipx_proc.c                |   2 +-
 net/key/af_key.c                  |   2 +-
 net/llc/llc_proc.c                |   2 +-
 net/netfilter/nfnetlink_log.c     |   4 +-
 net/packet/af_packet.c            |   2 +-
 net/packet/diag.c                 |   2 +-
 net/phonet/socket.c               |   4 +-
 net/sctp/proc.c                   |   4 +-
 net/sunrpc/svcauth_unix.c         |   4 +-
 security/keys/keyctl.c            |   4 +-
 security/keys/proc.c              |   6 +-
 64 files changed, 395 insertions(+), 197 deletions(-)

diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index ffb93f49..6440f8e 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -277,8 +277,8 @@ linux_to_osf_stat(struct kstat *lstat, struct osf_stat __user *osf_stat)
 	tmp.st_dev	= lstat->dev;
 	tmp.st_mode	= lstat->mode;
 	tmp.st_nlink	= lstat->nlink;
-	tmp.st_uid	= from_kuid_munged(current_user_ns(), lstat->uid);
-	tmp.st_gid	= from_kgid_munged(current_user_ns(), lstat->gid);
+	tmp.st_uid	= from_kuid_tp_munged(current_user_ns(), lstat->uid);
+	tmp.st_gid	= from_kgid_tp_munged(current_user_ns(), lstat->gid);
 	tmp.st_rdev	= lstat->rdev;
 	tmp.st_ldev	= lstat->rdev;
 	tmp.st_size	= lstat->size;
diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
index 087acb5..47748eb 100644
--- a/arch/arm/kernel/sys_oabi-compat.c
+++ b/arch/arm/kernel/sys_oabi-compat.c
@@ -124,8 +124,8 @@ static long cp_oldabi_stat64(struct kstat *stat,
 	tmp.__st_ino = stat->ino;
 	tmp.st_mode = stat->mode;
 	tmp.st_nlink = stat->nlink;
-	tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
-	tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
+	tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
+	tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
 	tmp.st_rdev = huge_encode_dev(stat->rdev);
 	tmp.st_size = stat->size;
 	tmp.st_blocks = stat->blocks;
diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
index b3a124d..071b9c0 100644
--- a/arch/ia64/kernel/signal.c
+++ b/arch/ia64/kernel/signal.c
@@ -209,7 +209,7 @@ ia64_rt_sigreturn (struct sigscratch *scr)
 	si.si_errno = 0;
 	si.si_code = SI_KERNEL;
 	si.si_pid = task_pid_vnr(current);
-	si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	si.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 	si.si_addr = sc;
 	force_sig_info(SIGSEGV, &si, current);
 	return retval;
@@ -306,7 +306,7 @@ force_sigsegv_info (int sig, void __user *addr)
 	si.si_errno = 0;
 	si.si_code = SI_KERNEL;
 	si.si_pid = task_pid_vnr(current);
-	si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	si.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 	si.si_addr = addr;
 	force_sig_info(SIGSEGV, &si, current);
 	return 1;
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index 437e611..31a39ba 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -136,9 +136,9 @@ COMPAT_SYSCALL_DEFINE3(s390_getresuid16, u16 __user *, ruidp,
 	int retval;
 	u16 ruid, euid, suid;
 
-	ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
-	euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
-	suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
+	ruid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->uid));
+	euid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->euid));
+	suid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->suid));
 
 	if (!(retval   = put_user(ruid, ruidp)) &&
 	    !(retval   = put_user(euid, euidp)))
@@ -160,9 +160,9 @@ COMPAT_SYSCALL_DEFINE3(s390_getresgid16, u16 __user *, rgidp,
 	int retval;
 	u16 rgid, egid, sgid;
 
-	rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
-	egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
-	sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
+	rgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->gid));
+	egid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->egid));
+	sgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->sgid));
 
 	if (!(retval   = put_user(rgid, rgidp)) &&
 	    !(retval   = put_user(egid, egidp)))
@@ -190,7 +190,7 @@ static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info
 
 	for (i = 0; i < group_info->ngroups; i++) {
 		kgid = GROUP_AT(group_info, i);
-		group = (u16)from_kgid_munged(user_ns, kgid);
+		group = (u16)from_kgid_tp_munged(user_ns, kgid);
 		if (put_user(group, grouplist+i))
 			return -EFAULT;
 	}
@@ -271,22 +271,22 @@ COMPAT_SYSCALL_DEFINE2(s390_setgroups16, int, gidsetsize, u16 __user *, grouplis
 
 COMPAT_SYSCALL_DEFINE0(s390_getuid16)
 {
-	return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
+	return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_uid()));
 }
 
 COMPAT_SYSCALL_DEFINE0(s390_geteuid16)
 {
-	return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
+	return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_euid()));
 }
 
 COMPAT_SYSCALL_DEFINE0(s390_getgid16)
 {
-	return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
+	return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_gid()));
 }
 
 COMPAT_SYSCALL_DEFINE0(s390_getegid16)
 {
-	return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
+	return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_egid()));
 }
 
 #ifdef CONFIG_SYSVIPC
@@ -366,8 +366,8 @@ static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat)
 	tmp.__st_ino = (u32)stat->ino;
 	tmp.st_mode = stat->mode;
 	tmp.st_nlink = (unsigned int)stat->nlink;
-	tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
-	tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
+	tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
+	tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
 	tmp.st_rdev = huge_encode_dev(stat->rdev);
 	tmp.st_size = stat->size;
 	tmp.st_blksize = (u32)stat->blksize;
diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
index 022c30c..e65acab 100644
--- a/arch/sparc/kernel/sys_sparc32.c
+++ b/arch/sparc/kernel/sys_sparc32.c
@@ -76,8 +76,8 @@ static int cp_compat_stat64(struct kstat *stat,
 	err |= put_user(stat->ino, &statbuf->st_ino);
 	err |= put_user(stat->mode, &statbuf->st_mode);
 	err |= put_user(stat->nlink, &statbuf->st_nlink);
-	err |= put_user(from_kuid_munged(current_user_ns(), stat->uid), &statbuf->st_uid);
-	err |= put_user(from_kgid_munged(current_user_ns(), stat->gid), &statbuf->st_gid);
+	err |= put_user(from_kuid_tp_munged(current_user_ns(), stat->uid), &statbuf->st_uid);
+	err |= put_user(from_kgid_tp_munged(current_user_ns(), stat->gid), &statbuf->st_gid);
 	err |= put_user(huge_encode_dev(stat->rdev), &statbuf->st_rdev);
 	err |= put_user(0, (unsigned long __user *) &statbuf->__pad3[0]);
 	err |= put_user(stat->size, &statbuf->st_size);
diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
index 719cd70..e8d4532 100644
--- a/arch/x86/ia32/sys_ia32.c
+++ b/arch/x86/ia32/sys_ia32.c
@@ -71,8 +71,8 @@ static int cp_stat64(struct stat64 __user *ubuf, struct kstat *stat)
 {
 	typeof(ubuf->st_uid) uid = 0;
 	typeof(ubuf->st_gid) gid = 0;
-	SET_UID(uid, from_kuid_munged(current_user_ns(), stat->uid));
-	SET_GID(gid, from_kgid_munged(current_user_ns(), stat->gid));
+	SET_UID(uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
+	SET_GID(gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
 	if (!access_ok(VERIFY_WRITE, ubuf, sizeof(struct stat64)) ||
 	    __put_user(huge_encode_dev(stat->dev), &ubuf->st_dev) ||
 	    __put_user(stat->ino, &ubuf->__st_ino) ||
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 16288e7..c8fcf71 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -2400,7 +2400,7 @@ retry:
 		}
 		tr.code = t->code;
 		tr.flags = t->flags;
-		tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
+		tr.sender_euid = from_kuid_tp(current_user_ns(), t->sender_euid);
 
 		if (t->from) {
 			struct task_struct *sender = t->from->proc->tsk;
diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
index 5d469b2..07ab3d4 100644
--- a/drivers/gpu/drm/drm_info.c
+++ b/drivers/gpu/drm/drm_info.c
@@ -186,7 +186,7 @@ int drm_clients_info(struct seq_file *m, void *data)
 			   priv->minor->index,
 			   priv->is_master ? 'y' : 'n',
 			   priv->authenticated ? 'y' : 'n',
-			   from_kuid_munged(seq_user_ns(m), priv->uid),
+			   from_kuid_tp_munged(seq_user_ns(m), priv->uid),
 			   priv->magic);
 		rcu_read_unlock();
 	}
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index b7a39771c..2e18837 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -181,7 +181,7 @@ static int drm_getclient(struct drm_device *dev, void *data,
 	if (client->idx == 0) {
 		client->auth = file_priv->authenticated;
 		client->pid = pid_vnr(file_priv->pid);
-		client->uid = from_kuid_munged(current_user_ns(),
+		client->uid = from_kuid_tp_munged(current_user_ns(),
 					       file_priv->uid);
 		client->magic = 0;
 		client->iocs = 0;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index e16487c..8965a26 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1659,7 +1659,7 @@ static ssize_t tun_show_owner(struct device *dev, struct device_attribute *attr,
 	struct tun_struct *tun = netdev_priv(to_net_dev(dev));
 	return uid_valid(tun->owner)?
 		sprintf(buf, "%u\n",
-			from_kuid_munged(current_user_ns(), tun->owner)):
+			from_kuid_tp_munged(current_user_ns(), tun->owner)) :
 		sprintf(buf, "-1\n");
 }
 
@@ -1669,7 +1669,7 @@ static ssize_t tun_show_group(struct device *dev, struct device_attribute *attr,
 	struct tun_struct *tun = netdev_priv(to_net_dev(dev));
 	return gid_valid(tun->group) ?
 		sprintf(buf, "%u\n",
-			from_kgid_munged(current_user_ns(), tun->group)):
+			from_kgid_tp_munged(current_user_ns(), tun->group)) :
 		sprintf(buf, "-1\n");
 }
 
diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
index c7fcc74..4c75a4c 100644
--- a/fs/autofs4/dev-ioctl.c
+++ b/fs/autofs4/dev-ioctl.c
@@ -460,9 +460,9 @@ static int autofs_dev_ioctl_requester(struct file *fp,
 		autofs4_expire_wait(path.dentry, 0);
 		spin_lock(&sbi->fs_lock);
 		param->requester.uid =
-			from_kuid_munged(current_user_ns(), ino->uid);
+			from_kuid_tp_munged(current_user_ns(), ino->uid);
 		param->requester.gid =
-			from_kgid_munged(current_user_ns(), ino->gid);
+			from_kgid_tp_munged(current_user_ns(), ino->gid);
 		spin_unlock(&sbi->fs_lock);
 	}
 	path_put(&path);
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index 0146d91..7d55752 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -157,8 +157,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
 		packet->name[wq->name.len] = '\0';
 		packet->dev = wq->dev;
 		packet->ino = wq->ino;
-		packet->uid = from_kuid_munged(user_ns, wq->uid);
-		packet->gid = from_kgid_munged(user_ns, wq->gid);
+		packet->uid = from_kuid_tp_munged(user_ns, wq->uid);
+		packet->gid = from_kgid_tp_munged(user_ns, wq->gid);
 		packet->pid = wq->pid;
 		packet->tgid = wq->tgid;
 		break;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index a7a28110..3f9be45 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -240,10 +240,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	NEW_AUX_ENT(AT_BASE, interp_load_addr);
 	NEW_AUX_ENT(AT_FLAGS, 0);
 	NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
-	NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
-	NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
-	NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
-	NEW_AUX_ENT(AT_EGID, from_kgid_munged(cred->user_ns, cred->egid));
+	NEW_AUX_ENT(AT_UID, from_kuid_tp_munged(cred->user_ns, cred->uid));
+	NEW_AUX_ENT(AT_EUID, from_kuid_tp_munged(cred->user_ns, cred->euid));
+	NEW_AUX_ENT(AT_GID, from_kgid_tp_munged(cred->user_ns, cred->gid));
+	NEW_AUX_ENT(AT_EGID, from_kgid_tp_munged(cred->user_ns, cred->egid));
  	NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
 	NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes);
 #ifdef ELF_HWCAP2
@@ -1474,8 +1474,8 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
 	psinfo->pr_flag = p->flags;
 	rcu_read_lock();
 	cred = __task_cred(p);
-	SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
-	SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
+	SET_UID(psinfo->pr_uid, from_kuid_tp_munged(cred->user_ns, cred->uid));
+	SET_GID(psinfo->pr_gid, from_kgid_tp_munged(cred->user_ns, cred->gid));
 	rcu_read_unlock();
 	strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
 	
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 2035893..9d76bb7 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -644,10 +644,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
 	NEW_AUX_ENT(AT_BASE,	interp_params->elfhdr_addr);
 	NEW_AUX_ENT(AT_FLAGS,	0);
 	NEW_AUX_ENT(AT_ENTRY,	exec_params->entry_addr);
-	NEW_AUX_ENT(AT_UID,	(elf_addr_t) from_kuid_munged(cred->user_ns, cred->uid));
-	NEW_AUX_ENT(AT_EUID,	(elf_addr_t) from_kuid_munged(cred->user_ns, cred->euid));
-	NEW_AUX_ENT(AT_GID,	(elf_addr_t) from_kgid_munged(cred->user_ns, cred->gid));
-	NEW_AUX_ENT(AT_EGID,	(elf_addr_t) from_kgid_munged(cred->user_ns, cred->egid));
+	NEW_AUX_ENT(AT_UID,	(elf_addr_t) from_kuid_tp_munged(cred->user_ns, cred->uid));
+	NEW_AUX_ENT(AT_EUID,	(elf_addr_t) from_kuid_tp_munged(cred->user_ns, cred->euid));
+	NEW_AUX_ENT(AT_GID,	(elf_addr_t) from_kgid_tp_munged(cred->user_ns, cred->gid));
+	NEW_AUX_ENT(AT_EGID,	(elf_addr_t) from_kgid_tp_munged(cred->user_ns, cred->egid));
 	NEW_AUX_ENT(AT_SECURE,	security_bprm_secureexec(bprm));
 	NEW_AUX_ENT(AT_EXECFN,	bprm->exec);
 
@@ -1434,8 +1434,8 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
 	psinfo->pr_flag = p->flags;
 	rcu_read_lock();
 	cred = __task_cred(p);
-	SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
-	SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
+	SET_UID(psinfo->pr_uid, from_kuid_tp_munged(cred->user_ns, cred->uid));
+	SET_GID(psinfo->pr_gid, from_kgid_tp_munged(cred->user_ns, cred->gid));
 	rcu_read_unlock();
 	strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
 
diff --git a/fs/compat.c b/fs/compat.c
index be6e48b..8e3cb5d3 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -142,8 +142,8 @@ static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
 	tmp.st_nlink = stat->nlink;
 	if (tmp.st_nlink != stat->nlink)
 		return -EOVERFLOW;
-	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
-	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
+	SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
+	SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
 	tmp.st_rdev = old_encode_dev(stat->rdev);
 	if ((u64) stat->size > MAX_NON_LFS)
 		return -EOVERFLOW;
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 350a2c8..bcba367 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -225,8 +225,8 @@ static int f_getowner_uids(struct file *filp, unsigned long arg)
 	int err;
 
 	read_lock(&filp->f_owner.lock);
-	src[0] = from_kuid(user_ns, filp->f_owner.uid);
-	src[1] = from_kuid(user_ns, filp->f_owner.euid);
+	src[0] = from_kuid_tp(user_ns, filp->f_owner.uid);
+	src[1] = from_kuid_tp(user_ns, filp->f_owner.euid);
 	read_unlock(&filp->f_owner.lock);
 
 	err  = put_user(src[0], &dst[0]);
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
index 0a3f9b5..65631c2 100644
--- a/fs/ncpfs/ioctl.c
+++ b/fs/ncpfs/ioctl.c
@@ -45,7 +45,7 @@ ncp_get_fs_info(struct ncp_server * server, struct inode *inode,
 		return -EINVAL;
 	}
 	/* TODO: info.addr = server->m.serv_addr; */
-	SET_UID(info.mounted_uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
+	SET_UID(info.mounted_uid, from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid));
 	info.connection		= server->connection;
 	info.buffer_size	= server->buffer_size;
 	info.volume_number	= NCP_FINFO(inode)->volNumber;
@@ -69,7 +69,7 @@ ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode,
 		ncp_dbg(1, "info.version invalid: %d\n", info2.version);
 		return -EINVAL;
 	}
-	info2.mounted_uid   = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
+	info2.mounted_uid   = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
 	info2.connection    = server->connection;
 	info2.buffer_size   = server->buffer_size;
 	info2.volume_number = NCP_FINFO(inode)->volNumber;
@@ -135,7 +135,7 @@ ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode,
 		ncp_dbg(1, "info.version invalid: %d\n", info2.version);
 		return -EINVAL;
 	}
-	info2.mounted_uid   = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
+	info2.mounted_uid   = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
 	info2.connection    = server->connection;
 	info2.buffer_size   = server->buffer_size;
 	info2.volume_number = NCP_FINFO(inode)->volNumber;
@@ -347,21 +347,21 @@ static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg
 		{
 			u16 uid;
 
-			SET_UID(uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
+			SET_UID(uid, from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid));
 			if (put_user(uid, (u16 __user *)argp))
 				return -EFAULT;
 			return 0;
 		}
 	case NCP_IOC_GETMOUNTUID32:
 	{
-		uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
+		uid_t uid = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
 		if (put_user(uid, (u32 __user *)argp))
 			return -EFAULT;
 		return 0;
 	}
 	case NCP_IOC_GETMOUNTUID64:
 	{
-		uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
+		uid_t uid = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
 		if (put_user(uid, (u64 __user *)argp))
 			return -EFAULT;
 		return 0;
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 8a4a266..c3e7ecb 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -653,14 +653,21 @@ static void posix_acl_fix_xattr_userns(
 		return;
 
 	for (end = entry + count; entry != end; entry++) {
+		/* from_k[ug]id_tp is safe here because the callers are:
+		 *  - posix_acl_fix_xattr_from_user() calls this with
+		 *    to=init_user_ns
+		 *  - posix_acl_fix_xattr_to_user() calls this with
+		 *    to=current_user_ns() and is only used in getxattr(),
+		 *    which copies the result to the caller
+		 */
 		switch(le16_to_cpu(entry->e_tag)) {
 		case ACL_USER:
 			uid = make_kuid(from, le32_to_cpu(entry->e_id));
-			entry->e_id = cpu_to_le32(from_kuid(to, uid));
+			entry->e_id = cpu_to_le32(from_kuid_tp(to, uid));
 			break;
 		case ACL_GROUP:
 			gid = make_kgid(from, le32_to_cpu(entry->e_id));
-			entry->e_id = cpu_to_le32(from_kgid(to, gid));
+			entry->e_id = cpu_to_le32(from_kgid_tp(to, gid));
 			break;
 		default:
 			break;
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 88c7de1..a827d6e 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -198,20 +198,20 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
 		"FDSize:\t%d\nGroups:\t",
 		get_task_state(p),
 		tgid, ngid, pid_nr_ns(pid, ns), ppid, tpid,
-		from_kuid_munged(user_ns, cred->uid),
-		from_kuid_munged(user_ns, cred->euid),
-		from_kuid_munged(user_ns, cred->suid),
-		from_kuid_munged(user_ns, cred->fsuid),
-		from_kgid_munged(user_ns, cred->gid),
-		from_kgid_munged(user_ns, cred->egid),
-		from_kgid_munged(user_ns, cred->sgid),
-		from_kgid_munged(user_ns, cred->fsgid),
+		from_kuid_tp_munged(user_ns, cred->uid),
+		from_kuid_tp_munged(user_ns, cred->euid),
+		from_kuid_tp_munged(user_ns, cred->suid),
+		from_kuid_tp_munged(user_ns, cred->fsuid),
+		from_kgid_tp_munged(user_ns, cred->gid),
+		from_kgid_tp_munged(user_ns, cred->egid),
+		from_kgid_tp_munged(user_ns, cred->sgid),
+		from_kgid_tp_munged(user_ns, cred->fsgid),
 		max_fds);
 
 	group_info = cred->group_info;
 	for (g = 0; g < group_info->ngroups; g++)
 		seq_printf(m, "%d ",
-			   from_kgid_munged(user_ns, GROUP_AT(group_info, g)));
+			   from_kgid_tp_munged(user_ns, GROUP_AT(group_info, g)));
 	put_cred(cred);
 
 #ifdef CONFIG_PID_NS
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a11eb71..2203b81 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1236,7 +1236,7 @@ static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
 	if (!task)
 		return -ESRCH;
 	length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
-			   from_kuid(file->f_cred->user_ns,
+			   from_kuid_tp(file->f_cred->user_ns,
 				     audit_get_loginuid(task)));
 	put_task_struct(task);
 	return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
@@ -2744,7 +2744,8 @@ static const struct file_operations proc_projid_map_operations = {
 	.release	= proc_id_map_release,
 };
 
-static int proc_setgroups_open(struct inode *inode, struct file *file)
+static int proc_nsadmin_open(struct inode *inode, struct file *file,
+	int (*show)(struct seq_file *, void *))
 {
 	struct user_namespace *ns = NULL;
 	struct task_struct *task;
@@ -2767,7 +2768,7 @@ static int proc_setgroups_open(struct inode *inode, struct file *file)
 			goto err_put_ns;
 	}
 
-	ret = single_open(file, &proc_setgroups_show, ns);
+	ret = single_open(file, show, ns);
 	if (ret)
 		goto err_put_ns;
 
@@ -2778,7 +2779,7 @@ err:
 	return ret;
 }
 
-static int proc_setgroups_release(struct inode *inode, struct file *file)
+static int proc_nsadmin_release(struct inode *inode, struct file *file)
 {
 	struct seq_file *seq = file->private_data;
 	struct user_namespace *ns = seq->private;
@@ -2787,12 +2788,30 @@ static int proc_setgroups_release(struct inode *inode, struct file *file)
 	return ret;
 }
 
+static int proc_setgroups_open(struct inode *inode, struct file *file)
+{
+	return proc_nsadmin_open(inode, file, &proc_setgroups_show);
+}
+
 static const struct file_operations proc_setgroups_operations = {
 	.open		= proc_setgroups_open,
 	.write		= proc_setgroups_write,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
-	.release	= proc_setgroups_release,
+	.release	= proc_nsadmin_release,
+};
+
+static int proc_transparent_open(struct inode *inode, struct file *file)
+{
+	return proc_nsadmin_open(inode, file, &proc_transparent_show);
+}
+
+static const struct file_operations proc_transparent_operations = {
+	.open		= proc_transparent_open,
+	.write		= proc_transparent_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= proc_nsadmin_release,
 };
 #endif /* CONFIG_USER_NS */
 
@@ -2901,6 +2920,7 @@ static const struct pid_entry tgid_base_stuff[] = {
 	REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
 	REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
 	REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
+	REG("transparent", S_IRUGO|S_IWUSR, proc_transparent_operations),
 #endif
 #ifdef CONFIG_CHECKPOINT_RESTORE
 	REG("timers",	  S_IRUGO, proc_timers_operations),
diff --git a/fs/quota/kqid.c b/fs/quota/kqid.c
index ebc5e62..1cd3d8b 100644
--- a/fs/quota/kqid.c
+++ b/fs/quota/kqid.c
@@ -66,11 +66,15 @@ EXPORT_SYMBOL(qid_lt);
  */
 qid_t from_kqid(struct user_namespace *targ, struct kqid kqid)
 {
+	/* transparent UID/GID are okay here; this method is only
+	 * called either with targ=&init_user_ns or directly
+	 * before a copy_from_user(), with current_user_ns()
+	 */
 	switch (kqid.type) {
 	case USRQUOTA:
-		return from_kuid(targ, kqid.uid);
+		return from_kuid_tp(targ, kqid.uid);
 	case GRPQUOTA:
-		return from_kgid(targ, kqid.gid);
+		return from_kgid_tp(targ, kqid.gid);
 	case PRJQUOTA:
 		return from_kprojid(targ, kqid.projid);
 	default:
@@ -101,9 +105,9 @@ qid_t from_kqid_munged(struct user_namespace *targ, struct kqid kqid)
 {
 	switch (kqid.type) {
 	case USRQUOTA:
-		return from_kuid_munged(targ, kqid.uid);
+		return from_kuid_tp_munged(targ, kqid.uid);
 	case GRPQUOTA:
-		return from_kgid_munged(targ, kqid.gid);
+		return from_kgid_tp_munged(targ, kqid.gid);
 	case PRJQUOTA:
 		return from_kprojid_munged(targ, kqid.projid);
 	default:
diff --git a/fs/stat.c b/fs/stat.c
index bc045c7..b8cff6c 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -160,8 +160,8 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta
 	tmp.st_nlink = stat->nlink;
 	if (tmp.st_nlink != stat->nlink)
 		return -EOVERFLOW;
-	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
-	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
+	SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
+	SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
 	tmp.st_rdev = old_encode_dev(stat->rdev);
 #if BITS_PER_LONG == 32
 	if (stat->size > MAX_NON_LFS)
@@ -246,8 +246,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
 	tmp.st_nlink = stat->nlink;
 	if (tmp.st_nlink != stat->nlink)
 		return -EOVERFLOW;
-	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
-	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
+	SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
+	SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
 	tmp.st_rdev = encode_dev(stat->rdev);
 	tmp.st_size = stat->size;
 	tmp.st_atime = stat->atime.tv_sec;
@@ -381,8 +381,8 @@ static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
 #endif
 	tmp.st_mode = stat->mode;
 	tmp.st_nlink = stat->nlink;
-	tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
-	tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
+	tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
+	tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
 	tmp.st_atime = stat->atime.tv_sec;
 	tmp.st_atime_nsec = stat->atime.tv_nsec;
 	tmp.st_mtime = stat->mtime.tv_sec;
diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
index 0383552..bb7e2c5 100644
--- a/include/linux/uidgid.h
+++ b/include/linux/uidgid.h
@@ -124,8 +124,10 @@ extern kgid_t make_kgid(struct user_namespace *from, gid_t gid);
 
 extern uid_t from_kuid(struct user_namespace *to, kuid_t uid);
 extern gid_t from_kgid(struct user_namespace *to, kgid_t gid);
-extern uid_t from_kuid_munged(struct user_namespace *to, kuid_t uid);
-extern gid_t from_kgid_munged(struct user_namespace *to, kgid_t gid);
+extern uid_t from_kuid_tp(struct user_namespace *to, kuid_t uid);
+extern gid_t from_kgid_tp(struct user_namespace *to, kgid_t gid);
+extern uid_t from_kuid_tp_munged(struct user_namespace *to, kuid_t uid);
+extern gid_t from_kgid_tp_munged(struct user_namespace *to, kgid_t gid);
 
 static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid)
 {
@@ -159,17 +161,27 @@ static inline gid_t from_kgid(struct user_namespace *to, kgid_t kgid)
 	return __kgid_val(kgid);
 }
 
-static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
+static inline uid_t from_kuid_tp(struct user_namespace *to, kuid_t kuid)
 {
-	uid_t uid = from_kuid(to, kuid);
+	return __kuid_val(kuid);
+}
+
+static inline gid_t from_kgid_tp(struct user_namespace *to, kgid_t kgid)
+{
+	return __kgid_val(kgid);
+}
+
+static inline uid_t from_kuid_tp_munged(struct user_namespace *to, kuid_t kuid)
+{
+	uid_t uid = from_kuid_tp(to, kuid);
 	if (uid == (uid_t)-1)
 		uid = overflowuid;
 	return uid;
 }
 
-static inline gid_t from_kgid_munged(struct user_namespace *to, kgid_t kgid)
+static inline gid_t from_kgid_tp_munged(struct user_namespace *to, kgid_t kgid)
 {
-	gid_t gid = from_kgid(to, kgid);
+	gid_t gid = from_kgid_tp(to, kgid);
 	if (gid == (gid_t)-1)
 		gid = overflowgid;
 	return gid;
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 8297e5b..18291ac 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -28,6 +28,8 @@ struct user_namespace {
 	struct uid_gid_map	projid_map;
 	atomic_t		count;
 	struct user_namespace	*parent;
+	/* self for normal ns; first opaque parent for transparent ns */
+	struct user_namespace	*opaque;
 	int			level;
 	kuid_t			owner;
 	kgid_t			group;
@@ -71,6 +73,8 @@ extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, lo
 extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_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 ssize_t proc_transparent_write(struct file *, const char __user *, size_t, loff_t *);
+extern int proc_transparent_show(struct seq_file *m, void *v);
 extern bool userns_may_setgroups(const struct user_namespace *ns);
 #else
 
diff --git a/include/net/scm.h b/include/net/scm.h
index 59fa93c..601450a 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -121,8 +121,8 @@ static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
 		struct user_namespace *current_ns = current_user_ns();
 		struct ucred ucreds = {
 			.pid = scm->creds.pid,
-			.uid = from_kuid_munged(current_ns, scm->creds.uid),
-			.gid = from_kgid_munged(current_ns, scm->creds.gid),
+			.uid = from_kuid_tp_munged(current_ns, scm->creds.uid),
+			.gid = from_kgid_tp_munged(current_ns, scm->creds.gid),
 		};
 		put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
 	}
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index ade739f..b6de6f4 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -645,7 +645,7 @@ static void __do_notify(struct mqueue_inode_info *info)
 			rcu_read_lock();
 			sig_i.si_pid = task_tgid_nr_ns(current,
 						ns_of_pid(info->notify_owner));
-			sig_i.si_uid = from_kuid_munged(info->notify_user_ns, current_uid());
+			sig_i.si_uid = from_kuid_tp_munged(info->notify_user_ns, current_uid());
 			rcu_read_unlock();
 
 			kill_pid_info(info->notify.sigev_signo,
diff --git a/ipc/msg.c b/ipc/msg.c
index 1471db9..e49adeb 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -1052,10 +1052,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
 		   msq->q_qnum,
 		   msq->q_lspid,
 		   msq->q_lrpid,
-		   from_kuid_munged(user_ns, msq->q_perm.uid),
-		   from_kgid_munged(user_ns, msq->q_perm.gid),
-		   from_kuid_munged(user_ns, msq->q_perm.cuid),
-		   from_kgid_munged(user_ns, msq->q_perm.cgid),
+		   from_kuid_tp_munged(user_ns, msq->q_perm.uid),
+		   from_kgid_tp_munged(user_ns, msq->q_perm.gid),
+		   from_kuid_tp_munged(user_ns, msq->q_perm.cuid),
+		   from_kgid_tp_munged(user_ns, msq->q_perm.cgid),
 		   msq->q_stime,
 		   msq->q_rtime,
 		   msq->q_ctime);
diff --git a/ipc/sem.c b/ipc/sem.c
index b3757ea..99589c3 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -2208,10 +2208,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
 		   sma->sem_perm.id,
 		   sma->sem_perm.mode,
 		   sma->sem_nsems,
-		   from_kuid_munged(user_ns, sma->sem_perm.uid),
-		   from_kgid_munged(user_ns, sma->sem_perm.gid),
-		   from_kuid_munged(user_ns, sma->sem_perm.cuid),
-		   from_kgid_munged(user_ns, sma->sem_perm.cgid),
+		   from_kuid_tp_munged(user_ns, sma->sem_perm.uid),
+		   from_kgid_tp_munged(user_ns, sma->sem_perm.gid),
+		   from_kuid_tp_munged(user_ns, sma->sem_perm.cuid),
+		   from_kgid_tp_munged(user_ns, sma->sem_perm.cgid),
 		   sem_otime,
 		   sma->sem_ctime);
 
diff --git a/ipc/shm.c b/ipc/shm.c
index 1328251..599ab25 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -1392,10 +1392,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
 		   shp->shm_cprid,
 		   shp->shm_lprid,
 		   shp->shm_nattch,
-		   from_kuid_munged(user_ns, shp->shm_perm.uid),
-		   from_kgid_munged(user_ns, shp->shm_perm.gid),
-		   from_kuid_munged(user_ns, shp->shm_perm.cuid),
-		   from_kgid_munged(user_ns, shp->shm_perm.cgid),
+		   from_kuid_tp_munged(user_ns, shp->shm_perm.uid),
+		   from_kgid_tp_munged(user_ns, shp->shm_perm.gid),
+		   from_kuid_tp_munged(user_ns, shp->shm_perm.cuid),
+		   from_kgid_tp_munged(user_ns, shp->shm_perm.cgid),
 		   shp->shm_atim,
 		   shp->shm_dtim,
 		   shp->shm_ctim,
diff --git a/ipc/util.c b/ipc/util.c
index 798cad1..81b39d5 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -513,10 +513,10 @@ int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
 void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out)
 {
 	out->key	= in->key;
-	out->uid	= from_kuid_munged(current_user_ns(), in->uid);
-	out->gid	= from_kgid_munged(current_user_ns(), in->gid);
-	out->cuid	= from_kuid_munged(current_user_ns(), in->cuid);
-	out->cgid	= from_kgid_munged(current_user_ns(), in->cgid);
+	out->uid	= from_kuid_tp_munged(current_user_ns(), in->uid);
+	out->gid	= from_kgid_tp_munged(current_user_ns(), in->gid);
+	out->cuid	= from_kuid_tp_munged(current_user_ns(), in->cuid);
+	out->cgid	= from_kgid_tp_munged(current_user_ns(), in->cgid);
 	out->mode	= in->mode;
 	out->seq	= in->seq;
 }
diff --git a/kernel/acct.c b/kernel/acct.c
index 74963d1..bbfa0b9 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -489,8 +489,8 @@ static void do_acct_process(struct bsd_acct_struct *acct)
 
 	fill_ac(&ac);
 	/* we really need to bite the bullet and change layout */
-	ac.ac_uid = from_kuid_munged(file->f_cred->user_ns, orig_cred->uid);
-	ac.ac_gid = from_kgid_munged(file->f_cred->user_ns, orig_cred->gid);
+	ac.ac_uid = from_kuid_tp_munged(file->f_cred->user_ns, orig_cred->uid);
+	ac.ac_gid = from_kgid_tp_munged(file->f_cred->user_ns, orig_cred->gid);
 #if ACCT_VERSION == 1 || ACCT_VERSION == 2
 	/* backward-compatible 16 bit fields */
 	ac.ac_uid16 = ac.ac_uid;
diff --git a/kernel/exit.c b/kernel/exit.c
index 9e6e135..207e284 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -983,7 +983,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
 {
 	int state, retval, status;
 	pid_t pid = task_pid_vnr(p);
-	uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
+	uid_t uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
 	struct siginfo __user *infop;
 
 	if (!likely(wo->wo_flags & WEXITED))
@@ -1189,7 +1189,7 @@ static int wait_task_stopped(struct wait_opts *wo,
 	if (!unlikely(wo->wo_flags & WNOWAIT))
 		*p_code = 0;
 
-	uid = from_kuid_munged(current_user_ns(), task_uid(p));
+	uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
 unlock_sig:
 	spin_unlock_irq(&p->sighand->siglock);
 	if (!exit_code)
@@ -1263,7 +1263,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
 	}
 	if (!unlikely(wo->wo_flags & WNOWAIT))
 		p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
-	uid = from_kuid_munged(current_user_ns(), task_uid(p));
+	uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
 	spin_unlock_irq(&p->sighand->siglock);
 
 	pid = task_pid_vnr(p);
diff --git a/kernel/groups.c b/kernel/groups.c
index 74d431d..ec2ecf8 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -70,7 +70,7 @@ static int groups_to_user(gid_t __user *grouplist,
 
 	for (i = 0; i < count; i++) {
 		gid_t gid;
-		gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i));
+		gid = from_kgid_tp_munged(user_ns, GROUP_AT(group_info, i));
 		if (put_user(gid, grouplist+i))
 			return -EFAULT;
 	}
diff --git a/kernel/signal.c b/kernel/signal.c
index 96e9bc4..2d7e071 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -958,7 +958,7 @@ static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_str
 		return;
 
 	rcu_read_lock();
-	info->si_uid = from_kuid_munged(task_cred_xxx(t, user_ns),
+	info->si_uid = from_kuid_tp_munged(task_cred_xxx(t, user_ns),
 					make_kuid(current_user_ns(), info->si_uid));
 	rcu_read_unlock();
 }
@@ -1027,7 +1027,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
 			q->info.si_code = SI_USER;
 			q->info.si_pid = task_tgid_nr_ns(current,
 							task_active_pid_ns(t));
-			q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+			q->info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 			break;
 		case (unsigned long) SEND_SIG_PRIV:
 			q->info.si_signo = sig;
@@ -1609,7 +1609,7 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
 	 */
 	rcu_read_lock();
 	info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(tsk->parent));
-	info.si_uid = from_kuid_munged(task_cred_xxx(tsk->parent, user_ns),
+	info.si_uid = from_kuid_tp_munged(task_cred_xxx(tsk->parent, user_ns),
 				       task_uid(tsk));
 	rcu_read_unlock();
 
@@ -1695,7 +1695,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,
 	 */
 	rcu_read_lock();
 	info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(parent));
-	info.si_uid = from_kuid_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
+	info.si_uid = from_kuid_tp_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
 	rcu_read_unlock();
 
 	task_cputime(tsk, &utime, &stime);
@@ -1904,7 +1904,7 @@ static void ptrace_do_notify(int signr, int exit_code, int why)
 	info.si_signo = signr;
 	info.si_code = exit_code;
 	info.si_pid = task_pid_vnr(current);
-	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 
 	/* Let the debugger run.  */
 	ptrace_stop(exit_code, why, 1, &info);
@@ -2113,7 +2113,7 @@ static int ptrace_signal(int signr, siginfo_t *info)
 		info->si_code = SI_USER;
 		rcu_read_lock();
 		info->si_pid = task_pid_vnr(current->parent);
-		info->si_uid = from_kuid_munged(current_user_ns(),
+		info->si_uid = from_kuid_tp_munged(current_user_ns(),
 						task_uid(current->parent));
 		rcu_read_unlock();
 	}
@@ -2856,7 +2856,7 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
 	info.si_errno = 0;
 	info.si_code = SI_USER;
 	info.si_pid = task_tgid_vnr(current);
-	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 
 	return kill_something_info(sig, &info, pid);
 }
@@ -2899,7 +2899,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
 	info.si_errno = 0;
 	info.si_code = SI_TKILL;
 	info.si_pid = task_tgid_vnr(current);
-	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 
 	return do_send_specific(tgid, pid, sig, &info);
 }
diff --git a/kernel/sys.c b/kernel/sys.c
index 89d5be4..ce7833d 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -648,9 +648,9 @@ SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t _
 	int retval;
 	uid_t ruid, euid, suid;
 
-	ruid = from_kuid_munged(cred->user_ns, cred->uid);
-	euid = from_kuid_munged(cred->user_ns, cred->euid);
-	suid = from_kuid_munged(cred->user_ns, cred->suid);
+	ruid = from_kuid_tp_munged(cred->user_ns, cred->uid);
+	euid = from_kuid_tp_munged(cred->user_ns, cred->euid);
+	suid = from_kuid_tp_munged(cred->user_ns, cred->suid);
 
 	retval = put_user(ruid, ruidp);
 	if (!retval) {
@@ -722,9 +722,9 @@ SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t _
 	int retval;
 	gid_t rgid, egid, sgid;
 
-	rgid = from_kgid_munged(cred->user_ns, cred->gid);
-	egid = from_kgid_munged(cred->user_ns, cred->egid);
-	sgid = from_kgid_munged(cred->user_ns, cred->sgid);
+	rgid = from_kgid_tp_munged(cred->user_ns, cred->gid);
+	egid = from_kgid_tp_munged(cred->user_ns, cred->egid);
+	sgid = from_kgid_tp_munged(cred->user_ns, cred->sgid);
 
 	retval = put_user(rgid, rgidp);
 	if (!retval) {
@@ -751,7 +751,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
 	kuid_t kuid;
 
 	old = current_cred();
-	old_fsuid = from_kuid_munged(old->user_ns, old->fsuid);
+	old_fsuid = from_kuid_tp_munged(old->user_ns, old->fsuid);
 
 	kuid = make_kuid(old->user_ns, uid);
 	if (!uid_valid(kuid))
@@ -790,7 +790,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
 	kgid_t kgid;
 
 	old = current_cred();
-	old_fsgid = from_kgid_munged(old->user_ns, old->fsgid);
+	old_fsgid = from_kgid_tp_munged(old->user_ns, old->fsgid);
 
 	kgid = make_kgid(old->user_ns, gid);
 	if (!gid_valid(kgid))
@@ -858,25 +858,25 @@ SYSCALL_DEFINE0(getppid)
 SYSCALL_DEFINE0(getuid)
 {
 	/* Only we change this so SMP safe */
-	return from_kuid_munged(current_user_ns(), current_uid());
+	return from_kuid_tp_munged(current_user_ns(), current_uid());
 }
 
 SYSCALL_DEFINE0(geteuid)
 {
 	/* Only we change this so SMP safe */
-	return from_kuid_munged(current_user_ns(), current_euid());
+	return from_kuid_tp_munged(current_user_ns(), current_euid());
 }
 
 SYSCALL_DEFINE0(getgid)
 {
 	/* Only we change this so SMP safe */
-	return from_kgid_munged(current_user_ns(), current_gid());
+	return from_kgid_tp_munged(current_user_ns(), current_gid());
 }
 
 SYSCALL_DEFINE0(getegid)
 {
 	/* Only we change this so SMP safe */
-	return from_kgid_munged(current_user_ns(), current_egid());
+	return from_kgid_tp_munged(current_user_ns(), current_egid());
 }
 
 void do_sys_times(struct tms *tms)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8a4bd6b..d4f03ee 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -2743,7 +2743,7 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter)
 	seq_printf(m, "#    | task: %.16s-%d "
 		   "(uid:%d nice:%ld policy:%ld rt_prio:%ld)\n",
 		   data->comm, data->pid,
-		   from_kuid_munged(seq_user_ns(m), data->uid), data->nice,
+		   from_kuid_tp_munged(seq_user_ns(m), data->uid), data->nice,
 		   data->policy, data->rt_priority);
 	seq_puts(m, "#    -----------------\n");
 
diff --git a/kernel/tsacct.c b/kernel/tsacct.c
index f8e26ab..19e3a61 100644
--- a/kernel/tsacct.c
+++ b/kernel/tsacct.c
@@ -60,8 +60,8 @@ void bacct_add_tsk(struct user_namespace *user_ns,
 	stats->ac_pid	 = task_pid_nr_ns(tsk, pid_ns);
 	rcu_read_lock();
 	tcred = __task_cred(tsk);
-	stats->ac_uid	 = from_kuid_munged(user_ns, tcred->uid);
-	stats->ac_gid	 = from_kgid_munged(user_ns, tcred->gid);
+	stats->ac_uid	 = from_kuid_tp_munged(user_ns, tcred->uid);
+	stats->ac_gid	 = from_kgid_tp_munged(user_ns, tcred->gid);
 	stats->ac_ppid	 = pid_alive(tsk) ?
 		task_tgid_nr_ns(rcu_dereference(tsk->real_parent), pid_ns) : 0;
 	rcu_read_unlock();
diff --git a/kernel/uid16.c b/kernel/uid16.c
index d58cc4d..f91f270 100644
--- a/kernel/uid16.c
+++ b/kernel/uid16.c
@@ -63,9 +63,9 @@ SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euid
 	int retval;
 	old_uid_t ruid, euid, suid;
 
-	ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
-	euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
-	suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
+	ruid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->uid));
+	euid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->euid));
+	suid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->suid));
 
 	if (!(retval   = put_user(ruid, ruidp)) &&
 	    !(retval   = put_user(euid, euidp)))
@@ -87,9 +87,9 @@ SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egid
 	int retval;
 	old_gid_t rgid, egid, sgid;
 
-	rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
-	egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
-	sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
+	rgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->gid));
+	egid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->egid));
+	sgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->sgid));
 
 	if (!(retval   = put_user(rgid, rgidp)) &&
 	    !(retval   = put_user(egid, egidp)))
@@ -118,7 +118,7 @@ static int groups16_to_user(old_gid_t __user *grouplist,
 
 	for (i = 0; i < group_info->ngroups; i++) {
 		kgid = GROUP_AT(group_info, i);
-		group = high2lowgid(from_kgid_munged(user_ns, kgid));
+		group = high2lowgid(from_kgid_tp_munged(user_ns, kgid));
 		if (put_user(group, grouplist+i))
 			return -EFAULT;
 	}
@@ -198,20 +198,20 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)
 
 SYSCALL_DEFINE0(getuid16)
 {
-	return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
+	return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_uid()));
 }
 
 SYSCALL_DEFINE0(geteuid16)
 {
-	return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
+	return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_euid()));
 }
 
 SYSCALL_DEFINE0(getgid16)
 {
-	return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
+	return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_gid()));
 }
 
 SYSCALL_DEFINE0(getegid16)
 {
-	return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
+	return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_egid()));
 }
diff --git a/kernel/user.c b/kernel/user.c
index b069ccb..e1fd9e5 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -48,6 +48,7 @@ struct user_namespace init_user_ns = {
 		},
 	},
 	.count = ATOMIC_INIT(3),
+	.opaque = &init_user_ns,
 	.owner = GLOBAL_ROOT_UID,
 	.group = GLOBAL_ROOT_GID,
 	.ns.inum = PROC_USER_INIT_INO,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 9bafc21..44a7d3d 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -98,6 +98,7 @@ int create_user_ns(struct cred *new)
 	atomic_set(&ns->count, 1);
 	/* Leave the new->user_ns reference with the new user namespace. */
 	ns->parent = parent_ns;
+	ns->opaque = ns;
 	ns->level = parent_ns->level + 1;
 	ns->owner = owner;
 	ns->group = group;
@@ -249,7 +250,8 @@ EXPORT_SYMBOL(make_kuid);
  *	@kuid: The kernel internal uid to start with.
  *
  *	Map @kuid into the user-namespace specified by @targ and
- *	return the resulting uid.
+ *	return the resulting uid. This ignores transparent user
+ *	namespaces and is therefore appropriate for security checks.
  *
  *	There is always a mapping into the initial user_namespace.
  *
@@ -263,33 +265,63 @@ uid_t from_kuid(struct user_namespace *targ, kuid_t kuid)
 EXPORT_SYMBOL(from_kuid);
 
 /**
- *	from_kuid_munged - Create a uid from a kuid user-namespace pair.
+ *	from_kuid_tp - Create a uid from a kuid user-namespace pair.
+ *	@targ: The user namespace we want a uid in.
+ *	@kuid: The kernel internal uid to start with.
+ *
+ *	Map @kuid into the user-namespace specified by @targ and
+ *	return the resulting uid.
+ *
+ *	This function is *not* appropriate for security checks because
+ *	if @targ is transparent, the mappings of an ancestor namespace
+ *	are used. If you intend to do anything with the result apart from
+ *	returning it to a process in @targ, you might want to use
+ *	from_kuid() instead.
+ *
+ *	There is always a mapping into the initial user_namespace.
+ *
+ *	If @kuid is not visible in @targ (uid_t)-1 is returned.
+ */
+uid_t from_kuid_tp(struct user_namespace *targ, kuid_t kuid)
+{
+	/* Map the uid from a global kernel uid */
+	struct user_namespace *opaque = READ_ONCE(targ->opaque);
+
+	return map_id_up(&opaque->uid_map, __kuid_val(kuid));
+}
+EXPORT_SYMBOL(from_kuid_tp);
+
+/**
+ *	from_kuid_tp_munged - Create a uid from a kuid user-namespace pair.
  *	@targ: The user namespace we want a uid in.
  *	@kuid: The kernel internal uid to start with.
  *
  *	Map @kuid into the user-namespace specified by @targ and
  *	return the resulting uid.
  *
+ *	This function is *not* appropriate for security checks; see the
+ *	comment above from_kuid_tp().
+ *
  *	There is always a mapping into the initial user_namespace.
  *
- *	Unlike from_kuid from_kuid_munged never fails and always
- *	returns a valid uid.  This makes from_kuid_munged appropriate
+ *	Unlike from_kuid_tp from_kuid_tp_munged never fails and always
+ *	returns a valid uid.  This makes from_kuid_tp_munged appropriate
  *	for use in syscalls like stat and getuid where failing the
  *	system call and failing to provide a valid uid are not an
  *	options.
  *
  *	If @kuid has no mapping in @targ overflowuid is returned.
  */
-uid_t from_kuid_munged(struct user_namespace *targ, kuid_t kuid)
+uid_t from_kuid_tp_munged(struct user_namespace *targ, kuid_t kuid)
 {
 	uid_t uid;
-	uid = from_kuid(targ, kuid);
+	uid = from_kuid_tp(targ, kuid);
 
 	if (uid == (uid_t) -1)
 		uid = overflowuid;
 	return uid;
 }
-EXPORT_SYMBOL(from_kuid_munged);
+EXPORT_SYMBOL(from_kuid_tp_munged);
 
 /**
  *	make_kgid - Map a user-namespace gid pair into a kgid.
@@ -317,7 +349,8 @@ EXPORT_SYMBOL(make_kgid);
  *	@kgid: The kernel internal gid to start with.
  *
  *	Map @kgid into the user-namespace specified by @targ and
- *	return the resulting gid.
+ *	return the resulting gid. This ignores transparent user
+ *	namespaces and is therefore appropriate for security checks.
  *
  *	There is always a mapping into the initial user_namespace.
  *
@@ -331,32 +364,62 @@ gid_t from_kgid(struct user_namespace *targ, kgid_t kgid)
 EXPORT_SYMBOL(from_kgid);
 
 /**
- *	from_kgid_munged - Create a gid from a kgid user-namespace pair.
+ *	from_kgid_tp - Create a gid from a kgid user-namespace pair.
  *	@targ: The user namespace we want a gid in.
  *	@kgid: The kernel internal gid to start with.
  *
  *	Map @kgid into the user-namespace specified by @targ and
  *	return the resulting gid.
  *
+ *	This function is *not* appropriate for security checks because
+ *	if @targ is transparent, the mappings of an ancestor namespace
+ *	are used. If you intend to do anything with the result apart from
+ *	returning it to a process in @targ, you might want to use
+ *	from_kgid() instead.
+ *
  *	There is always a mapping into the initial user_namespace.
  *
- *	Unlike from_kgid from_kgid_munged never fails and always
- *	returns a valid gid.  This makes from_kgid_munged appropriate
+ *	If @kgid is not visible in @targ (gid_t)-1 is returned.
+ */
+gid_t from_kgid_tp(struct user_namespace *targ, kgid_t kgid)
+{
+	/* Map the gid from a global kernel gid */
+	struct user_namespace *opaque = READ_ONCE(targ->opaque);
+
+	return map_id_up(&opaque->gid_map, __kgid_val(kgid));
+}
+EXPORT_SYMBOL(from_kgid_tp);
+
+/**
+ *	from_kgid_tp_munged - Create a gid from a kgid user-namespace pair.
+ *	@targ: The user namespace we want a gid in.
+ *	@kgid: The kernel internal gid to start with.
+ *
+ *	Map @kgid into the user-namespace specified by @targ and
+ *	return the resulting gid.
+ *
+ *	This function is *not* appropriate for security checks; see the
+ *	comment above from_kgid_tp().
+ *
+ *	There is always a mapping into the initial user_namespace.
+ *
+ *	Unlike from_kgid_tp from_kgid_tp_munged never fails and always
+ *	returns a valid gid.  This makes from_kgid_tp_munged appropriate
  *	for use in syscalls like stat and getgid where failing the
  *	system call and failing to provide a valid gid are not options.
  *
  *	If @kgid has no mapping in @targ overflowgid is returned.
  */
-gid_t from_kgid_munged(struct user_namespace *targ, kgid_t kgid)
+gid_t from_kgid_tp_munged(struct user_namespace *targ, kgid_t kgid)
 {
 	gid_t gid;
-	gid = from_kgid(targ, kgid);
+	gid = from_kgid_tp(targ, kgid);
 
 	if (gid == (gid_t) -1)
 		gid = overflowgid;
 	return gid;
 }
-EXPORT_SYMBOL(from_kgid_munged);
+EXPORT_SYMBOL(from_kgid_tp_munged);
 
 /**
  *	make_kprojid - Map a user-namespace projid pair into a kprojid.
@@ -811,6 +874,18 @@ static bool new_idmap_permitted(const struct file *file,
 				struct uid_gid_map *new_map)
 {
 	const struct cred *cred = file->f_cred;
+	unsigned int idx;
+
+	/* Don't allow non-identity mappings in transparent namespaces. */
+	if (ns != ns->opaque) {
+		for (idx = 0; idx < new_map->nr_extents; idx++) {
+			struct uid_gid_extent *ext = &new_map->extent[idx];
+
+			if (ext->first != ext->lower_first)
+				return false;
+		}
+	}
+
 	/* Don't allow mappings that would allow anything that wouldn't
 	 * be allowed without the establishment of unprivileged mappings.
 	 */
@@ -922,6 +997,81 @@ out_unlock:
 	goto out;
 }
 
+int proc_transparent_show(struct seq_file *seq, void *v)
+{
+	struct user_namespace *ns = seq->private;
+	struct user_namespace *opaque = READ_ONCE(ns->opaque);
+
+	seq_printf(seq, "%d\n", (ns == opaque) ? 0 : 1);
+	return 0;
+}
+
+ssize_t proc_transparent_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct seq_file *seq = file->private_data;
+	struct user_namespace *ns = seq->private;
+	char kbuf[8], *pos;
+	bool transparent;
+	ssize_t ret;
+
+	/* Only allow a very narrow range of strings to be written */
+	ret = -EINVAL;
+	if ((*ppos != 0) || (count >= sizeof(kbuf)))
+		goto out;
+
+	/* What was written? */
+	ret = -EFAULT;
+	if (copy_from_user(kbuf, buf, count))
+		goto out;
+	kbuf[count] = '\0';
+	pos = kbuf;
+
+	/* What is being requested? */
+	ret = -EINVAL;
+	if (pos[0] == '1') {
+		pos += 1;
+		transparent = true;
+	} else if (pos[0] == '0') {
+		pos += 1;
+		transparent = false;
+	} else
+		goto out;
+
+	/* Verify there is not trailing junk on the line */
+	pos = skip_spaces(pos);
+	if (*pos != '\0')
+		goto out;
+
+	ret = -EPERM;
+	mutex_lock(&userns_state_mutex);
+	/* Is the requested state different from the current one? */
+	if (transparent != (ns->opaque != ns)) {
+		/* You can't turn off transparent mode. */
+		if (!transparent)
+			goto out_unlock;
+		/* If there are existing mappings, they might be
+		 * non-identity mappings. Therefore, block transparent
+		 * mode. This also prevents making the init namespace
+		 * transparent (which wouldn't work).
+		 */
+		if (ns->uid_map.nr_extents != 0 || ns->gid_map.nr_extents != 0)
+			goto out_unlock;
+		/* Okay! Make the namespace transparent. */
+		ns->opaque = ns->parent->opaque;
+	}
+	mutex_unlock(&userns_state_mutex);
+
+	/* Report a successful write */
+	*ppos = count;
+	ret = count;
+out:
+	return ret;
+out_unlock:
+	mutex_unlock(&userns_state_mutex);
+	goto out;
+}
+
 bool userns_may_setgroups(const struct user_namespace *ns)
 {
 	bool allowed;
diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c
index af46bc4..c0f45bc 100644
--- a/net/appletalk/atalk_proc.c
+++ b/net/appletalk/atalk_proc.c
@@ -184,7 +184,7 @@ static int atalk_seq_socket_show(struct seq_file *seq, void *v)
 		   sk_wmem_alloc_get(s),
 		   sk_rmem_alloc_get(s),
 		   s->sk_state,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)));
 out:
 	return 0;
 }
diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c
index 4ad2fb7..b28e339 100644
--- a/net/ax25/ax25_uid.c
+++ b/net/ax25/ax25_uid.c
@@ -81,7 +81,7 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
 		read_lock(&ax25_uid_lock);
 		ax25_uid_for_each(ax25_uid, &ax25_uid_list) {
 			if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
-				res = from_kuid_munged(current_user_ns(), ax25_uid->uid);
+				res = from_kuid_tp_munged(current_user_ns(), ax25_uid->uid);
 				break;
 			}
 		}
@@ -175,7 +175,7 @@ static int ax25_uid_seq_show(struct seq_file *seq, void *v)
 
 		pt = hlist_entry(v, struct ax25_uid_assoc, uid_node);
 		seq_printf(seq, "%6d %s\n",
-			from_kuid_munged(seq_user_ns(seq), pt->uid),
+			from_kuid_tp_munged(seq_user_ns(seq), pt->uid),
 			ax2asc(buf, &pt->call));
 	}
 	return 0;
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 3df7aef..5e9906b 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -625,7 +625,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
 			   atomic_read(&sk->sk_refcnt),
 			   sk_rmem_alloc_get(sk),
 			   sk_wmem_alloc_get(sk),
-			   from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
+			   from_kuid_tp(seq_user_ns(seq), sock_i_uid(sk)),
 			   sock_i_ino(sk),
 			   bt->parent? sock_i_ino(bt->parent): 0LU);
 
diff --git a/net/core/sock.c b/net/core/sock.c
index 08bf97e..1e6192d 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1020,8 +1020,8 @@ static void cred_to_ucred(struct pid *pid, const struct cred *cred,
 	if (cred) {
 		struct user_namespace *current_ns = current_user_ns();
 
-		ucred->uid = from_kuid_munged(current_ns, cred->euid);
-		ucred->gid = from_kgid_munged(current_ns, cred->egid);
+		ucred->uid = from_kuid_tp_munged(current_ns, cred->euid);
+		ucred->gid = from_kgid_tp_munged(current_ns, cred->egid);
 	}
 }
 
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 25af124..26f1ec2 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -134,7 +134,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
 	}
 #endif
 
-	r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
+	r->idiag_uid = from_kuid_tp_munged(user_ns, sock_i_uid(sk));
 	r->idiag_inode = sock_i_ino(sk);
 
 	return 0;
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 66ddcb6..f3b27ea 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -1121,7 +1121,7 @@ static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
 		sk_wmem_alloc_get(sp),
 		sk_rmem_alloc_get(sp),
 		0, 0L, 0,
-		from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
+		from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sp)),
 		0, sock_i_ino(sp),
 		atomic_read(&sp->sk_refcnt), sp,
 		atomic_read(&sp->sk_drops));
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 438f50c..759095ee 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -1033,7 +1033,7 @@ static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
 		sk_wmem_alloc_get(sp),
 		sk_rmem_alloc_get(sp),
 		0, 0L, 0,
-		from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
+		from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
 		0, sock_i_ino(sp),
 		atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));
 }
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 1cb67de..4e19885 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -133,8 +133,8 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write,
 	};
 
 	inet_get_ping_group_range_table(table, &low, &high);
-	urange[0] = from_kgid_munged(user_ns, low);
-	urange[1] = from_kgid_munged(user_ns, high);
+	urange[0] = from_kgid_tp_munged(user_ns, low);
+	urange[1] = from_kgid_tp_munged(user_ns, high);
 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
 	if (write && ret == 0) {
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 3708de2..5a5ae86 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2161,8 +2161,8 @@ static void get_openreq4(const struct request_sock *req,
 		1,    /* timers active (only the expire timer) */
 		jiffies_delta_to_clock_t(delta),
 		req->num_timeout,
-		from_kuid_munged(seq_user_ns(f),
-				 sock_i_uid(req->rsk_listener)),
+		from_kuid_tp_munged(seq_user_ns(f),
+				    sock_i_uid(req->rsk_listener)),
 		0,  /* non standard timer */
 		0, /* open_requests have no inode */
 		0,
@@ -2217,7 +2217,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
 		timer_active,
 		jiffies_delta_to_clock_t(timer_expires - jiffies),
 		icsk->icsk_retransmits,
-		from_kuid_munged(seq_user_ns(f), sock_i_uid(sk)),
+		from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sk)),
 		icsk->icsk_probes_out,
 		sock_i_ino(sk),
 		atomic_read(&sk->sk_refcnt), sk,
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 0ff31d9..e3579b2 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2408,7 +2408,7 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f,
 		sk_wmem_alloc_get(sp),
 		sk_rmem_alloc_get(sp),
 		0, 0L, 0,
-		from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
+		from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sp)),
 		0, sock_i_ino(sp),
 		atomic_read(&sp->sk_refcnt), sp,
 		atomic_read(&sp->sk_drops));
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 37874e2..d66dd7c 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -1028,7 +1028,7 @@ void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
 		   sk_wmem_alloc_get(sp),
 		   sk_rmem_alloc_get(sp),
 		   0, 0L, 0,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
 		   0,
 		   sock_i_ino(sp),
 		   atomic_read(&sp->sk_refcnt), sp,
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index b912f0d..a1c7516 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -789,7 +789,7 @@ static int ip6fl_seq_show(struct seq_file *seq, void *v)
 			   ((fl->share == IPV6_FL_S_PROCESS) ?
 			    pid_nr_ns(fl->owner.pid, state->pid_ns) :
 			    ((fl->share == IPV6_FL_S_USER) ?
-			     from_kuid_munged(seq_user_ns(seq), fl->owner.uid) :
+			     from_kuid_tp_munged(seq_user_ns(seq), fl->owner.uid) :
 			     0)),
 			   atomic_read(&fl->users),
 			   fl->linger/HZ,
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f36c2d0..04643ca 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1696,8 +1696,8 @@ static void get_openreq6(struct seq_file *seq,
 		   1,   /* timers active (only the expire timer) */
 		   jiffies_to_clock_t(ttd),
 		   req->num_timeout,
-		   from_kuid_munged(seq_user_ns(seq),
-				    sock_i_uid(req->rsk_listener)),
+		   from_kuid_tp_munged(seq_user_ns(seq),
+				       sock_i_uid(req->rsk_listener)),
 		   0,  /* non standard timer */
 		   0, /* open_requests have no inode */
 		   0, req);
@@ -1760,7 +1760,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
 		   timer_active,
 		   jiffies_delta_to_clock_t(timer_expires - jiffies),
 		   icsk->icsk_retransmits,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
 		   icsk->icsk_probes_out,
 		   sock_i_ino(sp),
 		   atomic_read(&sp->sk_refcnt), sp,
diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c
index c1d247e..fc1d1fe 100644
--- a/net/ipx/ipx_proc.c
+++ b/net/ipx/ipx_proc.c
@@ -217,7 +217,7 @@ static int ipx_seq_socket_show(struct seq_file *seq, void *v)
 		   sk_wmem_alloc_get(s),
 		   sk_rmem_alloc_get(s),
 		   s->sk_state,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)));
 out:
 	return 0;
 }
diff --git a/net/key/af_key.c b/net/key/af_key.c
index f9c9ecb..b76105f 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3714,7 +3714,7 @@ static int pfkey_seq_show(struct seq_file *f, void *v)
 			       atomic_read(&s->sk_refcnt),
 			       sk_rmem_alloc_get(s),
 			       sk_wmem_alloc_get(s),
-			       from_kuid_munged(seq_user_ns(f), sock_i_uid(s)),
+			       from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(s)),
 			       sock_i_ino(s)
 			       );
 	return 0;
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
index 29c509c..96d2dff 100644
--- a/net/llc/llc_proc.c
+++ b/net/llc/llc_proc.c
@@ -151,7 +151,7 @@ static int llc_seq_socket_show(struct seq_file *seq, void *v)
 		   sk_wmem_alloc_get(sk),
 		   sk_rmem_alloc_get(sk) - llc->copied_seq,
 		   sk->sk_state,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 		   llc->link);
 out:
 	return 0;
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 11f81c8..3263bca 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -552,8 +552,8 @@ __build_packet_message(struct nfnl_log_net *log,
 			struct file *file = sk->sk_socket->file;
 			const struct cred *cred = file->f_cred;
 			struct user_namespace *user_ns = inst->peer_user_ns;
-			__be32 uid = htonl(from_kuid_munged(user_ns, cred->fsuid));
-			__be32 gid = htonl(from_kgid_munged(user_ns, cred->fsgid));
+			__be32 uid = htonl(from_kuid_tp_munged(user_ns, cred->fsuid));
+			__be32 gid = htonl(from_kgid_tp_munged(user_ns, cred->fsgid));
 			read_unlock_bh(&sk->sk_callback_lock);
 			if (nla_put_be32(inst->skb, NFULA_UID, uid) ||
 			    nla_put_be32(inst->skb, NFULA_GID, gid))
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 9bff6ef..21443c8 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -4497,7 +4497,7 @@ static int packet_seq_show(struct seq_file *seq, void *v)
 			   po->ifindex,
 			   po->running,
 			   atomic_read(&s->sk_rmem_alloc),
-			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)),
+			   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)),
 			   sock_i_ino(s));
 	}
 
diff --git a/net/packet/diag.c b/net/packet/diag.c
index 0ed68f0..40b8df7 100644
--- a/net/packet/diag.c
+++ b/net/packet/diag.c
@@ -153,7 +153,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
 
 	if ((req->pdiag_show & PACKET_SHOW_INFO) &&
 	    nla_put_u32(skb, PACKET_DIAG_UID,
-			from_kuid_munged(user_ns, sock_i_uid(sk))))
+			from_kuid_tp_munged(user_ns, sock_i_uid(sk))))
 		goto out_nlmsg_trim;
 
 	if ((req->pdiag_show & PACKET_SHOW_MCLIST) &&
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index ffd5f22..fa90d85 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -610,7 +610,7 @@ static int pn_sock_seq_show(struct seq_file *seq, void *v)
 			sk->sk_protocol, pn->sobject, pn->dobject,
 			pn->resource, sk->sk_state,
 			sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
-			from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+			from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 			sock_i_ino(sk),
 			atomic_read(&sk->sk_refcnt), sk,
 			atomic_read(&sk->sk_drops));
@@ -795,7 +795,7 @@ static int pn_res_seq_show(struct seq_file *seq, void *v)
 
 		seq_printf(seq, "%02X %5u %lu",
 			   (int) (psk - pnres.sk),
-			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+			   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 			   sock_i_ino(sk));
 	}
 	seq_pad(seq, '\n');
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 4cb5aed..a893492 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -224,7 +224,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v)
 		seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5u %5lu ", ep, sk,
 			   sctp_sk(sk)->type, sk->sk_state, hash,
 			   epb->bind_addr.port,
-			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+			   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 			   sock_i_ino(sk));
 
 		sctp_seq_dump_local_addrs(seq, epb);
@@ -346,7 +346,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
 		   assoc->assoc_id,
 		   assoc->sndbuf_used,
 		   atomic_read(&assoc->rmem_alloc),
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 		   sock_i_ino(sk),
 		   epb->bind_addr.port,
 		   assoc->peer.port);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index dfacdc9..ce7a1ce 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -562,9 +562,9 @@ static int unix_gid_show(struct seq_file *m,
 	else
 		glen = 0;
 
-	seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen);
+	seq_printf(m, "%u %d:", from_kuid_tp_munged(user_ns, ug->uid), glen);
 	for (i = 0; i < glen; i++)
-		seq_printf(m, " %d", from_kgid_munged(user_ns, GROUP_AT(ug->gi, i)));
+		seq_printf(m, " %d", from_kgid_tp_munged(user_ns, GROUP_AT(ug->gi, i)));
 	seq_printf(m, "\n");
 	return 0;
 }
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index d580ad0..b88f73d 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -619,8 +619,8 @@ okay:
 	infobuf = kasprintf(GFP_KERNEL,
 			    "%s;%d;%d;%08x;",
 			    key->type->name,
-			    from_kuid_munged(current_user_ns(), key->uid),
-			    from_kgid_munged(current_user_ns(), key->gid),
+			    from_kuid_tp_munged(current_user_ns(), key->uid),
+			    from_kgid_tp_munged(current_user_ns(), key->gid),
 			    key->perm);
 	if (!infobuf)
 		goto error2;
diff --git a/security/keys/proc.c b/security/keys/proc.c
index f0611a6..f71449d 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -255,8 +255,8 @@ static int proc_keys_show(struct seq_file *m, void *v)
 		   atomic_read(&key->usage),
 		   xbuf,
 		   key->perm,
-		   from_kuid_munged(seq_user_ns(m), key->uid),
-		   from_kgid_munged(seq_user_ns(m), key->gid),
+		   from_kuid_tp_munged(seq_user_ns(m), key->uid),
+		   from_kgid_tp_munged(seq_user_ns(m), key->gid),
 		   key->type->name);
 
 #undef showflag
@@ -339,7 +339,7 @@ static int proc_key_users_show(struct seq_file *m, void *v)
 		key_quota_root_maxbytes : key_quota_maxbytes;
 
 	seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
-		   from_kuid_munged(seq_user_ns(m), user->uid),
+		   from_kuid_tp_munged(seq_user_ns(m), user->uid),
 		   atomic_read(&user->usage),
 		   atomic_read(&user->nkeys),
 		   atomic_read(&user->nikeys),
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] namespaces: add transparent user namespaces
       [not found]         ` <1466814210-3778-2-git-send-email-jannh-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
@ 2016-06-27  3:34           ` Michael Kerrisk
  0 siblings, 0 replies; 14+ messages in thread
From: Michael Kerrisk @ 2016-06-27  3:34 UTC (permalink / raw)
  To: Jann Horn
  Cc: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Eric W. Biederman,
	Andy Lutomirski, Linux Kernel, Linux API,
	Michael Kerrisk-manpages

Hi Jann,

Patches such as this really should CC linux-api@ (added).

On Sat, Jun 25, 2016 at 2:23 AM, Jann Horn <jannh@google.com> wrote:
> This allows the admin of a user namespace to mark the namespace as
> transparent. All other namespaces, by default, are opaque.
>
> While the current behavior of user namespaces is appropriate for use in
> containers, there are many programs that only use user namespaces because
> doing so enables them to do other things (e.g. unsharing the mount or
> network namespace) that require namespaced capabilities. For them, the
> inability to see the real UIDs and GIDs of things from inside the user
> namespace can be very annoying.
>
> In a transparent namespace, all UIDs and GIDs that are mapped into its
> first opaque ancestor are visible and are not remapped. This means that if
> a process e.g. stat()s the real root directory in a namespace, it will
> still see it as owned by UID 0.
>
> Traditionally, any UID or GID that was visible in a user namespace was also
> mapped into the namespace, giving the namespace admin full access to it.
> This patch introduces a distinction: In a transparent namespace, UIDs and
> GIDs can be visible without being mapped. Non-mapped, visible UIDs can be
> passed from the kernel to userspace, but userspace can't send them back to
> the kernel.

Can you explain "can't send them back to the kernel" in more detail?
(Some examples of what is and isn't possible would be helpul.)

> In order to be able to fully use specific UIDs/GIDs and gain
> privileges over them, mappings need to be set up in the usual way -
> however, to avoid aliasing problems, only identity mappings are permitted.
>
> v2:
> Ensure that all relevant from_k[ug]id callers show up in the patch.
> _transparent would be more verbose than _tp, but considering the line
> length rule, that's just too long.
>
> Yes, this makes the patch rather large.
>
> Behavior should be the same as in v1, except that I'm not touching orangefs
> in this patch because every single use of from_k[ug]id in it is wrong in
> some way. (Thanks for making me reread all that stuff, Eric.) I'll write a
> separate patch or at least report the issue with more detail later.
>
> (Also, the handling of user namespaces when dealing with signals is
> super-ugly and kind of incorrect. That should probably be cleaned up.)

I'm curious about this detail: can you say some more about the issues here?

> posix_acl_to_xattr would have changed behavior in the v1 patch, but isn't
> changed here. Because it's only used with init_user_ns, that won't change
> user-visible behavior relative to v1.
>
> This patch was compile-tested with allyesconfig. I also ran a VM with this
> patch applied and checked that it still works, but that probably doesn't
> mean much.

One of the things notably lacking from this commit message is any sort
of description of the user-space-API changes that it makes. I presume
it's a matter of some /proc files. Could you explain the changes (ad
add that detail in any further commit message)?

Thanks,

Michael

> Signed-off-by: Jann Horn <jannh@google.com>
> ---
>  arch/alpha/kernel/osf_sys.c       |   4 +-
>  arch/arm/kernel/sys_oabi-compat.c |   4 +-
>  arch/ia64/kernel/signal.c         |   4 +-
>  arch/s390/kernel/compat_linux.c   |  26 +++---
>  arch/sparc/kernel/sys_sparc32.c   |   4 +-
>  arch/x86/ia32/sys_ia32.c          |   4 +-
>  drivers/android/binder.c          |   2 +-
>  drivers/gpu/drm/drm_info.c        |   2 +-
>  drivers/gpu/drm/drm_ioctl.c       |   2 +-
>  drivers/net/tun.c                 |   4 +-
>  fs/autofs4/dev-ioctl.c            |   4 +-
>  fs/autofs4/waitq.c                |   4 +-
>  fs/binfmt_elf.c                   |  12 +--
>  fs/binfmt_elf_fdpic.c             |  12 +--
>  fs/compat.c                       |   4 +-
>  fs/fcntl.c                        |   4 +-
>  fs/ncpfs/ioctl.c                  |  12 +--
>  fs/posix_acl.c                    |  11 ++-
>  fs/proc/array.c                   |  18 ++--
>  fs/proc/base.c                    |  30 +++++--
>  fs/quota/kqid.c                   |  12 ++-
>  fs/stat.c                         |  12 +--
>  include/linux/uidgid.h            |  24 +++--
>  include/linux/user_namespace.h    |   4 +
>  include/net/scm.h                 |   4 +-
>  ipc/mqueue.c                      |   2 +-
>  ipc/msg.c                         |   8 +-
>  ipc/sem.c                         |   8 +-
>  ipc/shm.c                         |   8 +-
>  ipc/util.c                        |   8 +-
>  kernel/acct.c                     |   4 +-
>  kernel/exit.c                     |   6 +-
>  kernel/groups.c                   |   2 +-
>  kernel/signal.c                   |  16 ++--
>  kernel/sys.c                      |  24 ++---
>  kernel/trace/trace.c              |   2 +-
>  kernel/tsacct.c                   |   4 +-
>  kernel/uid16.c                    |  22 ++---
>  kernel/user.c                     |   1 +
>  kernel/user_namespace.c           | 178 +++++++++++++++++++++++++++++++++++---
>  net/appletalk/atalk_proc.c        |   2 +-
>  net/ax25/ax25_uid.c               |   4 +-
>  net/bluetooth/af_bluetooth.c      |   2 +-
>  net/core/sock.c                   |   4 +-
>  net/ipv4/inet_diag.c              |   2 +-
>  net/ipv4/ping.c                   |   2 +-
>  net/ipv4/raw.c                    |   2 +-
>  net/ipv4/sysctl_net_ipv4.c        |   4 +-
>  net/ipv4/tcp_ipv4.c               |   6 +-
>  net/ipv4/udp.c                    |   2 +-
>  net/ipv6/datagram.c               |   2 +-
>  net/ipv6/ip6_flowlabel.c          |   2 +-
>  net/ipv6/tcp_ipv6.c               |   6 +-
>  net/ipx/ipx_proc.c                |   2 +-
>  net/key/af_key.c                  |   2 +-
>  net/llc/llc_proc.c                |   2 +-
>  net/netfilter/nfnetlink_log.c     |   4 +-
>  net/packet/af_packet.c            |   2 +-
>  net/packet/diag.c                 |   2 +-
>  net/phonet/socket.c               |   4 +-
>  net/sctp/proc.c                   |   4 +-
>  net/sunrpc/svcauth_unix.c         |   4 +-
>  security/keys/keyctl.c            |   4 +-
>  security/keys/proc.c              |   6 +-
>  64 files changed, 395 insertions(+), 197 deletions(-)
>
> diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
> index ffb93f49..6440f8e 100644
> --- a/arch/alpha/kernel/osf_sys.c
> +++ b/arch/alpha/kernel/osf_sys.c
> @@ -277,8 +277,8 @@ linux_to_osf_stat(struct kstat *lstat, struct osf_stat __user *osf_stat)
>         tmp.st_dev      = lstat->dev;
>         tmp.st_mode     = lstat->mode;
>         tmp.st_nlink    = lstat->nlink;
> -       tmp.st_uid      = from_kuid_munged(current_user_ns(), lstat->uid);
> -       tmp.st_gid      = from_kgid_munged(current_user_ns(), lstat->gid);
> +       tmp.st_uid      = from_kuid_tp_munged(current_user_ns(), lstat->uid);
> +       tmp.st_gid      = from_kgid_tp_munged(current_user_ns(), lstat->gid);
>         tmp.st_rdev     = lstat->rdev;
>         tmp.st_ldev     = lstat->rdev;
>         tmp.st_size     = lstat->size;
> diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
> index 087acb5..47748eb 100644
> --- a/arch/arm/kernel/sys_oabi-compat.c
> +++ b/arch/arm/kernel/sys_oabi-compat.c
> @@ -124,8 +124,8 @@ static long cp_oldabi_stat64(struct kstat *stat,
>         tmp.__st_ino = stat->ino;
>         tmp.st_mode = stat->mode;
>         tmp.st_nlink = stat->nlink;
> -       tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
> -       tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
> +       tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
> +       tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
>         tmp.st_rdev = huge_encode_dev(stat->rdev);
>         tmp.st_size = stat->size;
>         tmp.st_blocks = stat->blocks;
> diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
> index b3a124d..071b9c0 100644
> --- a/arch/ia64/kernel/signal.c
> +++ b/arch/ia64/kernel/signal.c
> @@ -209,7 +209,7 @@ ia64_rt_sigreturn (struct sigscratch *scr)
>         si.si_errno = 0;
>         si.si_code = SI_KERNEL;
>         si.si_pid = task_pid_vnr(current);
> -       si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       si.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>         si.si_addr = sc;
>         force_sig_info(SIGSEGV, &si, current);
>         return retval;
> @@ -306,7 +306,7 @@ force_sigsegv_info (int sig, void __user *addr)
>         si.si_errno = 0;
>         si.si_code = SI_KERNEL;
>         si.si_pid = task_pid_vnr(current);
> -       si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       si.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>         si.si_addr = addr;
>         force_sig_info(SIGSEGV, &si, current);
>         return 1;
> diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
> index 437e611..31a39ba 100644
> --- a/arch/s390/kernel/compat_linux.c
> +++ b/arch/s390/kernel/compat_linux.c
> @@ -136,9 +136,9 @@ COMPAT_SYSCALL_DEFINE3(s390_getresuid16, u16 __user *, ruidp,
>         int retval;
>         u16 ruid, euid, suid;
>
> -       ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
> -       euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
> -       suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
> +       ruid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       euid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->euid));
> +       suid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->suid));
>
>         if (!(retval   = put_user(ruid, ruidp)) &&
>             !(retval   = put_user(euid, euidp)))
> @@ -160,9 +160,9 @@ COMPAT_SYSCALL_DEFINE3(s390_getresgid16, u16 __user *, rgidp,
>         int retval;
>         u16 rgid, egid, sgid;
>
> -       rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
> -       egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
> -       sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
> +       rgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->gid));
> +       egid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->egid));
> +       sgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->sgid));
>
>         if (!(retval   = put_user(rgid, rgidp)) &&
>             !(retval   = put_user(egid, egidp)))
> @@ -190,7 +190,7 @@ static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info
>
>         for (i = 0; i < group_info->ngroups; i++) {
>                 kgid = GROUP_AT(group_info, i);
> -               group = (u16)from_kgid_munged(user_ns, kgid);
> +               group = (u16)from_kgid_tp_munged(user_ns, kgid);
>                 if (put_user(group, grouplist+i))
>                         return -EFAULT;
>         }
> @@ -271,22 +271,22 @@ COMPAT_SYSCALL_DEFINE2(s390_setgroups16, int, gidsetsize, u16 __user *, grouplis
>
>  COMPAT_SYSCALL_DEFINE0(s390_getuid16)
>  {
> -       return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
> +       return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_uid()));
>  }
>
>  COMPAT_SYSCALL_DEFINE0(s390_geteuid16)
>  {
> -       return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
> +       return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_euid()));
>  }
>
>  COMPAT_SYSCALL_DEFINE0(s390_getgid16)
>  {
> -       return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
> +       return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_gid()));
>  }
>
>  COMPAT_SYSCALL_DEFINE0(s390_getegid16)
>  {
> -       return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
> +       return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_egid()));
>  }
>
>  #ifdef CONFIG_SYSVIPC
> @@ -366,8 +366,8 @@ static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat)
>         tmp.__st_ino = (u32)stat->ino;
>         tmp.st_mode = stat->mode;
>         tmp.st_nlink = (unsigned int)stat->nlink;
> -       tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
> -       tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
> +       tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
> +       tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
>         tmp.st_rdev = huge_encode_dev(stat->rdev);
>         tmp.st_size = stat->size;
>         tmp.st_blksize = (u32)stat->blksize;
> diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
> index 022c30c..e65acab 100644
> --- a/arch/sparc/kernel/sys_sparc32.c
> +++ b/arch/sparc/kernel/sys_sparc32.c
> @@ -76,8 +76,8 @@ static int cp_compat_stat64(struct kstat *stat,
>         err |= put_user(stat->ino, &statbuf->st_ino);
>         err |= put_user(stat->mode, &statbuf->st_mode);
>         err |= put_user(stat->nlink, &statbuf->st_nlink);
> -       err |= put_user(from_kuid_munged(current_user_ns(), stat->uid), &statbuf->st_uid);
> -       err |= put_user(from_kgid_munged(current_user_ns(), stat->gid), &statbuf->st_gid);
> +       err |= put_user(from_kuid_tp_munged(current_user_ns(), stat->uid), &statbuf->st_uid);
> +       err |= put_user(from_kgid_tp_munged(current_user_ns(), stat->gid), &statbuf->st_gid);
>         err |= put_user(huge_encode_dev(stat->rdev), &statbuf->st_rdev);
>         err |= put_user(0, (unsigned long __user *) &statbuf->__pad3[0]);
>         err |= put_user(stat->size, &statbuf->st_size);
> diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
> index 719cd70..e8d4532 100644
> --- a/arch/x86/ia32/sys_ia32.c
> +++ b/arch/x86/ia32/sys_ia32.c
> @@ -71,8 +71,8 @@ static int cp_stat64(struct stat64 __user *ubuf, struct kstat *stat)
>  {
>         typeof(ubuf->st_uid) uid = 0;
>         typeof(ubuf->st_gid) gid = 0;
> -       SET_UID(uid, from_kuid_munged(current_user_ns(), stat->uid));
> -       SET_GID(gid, from_kgid_munged(current_user_ns(), stat->gid));
> +       SET_UID(uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
> +       SET_GID(gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
>         if (!access_ok(VERIFY_WRITE, ubuf, sizeof(struct stat64)) ||
>             __put_user(huge_encode_dev(stat->dev), &ubuf->st_dev) ||
>             __put_user(stat->ino, &ubuf->__st_ino) ||
> diff --git a/drivers/android/binder.c b/drivers/android/binder.c
> index 16288e7..c8fcf71 100644
> --- a/drivers/android/binder.c
> +++ b/drivers/android/binder.c
> @@ -2400,7 +2400,7 @@ retry:
>                 }
>                 tr.code = t->code;
>                 tr.flags = t->flags;
> -               tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
> +               tr.sender_euid = from_kuid_tp(current_user_ns(), t->sender_euid);
>
>                 if (t->from) {
>                         struct task_struct *sender = t->from->proc->tsk;
> diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
> index 5d469b2..07ab3d4 100644
> --- a/drivers/gpu/drm/drm_info.c
> +++ b/drivers/gpu/drm/drm_info.c
> @@ -186,7 +186,7 @@ int drm_clients_info(struct seq_file *m, void *data)
>                            priv->minor->index,
>                            priv->is_master ? 'y' : 'n',
>                            priv->authenticated ? 'y' : 'n',
> -                          from_kuid_munged(seq_user_ns(m), priv->uid),
> +                          from_kuid_tp_munged(seq_user_ns(m), priv->uid),
>                            priv->magic);
>                 rcu_read_unlock();
>         }
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index b7a39771c..2e18837 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -181,7 +181,7 @@ static int drm_getclient(struct drm_device *dev, void *data,
>         if (client->idx == 0) {
>                 client->auth = file_priv->authenticated;
>                 client->pid = pid_vnr(file_priv->pid);
> -               client->uid = from_kuid_munged(current_user_ns(),
> +               client->uid = from_kuid_tp_munged(current_user_ns(),
>                                                file_priv->uid);
>                 client->magic = 0;
>                 client->iocs = 0;
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index e16487c..8965a26 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -1659,7 +1659,7 @@ static ssize_t tun_show_owner(struct device *dev, struct device_attribute *attr,
>         struct tun_struct *tun = netdev_priv(to_net_dev(dev));
>         return uid_valid(tun->owner)?
>                 sprintf(buf, "%u\n",
> -                       from_kuid_munged(current_user_ns(), tun->owner)):
> +                       from_kuid_tp_munged(current_user_ns(), tun->owner)) :
>                 sprintf(buf, "-1\n");
>  }
>
> @@ -1669,7 +1669,7 @@ static ssize_t tun_show_group(struct device *dev, struct device_attribute *attr,
>         struct tun_struct *tun = netdev_priv(to_net_dev(dev));
>         return gid_valid(tun->group) ?
>                 sprintf(buf, "%u\n",
> -                       from_kgid_munged(current_user_ns(), tun->group)):
> +                       from_kgid_tp_munged(current_user_ns(), tun->group)) :
>                 sprintf(buf, "-1\n");
>  }
>
> diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
> index c7fcc74..4c75a4c 100644
> --- a/fs/autofs4/dev-ioctl.c
> +++ b/fs/autofs4/dev-ioctl.c
> @@ -460,9 +460,9 @@ static int autofs_dev_ioctl_requester(struct file *fp,
>                 autofs4_expire_wait(path.dentry, 0);
>                 spin_lock(&sbi->fs_lock);
>                 param->requester.uid =
> -                       from_kuid_munged(current_user_ns(), ino->uid);
> +                       from_kuid_tp_munged(current_user_ns(), ino->uid);
>                 param->requester.gid =
> -                       from_kgid_munged(current_user_ns(), ino->gid);
> +                       from_kgid_tp_munged(current_user_ns(), ino->gid);
>                 spin_unlock(&sbi->fs_lock);
>         }
>         path_put(&path);
> diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
> index 0146d91..7d55752 100644
> --- a/fs/autofs4/waitq.c
> +++ b/fs/autofs4/waitq.c
> @@ -157,8 +157,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
>                 packet->name[wq->name.len] = '\0';
>                 packet->dev = wq->dev;
>                 packet->ino = wq->ino;
> -               packet->uid = from_kuid_munged(user_ns, wq->uid);
> -               packet->gid = from_kgid_munged(user_ns, wq->gid);
> +               packet->uid = from_kuid_tp_munged(user_ns, wq->uid);
> +               packet->gid = from_kgid_tp_munged(user_ns, wq->gid);
>                 packet->pid = wq->pid;
>                 packet->tgid = wq->tgid;
>                 break;
> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
> index a7a28110..3f9be45 100644
> --- a/fs/binfmt_elf.c
> +++ b/fs/binfmt_elf.c
> @@ -240,10 +240,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
>         NEW_AUX_ENT(AT_BASE, interp_load_addr);
>         NEW_AUX_ENT(AT_FLAGS, 0);
>         NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
> -       NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
> -       NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
> -       NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
> -       NEW_AUX_ENT(AT_EGID, from_kgid_munged(cred->user_ns, cred->egid));
> +       NEW_AUX_ENT(AT_UID, from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       NEW_AUX_ENT(AT_EUID, from_kuid_tp_munged(cred->user_ns, cred->euid));
> +       NEW_AUX_ENT(AT_GID, from_kgid_tp_munged(cred->user_ns, cred->gid));
> +       NEW_AUX_ENT(AT_EGID, from_kgid_tp_munged(cred->user_ns, cred->egid));
>         NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
>         NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes);
>  #ifdef ELF_HWCAP2
> @@ -1474,8 +1474,8 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
>         psinfo->pr_flag = p->flags;
>         rcu_read_lock();
>         cred = __task_cred(p);
> -       SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
> -       SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
> +       SET_UID(psinfo->pr_uid, from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       SET_GID(psinfo->pr_gid, from_kgid_tp_munged(cred->user_ns, cred->gid));
>         rcu_read_unlock();
>         strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
>
> diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
> index 2035893..9d76bb7 100644
> --- a/fs/binfmt_elf_fdpic.c
> +++ b/fs/binfmt_elf_fdpic.c
> @@ -644,10 +644,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
>         NEW_AUX_ENT(AT_BASE,    interp_params->elfhdr_addr);
>         NEW_AUX_ENT(AT_FLAGS,   0);
>         NEW_AUX_ENT(AT_ENTRY,   exec_params->entry_addr);
> -       NEW_AUX_ENT(AT_UID,     (elf_addr_t) from_kuid_munged(cred->user_ns, cred->uid));
> -       NEW_AUX_ENT(AT_EUID,    (elf_addr_t) from_kuid_munged(cred->user_ns, cred->euid));
> -       NEW_AUX_ENT(AT_GID,     (elf_addr_t) from_kgid_munged(cred->user_ns, cred->gid));
> -       NEW_AUX_ENT(AT_EGID,    (elf_addr_t) from_kgid_munged(cred->user_ns, cred->egid));
> +       NEW_AUX_ENT(AT_UID,     (elf_addr_t) from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       NEW_AUX_ENT(AT_EUID,    (elf_addr_t) from_kuid_tp_munged(cred->user_ns, cred->euid));
> +       NEW_AUX_ENT(AT_GID,     (elf_addr_t) from_kgid_tp_munged(cred->user_ns, cred->gid));
> +       NEW_AUX_ENT(AT_EGID,    (elf_addr_t) from_kgid_tp_munged(cred->user_ns, cred->egid));
>         NEW_AUX_ENT(AT_SECURE,  security_bprm_secureexec(bprm));
>         NEW_AUX_ENT(AT_EXECFN,  bprm->exec);
>
> @@ -1434,8 +1434,8 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
>         psinfo->pr_flag = p->flags;
>         rcu_read_lock();
>         cred = __task_cred(p);
> -       SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
> -       SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
> +       SET_UID(psinfo->pr_uid, from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       SET_GID(psinfo->pr_gid, from_kgid_tp_munged(cred->user_ns, cred->gid));
>         rcu_read_unlock();
>         strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
>
> diff --git a/fs/compat.c b/fs/compat.c
> index be6e48b..8e3cb5d3 100644
> --- a/fs/compat.c
> +++ b/fs/compat.c
> @@ -142,8 +142,8 @@ static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
>         tmp.st_nlink = stat->nlink;
>         if (tmp.st_nlink != stat->nlink)
>                 return -EOVERFLOW;
> -       SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
> -       SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
> +       SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
> +       SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
>         tmp.st_rdev = old_encode_dev(stat->rdev);
>         if ((u64) stat->size > MAX_NON_LFS)
>                 return -EOVERFLOW;
> diff --git a/fs/fcntl.c b/fs/fcntl.c
> index 350a2c8..bcba367 100644
> --- a/fs/fcntl.c
> +++ b/fs/fcntl.c
> @@ -225,8 +225,8 @@ static int f_getowner_uids(struct file *filp, unsigned long arg)
>         int err;
>
>         read_lock(&filp->f_owner.lock);
> -       src[0] = from_kuid(user_ns, filp->f_owner.uid);
> -       src[1] = from_kuid(user_ns, filp->f_owner.euid);
> +       src[0] = from_kuid_tp(user_ns, filp->f_owner.uid);
> +       src[1] = from_kuid_tp(user_ns, filp->f_owner.euid);
>         read_unlock(&filp->f_owner.lock);
>
>         err  = put_user(src[0], &dst[0]);
> diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
> index 0a3f9b5..65631c2 100644
> --- a/fs/ncpfs/ioctl.c
> +++ b/fs/ncpfs/ioctl.c
> @@ -45,7 +45,7 @@ ncp_get_fs_info(struct ncp_server * server, struct inode *inode,
>                 return -EINVAL;
>         }
>         /* TODO: info.addr = server->m.serv_addr; */
> -       SET_UID(info.mounted_uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
> +       SET_UID(info.mounted_uid, from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid));
>         info.connection         = server->connection;
>         info.buffer_size        = server->buffer_size;
>         info.volume_number      = NCP_FINFO(inode)->volNumber;
> @@ -69,7 +69,7 @@ ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode,
>                 ncp_dbg(1, "info.version invalid: %d\n", info2.version);
>                 return -EINVAL;
>         }
> -       info2.mounted_uid   = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
> +       info2.mounted_uid   = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
>         info2.connection    = server->connection;
>         info2.buffer_size   = server->buffer_size;
>         info2.volume_number = NCP_FINFO(inode)->volNumber;
> @@ -135,7 +135,7 @@ ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode,
>                 ncp_dbg(1, "info.version invalid: %d\n", info2.version);
>                 return -EINVAL;
>         }
> -       info2.mounted_uid   = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
> +       info2.mounted_uid   = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
>         info2.connection    = server->connection;
>         info2.buffer_size   = server->buffer_size;
>         info2.volume_number = NCP_FINFO(inode)->volNumber;
> @@ -347,21 +347,21 @@ static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg
>                 {
>                         u16 uid;
>
> -                       SET_UID(uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
> +                       SET_UID(uid, from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid));
>                         if (put_user(uid, (u16 __user *)argp))
>                                 return -EFAULT;
>                         return 0;
>                 }
>         case NCP_IOC_GETMOUNTUID32:
>         {
> -               uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
> +               uid_t uid = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
>                 if (put_user(uid, (u32 __user *)argp))
>                         return -EFAULT;
>                 return 0;
>         }
>         case NCP_IOC_GETMOUNTUID64:
>         {
> -               uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
> +               uid_t uid = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
>                 if (put_user(uid, (u64 __user *)argp))
>                         return -EFAULT;
>                 return 0;
> diff --git a/fs/posix_acl.c b/fs/posix_acl.c
> index 8a4a266..c3e7ecb 100644
> --- a/fs/posix_acl.c
> +++ b/fs/posix_acl.c
> @@ -653,14 +653,21 @@ static void posix_acl_fix_xattr_userns(
>                 return;
>
>         for (end = entry + count; entry != end; entry++) {
> +               /* from_k[ug]id_tp is safe here because the callers are:
> +                *  - posix_acl_fix_xattr_from_user() calls this with
> +                *    to=init_user_ns
> +                *  - posix_acl_fix_xattr_to_user() calls this with
> +                *    to=current_user_ns() and is only used in getxattr(),
> +                *    which copies the result to the caller
> +                */
>                 switch(le16_to_cpu(entry->e_tag)) {
>                 case ACL_USER:
>                         uid = make_kuid(from, le32_to_cpu(entry->e_id));
> -                       entry->e_id = cpu_to_le32(from_kuid(to, uid));
> +                       entry->e_id = cpu_to_le32(from_kuid_tp(to, uid));
>                         break;
>                 case ACL_GROUP:
>                         gid = make_kgid(from, le32_to_cpu(entry->e_id));
> -                       entry->e_id = cpu_to_le32(from_kgid(to, gid));
> +                       entry->e_id = cpu_to_le32(from_kgid_tp(to, gid));
>                         break;
>                 default:
>                         break;
> diff --git a/fs/proc/array.c b/fs/proc/array.c
> index 88c7de1..a827d6e 100644
> --- a/fs/proc/array.c
> +++ b/fs/proc/array.c
> @@ -198,20 +198,20 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
>                 "FDSize:\t%d\nGroups:\t",
>                 get_task_state(p),
>                 tgid, ngid, pid_nr_ns(pid, ns), ppid, tpid,
> -               from_kuid_munged(user_ns, cred->uid),
> -               from_kuid_munged(user_ns, cred->euid),
> -               from_kuid_munged(user_ns, cred->suid),
> -               from_kuid_munged(user_ns, cred->fsuid),
> -               from_kgid_munged(user_ns, cred->gid),
> -               from_kgid_munged(user_ns, cred->egid),
> -               from_kgid_munged(user_ns, cred->sgid),
> -               from_kgid_munged(user_ns, cred->fsgid),
> +               from_kuid_tp_munged(user_ns, cred->uid),
> +               from_kuid_tp_munged(user_ns, cred->euid),
> +               from_kuid_tp_munged(user_ns, cred->suid),
> +               from_kuid_tp_munged(user_ns, cred->fsuid),
> +               from_kgid_tp_munged(user_ns, cred->gid),
> +               from_kgid_tp_munged(user_ns, cred->egid),
> +               from_kgid_tp_munged(user_ns, cred->sgid),
> +               from_kgid_tp_munged(user_ns, cred->fsgid),
>                 max_fds);
>
>         group_info = cred->group_info;
>         for (g = 0; g < group_info->ngroups; g++)
>                 seq_printf(m, "%d ",
> -                          from_kgid_munged(user_ns, GROUP_AT(group_info, g)));
> +                          from_kgid_tp_munged(user_ns, GROUP_AT(group_info, g)));
>         put_cred(cred);
>
>  #ifdef CONFIG_PID_NS
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index a11eb71..2203b81 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -1236,7 +1236,7 @@ static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
>         if (!task)
>                 return -ESRCH;
>         length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
> -                          from_kuid(file->f_cred->user_ns,
> +                          from_kuid_tp(file->f_cred->user_ns,
>                                      audit_get_loginuid(task)));
>         put_task_struct(task);
>         return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
> @@ -2744,7 +2744,8 @@ static const struct file_operations proc_projid_map_operations = {
>         .release        = proc_id_map_release,
>  };
>
> -static int proc_setgroups_open(struct inode *inode, struct file *file)
> +static int proc_nsadmin_open(struct inode *inode, struct file *file,
> +       int (*show)(struct seq_file *, void *))
>  {
>         struct user_namespace *ns = NULL;
>         struct task_struct *task;
> @@ -2767,7 +2768,7 @@ static int proc_setgroups_open(struct inode *inode, struct file *file)
>                         goto err_put_ns;
>         }
>
> -       ret = single_open(file, &proc_setgroups_show, ns);
> +       ret = single_open(file, show, ns);
>         if (ret)
>                 goto err_put_ns;
>
> @@ -2778,7 +2779,7 @@ err:
>         return ret;
>  }
>
> -static int proc_setgroups_release(struct inode *inode, struct file *file)
> +static int proc_nsadmin_release(struct inode *inode, struct file *file)
>  {
>         struct seq_file *seq = file->private_data;
>         struct user_namespace *ns = seq->private;
> @@ -2787,12 +2788,30 @@ static int proc_setgroups_release(struct inode *inode, struct file *file)
>         return ret;
>  }
>
> +static int proc_setgroups_open(struct inode *inode, struct file *file)
> +{
> +       return proc_nsadmin_open(inode, file, &proc_setgroups_show);
> +}
> +
>  static const struct file_operations proc_setgroups_operations = {
>         .open           = proc_setgroups_open,
>         .write          = proc_setgroups_write,
>         .read           = seq_read,
>         .llseek         = seq_lseek,
> -       .release        = proc_setgroups_release,
> +       .release        = proc_nsadmin_release,
> +};
> +
> +static int proc_transparent_open(struct inode *inode, struct file *file)
> +{
> +       return proc_nsadmin_open(inode, file, &proc_transparent_show);
> +}
> +
> +static const struct file_operations proc_transparent_operations = {
> +       .open           = proc_transparent_open,
> +       .write          = proc_transparent_write,
> +       .read           = seq_read,
> +       .llseek         = seq_lseek,
> +       .release        = proc_nsadmin_release,
>  };
>  #endif /* CONFIG_USER_NS */
>
> @@ -2901,6 +2920,7 @@ static const struct pid_entry tgid_base_stuff[] = {
>         REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
>         REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
>         REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
> +       REG("transparent", S_IRUGO|S_IWUSR, proc_transparent_operations),
>  #endif
>  #ifdef CONFIG_CHECKPOINT_RESTORE
>         REG("timers",     S_IRUGO, proc_timers_operations),
> diff --git a/fs/quota/kqid.c b/fs/quota/kqid.c
> index ebc5e62..1cd3d8b 100644
> --- a/fs/quota/kqid.c
> +++ b/fs/quota/kqid.c
> @@ -66,11 +66,15 @@ EXPORT_SYMBOL(qid_lt);
>   */
>  qid_t from_kqid(struct user_namespace *targ, struct kqid kqid)
>  {
> +       /* transparent UID/GID are okay here; this method is only
> +        * called either with targ=&init_user_ns or directly
> +        * before a copy_from_user(), with current_user_ns()
> +        */
>         switch (kqid.type) {
>         case USRQUOTA:
> -               return from_kuid(targ, kqid.uid);
> +               return from_kuid_tp(targ, kqid.uid);
>         case GRPQUOTA:
> -               return from_kgid(targ, kqid.gid);
> +               return from_kgid_tp(targ, kqid.gid);
>         case PRJQUOTA:
>                 return from_kprojid(targ, kqid.projid);
>         default:
> @@ -101,9 +105,9 @@ qid_t from_kqid_munged(struct user_namespace *targ, struct kqid kqid)
>  {
>         switch (kqid.type) {
>         case USRQUOTA:
> -               return from_kuid_munged(targ, kqid.uid);
> +               return from_kuid_tp_munged(targ, kqid.uid);
>         case GRPQUOTA:
> -               return from_kgid_munged(targ, kqid.gid);
> +               return from_kgid_tp_munged(targ, kqid.gid);
>         case PRJQUOTA:
>                 return from_kprojid_munged(targ, kqid.projid);
>         default:
> diff --git a/fs/stat.c b/fs/stat.c
> index bc045c7..b8cff6c 100644
> --- a/fs/stat.c
> +++ b/fs/stat.c
> @@ -160,8 +160,8 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta
>         tmp.st_nlink = stat->nlink;
>         if (tmp.st_nlink != stat->nlink)
>                 return -EOVERFLOW;
> -       SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
> -       SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
> +       SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
> +       SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
>         tmp.st_rdev = old_encode_dev(stat->rdev);
>  #if BITS_PER_LONG == 32
>         if (stat->size > MAX_NON_LFS)
> @@ -246,8 +246,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
>         tmp.st_nlink = stat->nlink;
>         if (tmp.st_nlink != stat->nlink)
>                 return -EOVERFLOW;
> -       SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
> -       SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
> +       SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
> +       SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
>         tmp.st_rdev = encode_dev(stat->rdev);
>         tmp.st_size = stat->size;
>         tmp.st_atime = stat->atime.tv_sec;
> @@ -381,8 +381,8 @@ static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
>  #endif
>         tmp.st_mode = stat->mode;
>         tmp.st_nlink = stat->nlink;
> -       tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
> -       tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
> +       tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
> +       tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
>         tmp.st_atime = stat->atime.tv_sec;
>         tmp.st_atime_nsec = stat->atime.tv_nsec;
>         tmp.st_mtime = stat->mtime.tv_sec;
> diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
> index 0383552..bb7e2c5 100644
> --- a/include/linux/uidgid.h
> +++ b/include/linux/uidgid.h
> @@ -124,8 +124,10 @@ extern kgid_t make_kgid(struct user_namespace *from, gid_t gid);
>
>  extern uid_t from_kuid(struct user_namespace *to, kuid_t uid);
>  extern gid_t from_kgid(struct user_namespace *to, kgid_t gid);
> -extern uid_t from_kuid_munged(struct user_namespace *to, kuid_t uid);
> -extern gid_t from_kgid_munged(struct user_namespace *to, kgid_t gid);
> +extern uid_t from_kuid_tp(struct user_namespace *to, kuid_t uid);
> +extern gid_t from_kgid_tp(struct user_namespace *to, kgid_t gid);
> +extern uid_t from_kuid_tp_munged(struct user_namespace *to, kuid_t uid);
> +extern gid_t from_kgid_tp_munged(struct user_namespace *to, kgid_t gid);
>
>  static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid)
>  {
> @@ -159,17 +161,27 @@ static inline gid_t from_kgid(struct user_namespace *to, kgid_t kgid)
>         return __kgid_val(kgid);
>  }
>
> -static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
> +static inline uid_t from_kuid_tp(struct user_namespace *to, kuid_t kuid)
>  {
> -       uid_t uid = from_kuid(to, kuid);
> +       return __kuid_val(kuid);
> +}
> +
> +static inline gid_t from_kgid_tp(struct user_namespace *to, kgid_t kgid)
> +{
> +       return __kgid_val(kgid);
> +}
> +
> +static inline uid_t from_kuid_tp_munged(struct user_namespace *to, kuid_t kuid)
> +{
> +       uid_t uid = from_kuid_tp(to, kuid);
>         if (uid == (uid_t)-1)
>                 uid = overflowuid;
>         return uid;
>  }
>
> -static inline gid_t from_kgid_munged(struct user_namespace *to, kgid_t kgid)
> +static inline gid_t from_kgid_tp_munged(struct user_namespace *to, kgid_t kgid)
>  {
> -       gid_t gid = from_kgid(to, kgid);
> +       gid_t gid = from_kgid_tp(to, kgid);
>         if (gid == (gid_t)-1)
>                 gid = overflowgid;
>         return gid;
> diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
> index 8297e5b..18291ac 100644
> --- a/include/linux/user_namespace.h
> +++ b/include/linux/user_namespace.h
> @@ -28,6 +28,8 @@ struct user_namespace {
>         struct uid_gid_map      projid_map;
>         atomic_t                count;
>         struct user_namespace   *parent;
> +       /* self for normal ns; first opaque parent for transparent ns */
> +       struct user_namespace   *opaque;
>         int                     level;
>         kuid_t                  owner;
>         kgid_t                  group;
> @@ -71,6 +73,8 @@ extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, lo
>  extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_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 ssize_t proc_transparent_write(struct file *, const char __user *, size_t, loff_t *);
> +extern int proc_transparent_show(struct seq_file *m, void *v);
>  extern bool userns_may_setgroups(const struct user_namespace *ns);
>  #else
>
> diff --git a/include/net/scm.h b/include/net/scm.h
> index 59fa93c..601450a 100644
> --- a/include/net/scm.h
> +++ b/include/net/scm.h
> @@ -121,8 +121,8 @@ static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
>                 struct user_namespace *current_ns = current_user_ns();
>                 struct ucred ucreds = {
>                         .pid = scm->creds.pid,
> -                       .uid = from_kuid_munged(current_ns, scm->creds.uid),
> -                       .gid = from_kgid_munged(current_ns, scm->creds.gid),
> +                       .uid = from_kuid_tp_munged(current_ns, scm->creds.uid),
> +                       .gid = from_kgid_tp_munged(current_ns, scm->creds.gid),
>                 };
>                 put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
>         }
> diff --git a/ipc/mqueue.c b/ipc/mqueue.c
> index ade739f..b6de6f4 100644
> --- a/ipc/mqueue.c
> +++ b/ipc/mqueue.c
> @@ -645,7 +645,7 @@ static void __do_notify(struct mqueue_inode_info *info)
>                         rcu_read_lock();
>                         sig_i.si_pid = task_tgid_nr_ns(current,
>                                                 ns_of_pid(info->notify_owner));
> -                       sig_i.si_uid = from_kuid_munged(info->notify_user_ns, current_uid());
> +                       sig_i.si_uid = from_kuid_tp_munged(info->notify_user_ns, current_uid());
>                         rcu_read_unlock();
>
>                         kill_pid_info(info->notify.sigev_signo,
> diff --git a/ipc/msg.c b/ipc/msg.c
> index 1471db9..e49adeb 100644
> --- a/ipc/msg.c
> +++ b/ipc/msg.c
> @@ -1052,10 +1052,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
>                    msq->q_qnum,
>                    msq->q_lspid,
>                    msq->q_lrpid,
> -                  from_kuid_munged(user_ns, msq->q_perm.uid),
> -                  from_kgid_munged(user_ns, msq->q_perm.gid),
> -                  from_kuid_munged(user_ns, msq->q_perm.cuid),
> -                  from_kgid_munged(user_ns, msq->q_perm.cgid),
> +                  from_kuid_tp_munged(user_ns, msq->q_perm.uid),
> +                  from_kgid_tp_munged(user_ns, msq->q_perm.gid),
> +                  from_kuid_tp_munged(user_ns, msq->q_perm.cuid),
> +                  from_kgid_tp_munged(user_ns, msq->q_perm.cgid),
>                    msq->q_stime,
>                    msq->q_rtime,
>                    msq->q_ctime);
> diff --git a/ipc/sem.c b/ipc/sem.c
> index b3757ea..99589c3 100644
> --- a/ipc/sem.c
> +++ b/ipc/sem.c
> @@ -2208,10 +2208,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
>                    sma->sem_perm.id,
>                    sma->sem_perm.mode,
>                    sma->sem_nsems,
> -                  from_kuid_munged(user_ns, sma->sem_perm.uid),
> -                  from_kgid_munged(user_ns, sma->sem_perm.gid),
> -                  from_kuid_munged(user_ns, sma->sem_perm.cuid),
> -                  from_kgid_munged(user_ns, sma->sem_perm.cgid),
> +                  from_kuid_tp_munged(user_ns, sma->sem_perm.uid),
> +                  from_kgid_tp_munged(user_ns, sma->sem_perm.gid),
> +                  from_kuid_tp_munged(user_ns, sma->sem_perm.cuid),
> +                  from_kgid_tp_munged(user_ns, sma->sem_perm.cgid),
>                    sem_otime,
>                    sma->sem_ctime);
>
> diff --git a/ipc/shm.c b/ipc/shm.c
> index 1328251..599ab25 100644
> --- a/ipc/shm.c
> +++ b/ipc/shm.c
> @@ -1392,10 +1392,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
>                    shp->shm_cprid,
>                    shp->shm_lprid,
>                    shp->shm_nattch,
> -                  from_kuid_munged(user_ns, shp->shm_perm.uid),
> -                  from_kgid_munged(user_ns, shp->shm_perm.gid),
> -                  from_kuid_munged(user_ns, shp->shm_perm.cuid),
> -                  from_kgid_munged(user_ns, shp->shm_perm.cgid),
> +                  from_kuid_tp_munged(user_ns, shp->shm_perm.uid),
> +                  from_kgid_tp_munged(user_ns, shp->shm_perm.gid),
> +                  from_kuid_tp_munged(user_ns, shp->shm_perm.cuid),
> +                  from_kgid_tp_munged(user_ns, shp->shm_perm.cgid),
>                    shp->shm_atim,
>                    shp->shm_dtim,
>                    shp->shm_ctim,
> diff --git a/ipc/util.c b/ipc/util.c
> index 798cad1..81b39d5 100644
> --- a/ipc/util.c
> +++ b/ipc/util.c
> @@ -513,10 +513,10 @@ int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
>  void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out)
>  {
>         out->key        = in->key;
> -       out->uid        = from_kuid_munged(current_user_ns(), in->uid);
> -       out->gid        = from_kgid_munged(current_user_ns(), in->gid);
> -       out->cuid       = from_kuid_munged(current_user_ns(), in->cuid);
> -       out->cgid       = from_kgid_munged(current_user_ns(), in->cgid);
> +       out->uid        = from_kuid_tp_munged(current_user_ns(), in->uid);
> +       out->gid        = from_kgid_tp_munged(current_user_ns(), in->gid);
> +       out->cuid       = from_kuid_tp_munged(current_user_ns(), in->cuid);
> +       out->cgid       = from_kgid_tp_munged(current_user_ns(), in->cgid);
>         out->mode       = in->mode;
>         out->seq        = in->seq;
>  }
> diff --git a/kernel/acct.c b/kernel/acct.c
> index 74963d1..bbfa0b9 100644
> --- a/kernel/acct.c
> +++ b/kernel/acct.c
> @@ -489,8 +489,8 @@ static void do_acct_process(struct bsd_acct_struct *acct)
>
>         fill_ac(&ac);
>         /* we really need to bite the bullet and change layout */
> -       ac.ac_uid = from_kuid_munged(file->f_cred->user_ns, orig_cred->uid);
> -       ac.ac_gid = from_kgid_munged(file->f_cred->user_ns, orig_cred->gid);
> +       ac.ac_uid = from_kuid_tp_munged(file->f_cred->user_ns, orig_cred->uid);
> +       ac.ac_gid = from_kgid_tp_munged(file->f_cred->user_ns, orig_cred->gid);
>  #if ACCT_VERSION == 1 || ACCT_VERSION == 2
>         /* backward-compatible 16 bit fields */
>         ac.ac_uid16 = ac.ac_uid;
> diff --git a/kernel/exit.c b/kernel/exit.c
> index 9e6e135..207e284 100644
> --- a/kernel/exit.c
> +++ b/kernel/exit.c
> @@ -983,7 +983,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
>  {
>         int state, retval, status;
>         pid_t pid = task_pid_vnr(p);
> -       uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
> +       uid_t uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
>         struct siginfo __user *infop;
>
>         if (!likely(wo->wo_flags & WEXITED))
> @@ -1189,7 +1189,7 @@ static int wait_task_stopped(struct wait_opts *wo,
>         if (!unlikely(wo->wo_flags & WNOWAIT))
>                 *p_code = 0;
>
> -       uid = from_kuid_munged(current_user_ns(), task_uid(p));
> +       uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
>  unlock_sig:
>         spin_unlock_irq(&p->sighand->siglock);
>         if (!exit_code)
> @@ -1263,7 +1263,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
>         }
>         if (!unlikely(wo->wo_flags & WNOWAIT))
>                 p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
> -       uid = from_kuid_munged(current_user_ns(), task_uid(p));
> +       uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
>         spin_unlock_irq(&p->sighand->siglock);
>
>         pid = task_pid_vnr(p);
> diff --git a/kernel/groups.c b/kernel/groups.c
> index 74d431d..ec2ecf8 100644
> --- a/kernel/groups.c
> +++ b/kernel/groups.c
> @@ -70,7 +70,7 @@ static int groups_to_user(gid_t __user *grouplist,
>
>         for (i = 0; i < count; i++) {
>                 gid_t gid;
> -               gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i));
> +               gid = from_kgid_tp_munged(user_ns, GROUP_AT(group_info, i));
>                 if (put_user(gid, grouplist+i))
>                         return -EFAULT;
>         }
> diff --git a/kernel/signal.c b/kernel/signal.c
> index 96e9bc4..2d7e071 100644
> --- a/kernel/signal.c
> +++ b/kernel/signal.c
> @@ -958,7 +958,7 @@ static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_str
>                 return;
>
>         rcu_read_lock();
> -       info->si_uid = from_kuid_munged(task_cred_xxx(t, user_ns),
> +       info->si_uid = from_kuid_tp_munged(task_cred_xxx(t, user_ns),
>                                         make_kuid(current_user_ns(), info->si_uid));
>         rcu_read_unlock();
>  }
> @@ -1027,7 +1027,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
>                         q->info.si_code = SI_USER;
>                         q->info.si_pid = task_tgid_nr_ns(current,
>                                                         task_active_pid_ns(t));
> -                       q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +                       q->info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>                         break;
>                 case (unsigned long) SEND_SIG_PRIV:
>                         q->info.si_signo = sig;
> @@ -1609,7 +1609,7 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
>          */
>         rcu_read_lock();
>         info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(tsk->parent));
> -       info.si_uid = from_kuid_munged(task_cred_xxx(tsk->parent, user_ns),
> +       info.si_uid = from_kuid_tp_munged(task_cred_xxx(tsk->parent, user_ns),
>                                        task_uid(tsk));
>         rcu_read_unlock();
>
> @@ -1695,7 +1695,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,
>          */
>         rcu_read_lock();
>         info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(parent));
> -       info.si_uid = from_kuid_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
> +       info.si_uid = from_kuid_tp_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
>         rcu_read_unlock();
>
>         task_cputime(tsk, &utime, &stime);
> @@ -1904,7 +1904,7 @@ static void ptrace_do_notify(int signr, int exit_code, int why)
>         info.si_signo = signr;
>         info.si_code = exit_code;
>         info.si_pid = task_pid_vnr(current);
> -       info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>
>         /* Let the debugger run.  */
>         ptrace_stop(exit_code, why, 1, &info);
> @@ -2113,7 +2113,7 @@ static int ptrace_signal(int signr, siginfo_t *info)
>                 info->si_code = SI_USER;
>                 rcu_read_lock();
>                 info->si_pid = task_pid_vnr(current->parent);
> -               info->si_uid = from_kuid_munged(current_user_ns(),
> +               info->si_uid = from_kuid_tp_munged(current_user_ns(),
>                                                 task_uid(current->parent));
>                 rcu_read_unlock();
>         }
> @@ -2856,7 +2856,7 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
>         info.si_errno = 0;
>         info.si_code = SI_USER;
>         info.si_pid = task_tgid_vnr(current);
> -       info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>
>         return kill_something_info(sig, &info, pid);
>  }
> @@ -2899,7 +2899,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
>         info.si_errno = 0;
>         info.si_code = SI_TKILL;
>         info.si_pid = task_tgid_vnr(current);
> -       info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>
>         return do_send_specific(tgid, pid, sig, &info);
>  }
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 89d5be4..ce7833d 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -648,9 +648,9 @@ SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t _
>         int retval;
>         uid_t ruid, euid, suid;
>
> -       ruid = from_kuid_munged(cred->user_ns, cred->uid);
> -       euid = from_kuid_munged(cred->user_ns, cred->euid);
> -       suid = from_kuid_munged(cred->user_ns, cred->suid);
> +       ruid = from_kuid_tp_munged(cred->user_ns, cred->uid);
> +       euid = from_kuid_tp_munged(cred->user_ns, cred->euid);
> +       suid = from_kuid_tp_munged(cred->user_ns, cred->suid);
>
>         retval = put_user(ruid, ruidp);
>         if (!retval) {
> @@ -722,9 +722,9 @@ SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t _
>         int retval;
>         gid_t rgid, egid, sgid;
>
> -       rgid = from_kgid_munged(cred->user_ns, cred->gid);
> -       egid = from_kgid_munged(cred->user_ns, cred->egid);
> -       sgid = from_kgid_munged(cred->user_ns, cred->sgid);
> +       rgid = from_kgid_tp_munged(cred->user_ns, cred->gid);
> +       egid = from_kgid_tp_munged(cred->user_ns, cred->egid);
> +       sgid = from_kgid_tp_munged(cred->user_ns, cred->sgid);
>
>         retval = put_user(rgid, rgidp);
>         if (!retval) {
> @@ -751,7 +751,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
>         kuid_t kuid;
>
>         old = current_cred();
> -       old_fsuid = from_kuid_munged(old->user_ns, old->fsuid);
> +       old_fsuid = from_kuid_tp_munged(old->user_ns, old->fsuid);
>
>         kuid = make_kuid(old->user_ns, uid);
>         if (!uid_valid(kuid))
> @@ -790,7 +790,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
>         kgid_t kgid;
>
>         old = current_cred();
> -       old_fsgid = from_kgid_munged(old->user_ns, old->fsgid);
> +       old_fsgid = from_kgid_tp_munged(old->user_ns, old->fsgid);
>
>         kgid = make_kgid(old->user_ns, gid);
>         if (!gid_valid(kgid))
> @@ -858,25 +858,25 @@ SYSCALL_DEFINE0(getppid)
>  SYSCALL_DEFINE0(getuid)
>  {
>         /* Only we change this so SMP safe */
> -       return from_kuid_munged(current_user_ns(), current_uid());
> +       return from_kuid_tp_munged(current_user_ns(), current_uid());
>  }
>
>  SYSCALL_DEFINE0(geteuid)
>  {
>         /* Only we change this so SMP safe */
> -       return from_kuid_munged(current_user_ns(), current_euid());
> +       return from_kuid_tp_munged(current_user_ns(), current_euid());
>  }
>
>  SYSCALL_DEFINE0(getgid)
>  {
>         /* Only we change this so SMP safe */
> -       return from_kgid_munged(current_user_ns(), current_gid());
> +       return from_kgid_tp_munged(current_user_ns(), current_gid());
>  }
>
>  SYSCALL_DEFINE0(getegid)
>  {
>         /* Only we change this so SMP safe */
> -       return from_kgid_munged(current_user_ns(), current_egid());
> +       return from_kgid_tp_munged(current_user_ns(), current_egid());
>  }
>
>  void do_sys_times(struct tms *tms)
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 8a4bd6b..d4f03ee 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -2743,7 +2743,7 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter)
>         seq_printf(m, "#    | task: %.16s-%d "
>                    "(uid:%d nice:%ld policy:%ld rt_prio:%ld)\n",
>                    data->comm, data->pid,
> -                  from_kuid_munged(seq_user_ns(m), data->uid), data->nice,
> +                  from_kuid_tp_munged(seq_user_ns(m), data->uid), data->nice,
>                    data->policy, data->rt_priority);
>         seq_puts(m, "#    -----------------\n");
>
> diff --git a/kernel/tsacct.c b/kernel/tsacct.c
> index f8e26ab..19e3a61 100644
> --- a/kernel/tsacct.c
> +++ b/kernel/tsacct.c
> @@ -60,8 +60,8 @@ void bacct_add_tsk(struct user_namespace *user_ns,
>         stats->ac_pid    = task_pid_nr_ns(tsk, pid_ns);
>         rcu_read_lock();
>         tcred = __task_cred(tsk);
> -       stats->ac_uid    = from_kuid_munged(user_ns, tcred->uid);
> -       stats->ac_gid    = from_kgid_munged(user_ns, tcred->gid);
> +       stats->ac_uid    = from_kuid_tp_munged(user_ns, tcred->uid);
> +       stats->ac_gid    = from_kgid_tp_munged(user_ns, tcred->gid);
>         stats->ac_ppid   = pid_alive(tsk) ?
>                 task_tgid_nr_ns(rcu_dereference(tsk->real_parent), pid_ns) : 0;
>         rcu_read_unlock();
> diff --git a/kernel/uid16.c b/kernel/uid16.c
> index d58cc4d..f91f270 100644
> --- a/kernel/uid16.c
> +++ b/kernel/uid16.c
> @@ -63,9 +63,9 @@ SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euid
>         int retval;
>         old_uid_t ruid, euid, suid;
>
> -       ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
> -       euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
> -       suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
> +       ruid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       euid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->euid));
> +       suid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->suid));
>
>         if (!(retval   = put_user(ruid, ruidp)) &&
>             !(retval   = put_user(euid, euidp)))
> @@ -87,9 +87,9 @@ SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egid
>         int retval;
>         old_gid_t rgid, egid, sgid;
>
> -       rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
> -       egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
> -       sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
> +       rgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->gid));
> +       egid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->egid));
> +       sgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->sgid));
>
>         if (!(retval   = put_user(rgid, rgidp)) &&
>             !(retval   = put_user(egid, egidp)))
> @@ -118,7 +118,7 @@ static int groups16_to_user(old_gid_t __user *grouplist,
>
>         for (i = 0; i < group_info->ngroups; i++) {
>                 kgid = GROUP_AT(group_info, i);
> -               group = high2lowgid(from_kgid_munged(user_ns, kgid));
> +               group = high2lowgid(from_kgid_tp_munged(user_ns, kgid));
>                 if (put_user(group, grouplist+i))
>                         return -EFAULT;
>         }
> @@ -198,20 +198,20 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)
>
>  SYSCALL_DEFINE0(getuid16)
>  {
> -       return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
> +       return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_uid()));
>  }
>
>  SYSCALL_DEFINE0(geteuid16)
>  {
> -       return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
> +       return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_euid()));
>  }
>
>  SYSCALL_DEFINE0(getgid16)
>  {
> -       return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
> +       return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_gid()));
>  }
>
>  SYSCALL_DEFINE0(getegid16)
>  {
> -       return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
> +       return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_egid()));
>  }
> diff --git a/kernel/user.c b/kernel/user.c
> index b069ccb..e1fd9e5 100644
> --- a/kernel/user.c
> +++ b/kernel/user.c
> @@ -48,6 +48,7 @@ struct user_namespace init_user_ns = {
>                 },
>         },
>         .count = ATOMIC_INIT(3),
> +       .opaque = &init_user_ns,
>         .owner = GLOBAL_ROOT_UID,
>         .group = GLOBAL_ROOT_GID,
>         .ns.inum = PROC_USER_INIT_INO,
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 9bafc21..44a7d3d 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -98,6 +98,7 @@ int create_user_ns(struct cred *new)
>         atomic_set(&ns->count, 1);
>         /* Leave the new->user_ns reference with the new user namespace. */
>         ns->parent = parent_ns;
> +       ns->opaque = ns;
>         ns->level = parent_ns->level + 1;
>         ns->owner = owner;
>         ns->group = group;
> @@ -249,7 +250,8 @@ EXPORT_SYMBOL(make_kuid);
>   *     @kuid: The kernel internal uid to start with.
>   *
>   *     Map @kuid into the user-namespace specified by @targ and
> - *     return the resulting uid.
> + *     return the resulting uid. This ignores transparent user
> + *     namespaces and is therefore appropriate for security checks.
>   *
>   *     There is always a mapping into the initial user_namespace.
>   *
> @@ -263,33 +265,63 @@ uid_t from_kuid(struct user_namespace *targ, kuid_t kuid)
>  EXPORT_SYMBOL(from_kuid);
>
>  /**
> - *     from_kuid_munged - Create a uid from a kuid user-namespace pair.
> + *     from_kuid_tp - Create a uid from a kuid user-namespace pair.
> + *     @targ: The user namespace we want a uid in.
> + *     @kuid: The kernel internal uid to start with.
> + *
> + *     Map @kuid into the user-namespace specified by @targ and
> + *     return the resulting uid.
> + *
> + *     This function is *not* appropriate for security checks because
> + *     if @targ is transparent, the mappings of an ancestor namespace
> + *     are used. If you intend to do anything with the result apart from
> + *     returning it to a process in @targ, you might want to use
> + *     from_kuid() instead.
> + *
> + *     There is always a mapping into the initial user_namespace.
> + *
> + *     If @kuid is not visible in @targ (uid_t)-1 is returned.
> + */
> +uid_t from_kuid_tp(struct user_namespace *targ, kuid_t kuid)
> +{
> +       /* Map the uid from a global kernel uid */
> +       struct user_namespace *opaque = READ_ONCE(targ->opaque);
> +
> +       return map_id_up(&opaque->uid_map, __kuid_val(kuid));
> +}
> +EXPORT_SYMBOL(from_kuid_tp);
> +
> +/**
> + *     from_kuid_tp_munged - Create a uid from a kuid user-namespace pair.
>   *     @targ: The user namespace we want a uid in.
>   *     @kuid: The kernel internal uid to start with.
>   *
>   *     Map @kuid into the user-namespace specified by @targ and
>   *     return the resulting uid.
>   *
> + *     This function is *not* appropriate for security checks; see the
> + *     comment above from_kuid_tp().
> + *
>   *     There is always a mapping into the initial user_namespace.
>   *
> - *     Unlike from_kuid from_kuid_munged never fails and always
> - *     returns a valid uid.  This makes from_kuid_munged appropriate
> + *     Unlike from_kuid_tp from_kuid_tp_munged never fails and always
> + *     returns a valid uid.  This makes from_kuid_tp_munged appropriate
>   *     for use in syscalls like stat and getuid where failing the
>   *     system call and failing to provide a valid uid are not an
>   *     options.
>   *
>   *     If @kuid has no mapping in @targ overflowuid is returned.
>   */
> -uid_t from_kuid_munged(struct user_namespace *targ, kuid_t kuid)
> +uid_t from_kuid_tp_munged(struct user_namespace *targ, kuid_t kuid)
>  {
>         uid_t uid;
> -       uid = from_kuid(targ, kuid);
> +       uid = from_kuid_tp(targ, kuid);
>
>         if (uid == (uid_t) -1)
>                 uid = overflowuid;
>         return uid;
>  }
> -EXPORT_SYMBOL(from_kuid_munged);
> +EXPORT_SYMBOL(from_kuid_tp_munged);
>
>  /**
>   *     make_kgid - Map a user-namespace gid pair into a kgid.
> @@ -317,7 +349,8 @@ EXPORT_SYMBOL(make_kgid);
>   *     @kgid: The kernel internal gid to start with.
>   *
>   *     Map @kgid into the user-namespace specified by @targ and
> - *     return the resulting gid.
> + *     return the resulting gid. This ignores transparent user
> + *     namespaces and is therefore appropriate for security checks.
>   *
>   *     There is always a mapping into the initial user_namespace.
>   *
> @@ -331,32 +364,62 @@ gid_t from_kgid(struct user_namespace *targ, kgid_t kgid)
>  EXPORT_SYMBOL(from_kgid);
>
>  /**
> - *     from_kgid_munged - Create a gid from a kgid user-namespace pair.
> + *     from_kgid_tp - Create a gid from a kgid user-namespace pair.
>   *     @targ: The user namespace we want a gid in.
>   *     @kgid: The kernel internal gid to start with.
>   *
>   *     Map @kgid into the user-namespace specified by @targ and
>   *     return the resulting gid.
>   *
> + *     This function is *not* appropriate for security checks because
> + *     if @targ is transparent, the mappings of an ancestor namespace
> + *     are used. If you intend to do anything with the result apart from
> + *     returning it to a process in @targ, you might want to use
> + *     from_kgid() instead.
> + *
>   *     There is always a mapping into the initial user_namespace.
>   *
> - *     Unlike from_kgid from_kgid_munged never fails and always
> - *     returns a valid gid.  This makes from_kgid_munged appropriate
> + *     If @kgid is not visible in @targ (gid_t)-1 is returned.
> + */
> +gid_t from_kgid_tp(struct user_namespace *targ, kgid_t kgid)
> +{
> +       /* Map the gid from a global kernel gid */
> +       struct user_namespace *opaque = READ_ONCE(targ->opaque);
> +
> +       return map_id_up(&opaque->gid_map, __kgid_val(kgid));
> +}
> +EXPORT_SYMBOL(from_kgid_tp);
> +
> +/**
> + *     from_kgid_tp_munged - Create a gid from a kgid user-namespace pair.
> + *     @targ: The user namespace we want a gid in.
> + *     @kgid: The kernel internal gid to start with.
> + *
> + *     Map @kgid into the user-namespace specified by @targ and
> + *     return the resulting gid.
> + *
> + *     This function is *not* appropriate for security checks; see the
> + *     comment above from_kgid_tp().
> + *
> + *     There is always a mapping into the initial user_namespace.
> + *
> + *     Unlike from_kgid_tp from_kgid_tp_munged never fails and always
> + *     returns a valid gid.  This makes from_kgid_tp_munged appropriate
>   *     for use in syscalls like stat and getgid where failing the
>   *     system call and failing to provide a valid gid are not options.
>   *
>   *     If @kgid has no mapping in @targ overflowgid is returned.
>   */
> -gid_t from_kgid_munged(struct user_namespace *targ, kgid_t kgid)
> +gid_t from_kgid_tp_munged(struct user_namespace *targ, kgid_t kgid)
>  {
>         gid_t gid;
> -       gid = from_kgid(targ, kgid);
> +       gid = from_kgid_tp(targ, kgid);
>
>         if (gid == (gid_t) -1)
>                 gid = overflowgid;
>         return gid;
>  }
> -EXPORT_SYMBOL(from_kgid_munged);
> +EXPORT_SYMBOL(from_kgid_tp_munged);
>
>  /**
>   *     make_kprojid - Map a user-namespace projid pair into a kprojid.
> @@ -811,6 +874,18 @@ static bool new_idmap_permitted(const struct file *file,
>                                 struct uid_gid_map *new_map)
>  {
>         const struct cred *cred = file->f_cred;
> +       unsigned int idx;
> +
> +       /* Don't allow non-identity mappings in transparent namespaces. */
> +       if (ns != ns->opaque) {
> +               for (idx = 0; idx < new_map->nr_extents; idx++) {
> +                       struct uid_gid_extent *ext = &new_map->extent[idx];
> +
> +                       if (ext->first != ext->lower_first)
> +                               return false;
> +               }
> +       }
> +
>         /* Don't allow mappings that would allow anything that wouldn't
>          * be allowed without the establishment of unprivileged mappings.
>          */
> @@ -922,6 +997,81 @@ out_unlock:
>         goto out;
>  }
>
> +int proc_transparent_show(struct seq_file *seq, void *v)
> +{
> +       struct user_namespace *ns = seq->private;
> +       struct user_namespace *opaque = READ_ONCE(ns->opaque);
> +
> +       seq_printf(seq, "%d\n", (ns == opaque) ? 0 : 1);
> +       return 0;
> +}
> +
> +ssize_t proc_transparent_write(struct file *file, const char __user *buf,
> +                            size_t count, loff_t *ppos)
> +{
> +       struct seq_file *seq = file->private_data;
> +       struct user_namespace *ns = seq->private;
> +       char kbuf[8], *pos;
> +       bool transparent;
> +       ssize_t ret;
> +
> +       /* Only allow a very narrow range of strings to be written */
> +       ret = -EINVAL;
> +       if ((*ppos != 0) || (count >= sizeof(kbuf)))
> +               goto out;
> +
> +       /* What was written? */
> +       ret = -EFAULT;
> +       if (copy_from_user(kbuf, buf, count))
> +               goto out;
> +       kbuf[count] = '\0';
> +       pos = kbuf;
> +
> +       /* What is being requested? */
> +       ret = -EINVAL;
> +       if (pos[0] == '1') {
> +               pos += 1;
> +               transparent = true;
> +       } else if (pos[0] == '0') {
> +               pos += 1;
> +               transparent = false;
> +       } else
> +               goto out;
> +
> +       /* Verify there is not trailing junk on the line */
> +       pos = skip_spaces(pos);
> +       if (*pos != '\0')
> +               goto out;
> +
> +       ret = -EPERM;
> +       mutex_lock(&userns_state_mutex);
> +       /* Is the requested state different from the current one? */
> +       if (transparent != (ns->opaque != ns)) {
> +               /* You can't turn off transparent mode. */
> +               if (!transparent)
> +                       goto out_unlock;
> +               /* If there are existing mappings, they might be
> +                * non-identity mappings. Therefore, block transparent
> +                * mode. This also prevents making the init namespace
> +                * transparent (which wouldn't work).
> +                */
> +               if (ns->uid_map.nr_extents != 0 || ns->gid_map.nr_extents != 0)
> +                       goto out_unlock;
> +               /* Okay! Make the namespace transparent. */
> +               ns->opaque = ns->parent->opaque;
> +       }
> +       mutex_unlock(&userns_state_mutex);
> +
> +       /* Report a successful write */
> +       *ppos = count;
> +       ret = count;
> +out:
> +       return ret;
> +out_unlock:
> +       mutex_unlock(&userns_state_mutex);
> +       goto out;
> +}
> +
>  bool userns_may_setgroups(const struct user_namespace *ns)
>  {
>         bool allowed;
> diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c
> index af46bc4..c0f45bc 100644
> --- a/net/appletalk/atalk_proc.c
> +++ b/net/appletalk/atalk_proc.c
> @@ -184,7 +184,7 @@ static int atalk_seq_socket_show(struct seq_file *seq, void *v)
>                    sk_wmem_alloc_get(s),
>                    sk_rmem_alloc_get(s),
>                    s->sk_state,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)));
>  out:
>         return 0;
>  }
> diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c
> index 4ad2fb7..b28e339 100644
> --- a/net/ax25/ax25_uid.c
> +++ b/net/ax25/ax25_uid.c
> @@ -81,7 +81,7 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
>                 read_lock(&ax25_uid_lock);
>                 ax25_uid_for_each(ax25_uid, &ax25_uid_list) {
>                         if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
> -                               res = from_kuid_munged(current_user_ns(), ax25_uid->uid);
> +                               res = from_kuid_tp_munged(current_user_ns(), ax25_uid->uid);
>                                 break;
>                         }
>                 }
> @@ -175,7 +175,7 @@ static int ax25_uid_seq_show(struct seq_file *seq, void *v)
>
>                 pt = hlist_entry(v, struct ax25_uid_assoc, uid_node);
>                 seq_printf(seq, "%6d %s\n",
> -                       from_kuid_munged(seq_user_ns(seq), pt->uid),
> +                       from_kuid_tp_munged(seq_user_ns(seq), pt->uid),
>                         ax2asc(buf, &pt->call));
>         }
>         return 0;
> diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
> index 3df7aef..5e9906b 100644
> --- a/net/bluetooth/af_bluetooth.c
> +++ b/net/bluetooth/af_bluetooth.c
> @@ -625,7 +625,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
>                            atomic_read(&sk->sk_refcnt),
>                            sk_rmem_alloc_get(sk),
>                            sk_wmem_alloc_get(sk),
> -                          from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
> +                          from_kuid_tp(seq_user_ns(seq), sock_i_uid(sk)),
>                            sock_i_ino(sk),
>                            bt->parent? sock_i_ino(bt->parent): 0LU);
>
> diff --git a/net/core/sock.c b/net/core/sock.c
> index 08bf97e..1e6192d 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1020,8 +1020,8 @@ static void cred_to_ucred(struct pid *pid, const struct cred *cred,
>         if (cred) {
>                 struct user_namespace *current_ns = current_user_ns();
>
> -               ucred->uid = from_kuid_munged(current_ns, cred->euid);
> -               ucred->gid = from_kgid_munged(current_ns, cred->egid);
> +               ucred->uid = from_kuid_tp_munged(current_ns, cred->euid);
> +               ucred->gid = from_kgid_tp_munged(current_ns, cred->egid);
>         }
>  }
>
> diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
> index 25af124..26f1ec2 100644
> --- a/net/ipv4/inet_diag.c
> +++ b/net/ipv4/inet_diag.c
> @@ -134,7 +134,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
>         }
>  #endif
>
> -       r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
> +       r->idiag_uid = from_kuid_tp_munged(user_ns, sock_i_uid(sk));
>         r->idiag_inode = sock_i_ino(sk);
>
>         return 0;
> diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
> index 66ddcb6..f3b27ea 100644
> --- a/net/ipv4/ping.c
> +++ b/net/ipv4/ping.c
> @@ -1121,7 +1121,7 @@ static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
>                 sk_wmem_alloc_get(sp),
>                 sk_rmem_alloc_get(sp),
>                 0, 0L, 0,
> -               from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
> +               from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sp)),
>                 0, sock_i_ino(sp),
>                 atomic_read(&sp->sk_refcnt), sp,
>                 atomic_read(&sp->sk_drops));
> diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
> index 438f50c..759095ee 100644
> --- a/net/ipv4/raw.c
> +++ b/net/ipv4/raw.c
> @@ -1033,7 +1033,7 @@ static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
>                 sk_wmem_alloc_get(sp),
>                 sk_rmem_alloc_get(sp),
>                 0, 0L, 0,
> -               from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
> +               from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
>                 0, sock_i_ino(sp),
>                 atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));
>  }
> diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
> index 1cb67de..4e19885 100644
> --- a/net/ipv4/sysctl_net_ipv4.c
> +++ b/net/ipv4/sysctl_net_ipv4.c
> @@ -133,8 +133,8 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write,
>         };
>
>         inet_get_ping_group_range_table(table, &low, &high);
> -       urange[0] = from_kgid_munged(user_ns, low);
> -       urange[1] = from_kgid_munged(user_ns, high);
> +       urange[0] = from_kgid_tp_munged(user_ns, low);
> +       urange[1] = from_kgid_tp_munged(user_ns, high);
>         ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
>
>         if (write && ret == 0) {
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index 3708de2..5a5ae86 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -2161,8 +2161,8 @@ static void get_openreq4(const struct request_sock *req,
>                 1,    /* timers active (only the expire timer) */
>                 jiffies_delta_to_clock_t(delta),
>                 req->num_timeout,
> -               from_kuid_munged(seq_user_ns(f),
> -                                sock_i_uid(req->rsk_listener)),
> +               from_kuid_tp_munged(seq_user_ns(f),
> +                                   sock_i_uid(req->rsk_listener)),
>                 0,  /* non standard timer */
>                 0, /* open_requests have no inode */
>                 0,
> @@ -2217,7 +2217,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
>                 timer_active,
>                 jiffies_delta_to_clock_t(timer_expires - jiffies),
>                 icsk->icsk_retransmits,
> -               from_kuid_munged(seq_user_ns(f), sock_i_uid(sk)),
> +               from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sk)),
>                 icsk->icsk_probes_out,
>                 sock_i_ino(sk),
>                 atomic_read(&sk->sk_refcnt), sk,
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index 0ff31d9..e3579b2 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -2408,7 +2408,7 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f,
>                 sk_wmem_alloc_get(sp),
>                 sk_rmem_alloc_get(sp),
>                 0, 0L, 0,
> -               from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
> +               from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sp)),
>                 0, sock_i_ino(sp),
>                 atomic_read(&sp->sk_refcnt), sp,
>                 atomic_read(&sp->sk_drops));
> diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
> index 37874e2..d66dd7c 100644
> --- a/net/ipv6/datagram.c
> +++ b/net/ipv6/datagram.c
> @@ -1028,7 +1028,7 @@ void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
>                    sk_wmem_alloc_get(sp),
>                    sk_rmem_alloc_get(sp),
>                    0, 0L, 0,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
>                    0,
>                    sock_i_ino(sp),
>                    atomic_read(&sp->sk_refcnt), sp,
> diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
> index b912f0d..a1c7516 100644
> --- a/net/ipv6/ip6_flowlabel.c
> +++ b/net/ipv6/ip6_flowlabel.c
> @@ -789,7 +789,7 @@ static int ip6fl_seq_show(struct seq_file *seq, void *v)
>                            ((fl->share == IPV6_FL_S_PROCESS) ?
>                             pid_nr_ns(fl->owner.pid, state->pid_ns) :
>                             ((fl->share == IPV6_FL_S_USER) ?
> -                            from_kuid_munged(seq_user_ns(seq), fl->owner.uid) :
> +                            from_kuid_tp_munged(seq_user_ns(seq), fl->owner.uid) :
>                              0)),
>                            atomic_read(&fl->users),
>                            fl->linger/HZ,
> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
> index f36c2d0..04643ca 100644
> --- a/net/ipv6/tcp_ipv6.c
> +++ b/net/ipv6/tcp_ipv6.c
> @@ -1696,8 +1696,8 @@ static void get_openreq6(struct seq_file *seq,
>                    1,   /* timers active (only the expire timer) */
>                    jiffies_to_clock_t(ttd),
>                    req->num_timeout,
> -                  from_kuid_munged(seq_user_ns(seq),
> -                                   sock_i_uid(req->rsk_listener)),
> +                  from_kuid_tp_munged(seq_user_ns(seq),
> +                                      sock_i_uid(req->rsk_listener)),
>                    0,  /* non standard timer */
>                    0, /* open_requests have no inode */
>                    0, req);
> @@ -1760,7 +1760,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
>                    timer_active,
>                    jiffies_delta_to_clock_t(timer_expires - jiffies),
>                    icsk->icsk_retransmits,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
>                    icsk->icsk_probes_out,
>                    sock_i_ino(sp),
>                    atomic_read(&sp->sk_refcnt), sp,
> diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c
> index c1d247e..fc1d1fe 100644
> --- a/net/ipx/ipx_proc.c
> +++ b/net/ipx/ipx_proc.c
> @@ -217,7 +217,7 @@ static int ipx_seq_socket_show(struct seq_file *seq, void *v)
>                    sk_wmem_alloc_get(s),
>                    sk_rmem_alloc_get(s),
>                    s->sk_state,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)));
>  out:
>         return 0;
>  }
> diff --git a/net/key/af_key.c b/net/key/af_key.c
> index f9c9ecb..b76105f 100644
> --- a/net/key/af_key.c
> +++ b/net/key/af_key.c
> @@ -3714,7 +3714,7 @@ static int pfkey_seq_show(struct seq_file *f, void *v)
>                                atomic_read(&s->sk_refcnt),
>                                sk_rmem_alloc_get(s),
>                                sk_wmem_alloc_get(s),
> -                              from_kuid_munged(seq_user_ns(f), sock_i_uid(s)),
> +                              from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(s)),
>                                sock_i_ino(s)
>                                );
>         return 0;
> diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
> index 29c509c..96d2dff 100644
> --- a/net/llc/llc_proc.c
> +++ b/net/llc/llc_proc.c
> @@ -151,7 +151,7 @@ static int llc_seq_socket_show(struct seq_file *seq, void *v)
>                    sk_wmem_alloc_get(sk),
>                    sk_rmem_alloc_get(sk) - llc->copied_seq,
>                    sk->sk_state,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                    llc->link);
>  out:
>         return 0;
> diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
> index 11f81c8..3263bca 100644
> --- a/net/netfilter/nfnetlink_log.c
> +++ b/net/netfilter/nfnetlink_log.c
> @@ -552,8 +552,8 @@ __build_packet_message(struct nfnl_log_net *log,
>                         struct file *file = sk->sk_socket->file;
>                         const struct cred *cred = file->f_cred;
>                         struct user_namespace *user_ns = inst->peer_user_ns;
> -                       __be32 uid = htonl(from_kuid_munged(user_ns, cred->fsuid));
> -                       __be32 gid = htonl(from_kgid_munged(user_ns, cred->fsgid));
> +                       __be32 uid = htonl(from_kuid_tp_munged(user_ns, cred->fsuid));
> +                       __be32 gid = htonl(from_kgid_tp_munged(user_ns, cred->fsgid));
>                         read_unlock_bh(&sk->sk_callback_lock);
>                         if (nla_put_be32(inst->skb, NFULA_UID, uid) ||
>                             nla_put_be32(inst->skb, NFULA_GID, gid))
> diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
> index 9bff6ef..21443c8 100644
> --- a/net/packet/af_packet.c
> +++ b/net/packet/af_packet.c
> @@ -4497,7 +4497,7 @@ static int packet_seq_show(struct seq_file *seq, void *v)
>                            po->ifindex,
>                            po->running,
>                            atomic_read(&s->sk_rmem_alloc),
> -                          from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)),
> +                          from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)),
>                            sock_i_ino(s));
>         }
>
> diff --git a/net/packet/diag.c b/net/packet/diag.c
> index 0ed68f0..40b8df7 100644
> --- a/net/packet/diag.c
> +++ b/net/packet/diag.c
> @@ -153,7 +153,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
>
>         if ((req->pdiag_show & PACKET_SHOW_INFO) &&
>             nla_put_u32(skb, PACKET_DIAG_UID,
> -                       from_kuid_munged(user_ns, sock_i_uid(sk))))
> +                       from_kuid_tp_munged(user_ns, sock_i_uid(sk))))
>                 goto out_nlmsg_trim;
>
>         if ((req->pdiag_show & PACKET_SHOW_MCLIST) &&
> diff --git a/net/phonet/socket.c b/net/phonet/socket.c
> index ffd5f22..fa90d85 100644
> --- a/net/phonet/socket.c
> +++ b/net/phonet/socket.c
> @@ -610,7 +610,7 @@ static int pn_sock_seq_show(struct seq_file *seq, void *v)
>                         sk->sk_protocol, pn->sobject, pn->dobject,
>                         pn->resource, sk->sk_state,
>                         sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
> -                       from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                       from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                         sock_i_ino(sk),
>                         atomic_read(&sk->sk_refcnt), sk,
>                         atomic_read(&sk->sk_drops));
> @@ -795,7 +795,7 @@ static int pn_res_seq_show(struct seq_file *seq, void *v)
>
>                 seq_printf(seq, "%02X %5u %lu",
>                            (int) (psk - pnres.sk),
> -                          from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                          from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                            sock_i_ino(sk));
>         }
>         seq_pad(seq, '\n');
> diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> index 4cb5aed..a893492 100644
> --- a/net/sctp/proc.c
> +++ b/net/sctp/proc.c
> @@ -224,7 +224,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v)
>                 seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5u %5lu ", ep, sk,
>                            sctp_sk(sk)->type, sk->sk_state, hash,
>                            epb->bind_addr.port,
> -                          from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                          from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                            sock_i_ino(sk));
>
>                 sctp_seq_dump_local_addrs(seq, epb);
> @@ -346,7 +346,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
>                    assoc->assoc_id,
>                    assoc->sndbuf_used,
>                    atomic_read(&assoc->rmem_alloc),
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                    sock_i_ino(sk),
>                    epb->bind_addr.port,
>                    assoc->peer.port);
> diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
> index dfacdc9..ce7a1ce 100644
> --- a/net/sunrpc/svcauth_unix.c
> +++ b/net/sunrpc/svcauth_unix.c
> @@ -562,9 +562,9 @@ static int unix_gid_show(struct seq_file *m,
>         else
>                 glen = 0;
>
> -       seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen);
> +       seq_printf(m, "%u %d:", from_kuid_tp_munged(user_ns, ug->uid), glen);
>         for (i = 0; i < glen; i++)
> -               seq_printf(m, " %d", from_kgid_munged(user_ns, GROUP_AT(ug->gi, i)));
> +               seq_printf(m, " %d", from_kgid_tp_munged(user_ns, GROUP_AT(ug->gi, i)));
>         seq_printf(m, "\n");
>         return 0;
>  }
> diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
> index d580ad0..b88f73d 100644
> --- a/security/keys/keyctl.c
> +++ b/security/keys/keyctl.c
> @@ -619,8 +619,8 @@ okay:
>         infobuf = kasprintf(GFP_KERNEL,
>                             "%s;%d;%d;%08x;",
>                             key->type->name,
> -                           from_kuid_munged(current_user_ns(), key->uid),
> -                           from_kgid_munged(current_user_ns(), key->gid),
> +                           from_kuid_tp_munged(current_user_ns(), key->uid),
> +                           from_kgid_tp_munged(current_user_ns(), key->gid),
>                             key->perm);
>         if (!infobuf)
>                 goto error2;
> diff --git a/security/keys/proc.c b/security/keys/proc.c
> index f0611a6..f71449d 100644
> --- a/security/keys/proc.c
> +++ b/security/keys/proc.c
> @@ -255,8 +255,8 @@ static int proc_keys_show(struct seq_file *m, void *v)
>                    atomic_read(&key->usage),
>                    xbuf,
>                    key->perm,
> -                  from_kuid_munged(seq_user_ns(m), key->uid),
> -                  from_kgid_munged(seq_user_ns(m), key->gid),
> +                  from_kuid_tp_munged(seq_user_ns(m), key->uid),
> +                  from_kgid_tp_munged(seq_user_ns(m), key->gid),
>                    key->type->name);
>
>  #undef showflag
> @@ -339,7 +339,7 @@ static int proc_key_users_show(struct seq_file *m, void *v)
>                 key_quota_root_maxbytes : key_quota_maxbytes;
>
>         seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
> -                  from_kuid_munged(seq_user_ns(m), user->uid),
> +                  from_kuid_tp_munged(seq_user_ns(m), user->uid),
>                    atomic_read(&user->usage),
>                    atomic_read(&user->nkeys),
>                    atomic_read(&user->nikeys),
> --
> 2.8.0.rc3.226.g39d4020
>



-- 
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] namespaces: add transparent user namespaces
@ 2016-06-27  3:34           ` Michael Kerrisk
  0 siblings, 0 replies; 14+ messages in thread
From: Michael Kerrisk @ 2016-06-27  3:34 UTC (permalink / raw)
  To: Jann Horn
  Cc: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Eric W. Biederman,
	Andy Lutomirski, Linux Kernel, Linux API,
	Michael Kerrisk-manpages

Hi Jann,

Patches such as this really should CC linux-api@ (added).

On Sat, Jun 25, 2016 at 2:23 AM, Jann Horn <jannh-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> wrote:
> This allows the admin of a user namespace to mark the namespace as
> transparent. All other namespaces, by default, are opaque.
>
> While the current behavior of user namespaces is appropriate for use in
> containers, there are many programs that only use user namespaces because
> doing so enables them to do other things (e.g. unsharing the mount or
> network namespace) that require namespaced capabilities. For them, the
> inability to see the real UIDs and GIDs of things from inside the user
> namespace can be very annoying.
>
> In a transparent namespace, all UIDs and GIDs that are mapped into its
> first opaque ancestor are visible and are not remapped. This means that if
> a process e.g. stat()s the real root directory in a namespace, it will
> still see it as owned by UID 0.
>
> Traditionally, any UID or GID that was visible in a user namespace was also
> mapped into the namespace, giving the namespace admin full access to it.
> This patch introduces a distinction: In a transparent namespace, UIDs and
> GIDs can be visible without being mapped. Non-mapped, visible UIDs can be
> passed from the kernel to userspace, but userspace can't send them back to
> the kernel.

Can you explain "can't send them back to the kernel" in more detail?
(Some examples of what is and isn't possible would be helpul.)

> In order to be able to fully use specific UIDs/GIDs and gain
> privileges over them, mappings need to be set up in the usual way -
> however, to avoid aliasing problems, only identity mappings are permitted.
>
> v2:
> Ensure that all relevant from_k[ug]id callers show up in the patch.
> _transparent would be more verbose than _tp, but considering the line
> length rule, that's just too long.
>
> Yes, this makes the patch rather large.
>
> Behavior should be the same as in v1, except that I'm not touching orangefs
> in this patch because every single use of from_k[ug]id in it is wrong in
> some way. (Thanks for making me reread all that stuff, Eric.) I'll write a
> separate patch or at least report the issue with more detail later.
>
> (Also, the handling of user namespaces when dealing with signals is
> super-ugly and kind of incorrect. That should probably be cleaned up.)

I'm curious about this detail: can you say some more about the issues here?

> posix_acl_to_xattr would have changed behavior in the v1 patch, but isn't
> changed here. Because it's only used with init_user_ns, that won't change
> user-visible behavior relative to v1.
>
> This patch was compile-tested with allyesconfig. I also ran a VM with this
> patch applied and checked that it still works, but that probably doesn't
> mean much.

One of the things notably lacking from this commit message is any sort
of description of the user-space-API changes that it makes. I presume
it's a matter of some /proc files. Could you explain the changes (ad
add that detail in any further commit message)?

Thanks,

Michael

> Signed-off-by: Jann Horn <jannh-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> ---
>  arch/alpha/kernel/osf_sys.c       |   4 +-
>  arch/arm/kernel/sys_oabi-compat.c |   4 +-
>  arch/ia64/kernel/signal.c         |   4 +-
>  arch/s390/kernel/compat_linux.c   |  26 +++---
>  arch/sparc/kernel/sys_sparc32.c   |   4 +-
>  arch/x86/ia32/sys_ia32.c          |   4 +-
>  drivers/android/binder.c          |   2 +-
>  drivers/gpu/drm/drm_info.c        |   2 +-
>  drivers/gpu/drm/drm_ioctl.c       |   2 +-
>  drivers/net/tun.c                 |   4 +-
>  fs/autofs4/dev-ioctl.c            |   4 +-
>  fs/autofs4/waitq.c                |   4 +-
>  fs/binfmt_elf.c                   |  12 +--
>  fs/binfmt_elf_fdpic.c             |  12 +--
>  fs/compat.c                       |   4 +-
>  fs/fcntl.c                        |   4 +-
>  fs/ncpfs/ioctl.c                  |  12 +--
>  fs/posix_acl.c                    |  11 ++-
>  fs/proc/array.c                   |  18 ++--
>  fs/proc/base.c                    |  30 +++++--
>  fs/quota/kqid.c                   |  12 ++-
>  fs/stat.c                         |  12 +--
>  include/linux/uidgid.h            |  24 +++--
>  include/linux/user_namespace.h    |   4 +
>  include/net/scm.h                 |   4 +-
>  ipc/mqueue.c                      |   2 +-
>  ipc/msg.c                         |   8 +-
>  ipc/sem.c                         |   8 +-
>  ipc/shm.c                         |   8 +-
>  ipc/util.c                        |   8 +-
>  kernel/acct.c                     |   4 +-
>  kernel/exit.c                     |   6 +-
>  kernel/groups.c                   |   2 +-
>  kernel/signal.c                   |  16 ++--
>  kernel/sys.c                      |  24 ++---
>  kernel/trace/trace.c              |   2 +-
>  kernel/tsacct.c                   |   4 +-
>  kernel/uid16.c                    |  22 ++---
>  kernel/user.c                     |   1 +
>  kernel/user_namespace.c           | 178 +++++++++++++++++++++++++++++++++++---
>  net/appletalk/atalk_proc.c        |   2 +-
>  net/ax25/ax25_uid.c               |   4 +-
>  net/bluetooth/af_bluetooth.c      |   2 +-
>  net/core/sock.c                   |   4 +-
>  net/ipv4/inet_diag.c              |   2 +-
>  net/ipv4/ping.c                   |   2 +-
>  net/ipv4/raw.c                    |   2 +-
>  net/ipv4/sysctl_net_ipv4.c        |   4 +-
>  net/ipv4/tcp_ipv4.c               |   6 +-
>  net/ipv4/udp.c                    |   2 +-
>  net/ipv6/datagram.c               |   2 +-
>  net/ipv6/ip6_flowlabel.c          |   2 +-
>  net/ipv6/tcp_ipv6.c               |   6 +-
>  net/ipx/ipx_proc.c                |   2 +-
>  net/key/af_key.c                  |   2 +-
>  net/llc/llc_proc.c                |   2 +-
>  net/netfilter/nfnetlink_log.c     |   4 +-
>  net/packet/af_packet.c            |   2 +-
>  net/packet/diag.c                 |   2 +-
>  net/phonet/socket.c               |   4 +-
>  net/sctp/proc.c                   |   4 +-
>  net/sunrpc/svcauth_unix.c         |   4 +-
>  security/keys/keyctl.c            |   4 +-
>  security/keys/proc.c              |   6 +-
>  64 files changed, 395 insertions(+), 197 deletions(-)
>
> diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
> index ffb93f49..6440f8e 100644
> --- a/arch/alpha/kernel/osf_sys.c
> +++ b/arch/alpha/kernel/osf_sys.c
> @@ -277,8 +277,8 @@ linux_to_osf_stat(struct kstat *lstat, struct osf_stat __user *osf_stat)
>         tmp.st_dev      = lstat->dev;
>         tmp.st_mode     = lstat->mode;
>         tmp.st_nlink    = lstat->nlink;
> -       tmp.st_uid      = from_kuid_munged(current_user_ns(), lstat->uid);
> -       tmp.st_gid      = from_kgid_munged(current_user_ns(), lstat->gid);
> +       tmp.st_uid      = from_kuid_tp_munged(current_user_ns(), lstat->uid);
> +       tmp.st_gid      = from_kgid_tp_munged(current_user_ns(), lstat->gid);
>         tmp.st_rdev     = lstat->rdev;
>         tmp.st_ldev     = lstat->rdev;
>         tmp.st_size     = lstat->size;
> diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
> index 087acb5..47748eb 100644
> --- a/arch/arm/kernel/sys_oabi-compat.c
> +++ b/arch/arm/kernel/sys_oabi-compat.c
> @@ -124,8 +124,8 @@ static long cp_oldabi_stat64(struct kstat *stat,
>         tmp.__st_ino = stat->ino;
>         tmp.st_mode = stat->mode;
>         tmp.st_nlink = stat->nlink;
> -       tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
> -       tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
> +       tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
> +       tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
>         tmp.st_rdev = huge_encode_dev(stat->rdev);
>         tmp.st_size = stat->size;
>         tmp.st_blocks = stat->blocks;
> diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
> index b3a124d..071b9c0 100644
> --- a/arch/ia64/kernel/signal.c
> +++ b/arch/ia64/kernel/signal.c
> @@ -209,7 +209,7 @@ ia64_rt_sigreturn (struct sigscratch *scr)
>         si.si_errno = 0;
>         si.si_code = SI_KERNEL;
>         si.si_pid = task_pid_vnr(current);
> -       si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       si.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>         si.si_addr = sc;
>         force_sig_info(SIGSEGV, &si, current);
>         return retval;
> @@ -306,7 +306,7 @@ force_sigsegv_info (int sig, void __user *addr)
>         si.si_errno = 0;
>         si.si_code = SI_KERNEL;
>         si.si_pid = task_pid_vnr(current);
> -       si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       si.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>         si.si_addr = addr;
>         force_sig_info(SIGSEGV, &si, current);
>         return 1;
> diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
> index 437e611..31a39ba 100644
> --- a/arch/s390/kernel/compat_linux.c
> +++ b/arch/s390/kernel/compat_linux.c
> @@ -136,9 +136,9 @@ COMPAT_SYSCALL_DEFINE3(s390_getresuid16, u16 __user *, ruidp,
>         int retval;
>         u16 ruid, euid, suid;
>
> -       ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
> -       euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
> -       suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
> +       ruid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       euid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->euid));
> +       suid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->suid));
>
>         if (!(retval   = put_user(ruid, ruidp)) &&
>             !(retval   = put_user(euid, euidp)))
> @@ -160,9 +160,9 @@ COMPAT_SYSCALL_DEFINE3(s390_getresgid16, u16 __user *, rgidp,
>         int retval;
>         u16 rgid, egid, sgid;
>
> -       rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
> -       egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
> -       sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
> +       rgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->gid));
> +       egid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->egid));
> +       sgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->sgid));
>
>         if (!(retval   = put_user(rgid, rgidp)) &&
>             !(retval   = put_user(egid, egidp)))
> @@ -190,7 +190,7 @@ static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info
>
>         for (i = 0; i < group_info->ngroups; i++) {
>                 kgid = GROUP_AT(group_info, i);
> -               group = (u16)from_kgid_munged(user_ns, kgid);
> +               group = (u16)from_kgid_tp_munged(user_ns, kgid);
>                 if (put_user(group, grouplist+i))
>                         return -EFAULT;
>         }
> @@ -271,22 +271,22 @@ COMPAT_SYSCALL_DEFINE2(s390_setgroups16, int, gidsetsize, u16 __user *, grouplis
>
>  COMPAT_SYSCALL_DEFINE0(s390_getuid16)
>  {
> -       return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
> +       return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_uid()));
>  }
>
>  COMPAT_SYSCALL_DEFINE0(s390_geteuid16)
>  {
> -       return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
> +       return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_euid()));
>  }
>
>  COMPAT_SYSCALL_DEFINE0(s390_getgid16)
>  {
> -       return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
> +       return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_gid()));
>  }
>
>  COMPAT_SYSCALL_DEFINE0(s390_getegid16)
>  {
> -       return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
> +       return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_egid()));
>  }
>
>  #ifdef CONFIG_SYSVIPC
> @@ -366,8 +366,8 @@ static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat)
>         tmp.__st_ino = (u32)stat->ino;
>         tmp.st_mode = stat->mode;
>         tmp.st_nlink = (unsigned int)stat->nlink;
> -       tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
> -       tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
> +       tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
> +       tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
>         tmp.st_rdev = huge_encode_dev(stat->rdev);
>         tmp.st_size = stat->size;
>         tmp.st_blksize = (u32)stat->blksize;
> diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
> index 022c30c..e65acab 100644
> --- a/arch/sparc/kernel/sys_sparc32.c
> +++ b/arch/sparc/kernel/sys_sparc32.c
> @@ -76,8 +76,8 @@ static int cp_compat_stat64(struct kstat *stat,
>         err |= put_user(stat->ino, &statbuf->st_ino);
>         err |= put_user(stat->mode, &statbuf->st_mode);
>         err |= put_user(stat->nlink, &statbuf->st_nlink);
> -       err |= put_user(from_kuid_munged(current_user_ns(), stat->uid), &statbuf->st_uid);
> -       err |= put_user(from_kgid_munged(current_user_ns(), stat->gid), &statbuf->st_gid);
> +       err |= put_user(from_kuid_tp_munged(current_user_ns(), stat->uid), &statbuf->st_uid);
> +       err |= put_user(from_kgid_tp_munged(current_user_ns(), stat->gid), &statbuf->st_gid);
>         err |= put_user(huge_encode_dev(stat->rdev), &statbuf->st_rdev);
>         err |= put_user(0, (unsigned long __user *) &statbuf->__pad3[0]);
>         err |= put_user(stat->size, &statbuf->st_size);
> diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
> index 719cd70..e8d4532 100644
> --- a/arch/x86/ia32/sys_ia32.c
> +++ b/arch/x86/ia32/sys_ia32.c
> @@ -71,8 +71,8 @@ static int cp_stat64(struct stat64 __user *ubuf, struct kstat *stat)
>  {
>         typeof(ubuf->st_uid) uid = 0;
>         typeof(ubuf->st_gid) gid = 0;
> -       SET_UID(uid, from_kuid_munged(current_user_ns(), stat->uid));
> -       SET_GID(gid, from_kgid_munged(current_user_ns(), stat->gid));
> +       SET_UID(uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
> +       SET_GID(gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
>         if (!access_ok(VERIFY_WRITE, ubuf, sizeof(struct stat64)) ||
>             __put_user(huge_encode_dev(stat->dev), &ubuf->st_dev) ||
>             __put_user(stat->ino, &ubuf->__st_ino) ||
> diff --git a/drivers/android/binder.c b/drivers/android/binder.c
> index 16288e7..c8fcf71 100644
> --- a/drivers/android/binder.c
> +++ b/drivers/android/binder.c
> @@ -2400,7 +2400,7 @@ retry:
>                 }
>                 tr.code = t->code;
>                 tr.flags = t->flags;
> -               tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
> +               tr.sender_euid = from_kuid_tp(current_user_ns(), t->sender_euid);
>
>                 if (t->from) {
>                         struct task_struct *sender = t->from->proc->tsk;
> diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
> index 5d469b2..07ab3d4 100644
> --- a/drivers/gpu/drm/drm_info.c
> +++ b/drivers/gpu/drm/drm_info.c
> @@ -186,7 +186,7 @@ int drm_clients_info(struct seq_file *m, void *data)
>                            priv->minor->index,
>                            priv->is_master ? 'y' : 'n',
>                            priv->authenticated ? 'y' : 'n',
> -                          from_kuid_munged(seq_user_ns(m), priv->uid),
> +                          from_kuid_tp_munged(seq_user_ns(m), priv->uid),
>                            priv->magic);
>                 rcu_read_unlock();
>         }
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index b7a39771c..2e18837 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -181,7 +181,7 @@ static int drm_getclient(struct drm_device *dev, void *data,
>         if (client->idx == 0) {
>                 client->auth = file_priv->authenticated;
>                 client->pid = pid_vnr(file_priv->pid);
> -               client->uid = from_kuid_munged(current_user_ns(),
> +               client->uid = from_kuid_tp_munged(current_user_ns(),
>                                                file_priv->uid);
>                 client->magic = 0;
>                 client->iocs = 0;
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index e16487c..8965a26 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -1659,7 +1659,7 @@ static ssize_t tun_show_owner(struct device *dev, struct device_attribute *attr,
>         struct tun_struct *tun = netdev_priv(to_net_dev(dev));
>         return uid_valid(tun->owner)?
>                 sprintf(buf, "%u\n",
> -                       from_kuid_munged(current_user_ns(), tun->owner)):
> +                       from_kuid_tp_munged(current_user_ns(), tun->owner)) :
>                 sprintf(buf, "-1\n");
>  }
>
> @@ -1669,7 +1669,7 @@ static ssize_t tun_show_group(struct device *dev, struct device_attribute *attr,
>         struct tun_struct *tun = netdev_priv(to_net_dev(dev));
>         return gid_valid(tun->group) ?
>                 sprintf(buf, "%u\n",
> -                       from_kgid_munged(current_user_ns(), tun->group)):
> +                       from_kgid_tp_munged(current_user_ns(), tun->group)) :
>                 sprintf(buf, "-1\n");
>  }
>
> diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
> index c7fcc74..4c75a4c 100644
> --- a/fs/autofs4/dev-ioctl.c
> +++ b/fs/autofs4/dev-ioctl.c
> @@ -460,9 +460,9 @@ static int autofs_dev_ioctl_requester(struct file *fp,
>                 autofs4_expire_wait(path.dentry, 0);
>                 spin_lock(&sbi->fs_lock);
>                 param->requester.uid =
> -                       from_kuid_munged(current_user_ns(), ino->uid);
> +                       from_kuid_tp_munged(current_user_ns(), ino->uid);
>                 param->requester.gid =
> -                       from_kgid_munged(current_user_ns(), ino->gid);
> +                       from_kgid_tp_munged(current_user_ns(), ino->gid);
>                 spin_unlock(&sbi->fs_lock);
>         }
>         path_put(&path);
> diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
> index 0146d91..7d55752 100644
> --- a/fs/autofs4/waitq.c
> +++ b/fs/autofs4/waitq.c
> @@ -157,8 +157,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
>                 packet->name[wq->name.len] = '\0';
>                 packet->dev = wq->dev;
>                 packet->ino = wq->ino;
> -               packet->uid = from_kuid_munged(user_ns, wq->uid);
> -               packet->gid = from_kgid_munged(user_ns, wq->gid);
> +               packet->uid = from_kuid_tp_munged(user_ns, wq->uid);
> +               packet->gid = from_kgid_tp_munged(user_ns, wq->gid);
>                 packet->pid = wq->pid;
>                 packet->tgid = wq->tgid;
>                 break;
> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
> index a7a28110..3f9be45 100644
> --- a/fs/binfmt_elf.c
> +++ b/fs/binfmt_elf.c
> @@ -240,10 +240,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
>         NEW_AUX_ENT(AT_BASE, interp_load_addr);
>         NEW_AUX_ENT(AT_FLAGS, 0);
>         NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
> -       NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
> -       NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
> -       NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
> -       NEW_AUX_ENT(AT_EGID, from_kgid_munged(cred->user_ns, cred->egid));
> +       NEW_AUX_ENT(AT_UID, from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       NEW_AUX_ENT(AT_EUID, from_kuid_tp_munged(cred->user_ns, cred->euid));
> +       NEW_AUX_ENT(AT_GID, from_kgid_tp_munged(cred->user_ns, cred->gid));
> +       NEW_AUX_ENT(AT_EGID, from_kgid_tp_munged(cred->user_ns, cred->egid));
>         NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
>         NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes);
>  #ifdef ELF_HWCAP2
> @@ -1474,8 +1474,8 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
>         psinfo->pr_flag = p->flags;
>         rcu_read_lock();
>         cred = __task_cred(p);
> -       SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
> -       SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
> +       SET_UID(psinfo->pr_uid, from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       SET_GID(psinfo->pr_gid, from_kgid_tp_munged(cred->user_ns, cred->gid));
>         rcu_read_unlock();
>         strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
>
> diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
> index 2035893..9d76bb7 100644
> --- a/fs/binfmt_elf_fdpic.c
> +++ b/fs/binfmt_elf_fdpic.c
> @@ -644,10 +644,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
>         NEW_AUX_ENT(AT_BASE,    interp_params->elfhdr_addr);
>         NEW_AUX_ENT(AT_FLAGS,   0);
>         NEW_AUX_ENT(AT_ENTRY,   exec_params->entry_addr);
> -       NEW_AUX_ENT(AT_UID,     (elf_addr_t) from_kuid_munged(cred->user_ns, cred->uid));
> -       NEW_AUX_ENT(AT_EUID,    (elf_addr_t) from_kuid_munged(cred->user_ns, cred->euid));
> -       NEW_AUX_ENT(AT_GID,     (elf_addr_t) from_kgid_munged(cred->user_ns, cred->gid));
> -       NEW_AUX_ENT(AT_EGID,    (elf_addr_t) from_kgid_munged(cred->user_ns, cred->egid));
> +       NEW_AUX_ENT(AT_UID,     (elf_addr_t) from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       NEW_AUX_ENT(AT_EUID,    (elf_addr_t) from_kuid_tp_munged(cred->user_ns, cred->euid));
> +       NEW_AUX_ENT(AT_GID,     (elf_addr_t) from_kgid_tp_munged(cred->user_ns, cred->gid));
> +       NEW_AUX_ENT(AT_EGID,    (elf_addr_t) from_kgid_tp_munged(cred->user_ns, cred->egid));
>         NEW_AUX_ENT(AT_SECURE,  security_bprm_secureexec(bprm));
>         NEW_AUX_ENT(AT_EXECFN,  bprm->exec);
>
> @@ -1434,8 +1434,8 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
>         psinfo->pr_flag = p->flags;
>         rcu_read_lock();
>         cred = __task_cred(p);
> -       SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
> -       SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
> +       SET_UID(psinfo->pr_uid, from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       SET_GID(psinfo->pr_gid, from_kgid_tp_munged(cred->user_ns, cred->gid));
>         rcu_read_unlock();
>         strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
>
> diff --git a/fs/compat.c b/fs/compat.c
> index be6e48b..8e3cb5d3 100644
> --- a/fs/compat.c
> +++ b/fs/compat.c
> @@ -142,8 +142,8 @@ static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
>         tmp.st_nlink = stat->nlink;
>         if (tmp.st_nlink != stat->nlink)
>                 return -EOVERFLOW;
> -       SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
> -       SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
> +       SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
> +       SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
>         tmp.st_rdev = old_encode_dev(stat->rdev);
>         if ((u64) stat->size > MAX_NON_LFS)
>                 return -EOVERFLOW;
> diff --git a/fs/fcntl.c b/fs/fcntl.c
> index 350a2c8..bcba367 100644
> --- a/fs/fcntl.c
> +++ b/fs/fcntl.c
> @@ -225,8 +225,8 @@ static int f_getowner_uids(struct file *filp, unsigned long arg)
>         int err;
>
>         read_lock(&filp->f_owner.lock);
> -       src[0] = from_kuid(user_ns, filp->f_owner.uid);
> -       src[1] = from_kuid(user_ns, filp->f_owner.euid);
> +       src[0] = from_kuid_tp(user_ns, filp->f_owner.uid);
> +       src[1] = from_kuid_tp(user_ns, filp->f_owner.euid);
>         read_unlock(&filp->f_owner.lock);
>
>         err  = put_user(src[0], &dst[0]);
> diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
> index 0a3f9b5..65631c2 100644
> --- a/fs/ncpfs/ioctl.c
> +++ b/fs/ncpfs/ioctl.c
> @@ -45,7 +45,7 @@ ncp_get_fs_info(struct ncp_server * server, struct inode *inode,
>                 return -EINVAL;
>         }
>         /* TODO: info.addr = server->m.serv_addr; */
> -       SET_UID(info.mounted_uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
> +       SET_UID(info.mounted_uid, from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid));
>         info.connection         = server->connection;
>         info.buffer_size        = server->buffer_size;
>         info.volume_number      = NCP_FINFO(inode)->volNumber;
> @@ -69,7 +69,7 @@ ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode,
>                 ncp_dbg(1, "info.version invalid: %d\n", info2.version);
>                 return -EINVAL;
>         }
> -       info2.mounted_uid   = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
> +       info2.mounted_uid   = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
>         info2.connection    = server->connection;
>         info2.buffer_size   = server->buffer_size;
>         info2.volume_number = NCP_FINFO(inode)->volNumber;
> @@ -135,7 +135,7 @@ ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode,
>                 ncp_dbg(1, "info.version invalid: %d\n", info2.version);
>                 return -EINVAL;
>         }
> -       info2.mounted_uid   = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
> +       info2.mounted_uid   = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
>         info2.connection    = server->connection;
>         info2.buffer_size   = server->buffer_size;
>         info2.volume_number = NCP_FINFO(inode)->volNumber;
> @@ -347,21 +347,21 @@ static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg
>                 {
>                         u16 uid;
>
> -                       SET_UID(uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
> +                       SET_UID(uid, from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid));
>                         if (put_user(uid, (u16 __user *)argp))
>                                 return -EFAULT;
>                         return 0;
>                 }
>         case NCP_IOC_GETMOUNTUID32:
>         {
> -               uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
> +               uid_t uid = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
>                 if (put_user(uid, (u32 __user *)argp))
>                         return -EFAULT;
>                 return 0;
>         }
>         case NCP_IOC_GETMOUNTUID64:
>         {
> -               uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
> +               uid_t uid = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
>                 if (put_user(uid, (u64 __user *)argp))
>                         return -EFAULT;
>                 return 0;
> diff --git a/fs/posix_acl.c b/fs/posix_acl.c
> index 8a4a266..c3e7ecb 100644
> --- a/fs/posix_acl.c
> +++ b/fs/posix_acl.c
> @@ -653,14 +653,21 @@ static void posix_acl_fix_xattr_userns(
>                 return;
>
>         for (end = entry + count; entry != end; entry++) {
> +               /* from_k[ug]id_tp is safe here because the callers are:
> +                *  - posix_acl_fix_xattr_from_user() calls this with
> +                *    to=init_user_ns
> +                *  - posix_acl_fix_xattr_to_user() calls this with
> +                *    to=current_user_ns() and is only used in getxattr(),
> +                *    which copies the result to the caller
> +                */
>                 switch(le16_to_cpu(entry->e_tag)) {
>                 case ACL_USER:
>                         uid = make_kuid(from, le32_to_cpu(entry->e_id));
> -                       entry->e_id = cpu_to_le32(from_kuid(to, uid));
> +                       entry->e_id = cpu_to_le32(from_kuid_tp(to, uid));
>                         break;
>                 case ACL_GROUP:
>                         gid = make_kgid(from, le32_to_cpu(entry->e_id));
> -                       entry->e_id = cpu_to_le32(from_kgid(to, gid));
> +                       entry->e_id = cpu_to_le32(from_kgid_tp(to, gid));
>                         break;
>                 default:
>                         break;
> diff --git a/fs/proc/array.c b/fs/proc/array.c
> index 88c7de1..a827d6e 100644
> --- a/fs/proc/array.c
> +++ b/fs/proc/array.c
> @@ -198,20 +198,20 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
>                 "FDSize:\t%d\nGroups:\t",
>                 get_task_state(p),
>                 tgid, ngid, pid_nr_ns(pid, ns), ppid, tpid,
> -               from_kuid_munged(user_ns, cred->uid),
> -               from_kuid_munged(user_ns, cred->euid),
> -               from_kuid_munged(user_ns, cred->suid),
> -               from_kuid_munged(user_ns, cred->fsuid),
> -               from_kgid_munged(user_ns, cred->gid),
> -               from_kgid_munged(user_ns, cred->egid),
> -               from_kgid_munged(user_ns, cred->sgid),
> -               from_kgid_munged(user_ns, cred->fsgid),
> +               from_kuid_tp_munged(user_ns, cred->uid),
> +               from_kuid_tp_munged(user_ns, cred->euid),
> +               from_kuid_tp_munged(user_ns, cred->suid),
> +               from_kuid_tp_munged(user_ns, cred->fsuid),
> +               from_kgid_tp_munged(user_ns, cred->gid),
> +               from_kgid_tp_munged(user_ns, cred->egid),
> +               from_kgid_tp_munged(user_ns, cred->sgid),
> +               from_kgid_tp_munged(user_ns, cred->fsgid),
>                 max_fds);
>
>         group_info = cred->group_info;
>         for (g = 0; g < group_info->ngroups; g++)
>                 seq_printf(m, "%d ",
> -                          from_kgid_munged(user_ns, GROUP_AT(group_info, g)));
> +                          from_kgid_tp_munged(user_ns, GROUP_AT(group_info, g)));
>         put_cred(cred);
>
>  #ifdef CONFIG_PID_NS
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index a11eb71..2203b81 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -1236,7 +1236,7 @@ static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
>         if (!task)
>                 return -ESRCH;
>         length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
> -                          from_kuid(file->f_cred->user_ns,
> +                          from_kuid_tp(file->f_cred->user_ns,
>                                      audit_get_loginuid(task)));
>         put_task_struct(task);
>         return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
> @@ -2744,7 +2744,8 @@ static const struct file_operations proc_projid_map_operations = {
>         .release        = proc_id_map_release,
>  };
>
> -static int proc_setgroups_open(struct inode *inode, struct file *file)
> +static int proc_nsadmin_open(struct inode *inode, struct file *file,
> +       int (*show)(struct seq_file *, void *))
>  {
>         struct user_namespace *ns = NULL;
>         struct task_struct *task;
> @@ -2767,7 +2768,7 @@ static int proc_setgroups_open(struct inode *inode, struct file *file)
>                         goto err_put_ns;
>         }
>
> -       ret = single_open(file, &proc_setgroups_show, ns);
> +       ret = single_open(file, show, ns);
>         if (ret)
>                 goto err_put_ns;
>
> @@ -2778,7 +2779,7 @@ err:
>         return ret;
>  }
>
> -static int proc_setgroups_release(struct inode *inode, struct file *file)
> +static int proc_nsadmin_release(struct inode *inode, struct file *file)
>  {
>         struct seq_file *seq = file->private_data;
>         struct user_namespace *ns = seq->private;
> @@ -2787,12 +2788,30 @@ static int proc_setgroups_release(struct inode *inode, struct file *file)
>         return ret;
>  }
>
> +static int proc_setgroups_open(struct inode *inode, struct file *file)
> +{
> +       return proc_nsadmin_open(inode, file, &proc_setgroups_show);
> +}
> +
>  static const struct file_operations proc_setgroups_operations = {
>         .open           = proc_setgroups_open,
>         .write          = proc_setgroups_write,
>         .read           = seq_read,
>         .llseek         = seq_lseek,
> -       .release        = proc_setgroups_release,
> +       .release        = proc_nsadmin_release,
> +};
> +
> +static int proc_transparent_open(struct inode *inode, struct file *file)
> +{
> +       return proc_nsadmin_open(inode, file, &proc_transparent_show);
> +}
> +
> +static const struct file_operations proc_transparent_operations = {
> +       .open           = proc_transparent_open,
> +       .write          = proc_transparent_write,
> +       .read           = seq_read,
> +       .llseek         = seq_lseek,
> +       .release        = proc_nsadmin_release,
>  };
>  #endif /* CONFIG_USER_NS */
>
> @@ -2901,6 +2920,7 @@ static const struct pid_entry tgid_base_stuff[] = {
>         REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
>         REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
>         REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
> +       REG("transparent", S_IRUGO|S_IWUSR, proc_transparent_operations),
>  #endif
>  #ifdef CONFIG_CHECKPOINT_RESTORE
>         REG("timers",     S_IRUGO, proc_timers_operations),
> diff --git a/fs/quota/kqid.c b/fs/quota/kqid.c
> index ebc5e62..1cd3d8b 100644
> --- a/fs/quota/kqid.c
> +++ b/fs/quota/kqid.c
> @@ -66,11 +66,15 @@ EXPORT_SYMBOL(qid_lt);
>   */
>  qid_t from_kqid(struct user_namespace *targ, struct kqid kqid)
>  {
> +       /* transparent UID/GID are okay here; this method is only
> +        * called either with targ=&init_user_ns or directly
> +        * before a copy_from_user(), with current_user_ns()
> +        */
>         switch (kqid.type) {
>         case USRQUOTA:
> -               return from_kuid(targ, kqid.uid);
> +               return from_kuid_tp(targ, kqid.uid);
>         case GRPQUOTA:
> -               return from_kgid(targ, kqid.gid);
> +               return from_kgid_tp(targ, kqid.gid);
>         case PRJQUOTA:
>                 return from_kprojid(targ, kqid.projid);
>         default:
> @@ -101,9 +105,9 @@ qid_t from_kqid_munged(struct user_namespace *targ, struct kqid kqid)
>  {
>         switch (kqid.type) {
>         case USRQUOTA:
> -               return from_kuid_munged(targ, kqid.uid);
> +               return from_kuid_tp_munged(targ, kqid.uid);
>         case GRPQUOTA:
> -               return from_kgid_munged(targ, kqid.gid);
> +               return from_kgid_tp_munged(targ, kqid.gid);
>         case PRJQUOTA:
>                 return from_kprojid_munged(targ, kqid.projid);
>         default:
> diff --git a/fs/stat.c b/fs/stat.c
> index bc045c7..b8cff6c 100644
> --- a/fs/stat.c
> +++ b/fs/stat.c
> @@ -160,8 +160,8 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta
>         tmp.st_nlink = stat->nlink;
>         if (tmp.st_nlink != stat->nlink)
>                 return -EOVERFLOW;
> -       SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
> -       SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
> +       SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
> +       SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
>         tmp.st_rdev = old_encode_dev(stat->rdev);
>  #if BITS_PER_LONG == 32
>         if (stat->size > MAX_NON_LFS)
> @@ -246,8 +246,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
>         tmp.st_nlink = stat->nlink;
>         if (tmp.st_nlink != stat->nlink)
>                 return -EOVERFLOW;
> -       SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
> -       SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
> +       SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
> +       SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
>         tmp.st_rdev = encode_dev(stat->rdev);
>         tmp.st_size = stat->size;
>         tmp.st_atime = stat->atime.tv_sec;
> @@ -381,8 +381,8 @@ static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
>  #endif
>         tmp.st_mode = stat->mode;
>         tmp.st_nlink = stat->nlink;
> -       tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
> -       tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
> +       tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
> +       tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
>         tmp.st_atime = stat->atime.tv_sec;
>         tmp.st_atime_nsec = stat->atime.tv_nsec;
>         tmp.st_mtime = stat->mtime.tv_sec;
> diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
> index 0383552..bb7e2c5 100644
> --- a/include/linux/uidgid.h
> +++ b/include/linux/uidgid.h
> @@ -124,8 +124,10 @@ extern kgid_t make_kgid(struct user_namespace *from, gid_t gid);
>
>  extern uid_t from_kuid(struct user_namespace *to, kuid_t uid);
>  extern gid_t from_kgid(struct user_namespace *to, kgid_t gid);
> -extern uid_t from_kuid_munged(struct user_namespace *to, kuid_t uid);
> -extern gid_t from_kgid_munged(struct user_namespace *to, kgid_t gid);
> +extern uid_t from_kuid_tp(struct user_namespace *to, kuid_t uid);
> +extern gid_t from_kgid_tp(struct user_namespace *to, kgid_t gid);
> +extern uid_t from_kuid_tp_munged(struct user_namespace *to, kuid_t uid);
> +extern gid_t from_kgid_tp_munged(struct user_namespace *to, kgid_t gid);
>
>  static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid)
>  {
> @@ -159,17 +161,27 @@ static inline gid_t from_kgid(struct user_namespace *to, kgid_t kgid)
>         return __kgid_val(kgid);
>  }
>
> -static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
> +static inline uid_t from_kuid_tp(struct user_namespace *to, kuid_t kuid)
>  {
> -       uid_t uid = from_kuid(to, kuid);
> +       return __kuid_val(kuid);
> +}
> +
> +static inline gid_t from_kgid_tp(struct user_namespace *to, kgid_t kgid)
> +{
> +       return __kgid_val(kgid);
> +}
> +
> +static inline uid_t from_kuid_tp_munged(struct user_namespace *to, kuid_t kuid)
> +{
> +       uid_t uid = from_kuid_tp(to, kuid);
>         if (uid == (uid_t)-1)
>                 uid = overflowuid;
>         return uid;
>  }
>
> -static inline gid_t from_kgid_munged(struct user_namespace *to, kgid_t kgid)
> +static inline gid_t from_kgid_tp_munged(struct user_namespace *to, kgid_t kgid)
>  {
> -       gid_t gid = from_kgid(to, kgid);
> +       gid_t gid = from_kgid_tp(to, kgid);
>         if (gid == (gid_t)-1)
>                 gid = overflowgid;
>         return gid;
> diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
> index 8297e5b..18291ac 100644
> --- a/include/linux/user_namespace.h
> +++ b/include/linux/user_namespace.h
> @@ -28,6 +28,8 @@ struct user_namespace {
>         struct uid_gid_map      projid_map;
>         atomic_t                count;
>         struct user_namespace   *parent;
> +       /* self for normal ns; first opaque parent for transparent ns */
> +       struct user_namespace   *opaque;
>         int                     level;
>         kuid_t                  owner;
>         kgid_t                  group;
> @@ -71,6 +73,8 @@ extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, lo
>  extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_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 ssize_t proc_transparent_write(struct file *, const char __user *, size_t, loff_t *);
> +extern int proc_transparent_show(struct seq_file *m, void *v);
>  extern bool userns_may_setgroups(const struct user_namespace *ns);
>  #else
>
> diff --git a/include/net/scm.h b/include/net/scm.h
> index 59fa93c..601450a 100644
> --- a/include/net/scm.h
> +++ b/include/net/scm.h
> @@ -121,8 +121,8 @@ static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
>                 struct user_namespace *current_ns = current_user_ns();
>                 struct ucred ucreds = {
>                         .pid = scm->creds.pid,
> -                       .uid = from_kuid_munged(current_ns, scm->creds.uid),
> -                       .gid = from_kgid_munged(current_ns, scm->creds.gid),
> +                       .uid = from_kuid_tp_munged(current_ns, scm->creds.uid),
> +                       .gid = from_kgid_tp_munged(current_ns, scm->creds.gid),
>                 };
>                 put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
>         }
> diff --git a/ipc/mqueue.c b/ipc/mqueue.c
> index ade739f..b6de6f4 100644
> --- a/ipc/mqueue.c
> +++ b/ipc/mqueue.c
> @@ -645,7 +645,7 @@ static void __do_notify(struct mqueue_inode_info *info)
>                         rcu_read_lock();
>                         sig_i.si_pid = task_tgid_nr_ns(current,
>                                                 ns_of_pid(info->notify_owner));
> -                       sig_i.si_uid = from_kuid_munged(info->notify_user_ns, current_uid());
> +                       sig_i.si_uid = from_kuid_tp_munged(info->notify_user_ns, current_uid());
>                         rcu_read_unlock();
>
>                         kill_pid_info(info->notify.sigev_signo,
> diff --git a/ipc/msg.c b/ipc/msg.c
> index 1471db9..e49adeb 100644
> --- a/ipc/msg.c
> +++ b/ipc/msg.c
> @@ -1052,10 +1052,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
>                    msq->q_qnum,
>                    msq->q_lspid,
>                    msq->q_lrpid,
> -                  from_kuid_munged(user_ns, msq->q_perm.uid),
> -                  from_kgid_munged(user_ns, msq->q_perm.gid),
> -                  from_kuid_munged(user_ns, msq->q_perm.cuid),
> -                  from_kgid_munged(user_ns, msq->q_perm.cgid),
> +                  from_kuid_tp_munged(user_ns, msq->q_perm.uid),
> +                  from_kgid_tp_munged(user_ns, msq->q_perm.gid),
> +                  from_kuid_tp_munged(user_ns, msq->q_perm.cuid),
> +                  from_kgid_tp_munged(user_ns, msq->q_perm.cgid),
>                    msq->q_stime,
>                    msq->q_rtime,
>                    msq->q_ctime);
> diff --git a/ipc/sem.c b/ipc/sem.c
> index b3757ea..99589c3 100644
> --- a/ipc/sem.c
> +++ b/ipc/sem.c
> @@ -2208,10 +2208,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
>                    sma->sem_perm.id,
>                    sma->sem_perm.mode,
>                    sma->sem_nsems,
> -                  from_kuid_munged(user_ns, sma->sem_perm.uid),
> -                  from_kgid_munged(user_ns, sma->sem_perm.gid),
> -                  from_kuid_munged(user_ns, sma->sem_perm.cuid),
> -                  from_kgid_munged(user_ns, sma->sem_perm.cgid),
> +                  from_kuid_tp_munged(user_ns, sma->sem_perm.uid),
> +                  from_kgid_tp_munged(user_ns, sma->sem_perm.gid),
> +                  from_kuid_tp_munged(user_ns, sma->sem_perm.cuid),
> +                  from_kgid_tp_munged(user_ns, sma->sem_perm.cgid),
>                    sem_otime,
>                    sma->sem_ctime);
>
> diff --git a/ipc/shm.c b/ipc/shm.c
> index 1328251..599ab25 100644
> --- a/ipc/shm.c
> +++ b/ipc/shm.c
> @@ -1392,10 +1392,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
>                    shp->shm_cprid,
>                    shp->shm_lprid,
>                    shp->shm_nattch,
> -                  from_kuid_munged(user_ns, shp->shm_perm.uid),
> -                  from_kgid_munged(user_ns, shp->shm_perm.gid),
> -                  from_kuid_munged(user_ns, shp->shm_perm.cuid),
> -                  from_kgid_munged(user_ns, shp->shm_perm.cgid),
> +                  from_kuid_tp_munged(user_ns, shp->shm_perm.uid),
> +                  from_kgid_tp_munged(user_ns, shp->shm_perm.gid),
> +                  from_kuid_tp_munged(user_ns, shp->shm_perm.cuid),
> +                  from_kgid_tp_munged(user_ns, shp->shm_perm.cgid),
>                    shp->shm_atim,
>                    shp->shm_dtim,
>                    shp->shm_ctim,
> diff --git a/ipc/util.c b/ipc/util.c
> index 798cad1..81b39d5 100644
> --- a/ipc/util.c
> +++ b/ipc/util.c
> @@ -513,10 +513,10 @@ int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
>  void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out)
>  {
>         out->key        = in->key;
> -       out->uid        = from_kuid_munged(current_user_ns(), in->uid);
> -       out->gid        = from_kgid_munged(current_user_ns(), in->gid);
> -       out->cuid       = from_kuid_munged(current_user_ns(), in->cuid);
> -       out->cgid       = from_kgid_munged(current_user_ns(), in->cgid);
> +       out->uid        = from_kuid_tp_munged(current_user_ns(), in->uid);
> +       out->gid        = from_kgid_tp_munged(current_user_ns(), in->gid);
> +       out->cuid       = from_kuid_tp_munged(current_user_ns(), in->cuid);
> +       out->cgid       = from_kgid_tp_munged(current_user_ns(), in->cgid);
>         out->mode       = in->mode;
>         out->seq        = in->seq;
>  }
> diff --git a/kernel/acct.c b/kernel/acct.c
> index 74963d1..bbfa0b9 100644
> --- a/kernel/acct.c
> +++ b/kernel/acct.c
> @@ -489,8 +489,8 @@ static void do_acct_process(struct bsd_acct_struct *acct)
>
>         fill_ac(&ac);
>         /* we really need to bite the bullet and change layout */
> -       ac.ac_uid = from_kuid_munged(file->f_cred->user_ns, orig_cred->uid);
> -       ac.ac_gid = from_kgid_munged(file->f_cred->user_ns, orig_cred->gid);
> +       ac.ac_uid = from_kuid_tp_munged(file->f_cred->user_ns, orig_cred->uid);
> +       ac.ac_gid = from_kgid_tp_munged(file->f_cred->user_ns, orig_cred->gid);
>  #if ACCT_VERSION == 1 || ACCT_VERSION == 2
>         /* backward-compatible 16 bit fields */
>         ac.ac_uid16 = ac.ac_uid;
> diff --git a/kernel/exit.c b/kernel/exit.c
> index 9e6e135..207e284 100644
> --- a/kernel/exit.c
> +++ b/kernel/exit.c
> @@ -983,7 +983,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
>  {
>         int state, retval, status;
>         pid_t pid = task_pid_vnr(p);
> -       uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
> +       uid_t uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
>         struct siginfo __user *infop;
>
>         if (!likely(wo->wo_flags & WEXITED))
> @@ -1189,7 +1189,7 @@ static int wait_task_stopped(struct wait_opts *wo,
>         if (!unlikely(wo->wo_flags & WNOWAIT))
>                 *p_code = 0;
>
> -       uid = from_kuid_munged(current_user_ns(), task_uid(p));
> +       uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
>  unlock_sig:
>         spin_unlock_irq(&p->sighand->siglock);
>         if (!exit_code)
> @@ -1263,7 +1263,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
>         }
>         if (!unlikely(wo->wo_flags & WNOWAIT))
>                 p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
> -       uid = from_kuid_munged(current_user_ns(), task_uid(p));
> +       uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
>         spin_unlock_irq(&p->sighand->siglock);
>
>         pid = task_pid_vnr(p);
> diff --git a/kernel/groups.c b/kernel/groups.c
> index 74d431d..ec2ecf8 100644
> --- a/kernel/groups.c
> +++ b/kernel/groups.c
> @@ -70,7 +70,7 @@ static int groups_to_user(gid_t __user *grouplist,
>
>         for (i = 0; i < count; i++) {
>                 gid_t gid;
> -               gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i));
> +               gid = from_kgid_tp_munged(user_ns, GROUP_AT(group_info, i));
>                 if (put_user(gid, grouplist+i))
>                         return -EFAULT;
>         }
> diff --git a/kernel/signal.c b/kernel/signal.c
> index 96e9bc4..2d7e071 100644
> --- a/kernel/signal.c
> +++ b/kernel/signal.c
> @@ -958,7 +958,7 @@ static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_str
>                 return;
>
>         rcu_read_lock();
> -       info->si_uid = from_kuid_munged(task_cred_xxx(t, user_ns),
> +       info->si_uid = from_kuid_tp_munged(task_cred_xxx(t, user_ns),
>                                         make_kuid(current_user_ns(), info->si_uid));
>         rcu_read_unlock();
>  }
> @@ -1027,7 +1027,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
>                         q->info.si_code = SI_USER;
>                         q->info.si_pid = task_tgid_nr_ns(current,
>                                                         task_active_pid_ns(t));
> -                       q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +                       q->info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>                         break;
>                 case (unsigned long) SEND_SIG_PRIV:
>                         q->info.si_signo = sig;
> @@ -1609,7 +1609,7 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
>          */
>         rcu_read_lock();
>         info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(tsk->parent));
> -       info.si_uid = from_kuid_munged(task_cred_xxx(tsk->parent, user_ns),
> +       info.si_uid = from_kuid_tp_munged(task_cred_xxx(tsk->parent, user_ns),
>                                        task_uid(tsk));
>         rcu_read_unlock();
>
> @@ -1695,7 +1695,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,
>          */
>         rcu_read_lock();
>         info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(parent));
> -       info.si_uid = from_kuid_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
> +       info.si_uid = from_kuid_tp_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
>         rcu_read_unlock();
>
>         task_cputime(tsk, &utime, &stime);
> @@ -1904,7 +1904,7 @@ static void ptrace_do_notify(int signr, int exit_code, int why)
>         info.si_signo = signr;
>         info.si_code = exit_code;
>         info.si_pid = task_pid_vnr(current);
> -       info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>
>         /* Let the debugger run.  */
>         ptrace_stop(exit_code, why, 1, &info);
> @@ -2113,7 +2113,7 @@ static int ptrace_signal(int signr, siginfo_t *info)
>                 info->si_code = SI_USER;
>                 rcu_read_lock();
>                 info->si_pid = task_pid_vnr(current->parent);
> -               info->si_uid = from_kuid_munged(current_user_ns(),
> +               info->si_uid = from_kuid_tp_munged(current_user_ns(),
>                                                 task_uid(current->parent));
>                 rcu_read_unlock();
>         }
> @@ -2856,7 +2856,7 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
>         info.si_errno = 0;
>         info.si_code = SI_USER;
>         info.si_pid = task_tgid_vnr(current);
> -       info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>
>         return kill_something_info(sig, &info, pid);
>  }
> @@ -2899,7 +2899,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
>         info.si_errno = 0;
>         info.si_code = SI_TKILL;
>         info.si_pid = task_tgid_vnr(current);
> -       info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
> +       info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
>
>         return do_send_specific(tgid, pid, sig, &info);
>  }
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 89d5be4..ce7833d 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -648,9 +648,9 @@ SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t _
>         int retval;
>         uid_t ruid, euid, suid;
>
> -       ruid = from_kuid_munged(cred->user_ns, cred->uid);
> -       euid = from_kuid_munged(cred->user_ns, cred->euid);
> -       suid = from_kuid_munged(cred->user_ns, cred->suid);
> +       ruid = from_kuid_tp_munged(cred->user_ns, cred->uid);
> +       euid = from_kuid_tp_munged(cred->user_ns, cred->euid);
> +       suid = from_kuid_tp_munged(cred->user_ns, cred->suid);
>
>         retval = put_user(ruid, ruidp);
>         if (!retval) {
> @@ -722,9 +722,9 @@ SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t _
>         int retval;
>         gid_t rgid, egid, sgid;
>
> -       rgid = from_kgid_munged(cred->user_ns, cred->gid);
> -       egid = from_kgid_munged(cred->user_ns, cred->egid);
> -       sgid = from_kgid_munged(cred->user_ns, cred->sgid);
> +       rgid = from_kgid_tp_munged(cred->user_ns, cred->gid);
> +       egid = from_kgid_tp_munged(cred->user_ns, cred->egid);
> +       sgid = from_kgid_tp_munged(cred->user_ns, cred->sgid);
>
>         retval = put_user(rgid, rgidp);
>         if (!retval) {
> @@ -751,7 +751,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
>         kuid_t kuid;
>
>         old = current_cred();
> -       old_fsuid = from_kuid_munged(old->user_ns, old->fsuid);
> +       old_fsuid = from_kuid_tp_munged(old->user_ns, old->fsuid);
>
>         kuid = make_kuid(old->user_ns, uid);
>         if (!uid_valid(kuid))
> @@ -790,7 +790,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
>         kgid_t kgid;
>
>         old = current_cred();
> -       old_fsgid = from_kgid_munged(old->user_ns, old->fsgid);
> +       old_fsgid = from_kgid_tp_munged(old->user_ns, old->fsgid);
>
>         kgid = make_kgid(old->user_ns, gid);
>         if (!gid_valid(kgid))
> @@ -858,25 +858,25 @@ SYSCALL_DEFINE0(getppid)
>  SYSCALL_DEFINE0(getuid)
>  {
>         /* Only we change this so SMP safe */
> -       return from_kuid_munged(current_user_ns(), current_uid());
> +       return from_kuid_tp_munged(current_user_ns(), current_uid());
>  }
>
>  SYSCALL_DEFINE0(geteuid)
>  {
>         /* Only we change this so SMP safe */
> -       return from_kuid_munged(current_user_ns(), current_euid());
> +       return from_kuid_tp_munged(current_user_ns(), current_euid());
>  }
>
>  SYSCALL_DEFINE0(getgid)
>  {
>         /* Only we change this so SMP safe */
> -       return from_kgid_munged(current_user_ns(), current_gid());
> +       return from_kgid_tp_munged(current_user_ns(), current_gid());
>  }
>
>  SYSCALL_DEFINE0(getegid)
>  {
>         /* Only we change this so SMP safe */
> -       return from_kgid_munged(current_user_ns(), current_egid());
> +       return from_kgid_tp_munged(current_user_ns(), current_egid());
>  }
>
>  void do_sys_times(struct tms *tms)
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 8a4bd6b..d4f03ee 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -2743,7 +2743,7 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter)
>         seq_printf(m, "#    | task: %.16s-%d "
>                    "(uid:%d nice:%ld policy:%ld rt_prio:%ld)\n",
>                    data->comm, data->pid,
> -                  from_kuid_munged(seq_user_ns(m), data->uid), data->nice,
> +                  from_kuid_tp_munged(seq_user_ns(m), data->uid), data->nice,
>                    data->policy, data->rt_priority);
>         seq_puts(m, "#    -----------------\n");
>
> diff --git a/kernel/tsacct.c b/kernel/tsacct.c
> index f8e26ab..19e3a61 100644
> --- a/kernel/tsacct.c
> +++ b/kernel/tsacct.c
> @@ -60,8 +60,8 @@ void bacct_add_tsk(struct user_namespace *user_ns,
>         stats->ac_pid    = task_pid_nr_ns(tsk, pid_ns);
>         rcu_read_lock();
>         tcred = __task_cred(tsk);
> -       stats->ac_uid    = from_kuid_munged(user_ns, tcred->uid);
> -       stats->ac_gid    = from_kgid_munged(user_ns, tcred->gid);
> +       stats->ac_uid    = from_kuid_tp_munged(user_ns, tcred->uid);
> +       stats->ac_gid    = from_kgid_tp_munged(user_ns, tcred->gid);
>         stats->ac_ppid   = pid_alive(tsk) ?
>                 task_tgid_nr_ns(rcu_dereference(tsk->real_parent), pid_ns) : 0;
>         rcu_read_unlock();
> diff --git a/kernel/uid16.c b/kernel/uid16.c
> index d58cc4d..f91f270 100644
> --- a/kernel/uid16.c
> +++ b/kernel/uid16.c
> @@ -63,9 +63,9 @@ SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euid
>         int retval;
>         old_uid_t ruid, euid, suid;
>
> -       ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
> -       euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
> -       suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
> +       ruid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->uid));
> +       euid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->euid));
> +       suid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->suid));
>
>         if (!(retval   = put_user(ruid, ruidp)) &&
>             !(retval   = put_user(euid, euidp)))
> @@ -87,9 +87,9 @@ SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egid
>         int retval;
>         old_gid_t rgid, egid, sgid;
>
> -       rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
> -       egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
> -       sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
> +       rgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->gid));
> +       egid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->egid));
> +       sgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->sgid));
>
>         if (!(retval   = put_user(rgid, rgidp)) &&
>             !(retval   = put_user(egid, egidp)))
> @@ -118,7 +118,7 @@ static int groups16_to_user(old_gid_t __user *grouplist,
>
>         for (i = 0; i < group_info->ngroups; i++) {
>                 kgid = GROUP_AT(group_info, i);
> -               group = high2lowgid(from_kgid_munged(user_ns, kgid));
> +               group = high2lowgid(from_kgid_tp_munged(user_ns, kgid));
>                 if (put_user(group, grouplist+i))
>                         return -EFAULT;
>         }
> @@ -198,20 +198,20 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)
>
>  SYSCALL_DEFINE0(getuid16)
>  {
> -       return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
> +       return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_uid()));
>  }
>
>  SYSCALL_DEFINE0(geteuid16)
>  {
> -       return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
> +       return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_euid()));
>  }
>
>  SYSCALL_DEFINE0(getgid16)
>  {
> -       return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
> +       return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_gid()));
>  }
>
>  SYSCALL_DEFINE0(getegid16)
>  {
> -       return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
> +       return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_egid()));
>  }
> diff --git a/kernel/user.c b/kernel/user.c
> index b069ccb..e1fd9e5 100644
> --- a/kernel/user.c
> +++ b/kernel/user.c
> @@ -48,6 +48,7 @@ struct user_namespace init_user_ns = {
>                 },
>         },
>         .count = ATOMIC_INIT(3),
> +       .opaque = &init_user_ns,
>         .owner = GLOBAL_ROOT_UID,
>         .group = GLOBAL_ROOT_GID,
>         .ns.inum = PROC_USER_INIT_INO,
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 9bafc21..44a7d3d 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -98,6 +98,7 @@ int create_user_ns(struct cred *new)
>         atomic_set(&ns->count, 1);
>         /* Leave the new->user_ns reference with the new user namespace. */
>         ns->parent = parent_ns;
> +       ns->opaque = ns;
>         ns->level = parent_ns->level + 1;
>         ns->owner = owner;
>         ns->group = group;
> @@ -249,7 +250,8 @@ EXPORT_SYMBOL(make_kuid);
>   *     @kuid: The kernel internal uid to start with.
>   *
>   *     Map @kuid into the user-namespace specified by @targ and
> - *     return the resulting uid.
> + *     return the resulting uid. This ignores transparent user
> + *     namespaces and is therefore appropriate for security checks.
>   *
>   *     There is always a mapping into the initial user_namespace.
>   *
> @@ -263,33 +265,63 @@ uid_t from_kuid(struct user_namespace *targ, kuid_t kuid)
>  EXPORT_SYMBOL(from_kuid);
>
>  /**
> - *     from_kuid_munged - Create a uid from a kuid user-namespace pair.
> + *     from_kuid_tp - Create a uid from a kuid user-namespace pair.
> + *     @targ: The user namespace we want a uid in.
> + *     @kuid: The kernel internal uid to start with.
> + *
> + *     Map @kuid into the user-namespace specified by @targ and
> + *     return the resulting uid.
> + *
> + *     This function is *not* appropriate for security checks because
> + *     if @targ is transparent, the mappings of an ancestor namespace
> + *     are used. If you intend to do anything with the result apart from
> + *     returning it to a process in @targ, you might want to use
> + *     from_kuid() instead.
> + *
> + *     There is always a mapping into the initial user_namespace.
> + *
> + *     If @kuid is not visible in @targ (uid_t)-1 is returned.
> + */
> +uid_t from_kuid_tp(struct user_namespace *targ, kuid_t kuid)
> +{
> +       /* Map the uid from a global kernel uid */
> +       struct user_namespace *opaque = READ_ONCE(targ->opaque);
> +
> +       return map_id_up(&opaque->uid_map, __kuid_val(kuid));
> +}
> +EXPORT_SYMBOL(from_kuid_tp);
> +
> +/**
> + *     from_kuid_tp_munged - Create a uid from a kuid user-namespace pair.
>   *     @targ: The user namespace we want a uid in.
>   *     @kuid: The kernel internal uid to start with.
>   *
>   *     Map @kuid into the user-namespace specified by @targ and
>   *     return the resulting uid.
>   *
> + *     This function is *not* appropriate for security checks; see the
> + *     comment above from_kuid_tp().
> + *
>   *     There is always a mapping into the initial user_namespace.
>   *
> - *     Unlike from_kuid from_kuid_munged never fails and always
> - *     returns a valid uid.  This makes from_kuid_munged appropriate
> + *     Unlike from_kuid_tp from_kuid_tp_munged never fails and always
> + *     returns a valid uid.  This makes from_kuid_tp_munged appropriate
>   *     for use in syscalls like stat and getuid where failing the
>   *     system call and failing to provide a valid uid are not an
>   *     options.
>   *
>   *     If @kuid has no mapping in @targ overflowuid is returned.
>   */
> -uid_t from_kuid_munged(struct user_namespace *targ, kuid_t kuid)
> +uid_t from_kuid_tp_munged(struct user_namespace *targ, kuid_t kuid)
>  {
>         uid_t uid;
> -       uid = from_kuid(targ, kuid);
> +       uid = from_kuid_tp(targ, kuid);
>
>         if (uid == (uid_t) -1)
>                 uid = overflowuid;
>         return uid;
>  }
> -EXPORT_SYMBOL(from_kuid_munged);
> +EXPORT_SYMBOL(from_kuid_tp_munged);
>
>  /**
>   *     make_kgid - Map a user-namespace gid pair into a kgid.
> @@ -317,7 +349,8 @@ EXPORT_SYMBOL(make_kgid);
>   *     @kgid: The kernel internal gid to start with.
>   *
>   *     Map @kgid into the user-namespace specified by @targ and
> - *     return the resulting gid.
> + *     return the resulting gid. This ignores transparent user
> + *     namespaces and is therefore appropriate for security checks.
>   *
>   *     There is always a mapping into the initial user_namespace.
>   *
> @@ -331,32 +364,62 @@ gid_t from_kgid(struct user_namespace *targ, kgid_t kgid)
>  EXPORT_SYMBOL(from_kgid);
>
>  /**
> - *     from_kgid_munged - Create a gid from a kgid user-namespace pair.
> + *     from_kgid_tp - Create a gid from a kgid user-namespace pair.
>   *     @targ: The user namespace we want a gid in.
>   *     @kgid: The kernel internal gid to start with.
>   *
>   *     Map @kgid into the user-namespace specified by @targ and
>   *     return the resulting gid.
>   *
> + *     This function is *not* appropriate for security checks because
> + *     if @targ is transparent, the mappings of an ancestor namespace
> + *     are used. If you intend to do anything with the result apart from
> + *     returning it to a process in @targ, you might want to use
> + *     from_kgid() instead.
> + *
>   *     There is always a mapping into the initial user_namespace.
>   *
> - *     Unlike from_kgid from_kgid_munged never fails and always
> - *     returns a valid gid.  This makes from_kgid_munged appropriate
> + *     If @kgid is not visible in @targ (gid_t)-1 is returned.
> + */
> +gid_t from_kgid_tp(struct user_namespace *targ, kgid_t kgid)
> +{
> +       /* Map the gid from a global kernel gid */
> +       struct user_namespace *opaque = READ_ONCE(targ->opaque);
> +
> +       return map_id_up(&opaque->gid_map, __kgid_val(kgid));
> +}
> +EXPORT_SYMBOL(from_kgid_tp);
> +
> +/**
> + *     from_kgid_tp_munged - Create a gid from a kgid user-namespace pair.
> + *     @targ: The user namespace we want a gid in.
> + *     @kgid: The kernel internal gid to start with.
> + *
> + *     Map @kgid into the user-namespace specified by @targ and
> + *     return the resulting gid.
> + *
> + *     This function is *not* appropriate for security checks; see the
> + *     comment above from_kgid_tp().
> + *
> + *     There is always a mapping into the initial user_namespace.
> + *
> + *     Unlike from_kgid_tp from_kgid_tp_munged never fails and always
> + *     returns a valid gid.  This makes from_kgid_tp_munged appropriate
>   *     for use in syscalls like stat and getgid where failing the
>   *     system call and failing to provide a valid gid are not options.
>   *
>   *     If @kgid has no mapping in @targ overflowgid is returned.
>   */
> -gid_t from_kgid_munged(struct user_namespace *targ, kgid_t kgid)
> +gid_t from_kgid_tp_munged(struct user_namespace *targ, kgid_t kgid)
>  {
>         gid_t gid;
> -       gid = from_kgid(targ, kgid);
> +       gid = from_kgid_tp(targ, kgid);
>
>         if (gid == (gid_t) -1)
>                 gid = overflowgid;
>         return gid;
>  }
> -EXPORT_SYMBOL(from_kgid_munged);
> +EXPORT_SYMBOL(from_kgid_tp_munged);
>
>  /**
>   *     make_kprojid - Map a user-namespace projid pair into a kprojid.
> @@ -811,6 +874,18 @@ static bool new_idmap_permitted(const struct file *file,
>                                 struct uid_gid_map *new_map)
>  {
>         const struct cred *cred = file->f_cred;
> +       unsigned int idx;
> +
> +       /* Don't allow non-identity mappings in transparent namespaces. */
> +       if (ns != ns->opaque) {
> +               for (idx = 0; idx < new_map->nr_extents; idx++) {
> +                       struct uid_gid_extent *ext = &new_map->extent[idx];
> +
> +                       if (ext->first != ext->lower_first)
> +                               return false;
> +               }
> +       }
> +
>         /* Don't allow mappings that would allow anything that wouldn't
>          * be allowed without the establishment of unprivileged mappings.
>          */
> @@ -922,6 +997,81 @@ out_unlock:
>         goto out;
>  }
>
> +int proc_transparent_show(struct seq_file *seq, void *v)
> +{
> +       struct user_namespace *ns = seq->private;
> +       struct user_namespace *opaque = READ_ONCE(ns->opaque);
> +
> +       seq_printf(seq, "%d\n", (ns == opaque) ? 0 : 1);
> +       return 0;
> +}
> +
> +ssize_t proc_transparent_write(struct file *file, const char __user *buf,
> +                            size_t count, loff_t *ppos)
> +{
> +       struct seq_file *seq = file->private_data;
> +       struct user_namespace *ns = seq->private;
> +       char kbuf[8], *pos;
> +       bool transparent;
> +       ssize_t ret;
> +
> +       /* Only allow a very narrow range of strings to be written */
> +       ret = -EINVAL;
> +       if ((*ppos != 0) || (count >= sizeof(kbuf)))
> +               goto out;
> +
> +       /* What was written? */
> +       ret = -EFAULT;
> +       if (copy_from_user(kbuf, buf, count))
> +               goto out;
> +       kbuf[count] = '\0';
> +       pos = kbuf;
> +
> +       /* What is being requested? */
> +       ret = -EINVAL;
> +       if (pos[0] == '1') {
> +               pos += 1;
> +               transparent = true;
> +       } else if (pos[0] == '0') {
> +               pos += 1;
> +               transparent = false;
> +       } else
> +               goto out;
> +
> +       /* Verify there is not trailing junk on the line */
> +       pos = skip_spaces(pos);
> +       if (*pos != '\0')
> +               goto out;
> +
> +       ret = -EPERM;
> +       mutex_lock(&userns_state_mutex);
> +       /* Is the requested state different from the current one? */
> +       if (transparent != (ns->opaque != ns)) {
> +               /* You can't turn off transparent mode. */
> +               if (!transparent)
> +                       goto out_unlock;
> +               /* If there are existing mappings, they might be
> +                * non-identity mappings. Therefore, block transparent
> +                * mode. This also prevents making the init namespace
> +                * transparent (which wouldn't work).
> +                */
> +               if (ns->uid_map.nr_extents != 0 || ns->gid_map.nr_extents != 0)
> +                       goto out_unlock;
> +               /* Okay! Make the namespace transparent. */
> +               ns->opaque = ns->parent->opaque;
> +       }
> +       mutex_unlock(&userns_state_mutex);
> +
> +       /* Report a successful write */
> +       *ppos = count;
> +       ret = count;
> +out:
> +       return ret;
> +out_unlock:
> +       mutex_unlock(&userns_state_mutex);
> +       goto out;
> +}
> +
>  bool userns_may_setgroups(const struct user_namespace *ns)
>  {
>         bool allowed;
> diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c
> index af46bc4..c0f45bc 100644
> --- a/net/appletalk/atalk_proc.c
> +++ b/net/appletalk/atalk_proc.c
> @@ -184,7 +184,7 @@ static int atalk_seq_socket_show(struct seq_file *seq, void *v)
>                    sk_wmem_alloc_get(s),
>                    sk_rmem_alloc_get(s),
>                    s->sk_state,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)));
>  out:
>         return 0;
>  }
> diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c
> index 4ad2fb7..b28e339 100644
> --- a/net/ax25/ax25_uid.c
> +++ b/net/ax25/ax25_uid.c
> @@ -81,7 +81,7 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
>                 read_lock(&ax25_uid_lock);
>                 ax25_uid_for_each(ax25_uid, &ax25_uid_list) {
>                         if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
> -                               res = from_kuid_munged(current_user_ns(), ax25_uid->uid);
> +                               res = from_kuid_tp_munged(current_user_ns(), ax25_uid->uid);
>                                 break;
>                         }
>                 }
> @@ -175,7 +175,7 @@ static int ax25_uid_seq_show(struct seq_file *seq, void *v)
>
>                 pt = hlist_entry(v, struct ax25_uid_assoc, uid_node);
>                 seq_printf(seq, "%6d %s\n",
> -                       from_kuid_munged(seq_user_ns(seq), pt->uid),
> +                       from_kuid_tp_munged(seq_user_ns(seq), pt->uid),
>                         ax2asc(buf, &pt->call));
>         }
>         return 0;
> diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
> index 3df7aef..5e9906b 100644
> --- a/net/bluetooth/af_bluetooth.c
> +++ b/net/bluetooth/af_bluetooth.c
> @@ -625,7 +625,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
>                            atomic_read(&sk->sk_refcnt),
>                            sk_rmem_alloc_get(sk),
>                            sk_wmem_alloc_get(sk),
> -                          from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
> +                          from_kuid_tp(seq_user_ns(seq), sock_i_uid(sk)),
>                            sock_i_ino(sk),
>                            bt->parent? sock_i_ino(bt->parent): 0LU);
>
> diff --git a/net/core/sock.c b/net/core/sock.c
> index 08bf97e..1e6192d 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1020,8 +1020,8 @@ static void cred_to_ucred(struct pid *pid, const struct cred *cred,
>         if (cred) {
>                 struct user_namespace *current_ns = current_user_ns();
>
> -               ucred->uid = from_kuid_munged(current_ns, cred->euid);
> -               ucred->gid = from_kgid_munged(current_ns, cred->egid);
> +               ucred->uid = from_kuid_tp_munged(current_ns, cred->euid);
> +               ucred->gid = from_kgid_tp_munged(current_ns, cred->egid);
>         }
>  }
>
> diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
> index 25af124..26f1ec2 100644
> --- a/net/ipv4/inet_diag.c
> +++ b/net/ipv4/inet_diag.c
> @@ -134,7 +134,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
>         }
>  #endif
>
> -       r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
> +       r->idiag_uid = from_kuid_tp_munged(user_ns, sock_i_uid(sk));
>         r->idiag_inode = sock_i_ino(sk);
>
>         return 0;
> diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
> index 66ddcb6..f3b27ea 100644
> --- a/net/ipv4/ping.c
> +++ b/net/ipv4/ping.c
> @@ -1121,7 +1121,7 @@ static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
>                 sk_wmem_alloc_get(sp),
>                 sk_rmem_alloc_get(sp),
>                 0, 0L, 0,
> -               from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
> +               from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sp)),
>                 0, sock_i_ino(sp),
>                 atomic_read(&sp->sk_refcnt), sp,
>                 atomic_read(&sp->sk_drops));
> diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
> index 438f50c..759095ee 100644
> --- a/net/ipv4/raw.c
> +++ b/net/ipv4/raw.c
> @@ -1033,7 +1033,7 @@ static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
>                 sk_wmem_alloc_get(sp),
>                 sk_rmem_alloc_get(sp),
>                 0, 0L, 0,
> -               from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
> +               from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
>                 0, sock_i_ino(sp),
>                 atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));
>  }
> diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
> index 1cb67de..4e19885 100644
> --- a/net/ipv4/sysctl_net_ipv4.c
> +++ b/net/ipv4/sysctl_net_ipv4.c
> @@ -133,8 +133,8 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write,
>         };
>
>         inet_get_ping_group_range_table(table, &low, &high);
> -       urange[0] = from_kgid_munged(user_ns, low);
> -       urange[1] = from_kgid_munged(user_ns, high);
> +       urange[0] = from_kgid_tp_munged(user_ns, low);
> +       urange[1] = from_kgid_tp_munged(user_ns, high);
>         ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
>
>         if (write && ret == 0) {
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index 3708de2..5a5ae86 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -2161,8 +2161,8 @@ static void get_openreq4(const struct request_sock *req,
>                 1,    /* timers active (only the expire timer) */
>                 jiffies_delta_to_clock_t(delta),
>                 req->num_timeout,
> -               from_kuid_munged(seq_user_ns(f),
> -                                sock_i_uid(req->rsk_listener)),
> +               from_kuid_tp_munged(seq_user_ns(f),
> +                                   sock_i_uid(req->rsk_listener)),
>                 0,  /* non standard timer */
>                 0, /* open_requests have no inode */
>                 0,
> @@ -2217,7 +2217,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
>                 timer_active,
>                 jiffies_delta_to_clock_t(timer_expires - jiffies),
>                 icsk->icsk_retransmits,
> -               from_kuid_munged(seq_user_ns(f), sock_i_uid(sk)),
> +               from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sk)),
>                 icsk->icsk_probes_out,
>                 sock_i_ino(sk),
>                 atomic_read(&sk->sk_refcnt), sk,
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index 0ff31d9..e3579b2 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -2408,7 +2408,7 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f,
>                 sk_wmem_alloc_get(sp),
>                 sk_rmem_alloc_get(sp),
>                 0, 0L, 0,
> -               from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
> +               from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sp)),
>                 0, sock_i_ino(sp),
>                 atomic_read(&sp->sk_refcnt), sp,
>                 atomic_read(&sp->sk_drops));
> diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
> index 37874e2..d66dd7c 100644
> --- a/net/ipv6/datagram.c
> +++ b/net/ipv6/datagram.c
> @@ -1028,7 +1028,7 @@ void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
>                    sk_wmem_alloc_get(sp),
>                    sk_rmem_alloc_get(sp),
>                    0, 0L, 0,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
>                    0,
>                    sock_i_ino(sp),
>                    atomic_read(&sp->sk_refcnt), sp,
> diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
> index b912f0d..a1c7516 100644
> --- a/net/ipv6/ip6_flowlabel.c
> +++ b/net/ipv6/ip6_flowlabel.c
> @@ -789,7 +789,7 @@ static int ip6fl_seq_show(struct seq_file *seq, void *v)
>                            ((fl->share == IPV6_FL_S_PROCESS) ?
>                             pid_nr_ns(fl->owner.pid, state->pid_ns) :
>                             ((fl->share == IPV6_FL_S_USER) ?
> -                            from_kuid_munged(seq_user_ns(seq), fl->owner.uid) :
> +                            from_kuid_tp_munged(seq_user_ns(seq), fl->owner.uid) :
>                              0)),
>                            atomic_read(&fl->users),
>                            fl->linger/HZ,
> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
> index f36c2d0..04643ca 100644
> --- a/net/ipv6/tcp_ipv6.c
> +++ b/net/ipv6/tcp_ipv6.c
> @@ -1696,8 +1696,8 @@ static void get_openreq6(struct seq_file *seq,
>                    1,   /* timers active (only the expire timer) */
>                    jiffies_to_clock_t(ttd),
>                    req->num_timeout,
> -                  from_kuid_munged(seq_user_ns(seq),
> -                                   sock_i_uid(req->rsk_listener)),
> +                  from_kuid_tp_munged(seq_user_ns(seq),
> +                                      sock_i_uid(req->rsk_listener)),
>                    0,  /* non standard timer */
>                    0, /* open_requests have no inode */
>                    0, req);
> @@ -1760,7 +1760,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
>                    timer_active,
>                    jiffies_delta_to_clock_t(timer_expires - jiffies),
>                    icsk->icsk_retransmits,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
>                    icsk->icsk_probes_out,
>                    sock_i_ino(sp),
>                    atomic_read(&sp->sk_refcnt), sp,
> diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c
> index c1d247e..fc1d1fe 100644
> --- a/net/ipx/ipx_proc.c
> +++ b/net/ipx/ipx_proc.c
> @@ -217,7 +217,7 @@ static int ipx_seq_socket_show(struct seq_file *seq, void *v)
>                    sk_wmem_alloc_get(s),
>                    sk_rmem_alloc_get(s),
>                    s->sk_state,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)));
>  out:
>         return 0;
>  }
> diff --git a/net/key/af_key.c b/net/key/af_key.c
> index f9c9ecb..b76105f 100644
> --- a/net/key/af_key.c
> +++ b/net/key/af_key.c
> @@ -3714,7 +3714,7 @@ static int pfkey_seq_show(struct seq_file *f, void *v)
>                                atomic_read(&s->sk_refcnt),
>                                sk_rmem_alloc_get(s),
>                                sk_wmem_alloc_get(s),
> -                              from_kuid_munged(seq_user_ns(f), sock_i_uid(s)),
> +                              from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(s)),
>                                sock_i_ino(s)
>                                );
>         return 0;
> diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
> index 29c509c..96d2dff 100644
> --- a/net/llc/llc_proc.c
> +++ b/net/llc/llc_proc.c
> @@ -151,7 +151,7 @@ static int llc_seq_socket_show(struct seq_file *seq, void *v)
>                    sk_wmem_alloc_get(sk),
>                    sk_rmem_alloc_get(sk) - llc->copied_seq,
>                    sk->sk_state,
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                    llc->link);
>  out:
>         return 0;
> diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
> index 11f81c8..3263bca 100644
> --- a/net/netfilter/nfnetlink_log.c
> +++ b/net/netfilter/nfnetlink_log.c
> @@ -552,8 +552,8 @@ __build_packet_message(struct nfnl_log_net *log,
>                         struct file *file = sk->sk_socket->file;
>                         const struct cred *cred = file->f_cred;
>                         struct user_namespace *user_ns = inst->peer_user_ns;
> -                       __be32 uid = htonl(from_kuid_munged(user_ns, cred->fsuid));
> -                       __be32 gid = htonl(from_kgid_munged(user_ns, cred->fsgid));
> +                       __be32 uid = htonl(from_kuid_tp_munged(user_ns, cred->fsuid));
> +                       __be32 gid = htonl(from_kgid_tp_munged(user_ns, cred->fsgid));
>                         read_unlock_bh(&sk->sk_callback_lock);
>                         if (nla_put_be32(inst->skb, NFULA_UID, uid) ||
>                             nla_put_be32(inst->skb, NFULA_GID, gid))
> diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
> index 9bff6ef..21443c8 100644
> --- a/net/packet/af_packet.c
> +++ b/net/packet/af_packet.c
> @@ -4497,7 +4497,7 @@ static int packet_seq_show(struct seq_file *seq, void *v)
>                            po->ifindex,
>                            po->running,
>                            atomic_read(&s->sk_rmem_alloc),
> -                          from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)),
> +                          from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)),
>                            sock_i_ino(s));
>         }
>
> diff --git a/net/packet/diag.c b/net/packet/diag.c
> index 0ed68f0..40b8df7 100644
> --- a/net/packet/diag.c
> +++ b/net/packet/diag.c
> @@ -153,7 +153,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
>
>         if ((req->pdiag_show & PACKET_SHOW_INFO) &&
>             nla_put_u32(skb, PACKET_DIAG_UID,
> -                       from_kuid_munged(user_ns, sock_i_uid(sk))))
> +                       from_kuid_tp_munged(user_ns, sock_i_uid(sk))))
>                 goto out_nlmsg_trim;
>
>         if ((req->pdiag_show & PACKET_SHOW_MCLIST) &&
> diff --git a/net/phonet/socket.c b/net/phonet/socket.c
> index ffd5f22..fa90d85 100644
> --- a/net/phonet/socket.c
> +++ b/net/phonet/socket.c
> @@ -610,7 +610,7 @@ static int pn_sock_seq_show(struct seq_file *seq, void *v)
>                         sk->sk_protocol, pn->sobject, pn->dobject,
>                         pn->resource, sk->sk_state,
>                         sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
> -                       from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                       from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                         sock_i_ino(sk),
>                         atomic_read(&sk->sk_refcnt), sk,
>                         atomic_read(&sk->sk_drops));
> @@ -795,7 +795,7 @@ static int pn_res_seq_show(struct seq_file *seq, void *v)
>
>                 seq_printf(seq, "%02X %5u %lu",
>                            (int) (psk - pnres.sk),
> -                          from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                          from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                            sock_i_ino(sk));
>         }
>         seq_pad(seq, '\n');
> diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> index 4cb5aed..a893492 100644
> --- a/net/sctp/proc.c
> +++ b/net/sctp/proc.c
> @@ -224,7 +224,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v)
>                 seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5u %5lu ", ep, sk,
>                            sctp_sk(sk)->type, sk->sk_state, hash,
>                            epb->bind_addr.port,
> -                          from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                          from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                            sock_i_ino(sk));
>
>                 sctp_seq_dump_local_addrs(seq, epb);
> @@ -346,7 +346,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
>                    assoc->assoc_id,
>                    assoc->sndbuf_used,
>                    atomic_read(&assoc->rmem_alloc),
> -                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
> +                  from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
>                    sock_i_ino(sk),
>                    epb->bind_addr.port,
>                    assoc->peer.port);
> diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
> index dfacdc9..ce7a1ce 100644
> --- a/net/sunrpc/svcauth_unix.c
> +++ b/net/sunrpc/svcauth_unix.c
> @@ -562,9 +562,9 @@ static int unix_gid_show(struct seq_file *m,
>         else
>                 glen = 0;
>
> -       seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen);
> +       seq_printf(m, "%u %d:", from_kuid_tp_munged(user_ns, ug->uid), glen);
>         for (i = 0; i < glen; i++)
> -               seq_printf(m, " %d", from_kgid_munged(user_ns, GROUP_AT(ug->gi, i)));
> +               seq_printf(m, " %d", from_kgid_tp_munged(user_ns, GROUP_AT(ug->gi, i)));
>         seq_printf(m, "\n");
>         return 0;
>  }
> diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
> index d580ad0..b88f73d 100644
> --- a/security/keys/keyctl.c
> +++ b/security/keys/keyctl.c
> @@ -619,8 +619,8 @@ okay:
>         infobuf = kasprintf(GFP_KERNEL,
>                             "%s;%d;%d;%08x;",
>                             key->type->name,
> -                           from_kuid_munged(current_user_ns(), key->uid),
> -                           from_kgid_munged(current_user_ns(), key->gid),
> +                           from_kuid_tp_munged(current_user_ns(), key->uid),
> +                           from_kgid_tp_munged(current_user_ns(), key->gid),
>                             key->perm);
>         if (!infobuf)
>                 goto error2;
> diff --git a/security/keys/proc.c b/security/keys/proc.c
> index f0611a6..f71449d 100644
> --- a/security/keys/proc.c
> +++ b/security/keys/proc.c
> @@ -255,8 +255,8 @@ static int proc_keys_show(struct seq_file *m, void *v)
>                    atomic_read(&key->usage),
>                    xbuf,
>                    key->perm,
> -                  from_kuid_munged(seq_user_ns(m), key->uid),
> -                  from_kgid_munged(seq_user_ns(m), key->gid),
> +                  from_kuid_tp_munged(seq_user_ns(m), key->uid),
> +                  from_kgid_tp_munged(seq_user_ns(m), key->gid),
>                    key->type->name);
>
>  #undef showflag
> @@ -339,7 +339,7 @@ static int proc_key_users_show(struct seq_file *m, void *v)
>                 key_quota_root_maxbytes : key_quota_maxbytes;
>
>         seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
> -                  from_kuid_munged(seq_user_ns(m), user->uid),
> +                  from_kuid_tp_munged(seq_user_ns(m), user->uid),
>                    atomic_read(&user->usage),
>                    atomic_read(&user->nkeys),
>                    atomic_read(&user->nikeys),
> --
> 2.8.0.rc3.226.g39d4020
>



-- 
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] namespaces: add transparent user namespaces
  2016-06-25  0:23       ` [PATCH v2 2/2] namespaces: add transparent user namespaces Jann Horn
@ 2016-06-27 15:09             ` Eric W. Biederman
       [not found]         ` <1466814210-3778-2-git-send-email-jannh-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
  1 sibling, 0 replies; 14+ messages in thread
From: Eric W. Biederman @ 2016-06-27 15:09 UTC (permalink / raw)
  To: Jann Horn
  Cc: Christoph Lameter, Kees Cook,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Linux Containers,
	Oleg Nesterov, Calvin Owens, Seth Forshee, John Stultz, Al Viro,
	Andy Lutomirski, Cyrill Gorcunov, Jann Horn, Andrew Morton,
	Janis Danisevskis, Alexey Dobriyan, Michael Kerrisk


Added a few more relevant cc's.

Jann Horn <jannh-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> writes:

> This allows the admin of a user namespace to mark the namespace as
> transparent. All other namespaces, by default, are opaque.


I have just skimmed through this and at a high level this doesn't seem
too scary.  Having an identity mapped user namespace that just limits
you to using a subset of uids and gids while allowing displaying the
full range of uids and gids.

I don't quite get the use case and I would like to a little better
but in the long term this shouldn't cause any significant maintenance
issues, so I don't have any objects.

At the same time this isn't quite the time to merge this.  I am in the
process of slowly going through Seth's vfs changes to support things
such as truly unprivileged fuse support.  Those changes alter which
places can always be assumed to be init_user_ns (many fewer), and also
slightly change the set of from_kuid calls being made.

The changes that have made it through my review currently reside at:

    git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-next

Those vfs changes make it conceivable and simple from an infrastructure
standpoint to transition fileystems to unprivileged user namespace
mounts, with perhaps as little work as just setting FS_USER_NS.  At the
same time that won't be recommend because of the difficulty verifying
evil filesystem contents can't cause fs implementations to do bad things
is difficult.

That change means your first patch that just zaps all from_kuid_munged
users in init_user_ns isn't a particularly good idea.  I don't think it
is a good idea to have one set of rules for things that will always be
init_user_ns and another set of rules for code that will change.

The long and short of this is I am asking you to wait a week or so and
rebase this on my for-next branch so that we can confirm this change
interacts nicely will all of the other on-going work.

Thank you,
Eric Biederman

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] namespaces: add transparent user namespaces
@ 2016-06-27 15:09             ` Eric W. Biederman
  0 siblings, 0 replies; 14+ messages in thread
From: Eric W. Biederman @ 2016-06-27 15:09 UTC (permalink / raw)
  To: Jann Horn
  Cc: Andrew Morton, Kees Cook, Al Viro, Cyrill Gorcunov,
	Alexey Dobriyan, John Stultz, Janis Danisevskis, Calvin Owens,
	Jann Horn, Oleg Nesterov, Christoph Lameter, Andy Lutomirski,
	linux-kernel, Michael Kerrisk, Seth Forshee, Linux Containers


Added a few more relevant cc's.

Jann Horn <jannh@google.com> writes:

> This allows the admin of a user namespace to mark the namespace as
> transparent. All other namespaces, by default, are opaque.


I have just skimmed through this and at a high level this doesn't seem
too scary.  Having an identity mapped user namespace that just limits
you to using a subset of uids and gids while allowing displaying the
full range of uids and gids.

I don't quite get the use case and I would like to a little better
but in the long term this shouldn't cause any significant maintenance
issues, so I don't have any objects.

At the same time this isn't quite the time to merge this.  I am in the
process of slowly going through Seth's vfs changes to support things
such as truly unprivileged fuse support.  Those changes alter which
places can always be assumed to be init_user_ns (many fewer), and also
slightly change the set of from_kuid calls being made.

The changes that have made it through my review currently reside at:

    git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-next

Those vfs changes make it conceivable and simple from an infrastructure
standpoint to transition fileystems to unprivileged user namespace
mounts, with perhaps as little work as just setting FS_USER_NS.  At the
same time that won't be recommend because of the difficulty verifying
evil filesystem contents can't cause fs implementations to do bad things
is difficult.

That change means your first patch that just zaps all from_kuid_munged
users in init_user_ns isn't a particularly good idea.  I don't think it
is a good idea to have one set of rules for things that will always be
init_user_ns and another set of rules for code that will change.

The long and short of this is I am asking you to wait a week or so and
rebase this on my for-next branch so that we can confirm this change
interacts nicely will all of the other on-going work.

Thank you,
Eric Biederman

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] namespaces: add transparent user namespaces
       [not found]             ` <87mvm6y8g9.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
@ 2016-07-12 20:11               ` Mickaël Salaün
  0 siblings, 0 replies; 14+ messages in thread
From: Mickaël Salaün @ 2016-07-12 20:11 UTC (permalink / raw)
  To: Eric W. Biederman, Jann Horn
  Cc: Michael Kerrisk, Kees Cook, Linux Containers, Oleg Nesterov,
	Andrew Morton, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Seth Forshee,
	John Stultz, Al Viro, Andy Lutomirski, Cyrill Gorcunov,
	Jann Horn, Christoph Lameter, Janis Danisevskis, Calvin Owens,
	Alexey Dobriyan


[-- Attachment #1.1.1: Type: text/plain, Size: 2926 bytes --]

Hi,

I have been looking for this kind of feature for StemJail [1]. One of the main idea is to being able to create mount points inside a jail as an unprivileged user but to keep as much as possible the same environment from outside the jail. For now, I can only create a mapping for the current user, so when a process list any files belonging to another user/group it get "nobody", which seems weird from a user point of view :)

Regards,
Mickaël


1. https://github.com/stemjail/stemjail


On 27/06/2016 17:09, Eric W. Biederman wrote:
> 
> Added a few more relevant cc's.
> 
> Jann Horn <jannh-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> writes:
> 
>> This allows the admin of a user namespace to mark the namespace as
>> transparent. All other namespaces, by default, are opaque.
> 
> 
> I have just skimmed through this and at a high level this doesn't seem
> too scary.  Having an identity mapped user namespace that just limits
> you to using a subset of uids and gids while allowing displaying the
> full range of uids and gids.
> 
> I don't quite get the use case and I would like to a little better
> but in the long term this shouldn't cause any significant maintenance
> issues, so I don't have any objects.
> 
> At the same time this isn't quite the time to merge this.  I am in the
> process of slowly going through Seth's vfs changes to support things
> such as truly unprivileged fuse support.  Those changes alter which
> places can always be assumed to be init_user_ns (many fewer), and also
> slightly change the set of from_kuid calls being made.
> 
> The changes that have made it through my review currently reside at:
> 
>     git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-next
> 
> Those vfs changes make it conceivable and simple from an infrastructure
> standpoint to transition fileystems to unprivileged user namespace
> mounts, with perhaps as little work as just setting FS_USER_NS.  At the
> same time that won't be recommend because of the difficulty verifying
> evil filesystem contents can't cause fs implementations to do bad things
> is difficult.
> 
> That change means your first patch that just zaps all from_kuid_munged
> users in init_user_ns isn't a particularly good idea.  I don't think it
> is a good idea to have one set of rules for things that will always be
> init_user_ns and another set of rules for code that will change.
> 
> The long and short of this is I am asking you to wait a week or so and
> rebase this on my for-next branch so that we can confirm this change
> interacts nicely will all of the other on-going work.
> 
> Thank you,
> Eric Biederman
> _______________________________________________
> Containers mailing list
> Containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
> https://lists.linuxfoundation.org/mailman/listinfo/containers
> 




[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

[-- Attachment #2: Type: text/plain, Size: 205 bytes --]

_______________________________________________
Containers mailing list
Containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
https://lists.linuxfoundation.org/mailman/listinfo/containers

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] namespaces: add transparent user namespaces
  2016-06-27 15:09             ` Eric W. Biederman
  (?)
@ 2016-07-12 20:11             ` Mickaël Salaün
       [not found]               ` <57854EE5.1030707-WFhQfpSGs3bR7s880joybQ@public.gmane.org>
  -1 siblings, 1 reply; 14+ messages in thread
From: Mickaël Salaün @ 2016-07-12 20:11 UTC (permalink / raw)
  To: Eric W. Biederman, Jann Horn
  Cc: Christoph Lameter, Kees Cook, linux-kernel, Linux Containers,
	Oleg Nesterov, Calvin Owens, Seth Forshee, John Stultz, Al Viro,
	Andy Lutomirski, Cyrill Gorcunov, Jann Horn, Andrew Morton,
	Janis Danisevskis, Alexey Dobriyan, Michael Kerrisk


[-- Attachment #1.1: Type: text/plain, Size: 2863 bytes --]

Hi,

I have been looking for this kind of feature for StemJail [1]. One of the main idea is to being able to create mount points inside a jail as an unprivileged user but to keep as much as possible the same environment from outside the jail. For now, I can only create a mapping for the current user, so when a process list any files belonging to another user/group it get "nobody", which seems weird from a user point of view :)

Regards,
Mickaël


1. https://github.com/stemjail/stemjail


On 27/06/2016 17:09, Eric W. Biederman wrote:
> 
> Added a few more relevant cc's.
> 
> Jann Horn <jannh@google.com> writes:
> 
>> This allows the admin of a user namespace to mark the namespace as
>> transparent. All other namespaces, by default, are opaque.
> 
> 
> I have just skimmed through this and at a high level this doesn't seem
> too scary.  Having an identity mapped user namespace that just limits
> you to using a subset of uids and gids while allowing displaying the
> full range of uids and gids.
> 
> I don't quite get the use case and I would like to a little better
> but in the long term this shouldn't cause any significant maintenance
> issues, so I don't have any objects.
> 
> At the same time this isn't quite the time to merge this.  I am in the
> process of slowly going through Seth's vfs changes to support things
> such as truly unprivileged fuse support.  Those changes alter which
> places can always be assumed to be init_user_ns (many fewer), and also
> slightly change the set of from_kuid calls being made.
> 
> The changes that have made it through my review currently reside at:
> 
>     git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-next
> 
> Those vfs changes make it conceivable and simple from an infrastructure
> standpoint to transition fileystems to unprivileged user namespace
> mounts, with perhaps as little work as just setting FS_USER_NS.  At the
> same time that won't be recommend because of the difficulty verifying
> evil filesystem contents can't cause fs implementations to do bad things
> is difficult.
> 
> That change means your first patch that just zaps all from_kuid_munged
> users in init_user_ns isn't a particularly good idea.  I don't think it
> is a good idea to have one set of rules for things that will always be
> init_user_ns and another set of rules for code that will change.
> 
> The long and short of this is I am asking you to wait a week or so and
> rebase this on my for-next branch so that we can confirm this change
> interacts nicely will all of the other on-going work.
> 
> Thank you,
> Eric Biederman
> _______________________________________________
> Containers mailing list
> Containers@lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/containers
> 




[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] namespaces: add transparent user namespaces
  2016-07-12 20:11             ` Mickaël Salaün
@ 2016-07-12 22:32                   ` Eric W. Biederman
  0 siblings, 0 replies; 14+ messages in thread
From: Eric W. Biederman @ 2016-07-12 22:32 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Kees Cook, Alexey Dobriyan, Linux Containers, Calvin Owens,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Oleg Nesterov, Seth Forshee,
	John Stultz, Michael Kerrisk, Andy Lutomirski, Cyrill Gorcunov,
	Jann Horn, Andrew Morton, Janis Danisevskis, Christoph Lameter,
	Al Viro

Mickaël Salaün <mic@digikod.net> writes:
>
 Hi,
>
> I have been looking for this kind of feature for StemJail [1]. One of
> the main idea is to being able to create mount points inside a jail as
> an unprivileged user but to keep as much as possible the same
> environment from outside the jail. For now, I can only create a
> mapping for the current user, so when a process list any files
> belonging to another user/group it get "nobody", which seems weird
> from a user point of view :)

Weird but I have never found it harmful.

The big thrashing about with adding s_user_ns is complete in my tree so
I can accept a reasonable patch, and transparent user namespaces is on
the edge.

If transparent user namespaces solve anything except for weird.  They
are simple enough that given a good quality patch I will merge them.

Still I want to make certain they solve something real or else I am not
certain the added complexity is worth the maintenance burden.
Especially in a part of the code where getting confused and making small
mistakes results in security issues.

On the flip side it isn't that much weirder than from_kuid_munged today
so it may not be any kind of problem at all.

>
> Regards,
> Mickaël
>
>
> 1. https://github.com/stemjail/stemjail
>
>
> On 27/06/2016 17:09, Eric W. Biederman wrote:
>> 
>> Added a few more relevant cc's.
>> 
>> Jann Horn <jannh@google.com> writes:
>> 
>>> This allows the admin of a user namespace to mark the namespace as
>>> transparent. All other namespaces, by default, are opaque.
>> 
>> 
>> I have just skimmed through this and at a high level this doesn't seem
>> too scary.  Having an identity mapped user namespace that just limits
>> you to using a subset of uids and gids while allowing displaying the
>> full range of uids and gids.
>> 
>> I don't quite get the use case and I would like to a little better
>> but in the long term this shouldn't cause any significant maintenance
>> issues, so I don't have any objects.
>> 
>> At the same time this isn't quite the time to merge this.  I am in the
>> process of slowly going through Seth's vfs changes to support things
>> such as truly unprivileged fuse support.  Those changes alter which
>> places can always be assumed to be init_user_ns (many fewer), and also
>> slightly change the set of from_kuid calls being made.
>> 
>> The changes that have made it through my review currently reside at:
>> 
>>     git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-next
>> 
>> Those vfs changes make it conceivable and simple from an infrastructure
>> standpoint to transition fileystems to unprivileged user namespace
>> mounts, with perhaps as little work as just setting FS_USER_NS.  At the
>> same time that won't be recommend because of the difficulty verifying
>> evil filesystem contents can't cause fs implementations to do bad things
>> is difficult.
>> 
>> That change means your first patch that just zaps all from_kuid_munged
>> users in init_user_ns isn't a particularly good idea.  I don't think it
>> is a good idea to have one set of rules for things that will always be
>> init_user_ns and another set of rules for code that will change.
>> 
>> The long and short of this is I am asking you to wait a week or so and
>> rebase this on my for-next branch so that we can confirm this change
>> interacts nicely will all of the other on-going work.
>> 
>> Thank you,
>> Eric Biederman

Eric
_______________________________________________
Containers mailing list
Containers@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/containers

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] namespaces: add transparent user namespaces
@ 2016-07-12 22:32                   ` Eric W. Biederman
  0 siblings, 0 replies; 14+ messages in thread
From: Eric W. Biederman @ 2016-07-12 22:32 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Michael Kerrisk, Kees Cook, Linux Containers, Oleg Nesterov,
	Andrew Morton, linux-kernel, Seth Forshee, John Stultz, Al Viro,
	Andy Lutomirski, Cyrill Gorcunov, Jann Horn, Christoph Lameter,
	Janis Danisevskis, Calvin Owens, Alexey Dobriyan

Mickaël Salaün <mic@digikod.net> writes:
>
 Hi,
>
> I have been looking for this kind of feature for StemJail [1]. One of
> the main idea is to being able to create mount points inside a jail as
> an unprivileged user but to keep as much as possible the same
> environment from outside the jail. For now, I can only create a
> mapping for the current user, so when a process list any files
> belonging to another user/group it get "nobody", which seems weird
> from a user point of view :)

Weird but I have never found it harmful.

The big thrashing about with adding s_user_ns is complete in my tree so
I can accept a reasonable patch, and transparent user namespaces is on
the edge.

If transparent user namespaces solve anything except for weird.  They
are simple enough that given a good quality patch I will merge them.

Still I want to make certain they solve something real or else I am not
certain the added complexity is worth the maintenance burden.
Especially in a part of the code where getting confused and making small
mistakes results in security issues.

On the flip side it isn't that much weirder than from_kuid_munged today
so it may not be any kind of problem at all.

>
> Regards,
> Mickaël
>
>
> 1. https://github.com/stemjail/stemjail
>
>
> On 27/06/2016 17:09, Eric W. Biederman wrote:
>> 
>> Added a few more relevant cc's.
>> 
>> Jann Horn <jannh@google.com> writes:
>> 
>>> This allows the admin of a user namespace to mark the namespace as
>>> transparent. All other namespaces, by default, are opaque.
>> 
>> 
>> I have just skimmed through this and at a high level this doesn't seem
>> too scary.  Having an identity mapped user namespace that just limits
>> you to using a subset of uids and gids while allowing displaying the
>> full range of uids and gids.
>> 
>> I don't quite get the use case and I would like to a little better
>> but in the long term this shouldn't cause any significant maintenance
>> issues, so I don't have any objects.
>> 
>> At the same time this isn't quite the time to merge this.  I am in the
>> process of slowly going through Seth's vfs changes to support things
>> such as truly unprivileged fuse support.  Those changes alter which
>> places can always be assumed to be init_user_ns (many fewer), and also
>> slightly change the set of from_kuid calls being made.
>> 
>> The changes that have made it through my review currently reside at:
>> 
>>     git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-next
>> 
>> Those vfs changes make it conceivable and simple from an infrastructure
>> standpoint to transition fileystems to unprivileged user namespace
>> mounts, with perhaps as little work as just setting FS_USER_NS.  At the
>> same time that won't be recommend because of the difficulty verifying
>> evil filesystem contents can't cause fs implementations to do bad things
>> is difficult.
>> 
>> That change means your first patch that just zaps all from_kuid_munged
>> users in init_user_ns isn't a particularly good idea.  I don't think it
>> is a good idea to have one set of rules for things that will always be
>> init_user_ns and another set of rules for code that will change.
>> 
>> The long and short of this is I am asking you to wait a week or so and
>> rebase this on my for-next branch so that we can confirm this change
>> interacts nicely will all of the other on-going work.
>> 
>> Thank you,
>> Eric Biederman

Eric

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2016-07-12 22:47 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-23 11:11 [PATCH] namespaces: add transparent user namespaces Jann Horn
2016-06-23 18:50 ` Eric W. Biederman
2016-06-23 20:02   ` Jann Horn
2016-06-23 20:04     ` Eric W. Biederman
2016-06-25  0:23     ` [PATCH v2 1/2] namespaces: don't use from_k*id_munged() with init_user_ns Jann Horn
2016-06-25  0:23       ` [PATCH v2 2/2] namespaces: add transparent user namespaces Jann Horn
2016-06-27  3:34         ` Michael Kerrisk
2016-06-27  3:34           ` Michael Kerrisk
     [not found]         ` <1466814210-3778-2-git-send-email-jannh-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
2016-06-27 15:09           ` Eric W. Biederman
2016-06-27 15:09             ` Eric W. Biederman
2016-07-12 20:11             ` Mickaël Salaün
     [not found]               ` <57854EE5.1030707-WFhQfpSGs3bR7s880joybQ@public.gmane.org>
2016-07-12 22:32                 ` Eric W. Biederman
2016-07-12 22:32                   ` Eric W. Biederman
     [not found]             ` <87mvm6y8g9.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
2016-07-12 20:11               ` Mickaël Salaün

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.