diff options
-rw-r--r-- | fs/proc/namespaces.c | 4 | ||||
-rw-r--r-- | include/linux/proc_fs.h | 1 | ||||
-rw-r--r-- | kernel/user_namespace.c | 90 |
3 files changed, 78 insertions, 17 deletions
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 2a17fd9ae6a9..030250c27d70 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <net/net_namespace.h> | 11 | #include <net/net_namespace.h> |
12 | #include <linux/ipc_namespace.h> | 12 | #include <linux/ipc_namespace.h> |
13 | #include <linux/pid_namespace.h> | 13 | #include <linux/pid_namespace.h> |
14 | #include <linux/user_namespace.h> | ||
14 | #include "internal.h" | 15 | #include "internal.h" |
15 | 16 | ||
16 | 17 | ||
@@ -27,6 +28,9 @@ static const struct proc_ns_operations *ns_entries[] = { | |||
27 | #ifdef CONFIG_PID_NS | 28 | #ifdef CONFIG_PID_NS |
28 | &pidns_operations, | 29 | &pidns_operations, |
29 | #endif | 30 | #endif |
31 | #ifdef CONFIG_USER_NS | ||
32 | &userns_operations, | ||
33 | #endif | ||
30 | &mntns_operations, | 34 | &mntns_operations, |
31 | }; | 35 | }; |
32 | 36 | ||
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 9014c041e752..31447819bc55 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h | |||
@@ -258,6 +258,7 @@ extern const struct proc_ns_operations netns_operations; | |||
258 | extern const struct proc_ns_operations utsns_operations; | 258 | extern const struct proc_ns_operations utsns_operations; |
259 | extern const struct proc_ns_operations ipcns_operations; | 259 | extern const struct proc_ns_operations ipcns_operations; |
260 | extern const struct proc_ns_operations pidns_operations; | 260 | extern const struct proc_ns_operations pidns_operations; |
261 | extern const struct proc_ns_operations userns_operations; | ||
261 | extern const struct proc_ns_operations mntns_operations; | 262 | extern const struct proc_ns_operations mntns_operations; |
262 | 263 | ||
263 | union proc_op { | 264 | union proc_op { |
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 49096d559e08..a9460774e77d 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/nsproxy.h> | 9 | #include <linux/nsproxy.h> |
10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
11 | #include <linux/user_namespace.h> | 11 | #include <linux/user_namespace.h> |
12 | #include <linux/proc_fs.h> | ||
12 | #include <linux/highuid.h> | 13 | #include <linux/highuid.h> |
13 | #include <linux/cred.h> | 14 | #include <linux/cred.h> |
14 | #include <linux/securebits.h> | 15 | #include <linux/securebits.h> |
@@ -26,6 +27,24 @@ static struct kmem_cache *user_ns_cachep __read_mostly; | |||
26 | static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid, | 27 | static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid, |
27 | struct uid_gid_map *map); | 28 | struct uid_gid_map *map); |
28 | 29 | ||
30 | static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns) | ||
31 | { | ||
32 | /* Start with the same capabilities as init but useless for doing | ||
33 | * anything as the capabilities are bound to the new user namespace. | ||
34 | */ | ||
35 | cred->securebits = SECUREBITS_DEFAULT; | ||
36 | cred->cap_inheritable = CAP_EMPTY_SET; | ||
37 | cred->cap_permitted = CAP_FULL_SET; | ||
38 | cred->cap_effective = CAP_FULL_SET; | ||
39 | cred->cap_bset = CAP_FULL_SET; | ||
40 | #ifdef CONFIG_KEYS | ||
41 | key_put(cred->request_key_auth); | ||
42 | cred->request_key_auth = NULL; | ||
43 | #endif | ||
44 | /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */ | ||
45 | cred->user_ns = user_ns; | ||
46 | } | ||
47 | |||
29 | /* | 48 | /* |
30 | * Create a new user namespace, deriving the creator from the user in the | 49 | * Create a new user namespace, deriving the creator from the user in the |
31 | * passed credentials, and replacing that user with the new root user for the | 50 | * passed credentials, and replacing that user with the new root user for the |
@@ -53,27 +72,12 @@ int create_user_ns(struct cred *new) | |||
53 | return -ENOMEM; | 72 | return -ENOMEM; |
54 | 73 | ||
55 | kref_init(&ns->kref); | 74 | kref_init(&ns->kref); |
75 | /* Leave the new->user_ns reference with the new user namespace. */ | ||
56 | ns->parent = parent_ns; | 76 | ns->parent = parent_ns; |
57 | ns->owner = owner; | 77 | ns->owner = owner; |
58 | ns->group = group; | 78 | ns->group = group; |
59 | 79 | ||
60 | /* Start with the same capabilities as init but useless for doing | 80 | set_cred_user_ns(new, ns); |
61 | * anything as the capabilities are bound to the new user namespace. | ||
62 | */ | ||
63 | new->securebits = SECUREBITS_DEFAULT; | ||
64 | new->cap_inheritable = CAP_EMPTY_SET; | ||
65 | new->cap_permitted = CAP_FULL_SET; | ||
66 | new->cap_effective = CAP_FULL_SET; | ||
67 | new->cap_bset = CAP_FULL_SET; | ||
68 | #ifdef CONFIG_KEYS | ||
69 | key_put(new->request_key_auth); | ||
70 | new->request_key_auth = NULL; | ||
71 | #endif | ||
72 | /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */ | ||
73 | |||
74 | /* Leave the new->user_ns reference with the new user namespace. */ | ||
75 | /* Leave the reference to our user_ns with the new cred. */ | ||
76 | new->user_ns = ns; | ||
77 | 81 | ||
78 | return 0; | 82 | return 0; |
79 | } | 83 | } |
@@ -737,6 +741,58 @@ static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid, | |||
737 | return false; | 741 | return false; |
738 | } | 742 | } |
739 | 743 | ||
744 | static void *userns_get(struct task_struct *task) | ||
745 | { | ||
746 | struct user_namespace *user_ns; | ||
747 | |||
748 | rcu_read_lock(); | ||
749 | user_ns = get_user_ns(__task_cred(task)->user_ns); | ||
750 | rcu_read_unlock(); | ||
751 | |||
752 | return user_ns; | ||
753 | } | ||
754 | |||
755 | static void userns_put(void *ns) | ||
756 | { | ||
757 | put_user_ns(ns); | ||
758 | } | ||
759 | |||
760 | static int userns_install(struct nsproxy *nsproxy, void *ns) | ||
761 | { | ||
762 | struct user_namespace *user_ns = ns; | ||
763 | struct cred *cred; | ||
764 | |||
765 | /* Don't allow gaining capabilities by reentering | ||
766 | * the same user namespace. | ||
767 | */ | ||
768 | if (user_ns == current_user_ns()) | ||
769 | return -EINVAL; | ||
770 | |||
771 | /* Threaded many not enter a different user namespace */ | ||
772 | if (atomic_read(¤t->mm->mm_users) > 1) | ||
773 | return -EINVAL; | ||
774 | |||
775 | if (!ns_capable(user_ns, CAP_SYS_ADMIN)) | ||
776 | return -EPERM; | ||
777 | |||
778 | cred = prepare_creds(); | ||
779 | if (!cred) | ||
780 | return -ENOMEM; | ||
781 | |||
782 | put_user_ns(cred->user_ns); | ||
783 | set_cred_user_ns(cred, get_user_ns(user_ns)); | ||
784 | |||
785 | return commit_creds(cred); | ||
786 | } | ||
787 | |||
788 | const struct proc_ns_operations userns_operations = { | ||
789 | .name = "user", | ||
790 | .type = CLONE_NEWUSER, | ||
791 | .get = userns_get, | ||
792 | .put = userns_put, | ||
793 | .install = userns_install, | ||
794 | }; | ||
795 | |||
740 | static __init int user_namespaces_init(void) | 796 | static __init int user_namespaces_init(void) |
741 | { | 797 | { |
742 | user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC); | 798 | user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC); |