diff options
author | Cedric Le Goater <clg@fr.ibm.com> | 2007-07-16 02:40:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:47 -0400 |
commit | acce292c82d4d82d35553b928df2b0597c3a9c78 (patch) | |
tree | 464288f40db9c254da214c400d0880ee50dc37f3 /kernel | |
parent | 7d69a1f4a72b18876c99c697692b78339d491568 (diff) |
user namespace: add the framework
Basically, it will allow a process to unshare its user_struct table,
resetting at the same time its own user_struct and all the associated
accounting.
A new root user (uid == 0) is added to the user namespace upon creation.
Such root users have full privileges and it seems that theses privileges
should be controlled through some means (process capabilities ?)
The unshare is not included in this patch.
Changes since [try #4]:
- Updated get_user_ns and put_user_ns to accept NULL, and
get_user_ns to return the namespace.
Changes since [try #3]:
- moved struct user_namespace to files user_namespace.{c,h}
Changes since [try #2]:
- removed struct user_namespace* argument from find_user()
Changes since [try #1]:
- removed struct user_namespace* argument from find_user()
- added a root_user per user namespace
Signed-off-by: Cedric Le Goater <clg@fr.ibm.com>
Signed-off-by: Serge E. Hallyn <serue@us.ibm.com>
Acked-by: Pavel Emelianov <xemul@openvz.org>
Cc: Herbert Poetzl <herbert@13thfloor.at>
Cc: Kirill Korotaev <dev@sw.ru>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Chris Wright <chrisw@sous-sol.org>
Cc: Stephen Smalley <sds@tycho.nsa.gov>
Cc: James Morris <jmorris@namei.org>
Cc: Andrew Morgan <agm@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/Makefile | 2 | ||||
-rw-r--r-- | kernel/fork.c | 2 | ||||
-rw-r--r-- | kernel/nsproxy.c | 9 | ||||
-rw-r--r-- | kernel/sys.c | 5 | ||||
-rw-r--r-- | kernel/user.c | 18 | ||||
-rw-r--r-- | kernel/user_namespace.c | 43 |
6 files changed, 66 insertions, 13 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index fa8efd437afb..2a999836ca18 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ | 5 | obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ |
6 | exit.o itimer.o time.o softirq.o resource.o \ | 6 | exit.o itimer.o time.o softirq.o resource.o \ |
7 | sysctl.o capability.o ptrace.o timer.o user.o \ | 7 | sysctl.o capability.o ptrace.o timer.o user.o user_namespace.o \ |
8 | signal.o sys.o kmod.o workqueue.o pid.o \ | 8 | signal.o sys.o kmod.o workqueue.o pid.o \ |
9 | rcupdate.o extable.o params.o posix-timers.o \ | 9 | rcupdate.o extable.o params.o posix-timers.o \ |
10 | kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ | 10 | kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ |
diff --git a/kernel/fork.c b/kernel/fork.c index 4015912aaac2..13cf0978780a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1002,7 +1002,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1002 | if (atomic_read(&p->user->processes) >= | 1002 | if (atomic_read(&p->user->processes) >= |
1003 | p->signal->rlim[RLIMIT_NPROC].rlim_cur) { | 1003 | p->signal->rlim[RLIMIT_NPROC].rlim_cur) { |
1004 | if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) && | 1004 | if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) && |
1005 | p->user != &root_user) | 1005 | p->user != current->nsproxy->user_ns->root_user) |
1006 | goto bad_fork_free; | 1006 | goto bad_fork_free; |
1007 | } | 1007 | } |
1008 | 1008 | ||
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index e38bed75367d..895e3a3f2044 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c | |||
@@ -79,8 +79,15 @@ static struct nsproxy *create_new_namespaces(int flags, struct task_struct *tsk, | |||
79 | if (IS_ERR(new_nsp->pid_ns)) | 79 | if (IS_ERR(new_nsp->pid_ns)) |
80 | goto out_pid; | 80 | goto out_pid; |
81 | 81 | ||
82 | new_nsp->user_ns = copy_user_ns(flags, tsk->nsproxy->user_ns); | ||
83 | if (IS_ERR(new_nsp->user_ns)) | ||
84 | goto out_user; | ||
85 | |||
82 | return new_nsp; | 86 | return new_nsp; |
83 | 87 | ||
88 | out_user: | ||
89 | if (new_nsp->pid_ns) | ||
90 | put_pid_ns(new_nsp->pid_ns); | ||
84 | out_pid: | 91 | out_pid: |
85 | if (new_nsp->ipc_ns) | 92 | if (new_nsp->ipc_ns) |
86 | put_ipc_ns(new_nsp->ipc_ns); | 93 | put_ipc_ns(new_nsp->ipc_ns); |
@@ -140,6 +147,8 @@ void free_nsproxy(struct nsproxy *ns) | |||
140 | put_ipc_ns(ns->ipc_ns); | 147 | put_ipc_ns(ns->ipc_ns); |
141 | if (ns->pid_ns) | 148 | if (ns->pid_ns) |
142 | put_pid_ns(ns->pid_ns); | 149 | put_pid_ns(ns->pid_ns); |
150 | if (ns->user_ns) | ||
151 | put_user_ns(ns->user_ns); | ||
143 | kfree(ns); | 152 | kfree(ns); |
144 | } | 153 | } |
145 | 154 | ||
diff --git a/kernel/sys.c b/kernel/sys.c index 872271ccc384..ed92e2f03342 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/compat.h> | 35 | #include <linux/compat.h> |
36 | #include <linux/syscalls.h> | 36 | #include <linux/syscalls.h> |
37 | #include <linux/kprobes.h> | 37 | #include <linux/kprobes.h> |
38 | #include <linux/user_namespace.h> | ||
38 | 39 | ||
39 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
40 | #include <asm/io.h> | 41 | #include <asm/io.h> |
@@ -1078,13 +1079,13 @@ static int set_user(uid_t new_ruid, int dumpclear) | |||
1078 | { | 1079 | { |
1079 | struct user_struct *new_user; | 1080 | struct user_struct *new_user; |
1080 | 1081 | ||
1081 | new_user = alloc_uid(new_ruid); | 1082 | new_user = alloc_uid(current->nsproxy->user_ns, new_ruid); |
1082 | if (!new_user) | 1083 | if (!new_user) |
1083 | return -EAGAIN; | 1084 | return -EAGAIN; |
1084 | 1085 | ||
1085 | if (atomic_read(&new_user->processes) >= | 1086 | if (atomic_read(&new_user->processes) >= |
1086 | current->signal->rlim[RLIMIT_NPROC].rlim_cur && | 1087 | current->signal->rlim[RLIMIT_NPROC].rlim_cur && |
1087 | new_user != &root_user) { | 1088 | new_user != current->nsproxy->user_ns->root_user) { |
1088 | free_uid(new_user); | 1089 | free_uid(new_user); |
1089 | return -EAGAIN; | 1090 | return -EAGAIN; |
1090 | } | 1091 | } |
diff --git a/kernel/user.c b/kernel/user.c index 4869563080e9..98b82507797a 100644 --- a/kernel/user.c +++ b/kernel/user.c | |||
@@ -14,20 +14,19 @@ | |||
14 | #include <linux/bitops.h> | 14 | #include <linux/bitops.h> |
15 | #include <linux/key.h> | 15 | #include <linux/key.h> |
16 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
17 | #include <linux/module.h> | ||
18 | #include <linux/user_namespace.h> | ||
17 | 19 | ||
18 | /* | 20 | /* |
19 | * UID task count cache, to get fast user lookup in "alloc_uid" | 21 | * UID task count cache, to get fast user lookup in "alloc_uid" |
20 | * when changing user ID's (ie setuid() and friends). | 22 | * when changing user ID's (ie setuid() and friends). |
21 | */ | 23 | */ |
22 | 24 | ||
23 | #define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 8) | ||
24 | #define UIDHASH_SZ (1 << UIDHASH_BITS) | ||
25 | #define UIDHASH_MASK (UIDHASH_SZ - 1) | 25 | #define UIDHASH_MASK (UIDHASH_SZ - 1) |
26 | #define __uidhashfn(uid) (((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK) | 26 | #define __uidhashfn(uid) (((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK) |
27 | #define uidhashentry(uid) (uidhash_table + __uidhashfn((uid))) | 27 | #define uidhashentry(ns, uid) ((ns)->uidhash_table + __uidhashfn((uid))) |
28 | 28 | ||
29 | static struct kmem_cache *uid_cachep; | 29 | static struct kmem_cache *uid_cachep; |
30 | static struct list_head uidhash_table[UIDHASH_SZ]; | ||
31 | 30 | ||
32 | /* | 31 | /* |
33 | * The uidhash_lock is mostly taken from process context, but it is | 32 | * The uidhash_lock is mostly taken from process context, but it is |
@@ -94,9 +93,10 @@ struct user_struct *find_user(uid_t uid) | |||
94 | { | 93 | { |
95 | struct user_struct *ret; | 94 | struct user_struct *ret; |
96 | unsigned long flags; | 95 | unsigned long flags; |
96 | struct user_namespace *ns = current->nsproxy->user_ns; | ||
97 | 97 | ||
98 | spin_lock_irqsave(&uidhash_lock, flags); | 98 | spin_lock_irqsave(&uidhash_lock, flags); |
99 | ret = uid_hash_find(uid, uidhashentry(uid)); | 99 | ret = uid_hash_find(uid, uidhashentry(ns, uid)); |
100 | spin_unlock_irqrestore(&uidhash_lock, flags); | 100 | spin_unlock_irqrestore(&uidhash_lock, flags); |
101 | return ret; | 101 | return ret; |
102 | } | 102 | } |
@@ -120,9 +120,9 @@ void free_uid(struct user_struct *up) | |||
120 | } | 120 | } |
121 | } | 121 | } |
122 | 122 | ||
123 | struct user_struct * alloc_uid(uid_t uid) | 123 | struct user_struct * alloc_uid(struct user_namespace *ns, uid_t uid) |
124 | { | 124 | { |
125 | struct list_head *hashent = uidhashentry(uid); | 125 | struct list_head *hashent = uidhashentry(ns, uid); |
126 | struct user_struct *up; | 126 | struct user_struct *up; |
127 | 127 | ||
128 | spin_lock_irq(&uidhash_lock); | 128 | spin_lock_irq(&uidhash_lock); |
@@ -211,11 +211,11 @@ static int __init uid_cache_init(void) | |||
211 | 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); | 211 | 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); |
212 | 212 | ||
213 | for(n = 0; n < UIDHASH_SZ; ++n) | 213 | for(n = 0; n < UIDHASH_SZ; ++n) |
214 | INIT_LIST_HEAD(uidhash_table + n); | 214 | INIT_LIST_HEAD(init_user_ns.uidhash_table + n); |
215 | 215 | ||
216 | /* Insert the root user immediately (init already runs as root) */ | 216 | /* Insert the root user immediately (init already runs as root) */ |
217 | spin_lock_irq(&uidhash_lock); | 217 | spin_lock_irq(&uidhash_lock); |
218 | uid_hash_insert(&root_user, uidhashentry(0)); | 218 | uid_hash_insert(&root_user, uidhashentry(&init_user_ns, 0)); |
219 | spin_unlock_irq(&uidhash_lock); | 219 | spin_unlock_irq(&uidhash_lock); |
220 | 220 | ||
221 | return 0; | 221 | return 0; |
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c new file mode 100644 index 000000000000..3d7964209774 --- /dev/null +++ b/kernel/user_namespace.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or | ||
3 | * modify it under the terms of the GNU General Public License as | ||
4 | * published by the Free Software Foundation, version 2 of the | ||
5 | * License. | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/version.h> | ||
10 | #include <linux/nsproxy.h> | ||
11 | #include <linux/user_namespace.h> | ||
12 | |||
13 | struct user_namespace init_user_ns = { | ||
14 | .kref = { | ||
15 | .refcount = ATOMIC_INIT(2), | ||
16 | }, | ||
17 | .root_user = &root_user, | ||
18 | }; | ||
19 | |||
20 | EXPORT_SYMBOL_GPL(init_user_ns); | ||
21 | |||
22 | #ifdef CONFIG_USER_NS | ||
23 | |||
24 | struct user_namespace * copy_user_ns(int flags, struct user_namespace *old_ns) | ||
25 | { | ||
26 | struct user_namespace *new_ns; | ||
27 | |||
28 | BUG_ON(!old_ns); | ||
29 | get_user_ns(old_ns); | ||
30 | |||
31 | new_ns = old_ns; | ||
32 | return new_ns; | ||
33 | } | ||
34 | |||
35 | void free_user_ns(struct kref *kref) | ||
36 | { | ||
37 | struct user_namespace *ns; | ||
38 | |||
39 | ns = container_of(kref, struct user_namespace, kref); | ||
40 | kfree(ns); | ||
41 | } | ||
42 | |||
43 | #endif /* CONFIG_USER_NS */ | ||