diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2012-07-26 08:15:35 -0400 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2012-11-20 07:18:14 -0500 |
commit | b2e0d98705e60e45bbb3c0032c48824ad7ae0704 (patch) | |
tree | e187c82e1c3babd34095f2b946614131719bbb03 /kernel | |
parent | cde1975bc242f3e1072bde623ef378e547b73f91 (diff) |
userns: Implement unshare of the user namespace
- Add CLONE_THREAD to the unshare flags if CLONE_NEWUSER is selected
As changing user namespaces is only valid if all there is only
a single thread.
- Restore the code to add CLONE_VM if CLONE_THREAD is selected and
the code to addCLONE_SIGHAND if CLONE_VM is selected.
Making the constraints in the code clear.
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/fork.c | 25 | ||||
-rw-r--r-- | kernel/nsproxy.c | 8 | ||||
-rw-r--r-- | kernel/user_namespace.c | 15 |
3 files changed, 41 insertions, 7 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 8c29abb19014..38e53b87402c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1687,7 +1687,7 @@ static int check_unshare_flags(unsigned long unshare_flags) | |||
1687 | if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND| | 1687 | if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND| |
1688 | CLONE_VM|CLONE_FILES|CLONE_SYSVSEM| | 1688 | CLONE_VM|CLONE_FILES|CLONE_SYSVSEM| |
1689 | CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET| | 1689 | CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET| |
1690 | CLONE_NEWPID)) | 1690 | CLONE_NEWUSER|CLONE_NEWPID)) |
1691 | return -EINVAL; | 1691 | return -EINVAL; |
1692 | /* | 1692 | /* |
1693 | * Not implemented, but pretend it works if there is nothing to | 1693 | * Not implemented, but pretend it works if there is nothing to |
@@ -1754,11 +1754,17 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) | |||
1754 | { | 1754 | { |
1755 | struct fs_struct *fs, *new_fs = NULL; | 1755 | struct fs_struct *fs, *new_fs = NULL; |
1756 | struct files_struct *fd, *new_fd = NULL; | 1756 | struct files_struct *fd, *new_fd = NULL; |
1757 | struct cred *new_cred = NULL; | ||
1757 | struct nsproxy *new_nsproxy = NULL; | 1758 | struct nsproxy *new_nsproxy = NULL; |
1758 | int do_sysvsem = 0; | 1759 | int do_sysvsem = 0; |
1759 | int err; | 1760 | int err; |
1760 | 1761 | ||
1761 | /* | 1762 | /* |
1763 | * If unsharing a user namespace must also unshare the thread. | ||
1764 | */ | ||
1765 | if (unshare_flags & CLONE_NEWUSER) | ||
1766 | unshare_flags |= CLONE_THREAD; | ||
1767 | /* | ||
1762 | * If unsharing a pid namespace must also unshare the thread. | 1768 | * If unsharing a pid namespace must also unshare the thread. |
1763 | */ | 1769 | */ |
1764 | if (unshare_flags & CLONE_NEWPID) | 1770 | if (unshare_flags & CLONE_NEWPID) |
@@ -1795,11 +1801,15 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) | |||
1795 | err = unshare_fd(unshare_flags, &new_fd); | 1801 | err = unshare_fd(unshare_flags, &new_fd); |
1796 | if (err) | 1802 | if (err) |
1797 | goto bad_unshare_cleanup_fs; | 1803 | goto bad_unshare_cleanup_fs; |
1798 | err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, new_fs); | 1804 | err = unshare_userns(unshare_flags, &new_cred); |
1799 | if (err) | 1805 | if (err) |
1800 | goto bad_unshare_cleanup_fd; | 1806 | goto bad_unshare_cleanup_fd; |
1807 | err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, | ||
1808 | new_cred, new_fs); | ||
1809 | if (err) | ||
1810 | goto bad_unshare_cleanup_cred; | ||
1801 | 1811 | ||
1802 | if (new_fs || new_fd || do_sysvsem || new_nsproxy) { | 1812 | if (new_fs || new_fd || do_sysvsem || new_cred || new_nsproxy) { |
1803 | if (do_sysvsem) { | 1813 | if (do_sysvsem) { |
1804 | /* | 1814 | /* |
1805 | * CLONE_SYSVSEM is equivalent to sys_exit(). | 1815 | * CLONE_SYSVSEM is equivalent to sys_exit(). |
@@ -1832,11 +1842,20 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) | |||
1832 | } | 1842 | } |
1833 | 1843 | ||
1834 | task_unlock(current); | 1844 | task_unlock(current); |
1845 | |||
1846 | if (new_cred) { | ||
1847 | /* Install the new user namespace */ | ||
1848 | commit_creds(new_cred); | ||
1849 | new_cred = NULL; | ||
1850 | } | ||
1835 | } | 1851 | } |
1836 | 1852 | ||
1837 | if (new_nsproxy) | 1853 | if (new_nsproxy) |
1838 | put_nsproxy(new_nsproxy); | 1854 | put_nsproxy(new_nsproxy); |
1839 | 1855 | ||
1856 | bad_unshare_cleanup_cred: | ||
1857 | if (new_cred) | ||
1858 | put_cred(new_cred); | ||
1840 | bad_unshare_cleanup_fd: | 1859 | bad_unshare_cleanup_fd: |
1841 | if (new_fd) | 1860 | if (new_fd) |
1842 | put_files_struct(new_fd); | 1861 | put_files_struct(new_fd); |
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 2ddd81657a2a..78e2ecb20165 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c | |||
@@ -186,7 +186,7 @@ void free_nsproxy(struct nsproxy *ns) | |||
186 | * On success, returns the new nsproxy. | 186 | * On success, returns the new nsproxy. |
187 | */ | 187 | */ |
188 | int unshare_nsproxy_namespaces(unsigned long unshare_flags, | 188 | int unshare_nsproxy_namespaces(unsigned long unshare_flags, |
189 | struct nsproxy **new_nsp, struct fs_struct *new_fs) | 189 | struct nsproxy **new_nsp, struct cred *new_cred, struct fs_struct *new_fs) |
190 | { | 190 | { |
191 | struct user_namespace *user_ns; | 191 | struct user_namespace *user_ns; |
192 | int err = 0; | 192 | int err = 0; |
@@ -195,12 +195,12 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags, | |||
195 | CLONE_NEWNET | CLONE_NEWPID))) | 195 | CLONE_NEWNET | CLONE_NEWPID))) |
196 | return 0; | 196 | return 0; |
197 | 197 | ||
198 | if (!nsown_capable(CAP_SYS_ADMIN)) | 198 | user_ns = new_cred ? new_cred->user_ns : current_user_ns(); |
199 | if (!ns_capable(user_ns, CAP_SYS_ADMIN)) | ||
199 | return -EPERM; | 200 | return -EPERM; |
200 | 201 | ||
201 | user_ns = current_user_ns(); | ||
202 | *new_nsp = create_new_namespaces(unshare_flags, current, user_ns, | 202 | *new_nsp = create_new_namespaces(unshare_flags, current, user_ns, |
203 | new_fs ? new_fs : current->fs); | 203 | new_fs ? new_fs : current->fs); |
204 | if (IS_ERR(*new_nsp)) { | 204 | if (IS_ERR(*new_nsp)) { |
205 | err = PTR_ERR(*new_nsp); | 205 | err = PTR_ERR(*new_nsp); |
206 | goto out; | 206 | goto out; |
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index a9460774e77d..ce92f7e6290a 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
@@ -82,6 +82,21 @@ int create_user_ns(struct cred *new) | |||
82 | return 0; | 82 | return 0; |
83 | } | 83 | } |
84 | 84 | ||
85 | int unshare_userns(unsigned long unshare_flags, struct cred **new_cred) | ||
86 | { | ||
87 | struct cred *cred; | ||
88 | |||
89 | if (!(unshare_flags & CLONE_NEWUSER)) | ||
90 | return 0; | ||
91 | |||
92 | cred = prepare_creds(); | ||
93 | if (!cred) | ||
94 | return -ENOMEM; | ||
95 | |||
96 | *new_cred = cred; | ||
97 | return create_user_ns(cred); | ||
98 | } | ||
99 | |||
85 | void free_user_ns(struct kref *kref) | 100 | void free_user_ns(struct kref *kref) |
86 | { | 101 | { |
87 | struct user_namespace *parent, *ns = | 102 | struct user_namespace *parent, *ns = |