aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/sched.h1
-rw-r--r--include/linux/utsname.h11
-rw-r--r--kernel/fork.c20
-rw-r--r--kernel/nsproxy.c2
-rw-r--r--kernel/utsname.c53
5 files changed, 83 insertions, 4 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 46d6f5be72f2..a973e7012315 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -24,6 +24,7 @@
24#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */ 24#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */
25#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */ 25#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */
26#define CLONE_STOPPED 0x02000000 /* Start in stopped state */ 26#define CLONE_STOPPED 0x02000000 /* Start in stopped state */
27#define CLONE_NEWUTS 0x04000000 /* New utsname group? */
27 28
28/* 29/*
29 * Scheduling policies 30 * Scheduling policies
diff --git a/include/linux/utsname.h b/include/linux/utsname.h
index 99e522369f4f..02e4b6972064 100644
--- a/include/linux/utsname.h
+++ b/include/linux/utsname.h
@@ -47,6 +47,8 @@ static inline void get_uts_ns(struct uts_namespace *ns)
47} 47}
48 48
49#ifdef CONFIG_UTS_NS 49#ifdef CONFIG_UTS_NS
50extern int unshare_utsname(unsigned long unshare_flags,
51 struct uts_namespace **new_uts);
50extern int copy_utsname(int flags, struct task_struct *tsk); 52extern int copy_utsname(int flags, struct task_struct *tsk);
51extern void free_uts_ns(struct kref *kref); 53extern void free_uts_ns(struct kref *kref);
52 54
@@ -55,6 +57,15 @@ static inline void put_uts_ns(struct uts_namespace *ns)
55 kref_put(&ns->kref, free_uts_ns); 57 kref_put(&ns->kref, free_uts_ns);
56} 58}
57#else 59#else
60static inline int unshare_utsname(unsigned long unshare_flags,
61 struct uts_namespace **new_uts)
62{
63 if (unshare_flags & CLONE_NEWUTS)
64 return -EINVAL;
65
66 return 0;
67}
68
58static inline int copy_utsname(int flags, struct task_struct *tsk) 69static inline int copy_utsname(int flags, struct task_struct *tsk)
59{ 70{
60 return 0; 71 return 0;
diff --git a/kernel/fork.c b/kernel/fork.c
index 33fcf0733ca6..c08cf05dd59b 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1607,13 +1607,14 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
1607 struct files_struct *fd, *new_fd = NULL; 1607 struct files_struct *fd, *new_fd = NULL;
1608 struct sem_undo_list *new_ulist = NULL; 1608 struct sem_undo_list *new_ulist = NULL;
1609 struct nsproxy *new_nsproxy, *old_nsproxy; 1609 struct nsproxy *new_nsproxy, *old_nsproxy;
1610 struct uts_namespace *uts, *new_uts = NULL;
1610 1611
1611 check_unshare_flags(&unshare_flags); 1612 check_unshare_flags(&unshare_flags);
1612 1613
1613 /* Return -EINVAL for all unsupported flags */ 1614 /* Return -EINVAL for all unsupported flags */
1614 err = -EINVAL; 1615 err = -EINVAL;
1615 if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND| 1616 if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
1616 CLONE_VM|CLONE_FILES|CLONE_SYSVSEM)) 1617 CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|CLONE_NEWUTS))
1617 goto bad_unshare_out; 1618 goto bad_unshare_out;
1618 1619
1619 if ((err = unshare_thread(unshare_flags))) 1620 if ((err = unshare_thread(unshare_flags)))
@@ -1630,14 +1631,17 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
1630 goto bad_unshare_cleanup_vm; 1631 goto bad_unshare_cleanup_vm;
1631 if ((err = unshare_semundo(unshare_flags, &new_ulist))) 1632 if ((err = unshare_semundo(unshare_flags, &new_ulist)))
1632 goto bad_unshare_cleanup_fd; 1633 goto bad_unshare_cleanup_fd;
1634 if ((err = unshare_utsname(unshare_flags, &new_uts)))
1635 goto bad_unshare_cleanup_semundo;
1633 1636
1634 if (new_fs || new_ns || new_sigh || new_mm || new_fd || new_ulist) { 1637 if (new_fs || new_ns || new_sigh || new_mm || new_fd || new_ulist ||
1638 new_uts) {
1635 1639
1636 old_nsproxy = current->nsproxy; 1640 old_nsproxy = current->nsproxy;
1637 new_nsproxy = dup_namespaces(old_nsproxy); 1641 new_nsproxy = dup_namespaces(old_nsproxy);
1638 if (!new_nsproxy) { 1642 if (!new_nsproxy) {
1639 err = -ENOMEM; 1643 err = -ENOMEM;
1640 goto bad_unshare_cleanup_semundo; 1644 goto bad_unshare_cleanup_uts;
1641 } 1645 }
1642 1646
1643 task_lock(current); 1647 task_lock(current);
@@ -1676,10 +1680,20 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
1676 new_fd = fd; 1680 new_fd = fd;
1677 } 1681 }
1678 1682
1683 if (new_uts) {
1684 uts = current->nsproxy->uts_ns;
1685 current->nsproxy->uts_ns = new_uts;
1686 new_uts = uts;
1687 }
1688
1679 task_unlock(current); 1689 task_unlock(current);
1680 put_nsproxy(old_nsproxy); 1690 put_nsproxy(old_nsproxy);
1681 } 1691 }
1682 1692
1693bad_unshare_cleanup_uts:
1694 if (new_uts)
1695 put_uts_ns(new_uts);
1696
1683bad_unshare_cleanup_semundo: 1697bad_unshare_cleanup_semundo:
1684bad_unshare_cleanup_fd: 1698bad_unshare_cleanup_fd:
1685 if (new_fd) 1699 if (new_fd)
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 47c19280c55b..8246813335cc 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -82,7 +82,7 @@ int copy_namespaces(int flags, struct task_struct *tsk)
82 82
83 get_nsproxy(old_ns); 83 get_nsproxy(old_ns);
84 84
85 if (!(flags & CLONE_NEWNS)) 85 if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS)))
86 return 0; 86 return 0;
87 87
88 new_ns = clone_namespaces(old_ns); 88 new_ns = clone_namespaces(old_ns);
diff --git a/kernel/utsname.c b/kernel/utsname.c
index 1824384ecfa3..c859164a6993 100644
--- a/kernel/utsname.c
+++ b/kernel/utsname.c
@@ -15,6 +15,41 @@
15#include <linux/version.h> 15#include <linux/version.h>
16 16
17/* 17/*
18 * Clone a new ns copying an original utsname, setting refcount to 1
19 * @old_ns: namespace to clone
20 * Return NULL on error (failure to kmalloc), new ns otherwise
21 */
22static struct uts_namespace *clone_uts_ns(struct uts_namespace *old_ns)
23{
24 struct uts_namespace *ns;
25
26 ns = kmalloc(sizeof(struct uts_namespace), GFP_KERNEL);
27 if (ns) {
28 memcpy(&ns->name, &old_ns->name, sizeof(ns->name));
29 kref_init(&ns->kref);
30 }
31 return ns;
32}
33
34/*
35 * unshare the current process' utsname namespace.
36 * called only in sys_unshare()
37 */
38int unshare_utsname(unsigned long unshare_flags, struct uts_namespace **new_uts)
39{
40 if (unshare_flags & CLONE_NEWUTS) {
41 if (!capable(CAP_SYS_ADMIN))
42 return -EPERM;
43
44 *new_uts = clone_uts_ns(current->nsproxy->uts_ns);
45 if (!*new_uts)
46 return -ENOMEM;
47 }
48
49 return 0;
50}
51
52/*
18 * Copy task tsk's utsname namespace, or clone it if flags 53 * Copy task tsk's utsname namespace, or clone it if flags
19 * specifies CLONE_NEWUTS. In latter case, changes to the 54 * specifies CLONE_NEWUTS. In latter case, changes to the
20 * utsname of this process won't be seen by parent, and vice 55 * utsname of this process won't be seen by parent, and vice
@@ -23,6 +58,7 @@
23int copy_utsname(int flags, struct task_struct *tsk) 58int copy_utsname(int flags, struct task_struct *tsk)
24{ 59{
25 struct uts_namespace *old_ns = tsk->nsproxy->uts_ns; 60 struct uts_namespace *old_ns = tsk->nsproxy->uts_ns;
61 struct uts_namespace *new_ns;
26 int err = 0; 62 int err = 0;
27 63
28 if (!old_ns) 64 if (!old_ns)
@@ -30,6 +66,23 @@ int copy_utsname(int flags, struct task_struct *tsk)
30 66
31 get_uts_ns(old_ns); 67 get_uts_ns(old_ns);
32 68
69 if (!(flags & CLONE_NEWUTS))
70 return 0;
71
72 if (!capable(CAP_SYS_ADMIN)) {
73 err = -EPERM;
74 goto out;
75 }
76
77 new_ns = clone_uts_ns(old_ns);
78 if (!new_ns) {
79 err = -ENOMEM;
80 goto out;
81 }
82 tsk->nsproxy->uts_ns = new_ns;
83
84out:
85 put_uts_ns(old_ns);
33 return err; 86 return err;
34} 87}
35 88