diff options
-rw-r--r-- | Documentation/sysctl/kernel.txt | 20 | ||||
-rw-r--r-- | fs/exec.c | 23 | ||||
-rw-r--r-- | fs/proc/base.c | 6 | ||||
-rw-r--r-- | include/linux/binfmts.h | 5 | ||||
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | include/linux/sysctl.h | 1 | ||||
-rw-r--r-- | kernel/sys.c | 22 | ||||
-rw-r--r-- | kernel/sysctl.c | 9 | ||||
-rw-r--r-- | security/commoncap.c | 2 | ||||
-rw-r--r-- | security/dummy.c | 2 |
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 | ||
304 | suid_dumpable: | ||
305 | |||
306 | This value can be used to query and set the core dump mode for setuid | ||
307 | or otherwise protected/tainted binaries. The modes are | ||
308 | |||
309 | 0 - (default) - traditional behaviour. Any process which has changed | ||
310 | privilege levels or is execute only will not be dumped | ||
311 | 1 - (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. | ||
314 | 2 - (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 | |||
303 | tainted: | 323 | tainted: |
304 | 324 | ||
305 | Non-zero if the kernel has been tainted. Numeric values, which | 325 | Non-zero if the kernel has been tainted. Numeric values, which |
@@ -58,6 +58,9 @@ | |||
58 | 58 | ||
59 | int core_uses_pid; | 59 | int core_uses_pid; |
60 | char core_pattern[65] = "core"; | 60 | char core_pattern[65] = "core"; |
61 | int suid_dumpable = 0; | ||
62 | |||
63 | EXPORT_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 | ||
63 | static struct linux_binfmt *formats; | 66 | static 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(¤t->sighand->siglock); | 1464 | spin_lock_irq(¤t->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) | |||
1491 | close_fail: | 1509 | close_fail: |
1492 | filp_close(file, NULL); | 1510 | filp_close(file, NULL); |
1493 | fail_unlock: | 1511 | fail_unlock: |
1512 | current->fsuid = fsuid; | ||
1494 | complete_all(&mm->core_done); | 1513 | complete_all(&mm->core_done); |
1495 | fail: | 1514 | fail: |
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 *); | |||
69 | extern int search_binary_handler(struct linux_binprm *,struct pt_regs *); | 69 | extern int search_binary_handler(struct linux_binprm *,struct pt_regs *); |
70 | extern int flush_old_exec(struct linux_binprm * bprm); | 70 | extern int flush_old_exec(struct linux_binprm * bprm); |
71 | 71 | ||
72 | extern 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; | |||
58 | extern int max_threads; | 58 | extern int max_threads; |
59 | extern int sysrq_enabled; | 59 | extern int sysrq_enabled; |
60 | extern int core_uses_pid; | 60 | extern int core_uses_pid; |
61 | extern int suid_dumpable; | ||
61 | extern char core_pattern[]; | 62 | extern char core_pattern[]; |
62 | extern int cad_pid; | 63 | extern int cad_pid; |
63 | extern int pid_max; | 64 | extern 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) | |||
130 | static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | 130 | static 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; |