userns: Implent proc namespace operations
authorEric W. Biederman <ebiederm@xmission.com>
Thu, 26 Jul 2012 13:24:06 +0000 (06:24 -0700)
committerEric W. Biederman <ebiederm@xmission.com>
Tue, 20 Nov 2012 12:18:13 +0000 (04:18 -0800)
This allows entering a user namespace, and the ability
to store a reference to a user namespace with a bind
mount.

Addition of missing userns_ns_put in userns_install
from Gao feng <gaofeng@cn.fujitsu.com>

Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
fs/proc/namespaces.c
include/linux/proc_fs.h
kernel/user_namespace.c

index 2a17fd9ae6a9ae75cfbbe9e92f69ac77aa945c5c..030250c27d70c4c182016c19399e45f929b7b53c 100644 (file)
@@ -11,6 +11,7 @@
 #include <net/net_namespace.h>
 #include <linux/ipc_namespace.h>
 #include <linux/pid_namespace.h>
+#include <linux/user_namespace.h>
 #include "internal.h"
 
 
@@ -26,6 +27,9 @@ static const struct proc_ns_operations *ns_entries[] = {
 #endif
 #ifdef CONFIG_PID_NS
        &pidns_operations,
+#endif
+#ifdef CONFIG_USER_NS
+       &userns_operations,
 #endif
        &mntns_operations,
 };
index 9014c041e752d3a1e160f9d0d7a106eb3ff9f97b..31447819bc556ca7f07f5c55633a2b1d179815f0 100644 (file)
@@ -258,6 +258,7 @@ extern const struct proc_ns_operations netns_operations;
 extern const struct proc_ns_operations utsns_operations;
 extern const struct proc_ns_operations ipcns_operations;
 extern const struct proc_ns_operations pidns_operations;
+extern const struct proc_ns_operations userns_operations;
 extern const struct proc_ns_operations mntns_operations;
 
 union proc_op {
index 49096d559e08755e14f6f88f3dfffdfa81caccaa..a9460774e77dd6535b04e2b5944fc7533791374a 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/nsproxy.h>
 #include <linux/slab.h>
 #include <linux/user_namespace.h>
+#include <linux/proc_fs.h>
 #include <linux/highuid.h>
 #include <linux/cred.h>
 #include <linux/securebits.h>
@@ -26,6 +27,24 @@ static struct kmem_cache *user_ns_cachep __read_mostly;
 static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
                                struct uid_gid_map *map);
 
+static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
+{
+       /* Start with the same capabilities as init but useless for doing
+        * anything as the capabilities are bound to the new user namespace.
+        */
+       cred->securebits = SECUREBITS_DEFAULT;
+       cred->cap_inheritable = CAP_EMPTY_SET;
+       cred->cap_permitted = CAP_FULL_SET;
+       cred->cap_effective = CAP_FULL_SET;
+       cred->cap_bset = CAP_FULL_SET;
+#ifdef CONFIG_KEYS
+       key_put(cred->request_key_auth);
+       cred->request_key_auth = NULL;
+#endif
+       /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
+       cred->user_ns = user_ns;
+}
+
 /*
  * Create a new user namespace, deriving the creator from the user in the
  * passed credentials, and replacing that user with the new root user for the
@@ -53,27 +72,12 @@ int create_user_ns(struct cred *new)
                return -ENOMEM;
 
        kref_init(&ns->kref);
+       /* Leave the new->user_ns reference with the new user namespace. */
        ns->parent = parent_ns;
        ns->owner = owner;
        ns->group = group;
 
-       /* Start with the same capabilities as init but useless for doing
-        * anything as the capabilities are bound to the new user namespace.
-        */
-       new->securebits = SECUREBITS_DEFAULT;
-       new->cap_inheritable = CAP_EMPTY_SET;
-       new->cap_permitted = CAP_FULL_SET;
-       new->cap_effective = CAP_FULL_SET;
-       new->cap_bset = CAP_FULL_SET;
-#ifdef CONFIG_KEYS
-       key_put(new->request_key_auth);
-       new->request_key_auth = NULL;
-#endif
-       /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
-
-       /* Leave the new->user_ns reference with the new user namespace. */
-       /* Leave the reference to our user_ns with the new cred. */
-       new->user_ns = ns;
+       set_cred_user_ns(new, ns);
 
        return 0;
 }
@@ -737,6 +741,58 @@ static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
        return false;
 }
 
+static void *userns_get(struct task_struct *task)
+{
+       struct user_namespace *user_ns;
+
+       rcu_read_lock();
+       user_ns = get_user_ns(__task_cred(task)->user_ns);
+       rcu_read_unlock();
+
+       return user_ns;
+}
+
+static void userns_put(void *ns)
+{
+       put_user_ns(ns);
+}
+
+static int userns_install(struct nsproxy *nsproxy, void *ns)
+{
+       struct user_namespace *user_ns = ns;
+       struct cred *cred;
+
+       /* Don't allow gaining capabilities by reentering
+        * the same user namespace.
+        */
+       if (user_ns == current_user_ns())
+               return -EINVAL;
+
+       /* Threaded many not enter a different user namespace */
+       if (atomic_read(&current->mm->mm_users) > 1)
+               return -EINVAL;
+
+       if (!ns_capable(user_ns, CAP_SYS_ADMIN))
+               return -EPERM;
+
+       cred = prepare_creds();
+       if (!cred)
+               return -ENOMEM;
+
+       put_user_ns(cred->user_ns);
+       set_cred_user_ns(cred, get_user_ns(user_ns));
+
+       return commit_creds(cred);
+}
+
+const struct proc_ns_operations userns_operations = {
+       .name           = "user",
+       .type           = CLONE_NEWUSER,
+       .get            = userns_get,
+       .put            = userns_put,
+       .install        = userns_install,
+};
+
 static __init int user_namespaces_init(void)
 {
        user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC);