diff options
Diffstat (limited to 'kernel/utsname.c')
| -rw-r--r-- | kernel/utsname.c | 135 |
1 files changed, 90 insertions, 45 deletions
diff --git a/kernel/utsname.c b/kernel/utsname.c index c859164a699..fd393124e50 100644 --- a/kernel/utsname.c +++ b/kernel/utsname.c @@ -9,44 +9,50 @@ * License. */ -#include <linux/module.h> +#include <linux/export.h> #include <linux/uts.h> #include <linux/utsname.h> -#include <linux/version.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/user_namespace.h> +#include <linux/proc_ns.h> -/* - * 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 - */ -static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns) +static struct uts_namespace *create_uts_ns(void) { - struct uts_namespace *ns; + struct uts_namespace *uts_ns; - ns = kmalloc(sizeof(struct uts_namespace), GFP_KERNEL); - if (ns) { - memcpy(&ns->name, &old_ns->name, sizeof(ns->name)); - kref_init(&ns->kref); - } - return ns; + uts_ns = kmalloc(sizeof(struct uts_namespace), GFP_KERNEL); + if (uts_ns) + kref_init(&uts_ns->kref); + return uts_ns; } /* - * unshare the current process' utsname namespace. - * called only in sys_unshare() + * Clone a new ns copying an original utsname, setting refcount to 1 + * @old_ns: namespace to clone + * Return ERR_PTR(-ENOMEM) on error (failure to kmalloc), new ns otherwise */ -int unshare_utsname(unsigned long unshare_flags, struct uts_namespace **new_uts) +static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns, + struct uts_namespace *old_ns) { - if (unshare_flags & CLONE_NEWUTS) { - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + struct uts_namespace *ns; + int err; - *new_uts = clone_uts_ns(current->nsproxy->uts_ns); - if (!*new_uts) - return -ENOMEM; + 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); } - return 0; + 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; } /* @@ -55,35 +61,21 @@ int unshare_utsname(unsigned long unshare_flags, struct uts_namespace **new_uts) * utsname of this process won't be seen by parent, and vice * versa. */ -int copy_utsname(int flags, struct task_struct *tsk) +struct uts_namespace *copy_utsname(unsigned long flags, + struct user_namespace *user_ns, struct uts_namespace *old_ns) { - struct uts_namespace *old_ns = tsk->nsproxy->uts_ns; struct uts_namespace *new_ns; - int err = 0; - - if (!old_ns) - return 0; + BUG_ON(!old_ns); get_uts_ns(old_ns); if (!(flags & CLONE_NEWUTS)) - return 0; + return old_ns; - if (!capable(CAP_SYS_ADMIN)) { - err = -EPERM; - goto out; - } + new_ns = clone_uts_ns(user_ns, old_ns); - new_ns = clone_uts_ns(old_ns); - if (!new_ns) { - err = -ENOMEM; - goto out; - } - tsk->nsproxy->uts_ns = new_ns; - -out: put_uts_ns(old_ns); - return err; + return new_ns; } void free_uts_ns(struct kref *kref) @@ -91,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, +}; |
