coredump: warn about unsafe suid_dumpable / core_pattern combo
When suid_dumpable=2, detect unsafe core_pattern settings and warn when they are seen. Signed-off-by: Kees Cook <keescook@chromium.org> Suggested-by: Andrew Morton <akpm@linux-foundation.org> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Alan Cox <alan@linux.intel.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Doug Ledford <dledford@redhat.com> Cc: Serge Hallyn <serge.hallyn@canonical.com> Cc: James Morris <james.l.morris@oracle.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
9520628e8c
commit
54b501992d
10
fs/exec.c
10
fs/exec.c
@ -2002,17 +2002,17 @@ static void coredump_finish(struct mm_struct *mm)
|
|||||||
void set_dumpable(struct mm_struct *mm, int value)
|
void set_dumpable(struct mm_struct *mm, int value)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 0:
|
case SUID_DUMPABLE_DISABLED:
|
||||||
clear_bit(MMF_DUMPABLE, &mm->flags);
|
clear_bit(MMF_DUMPABLE, &mm->flags);
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
clear_bit(MMF_DUMP_SECURELY, &mm->flags);
|
clear_bit(MMF_DUMP_SECURELY, &mm->flags);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case SUID_DUMPABLE_ENABLED:
|
||||||
set_bit(MMF_DUMPABLE, &mm->flags);
|
set_bit(MMF_DUMPABLE, &mm->flags);
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
clear_bit(MMF_DUMP_SECURELY, &mm->flags);
|
clear_bit(MMF_DUMP_SECURELY, &mm->flags);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case SUID_DUMPABLE_SAFE:
|
||||||
set_bit(MMF_DUMP_SECURELY, &mm->flags);
|
set_bit(MMF_DUMP_SECURELY, &mm->flags);
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
set_bit(MMF_DUMPABLE, &mm->flags);
|
set_bit(MMF_DUMPABLE, &mm->flags);
|
||||||
@ -2025,7 +2025,7 @@ static int __get_dumpable(unsigned long mm_flags)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = mm_flags & MMF_DUMPABLE_MASK;
|
ret = mm_flags & MMF_DUMPABLE_MASK;
|
||||||
return (ret >= 2) ? 2 : ret;
|
return (ret > SUID_DUMPABLE_ENABLED) ? SUID_DUMPABLE_SAFE : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_dumpable(struct mm_struct *mm)
|
int get_dumpable(struct mm_struct *mm)
|
||||||
@ -2142,7 +2142,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
|||||||
* so we dump it as root in mode 2, and only into a controlled
|
* so we dump it as root in mode 2, and only into a controlled
|
||||||
* environment (pipe handler or fully qualified path).
|
* environment (pipe handler or fully qualified path).
|
||||||
*/
|
*/
|
||||||
if (__get_dumpable(cprm.mm_flags) == 2) {
|
if (__get_dumpable(cprm.mm_flags) == SUID_DUMPABLE_SAFE) {
|
||||||
/* Setuid core dump mode */
|
/* Setuid core dump mode */
|
||||||
flag = O_EXCL; /* Stop rewrite attacks */
|
flag = O_EXCL; /* Stop rewrite attacks */
|
||||||
cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */
|
cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */
|
||||||
|
@ -406,6 +406,11 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) {}
|
|||||||
extern void set_dumpable(struct mm_struct *mm, int value);
|
extern void set_dumpable(struct mm_struct *mm, int value);
|
||||||
extern int get_dumpable(struct mm_struct *mm);
|
extern int get_dumpable(struct mm_struct *mm);
|
||||||
|
|
||||||
|
/* get/set_dumpable() values */
|
||||||
|
#define SUID_DUMPABLE_DISABLED 0
|
||||||
|
#define SUID_DUMPABLE_ENABLED 1
|
||||||
|
#define SUID_DUMPABLE_SAFE 2
|
||||||
|
|
||||||
/* mm flags */
|
/* mm flags */
|
||||||
/* dumpable bits */
|
/* dumpable bits */
|
||||||
#define MMF_DUMPABLE 0 /* core dump is permitted */
|
#define MMF_DUMPABLE 0 /* core dump is permitted */
|
||||||
|
@ -174,6 +174,11 @@ static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write,
|
|||||||
void __user *buffer, size_t *lenp, loff_t *ppos);
|
void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||||
|
static int proc_dostring_coredump(struct ctl_table *table, int write,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||||
|
|
||||||
#ifdef CONFIG_MAGIC_SYSRQ
|
#ifdef CONFIG_MAGIC_SYSRQ
|
||||||
/* Note: sysrq code uses it's own private copy */
|
/* Note: sysrq code uses it's own private copy */
|
||||||
static int __sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
|
static int __sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
|
||||||
@ -410,7 +415,7 @@ static struct ctl_table kern_table[] = {
|
|||||||
.data = core_pattern,
|
.data = core_pattern,
|
||||||
.maxlen = CORENAME_MAX_SIZE,
|
.maxlen = CORENAME_MAX_SIZE,
|
||||||
.mode = 0644,
|
.mode = 0644,
|
||||||
.proc_handler = proc_dostring,
|
.proc_handler = proc_dostring_coredump,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.procname = "core_pipe_limit",
|
.procname = "core_pipe_limit",
|
||||||
@ -1498,7 +1503,7 @@ static struct ctl_table fs_table[] = {
|
|||||||
.data = &suid_dumpable,
|
.data = &suid_dumpable,
|
||||||
.maxlen = sizeof(int),
|
.maxlen = sizeof(int),
|
||||||
.mode = 0644,
|
.mode = 0644,
|
||||||
.proc_handler = proc_dointvec_minmax,
|
.proc_handler = proc_dointvec_minmax_coredump,
|
||||||
.extra1 = &zero,
|
.extra1 = &zero,
|
||||||
.extra2 = &two,
|
.extra2 = &two,
|
||||||
},
|
},
|
||||||
@ -2009,6 +2014,34 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
|
|||||||
do_proc_dointvec_minmax_conv, ¶m);
|
do_proc_dointvec_minmax_conv, ¶m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void validate_coredump_safety(void)
|
||||||
|
{
|
||||||
|
if (suid_dumpable == SUID_DUMPABLE_SAFE &&
|
||||||
|
core_pattern[0] != '/' && core_pattern[0] != '|') {
|
||||||
|
printk(KERN_WARNING "Unsafe core_pattern used with "\
|
||||||
|
"suid_dumpable=2. Pipe handler or fully qualified "\
|
||||||
|
"core dump path required.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
|
||||||
|
if (!error)
|
||||||
|
validate_coredump_safety();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int proc_dostring_coredump(struct ctl_table *table, int write,
|
||||||
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int error = proc_dostring(table, write, buffer, lenp, ppos);
|
||||||
|
if (!error)
|
||||||
|
validate_coredump_safety();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
|
static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
|
||||||
void __user *buffer,
|
void __user *buffer,
|
||||||
size_t *lenp, loff_t *ppos,
|
size_t *lenp, loff_t *ppos,
|
||||||
|
Loading…
Reference in New Issue
Block a user