diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/fork.c | 20 | ||||
-rw-r--r-- | kernel/nsproxy.c | 2 | ||||
-rw-r--r-- | kernel/utsname.c | 53 |
3 files changed, 71 insertions, 4 deletions
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 | ||