Merge branches 'powercap' and 'pm-misc'
[linux-2.6-microblaze.git] / security / commoncap.c
index 0ca31c8..26c1cb7 100644 (file)
@@ -371,10 +371,11 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
 {
        int size, ret;
        kuid_t kroot;
+       u32 nsmagic, magic;
        uid_t root, mappedroot;
        char *tmpbuf = NULL;
        struct vfs_cap_data *cap;
-       struct vfs_ns_cap_data *nscap;
+       struct vfs_ns_cap_data *nscap = NULL;
        struct dentry *dentry;
        struct user_namespace *fs_ns;
 
@@ -396,46 +397,61 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
        fs_ns = inode->i_sb->s_user_ns;
        cap = (struct vfs_cap_data *) tmpbuf;
        if (is_v2header((size_t) ret, cap)) {
-               /* If this is sizeof(vfs_cap_data) then we're ok with the
-                * on-disk value, so return that.  */
-               if (alloc)
-                       *buffer = tmpbuf;
-               else
-                       kfree(tmpbuf);
-               return ret;
-       } else if (!is_v3header((size_t) ret, cap)) {
-               kfree(tmpbuf);
-               return -EINVAL;
+               root = 0;
+       } else if (is_v3header((size_t) ret, cap)) {
+               nscap = (struct vfs_ns_cap_data *) tmpbuf;
+               root = le32_to_cpu(nscap->rootid);
+       } else {
+               size = -EINVAL;
+               goto out_free;
        }
 
-       nscap = (struct vfs_ns_cap_data *) tmpbuf;
-       root = le32_to_cpu(nscap->rootid);
        kroot = make_kuid(fs_ns, root);
 
        /* If the root kuid maps to a valid uid in current ns, then return
         * this as a nscap. */
        mappedroot = from_kuid(current_user_ns(), kroot);
        if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) {
+               size = sizeof(struct vfs_ns_cap_data);
                if (alloc) {
-                       *buffer = tmpbuf;
+                       if (!nscap) {
+                               /* v2 -> v3 conversion */
+                               nscap = kzalloc(size, GFP_ATOMIC);
+                               if (!nscap) {
+                                       size = -ENOMEM;
+                                       goto out_free;
+                               }
+                               nsmagic = VFS_CAP_REVISION_3;
+                               magic = le32_to_cpu(cap->magic_etc);
+                               if (magic & VFS_CAP_FLAGS_EFFECTIVE)
+                                       nsmagic |= VFS_CAP_FLAGS_EFFECTIVE;
+                               memcpy(&nscap->data, &cap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
+                               nscap->magic_etc = cpu_to_le32(nsmagic);
+                       } else {
+                               /* use allocated v3 buffer */
+                               tmpbuf = NULL;
+                       }
                        nscap->rootid = cpu_to_le32(mappedroot);
-               } else
-                       kfree(tmpbuf);
-               return size;
+                       *buffer = nscap;
+               }
+               goto out_free;
        }
 
        if (!rootid_owns_currentns(kroot)) {
-               kfree(tmpbuf);
-               return -EOPNOTSUPP;
+               size = -EOVERFLOW;
+               goto out_free;
        }
 
        /* This comes from a parent namespace.  Return as a v2 capability */
        size = sizeof(struct vfs_cap_data);
        if (alloc) {
-               *buffer = kmalloc(size, GFP_ATOMIC);
-               if (*buffer) {
-                       struct vfs_cap_data *cap = *buffer;
-                       __le32 nsmagic, magic;
+               if (nscap) {
+                       /* v3 -> v2 conversion */
+                       cap = kzalloc(size, GFP_ATOMIC);
+                       if (!cap) {
+                               size = -ENOMEM;
+                               goto out_free;
+                       }
                        magic = VFS_CAP_REVISION_2;
                        nsmagic = le32_to_cpu(nscap->magic_etc);
                        if (nsmagic & VFS_CAP_FLAGS_EFFECTIVE)
@@ -443,9 +459,12 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
                        memcpy(&cap->data, &nscap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
                        cap->magic_etc = cpu_to_le32(magic);
                } else {
-                       size = -ENOMEM;
+                       /* use unconverted v2 */
+                       tmpbuf = NULL;
                }
+               *buffer = cap;
        }
+out_free:
        kfree(tmpbuf);
        return size;
 }
@@ -473,7 +492,7 @@ static bool validheader(size_t size, const struct vfs_cap_data *cap)
  *
  * If all is ok, we return the new size, on error return < 0.
  */
-int cap_convert_nscap(struct dentry *dentry, void **ivalue, size_t size)
+int cap_convert_nscap(struct dentry *dentry, const void **ivalue, size_t size)
 {
        struct vfs_ns_cap_data *nscap;
        uid_t nsrootid;
@@ -516,7 +535,6 @@ int cap_convert_nscap(struct dentry *dentry, void **ivalue, size_t size)
        nscap->magic_etc = cpu_to_le32(nsmagic);
        memcpy(&nscap->data, &cap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
 
-       kvfree(*ivalue);
        *ivalue = nscap;
        return newsize;
 }
@@ -647,7 +665,8 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
  * its xattrs and, if present, apply them to the proposed credentials being
  * constructed by execve().
  */
-static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_fcap)
+static int get_file_caps(struct linux_binprm *bprm, struct file *file,
+                        bool *effective, bool *has_fcap)
 {
        int rc = 0;
        struct cpu_vfs_cap_data vcaps;
@@ -657,7 +676,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_f
        if (!file_caps_enabled)
                return 0;
 
-       if (!mnt_may_suid(bprm->file->f_path.mnt))
+       if (!mnt_may_suid(file->f_path.mnt))
                return 0;
 
        /*
@@ -665,10 +684,10 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_f
         * explicit that capability bits are limited to s_user_ns and its
         * descendants.
         */
-       if (!current_in_userns(bprm->file->f_path.mnt->mnt_sb->s_user_ns))
+       if (!current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns))
                return 0;
 
-       rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps);
+       rc = get_vfs_caps_from_disk(file->f_path.dentry, &vcaps);
        if (rc < 0) {
                if (rc == -EINVAL)
                        printk(KERN_NOTICE "Invalid argument reading file caps for %s\n",
@@ -797,26 +816,27 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old,
 }
 
 /**
- * cap_bprm_set_creds - Set up the proposed credentials for execve().
+ * cap_bprm_creds_from_file - Set up the proposed credentials for execve().
  * @bprm: The execution parameters, including the proposed creds
+ * @file: The file to pull the credentials from
  *
  * Set up the proposed credentials for a new execution context being
  * constructed by execve().  The proposed creds in @bprm->cred is altered,
  * which won't take effect immediately.  Returns 0 if successful, -ve on error.
  */
-int cap_bprm_set_creds(struct linux_binprm *bprm)
+int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file)
 {
+       /* Process setpcap binaries and capabilities for uid 0 */
        const struct cred *old = current_cred();
        struct cred *new = bprm->cred;
        bool effective = false, has_fcap = false, is_setid;
        int ret;
        kuid_t root_uid;
 
-       new->cap_ambient = old->cap_ambient;
        if (WARN_ON(!cap_ambient_invariant_ok(old)))
                return -EPERM;
 
-       ret = get_file_caps(bprm, &effective, &has_fcap);
+       ret = get_file_caps(bprm, file, &effective, &has_fcap);
        if (ret < 0)
                return ret;
 
@@ -885,12 +905,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
                return -EPERM;
 
        /* Check for privilege-elevated exec. */
-       bprm->cap_elevated = 0;
        if (is_setid ||
            (!__is_real(root_uid, new) &&
             (effective ||
              __cap_grew(permitted, ambient, new))))
-               bprm->cap_elevated = 1;
+               bprm->secureexec = 1;
 
        return 0;
 }
@@ -1347,7 +1366,7 @@ static struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme),
        LSM_HOOK_INIT(capget, cap_capget),
        LSM_HOOK_INIT(capset, cap_capset),
-       LSM_HOOK_INIT(bprm_set_creds, cap_bprm_set_creds),
+       LSM_HOOK_INIT(bprm_creds_from_file, cap_bprm_creds_from_file),
        LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
        LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
        LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity),