Commit | Line | Data |
---|---|---|
2d514487 KC |
1 | /* |
2 | * Yama Linux Security Module | |
3 | * | |
4 | * Author: Kees Cook <keescook@chromium.org> | |
5 | * | |
6 | * Copyright (C) 2010 Canonical, Ltd. | |
7 | * Copyright (C) 2011 The Chromium OS Authors. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2, as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include <linux/security.h> | |
16 | #include <linux/sysctl.h> | |
17 | #include <linux/ptrace.h> | |
18 | #include <linux/prctl.h> | |
19 | #include <linux/ratelimit.h> | |
20 | ||
389da25f KC |
21 | #define YAMA_SCOPE_DISABLED 0 |
22 | #define YAMA_SCOPE_RELATIONAL 1 | |
23 | #define YAMA_SCOPE_CAPABILITY 2 | |
24 | #define YAMA_SCOPE_NO_ATTACH 3 | |
25 | ||
26 | static int ptrace_scope = YAMA_SCOPE_RELATIONAL; | |
2d514487 KC |
27 | |
28 | /* describe a ptrace relationship for potential exception */ | |
29 | struct ptrace_relation { | |
30 | struct task_struct *tracer; | |
31 | struct task_struct *tracee; | |
32 | struct list_head node; | |
33 | }; | |
34 | ||
35 | static LIST_HEAD(ptracer_relations); | |
36 | static DEFINE_SPINLOCK(ptracer_relations_lock); | |
37 | ||
38 | /** | |
39 | * yama_ptracer_add - add/replace an exception for this tracer/tracee pair | |
40 | * @tracer: the task_struct of the process doing the ptrace | |
41 | * @tracee: the task_struct of the process to be ptraced | |
42 | * | |
43 | * Each tracee can have, at most, one tracer registered. Each time this | |
44 | * is called, the prior registered tracer will be replaced for the tracee. | |
45 | * | |
46 | * Returns 0 if relationship was added, -ve on error. | |
47 | */ | |
48 | static int yama_ptracer_add(struct task_struct *tracer, | |
49 | struct task_struct *tracee) | |
50 | { | |
51 | int rc = 0; | |
52 | struct ptrace_relation *added; | |
53 | struct ptrace_relation *entry, *relation = NULL; | |
54 | ||
55 | added = kmalloc(sizeof(*added), GFP_KERNEL); | |
56 | if (!added) | |
57 | return -ENOMEM; | |
58 | ||
59 | spin_lock_bh(&ptracer_relations_lock); | |
60 | list_for_each_entry(entry, &ptracer_relations, node) | |
61 | if (entry->tracee == tracee) { | |
62 | relation = entry; | |
63 | break; | |
64 | } | |
65 | if (!relation) { | |
66 | relation = added; | |
67 | relation->tracee = tracee; | |
68 | list_add(&relation->node, &ptracer_relations); | |
69 | } | |
70 | relation->tracer = tracer; | |
71 | ||
72 | spin_unlock_bh(&ptracer_relations_lock); | |
73 | if (added != relation) | |
74 | kfree(added); | |
75 | ||
76 | return rc; | |
77 | } | |
78 | ||
79 | /** | |
80 | * yama_ptracer_del - remove exceptions related to the given tasks | |
81 | * @tracer: remove any relation where tracer task matches | |
82 | * @tracee: remove any relation where tracee task matches | |
83 | */ | |
84 | static void yama_ptracer_del(struct task_struct *tracer, | |
85 | struct task_struct *tracee) | |
86 | { | |
87 | struct ptrace_relation *relation, *safe; | |
88 | ||
89 | spin_lock_bh(&ptracer_relations_lock); | |
90 | list_for_each_entry_safe(relation, safe, &ptracer_relations, node) | |
91 | if (relation->tracee == tracee || | |
bf06189e | 92 | (tracer && relation->tracer == tracer)) { |
2d514487 KC |
93 | list_del(&relation->node); |
94 | kfree(relation); | |
95 | } | |
96 | spin_unlock_bh(&ptracer_relations_lock); | |
97 | } | |
98 | ||
99 | /** | |
100 | * yama_task_free - check for task_pid to remove from exception list | |
101 | * @task: task being removed | |
102 | */ | |
103 | static void yama_task_free(struct task_struct *task) | |
104 | { | |
105 | yama_ptracer_del(task, task); | |
106 | } | |
107 | ||
108 | /** | |
109 | * yama_task_prctl - check for Yama-specific prctl operations | |
110 | * @option: operation | |
111 | * @arg2: argument | |
112 | * @arg3: argument | |
113 | * @arg4: argument | |
114 | * @arg5: argument | |
115 | * | |
116 | * Return 0 on success, -ve on error. -ENOSYS is returned when Yama | |
117 | * does not handle the given option. | |
118 | */ | |
119 | static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, | |
120 | unsigned long arg4, unsigned long arg5) | |
121 | { | |
122 | int rc; | |
123 | struct task_struct *myself = current; | |
124 | ||
125 | rc = cap_task_prctl(option, arg2, arg3, arg4, arg5); | |
126 | if (rc != -ENOSYS) | |
127 | return rc; | |
128 | ||
129 | switch (option) { | |
130 | case PR_SET_PTRACER: | |
131 | /* Since a thread can call prctl(), find the group leader | |
132 | * before calling _add() or _del() on it, since we want | |
133 | * process-level granularity of control. The tracer group | |
134 | * leader checking is handled later when walking the ancestry | |
135 | * at the time of PTRACE_ATTACH check. | |
136 | */ | |
137 | rcu_read_lock(); | |
138 | if (!thread_group_leader(myself)) | |
139 | myself = rcu_dereference(myself->group_leader); | |
140 | get_task_struct(myself); | |
141 | rcu_read_unlock(); | |
142 | ||
143 | if (arg2 == 0) { | |
144 | yama_ptracer_del(NULL, myself); | |
145 | rc = 0; | |
bf06189e KC |
146 | } else if (arg2 == PR_SET_PTRACER_ANY) { |
147 | rc = yama_ptracer_add(NULL, myself); | |
2d514487 KC |
148 | } else { |
149 | struct task_struct *tracer; | |
150 | ||
151 | rcu_read_lock(); | |
152 | tracer = find_task_by_vpid(arg2); | |
153 | if (tracer) | |
154 | get_task_struct(tracer); | |
155 | else | |
156 | rc = -EINVAL; | |
157 | rcu_read_unlock(); | |
158 | ||
159 | if (tracer) { | |
160 | rc = yama_ptracer_add(tracer, myself); | |
161 | put_task_struct(tracer); | |
162 | } | |
163 | } | |
164 | ||
165 | put_task_struct(myself); | |
166 | break; | |
167 | } | |
168 | ||
169 | return rc; | |
170 | } | |
171 | ||
172 | /** | |
173 | * task_is_descendant - walk up a process family tree looking for a match | |
174 | * @parent: the process to compare against while walking up from child | |
175 | * @child: the process to start from while looking upwards for parent | |
176 | * | |
177 | * Returns 1 if child is a descendant of parent, 0 if not. | |
178 | */ | |
179 | static int task_is_descendant(struct task_struct *parent, | |
180 | struct task_struct *child) | |
181 | { | |
182 | int rc = 0; | |
183 | struct task_struct *walker = child; | |
184 | ||
185 | if (!parent || !child) | |
186 | return 0; | |
187 | ||
188 | rcu_read_lock(); | |
189 | if (!thread_group_leader(parent)) | |
190 | parent = rcu_dereference(parent->group_leader); | |
191 | while (walker->pid > 0) { | |
192 | if (!thread_group_leader(walker)) | |
193 | walker = rcu_dereference(walker->group_leader); | |
194 | if (walker == parent) { | |
195 | rc = 1; | |
196 | break; | |
197 | } | |
198 | walker = rcu_dereference(walker->real_parent); | |
199 | } | |
200 | rcu_read_unlock(); | |
201 | ||
202 | return rc; | |
203 | } | |
204 | ||
205 | /** | |
206 | * ptracer_exception_found - tracer registered as exception for this tracee | |
207 | * @tracer: the task_struct of the process attempting ptrace | |
208 | * @tracee: the task_struct of the process to be ptraced | |
209 | * | |
210 | * Returns 1 if tracer has is ptracer exception ancestor for tracee. | |
211 | */ | |
212 | static int ptracer_exception_found(struct task_struct *tracer, | |
213 | struct task_struct *tracee) | |
214 | { | |
215 | int rc = 0; | |
216 | struct ptrace_relation *relation; | |
217 | struct task_struct *parent = NULL; | |
bf06189e | 218 | bool found = false; |
2d514487 KC |
219 | |
220 | spin_lock_bh(&ptracer_relations_lock); | |
221 | rcu_read_lock(); | |
222 | if (!thread_group_leader(tracee)) | |
223 | tracee = rcu_dereference(tracee->group_leader); | |
224 | list_for_each_entry(relation, &ptracer_relations, node) | |
225 | if (relation->tracee == tracee) { | |
226 | parent = relation->tracer; | |
bf06189e | 227 | found = true; |
2d514487 KC |
228 | break; |
229 | } | |
230 | ||
bf06189e | 231 | if (found && (parent == NULL || task_is_descendant(parent, tracer))) |
2d514487 KC |
232 | rc = 1; |
233 | rcu_read_unlock(); | |
234 | spin_unlock_bh(&ptracer_relations_lock); | |
235 | ||
236 | return rc; | |
237 | } | |
238 | ||
239 | /** | |
240 | * yama_ptrace_access_check - validate PTRACE_ATTACH calls | |
241 | * @child: task that current task is attempting to ptrace | |
242 | * @mode: ptrace attach mode | |
243 | * | |
244 | * Returns 0 if following the ptrace is allowed, -ve on error. | |
245 | */ | |
246 | static int yama_ptrace_access_check(struct task_struct *child, | |
247 | unsigned int mode) | |
248 | { | |
249 | int rc; | |
250 | ||
251 | /* If standard caps disallows it, so does Yama. We should | |
252 | * only tighten restrictions further. | |
253 | */ | |
254 | rc = cap_ptrace_access_check(child, mode); | |
255 | if (rc) | |
256 | return rc; | |
257 | ||
258 | /* require ptrace target be a child of ptracer on attach */ | |
389da25f KC |
259 | if (mode == PTRACE_MODE_ATTACH) { |
260 | switch (ptrace_scope) { | |
261 | case YAMA_SCOPE_DISABLED: | |
262 | /* No additional restrictions. */ | |
263 | break; | |
264 | case YAMA_SCOPE_RELATIONAL: | |
265 | if (!task_is_descendant(current, child) && | |
266 | !ptracer_exception_found(current, child) && | |
2cc8a716 | 267 | !ns_capable(task_user_ns(child), CAP_SYS_PTRACE)) |
389da25f KC |
268 | rc = -EPERM; |
269 | break; | |
270 | case YAMA_SCOPE_CAPABILITY: | |
2cc8a716 | 271 | if (!ns_capable(task_user_ns(child), CAP_SYS_PTRACE)) |
389da25f KC |
272 | rc = -EPERM; |
273 | break; | |
274 | case YAMA_SCOPE_NO_ATTACH: | |
275 | default: | |
276 | rc = -EPERM; | |
277 | break; | |
278 | } | |
279 | } | |
2d514487 KC |
280 | |
281 | if (rc) { | |
389da25f KC |
282 | printk_ratelimited(KERN_NOTICE |
283 | "ptrace of pid %d was attempted by: %s (pid %d)\n", | |
7612bfee | 284 | child->pid, current->comm, current->pid); |
2d514487 KC |
285 | } |
286 | ||
287 | return rc; | |
288 | } | |
289 | ||
9d8dad74 KC |
290 | /** |
291 | * yama_ptrace_traceme - validate PTRACE_TRACEME calls | |
292 | * @parent: task that will become the ptracer of the current task | |
293 | * | |
294 | * Returns 0 if following the ptrace is allowed, -ve on error. | |
295 | */ | |
296 | static int yama_ptrace_traceme(struct task_struct *parent) | |
297 | { | |
298 | int rc; | |
299 | ||
300 | /* If standard caps disallows it, so does Yama. We should | |
301 | * only tighten restrictions further. | |
302 | */ | |
303 | rc = cap_ptrace_traceme(parent); | |
304 | if (rc) | |
305 | return rc; | |
306 | ||
307 | /* Only disallow PTRACE_TRACEME on more aggressive settings. */ | |
308 | switch (ptrace_scope) { | |
309 | case YAMA_SCOPE_CAPABILITY: | |
310 | if (!ns_capable(task_user_ns(parent), CAP_SYS_PTRACE)) | |
311 | rc = -EPERM; | |
312 | break; | |
313 | case YAMA_SCOPE_NO_ATTACH: | |
314 | rc = -EPERM; | |
315 | break; | |
316 | } | |
317 | ||
318 | if (rc) { | |
9d8dad74 KC |
319 | printk_ratelimited(KERN_NOTICE |
320 | "ptraceme of pid %d was attempted by: %s (pid %d)\n", | |
7612bfee | 321 | current->pid, parent->comm, parent->pid); |
9d8dad74 KC |
322 | } |
323 | ||
324 | return rc; | |
325 | } | |
326 | ||
2d514487 KC |
327 | static struct security_operations yama_ops = { |
328 | .name = "yama", | |
329 | ||
330 | .ptrace_access_check = yama_ptrace_access_check, | |
9d8dad74 | 331 | .ptrace_traceme = yama_ptrace_traceme, |
2d514487 KC |
332 | .task_prctl = yama_task_prctl, |
333 | .task_free = yama_task_free, | |
334 | }; | |
335 | ||
336 | #ifdef CONFIG_SYSCTL | |
389da25f KC |
337 | static int yama_dointvec_minmax(struct ctl_table *table, int write, |
338 | void __user *buffer, size_t *lenp, loff_t *ppos) | |
339 | { | |
340 | int rc; | |
341 | ||
342 | if (write && !capable(CAP_SYS_PTRACE)) | |
343 | return -EPERM; | |
344 | ||
345 | rc = proc_dointvec_minmax(table, write, buffer, lenp, ppos); | |
346 | if (rc) | |
347 | return rc; | |
348 | ||
349 | /* Lock the max value if it ever gets set. */ | |
350 | if (write && *(int *)table->data == *(int *)table->extra2) | |
351 | table->extra1 = table->extra2; | |
352 | ||
353 | return rc; | |
354 | } | |
355 | ||
2d514487 | 356 | static int zero; |
389da25f | 357 | static int max_scope = YAMA_SCOPE_NO_ATTACH; |
2d514487 KC |
358 | |
359 | struct ctl_path yama_sysctl_path[] = { | |
360 | { .procname = "kernel", }, | |
361 | { .procname = "yama", }, | |
362 | { } | |
363 | }; | |
364 | ||
365 | static struct ctl_table yama_sysctl_table[] = { | |
366 | { | |
367 | .procname = "ptrace_scope", | |
368 | .data = &ptrace_scope, | |
369 | .maxlen = sizeof(int), | |
370 | .mode = 0644, | |
389da25f | 371 | .proc_handler = yama_dointvec_minmax, |
2d514487 | 372 | .extra1 = &zero, |
389da25f | 373 | .extra2 = &max_scope, |
2d514487 KC |
374 | }, |
375 | { } | |
376 | }; | |
377 | #endif /* CONFIG_SYSCTL */ | |
378 | ||
379 | static __init int yama_init(void) | |
380 | { | |
381 | if (!security_module_enable(&yama_ops)) | |
382 | return 0; | |
383 | ||
384 | printk(KERN_INFO "Yama: becoming mindful.\n"); | |
385 | ||
386 | if (register_security(&yama_ops)) | |
387 | panic("Yama: kernel registration failed.\n"); | |
388 | ||
389 | #ifdef CONFIG_SYSCTL | |
390 | if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table)) | |
391 | panic("Yama: sysctl registration failed.\n"); | |
392 | #endif | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
397 | security_initcall(yama_init); |