]> Pileus Git - ~andy/linux/blobdiff - fs/exec.c
Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[~andy/linux] / fs / exec.c
index b4e5b8a9216a29b4531a701b3d76247b6ee5471f..02d2e120542d76ca26ee233e638749e53bc227f0 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -55,6 +55,7 @@
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
 #include <asm/tlb.h>
+#include "internal.h"
 
 #ifdef __alpha__
 /* for /sbin/loader handling in search_binary_handler() */
@@ -978,7 +979,7 @@ int flush_old_exec(struct linux_binprm * bprm)
        /* This is the point of no return */
        current->sas_ss_sp = current->sas_ss_size = 0;
 
-       if (current->euid == current->uid && current->egid == current->gid)
+       if (current_euid() == current_uid() && current_egid() == current_gid())
                set_dumpable(current->mm, 1);
        else
                set_dumpable(current->mm, suid_dumpable);
@@ -1005,16 +1006,17 @@ int flush_old_exec(struct linux_binprm * bprm)
         */
        current->mm->task_size = TASK_SIZE;
 
-       if (bprm->e_uid != current->euid || bprm->e_gid != current->egid) {
-               suid_keys(current);
-               set_dumpable(current->mm, suid_dumpable);
+       /* install the new credentials */
+       if (bprm->cred->uid != current_euid() ||
+           bprm->cred->gid != current_egid()) {
                current->pdeath_signal = 0;
        } else if (file_permission(bprm->file, MAY_READ) ||
-                       (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
-               suid_keys(current);
+                  bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) {
                set_dumpable(current->mm, suid_dumpable);
        }
 
+       current->personality &= ~bprm->per_clear;
+
        /* An exec changes our domain. We are no longer part of the thread
           group */
 
@@ -1031,13 +1033,50 @@ out:
 
 EXPORT_SYMBOL(flush_old_exec);
 
+/*
+ * install the new credentials for this executable
+ */
+void install_exec_creds(struct linux_binprm *bprm)
+{
+       security_bprm_committing_creds(bprm);
+
+       commit_creds(bprm->cred);
+       bprm->cred = NULL;
+
+       /* cred_exec_mutex must be held at least to this point to prevent
+        * ptrace_attach() from altering our determination of the task's
+        * credentials; any time after this it may be unlocked */
+
+       security_bprm_committed_creds(bprm);
+}
+EXPORT_SYMBOL(install_exec_creds);
+
+/*
+ * determine how safe it is to execute the proposed program
+ * - the caller must hold current->cred_exec_mutex to protect against
+ *   PTRACE_ATTACH
+ */
+void check_unsafe_exec(struct linux_binprm *bprm)
+{
+       struct task_struct *p = current;
+
+       bprm->unsafe = tracehook_unsafe_exec(p);
+
+       if (atomic_read(&p->fs->count) > 1 ||
+           atomic_read(&p->files->count) > 1 ||
+           atomic_read(&p->sighand->count) > 1)
+               bprm->unsafe |= LSM_UNSAFE_SHARE;
+}
+
 /* 
  * Fill the binprm structure from the inode. 
  * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
+ *
+ * This may be called multiple times for binary chains (scripts for example).
  */
 int prepare_binprm(struct linux_binprm *bprm)
 {
-       int mode;
+       umode_t mode;
        struct inode * inode = bprm->file->f_path.dentry->d_inode;
        int retval;
 
@@ -1045,14 +1084,15 @@ int prepare_binprm(struct linux_binprm *bprm)
        if (bprm->file->f_op == NULL)
                return -EACCES;
 
-       bprm->e_uid = current->euid;
-       bprm->e_gid = current->egid;
+       /* clear any previous set[ug]id data from a previous binary */
+       bprm->cred->euid = current_euid();
+       bprm->cred->egid = current_egid();
 
-       if(!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
+       if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
                /* Set-uid? */
                if (mode & S_ISUID) {
-                       current->personality &= ~PER_CLEAR_ON_SETID;
-                       bprm->e_uid = inode->i_uid;
+                       bprm->per_clear |= PER_CLEAR_ON_SETID;
+                       bprm->cred->euid = inode->i_uid;
                }
 
                /* Set-gid? */
@@ -1062,52 +1102,23 @@ int prepare_binprm(struct linux_binprm *bprm)
                 * executable.
                 */
                if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
-                       current->personality &= ~PER_CLEAR_ON_SETID;
-                       bprm->e_gid = inode->i_gid;
+                       bprm->per_clear |= PER_CLEAR_ON_SETID;
+                       bprm->cred->egid = inode->i_gid;
                }
        }
 
        /* fill in binprm security blob */
-       retval = security_bprm_set(bprm);
+       retval = security_bprm_set_creds(bprm);
        if (retval)
                return retval;
+       bprm->cred_prepared = 1;
 
-       memset(bprm->buf,0,BINPRM_BUF_SIZE);
-       return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
+       memset(bprm->buf, 0, BINPRM_BUF_SIZE);
+       return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
 }
 
 EXPORT_SYMBOL(prepare_binprm);
 
-static int unsafe_exec(struct task_struct *p)
-{
-       int unsafe = tracehook_unsafe_exec(p);
-
-       if (atomic_read(&p->fs->count) > 1 ||
-           atomic_read(&p->files->count) > 1 ||
-           atomic_read(&p->sighand->count) > 1)
-               unsafe |= LSM_UNSAFE_SHARE;
-
-       return unsafe;
-}
-
-void compute_creds(struct linux_binprm *bprm)
-{
-       int unsafe;
-
-       if (bprm->e_uid != current->uid) {
-               suid_keys(current);
-               current->pdeath_signal = 0;
-       }
-       exec_keys(current);
-
-       task_lock(current);
-       unsafe = unsafe_exec(current);
-       security_bprm_apply_creds(bprm, unsafe);
-       task_unlock(current);
-       security_bprm_post_apply_creds(bprm);
-}
-EXPORT_SYMBOL(compute_creds);
-
 /*
  * Arguments are '\0' separated strings found at the location bprm->p
  * points to; chop off the first by relocating brpm->p to right after
@@ -1268,6 +1279,8 @@ EXPORT_SYMBOL(search_binary_handler);
 void free_bprm(struct linux_binprm *bprm)
 {
        free_arg_pages(bprm);
+       if (bprm->cred)
+               abort_creds(bprm->cred);
        kfree(bprm);
 }
 
@@ -1293,10 +1306,20 @@ int do_execve(char * filename,
        if (!bprm)
                goto out_files;
 
+       retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+       if (retval < 0)
+               goto out_free;
+
+       retval = -ENOMEM;
+       bprm->cred = prepare_exec_creds();
+       if (!bprm->cred)
+               goto out_unlock;
+       check_unsafe_exec(bprm);
+
        file = open_exec(filename);
        retval = PTR_ERR(file);
        if (IS_ERR(file))
-               goto out_kfree;
+               goto out_unlock;
 
        sched_exec();
 
@@ -1310,14 +1333,10 @@ int do_execve(char * filename,
 
        bprm->argc = count(argv, MAX_ARG_STRINGS);
        if ((retval = bprm->argc) < 0)
-               goto out_mm;
+               goto out;
 
        bprm->envc = count(envp, MAX_ARG_STRINGS);
        if ((retval = bprm->envc) < 0)
-               goto out_mm;
-
-       retval = security_bprm_alloc(bprm);
-       if (retval)
                goto out;
 
        retval = prepare_binprm(bprm);
@@ -1339,21 +1358,18 @@ int do_execve(char * filename,
 
        current->flags &= ~PF_KTHREAD;
        retval = search_binary_handler(bprm,regs);
-       if (retval >= 0) {
-               /* execve success */
-               security_bprm_free(bprm);
-               acct_update_integrals(current);
-               free_bprm(bprm);
-               if (displaced)
-                       put_files_struct(displaced);
-               return retval;
-       }
+       if (retval < 0)
+               goto out;
 
-out:
-       if (bprm->security)
-               security_bprm_free(bprm);
+       /* execve succeeded */
+       mutex_unlock(&current->cred_exec_mutex);
+       acct_update_integrals(current);
+       free_bprm(bprm);
+       if (displaced)
+               put_files_struct(displaced);
+       return retval;
 
-out_mm:
+out:
        if (bprm->mm)
                mmput (bprm->mm);
 
@@ -1362,7 +1378,11 @@ out_file:
                allow_write_access(bprm->file);
                fput(bprm->file);
        }
-out_kfree:
+
+out_unlock:
+       mutex_unlock(&current->cred_exec_mutex);
+
+out_free:
        free_bprm(bprm);
 
 out_files:
@@ -1394,6 +1414,7 @@ EXPORT_SYMBOL(set_binfmt);
  */
 static int format_corename(char *corename, long signr)
 {
+       const struct cred *cred = current_cred();
        const char *pat_ptr = core_pattern;
        int ispipe = (*pat_ptr == '|');
        char *out_ptr = corename;
@@ -1430,7 +1451,7 @@ static int format_corename(char *corename, long signr)
                        /* uid */
                        case 'u':
                                rc = snprintf(out_ptr, out_end - out_ptr,
-                                             "%d", current->uid);
+                                             "%d", cred->uid);
                                if (rc > out_end - out_ptr)
                                        goto out;
                                out_ptr += rc;
