tracing: Change tracing_buffers_fops to rely on tracing_get_cpu()
[firefly-linux-kernel-4.4.55.git] / kernel / user_namespace.c
index a54f26f82eb250a60c7f24ecc651c6c1bbf8cc88..9064b919a4066fe9fca676581479e9162de56717 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/nsproxy.h>
 #include <linux/slab.h>
 #include <linux/user_namespace.h>
-#include <linux/proc_fs.h>
+#include <linux/proc_ns.h>
 #include <linux/highuid.h>
 #include <linux/cred.h>
 #include <linux/securebits.h>
@@ -25,7 +25,8 @@
 
 static struct kmem_cache *user_ns_cachep __read_mostly;
 
-static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
+static bool new_idmap_permitted(const struct file *file,
+                               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)
@@ -61,6 +62,9 @@ int create_user_ns(struct cred *new)
        kgid_t group = new->egid;
        int ret;
 
+       if (parent_ns->level > 32)
+               return -EUSERS;
+
        /*
         * Verify that we can not violate the policy of which files
         * may be accessed that is specified by the root directory,
@@ -91,6 +95,7 @@ int create_user_ns(struct cred *new)
        atomic_set(&ns->count, 1);
        /* Leave the new->user_ns reference with the new user namespace. */
        ns->parent = parent_ns;
+       ns->level = parent_ns->level + 1;
        ns->owner = owner;
        ns->group = group;
 
@@ -104,16 +109,21 @@ int create_user_ns(struct cred *new)
 int unshare_userns(unsigned long unshare_flags, struct cred **new_cred)
 {
        struct cred *cred;
+       int err = -ENOMEM;
 
        if (!(unshare_flags & CLONE_NEWUSER))
                return 0;
 
        cred = prepare_creds();
-       if (!cred)
-               return -ENOMEM;
+       if (cred) {
+               err = create_user_ns(cred);
+               if (err)
+                       put_cred(cred);
+               else
+                       *new_cred = cred;
+       }
 
-       *new_cred = cred;
-       return create_user_ns(cred);
+       return err;
 }
 
 void free_user_ns(struct user_namespace *ns)
@@ -612,10 +622,10 @@ static ssize_t map_write(struct file *file, const char __user *buf,
        if (map->nr_extents != 0)
                goto out;
 
-       /* Require the appropriate privilege CAP_SETUID or CAP_SETGID
-        * over the user namespace in order to set the id mapping.
+       /*
+        * Adjusting namespace settings requires capabilities on the target.
         */
-       if (cap_valid(cap_setid) && !ns_capable(ns, cap_setid))
+       if (cap_valid(cap_setid) && !file_ns_capable(file, ns, CAP_SYS_ADMIN))
                goto out;
 
        /* Get a buffer */
@@ -700,7 +710,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
 
        ret = -EPERM;
        /* Validate the user is allowed to use user id's mapped to. */
-       if (!new_idmap_permitted(ns, cap_setid, &new_map))
+       if (!new_idmap_permitted(file, ns, cap_setid, &new_map))
                goto out;
 
        /* Map the lower ids from the parent user namespace to the
@@ -787,7 +797,8 @@ ssize_t proc_projid_map_write(struct file *file, const char __user *buf, size_t
                         &ns->projid_map, &ns->parent->projid_map);
 }
 
-static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
+static bool new_idmap_permitted(const struct file *file, 
+                               struct user_namespace *ns, int cap_setid,
                                struct uid_gid_map *new_map)
 {
        /* Allow mapping to your own filesystem ids */
@@ -795,12 +806,12 @@ static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
                u32 id = new_map->extent[0].lower_first;
                if (cap_setid == CAP_SETUID) {
                        kuid_t uid = make_kuid(ns->parent, id);
-                       if (uid_eq(uid, current_fsuid()))
+                       if (uid_eq(uid, file->f_cred->fsuid))
                                return true;
                }
                else if (cap_setid == CAP_SETGID) {
                        kgid_t gid = make_kgid(ns->parent, id);
-                       if (gid_eq(gid, current_fsgid()))
+                       if (gid_eq(gid, file->f_cred->fsgid))
                                return true;
                }
        }
@@ -811,8 +822,10 @@ static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
 
        /* Allow the specified ids if we have the appropriate capability
         * (CAP_SETUID or CAP_SETGID) over the parent user namespace.
+        * And the opener of the id file also had the approprpiate capability.
         */
-       if (ns_capable(ns->parent, cap_setid))
+       if (ns_capable(ns->parent, cap_setid) &&
+           file_ns_capable(file, ns->parent, cap_setid))
                return true;
 
        return false;