diff options
Diffstat (limited to 'kernel/utsname.c')
| -rw-r--r-- | kernel/utsname.c | 75 | 
1 files changed, 70 insertions, 5 deletions
diff --git a/kernel/utsname.c b/kernel/utsname.c index 8a82b4b8ea5..fd393124e50 100644 --- a/kernel/utsname.c +++ b/kernel/utsname.c @@ -9,11 +9,13 @@   *  License.   */ -#include <linux/module.h> +#include <linux/export.h>  #include <linux/uts.h>  #include <linux/utsname.h>  #include <linux/err.h>  #include <linux/slab.h> +#include <linux/user_namespace.h> +#include <linux/proc_ns.h>  static struct uts_namespace *create_uts_ns(void)  { @@ -28,18 +30,27 @@ static struct uts_namespace *create_uts_ns(void)  /*   * Clone a new ns copying an original utsname, setting refcount to 1   * @old_ns: namespace to clone - * Return NULL on error (failure to kmalloc), new ns otherwise + * Return ERR_PTR(-ENOMEM) on error (failure to kmalloc), new ns otherwise   */ -static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns) +static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns, +					  struct uts_namespace *old_ns)  {  	struct uts_namespace *ns; +	int err;  	ns = create_uts_ns();  	if (!ns)  		return ERR_PTR(-ENOMEM); +	err = proc_alloc_inum(&ns->proc_inum); +	if (err) { +		kfree(ns); +		return ERR_PTR(err); +	} +  	down_read(&uts_sem);  	memcpy(&ns->name, &old_ns->name, sizeof(ns->name)); +	ns->user_ns = get_user_ns(user_ns);  	up_read(&uts_sem);  	return ns;  } @@ -50,7 +61,8 @@ static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)   * utsname of this process won't be seen by parent, and vice   * versa.   */ -struct uts_namespace *copy_utsname(unsigned long flags, struct uts_namespace *old_ns) +struct uts_namespace *copy_utsname(unsigned long flags, +	struct user_namespace *user_ns, struct uts_namespace *old_ns)  {  	struct uts_namespace *new_ns; @@ -60,7 +72,7 @@ struct uts_namespace *copy_utsname(unsigned long flags, struct uts_namespace *ol  	if (!(flags & CLONE_NEWUTS))  		return old_ns; -	new_ns = clone_uts_ns(old_ns); +	new_ns = clone_uts_ns(user_ns, old_ns);  	put_uts_ns(old_ns);  	return new_ns; @@ -71,5 +83,58 @@ void free_uts_ns(struct kref *kref)  	struct uts_namespace *ns;  	ns = container_of(kref, struct uts_namespace, kref); +	put_user_ns(ns->user_ns); +	proc_free_inum(ns->proc_inum);  	kfree(ns);  } + +static void *utsns_get(struct task_struct *task) +{ +	struct uts_namespace *ns = NULL; +	struct nsproxy *nsproxy; + +	rcu_read_lock(); +	nsproxy = task_nsproxy(task); +	if (nsproxy) { +		ns = nsproxy->uts_ns; +		get_uts_ns(ns); +	} +	rcu_read_unlock(); + +	return ns; +} + +static void utsns_put(void *ns) +{ +	put_uts_ns(ns); +} + +static int utsns_install(struct nsproxy *nsproxy, void *new) +{ +	struct uts_namespace *ns = new; + +	if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) || +	    !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) +		return -EPERM; + +	get_uts_ns(ns); +	put_uts_ns(nsproxy->uts_ns); +	nsproxy->uts_ns = ns; +	return 0; +} + +static unsigned int utsns_inum(void *vp) +{ +	struct uts_namespace *ns = vp; + +	return ns->proc_inum; +} + +const struct proc_ns_operations utsns_operations = { +	.name		= "uts", +	.type		= CLONE_NEWUTS, +	.get		= utsns_get, +	.put		= utsns_put, +	.install	= utsns_install, +	.inum		= utsns_inum, +};  | 
