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 | |
| 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>
| -rw-r--r-- | include/linux/nsproxy.h | 2 | ||||
| -rw-r--r-- | include/linux/user_namespace.h | 9 | ||||
| -rw-r--r-- | kernel/fork.c | 25 | ||||
| -rw-r--r-- | kernel/nsproxy.c | 8 | ||||
| -rw-r--r-- | kernel/user_namespace.c | 15 |
5 files changed, 51 insertions, 8 deletions
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h index cc37a55ad004..10e5947491c7 100644 --- a/include/linux/nsproxy.h +++ b/include/linux/nsproxy.h | |||
| @@ -67,7 +67,7 @@ void exit_task_namespaces(struct task_struct *tsk); | |||
| 67 | void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); | 67 | void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); |
| 68 | void free_nsproxy(struct nsproxy *ns); | 68 | void free_nsproxy(struct nsproxy *ns); |
| 69 | int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **, | 69 | int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **, |
| 70 | struct fs_struct *); | 70 | struct cred *, struct fs_struct *); |
| 71 | int __init nsproxy_cache_init(void); | 71 | int __init nsproxy_cache_init(void); |
| 72 | 72 | ||
| 73 | static inline void put_nsproxy(struct nsproxy *ns) | 73 | static inline void put_nsproxy(struct nsproxy *ns) |
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 95142cae446a..17651f08d67f 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h | |||
| @@ -39,6 +39,7 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns) | |||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | extern int create_user_ns(struct cred *new); | 41 | extern int create_user_ns(struct cred *new); |
| 42 | extern int unshare_userns(unsigned long unshare_flags, struct cred **new_cred); | ||
| 42 | extern void free_user_ns(struct kref *kref); | 43 | extern void free_user_ns(struct kref *kref); |
| 43 | 44 | ||
| 44 | static inline void put_user_ns(struct user_namespace *ns) | 45 | static inline void put_user_ns(struct user_namespace *ns) |
| @@ -66,6 +67,14 @@ static inline int create_user_ns(struct cred *new) | |||
| 66 | return -EINVAL; | 67 | return -EINVAL; |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 70 | static inline int unshare_userns(unsigned long unshare_flags, | ||
| 71 | struct cred **new_cred) | ||
| 72 | { | ||
| 73 | if (unshare_flags & CLONE_NEWUSER) | ||
| 74 | return -EINVAL; | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 69 | static inline void put_user_ns(struct user_namespace *ns) | 78 | static inline void put_user_ns(struct user_namespace *ns) |
| 70 | { | 79 | { |
| 71 | } | 80 | } |
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 = |
