Parcourir la source

Merge branch 'serge-next' into next

James Morris il y a 16 ans
Parent
commit
3496f92beb

+ 6 - 7
fs/ecryptfs/messaging.c

@@ -360,7 +360,7 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
 	struct ecryptfs_msg_ctx *msg_ctx;
 	struct ecryptfs_msg_ctx *msg_ctx;
 	size_t msg_size;
 	size_t msg_size;
 	struct nsproxy *nsproxy;
 	struct nsproxy *nsproxy;
-	struct user_namespace *current_user_ns;
+	struct user_namespace *tsk_user_ns;
 	uid_t ctx_euid;
 	uid_t ctx_euid;
 	int rc;
 	int rc;
 
 
@@ -385,9 +385,9 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
 		mutex_unlock(&ecryptfs_daemon_hash_mux);
 		mutex_unlock(&ecryptfs_daemon_hash_mux);
 		goto wake_up;
 		goto wake_up;
 	}
 	}
-	current_user_ns = nsproxy->user_ns;
+	tsk_user_ns = __task_cred(msg_ctx->task)->user->user_ns;
 	ctx_euid = task_euid(msg_ctx->task);
 	ctx_euid = task_euid(msg_ctx->task);
-	rc = ecryptfs_find_daemon_by_euid(&daemon, ctx_euid, current_user_ns);
+	rc = ecryptfs_find_daemon_by_euid(&daemon, ctx_euid, tsk_user_ns);
 	rcu_read_unlock();
 	rcu_read_unlock();
 	mutex_unlock(&ecryptfs_daemon_hash_mux);
 	mutex_unlock(&ecryptfs_daemon_hash_mux);
 	if (rc) {
 	if (rc) {
@@ -405,11 +405,11 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
 		       euid, ctx_euid);
 		       euid, ctx_euid);
 		goto unlock;
 		goto unlock;
 	}
 	}
-	if (current_user_ns != user_ns) {
+	if (tsk_user_ns != user_ns) {
 		rc = -EBADMSG;
 		rc = -EBADMSG;
 		printk(KERN_WARNING "%s: Received message from user_ns "
 		printk(KERN_WARNING "%s: Received message from user_ns "
 		       "[0x%p]; expected message from user_ns [0x%p]\n",
 		       "[0x%p]; expected message from user_ns [0x%p]\n",
-		       __func__, user_ns, nsproxy->user_ns);
+		       __func__, user_ns, tsk_user_ns);
 		goto unlock;
 		goto unlock;
 	}
 	}
 	if (daemon->pid != pid) {
 	if (daemon->pid != pid) {
@@ -468,8 +468,7 @@ ecryptfs_send_message_locked(char *data, int data_len, u8 msg_type,
 	uid_t euid = current_euid();
 	uid_t euid = current_euid();
 	int rc;
 	int rc;
 
 
-	rc = ecryptfs_find_daemon_by_euid(&daemon, euid,
-					  current->nsproxy->user_ns);
+	rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
 	if (rc || !daemon) {
 	if (rc || !daemon) {
 		rc = -ENOTCONN;
 		rc = -ENOTCONN;
 		printk(KERN_ERR "%s: User [%d] does not have a daemon "
 		printk(KERN_ERR "%s: User [%d] does not have a daemon "

+ 7 - 12
fs/ecryptfs/miscdev.c

@@ -47,8 +47,7 @@ ecryptfs_miscdev_poll(struct file *file, poll_table *pt)
 
 
 	mutex_lock(&ecryptfs_daemon_hash_mux);
 	mutex_lock(&ecryptfs_daemon_hash_mux);
 	/* TODO: Just use file->private_data? */
 	/* TODO: Just use file->private_data? */
-	rc = ecryptfs_find_daemon_by_euid(&daemon, euid,
-					  current->nsproxy->user_ns);
+	rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
 	BUG_ON(rc || !daemon);
 	BUG_ON(rc || !daemon);
 	mutex_lock(&daemon->mux);
 	mutex_lock(&daemon->mux);
 	mutex_unlock(&ecryptfs_daemon_hash_mux);
 	mutex_unlock(&ecryptfs_daemon_hash_mux);
@@ -95,11 +94,9 @@ ecryptfs_miscdev_open(struct inode *inode, struct file *file)
 		       "count; rc = [%d]\n", __func__, rc);
 		       "count; rc = [%d]\n", __func__, rc);
 		goto out_unlock_daemon_list;
 		goto out_unlock_daemon_list;
 	}
 	}
-	rc = ecryptfs_find_daemon_by_euid(&daemon, euid,
-					  current->nsproxy->user_ns);
+	rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
 	if (rc || !daemon) {
 	if (rc || !daemon) {
-		rc = ecryptfs_spawn_daemon(&daemon, euid,
-					   current->nsproxy->user_ns,
+		rc = ecryptfs_spawn_daemon(&daemon, euid, current_user_ns(),
 					   task_pid(current));
 					   task_pid(current));
 		if (rc) {
 		if (rc) {
 			printk(KERN_ERR "%s: Error attempting to spawn daemon; "
 			printk(KERN_ERR "%s: Error attempting to spawn daemon; "
@@ -153,8 +150,7 @@ ecryptfs_miscdev_release(struct inode *inode, struct file *file)
 	int rc;
 	int rc;
 
 
 	mutex_lock(&ecryptfs_daemon_hash_mux);
 	mutex_lock(&ecryptfs_daemon_hash_mux);
-	rc = ecryptfs_find_daemon_by_euid(&daemon, euid,
-					  current->nsproxy->user_ns);
+	rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
 	BUG_ON(rc || !daemon);
 	BUG_ON(rc || !daemon);
 	mutex_lock(&daemon->mux);
 	mutex_lock(&daemon->mux);
 	BUG_ON(daemon->pid != task_pid(current));
 	BUG_ON(daemon->pid != task_pid(current));
@@ -254,8 +250,7 @@ ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count,
 
 
 	mutex_lock(&ecryptfs_daemon_hash_mux);
 	mutex_lock(&ecryptfs_daemon_hash_mux);
 	/* TODO: Just use file->private_data? */
 	/* TODO: Just use file->private_data? */
-	rc = ecryptfs_find_daemon_by_euid(&daemon, euid,
-					  current->nsproxy->user_ns);
+	rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
 	BUG_ON(rc || !daemon);
 	BUG_ON(rc || !daemon);
 	mutex_lock(&daemon->mux);
 	mutex_lock(&daemon->mux);
 	if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
 	if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
@@ -295,7 +290,7 @@ check_list:
 		goto check_list;
 		goto check_list;
 	}
 	}
 	BUG_ON(euid != daemon->euid);
 	BUG_ON(euid != daemon->euid);
-	BUG_ON(current->nsproxy->user_ns != daemon->user_ns);
+	BUG_ON(current_user_ns() != daemon->user_ns);
 	BUG_ON(task_pid(current) != daemon->pid);
 	BUG_ON(task_pid(current) != daemon->pid);
 	msg_ctx = list_first_entry(&daemon->msg_ctx_out_queue,
 	msg_ctx = list_first_entry(&daemon->msg_ctx_out_queue,
 				   struct ecryptfs_msg_ctx, daemon_out_list);
 				   struct ecryptfs_msg_ctx, daemon_out_list);
@@ -468,7 +463,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
 			goto out_free;
 			goto out_free;
 		}
 		}
 		rc = ecryptfs_miscdev_response(&data[i], packet_size,
 		rc = ecryptfs_miscdev_response(&data[i], packet_size,
-					       euid, current->nsproxy->user_ns,
+					       euid, current_user_ns(),
 					       task_pid(current), seq);
 					       task_pid(current), seq);
 		if (rc)
 		if (rc)
 			printk(KERN_WARNING "%s: Failed to deliver miscdev "
 			printk(KERN_WARNING "%s: Failed to deliver miscdev "

+ 2 - 0
include/linux/cred.h

@@ -60,6 +60,7 @@ do {							\
 } while (0)
 } while (0)
 
 
 extern struct group_info *groups_alloc(int);
 extern struct group_info *groups_alloc(int);
+extern struct group_info init_groups;
 extern void groups_free(struct group_info *);
 extern void groups_free(struct group_info *);
 extern int set_current_groups(struct group_info *);
 extern int set_current_groups(struct group_info *);
 extern int set_groups(struct cred *, struct group_info *);
 extern int set_groups(struct cred *, struct group_info *);
@@ -315,6 +316,7 @@ static inline void put_cred(const struct cred *_cred)
 #define current_fsgid() 	(current_cred_xxx(fsgid))
 #define current_fsgid() 	(current_cred_xxx(fsgid))
 #define current_cap()		(current_cred_xxx(cap_effective))
 #define current_cap()		(current_cred_xxx(cap_effective))
 #define current_user()		(current_cred_xxx(user))
 #define current_user()		(current_cred_xxx(user))
+#define current_user_ns()	(current_cred_xxx(user)->user_ns)
 #define current_security()	(current_cred_xxx(security))
 #define current_security()	(current_cred_xxx(security))
 
 
 #define current_uid_gid(_uid, _gid)		\
 #define current_uid_gid(_uid, _gid)		\

+ 0 - 1
include/linux/init_task.h

@@ -57,7 +57,6 @@ extern struct nsproxy init_nsproxy;
 	.mnt_ns		= NULL,						\
 	.mnt_ns		= NULL,						\
 	INIT_NET_NS(net_ns)                                             \
 	INIT_NET_NS(net_ns)                                             \
 	INIT_IPC_NS(ipc_ns)						\
 	INIT_IPC_NS(ipc_ns)						\
-	.user_ns	= &init_user_ns,				\
 }
 }
 
 
 #define INIT_SIGHAND(sighand) {						\
 #define INIT_SIGHAND(sighand) {						\

+ 0 - 1
include/linux/nsproxy.h

@@ -27,7 +27,6 @@ struct nsproxy {
 	struct ipc_namespace *ipc_ns;
 	struct ipc_namespace *ipc_ns;
 	struct mnt_namespace *mnt_ns;
 	struct mnt_namespace *mnt_ns;
 	struct pid_namespace *pid_ns;
 	struct pid_namespace *pid_ns;
-	struct user_namespace *user_ns;
 	struct net 	     *net_ns;
 	struct net 	     *net_ns;
 };
 };
 extern struct nsproxy init_nsproxy;
 extern struct nsproxy init_nsproxy;

+ 1 - 0
include/linux/sched.h

@@ -638,6 +638,7 @@ struct user_struct {
 	/* Hash table maintenance information */
 	/* Hash table maintenance information */
 	struct hlist_node uidhash_node;
 	struct hlist_node uidhash_node;
 	uid_t uid;
 	uid_t uid;
+	struct user_namespace *user_ns;
 
 
 #ifdef CONFIG_USER_SCHED
 #ifdef CONFIG_USER_SCHED
 	struct task_group *tg;
 	struct task_group *tg;

+ 4 - 9
include/linux/user_namespace.h

@@ -12,7 +12,7 @@
 struct user_namespace {
 struct user_namespace {
 	struct kref		kref;
 	struct kref		kref;
 	struct hlist_head	uidhash_table[UIDHASH_SZ];
 	struct hlist_head	uidhash_table[UIDHASH_SZ];
-	struct user_struct	*root_user;
+	struct user_struct	*creator;
 };
 };
 
 
 extern struct user_namespace init_user_ns;
 extern struct user_namespace init_user_ns;
@@ -26,8 +26,7 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
 	return ns;
 	return ns;
 }
 }
 
 
-extern struct user_namespace *copy_user_ns(int flags,
-					   struct user_namespace *old_ns);
+extern int create_user_ns(struct cred *new);
 extern void free_user_ns(struct kref *kref);
 extern void free_user_ns(struct kref *kref);
 
 
 static inline void put_user_ns(struct user_namespace *ns)
 static inline void put_user_ns(struct user_namespace *ns)
@@ -43,13 +42,9 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
 	return &init_user_ns;
 	return &init_user_ns;
 }
 }
 
 
