rk: Makefile set default CROSS_COMPILE for arm
[firefly-linux-kernel-4.4.55.git] / security / commoncap.c
index 0405522995c5742c29d7c792bd9c17cefd40b795..7fa251aea32f88d3bd50be9d06b890991d1eec9e 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
 #include <linux/file.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
@@ -57,11 +57,6 @@ static void warn_setuid_and_fcaps_mixed(const char *fname)
        }
 }
 
-int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
-{
-       return 0;
-}
-
 /**
  * cap_capable - Determine whether a task has a particular effective capability
  * @cred: The credentials to use
@@ -153,12 +148,17 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
 {
        int ret = 0;
        const struct cred *cred, *child_cred;
+       const kernel_cap_t *caller_caps;
 
        rcu_read_lock();
        cred = current_cred();
        child_cred = __task_cred(child);
+       if (mode & PTRACE_MODE_FSCREDS)
+               caller_caps = &cred->cap_effective;
+       else
+               caller_caps = &cred->cap_permitted;
        if (cred->user_ns == child_cred->user_ns &&
-           cap_issubset(child_cred->cap_permitted, cred->cap_permitted))
+           cap_issubset(child_cred->cap_permitted, *caller_caps))
                goto out;
        if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE))
                goto out;
@@ -283,6 +283,16 @@ int cap_capset(struct cred *new,
        new->cap_effective   = *effective;
        new->cap_inheritable = *inheritable;
        new->cap_permitted   = *permitted;
+
+       /*
+        * Mask off ambient bits that are no longer both permitted and
+        * inheritable.
+        */
+       new->cap_ambient = cap_intersect(new->cap_ambient,
+                                        cap_intersect(*permitted,
+                                                      *inheritable));
+       if (WARN_ON(!cap_ambient_invariant_ok(new)))
+               return -EINVAL;
        return 0;
 }
 
@@ -308,7 +318,7 @@ static inline void bprm_clear_caps(struct linux_binprm *bprm)
  */
 int cap_inode_need_killpriv(struct dentry *dentry)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = d_backing_inode(dentry);
        int error;
 
        if (!inode->i_op->getxattr)
@@ -330,7 +340,7 @@ int cap_inode_need_killpriv(struct dentry *dentry)
  */
 int cap_inode_killpriv(struct dentry *dentry)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = d_backing_inode(dentry);
 
        if (!inode->i_op->removexattr)
               return 0;
@@ -363,6 +373,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
 
                /*
                 * pP' = (X & fP) | (pI & fI)
+                * The addition of pA' is handled later.
                 */
                new->cap_permitted.cap[i] =
                        (new->cap_bset.cap[i] & permitted) |
@@ -386,7 +397,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
  */
 int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = d_backing_inode(dentry);
        __u32 magic_etc;
        unsigned tocopy, i;
        int size;
@@ -445,7 +456,6 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
  */
 static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap)
 {
-       struct dentry *dentry;
        int rc = 0;
        struct cpu_vfs_cap_data vcaps;
 
@@ -457,9 +467,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c
        if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
                return 0;
 
-       dentry = dget(bprm->file->f_dentry);
-
-       rc = get_vfs_caps_from_disk(dentry, &vcaps);
+       rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps);
        if (rc < 0) {
                if (rc == -EINVAL)
                        printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n",
@@ -475,7 +483,6 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c
                       __func__, rc, bprm->filename);
 
 out:
-       dput(dentry);
        if (rc)
                bprm_clear_caps(bprm);
 
