diff options
author | Jann Horn <jann@thejh.net> | 2016-01-20 18:00:04 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-20 20:09:18 -0500 |
commit | caaee6234d05a58c5b4d05e7bf766131b810a657 (patch) | |
tree | 6227530109dd91ab5447fbd2211f09bc636845a7 | |
parent | 3dfb7d8cdbc7ea0c2970450e60818bb3eefbad69 (diff) |
ptrace: use fsuid, fsgid, effective creds for fs access checks
By checking the effective credentials instead of the real UID / permitted
capabilities, ensure that the calling process actually intended to use its
credentials.
To ensure that all ptrace checks use the correct caller credentials (e.g.
in case out-of-tree code or newly added code omits the PTRACE_MODE_*CREDS
flag), use two new flags and require one of them to be set.
The problem was that when a privileged task had temporarily dropped its
privileges, e.g. by calling setreuid(0, user_uid), with the intent to
perform following syscalls with the credentials of a user, it still passed
ptrace access checks that the user would not be able to pass.
While an attacker should not be able to convince the privileged task to
perform a ptrace() syscall, this is a problem because the ptrace access
check is reused for things in procfs.
In particular, the following somewhat interesting procfs entries only rely
on ptrace access checks:
/proc/$pid/stat - uses the check for determining whether pointers
should be visible, useful for bypassing ASLR
/proc/$pid/maps - also useful for bypassing ASLR
/proc/$pid/cwd - useful for gaining access to restricted
directories that contain files with lax permissions, e.g. in
this scenario:
lrwxrwxrwx root root /proc/13020/cwd -> /root/foobar
drwx------ root root /root
drwxr-xr-x root root /root/foobar
-rw-r--r-- root root /root/foobar/secret
Therefore, on a system where a root-owned mode 6755 binary changes its
effective credentials as described and then dumps a user-specified file,
this could be used by an attacker to reveal the memory layout of root's
processes or reveal the contents of files he is not allowed to access
(through /proc/$pid/cwd).
[akpm@linux-foundation.org: fix warning]
Signed-off-by: Jann Horn <jann@thejh.net>
Acked-by: Kees Cook <keescook@chromium.org>
Cc: Casey Schaufler <casey@schaufler-ca.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: "Serge E. Hallyn" <serge.hallyn@ubuntu.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Willy Tarreau <w@1wt.eu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/proc/array.c | 2 | ||||
-rw-r--r-- | fs/proc/base.c | 21 | ||||
-rw-r--r-- | fs/proc/namespaces.c | 4 | ||||
-rw-r--r-- | include/linux/ptrace.h | 24 | ||||
-rw-r--r-- | kernel/events/core.c | 2 | ||||
-rw-r--r-- | kernel/futex.c | 2 | ||||
-rw-r--r-- | kernel/futex_compat.c | 2 | ||||
-rw-r--r-- | kernel/kcmp.c | 4 | ||||
-rw-r--r-- | kernel/ptrace.c | 39 | ||||
-rw-r--r-- | mm/process_vm_access.c | 2 | ||||
-rw-r--r-- | security/commoncap.c | 7 |
11 files changed, 80 insertions, 29 deletions
diff --git a/fs/proc/array.c b/fs/proc/array.c index d73291f5f0fc..b6c00ce0e29e 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c | |||
@@ -395,7 +395,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, | |||
395 | 395 | ||
396 | state = *get_task_state(task); | 396 | state = *get_task_state(task); |
397 | vsize = eip = esp = 0; | 397 | vsize = eip = esp = 0; |
398 | permitted = ptrace_may_access(task, PTRACE_MODE_READ | PTRACE_MODE_NOAUDIT); | 398 | permitted = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT); |
399 | mm = get_task_mm(task); | 399 | mm = get_task_mm(task); |
400 | if (mm) { | 400 | if (mm) { |
401 | vsize = task_vsize(mm); | 401 | vsize = task_vsize(mm); |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 2cf5d7e37375..e665097c1da5 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -403,7 +403,7 @@ static const struct file_operations proc_pid_cmdline_ops = { | |||
403 | static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, | 403 | static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, |
404 | struct pid *pid, struct task_struct *task) | 404 | struct pid *pid, struct task_struct *task) |
405 | { | 405 | { |
406 | struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ); | 406 | struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); |
407 | if (mm && !IS_ERR(mm)) { | 407 | if (mm && !IS_ERR(mm)) { |
408 | unsigned int nwords = 0; | 408 | unsigned int nwords = 0; |
409 | do { | 409 | do { |
@@ -430,7 +430,8 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns, | |||
430 | 430 | ||
431 | wchan = get_wchan(task); | 431 | wchan = get_wchan(task); |
432 | 432 | ||
433 | if (wchan && ptrace_may_access(task, PTRACE_MODE_READ) && !lookup_symbol_name(wchan, symname)) | 433 | if (wchan && ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS) |
434 | && !lookup_symbol_name(wchan, symname)) | ||
434 | seq_printf(m, "%s", symname); | 435 | seq_printf(m, "%s", symname); |
435 | else | 436 | else |
436 | seq_putc(m, '0'); | 437 | seq_putc(m, '0'); |
@@ -444,7 +445,7 @@ static int lock_trace(struct task_struct *task) | |||
444 | int err = mutex_lock_killable(&task->signal->cred_guard_mutex); | 445 | int err = mutex_lock_killable(&task->signal->cred_guard_mutex); |
445 | if (err) | 446 | if (err) |
446 | return err; | 447 | return err; |
447 | if (!ptrace_may_access(task, PTRACE_MODE_ATTACH)) { | 448 | if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_FSCREDS)) { |
448 | mutex_unlock(&task->signal->cred_guard_mutex); | 449 | mutex_unlock(&task->signal->cred_guard_mutex); |
449 | return -EPERM; | 450 | return -EPERM; |
450 | } | 451 | } |
@@ -697,7 +698,7 @@ static int proc_fd_access_allowed(struct inode *inode) | |||
697 | */ | 698 | */ |
698 | task = get_proc_task(inode); | 699 | task = get_proc_task(inode); |
699 | if (task) { | 700 | if (task) { |
700 | allowed = ptrace_may_access(task, PTRACE_MODE_READ); | 701 | allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS); |
701 | put_task_struct(task); | 702 | put_task_struct(task); |
702 | } | 703 | } |
703 | return allowed; | 704 | return allowed; |
@@ -732,7 +733,7 @@ static bool has_pid_permissions(struct pid_namespace *pid, | |||
732 | return true; | 733 | return true; |
733 | if (in_group_p(pid->pid_gid)) | 734 | if (in_group_p(pid->pid_gid)) |
734 | return true; | 735 | return true; |
735 | return ptrace_may_access(task, PTRACE_MODE_READ); | 736 | return ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS); |
736 | } | 737 | } |
737 | 738 | ||
738 | 739 | ||
@@ -809,7 +810,7 @@ struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode) | |||
809 | struct mm_struct *mm = ERR_PTR(-ESRCH); | 810 | struct mm_struct *mm = ERR_PTR(-ESRCH); |
810 | 811 | ||
811 | if (task) { | 812 | if (task) { |
812 | mm = mm_access(task, mode); | 813 | mm = mm_access(task, mode | PTRACE_MODE_FSCREDS); |
813 | put_task_struct(task); | 814 | put_task_struct(task); |
814 | 815 | ||
815 | if (!IS_ERR_OR_NULL(mm)) { | 816 | if (!IS_ERR_OR_NULL(mm)) { |
@@ -1860,7 +1861,7 @@ static int map_files_d_revalidate(struct dentry *dentry, unsigned int flags) | |||
1860 | if (!task) | 1861 | if (!task) |
1861 | goto out_notask; | 1862 | goto out_notask; |
1862 | 1863 | ||
1863 | mm = mm_access(task, PTRACE_MODE_READ); | 1864 | mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); |
1864 | if (IS_ERR_OR_NULL(mm)) | 1865 | if (IS_ERR_OR_NULL(mm)) |
1865 | goto out; | 1866 | goto out; |
1866 | 1867 | ||
@@ -2013,7 +2014,7 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, | |||
2013 | goto out; | 2014 | goto out; |
2014 | 2015 | ||
2015 | result = -EACCES; | 2016 | result = -EACCES; |
2016 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | 2017 | if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) |
2017 | goto out_put_task; | 2018 | goto out_put_task; |
2018 | 2019 | ||
2019 | result = -ENOENT; | 2020 | result = -ENOENT; |
@@ -2066,7 +2067,7 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) | |||
2066 | goto out; | 2067 | goto out; |
2067 | 2068 | ||
2068 | ret = -EACCES; | 2069 | ret = -EACCES; |
2069 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | 2070 | if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) |
2070 | goto out_put_task; | 2071 | goto out_put_task; |
2071 | 2072 | ||
2072 | ret = 0; | 2073 | ret = 0; |
@@ -2533,7 +2534,7 @@ static int do_io_accounting(struct task_struct *task, struct seq_file *m, int wh | |||
2533 | if (result) | 2534 | if (result) |
2534 | return result; | 2535 | return result; |
2535 | 2536 | ||
2536 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) { | 2537 | if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { |
2537 | result = -EACCES; | 2538 | result = -EACCES; |
2538 | goto out_unlock; | 2539 | goto out_unlock; |
2539 | } | 2540 | } |
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 1dece8781f91..276f12431dbf 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c | |||
@@ -46,7 +46,7 @@ static const char *proc_ns_get_link(struct dentry *dentry, | |||
46 | if (!task) | 46 | if (!task) |
47 | return error; | 47 | return error; |
48 | 48 | ||
49 | if (ptrace_may_access(task, PTRACE_MODE_READ)) { | 49 | if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { |
50 | error = ns_get_path(&ns_path, task, ns_ops); | 50 | error = ns_get_path(&ns_path, task, ns_ops); |
51 | if (!error) | 51 | if (!error) |
52 | nd_jump_link(&ns_path); | 52 | nd_jump_link(&ns_path); |
@@ -67,7 +67,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl | |||
67 | if (!task) | 67 | if (!task) |
68 | return res; | 68 | return res; |
69 | 69 | ||
70 | if (ptrace_may_access(task, PTRACE_MODE_READ)) { | 70 | if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { |
71 | res = ns_get_name(name, sizeof(name), task, ns_ops); | 71 | res = ns_get_name(name, sizeof(name), task, ns_ops); |
72 | if (res >= 0) | 72 | if (res >= 0) |
73 | res = readlink_copy(buffer, buflen, name); | 73 | res = readlink_copy(buffer, buflen, name); |
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 061265f92876..504c98a278d4 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h | |||
@@ -57,7 +57,29 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead); | |||
57 | #define PTRACE_MODE_READ 0x01 | 57 | #define PTRACE_MODE_READ 0x01 |
58 | #define PTRACE_MODE_ATTACH 0x02 | 58 | #define PTRACE_MODE_ATTACH 0x02 |
59 | #define PTRACE_MODE_NOAUDIT 0x04 | 59 | #define PTRACE_MODE_NOAUDIT 0x04 |
60 | /* Returns true on success, false on denial. */ | 60 | #define PTRACE_MODE_FSCREDS 0x08 |
61 | #define PTRACE_MODE_REALCREDS 0x10 | ||
62 | |||
63 | /* shorthands for READ/ATTACH and FSCREDS/REALCREDS combinations */ | ||
64 | #define PTRACE_MODE_READ_FSCREDS (PTRACE_MODE_READ | PTRACE_MODE_FSCREDS) | ||
65 | #define PTRACE_MODE_READ_REALCREDS (PTRACE_MODE_READ | PTRACE_MODE_REALCREDS) | ||
66 | #define PTRACE_MODE_ATTACH_FSCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS) | ||
67 | #define PTRACE_MODE_ATTACH_REALCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS) | ||
68 | |||
69 | /** | ||
70 | * ptrace_may_access - check whether the caller is permitted to access | ||
71 | * a target task. | ||
72 | * @task: target task | ||
73 | * @mode: selects type of access and caller credentials | ||
74 | * | ||
75 | * Returns true on success, false on denial. | ||
76 | * | ||
77 | * One of the flags PTRACE_MODE_FSCREDS and PTRACE_MODE_REALCREDS must | ||
78 | * be set in @mode to specify whether the access was requested through | ||
79 | * a filesystem syscall (should use effective capabilities and fsuid | ||
80 | * of the caller) or through an explicit syscall such as | ||
81 | * process_vm_writev or ptrace (and should use the real credentials). | ||
82 | */ | ||
61 | extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); | 83 | extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); |
62 | 84 | ||
63 | static inline int ptrace_reparented(struct task_struct *child) | 85 | static inline int ptrace_reparented(struct task_struct *child) |
diff --git a/kernel/events/core.c b/kernel/events/core.c index bf8244190d0f..c0957416b32e 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -3376,7 +3376,7 @@ find_lively_task_by_vpid(pid_t vpid) | |||
3376 | 3376 | ||
3377 | /* Reuse ptrace permission checks for now. */ | 3377 | /* Reuse ptrace permission checks for now. */ |
3378 | err = -EACCES; | 3378 | err = -EACCES; |
3379 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | 3379 | if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) |
3380 | goto errout; | 3380 | goto errout; |
3381 | 3381 | ||
3382 | return task; | 3382 | return task; |
diff --git a/kernel/futex.c b/kernel/futex.c index c6f514573b28..0773f2b23b10 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
@@ -2884,7 +2884,7 @@ SYSCALL_DEFINE3(get_robust_list, int, pid, | |||
2884 | } | 2884 | } |
2885 | 2885 | ||
2886 | ret = -EPERM; | 2886 | ret = -EPERM; |
2887 | if (!ptrace_may_access(p, PTRACE_MODE_READ)) | 2887 | if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS)) |
2888 | goto err_unlock; | 2888 | goto err_unlock; |
2889 | 2889 | ||
2890 | head = p->robust_list; | 2890 | head = p->robust_list; |
diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c index 55c8c9349cfe..4ae3232e7a28 100644 --- a/kernel/futex_compat.c +++ b/kernel/futex_compat.c | |||
@@ -155,7 +155,7 @@ COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid, | |||
155 | } | 155 | } |
156 | 156 | ||
157 | ret = -EPERM; | 157 | ret = -EPERM; |
158 | if (!ptrace_may_access(p, PTRACE_MODE_READ)) | 158 | if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS)) |
159 | goto err_unlock; | 159 | goto err_unlock; |
160 | 160 | ||
161 | head = p->compat_robust_list; | 161 | head = p->compat_robust_list; |
diff --git a/kernel/kcmp.c b/kernel/kcmp.c index 0aa69ea1d8fd..3a47fa998fe0 100644 --- a/kernel/kcmp.c +++ b/kernel/kcmp.c | |||
@@ -122,8 +122,8 @@ SYSCALL_DEFINE5(kcmp, pid_t, pid1, pid_t, pid2, int, type, | |||
122 | &task2->signal->cred_guard_mutex); | 122 | &task2->signal->cred_guard_mutex); |
123 | if (ret) | 123 | if (ret) |
124 | goto err; | 124 | goto err; |
125 | if (!ptrace_may_access(task1, PTRACE_MODE_READ) || | 125 | if (!ptrace_may_access(task1, PTRACE_MODE_READ_REALCREDS) || |
126 | !ptrace_may_access(task2, PTRACE_MODE_READ)) { | 126 | !ptrace_may_access(task2, PTRACE_MODE_READ_REALCREDS)) { |
127 | ret = -EPERM; | 127 | ret = -EPERM; |
128 | goto err_unlock; | 128 | goto err_unlock; |
129 | } | 129 | } |
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index aa94aee9d4c9..2341efe7fe02 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -219,6 +219,14 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) | |||
219 | static int __ptrace_may_access(struct task_struct *task, unsigned int mode) | 219 | static int __ptrace_may_access(struct task_struct *task, unsigned int mode) |
220 | { | 220 | { |
221 | const struct cred *cred = current_cred(), *tcred; | 221 | const struct cred *cred = current_cred(), *tcred; |
222 | int dumpable = 0; | ||
223 | kuid_t caller_uid; | ||
224 | kgid_t caller_gid; | ||
225 | |||
226 | if (!(mode & PTRACE_MODE_FSCREDS) == !(mode & PTRACE_MODE_REALCREDS)) { | ||
227 | WARN(1, "denying ptrace access check without PTRACE_MODE_*CREDS\n"); | ||
228 | return -EPERM; | ||
229 | } | ||
222 | 230 | ||
223 | /* May we inspect the given task? | 231 | /* May we inspect the given task? |
224 | * This check is used both for attaching with ptrace | 232 | * This check is used both for attaching with ptrace |
@@ -228,18 +236,33 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
228 | * because setting up the necessary parent/child relationship | 236 | * because setting up the necessary parent/child relationship |
229 | * or halting the specified task is impossible. | 237 | * or halting the specified task is impossible. |
230 | */ | 238 | */ |
231 | int dumpable = 0; | 239 | |
232 | /* Don't let security modules deny introspection */ | 240 | /* Don't let security modules deny introspection */ |
233 | if (same_thread_group(task, current)) | 241 | if (same_thread_group(task, current)) |
234 | return 0; | 242 | return 0; |
235 | rcu_read_lock(); | 243 | rcu_read_lock(); |
244 | if (mode & PTRACE_MODE_FSCREDS) { | ||
245 | caller_uid = cred->fsuid; | ||
246 | caller_gid = cred->fsgid; | ||
247 | } else { | ||
248 | /* | ||
249 | * Using the euid would make more sense here, but something | ||
250 | * in userland might rely on the old behavior, and this | ||
251 | * shouldn't be a security problem since | ||
252 | * PTRACE_MODE_REALCREDS implies that the caller explicitly | ||
253 | * used a syscall that requests access to another process | ||
254 | * (and not a filesystem syscall to procfs). | ||
255 | */ | ||
256 | caller_uid = cred->uid; | ||
257 | caller_gid = cred->gid; | ||
258 | } | ||
236 | tcred = __task_cred(task); | 259 | tcred = __task_cred(task); |
237 | if (uid_eq(cred->uid, tcred->euid) && | 260 | if (uid_eq(caller_uid, tcred->euid) && |
238 | uid_eq(cred->uid, tcred->suid) && | 261 | uid_eq(caller_uid, tcred->suid) && |
239 | uid_eq(cred->uid, tcred->uid) && | 262 | uid_eq(caller_uid, tcred->uid) && |
240 | gid_eq(cred->gid, tcred->egid) && | 263 | gid_eq(caller_gid, tcred->egid) && |
241 | gid_eq(cred->gid, tcred->sgid) && | 264 | gid_eq(caller_gid, tcred->sgid) && |
242 | gid_eq(cred->gid, tcred->gid)) | 265 | gid_eq(caller_gid, tcred->gid)) |
243 | goto ok; | 266 | goto ok; |
244 | if (ptrace_has_cap(tcred->user_ns, mode)) | 267 | if (ptrace_has_cap(tcred->user_ns, mode)) |
245 | goto ok; | 268 | goto ok; |
@@ -306,7 +329,7 @@ static int ptrace_attach(struct task_struct *task, long request, | |||
306 | goto out; | 329 | goto out; |
307 | 330 | ||
308 | task_lock(task); | 331 | task_lock(task); |
309 | retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH); | 332 | retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS); |
310 | task_unlock(task); | 333 | task_unlock(task); |
311 | if (retval) | 334 | if (retval) |
312 | goto unlock_creds; | 335 | goto unlock_creds; |
diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c index e88d071648c2..5d453e58ddbf 100644 --- a/mm/process_vm_access.c +++ b/mm/process_vm_access.c | |||
@@ -194,7 +194,7 @@ static ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter, | |||
194 | goto free_proc_pages; | 194 | goto free_proc_pages; |
195 | } | 195 | } |
196 | 196 | ||
197 | mm = mm_access(task, PTRACE_MODE_ATTACH); | 197 | mm = mm_access(task, PTRACE_MODE_ATTACH_REALCREDS); |
198 | if (!mm || IS_ERR(mm)) { | 198 | if (!mm || IS_ERR(mm)) { |
199 | rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; | 199 | rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; |
200 | /* | 200 | /* |
diff --git a/security/commoncap.c b/security/commoncap.c index 1832cf701c3d..48071ed7c445 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -137,12 +137,17 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode) | |||
137 | { | 137 | { |
138 | int ret = 0; | 138 | int ret = 0; |
139 | const struct cred *cred, *child_cred; | 139 | const struct cred *cred, *child_cred; |
140 | const kernel_cap_t *caller_caps; | ||
140 | 141 | ||
141 | rcu_read_lock(); | 142 | rcu_read_lock(); |
142 | cred = current_cred(); | 143 | cred = current_cred(); |
143 | child_cred = __task_cred(child); | 144 | child_cred = __task_cred(child); |
145 | if (mode & PTRACE_MODE_FSCREDS) | ||
146 | caller_caps = &cred->cap_effective; | ||
147 | else | ||
148 | caller_caps = &cred->cap_permitted; | ||
144 | if (cred->user_ns == child_cred->user_ns && | 149 | if (cred->user_ns == child_cred->user_ns && |
145 | cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) | 150 | cap_issubset(child_cred->cap_permitted, *caller_caps)) |
146 | goto out; | 151 | goto out; |
147 | if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE)) | 152 | if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE)) |
148 | goto out; | 153 | goto out; |