-static inline struct user_namespace *copy_user_ns(int flags,
-						  struct user_namespace *old_ns)
+static inline int create_user_ns(struct cred *new)
 {
 {
-	if (flags & CLONE_NEWUSER)
-		return ERR_PTR(-EINVAL);
-
-	return old_ns;
+	return -EINVAL;
 }
 }
 
 
 static inline void put_user_ns(struct user_namespace *ns)
 static inline void put_user_ns(struct user_namespace *ns)

+ 13 - 2
kernel/cred.c

@@ -274,6 +274,7 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
 	struct thread_group_cred *tgcred;
 	struct thread_group_cred *tgcred;
 #endif
 #endif
 	struct cred *new;
 	struct cred *new;
+	int ret;
 
 
 	mutex_init(&p->cred_exec_mutex);
 	mutex_init(&p->cred_exec_mutex);
 
 
@@ -293,6 +294,12 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
 	if (!new)
 	if (!new)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
+	if (clone_flags & CLONE_NEWUSER) {
+		ret = create_user_ns(new);
+		if (ret < 0)
+			goto error_put;
+	}
+
 #ifdef CONFIG_KEYS
 #ifdef CONFIG_KEYS
 	/* new threads get their own thread keyrings if their parent already
 	/* new threads get their own thread keyrings if their parent already
 	 * had one */
 	 * had one */
@@ -309,8 +316,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
 	if (!(clone_flags & CLONE_THREAD)) {
 	if (!(clone_flags & CLONE_THREAD)) {
 		tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
 		tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
 		if (!tgcred) {
 		if (!tgcred) {
-			put_cred(new);
-			return -ENOMEM;
+			ret = -ENOMEM;
+			goto error_put;
 		}
 		}
 		atomic_set(&tgcred->usage, 1);
 		atomic_set(&tgcred->usage, 1);
 		spin_lock_init(&tgcred->lock);
 		spin_lock_init(&tgcred->lock);
@@ -325,6 +332,10 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
 	atomic_inc(&new->user->processes);
 	atomic_inc(&new->user->processes);
 	p->cred = p->real_cred = get_cred(new);
 	p->cred = p->real_cred = get_cred(new);
 	return 0;
 	return 0;
+
+error_put:
+	put_cred(new);
+	return ret;
 }
 }
 
 
 /**
 /**

+ 16 - 3
kernel/fork.c

@@ -976,7 +976,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	if (atomic_read(&p->real_cred->user->processes) >=
 	if (atomic_read(&p->real_cred->user->processes) >=
 			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
 			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
 		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
 		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
-		    p->real_cred->user != current->nsproxy->user_ns->root_user)
+		    p->real_cred->user != INIT_USER)
 			goto bad_fork_free;
 			goto bad_fork_free;
 	}
 	}
 
 
@@ -1334,6 +1334,20 @@ long do_fork(unsigned long clone_flags,
 	int trace = 0;
 	int trace = 0;
 	long nr;
 	long nr;
 
 
+	/*
+	 * Do some preliminary argument and permissions checking before we
+	 * actually start allocating stuff
+	 */
+	if (clone_flags & CLONE_NEWUSER) {
+		if (clone_flags & CLONE_THREAD)
+			return -EINVAL;
+		/* hopefully this check will go away when userns support is
+		 * complete
+		 */
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	}
+
 	/*
 	/*
 	 * We hope to recycle these flags after 2.6.26
 	 * We hope to recycle these flags after 2.6.26
 	 */
 	 */
@@ -1581,8 +1595,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
 	err = -EINVAL;
 	err = -EINVAL;
 	if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
 	if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
 				CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
 				CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
-				CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|
-				CLONE_NEWNET))
+				CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET))
 		goto bad_unshare_out;
 		goto bad_unshare_out;
 
 
 	/*
 	/*

+ 2 - 13
kernel/nsproxy.c

@@ -80,12 +80,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
 		goto out_pid;
 		goto out_pid;
 	}
 	}
 
 
-	new_nsp->user_ns = copy_user_ns(flags, tsk->nsproxy->user_ns);
-	if (IS_ERR(new_nsp->user_ns)) {
-		err = PTR_ERR(new_nsp->user_ns);
-		goto out_user;
-	}
-
 	new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
 	new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
 	if (IS_ERR(new_nsp->net_ns)) {
 	if (IS_ERR(new_nsp->net_ns)) {
 		err = PTR_ERR(new_nsp->net_ns);
 		err = PTR_ERR(new_nsp->net_ns);
@@ -95,9 +89,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
 	return new_nsp;
 	return new_nsp;
 
 
 out_net:
 out_net:
-	if (new_nsp->user_ns)
-		put_user_ns(new_nsp->user_ns);
-out_user:
 	if (new_nsp->pid_ns)
 	if (new_nsp->pid_ns)
 		put_pid_ns(new_nsp->pid_ns);
 		put_pid_ns(new_nsp->pid_ns);
 out_pid:
 out_pid:
@@ -130,7 +121,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
 	get_nsproxy(old_ns);
 	get_nsproxy(old_ns);
 
 
 	if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
 	if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
-				CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)))
+				CLONE_NEWPID | CLONE_NEWNET)))
 		return 0;
 		return 0;
 
 
 	if (!capable(CAP_SYS_ADMIN)) {
 	if (!capable(CAP_SYS_ADMIN)) {
@@ -173,8 +164,6 @@ void free_nsproxy(struct nsproxy *ns)
 		put_ipc_ns(ns->ipc_ns);
 		put_ipc_ns(ns->ipc_ns);
 	if (ns->pid_ns)
 	if (ns->pid_ns)
 		put_pid_ns(ns->pid_ns);
 		put_pid_ns(ns->pid_ns);
-	if (ns->user_ns)
-		put_user_ns(ns->user_ns);
 	put_net(ns->net_ns);
 	put_net(ns->net_ns);
 	kmem_cache_free(nsproxy_cachep, ns);
 	kmem_cache_free(nsproxy_cachep, ns);
 }
 }
@@ -189,7 +178,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
 	int err = 0;
 	int err = 0;
 
 
 	if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
 	if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
-			       CLONE_NEWUSER | CLONE_NEWNET)))
+			       CLONE_NEWNET)))
 		return 0;
 		return 0;
 
 
 	if (!capable(CAP_SYS_ADMIN))
 	if (!capable(CAP_SYS_ADMIN))

+ 2 - 2
kernel/sys.c

@@ -565,13 +565,13 @@ static int set_user(struct cred *new)
 {
 {
 	struct user_struct *new_user;
 	struct user_struct *new_user;
 
 
-	new_user = alloc_uid(current->nsproxy->user_ns, new->uid);
+	new_user = alloc_uid(current_user_ns(), new->uid);
 	if (!new_user)
 	if (!new_user)
 		return -EAGAIN;
 		return -EAGAIN;
 
 
 	if (atomic_read(&new_user->processes) >=
 	if (atomic_read(&new_user->processes) >=
 				current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
 				current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
-			new_user != current->nsproxy->user_ns->root_user) {
+			new_user != INIT_USER) {
 		free_uid(new_user);
 		free_uid(new_user);
 		return -EAGAIN;
 		return -EAGAIN;
 	}
 	}

+ 13 - 34
kernel/user.c

@@ -20,9 +20,9 @@
 
 
 struct user_namespace init_user_ns = {
 struct user_namespace init_user_ns = {
 	.kref = {
 	.kref = {
-		.refcount	= ATOMIC_INIT(2),
+		.refcount	= ATOMIC_INIT(1),
 	},
 	},
-	.root_user = &root_user,
+	.creator = &root_user,
 };
 };
 EXPORT_SYMBOL_GPL(init_user_ns);
 EXPORT_SYMBOL_GPL(init_user_ns);
 
 
@@ -48,12 +48,14 @@ static struct kmem_cache *uid_cachep;
  */
  */
 static DEFINE_SPINLOCK(uidhash_lock);
 static DEFINE_SPINLOCK(uidhash_lock);
 
 
