]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - security/commoncap.c
CRED: Make execve() take advantage of copy-on-write credentials
[linux-2.6-omap-h63xx.git] / security / commoncap.c
index b5419273f92d89f2597fdb08d72eb26266a89497..51dfa11e8e56348d8c88ead0612df718e225a621 100644 (file)
@@ -167,7 +167,7 @@ int cap_capset(struct cred *new,
 
 static inline void bprm_clear_caps(struct linux_binprm *bprm)
 {
-       cap_clear(bprm->cap_post_exec_permitted);
+       cap_clear(bprm->cred->cap_permitted);
        bprm->cap_effective = false;
 }
 
@@ -198,15 +198,15 @@ int cap_inode_killpriv(struct dentry *dentry)
 }
 
 static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
-                                         struct linux_binprm *bprm)
+                                         struct linux_binprm *bprm,
+                                         bool *effective)
 {
+       struct cred *new = bprm->cred;
        unsigned i;
        int ret = 0;
 
        if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
-               bprm->cap_effective = true;
-       else
-               bprm->cap_effective = false;
+               *effective = true;
 
        CAP_FOR_EACH_U32(i) {
                __u32 permitted = caps->permitted.cap[i];
@@ -215,16 +215,13 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
                /*
                 * pP' = (X & fP) | (pI & fI)
                 */
-               bprm->cap_post_exec_permitted.cap[i] =
-                       (current->cred->cap_bset.cap[i] & permitted) |
-                       (current->cred->cap_inheritable.cap[i] & inheritable);
+               new->cap_permitted.cap[i] =
+                       (new->cap_bset.cap[i] & permitted) |
+                       (new->cap_inheritable.cap[i] & inheritable);
 
-               if (permitted & ~bprm->cap_post_exec_permitted.cap[i]) {
-                       /*
-                        * insufficient to execute correctly
-                        */
+               if (permitted & ~new->cap_permitted.cap[i])
+                       /* insufficient to execute correctly */
                        ret = -EPERM;
-               }
        }
 
        /*
@@ -232,7 +229,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
         * do not have enough capabilities, we return an error if they are
         * missing some "forced" (aka file-permitted) capabilities.
         */
-       return bprm->cap_effective ? ret : 0;
+       return *effective ? ret : 0;
 }
 
 int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
@@ -250,10 +247,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
 
        size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps,
                                   XATTR_CAPS_SZ);
-       if (size == -ENODATA || size == -EOPNOTSUPP) {
+       if (size == -ENODATA || size == -EOPNOTSUPP)
                /* no data, that's ok */
                return -ENODATA;
-       }
        if (size < 0)
                return size;
 
@@ -262,7 +258,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
 
        cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc);
 
-       switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
+       switch (magic_etc & VFS_CAP_REVISION_MASK) {
        case VFS_CAP_REVISION_1:
                if (size != XATTR_CAPS_SZ_1)
                        return -EINVAL;
@@ -283,11 +279,12 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
                cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted);
                cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);
        }
+
        return 0;
 }
 
 /* Locate any VFS capabilities: */
-static int get_file_caps(struct linux_binprm *bprm)
+static int get_file_caps(struct linux_binprm *bprm, bool *effective)
 {
        struct dentry *dentry;
        int rc = 0;
@@ -313,7 +310,10 @@ static int get_file_caps(struct linux_binprm *bprm)
                goto out;
        }
 
-       rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
+       rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective);
+       if (rc == -EINVAL)
+               printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
+                      __func__, rc, bprm->filename);
 
 out:
        dput(dentry);
@@ -334,18 +334,27 @@ int cap_inode_killpriv(struct dentry *dentry)
        return 0;
 }
 
-static inline int get_file_caps(struct linux_binprm *bprm)
+static inline int get_file_caps(struct linux_binprm *bprm, bool *effective)
 {
        bprm_clear_caps(bprm);
        return 0;
 }
 #endif
 
