diff options
author | Kees Cook <keescook@chromium.org> | 2012-07-30 17:39:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-30 20:25:11 -0400 |
commit | 9520628e8ceb69fa9a4aee6b57f22675d9e1b709 (patch) | |
tree | c8e1dbd5820e818eef930cf55cbd94dec941eb44 /fs/exec.c | |
parent | 779302e67835fe9a6b74327e54969ba59cb3478a (diff) |
fs: make dumpable=2 require fully qualified path
When the suid_dumpable sysctl is set to "2", and there is no core dump
pipe defined in the core_pattern sysctl, a local user can cause core files
to be written to root-writable directories, potentially with
user-controlled content.
This means an admin can unknowningly reintroduce a variation of
CVE-2006-2451, allowing local users to gain root privileges.
$ cat /proc/sys/fs/suid_dumpable
2
$ cat /proc/sys/kernel/core_pattern
core
$ ulimit -c unlimited
$ cd /
$ ls -l core
ls: cannot access core: No such file or directory
$ touch core
touch: cannot touch `core': Permission denied
$ OHAI="evil-string-here" ping localhost >/dev/null 2>&1 &
$ pid=$!
$ sleep 1
$ kill -SEGV $pid
$ ls -l core
-rw------- 1 root kees 458752 Jun 21 11:35 core
$ sudo strings core | grep evil
OHAI=evil-string-here
While cron has been fixed to abort reading a file when there is any
parse error, there are still other sensitive directories that will read
any file present and skip unparsable lines.
Instead of introducing a suid_dumpable=3 mode and breaking all users of
mode 2, this only disables the unsafe portion of mode 2 (writing to disk
via relative path). Most users of mode 2 (e.g. Chrome OS) already use
a core dump pipe handler, so this change will not break them. For the
situations where a pipe handler is not defined but mode 2 is still
active, crash dumps will only be written to fully qualified paths. If a
relative path is defined (e.g. the default "core" pattern), dump
attempts will trigger a printk yelling about the lack of a fully
qualified path.
Signed-off-by: Kees Cook <keescook@chromium.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>
Diffstat (limited to 'fs/exec.c')
-rw-r--r-- | fs/exec.c | 17 |
1 files changed, 14 insertions, 3 deletions
@@ -2111,6 +2111,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) | |||
2111 | int retval = 0; | 2111 | int retval = 0; |
2112 | int flag = 0; | 2112 | int flag = 0; |
2113 | int ispipe; | 2113 | int ispipe; |
2114 | bool need_nonrelative = false; | ||
2114 | static atomic_t core_dump_count = ATOMIC_INIT(0); | 2115 | static atomic_t core_dump_count = ATOMIC_INIT(0); |
2115 | struct coredump_params cprm = { | 2116 | struct coredump_params cprm = { |
2116 | .signr = signr, | 2117 | .signr = signr, |
@@ -2136,14 +2137,16 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) | |||
2136 | if (!cred) | 2137 | if (!cred) |
2137 | goto fail; | 2138 | goto fail; |
2138 | /* | 2139 | /* |
2139 | * We cannot trust fsuid as being the "true" uid of the | 2140 | * We cannot trust fsuid as being the "true" uid of the process |
2140 | * process nor do we know its entire history. We only know it | 2141 | * nor do we know its entire history. We only know it was tainted |
2141 | * was tainted so we dump it as root in mode 2. | 2142 | * so we dump it as root in mode 2, and only into a controlled |
2143 | * environment (pipe handler or fully qualified path). | ||
2142 | */ | 2144 | */ |
2143 | if (__get_dumpable(cprm.mm_flags) == 2) { | 2145 | if (__get_dumpable(cprm.mm_flags) == 2) { |
2144 | /* Setuid core dump mode */ | 2146 | /* Setuid core dump mode */ |
2145 | flag = O_EXCL; /* Stop rewrite attacks */ | 2147 | flag = O_EXCL; /* Stop rewrite attacks */ |
2146 | cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */ | 2148 | cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */ |
2149 | need_nonrelative = true; | ||
2147 | } | 2150 | } |
2148 | 2151 | ||
2149 | retval = coredump_wait(exit_code, &core_state); | 2152 | retval = coredump_wait(exit_code, &core_state); |
@@ -2223,6 +2226,14 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) | |||
2223 | if (cprm.limit < binfmt->min_coredump) | 2226 | if (cprm.limit < binfmt->min_coredump) |
2224 | goto fail_unlock; | 2227 | goto fail_unlock; |
2225 | 2228 | ||
2229 | if (need_nonrelative && cn.corename[0] != '/') { | ||
2230 | printk(KERN_WARNING "Pid %d(%s) can only dump core "\ | ||
2231 | "to fully qualified path!\n", | ||
2232 | task_tgid_vnr(current), current->comm); | ||
2233 | printk(KERN_WARNING "Skipping core dump\n"); | ||
2234 | goto fail_unlock; | ||
2235 | } | ||
2236 | |||
2226 | cprm.file = filp_open(cn.corename, | 2237 | cprm.file = filp_open(cn.corename, |
2227 | O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, | 2238 | O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, |
2228 | 0600); | 2239 | 0600); |