@@ -494,10 +501,13 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
 {
        const struct cred *old = current_cred();
        struct cred *new = bprm->cred;
-       bool effective, has_cap = false;
+       bool effective, has_cap = false, is_setid;
        int ret;
        kuid_t root_uid;
 
+       if (WARN_ON(!cap_ambient_invariant_ok(old)))
+               return -EPERM;
+
        effective = false;
        ret = get_file_caps(bprm, &effective, &has_cap);
        if (ret < 0)
@@ -542,8 +552,9 @@ skip:
         *
         * In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
         */
-       if ((!uid_eq(new->euid, old->uid) ||
-            !gid_eq(new->egid, old->gid) ||
+       is_setid = !uid_eq(new->euid, old->uid) || !gid_eq(new->egid, old->gid);
+
+       if ((is_setid ||
             !cap_issubset(new->cap_permitted, old->cap_permitted)) &&
            bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
                /* downgrade; they get no more than they had, and maybe less */
@@ -559,10 +570,28 @@ skip:
        new->suid = new->fsuid = new->euid;
        new->sgid = new->fsgid = new->egid;
 
+       /* File caps or setid cancels ambient. */
+       if (has_cap || is_setid)
+               cap_clear(new->cap_ambient);
+
+       /*
+        * Now that we've computed pA', update pP' to give:
+        *   pP' = (X & fP) | (pI & fI) | pA'
+        */
+       new->cap_permitted = cap_combine(new->cap_permitted, new->cap_ambient);
+
+       /*
+        * Set pE' = (fE ? pP' : pA').  Because pA' is zero if fE is set,
+        * this is the same as pE' = (fE ? pP' : 0) | pA'.
+        */
        if (effective)
                new->cap_effective = new->cap_permitted;
        else
-               cap_clear(new->cap_effective);
+               new->cap_effective = new->cap_ambient;
+
+       if (WARN_ON(!cap_ambient_invariant_ok(new)))
+               return -EPERM;
+
        bprm->cap_effective = effective;
 
        /*
@@ -577,7 +606,7 @@ skip:
         * Number 1 above might fail if you don't have a full bset, but I think
         * that is interesting information to audit.
         */
-       if (!cap_isclear(new->cap_effective)) {
+       if (!cap_issubset(new->cap_effective, new->cap_ambient)) {
                if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
                    !uid_eq(new->euid, root_uid) || !uid_eq(new->uid, root_uid) ||
                    issecure(SECURE_NOROOT)) {
@@ -588,6 +617,10 @@ skip:
        }
 
        new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+
+       if (WARN_ON(!cap_ambient_invariant_ok(new)))
+               return -EPERM;
+
        return 0;
 }
 
@@ -609,7 +642,7 @@ int cap_bprm_secureexec(struct linux_binprm *bprm)
        if (!uid_eq(cred->uid, root_uid)) {
                if (bprm->cap_effective)
                        return 1;
-               if (!cap_isclear(cred->cap_permitted))
+               if (!cap_issubset(cred->cap_permitted, cred->cap_ambient))
                        return 1;
        }
 
@@ -711,10 +744,18 @@ static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old)
             uid_eq(old->suid, root_uid)) &&
            (!uid_eq(new->uid, root_uid) &&
             !uid_eq(new->euid, root_uid) &&
-            !uid_eq(new->suid, root_uid)) &&
-           !issecure(SECURE_KEEP_CAPS)) {
-               cap_clear(new->cap_permitted);
-               cap_clear(new->cap_effective);
+            !uid_eq(new->suid, root_uid))) {
+               if (!issecure(SECURE_KEEP_CAPS)) {
+                       cap_clear(new->cap_permitted);
+                       cap_clear(new->cap_effective);
+               }
+
+               /*
+                * Pre-ambient programs expect setresuid to nonroot followed
+                * by exec to drop capabilities.  We should make sure that
+                * this remains the case.
+                */
+               cap_clear(new->cap_ambient);
        }
        if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid))
                cap_clear(new->cap_effective);
@@ -782,16 +823,16 @@ int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags)
  */
 static int cap_safe_nice(struct task_struct *p)
 {
-       int is_subset;
+       int is_subset, ret = 0;
 
        rcu_read_lock();
        is_subset = cap_issubset(__task_cred(p)->cap_permitted,
                                 current_cred()->cap_permitted);
+       if (!is_subset && !ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE))
+               ret = -EPERM;
        rcu_read_unlock();
 
-       if (!is_subset && !capable(CAP_SYS_NICE))
-               return -EPERM;
-       return 0;
+       return ret;
 }
 
 /**
@@ -836,15 +877,20 @@ int cap_task_setnice(struct task_struct *p, int nice)
  * Implement PR_CAPBSET_DROP.  Attempt to remove the specified capability from
  * the current task's bounding set.  Returns 0 on success, -ve on error.
  */
-static long cap_prctl_drop(struct cred *new, unsigned long cap)
+static int cap_prctl_drop(unsigned long cap)
 {
-       if (!capable(CAP_SETPCAP))
+       struct cred *new;
+
+       if (!ns_capable(current_user_ns(), CAP_SETPCAP))
                return -EPERM;
        if (!cap_valid(cap))
                return -EINVAL;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
        cap_lower(new->cap_bset, cap);
-       return 0;
+       return commit_creds(new);
 }
 
 /**
@@ -862,26 +908,17 @@ static long cap_prctl_drop(struct cred *new, unsigned long cap)
 int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                   unsigned long arg4, unsigned long arg5)
 {
+       const struct cred *old = current_cred();
        struct cred *new;
-       long error = 0;
-
-       new = prepare_creds();
-       if (!new)
-               return -ENOMEM;
 
        switch (option) {
        case PR_CAPBSET_READ:
-               error = -EINVAL;
                if (!cap_valid(arg2))
-                       goto error;
-               error = !!cap_raised(new->cap_bset, arg2);
-               goto no_change;
+                       return -EINVAL;
+               return !!cap_raised(old->cap_bset, arg2);
 
        case PR_CAPBSET_DROP:
-               error = cap_prctl_drop(new, arg2);
-               if (error < 0)
-                       goto error;
-               goto changed;
+               return cap_prctl_drop(arg2);
 
        /*
         * The next four prctl's remain to assist with transitioning a
@@ -903,10 +940,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
         * capability-based-privilege environment.
         */
        case PR_SET_SECUREBITS:
