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; |