@@ -1438,7 +1459,7 @@ static int format_corename(char *corename, long signr)
                        /* gid */
                        case 'g':
                                rc = snprintf(out_ptr, out_end - out_ptr,
-                                             "%d", current->gid);
+                                             "%d", cred->gid);
                                if (rc > out_end - out_ptr)
                                        goto out;
                                out_ptr += rc;
@@ -1714,8 +1735,9 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
        struct linux_binfmt * binfmt;
        struct inode * inode;
        struct file * file;
+       const struct cred *old_cred;
+       struct cred *cred;
        int retval = 0;
-       int fsuid = current->fsuid;
        int flag = 0;
        int ispipe = 0;
        unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
@@ -1728,12 +1750,20 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
        binfmt = current->binfmt;
        if (!binfmt || !binfmt->core_dump)
                goto fail;
+
+       cred = prepare_creds();
+       if (!cred) {
+               retval = -ENOMEM;
+               goto fail;
+       }
+
        down_write(&mm->mmap_sem);
        /*
         * If another thread got here first, or we are not dumpable, bail out.
         */
        if (mm->core_state || !get_dumpable(mm)) {
                up_write(&mm->mmap_sem);
+               put_cred(cred);
                goto fail;
        }
 
@@ -1744,12 +1774,16 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
         */
        if (get_dumpable(mm) == 2) {    /* Setuid core dump mode */
                flag = O_EXCL;          /* Stop rewrite attacks */
-               current->fsuid = 0;     /* Dump root private */
+               cred->fsuid = 0;        /* Dump root private */
        }
 
        retval = coredump_wait(exit_code, &core_state);
-       if (retval < 0)
+       if (retval < 0) {
+               put_cred(cred);
                goto fail;
+       }
+
+       old_cred = override_creds(cred);
 
        /*
         * Clear any false indication of pending signals that might
@@ -1821,7 +1855,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
         * Dont allow local users get cute and trick others to coredump
         * into their pre-created files:
         */
-       if (inode->i_uid != current->fsuid)
+       if (inode->i_uid != current_fsuid())
                goto close_fail;
        if (!file->f_op)
                goto close_fail;
@@ -1840,7 +1874,8 @@ fail_unlock:
        if (helper_argv)
                argv_free(helper_argv);
 
-       current->fsuid = fsuid;
+       revert_creds(old_cred);
+       put_cred(cred);
        coredump_finish(mm);
 fail:
        return retval;