user_namespace.c 3.4 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation, version 2 of the
 *  License.
 */

#include <linux/module.h>
#include <linux/nsproxy.h>
10
#include <linux/slab.h>
11
#include <linux/user_namespace.h>
12
#include <linux/highuid.h>
13
#include <linux/cred.h>
14

15 16
static struct kmem_cache *user_ns_cachep __read_mostly;

S
Serge E. Hallyn 已提交
17
/*
18 19 20 21 22 23
 * Create a new user namespace, deriving the creator from the user in the
 * passed credentials, and replacing that user with the new root user for the
 * new namespace.
 *
 * This is called by copy_creds(), which will finish setting the target task's
 * credentials.
S
Serge E. Hallyn 已提交
24
 */
25
int create_user_ns(struct cred *new)
S
Serge E. Hallyn 已提交
26 27
{
	struct user_namespace *ns;
28
	struct user_struct *root_user;
S
Serge E. Hallyn 已提交
29 30
	int n;

31
	ns = kmem_cache_alloc(user_ns_cachep, GFP_KERNEL);
S
Serge E. Hallyn 已提交
32
	if (!ns)
33
		return -ENOMEM;
S
Serge E. Hallyn 已提交
34 35 36 37

	kref_init(&ns->kref);

	for (n = 0; n < UIDHASH_SZ; ++n)
P
Pavel Emelyanov 已提交
38
		INIT_HLIST_HEAD(ns->uidhash_table + n);
S
Serge E. Hallyn 已提交
39

40 41 42
	/* Alloc new root user.  */
	root_user = alloc_uid(ns, 0);
	if (!root_user) {
43
		kmem_cache_free(user_ns_cachep, ns);
44
		return -ENOMEM;
S
Serge E. Hallyn 已提交
45 46
	}

47 48 49 50 51 52 53 54 55 56 57 58
	/* set the new root user in the credentials under preparation */
	ns->creator = new->user;
	new->user = root_user;
	new->uid = new->euid = new->suid = new->fsuid = 0;
	new->gid = new->egid = new->sgid = new->fsgid = 0;
	put_group_info(new->group_info);
	new->group_info = get_group_info(&init_groups);
#ifdef CONFIG_KEYS
	key_put(new->request_key_auth);
	new->request_key_auth = NULL;
#endif
	/* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
S
Serge E. Hallyn 已提交
59

N
NeilBrown 已提交
60 61
	/* root_user holds a reference to ns, our reference can be dropped */
	put_user_ns(ns);
S
Serge E. Hallyn 已提交
62

63
	return 0;
64 65
}

66 67 68 69 70 71
/*
 * Deferred destructor for a user namespace.  This is required because
 * free_user_ns() may be called with uidhash_lock held, but we need to call
 * back to free_uid() which will want to take the lock again.
 */
static void free_user_ns_work(struct work_struct *work)
72
{
73 74
	struct user_namespace *ns =
		container_of(work, struct user_namespace, destroyer);
75
	free_uid(ns->creator);
76
	kmem_cache_free(user_ns_cachep, ns);
77
}
78 79 80 81 82 83 84 85 86

void free_user_ns(struct kref *kref)
{
	struct user_namespace *ns =
		container_of(kref, struct user_namespace, kref);

	INIT_WORK(&ns->destroyer, free_user_ns_work);
	schedule_work(&ns->destroyer);
}
87
EXPORT_SYMBOL(free_user_ns);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130

uid_t user_ns_map_uid(struct user_namespace *to, const struct cred *cred, uid_t uid)
{
	struct user_namespace *tmp;

	if (likely(to == cred->user->user_ns))
		return uid;


	/* Is cred->user the creator of the target user_ns
	 * or the creator of one of it's parents?
	 */
	for ( tmp = to; tmp != &init_user_ns;
	      tmp = tmp->creator->user_ns ) {
		if (cred->user == tmp->creator) {
			return (uid_t)0;
		}
	}

	/* No useful relationship so no mapping */
	return overflowuid;
}

gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t gid)
{
	struct user_namespace *tmp;

	if (likely(to == cred->user->user_ns))
		return gid;

	/* Is cred->user the creator of the target user_ns
	 * or the creator of one of it's parents?
	 */
	for ( tmp = to; tmp != &init_user_ns;
	      tmp = tmp->creator->user_ns ) {
		if (cred->user == tmp->creator) {
			return (gid_t)0;
		}
	}

	/* No useful relationship so no mapping */
	return overflowgid;
}
131 132 133 134 135 136 137

static __init int user_namespaces_init(void)
{
	user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC);
	return 0;
}
module_init(user_namespaces_init);