+/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->creator */
 struct user_struct root_user = {
 struct user_struct root_user = {
-	.__count	= ATOMIC_INIT(1),
+	.__count	= ATOMIC_INIT(2),
 	.processes	= ATOMIC_INIT(1),
 	.processes	= ATOMIC_INIT(1),
 	.files		= ATOMIC_INIT(0),
 	.files		= ATOMIC_INIT(0),
 	.sigpending	= ATOMIC_INIT(0),
 	.sigpending	= ATOMIC_INIT(0),
 	.locked_shm     = 0,
 	.locked_shm     = 0,
+	.user_ns	= &init_user_ns,
 #ifdef CONFIG_USER_SCHED
 #ifdef CONFIG_USER_SCHED
 	.tg		= &init_task_group,
 	.tg		= &init_task_group,
 #endif
 #endif
@@ -314,12 +316,13 @@ done:
  * IRQ state (as stored in flags) is restored and uidhash_lock released
  * IRQ state (as stored in flags) is restored and uidhash_lock released
  * upon function exit.
  * upon function exit.
  */
  */
-static inline void free_user(struct user_struct *up, unsigned long flags)
+static void free_user(struct user_struct *up, unsigned long flags)
 {
 {
 	/* restore back the count */
 	/* restore back the count */
 	atomic_inc(&up->__count);
 	atomic_inc(&up->__count);
 	spin_unlock_irqrestore(&uidhash_lock, flags);
 	spin_unlock_irqrestore(&uidhash_lock, flags);
 
 
+	put_user_ns(up->user_ns);
 	INIT_WORK(&up->work, remove_user_sysfs_dir);
 	INIT_WORK(&up->work, remove_user_sysfs_dir);
 	schedule_work(&up->work);
 	schedule_work(&up->work);
 }
 }
@@ -335,13 +338,14 @@ static inline void uids_mutex_unlock(void) { }
  * IRQ state (as stored in flags) is restored and uidhash_lock released
  * IRQ state (as stored in flags) is restored and uidhash_lock released
  * upon function exit.
  * upon function exit.
  */
  */
-static inline void free_user(struct user_struct *up, unsigned long flags)
+static void free_user(struct user_struct *up, unsigned long flags)
 {
 {
 	uid_hash_remove(up);
 	uid_hash_remove(up);
 	spin_unlock_irqrestore(&uidhash_lock, flags);
 	spin_unlock_irqrestore(&uidhash_lock, flags);
 	sched_destroy_user(up);
 	sched_destroy_user(up);
 	key_put(up->uid_keyring);
 	key_put(up->uid_keyring);
 	key_put(up->session_keyring);
 	key_put(up->session_keyring);
+	put_user_ns(up->user_ns);
 	kmem_cache_free(uid_cachep, up);
 	kmem_cache_free(uid_cachep, up);
 }
 }
 
 
@@ -357,7 +361,7 @@ struct user_struct *find_user(uid_t uid)
 {
 {
 	struct user_struct *ret;
 	struct user_struct *ret;
 	unsigned long flags;
 	unsigned long flags;
-	struct user_namespace *ns = current->nsproxy->user_ns;
+	struct user_namespace *ns = current_user_ns();
 
 
 	spin_lock_irqsave(&uidhash_lock, flags);
 	spin_lock_irqsave(&uidhash_lock, flags);
 	ret = uid_hash_find(uid, uidhashentry(ns, uid));
 	ret = uid_hash_find(uid, uidhashentry(ns, uid));
@@ -404,6 +408,8 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
 		if (sched_create_user(new) < 0)
 		if (sched_create_user(new) < 0)
 			goto out_free_user;
 			goto out_free_user;
 
 
+		new->user_ns = get_user_ns(ns);
+
 		if (uids_user_create(new))
 		if (uids_user_create(new))
 			goto out_destoy_sched;
 			goto out_destoy_sched;
 
 
@@ -427,7 +433,6 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
 			up = new;
 			up = new;
 		}
 		}
 		spin_unlock_irq(&uidhash_lock);
 		spin_unlock_irq(&uidhash_lock);
-
 	}
 	}
 
 
 	uids_mutex_unlock();
 	uids_mutex_unlock();
@@ -436,6 +441,7 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
 
 
 out_destoy_sched:
 out_destoy_sched:
 	sched_destroy_user(new);
 	sched_destroy_user(new);
+	put_user_ns(new->user_ns);
 out_free_user:
 out_free_user:
 	kmem_cache_free(uid_cachep, new);
 	kmem_cache_free(uid_cachep, new);
 out_unlock:
 out_unlock:
@@ -443,33 +449,6 @@ out_unlock:
 	return NULL;
 	return NULL;
 }
 }
 
 
