diff options
Diffstat (limited to 'kernel/nsproxy.c')
| -rw-r--r-- | kernel/nsproxy.c | 138 | 
1 files changed, 82 insertions, 56 deletions
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index f74e6c00e26..8e7811086b8 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -14,7 +14,7 @@   */  #include <linux/slab.h> -#include <linux/module.h> +#include <linux/export.h>  #include <linux/nsproxy.h>  #include <linux/init_task.h>  #include <linux/mnt_namespace.h> @@ -22,19 +22,22 @@  #include <linux/pid_namespace.h>  #include <net/net_namespace.h>  #include <linux/ipc_namespace.h> +#include <linux/proc_ns.h> +#include <linux/file.h> +#include <linux/syscalls.h>  static struct kmem_cache *nsproxy_cachep;  struct nsproxy init_nsproxy = { -	.count	= ATOMIC_INIT(1), -	.uts_ns	= &init_uts_ns, +	.count			= ATOMIC_INIT(1), +	.uts_ns			= &init_uts_ns,  #if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC) -	.ipc_ns	= &init_ipc_ns, +	.ipc_ns			= &init_ipc_ns,  #endif -	.mnt_ns	= NULL, -	.pid_ns	= &init_pid_ns, +	.mnt_ns			= NULL, +	.pid_ns_for_children	= &init_pid_ns,  #ifdef CONFIG_NET -	.net_ns	= &init_net, +	.net_ns			= &init_net,  #endif  }; @@ -54,7 +57,8 @@ static inline struct nsproxy *create_nsproxy(void)   * leave it to the caller to do proper locking and attach it to task.   */  static struct nsproxy *create_new_namespaces(unsigned long flags, -			struct task_struct *tsk, struct fs_struct *new_fs) +	struct task_struct *tsk, struct user_namespace *user_ns, +	struct fs_struct *new_fs)  {  	struct nsproxy *new_nsp;  	int err; @@ -63,31 +67,32 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,  	if (!new_nsp)  		return ERR_PTR(-ENOMEM); -	new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, new_fs); +	new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, user_ns, new_fs);  	if (IS_ERR(new_nsp->mnt_ns)) {  		err = PTR_ERR(new_nsp->mnt_ns);  		goto out_ns;  	} -	new_nsp->uts_ns = copy_utsname(flags, tsk->nsproxy->uts_ns); +	new_nsp->uts_ns = copy_utsname(flags, user_ns, tsk->nsproxy->uts_ns);  	if (IS_ERR(new_nsp->uts_ns)) {  		err = PTR_ERR(new_nsp->uts_ns);  		goto out_uts;  	} -	new_nsp->ipc_ns = copy_ipcs(flags, tsk->nsproxy->ipc_ns); +	new_nsp->ipc_ns = copy_ipcs(flags, user_ns, tsk->nsproxy->ipc_ns);  	if (IS_ERR(new_nsp->ipc_ns)) {  		err = PTR_ERR(new_nsp->ipc_ns);  		goto out_ipc;  	} -	new_nsp->pid_ns = copy_pid_ns(flags, task_active_pid_ns(tsk)); -	if (IS_ERR(new_nsp->pid_ns)) { -		err = PTR_ERR(new_nsp->pid_ns); +	new_nsp->pid_ns_for_children = +		copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns_for_children); +	if (IS_ERR(new_nsp->pid_ns_for_children)) { +		err = PTR_ERR(new_nsp->pid_ns_for_children);  		goto out_pid;  	} -	new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns); +	new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns);  	if (IS_ERR(new_nsp->net_ns)) {  		err = PTR_ERR(new_nsp->net_ns);  		goto out_net; @@ -96,8 +101,8 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,  	return new_nsp;  out_net: -	if (new_nsp->pid_ns) -		put_pid_ns(new_nsp->pid_ns); +	if (new_nsp->pid_ns_for_children) +		put_pid_ns(new_nsp->pid_ns_for_children);  out_pid:  	if (new_nsp->ipc_ns)  		put_ipc_ns(new_nsp->ipc_ns); @@ -119,23 +124,18 @@ out_ns:  int copy_namespaces(unsigned long flags, struct task_struct *tsk)  {  	struct nsproxy *old_ns = tsk->nsproxy; +	struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);  	struct nsproxy *new_ns; -	int err = 0; -	if (!old_ns) +	if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | +			      CLONE_NEWPID | CLONE_NEWNET)))) { +		get_nsproxy(old_ns);  		return 0; - -	get_nsproxy(old_ns); - -	if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | -				CLONE_NEWPID | CLONE_NEWNET))) -		return 0; - -	if (!capable(CAP_SYS_ADMIN)) { -		err = -EPERM; -		goto out;  	} +	if (!ns_capable(user_ns, CAP_SYS_ADMIN)) +		return -EPERM; +  	/*  	 * CLONE_NEWIPC must detach from the undolist: after switching  	 * to a new ipc namespace, the semaphore arrays from the old @@ -143,22 +143,16 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)  	 * means share undolist with parent, so we must forbid using  	 * it along with CLONE_NEWIPC.  	 */ -	if ((flags & CLONE_NEWIPC) && (flags & CLONE_SYSVSEM)) { -		err = -EINVAL; -		goto out; -	} +	if ((flags & (CLONE_NEWIPC | CLONE_SYSVSEM)) == +		(CLONE_NEWIPC | CLONE_SYSVSEM))  +		return -EINVAL; -	new_ns = create_new_namespaces(flags, tsk, tsk->fs); -	if (IS_ERR(new_ns)) { -		err = PTR_ERR(new_ns); -		goto out; -	} +	new_ns = create_new_namespaces(flags, tsk, user_ns, tsk->fs); +	if (IS_ERR(new_ns)) +		return  PTR_ERR(new_ns);  	tsk->nsproxy = new_ns; - -out: -	put_nsproxy(old_ns); -	return err; +	return 0;  }  void free_nsproxy(struct nsproxy *ns) @@ -169,8 +163,8 @@ void free_nsproxy(struct nsproxy *ns)  		put_uts_ns(ns->uts_ns);  	if (ns->ipc_ns)  		put_ipc_ns(ns->ipc_ns); -	if (ns->pid_ns) -		put_pid_ns(ns->pid_ns); +	if (ns->pid_ns_for_children) +		put_pid_ns(ns->pid_ns_for_children);  	put_net(ns->net_ns);  	kmem_cache_free(nsproxy_cachep, ns);  } @@ -180,28 +174,26 @@ void free_nsproxy(struct nsproxy *ns)   * On success, returns the new nsproxy.   */  int unshare_nsproxy_namespaces(unsigned long unshare_flags, -		struct nsproxy **new_nsp, struct fs_struct *new_fs) +	struct nsproxy **new_nsp, struct cred *new_cred, struct fs_struct *new_fs)  { +	struct user_namespace *user_ns;  	int err = 0;  	if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | -			       CLONE_NEWNET))) +			       CLONE_NEWNET | CLONE_NEWPID)))  		return 0; -	if (!capable(CAP_SYS_ADMIN)) +	user_ns = new_cred ? new_cred->user_ns : current_user_ns(); +	if (!ns_capable(user_ns, CAP_SYS_ADMIN))  		return -EPERM; -	*new_nsp = create_new_namespaces(unshare_flags, current, -				new_fs ? new_fs : current->fs); +	*new_nsp = create_new_namespaces(unshare_flags, current, user_ns, +					 new_fs ? new_fs : current->fs);  	if (IS_ERR(*new_nsp)) {  		err = PTR_ERR(*new_nsp);  		goto out;  	} -	err = ns_cgroup_clone(current, task_pid(current)); -	if (err) -		put_nsproxy(*new_nsp); -  out:  	return err;  } @@ -233,10 +225,44 @@ void exit_task_namespaces(struct task_struct *p)  	switch_task_namespaces(p, NULL);  } -static int __init nsproxy_cache_init(void) +SYSCALL_DEFINE2(setns, int, fd, int, nstype) +{ +	const struct proc_ns_operations *ops; +	struct task_struct *tsk = current; +	struct nsproxy *new_nsproxy; +	struct proc_ns *ei; +	struct file *file; +	int err; + +	file = proc_ns_fget(fd); +	if (IS_ERR(file)) +		return PTR_ERR(file); + +	err = -EINVAL; +	ei = get_proc_ns(file_inode(file)); +	ops = ei->ns_ops; +	if (nstype && (ops->type != nstype)) +		goto out; + +	new_nsproxy = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs); +	if (IS_ERR(new_nsproxy)) { +		err = PTR_ERR(new_nsproxy); +		goto out; +	} + +	err = ops->install(new_nsproxy, ei->ns); +	if (err) { +		free_nsproxy(new_nsproxy); +		goto out; +	} +	switch_task_namespaces(tsk, new_nsproxy); +out: +	fput(file); +	return err; +} + +int __init nsproxy_cache_init(void)  {  	nsproxy_cachep = KMEM_CACHE(nsproxy, SLAB_PANIC);  	return 0;  } - -module_init(nsproxy_cache_init);  | 