-int cap_bprm_set_security (struct linux_binprm *bprm)
+/*
+ * set up the new credentials for an exec'd task
+ */
+int cap_bprm_set_creds(struct linux_binprm *bprm)
 {
+       const struct cred *old = current_cred();
+       struct cred *new = bprm->cred;
+       bool effective;
        int ret;
 
-       ret = get_file_caps(bprm);
+       effective = false;
+       ret = get_file_caps(bprm, &effective);
+       if (ret < 0)
+               return ret;
 
        if (!issecure(SECURE_NOROOT)) {
                /*
@@ -353,63 +362,47 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
                 * executables under compatibility mode, we override the
                 * capability sets for the file.
                 *
-                * If only the real uid is 0, we do not set the effective
-                * bit.
+                * If only the real uid is 0, we do not set the effective bit.
                 */
-               if (bprm->e_uid == 0 || current_uid() == 0) {
+               if (new->euid == 0 || new->uid == 0) {
                        /* pP' = (cap_bset & ~0) | (pI & ~0) */
-                       bprm->cap_post_exec_permitted = cap_combine(
-                               current->cred->cap_bset,
-                               current->cred->cap_inheritable);
-                       bprm->cap_effective = (bprm->e_uid == 0);
-                       ret = 0;
+                       new->cap_permitted = cap_combine(old->cap_bset,
+                                                        old->cap_inheritable);
                }
+               if (new->euid == 0)
+                       effective = true;
        }
 
-       return ret;
-}
-
-int cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
-{
-       const struct cred *old = current_cred();
-       struct cred *new;
-
-       new = prepare_creds();
-       if (!new)
-               return -ENOMEM;
-
-       if (bprm->e_uid != old->uid || bprm->e_gid != old->gid ||
-           !cap_issubset(bprm->cap_post_exec_permitted,
-                         old->cap_permitted)) {
-               set_dumpable(current->mm, suid_dumpable);
-               current->pdeath_signal = 0;
-
-               if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
-                       if (!capable(CAP_SETUID)) {
-                               bprm->e_uid = old->uid;
-                               bprm->e_gid = old->gid;
-                       }
-                       if (cap_limit_ptraced_target()) {
-                               bprm->cap_post_exec_permitted = cap_intersect(
-                                       bprm->cap_post_exec_permitted,
-                                       new->cap_permitted);
-                       }
+       /* Don't let someone trace a set[ug]id/setpcap binary with the revised
+        * credentials unless they have the appropriate permit
+        */
+       if ((new->euid != old->uid ||
+            new->egid != old->gid ||
+            !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 */
+               if (!capable(CAP_SETUID)) {
+                       new->euid = new->uid;
+                       new->egid = new->gid;
                }
+               if (cap_limit_ptraced_target())
+                       new->cap_permitted = cap_intersect(new->cap_permitted,
+                                                          old->cap_permitted);
        }
 
-       new->suid = new->euid = new->fsuid = bprm->e_uid;
-       new->sgid = new->egid = new->fsgid = bprm->e_gid;
+       new->suid = new->fsuid = new->euid;
+       new->sgid = new->fsgid = new->egid;
 
-       /* For init, we want to retain the capabilities set
-        * in the init_task struct. Thus we skip the usual
-        * capability rules */
+       /* For init, we want to retain the capabilities set in the initial
+        * task.  Thus we skip the usual capability rules
+        */
        if (!is_global_init(current)) {
-               new->cap_permitted = bprm->cap_post_exec_permitted;
-               if (bprm->cap_effective)
-                       new->cap_effective = bprm->cap_post_exec_permitted;
+               if (effective)
+                       new->cap_effective = new->cap_permitted;
                else
                        cap_clear(new->cap_effective);
        }
+       bprm->cap_effective = effective;
 
        /*
         * Audit candidate if current->cap_effective is set
@@ -425,23 +418,31 @@ int cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
         */
        if (!cap_isclear(new->cap_effective)) {
                if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
-                   bprm->e_uid != 0 || new->uid != 0 ||
-                   issecure(SECURE_NOROOT))
-                       audit_log_bprm_fcaps(bprm, new, old);
+                   new->euid != 0 || new->uid != 0 ||
+                   issecure(SECURE_NOROOT)) {
+                       ret = audit_log_bprm_fcaps(bprm, new, old);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
-       return commit_creds(new);
+       return 0;
 }
 
-int cap_bprm_secureexec (struct linux_binprm *bprm)
+/*
+ * determine whether a secure execution is required
+ * - the creds have been committed at this point, and are no longer available
+ *   through bprm
+ */
+int cap_bprm_secureexec(struct linux_binprm *bprm)
 {
        const struct cred *cred = current_cred();
 
        if (cred->uid != 0) {
                if (bprm->cap_effective)
                        return 1;
-               if (!cap_isclear(bprm->cap_post_exec_permitted))
+               if (!cap_isclear(cred->cap_permitted))
                        return 1;
        }
 
@@ -477,7 +478,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
 }
 
 /* moved from kernel/sys.c. */
-/* 
+/*
  * cap_emulate_setxuid() fixes the effective / permitted capabilities of
  * a process after a call to setuid, setreuid, or setresuid.
  *
@@ -491,10 +492,10 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
  *  3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
  *  capabilities are set to the permitted capabilities.
  *
- *  fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should 
+ *  fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
  *  never happen.
  *
- *  -astor 
+ *  -astor
  *
  * cevans - New behaviour, Oct '99
  * A process may, via prctl(), elect to keep its capabilities when it
@@ -751,4 +752,3 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
                cap_sys_admin = 1;
        return __vm_enough_memory(mm, pages, cap_sys_admin);
 }
-