diff options
-rw-r--r-- | include/linux/sched.h | 1 | ||||
-rw-r--r-- | include/linux/utsname.h | 11 | ||||
-rw-r--r-- | kernel/fork.c | 20 | ||||
-rw-r--r-- | kernel/nsproxy.c | 2 | ||||
-rw-r--r-- | kernel/utsname.c | 53 |
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 |
50 | extern int unshare_utsname(unsigned long unshare_flags, | ||
51 | struct uts_namespace **new_uts); | ||
50 | extern int copy_utsname(int flags, struct task_struct *tsk); | 52 | extern int copy_utsname(int flags, struct task_struct *tsk); |
51 | extern void free_uts_ns(struct kref *kref); | 53 | extern 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 |
60 | static 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 | |||
58 | static inline int copy_utsname(int flags, struct task_struct *tsk) | 69 | static 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 | ||
1693 | bad_unshare_cleanup_uts: | ||
1694 | if (new_uts) | ||
1695 | put_uts_ns(new_uts); | ||
1696 | |||
1683 | bad_unshare_cleanup_semundo: | 1697 | bad_unshare_cleanup_semundo: |
1684 | bad_unshare_cleanup_fd: | 1698 | bad_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 | */ | ||
22 | static 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 | */ | ||
38 | int 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 @@ | |||
23 | int copy_utsname(int flags, struct task_struct *tsk) | 58 | int 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 | |||
84 | out: | ||
85 | put_uts_ns(old_ns); | ||
33 | return err; | 86 | return err; |
34 | } | 87 | } |
35 | 88 | ||