]>
Commit | Line | Data |
---|---|---|
b732247b GKH |
1 | From 64b875f7ac8a5d60a4e191479299e931ee949b67 Mon Sep 17 00:00:00 2001 |
2 | From: "Eric W. Biederman" <ebiederm@xmission.com> | |
3 | Date: Mon, 14 Nov 2016 18:48:07 -0600 | |
4 | Subject: ptrace: Capture the ptracer's creds not PT_PTRACE_CAP | |
5 | ||
6 | From: Eric W. Biederman <ebiederm@xmission.com> | |
7 | ||
8 | commit 64b875f7ac8a5d60a4e191479299e931ee949b67 upstream. | |
9 | ||
10 | When the flag PT_PTRACE_CAP was added the PTRACE_TRACEME path was | |
11 | overlooked. This can result in incorrect behavior when an application | |
12 | like strace traces an exec of a setuid executable. | |
13 | ||
14 | Further PT_PTRACE_CAP does not have enough information for making good | |
15 | security decisions as it does not report which user namespace the | |
16 | capability is in. This has already allowed one mistake through | |
17 | insufficient granulariy. | |
18 | ||
19 | I found this issue when I was testing another corner case of exec and | |
20 | discovered that I could not get strace to set PT_PTRACE_CAP even when | |
21 | running strace as root with a full set of caps. | |
22 | ||
23 | This change fixes the above issue with strace allowing stracing as | |
24 | root a setuid executable without disabling setuid. More fundamentaly | |
25 | this change allows what is allowable at all times, by using the correct | |
26 | information in it's decision. | |
27 | ||
28 | Fixes: 4214e42f96d4 ("v2.4.9.11 -> v2.4.9.12") | |
29 | Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> | |
30 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
31 | ||
32 | --- | |
33 | fs/exec.c | 2 +- | |
34 | include/linux/capability.h | 1 + | |
35 | include/linux/ptrace.h | 1 - | |
36 | include/linux/sched.h | 1 + | |
37 | kernel/capability.c | 20 ++++++++++++++++++++ | |
38 | kernel/ptrace.c | 12 +++++++----- | |
39 | 6 files changed, 30 insertions(+), 7 deletions(-) | |
40 | ||
41 | --- a/fs/exec.c | |
42 | +++ b/fs/exec.c | |
43 | @@ -1420,7 +1420,7 @@ static void check_unsafe_exec(struct lin | |
44 | unsigned n_fs; | |
45 | ||
46 | if (p->ptrace) { | |
47 | - if (p->ptrace & PT_PTRACE_CAP) | |
48 | + if (ptracer_capable(p, current_user_ns())) | |
49 | bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP; | |
50 | else | |
51 | bprm->unsafe |= LSM_UNSAFE_PTRACE; | |
52 | --- a/include/linux/capability.h | |
53 | +++ b/include/linux/capability.h | |
54 | @@ -243,6 +243,7 @@ static inline bool ns_capable_noaudit(st | |
55 | extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode); | |
56 | extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); | |
57 | extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); | |
58 | +extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns); | |
59 | ||
60 | /* audit system wants to get cap info from files as well */ | |
61 | extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); | |
62 | --- a/include/linux/ptrace.h | |
63 | +++ b/include/linux/ptrace.h | |
64 | @@ -19,7 +19,6 @@ | |
65 | #define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */ | |
66 | #define PT_PTRACED 0x00000001 | |
67 | #define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ | |
68 | -#define PT_PTRACE_CAP 0x00000004 /* ptracer can follow suid-exec */ | |
69 | ||
70 | #define PT_OPT_FLAG_SHIFT 3 | |
71 | /* PT_TRACE_* event enable flags */ | |
72 | --- a/include/linux/sched.h | |
73 | +++ b/include/linux/sched.h | |
74 | @@ -1631,6 +1631,7 @@ struct task_struct { | |
75 | struct list_head cpu_timers[3]; | |
76 | ||
77 | /* process credentials */ | |
78 | + const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */ | |
79 | const struct cred __rcu *real_cred; /* objective and real subjective task | |
80 | * credentials (COW) */ | |
81 | const struct cred __rcu *cred; /* effective (overridable) subjective task | |
82 | --- a/kernel/capability.c | |
83 | +++ b/kernel/capability.c | |
84 | @@ -485,3 +485,23 @@ bool capable_wrt_inode_uidgid(const stru | |
85 | return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode); | |
86 | } | |
87 | EXPORT_SYMBOL(capable_wrt_inode_uidgid); | |
88 | + | |
89 | +/** | |
90 | + * ptracer_capable - Determine if the ptracer holds CAP_SYS_PTRACE in the namespace | |
91 | + * @tsk: The task that may be ptraced | |
92 | + * @ns: The user namespace to search for CAP_SYS_PTRACE in | |
93 | + * | |
94 | + * Return true if the task that is ptracing the current task had CAP_SYS_PTRACE | |
95 | + * in the specified user namespace. | |
96 | + */ | |
97 | +bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns) | |
98 | +{ | |
99 | + int ret = 0; /* An absent tracer adds no restrictions */ | |
100 | + const struct cred *cred; | |
101 | + rcu_read_lock(); | |
102 | + cred = rcu_dereference(tsk->ptracer_cred); | |
103 | + if (cred) | |
104 | + ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE); | |
105 | + rcu_read_unlock(); | |
106 | + return (ret == 0); | |
107 | +} | |
108 | --- a/kernel/ptrace.c | |
109 | +++ b/kernel/ptrace.c | |
110 | @@ -39,6 +39,9 @@ void __ptrace_link(struct task_struct *c | |
111 | BUG_ON(!list_empty(&child->ptrace_entry)); | |
112 | list_add(&child->ptrace_entry, &new_parent->ptraced); | |
113 | child->parent = new_parent; | |
114 | + rcu_read_lock(); | |
115 | + child->ptracer_cred = get_cred(__task_cred(new_parent)); | |
116 | + rcu_read_unlock(); | |
117 | } | |
118 | ||
119 | /** | |
120 | @@ -71,10 +74,14 @@ void __ptrace_link(struct task_struct *c | |
121 | */ | |
122 | void __ptrace_unlink(struct task_struct *child) | |
123 | { | |
124 | + const struct cred *old_cred; | |
125 | BUG_ON(!child->ptrace); | |
126 | ||
127 | child->parent = child->real_parent; | |
128 | list_del_init(&child->ptrace_entry); | |
129 | + old_cred = child->ptracer_cred; | |
130 | + child->ptracer_cred = NULL; | |
131 | + put_cred(old_cred); | |
132 | ||
133 | spin_lock(&child->sighand->siglock); | |
134 | child->ptrace = 0; | |
135 | @@ -324,11 +331,6 @@ static int ptrace_attach(struct task_str | |
136 | ||
137 | task_lock(task); | |
138 | retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS); | |
139 | - if (!retval) { | |
140 | - struct mm_struct *mm = task->mm; | |
141 | - if (mm && ns_capable(mm->user_ns, CAP_SYS_PTRACE)) | |
142 | - flags |= PT_PTRACE_CAP; | |
143 | - } | |
144 | task_unlock(task); | |
145 | if (retval) | |
146 | goto unlock_creds; |