-#ifdef CONFIG_USER_NS
-void release_uids(struct user_namespace *ns)
-{
-	int i;
-	unsigned long flags;
-	struct hlist_head *head;
-	struct hlist_node *nd;
-
-	spin_lock_irqsave(&uidhash_lock, flags);
-	/*
-	 * collapse the chains so that the user_struct-s will
-	 * be still alive, but not in hashes. subsequent free_uid()
-	 * will free them.
-	 */
-	for (i = 0; i < UIDHASH_SZ; i++) {
-		head = ns->uidhash_table + i;
-		while (!hlist_empty(head)) {
-			nd = head->first;
-			hlist_del_init(nd);
-		}
-	}
-	spin_unlock_irqrestore(&uidhash_lock, flags);
-
-	free_uid(ns->root_user);
-}
-#endif
-
 static int __init uid_cache_init(void)
 static int __init uid_cache_init(void)
 {
 {
 	int n;
 	int n;

+ 30 - 45
kernel/user_namespace.c

@@ -9,70 +9,55 @@
 #include <linux/nsproxy.h>
 #include <linux/nsproxy.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/user_namespace.h>
 #include <linux/user_namespace.h>
+#include <linux/cred.h>
 
 
 /*
 /*
- * Clone a new ns copying an original user ns, setting refcount to 1
- * @old_ns: namespace to clone
- * Return NULL on error (failure to kmalloc), new ns otherwise
+ * 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.
  */
  */
-static struct user_namespace *clone_user_ns(struct user_namespace *old_ns)
+int create_user_ns(struct cred *new)
 {
 {
 	struct user_namespace *ns;
 	struct user_namespace *ns;
-	struct user_struct *new_user;
-	struct cred *new;
+	struct user_struct *root_user;
 	int n;
 	int n;
 
 
 	ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL);
 	ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL);
 	if (!ns)
 	if (!ns)
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
 
 
 	kref_init(&ns->kref);
 	kref_init(&ns->kref);
 
 
 	for (n = 0; n < UIDHASH_SZ; ++n)
 	for (n = 0; n < UIDHASH_SZ; ++n)
 		INIT_HLIST_HEAD(ns->uidhash_table + n);
 		INIT_HLIST_HEAD(ns->uidhash_table + n);
 
 
-	/* Insert new root user.  */
-	ns->root_user = alloc_uid(ns, 0);
-	if (!ns->root_user) {
+	/* Alloc new root user.  */
+	root_user = alloc_uid(ns, 0);
+	if (!root_user) {
 		kfree(ns);
 		kfree(ns);
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
 	}
 	}
 
 
-	/* Reset current->user with a new one */
-	new_user = alloc_uid(ns, current_uid());
-	if (!new_user) {
-		free_uid(ns->root_user);
-		kfree(ns);
-		return ERR_PTR(-ENOMEM);
-	}
-
-	/* Install the new user */
-	new = prepare_creds();
-	if (!new) {
-		free_uid(new_user);
-		free_uid(ns->root_user);
-		kfree(ns);
-	}
-	free_uid(new->user);
-	new->user = new_user;
-	commit_creds(new);
-	return ns;
-}
-
-struct user_namespace * copy_user_ns(int flags, struct user_namespace *old_ns)
-{
-	struct user_namespace *new_ns;
-
-	BUG_ON(!old_ns);
-	get_user_ns(old_ns);
-
-	if (!(flags & CLONE_NEWUSER))
-		return old_ns;
+	/* 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 */
 
 
-	new_ns = clone_user_ns(old_ns);
+	/* alloc_uid() incremented the userns refcount.  Just set it to 1 */
+	kref_set(&ns->kref, 1);
 
 
-	put_user_ns(old_ns);
-	return new_ns;
+	return 0;
 }
 }
 
 
 void free_user_ns(struct kref *kref)
 void free_user_ns(struct kref *kref)
@@ -80,7 +65,7 @@ void free_user_ns(struct kref *kref)
 	struct user_namespace *ns;
 	struct user_namespace *ns;
 
 
 	ns = container_of(kref, struct user_namespace, kref);
 	ns = container_of(kref, struct user_namespace, kref);
-	release_uids(ns);
+	free_uid(ns->creator);
 	kfree(ns);
 	kfree(ns);
 }
 }
 EXPORT_SYMBOL(free_user_ns);
 EXPORT_SYMBOL(free_user_ns);