aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCedric Le Goater <clg@fr.ibm.com>2007-07-16 02:40:59 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 12:05:47 -0400
commitacce292c82d4d82d35553b928df2b0597c3a9c78 (patch)
tree464288f40db9c254da214c400d0880ee50dc37f3
parent7d69a1f4a72b18876c99c697692b78339d491568 (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>
-rw-r--r--include/linux/init_task.h2
-rw-r--r--include/linux/nsproxy.h1
-rw-r--r--include/linux/sched.h3
-rw-r--r--include/linux/user_namespace.h57
-rw-r--r--init/Kconfig9
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/fork.c2
-rw-r--r--kernel/nsproxy.c9
-rw-r--r--kernel/sys.c5
-rw-r--r--kernel/user.c18
-rw-r--r--kernel/user_namespace.c43
11 files changed, 137 insertions, 14 deletions
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 276ccaa2670c..cab741c2d603 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -8,6 +8,7 @@
8#include <linux/lockdep.h> 8#include <linux/lockdep.h>
9#include <linux/ipc.h> 9#include <linux/ipc.h>
10#include <linux/pid_namespace.h> 10#include <linux/pid_namespace.h>
11#include <linux/user_namespace.h>
11 12
12#define INIT_FDTABLE \ 13#define INIT_FDTABLE \
13{ \ 14{ \
@@ -78,6 +79,7 @@ extern struct nsproxy init_nsproxy;
78 .uts_ns = &init_uts_ns, \ 79 .uts_ns = &init_uts_ns, \
79 .mnt_ns = NULL, \ 80 .mnt_ns = NULL, \
80 INIT_IPC_NS(ipc_ns) \ 81 INIT_IPC_NS(ipc_ns) \
82 .user_ns = &init_user_ns, \
81} 83}
82 84
83#define INIT_SIGHAND(sighand) { \ 85#define INIT_SIGHAND(sighand) { \
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index 189e0dc993ab..6d179a397bfb 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -28,6 +28,7 @@ struct nsproxy {
28 struct ipc_namespace *ipc_ns; 28 struct ipc_namespace *ipc_ns;
29 struct mnt_namespace *mnt_ns; 29 struct mnt_namespace *mnt_ns;
30 struct pid_namespace *pid_ns; 30 struct pid_namespace *pid_ns;
31 struct user_namespace *user_ns;
31}; 32};
32extern struct nsproxy init_nsproxy; 33extern struct nsproxy init_nsproxy;
33 34
diff --git a/include/linux/sched.h b/include/linux/sched.h
index b579624477f4..c667255d70db 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -287,6 +287,7 @@ extern signed long schedule_timeout_uninterruptible(signed long timeout);
287asmlinkage void schedule(void); 287asmlinkage void schedule(void);
288 288
289struct nsproxy; 289struct nsproxy;
290struct user_namespace;
290 291
291/* Maximum number of active map areas.. This is a random (large) number */ 292/* Maximum number of active map areas.. This is a random (large) number */
292#define DEFAULT_MAX_MAP_COUNT 65536 293#define DEFAULT_MAX_MAP_COUNT 65536
@@ -1408,7 +1409,7 @@ extern struct task_struct *find_task_by_pid_type(int type, int pid);
1408extern void __set_special_pids(pid_t session, pid_t pgrp); 1409extern void __set_special_pids(pid_t session, pid_t pgrp);
1409 1410
1410/* per-UID process charging. */ 1411/* per-UID process charging. */
1411extern struct user_struct * alloc_uid(uid_t); 1412extern struct user_struct * alloc_uid(struct user_namespace *, uid_t);
1412static inline struct user_struct *get_uid(struct user_struct *u) 1413static inline struct user_struct *get_uid(struct user_struct *u)
1413{ 1414{
1414 atomic_inc(&u->__count); 1415 atomic_inc(&u->__count);
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
new file mode 100644
index 000000000000..92a45867ecfb
--- /dev/null
+++ b/include/linux/user_namespace.h
@@ -0,0 +1,57 @@
1#ifndef _LINUX_USER_NAMESPACE_H
2#define _LINUX_USER_NAMESPACE_H
3
4#include <linux/kref.h>
5#include <linux/nsproxy.h>
6#include <linux/sched.h>
7
8#define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 8)
9#define UIDHASH_SZ (1 << UIDHASH_BITS)
10
11struct user_namespace {
12 struct kref kref;
13 struct list_head uidhash_table[UIDHASH_SZ];
14 struct user_struct *root_user;
15};
16
17extern struct user_namespace init_user_ns;
18
19#ifdef CONFIG_USER_NS
20
21static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
22{
23 if (ns)
24 kref_get(&ns->kref);
25 return ns;
26}
27
28extern struct user_namespace *copy_user_ns(int flags,
29 struct user_namespace *old_ns);
30extern void free_user_ns(struct kref *kref);
31
32static inline void put_user_ns(struct user_namespace *ns)
33{
34 if (ns)
35 kref_put(&ns->kref, free_user_ns);
36}
37
38#else
39
40static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
41{
42 return &init_user_ns;
43}
44
45static inline struct user_namespace *copy_user_ns(int flags,
46 struct user_namespace *old_ns)
47{
48 return NULL;
49}
50
51static inline void put_user_ns(struct user_namespace *ns)
52{
53}
54
55#endif
56
57#endif /* _LINUX_USER_H */
diff --git a/init/Kconfig b/init/Kconfig
index 1e198b8c6936..0b0e29ed82d1 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -209,6 +209,15 @@ config TASK_IO_ACCOUNTING
209 209
210 Say N if unsure. 210 Say N if unsure.
211 211
212config USER_NS
213 bool "User Namespaces (EXPERIMENTAL)"
214 default n
215 depends on EXPERIMENTAL
216 help
217 Support user namespaces. This allows containers, i.e.
218 vservers, to use user namespaces to provide different
219 user info for different servers. If unsure, say N.
220
212config AUDIT 221config AUDIT
213 bool "Auditing support" 222 bool "Auditing support"
214 depends on NET 223 depends on NET
diff --git a/kernel/Makefile b/kernel/Makefile
index fa8efd437afb..2a999836ca18 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -4,7 +4,7 @@
4 4
5obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ 5obj-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
88out_user:
89 if (new_nsp->pid_ns)
90 put_pid_ns(new_nsp->pid_ns);
84out_pid: 91out_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
29static struct kmem_cache *uid_cachep; 29static struct kmem_cache *uid_cachep;
30static 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
123struct user_struct * alloc_uid(uid_t uid) 123struct 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
13struct user_namespace init_user_ns = {
14 .kref = {
15 .refcount = ATOMIC_INIT(2),
16 },
17 .root_user = &root_user,
18};
19
20EXPORT_SYMBOL_GPL(init_user_ns);
21
22#ifdef CONFIG_USER_NS
23
24struct 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
35void 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 */