PATCH: setuid core dump take 2

From: Alan Cox (alan_at_redhat.com)
Date: 09/29/04

  • Next message: Alan Cox: "PATCH: 3c59x 00:00:00:00:00:00 MAC failure"
    Date:	Wed, 29 Sep 2004 12:27:28 -0400
    To: linux-kernel@vger.kernel.org
    
    

    This fixes the /proc problems that were pointed out in the original. I've
    left the values numeric since I think the code actually reads better in
    that form, but thats open for debate.

    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/Documentation/sysctl/kernel.txt linux-2.6.9rc2/Documentation/sysctl/kernel.txt
    --- linux.vanilla-2.6.9rc2/Documentation/sysctl/kernel.txt 2004-09-14 14:22:52.000000000 +0100
    +++ linux-2.6.9rc2/Documentation/sysctl/kernel.txt 2004-09-27 19:01:53.729513832 +0100
    @@ -49,6 +49,7 @@
     - shmmax [ sysv ipc ]
     - shmmni
     - stop-a [ SPARC only ]
    +- suid_dumpable
     - sysrq ==> Documentation/sysrq.txt
     - tainted
     - threads-max
    @@ -300,6 +301,25 @@
     
     ==============================================================
     
    +suid_dumpable:
    +
    +This value can be used to query and set the core dump mode for setuid
    +or otherwise protected/tainted binaries. The modes are
    +
    +0 - (default) - traditional behaviour. Any process which has changed
    + privilege levels or is execute only will not be dumped
    +1 - (debug) - all processes dump core when possible. The core dump is
    + owned by the current user and no security is applied. This is
    + intended for system debugging situations only.
    +2 - (suidsafe) - any binary which normally not be dumped is dumped
    + readable by root only. This allows the end user to remove
    + such a dump but not access it directly. For security reasons
    + core dumps in this mode will not overwrite one another or
    + other files. This mode is appropriate when adminstrators are
    + attempting to debug problems in a normal environment.
    +
    +==============================================================
    +
     tainted:
     
     Non-zero if the kernel has been tainted. Numeric values, which
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/fs/exec.c linux-2.6.9rc2/fs/exec.c
    --- linux.vanilla-2.6.9rc2/fs/exec.c 2004-09-14 14:22:54.000000000 +0100
    +++ linux-2.6.9rc2/fs/exec.c 2004-09-27 18:57:17.531502312 +0100
    @@ -56,6 +56,8 @@
     
     int core_uses_pid;
     char core_pattern[65] = "core";
    +int suid_dumpable = 0;
    +
     /* The maximal length of core_pattern is also specified in sysctl.c */
     
     static struct linux_binfmt *formats;
    @@ -843,6 +845,9 @@
     
             if (current->euid == current->uid && current->egid == current->gid)
                     current->mm->dumpable = 1;
    + else
    + current->mm->dumpable = suid_dumpable;
    +
             name = bprm->filename;
             for (i=0; (ch = *(name++)) != '\0';) {
                     if (ch == '/')
    @@ -859,7 +864,7 @@
             if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
                 permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) ||
                 (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP))
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
     
             /* An exec changes our domain. We are no longer part of the thread
                group */
    @@ -1158,7 +1163,6 @@
             retval = search_binary_handler(&bprm,regs);
             if (retval >= 0) {
                     free_arg_pages(&bprm);
    -
                     /* execve success */
                     security_bprm_free(&bprm);
                     return retval;
    @@ -1374,7 +1378,9 @@
             struct inode * inode;
             struct file * file;
             int retval = 0;
    -
    + int fsuid = current->fsuid;
    + int flag = 0;
    +
             binfmt = current->binfmt;
             if (!binfmt || !binfmt->core_dump)
                     goto fail;
    @@ -1383,6 +1389,17 @@
                     up_write(&mm->mmap_sem);
                     goto fail;
             }
    +
    + /*
    + * We cannot trust fsuid as being the "true" uid of the
    + * process nor do we know its entire history. We only know it
    + * was tainted so we dump it as root in mode 2.
    + */
    + if (mm->dumpable == 2) /* Setuid core dump mode */
    + {
    + flag = O_EXCL; /* Stop rewrite attacks */
    + current->fsuid = 0; /* Dump root private */
    + }
             mm->dumpable = 0;
             init_completion(&mm->core_done);
             current->signal->group_exit = 1;
    @@ -1399,7 +1416,7 @@
              lock_kernel();
             format_corename(corename, core_pattern, signr);
             unlock_kernel();
    - file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE, 0600);
    + file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, 0600);
             if (IS_ERR(file))
                     goto fail_unlock;
             inode = file->f_dentry->d_inode;
    @@ -1423,6 +1440,7 @@
     close_fail:
             filp_close(file, NULL);
     fail_unlock:
    + current->fsuid = fsuid;
             complete_all(&mm->core_done);
     fail:
             return retval;
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/fs/proc/base.c linux-2.6.9rc2/fs/proc/base.c
    --- linux.vanilla-2.6.9rc2/fs/proc/base.c 2004-09-14 14:22:54.000000000 +0100
    +++ linux-2.6.9rc2/fs/proc/base.c 2004-09-27 22:13:33.610266224 +0100
    @@ -307,7 +307,7 @@
                  (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
                     goto out;
             rmb();
    - if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE))
    + if (task->mm->dumpable != 1 && !capable(CAP_SYS_PTRACE))
                     goto out;
             if (security_ptrace(current, task))
                     goto out;
    @@ -929,7 +929,9 @@
             if (mm)
                     dumpable = mm->dumpable;
             task_unlock(task);
    - return dumpable;
    + if(dumpable == 1)
    + return 1;
    + return 0;
     }
     
     
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/include/linux/binfmts.h linux-2.6.9rc2/include/linux/binfmts.h
    --- linux.vanilla-2.6.9rc2/include/linux/binfmts.h 2004-09-14 14:19:39.000000000 +0100
    +++ linux-2.6.9rc2/include/linux/binfmts.h 2004-09-29 00:54:44.882212920 +0100
    @@ -69,6 +69,8 @@
     extern int search_binary_handler(struct linux_binprm *,struct pt_regs *);
     extern int flush_old_exec(struct linux_binprm * bprm);
     
    +extern int suid_dumpable;
    +
     /* Stack area protections */
     #define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */
     #define EXSTACK_DISABLE_X 1 /* Disable executable stacks */
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/include/linux/sched.h linux-2.6.9rc2/include/linux/sched.h
    --- linux.vanilla-2.6.9rc2/include/linux/sched.h 2004-09-14 14:22:57.000000000 +0100
    +++ linux-2.6.9rc2/include/linux/sched.h 2004-09-27 16:12:04.000000000 +0100
    @@ -230,7 +230,7 @@
     
             unsigned long saved_auxv[42]; /* for /proc/PID/auxv */
     
    - unsigned dumpable:1;
    + unsigned dumpable:2;
             cpumask_t cpu_vm_mask;
     
             /* Architecture-specific MM context */
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/include/linux/sysctl.h linux-2.6.9rc2/include/linux/sysctl.h
    --- linux.vanilla-2.6.9rc2/include/linux/sysctl.h 2004-09-14 14:22:57.000000000 +0100
    +++ linux-2.6.9rc2/include/linux/sysctl.h 2004-09-29 00:53:21.220931368 +0100
    @@ -134,6 +134,7 @@
             KERN_SPARC_SCONS_PWROFF=64, /* int: serial console power-off halt */
             KERN_HZ_TIMER=65, /* int: hz timer on or off */
             KERN_UNKNOWN_NMI_PANIC=66, /* int: unknown nmi panic flag */
    + KERN_SETUID_DUMPABLE=67, /* int: behaviour of dumps for setuid core */
     };
     
     
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/kernel/sys.c linux-2.6.9rc2/kernel/sys.c
    --- linux.vanilla-2.6.9rc2/kernel/sys.c 2004-09-14 14:22:57.000000000 +0100
    +++ linux-2.6.9rc2/kernel/sys.c 2004-09-27 16:10:24.000000000 +0100
    @@ -589,7 +589,7 @@
             }
             if (new_egid != old_egid)
             {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
                     wmb();
             }
             if (rgid != (gid_t) -1 ||
    @@ -619,7 +619,7 @@
             {
                     if(old_egid != gid)
                     {
    - current->mm->dumpable=0;
    + current->mm->dumpable = suid_dumpable;
                             wmb();
                     }
                     current->gid = current->egid = current->sgid = current->fsgid = gid;
    @@ -628,7 +628,7 @@
             {
                     if(old_egid != gid)
                     {
    - current->mm->dumpable=0;
    + current->mm->dumpable = suid_dumpable;
                             wmb();
                     }
                     current->egid = current->fsgid = gid;
    @@ -657,7 +657,7 @@
     
             if(dumpclear)
             {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
                     wmb();
             }
             current->uid = new_ruid;
    @@ -714,7 +714,7 @@
     
             if (new_euid != old_euid)
             {
    - current->mm->dumpable=0;
    + current->mm->dumpable = suid_dumpable;
                     wmb();
             }
             current->fsuid = current->euid = new_euid;
    @@ -762,7 +762,7 @@
     
             if (old_euid != uid)
             {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
                     wmb();
             }
             current->fsuid = current->euid = uid;
    @@ -805,7 +805,7 @@
             if (euid != (uid_t) -1) {
                     if (euid != current->euid)
                     {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
                             wmb();
                     }
                     current->euid = euid;
    @@ -853,7 +853,7 @@
             if (egid != (gid_t) -1) {
                     if (egid != current->egid)
                     {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
                             wmb();
                     }
                     current->egid = egid;
    @@ -898,7 +898,7 @@
             {
                     if (uid != old_fsuid)
                     {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
                             wmb();
                     }
                     current->fsuid = uid;
    @@ -926,7 +926,7 @@
             {
                     if (gid != old_fsgid)
                     {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
                             wmb();
                     }
                     current->fsgid = gid;
    @@ -1681,7 +1681,7 @@
                                     error = 1;
                             break;
                     case PR_SET_DUMPABLE:
    - if (arg2 != 0 && arg2 != 1) {
    + if (arg2 < 0 || arg2 > 2) {
                                     error = -EINVAL;
                                     break;
                             }
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/kernel/sysctl.c linux-2.6.9rc2/kernel/sysctl.c
    --- linux.vanilla-2.6.9rc2/kernel/sysctl.c 2004-09-14 14:22:57.000000000 +0100
    +++ linux-2.6.9rc2/kernel/sysctl.c 2004-09-27 17:20:55.000000000 +0100
    @@ -58,6 +58,7 @@
     extern int max_threads;
     extern int sysrq_enabled;
     extern int core_uses_pid;
    +extern int suid_dumpable;
     extern char core_pattern[];
     extern int cad_pid;
     extern int pid_max;
    @@ -619,6 +620,14 @@
                     .proc_handler = &proc_unknown_nmi_panic,
             },
     #endif
    + {
    + .ctl_name = KERN_SETUID_DUMPABLE,
    + .procname = "suid_dumpable",
    + .data = &suid_dumpable,
    + .maxlen = sizeof(int),
    + .mode = 0644,
    + .proc_handler = &proc_dointvec,
    + },
             { .ctl_name = 0 }
     };
     
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/security/commoncap.c linux-2.6.9rc2/security/commoncap.c
    --- linux.vanilla-2.6.9rc2/security/commoncap.c 2004-09-14 14:22:57.000000000 +0100
    +++ linux-2.6.9rc2/security/commoncap.c 2004-09-27 18:46:09.000000000 +0100
    @@ -127,7 +127,7 @@
     
             if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
                 !cap_issubset (new_permitted, current->cap_permitted)) {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
     
                     if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
                             if (!capable(CAP_SETUID)) {
    diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.9rc2/security/dummy.c linux-2.6.9rc2/security/dummy.c
    --- linux.vanilla-2.6.9rc2/security/dummy.c 2004-09-14 14:19:28.000000000 +0100
    +++ linux-2.6.9rc2/security/dummy.c 2004-09-27 18:45:46.000000000 +0100
    @@ -174,7 +174,7 @@
     static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
     {
             if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
    - current->mm->dumpable = 0;
    + current->mm->dumpable = suid_dumpable;
     
                     if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) {
                             bprm->e_uid = current->uid;
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/


  • Next message: Alan Cox: "PATCH: 3c59x 00:00:00:00:00:00 MAC failure"

    Relevant Pages