diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 33 |
1 files changed, 28 insertions, 5 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 261ee21e62db..9650e7aee267 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/uio.h> | 20 | #include <linux/uio.h> |
21 | #include <linux/audit.h> | 21 | #include <linux/audit.h> |
22 | #include <linux/pid_namespace.h> | 22 | #include <linux/pid_namespace.h> |
23 | #include <linux/user_namespace.h> | ||
23 | #include <linux/syscalls.h> | 24 | #include <linux/syscalls.h> |
24 | #include <linux/uaccess.h> | 25 | #include <linux/uaccess.h> |
25 | #include <linux/regset.h> | 26 | #include <linux/regset.h> |
@@ -207,12 +208,34 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state) | |||
207 | return ret; | 208 | return ret; |
208 | } | 209 | } |
209 | 210 | ||
210 | static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) | 211 | static bool ptrace_has_cap(const struct cred *tcred, unsigned int mode) |
211 | { | 212 | { |
213 | struct user_namespace *tns = tcred->user_ns; | ||
214 | |||
215 | /* When a root-owned process enters a user namespace created by a | ||
216 | * malicious user, the user shouldn't be able to execute code under | ||
217 | * uid 0 by attaching to the root-owned process via ptrace. | ||
218 | * Therefore, similar to the capable_wrt_inode_uidgid() check, | ||
219 | * verify that all the uids and gids of the target process are | ||
220 | * mapped into a namespace below the current one in which the caller | ||
221 | * is capable. | ||
222 | * No fsuid/fsgid check because __ptrace_may_access doesn't do it | ||
223 | * either. | ||
224 | */ | ||
225 | while ( | ||
226 | !kuid_has_mapping(tns, tcred->euid) || | ||
227 | !kuid_has_mapping(tns, tcred->suid) || | ||
228 | !kuid_has_mapping(tns, tcred->uid) || | ||
229 | !kgid_has_mapping(tns, tcred->egid) || | ||
230 | !kgid_has_mapping(tns, tcred->sgid) || | ||
231 | !kgid_has_mapping(tns, tcred->gid)) { | ||
232 | tns = tns->parent; | ||
233 | } | ||
234 | |||
212 | if (mode & PTRACE_MODE_NOAUDIT) | 235 | if (mode & PTRACE_MODE_NOAUDIT) |
213 | return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE); | 236 | return has_ns_capability_noaudit(current, tns, CAP_SYS_PTRACE); |
214 | else | 237 | else |
215 | return has_ns_capability(current, ns, CAP_SYS_PTRACE); | 238 | return has_ns_capability(current, tns, CAP_SYS_PTRACE); |
216 | } | 239 | } |
217 | 240 | ||
218 | /* Returns 0 on success, -errno on denial. */ | 241 | /* Returns 0 on success, -errno on denial. */ |
@@ -264,7 +287,7 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
264 | gid_eq(caller_gid, tcred->sgid) && | 287 | gid_eq(caller_gid, tcred->sgid) && |
265 | gid_eq(caller_gid, tcred->gid)) | 288 | gid_eq(caller_gid, tcred->gid)) |
266 | goto ok; | 289 | goto ok; |
267 | if (ptrace_has_cap(tcred->user_ns, mode)) | 290 | if (ptrace_has_cap(tcred, mode)) |
268 | goto ok; | 291 | goto ok; |
269 | rcu_read_unlock(); | 292 | rcu_read_unlock(); |
270 | return -EPERM; | 293 | return -EPERM; |
@@ -275,7 +298,7 @@ ok: | |||
275 | dumpable = get_dumpable(task->mm); | 298 | dumpable = get_dumpable(task->mm); |
276 | rcu_read_lock(); | 299 | rcu_read_lock(); |
277 | if (dumpable != SUID_DUMP_USER && | 300 | if (dumpable != SUID_DUMP_USER && |
278 | !ptrace_has_cap(__task_cred(task)->user_ns, mode)) { | 301 | !ptrace_has_cap(__task_cred(task), mode)) { |
279 | rcu_read_unlock(); | 302 | rcu_read_unlock(); |
280 | return -EPERM; | 303 | return -EPERM; |
281 | } | 304 | } |