-               error = -EPERM;
-               if ((((new->securebits & SECURE_ALL_LOCKS) >> 1)
-                    & (new->securebits ^ arg2))                        /*[1]*/
-                   || ((new->securebits & SECURE_ALL_LOCKS & ~arg2))   /*[2]*/
+               if ((((old->securebits & SECURE_ALL_LOCKS) >> 1)
+                    & (old->securebits ^ arg2))                        /*[1]*/
+                   || ((old->securebits & SECURE_ALL_LOCKS & ~arg2))   /*[2]*/
                    || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))   /*[3]*/
                    || (cap_capable(current_cred(),
                                    current_cred()->user_ns, CAP_SETPCAP,
@@ -920,46 +956,77 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                         */
                    )
                        /* cannot change a locked bit */
-                       goto error;
+                       return -EPERM;
+
+               new = prepare_creds();
+               if (!new)
+                       return -ENOMEM;
                new->securebits = arg2;
-               goto changed;
+               return commit_creds(new);
 
        case PR_GET_SECUREBITS:
-               error = new->securebits;
-               goto no_change;
+               return old->securebits;
 
        case PR_GET_KEEPCAPS:
-               if (issecure(SECURE_KEEP_CAPS))
-                       error = 1;
-               goto no_change;
+               return !!issecure(SECURE_KEEP_CAPS);
 
        case PR_SET_KEEPCAPS:
-               error = -EINVAL;
                if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
-                       goto error;
-               error = -EPERM;
+                       return -EINVAL;
                if (issecure(SECURE_KEEP_CAPS_LOCKED))
-                       goto error;
+                       return -EPERM;
+
+               new = prepare_creds();
+               if (!new)
+                       return -ENOMEM;
                if (arg2)
                        new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
                else
                        new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
-               goto changed;
+               return commit_creds(new);
+
+       case PR_CAP_AMBIENT:
+               if (arg2 == PR_CAP_AMBIENT_CLEAR_ALL) {
+                       if (arg3 | arg4 | arg5)
+                               return -EINVAL;
+
+                       new = prepare_creds();
+                       if (!new)
+                               return -ENOMEM;
+                       cap_clear(new->cap_ambient);
+                       return commit_creds(new);
+               }
+
+               if (((!cap_valid(arg3)) | arg4 | arg5))
+                       return -EINVAL;
+
+               if (arg2 == PR_CAP_AMBIENT_IS_SET) {
+                       return !!cap_raised(current_cred()->cap_ambient, arg3);
+               } else if (arg2 != PR_CAP_AMBIENT_RAISE &&
+                          arg2 != PR_CAP_AMBIENT_LOWER) {
+                       return -EINVAL;
+               } else {
+                       if (arg2 == PR_CAP_AMBIENT_RAISE &&
+                           (!cap_raised(current_cred()->cap_permitted, arg3) ||
+                            !cap_raised(current_cred()->cap_inheritable,
+                                        arg3) ||
+                            issecure(SECURE_NO_CAP_AMBIENT_RAISE)))
+                               return -EPERM;
+
+                       new = prepare_creds();
+                       if (!new)
+                               return -ENOMEM;
+                       if (arg2 == PR_CAP_AMBIENT_RAISE)
+                               cap_raise(new->cap_ambient, arg3);
+                       else
+                               cap_lower(new->cap_ambient, arg3);
+                       return commit_creds(new);
+               }
 
        default:
                /* No functionality available - continue with default */
-               error = -ENOSYS;
-               goto error;
+               return -ENOSYS;
        }
-
-       /* Functionality provided */
-changed:
-       return commit_creds(new);
-
-no_change:
-error:
-       abort_creds(new);
-       return error;
 }
 
 /**
@@ -968,7 +1035,7 @@ error:
  * @pages: The size of the mapping
  *
  * Determine whether the allocation of a new virtual mapping by the current
- * task is permitted, returning 0 if permission is granted, -ve if not.
+ * task is permitted, returning 1 if permission is granted, 0 if not.
  */
 int cap_vm_enough_memory(struct mm_struct *mm, long pages)
 {
@@ -977,7 +1044,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
        if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
                        SECURITY_CAP_NOAUDIT) == 0)
                cap_sys_admin = 1;
-       return __vm_enough_memory(mm, pages, cap_sys_admin);
+       return cap_sys_admin;
 }
 
 /*
@@ -1008,3 +1075,33 @@ int cap_mmap_file(struct file *file, unsigned long reqprot,
 {
        return 0;
 }
+
+#ifdef CONFIG_SECURITY
+
+struct security_hook_list capability_hooks[] = {
+       LSM_HOOK_INIT(capable, cap_capable),
+       LSM_HOOK_INIT(settime, cap_settime),
+       LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
+       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_secureexec, cap_bprm_secureexec),
+       LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
+       LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
+       LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
+       LSM_HOOK_INIT(mmap_file, cap_mmap_file),
+       LSM_HOOK_INIT(task_fix_setuid, cap_task_fix_setuid),
+       LSM_HOOK_INIT(task_prctl, cap_task_prctl),
+       LSM_HOOK_INIT(task_setscheduler, cap_task_setscheduler),
+       LSM_HOOK_INIT(task_setioprio, cap_task_setioprio),
+       LSM_HOOK_INIT(task_setnice, cap_task_setnice),
+       LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory),
+};
+
+void __init capability_add_hooks(void)
+{
+       security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
+}
+
+#endif /* CONFIG_SECURITY */