aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Cox <alan@lxorguk.ukuu.org.uk>2005-06-23 03:09:43 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-23 12:45:26 -0400
commitd6e711448137ca3301512cec41a2c2ce852b3d0a (patch)
treef0765ebd90fdbdf270c05fcd7f3d32b24ba56681
parent8b0914ea7475615c7c8965c1ac8fe4069270f25c (diff)
[PATCH] setuid core dump
Add a new `suid_dumpable' sysctl: 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. Ptrace is unchecked. 2 - (suidsafe) - any binary which normally would 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. (akpm: > > +EXPORT_SYMBOL(suid_dumpable); > > EXPORT_SYMBOL_GPL? No problem to me. > > if (current->euid == current->uid && current->egid == current->gid) > > current->mm->dumpable = 1; > > Should this be SUID_DUMP_USER? Actually the feedback I had from last time was that the SUID_ defines should go because its clearer to follow the numbers. They can go everywhere (and there are lots of places where dumpable is tested/used as a bool in untouched code) > Maybe this should be renamed to `dump_policy' or something. Doing that > would help us catch any code which isn't using the #defines, too. Fair comment. The patch was designed to be easy to maintain for Red Hat rather than for merging. Changing that field would create a gigantic diff because it is used all over the place. ) Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--Documentation/sysctl/kernel.txt20
-rw-r--r--fs/exec.c23
-rw-r--r--fs/proc/base.c6
-rw-r--r--include/linux/binfmts.h5
-rw-r--r--include/linux/sched.h2
-rw-r--r--include/linux/sysctl.h1
-rw-r--r--kernel/sys.c22
-rw-r--r--kernel/sysctl.c9
-rw-r--r--security/commoncap.c2
-rw-r--r--security/dummy.c2
10 files changed, 74 insertions, 18 deletions
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 35159176997b..9f11d36a8c10 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -49,6 +49,7 @@ show up in /proc/sys/kernel:
49- shmmax [ sysv ipc ] 49- shmmax [ sysv ipc ]
50- shmmni 50- shmmni
51- stop-a [ SPARC only ] 51- stop-a [ SPARC only ]
52- suid_dumpable
52- sysrq ==> Documentation/sysrq.txt 53- sysrq ==> Documentation/sysrq.txt
53- tainted 54- tainted
54- threads-max 55- threads-max
@@ -300,6 +301,25 @@ kernel. This value defaults to SHMMAX.
300 301
301============================================================== 302==============================================================
302 303
304suid_dumpable:
305
306This value can be used to query and set the core dump mode for setuid
307or otherwise protected/tainted binaries. The modes are
308
3090 - (default) - traditional behaviour. Any process which has changed
310 privilege levels or is execute only will not be dumped
3111 - (debug) - all processes dump core when possible. The core dump is
312 owned by the current user and no security is applied. This is
313 intended for system debugging situations only. Ptrace is unchecked.
3142 - (suidsafe) - any binary which normally would not be dumped is dumped
315 readable by root only. This allows the end user to remove
316 such a dump but not access it directly. For security reasons
317 core dumps in this mode will not overwrite one another or
318 other files. This mode is appropriate when adminstrators are
319 attempting to debug problems in a normal environment.
320
321==============================================================
322
303tainted: 323tainted:
304 324
305Non-zero if the kernel has been tainted. Numeric values, which 325Non-zero if the kernel has been tainted. Numeric values, which
diff --git a/fs/exec.c b/fs/exec.c
index 3a4b35a14c0d..48871917d363 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -58,6 +58,9 @@
58 58
59int core_uses_pid; 59int core_uses_pid;
60char core_pattern[65] = "core"; 60char core_pattern[65] = "core";
61int suid_dumpable = 0;
62
63EXPORT_SYMBOL(suid_dumpable);
61/* The maximal length of core_pattern is also specified in sysctl.c */ 64/* The maximal length of core_pattern is also specified in sysctl.c */
62 65
63static struct linux_binfmt *formats; 66static struct linux_binfmt *formats;
@@ -864,6 +867,9 @@ int flush_old_exec(struct linux_binprm * bprm)
864 867
865 if (current->euid == current->uid && current->egid == current->gid) 868 if (current->euid == current->uid && current->egid == current->gid)
866 current->mm->dumpable = 1; 869 current->mm->dumpable = 1;
870 else
871 current->mm->dumpable = suid_dumpable;
872
867 name = bprm->filename; 873 name = bprm->filename;
868 874
869 /* Copies the binary name from after last slash */ 875 /* Copies the binary name from after last slash */
@@ -884,7 +890,7 @@ int flush_old_exec(struct linux_binprm * bprm)
884 permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) || 890 permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) ||
885 (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { 891 (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
886 suid_keys(current); 892 suid_keys(current);
887 current->mm->dumpable = 0; 893 current->mm->dumpable = suid_dumpable;
888 } 894 }
889 895
890 /* An exec changes our domain. We are no longer part of the thread 896 /* An exec changes our domain. We are no longer part of the thread
@@ -1432,6 +1438,8 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
1432 struct inode * inode; 1438 struct inode * inode;
1433 struct file * file; 1439 struct file * file;
1434 int retval = 0; 1440 int retval = 0;
1441 int fsuid = current->fsuid;
1442 int flag = 0;
1435 1443
1436 binfmt = current->binfmt; 1444 binfmt = current->binfmt;
1437 if (!binfmt || !binfmt->core_dump) 1445 if (!binfmt || !binfmt->core_dump)
@@ -1441,6 +1449,16 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
1441 up_write(&mm->mmap_sem); 1449 up_write(&mm->mmap_sem);
1442 goto fail; 1450 goto fail;
1443 } 1451 }
1452
1453 /*
1454 * We cannot trust fsuid as being the "true" uid of the
1455 * process nor do we know its entire history. We only know it
1456 * was tainted so we dump it as root in mode 2.
1457 */
1458 if (mm->dumpable == 2) { /* Setuid core dump mode */
1459 flag = O_EXCL; /* Stop rewrite attacks */
1460 current->fsuid = 0; /* Dump root private */
1461 }
1444 mm->dumpable = 0; 1462 mm->dumpable = 0;
1445 init_completion(&mm->core_done); 1463 init_completion(&mm->core_done);
1446 spin_lock_irq(&current->sighand->siglock); 1464 spin_lock_irq(&current->sighand->siglock);
@@ -1466,7 +1484,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
1466 lock_kernel(); 1484 lock_kernel();
1467 format_corename(corename, core_pattern, signr); 1485 format_corename(corename, core_pattern, signr);
1468 unlock_kernel(); 1486 unlock_kernel();
1469 file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE, 0600); 1487 file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, 0600);
1470 if (IS_ERR(file)) 1488 if (IS_ERR(file))
1471 goto fail_unlock; 1489 goto fail_unlock;
1472 inode = file->f_dentry->d_inode; 1490 inode = file->f_dentry->d_inode;
@@ -1491,6 +1509,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
1491close_fail: 1509close_fail:
1492 filp_close(file, NULL); 1510 filp_close(file, NULL);
1493fail_unlock: 1511fail_unlock:
1512 current->fsuid = fsuid;
1494 complete_all(&mm->core_done); 1513 complete_all(&mm->core_done);
1495fail: 1514fail:
1496 return retval; 1515 return retval;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index e31903aadd96..ace151fa4878 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -314,7 +314,7 @@ static int may_ptrace_attach(struct task_struct *task)
314 (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) 314 (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
315 goto out; 315 goto out;
316 rmb(); 316 rmb();
317 if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) 317 if (task->mm->dumpable != 1 && !capable(CAP_SYS_PTRACE))
318 goto out; 318 goto out;
319 if (security_ptrace(current, task)) 319 if (security_ptrace(current, task))
320 goto out; 320 goto out;
@@ -1113,7 +1113,9 @@ static int task_dumpable(struct task_struct *task)
1113 if (mm) 1113 if (mm)
1114 dumpable = mm->dumpable; 1114 dumpable = mm->dumpable;
1115 task_unlock(task); 1115 task_unlock(task);
1116 return dumpable; 1116 if(dumpable == 1)
1117 return 1;
1118 return 0;
1117} 1119}
1118 1120
1119 1121
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 7e736e201c46..c1e82c514443 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -69,6 +69,11 @@ extern void remove_arg_zero(struct linux_binprm *);
69extern int search_binary_handler(struct linux_binprm *,struct pt_regs *); 69extern int search_binary_handler(struct linux_binprm *,struct pt_regs *);
70extern int flush_old_exec(struct linux_binprm * bprm); 70extern int flush_old_exec(struct linux_binprm * bprm);
71 71
72extern int suid_dumpable;
73#define SUID_DUMP_DISABLE 0 /* No setuid dumping */
74#define SUID_DUMP_USER 1 /* Dump as user of process */
75#define SUID_DUMP_ROOT 2 /* Dump as root */
76
72/* Stack area protections */ 77/* Stack area protections */
73#define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */ 78#define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */
74#define EXSTACK_DISABLE_X 1 /* Disable executable stacks */ 79#define EXSTACK_DISABLE_X 1 /* Disable executable stacks */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index b58afd97a180..901742f92389 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -246,7 +246,7 @@ struct mm_struct {
246 246
247 unsigned long saved_auxv[42]; /* for /proc/PID/auxv */ 247 unsigned long saved_auxv[42]; /* for /proc/PID/auxv */
248 248
249 unsigned dumpable:1; 249 unsigned dumpable:2;
250 cpumask_t cpu_vm_mask; 250 cpumask_t cpu_vm_mask;
251 251
252 /* Architecture-specific MM context */ 252 /* Architecture-specific MM context */
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index a17745c80a91..614e939c78a4 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -136,6 +136,7 @@ enum
136 KERN_UNKNOWN_NMI_PANIC=66, /* int: unknown nmi panic flag */ 136 KERN_UNKNOWN_NMI_PANIC=66, /* int: unknown nmi panic flag */
137 KERN_BOOTLOADER_TYPE=67, /* int: boot loader type */ 137 KERN_BOOTLOADER_TYPE=67, /* int: boot loader type */
138 KERN_RANDOMIZE=68, /* int: randomize virtual address space */ 138 KERN_RANDOMIZE=68, /* int: randomize virtual address space */
139 KERN_SETUID_DUMPABLE=69, /* int: behaviour of dumps for setuid core */
139}; 140};
140 141
141 142
diff --git a/kernel/sys.c b/kernel/sys.c
index f006632c2ba7..0a2c8cda9638 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -525,7 +525,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
525 } 525 }
526 if (new_egid != old_egid) 526 if (new_egid != old_egid)
527 { 527 {
528 current->mm->dumpable = 0; 528 current->mm->dumpable = suid_dumpable;
529 smp_wmb(); 529 smp_wmb();
530 } 530 }
531 if (rgid != (gid_t) -1 || 531 if (rgid != (gid_t) -1 ||
@@ -556,7 +556,7 @@ asmlinkage long sys_setgid(gid_t gid)
556 { 556 {
557 if(old_egid != gid) 557 if(old_egid != gid)
558 { 558 {
559 current->mm->dumpable=0; 559 current->mm->dumpable = suid_dumpable;
560 smp_wmb(); 560 smp_wmb();
561 } 561 }
562 current->gid = current->egid = current->sgid = current->fsgid = gid; 562 current->gid = current->egid = current->sgid = current->fsgid = gid;
@@ -565,7 +565,7 @@ asmlinkage long sys_setgid(gid_t gid)
565 { 565 {
566 if(old_egid != gid) 566 if(old_egid != gid)
567 { 567 {
568 current->mm->dumpable=0; 568 current->mm->dumpable = suid_dumpable;
569 smp_wmb(); 569 smp_wmb();
570 } 570 }
571 current->egid = current->fsgid = gid; 571 current->egid = current->fsgid = gid;
@@ -596,7 +596,7 @@ static int set_user(uid_t new_ruid, int dumpclear)
596 596
597 if(dumpclear) 597 if(dumpclear)
598 { 598 {
599 current->mm->dumpable = 0; 599 current->mm->dumpable = suid_dumpable;
600 smp_wmb(); 600 smp_wmb();
601 } 601 }
602 current->uid = new_ruid; 602 current->uid = new_ruid;
@@ -653,7 +653,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
653 653
654 if (new_euid != old_euid) 654 if (new_euid != old_euid)
655 { 655 {
656 current->mm->dumpable=0; 656 current->mm->dumpable = suid_dumpable;
657 smp_wmb(); 657 smp_wmb();
658 } 658 }
659 current->fsuid = current->euid = new_euid; 659 current->fsuid = current->euid = new_euid;
@@ -703,7 +703,7 @@ asmlinkage long sys_setuid(uid_t uid)
703 703
704 if (old_euid != uid) 704 if (old_euid != uid)
705 { 705 {
706 current->mm->dumpable = 0; 706 current->mm->dumpable = suid_dumpable;
707 smp_wmb(); 707 smp_wmb();
708 } 708 }
709 current->fsuid = current->euid = uid; 709 current->fsuid = current->euid = uid;
@@ -748,7 +748,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
748 if (euid != (uid_t) -1) { 748 if (euid != (uid_t) -1) {
749 if (euid != current->euid) 749 if (euid != current->euid)
750 { 750 {
751 current->mm->dumpable = 0; 751 current->mm->dumpable = suid_dumpable;
752 smp_wmb(); 752 smp_wmb();
753 } 753 }
754 current->euid = euid; 754 current->euid = euid;
@@ -798,7 +798,7 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
798 if (egid != (gid_t) -1) { 798 if (egid != (gid_t) -1) {
799 if (egid != current->egid) 799 if (egid != current->egid)
800 { 800 {
801 current->mm->dumpable = 0; 801 current->mm->dumpable = suid_dumpable;
802 smp_wmb(); 802 smp_wmb();
803 } 803 }
804 current->egid = egid; 804 current->egid = egid;
@@ -845,7 +845,7 @@ asmlinkage long sys_setfsuid(uid_t uid)
845 { 845 {
846 if (uid != old_fsuid) 846 if (uid != old_fsuid)
847 { 847 {
848 current->mm->dumpable = 0; 848 current->mm->dumpable = suid_dumpable;
849 smp_wmb(); 849 smp_wmb();
850 } 850 }
851 current->fsuid = uid; 851 current->fsuid = uid;
@@ -875,7 +875,7 @@ asmlinkage long sys_setfsgid(gid_t gid)
875 { 875 {
876 if (gid != old_fsgid) 876 if (gid != old_fsgid)
877 { 877 {
878 current->mm->dumpable = 0; 878 current->mm->dumpable = suid_dumpable;
879 smp_wmb(); 879 smp_wmb();
880 } 880 }
881 current->fsgid = gid; 881 current->fsgid = gid;
@@ -1652,7 +1652,7 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
1652 error = 1; 1652 error = 1;
1653 break; 1653 break;
1654 case PR_SET_DUMPABLE: 1654 case PR_SET_DUMPABLE:
1655 if (arg2 != 0 && arg2 != 1) { 1655 if (arg2 < 0 || arg2 > 2) {
1656 error = -EINVAL; 1656 error = -EINVAL;
1657 break; 1657 break;
1658 } 1658 }
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 701d12c63068..24a4d12d5aa9 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -58,6 +58,7 @@ extern int sysctl_overcommit_ratio;
58extern int max_threads; 58extern int max_threads;
59extern int sysrq_enabled; 59extern int sysrq_enabled;
60extern int core_uses_pid; 60extern int core_uses_pid;
61extern int suid_dumpable;
61extern char core_pattern[]; 62extern char core_pattern[];
62extern int cad_pid; 63extern int cad_pid;
63extern int pid_max; 64extern int pid_max;
@@ -950,6 +951,14 @@ static ctl_table fs_table[] = {
950 .proc_handler = &proc_dointvec, 951 .proc_handler = &proc_dointvec,
951 }, 952 },
952#endif 953#endif
954 {
955 .ctl_name = KERN_SETUID_DUMPABLE,
956 .procname = "suid_dumpable",
957 .data = &suid_dumpable,
958 .maxlen = sizeof(int),
959 .mode = 0644,
960 .proc_handler = &proc_dointvec,
961 },
953 { .ctl_name = 0 } 962 { .ctl_name = 0 }
954}; 963};
955 964
diff --git a/security/commoncap.c b/security/commoncap.c
index 849b8c338ee8..04c12f58d656 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -149,7 +149,7 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
149 149
150 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || 150 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
151 !cap_issubset (new_permitted, current->cap_permitted)) { 151 !cap_issubset (new_permitted, current->cap_permitted)) {
152 current->mm->dumpable = 0; 152 current->mm->dumpable = suid_dumpable;
153 153
154 if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { 154 if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
155 if (!capable(CAP_SETUID)) { 155 if (!capable(CAP_SETUID)) {
diff --git a/security/dummy.c b/security/dummy.c
index b32eff146547..6ff887586479 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -130,7 +130,7 @@ static void dummy_bprm_free_security (struct linux_binprm *bprm)
130static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) 130static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
131{ 131{
132 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { 132 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
133 current->mm->dumpable = 0; 133 current->mm->dumpable = suid_dumpable;
134 134
135 if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) { 135 if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) {
136 bprm->e_uid = current->uid; 136 bprm->e_uid = current->uid;