2004-09-29 16:28:09

by Alan Cox

[permalink] [raw]
Subject: PATCH: setuid core dump take 2

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;