aboutsummaryrefslogtreecommitdiffstats
path: root/security/yama
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2011-12-21 15:17:04 -0500
committerJames Morris <jmorris@namei.org>2012-02-09 17:18:52 -0500
commit2d514487faf188938a4ee4fb3464eeecfbdcf8eb (patch)
tree42147f0459ab062375f63891943242e3b95797bb /security/yama
parent1a2a4d06e1e95260c470ebe3a945f61bbe8c1fd8 (diff)
security: Yama LSM
This adds the Yama Linux Security Module to collect DAC security improvements (specifically just ptrace restrictions for now) that have existed in various forms over the years and have been carried outside the mainline kernel by other Linux distributions like Openwall and grsecurity. Signed-off-by: Kees Cook <keescook@chromium.org> Acked-by: John Johansen <john.johansen@canonical.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/yama')
-rw-r--r--security/yama/Kconfig13
-rw-r--r--security/yama/Makefile3
-rw-r--r--security/yama/yama_lsm.c319
3 files changed, 335 insertions, 0 deletions
diff --git a/security/yama/Kconfig b/security/yama/Kconfig
new file mode 100644
index 000000000000..51d6709d8bbd
--- /dev/null
+++ b/security/yama/Kconfig
@@ -0,0 +1,13 @@
1config SECURITY_YAMA
2 bool "Yama support"
3 depends on SECURITY
4 select SECURITYFS
5 select SECURITY_PATH
6 default n
7 help
8 This selects Yama, which extends DAC support with additional
9 system-wide security settings beyond regular Linux discretionary
10 access controls. Currently available is ptrace scope restriction.
11 Further information can be found in Documentation/security/Yama.txt.
12
13 If you are unsure how to answer this question, answer N.
diff --git a/security/yama/Makefile b/security/yama/Makefile
new file mode 100644
index 000000000000..8b5e06588456
--- /dev/null
+++ b/security/yama/Makefile
@@ -0,0 +1,3 @@
1obj-$(CONFIG_SECURITY_YAMA) := yama.o
2
3yama-y := yama_lsm.o
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
new file mode 100644
index 000000000000..dd4d36067c50
--- /dev/null
+++ b/security/yama/yama_lsm.c
@@ -0,0 +1,319 @@
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
21static int ptrace_scope = 1;
22
23/* describe a ptrace relationship for potential exception */
24struct ptrace_relation {
25 struct task_struct *tracer;
26 struct task_struct *tracee;
27 struct list_head node;
28};
29
30static LIST_HEAD(ptracer_relations);
31static DEFINE_SPINLOCK(ptracer_relations_lock);
32
33/**
34 * yama_ptracer_add - add/replace an exception for this tracer/tracee pair
35 * @tracer: the task_struct of the process doing the ptrace
36 * @tracee: the task_struct of the process to be ptraced
37 *
38 * Each tracee can have, at most, one tracer registered. Each time this
39 * is called, the prior registered tracer will be replaced for the tracee.
40 *
41 * Returns 0 if relationship was added, -ve on error.
42 */
43static int yama_ptracer_add(struct task_struct *tracer,
44 struct task_struct *tracee)
45{
46 int rc = 0;
47 struct ptrace_relation *added;
48 struct ptrace_relation *entry, *relation = NULL;
49
50 added = kmalloc(sizeof(*added), GFP_KERNEL);
51 if (!added)
52 return -ENOMEM;
53
54 spin_lock_bh(&ptracer_relations_lock);
55 list_for_each_entry(entry, &ptracer_relations, node)
56 if (entry->tracee == tracee) {
57 relation = entry;
58 break;
59 }
60 if (!relation) {
61 relation = added;
62 relation->tracee = tracee;
63 list_add(&relation->node, &ptracer_relations);
64 }
65 relation->tracer = tracer;
66
67 spin_unlock_bh(&ptracer_relations_lock);
68 if (added != relation)
69 kfree(added);
70
71 return rc;
72}
73
74/**
75 * yama_ptracer_del - remove exceptions related to the given tasks
76 * @tracer: remove any relation where tracer task matches
77 * @tracee: remove any relation where tracee task matches
78 */
79static void yama_ptracer_del(struct task_struct *tracer,
80 struct task_struct *tracee)
81{
82 struct ptrace_relation *relation, *safe;
83
84 spin_lock_bh(&ptracer_relations_lock);
85 list_for_each_entry_safe(relation, safe, &ptracer_relations, node)
86 if (relation->tracee == tracee ||
87 relation->tracer == tracer) {
88 list_del(&relation->node);
89 kfree(relation);
90 }
91 spin_unlock_bh(&ptracer_relations_lock);
92}
93
94/**
95 * yama_task_free - check for task_pid to remove from exception list
96 * @task: task being removed
97 */
98static void yama_task_free(struct task_struct *task)
99{
100 yama_ptracer_del(task, task);
101}
102
103/**
104 * yama_task_prctl - check for Yama-specific prctl operations
105 * @option: operation
106 * @arg2: argument
107 * @arg3: argument
108 * @arg4: argument
109 * @arg5: argument
110 *
111 * Return 0 on success, -ve on error. -ENOSYS is returned when Yama
112 * does not handle the given option.
113 */
114static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
115 unsigned long arg4, unsigned long arg5)
116{
117 int rc;
118 struct task_struct *myself = current;
119
120 rc = cap_task_prctl(option, arg2, arg3, arg4, arg5);
121 if (rc != -ENOSYS)
122 return rc;
123
124 switch (option) {
125 case PR_SET_PTRACER:
126 /* Since a thread can call prctl(), find the group leader
127 * before calling _add() or _del() on it, since we want
128 * process-level granularity of control. The tracer group
129 * leader checking is handled later when walking the ancestry
130 * at the time of PTRACE_ATTACH check.
131 */
132 rcu_read_lock();
133 if (!thread_group_leader(myself))
134 myself = rcu_dereference(myself->group_leader);
135 get_task_struct(myself);
136 rcu_read_unlock();
137
138 if (arg2 == 0) {
139 yama_ptracer_del(NULL, myself);
140 rc = 0;
141 } else {
142 struct task_struct *tracer;
143
144 rcu_read_lock();
145 tracer = find_task_by_vpid(arg2);
146 if (tracer)
147 get_task_struct(tracer);
148 else
149 rc = -EINVAL;
150 rcu_read_unlock();
151
152 if (tracer) {
153 rc = yama_ptracer_add(tracer, myself);
154 put_task_struct(tracer);
155 }
156 }
157
158 put_task_struct(myself);
159 break;
160 }
161
162 return rc;
163}
164
165/**
166 * task_is_descendant - walk up a process family tree looking for a match
167 * @parent: the process to compare against while walking up from child
168 * @child: the process to start from while looking upwards for parent
169 *
170 * Returns 1 if child is a descendant of parent, 0 if not.
171 */
172static int task_is_descendant(struct task_struct *parent,
173 struct task_struct *child)
174{
175 int rc = 0;
176 struct task_struct *walker = child;
177
178 if (!parent || !child)
179 return 0;
180
181 rcu_read_lock();
182 if (!thread_group_leader(parent))
183 parent = rcu_dereference(parent->group_leader);
184 while (walker->pid > 0) {
185 if (!thread_group_leader(walker))
186 walker = rcu_dereference(walker->group_leader);
187 if (walker == parent) {
188 rc = 1;
189 break;
190 }
191 walker = rcu_dereference(walker->real_parent);
192 }
193 rcu_read_unlock();
194
195 return rc;
196}
197
198/**
199 * ptracer_exception_found - tracer registered as exception for this tracee
200 * @tracer: the task_struct of the process attempting ptrace
201 * @tracee: the task_struct of the process to be ptraced
202 *
203 * Returns 1 if tracer has is ptracer exception ancestor for tracee.
204 */
205static int ptracer_exception_found(struct task_struct *tracer,
206 struct task_struct *tracee)
207{
208 int rc = 0;
209 struct ptrace_relation *relation;
210 struct task_struct *parent = NULL;
211
212 spin_lock_bh(&ptracer_relations_lock);
213 rcu_read_lock();
214 if (!thread_group_leader(tracee))
215 tracee = rcu_dereference(tracee->group_leader);
216 list_for_each_entry(relation, &ptracer_relations, node)
217 if (relation->tracee == tracee) {
218 parent = relation->tracer;
219 break;
220 }
221
222 if (task_is_descendant(parent, tracer))
223 rc = 1;
224 rcu_read_unlock();
225 spin_unlock_bh(&ptracer_relations_lock);
226
227 return rc;
228}
229
230/**
231 * yama_ptrace_access_check - validate PTRACE_ATTACH calls
232 * @child: task that current task is attempting to ptrace
233 * @mode: ptrace attach mode
234 *
235 * Returns 0 if following the ptrace is allowed, -ve on error.
236 */
237static int yama_ptrace_access_check(struct task_struct *child,
238 unsigned int mode)
239{
240 int rc;
241
242 /* If standard caps disallows it, so does Yama. We should
243 * only tighten restrictions further.
244 */
245 rc = cap_ptrace_access_check(child, mode);
246 if (rc)
247 return rc;
248
249 /* require ptrace target be a child of ptracer on attach */
250 if (mode == PTRACE_MODE_ATTACH &&
251 ptrace_scope &&
252 !task_is_descendant(current, child) &&
253 !ptracer_exception_found(current, child) &&
254 !capable(CAP_SYS_PTRACE))
255 rc = -EPERM;
256
257 if (rc) {
258 char name[sizeof(current->comm)];
259 printk_ratelimited(KERN_NOTICE "ptrace of non-child"
260 " pid %d was attempted by: %s (pid %d)\n",
261 child->pid,
262 get_task_comm(name, current),
263 current->pid);
264 }
265
266 return rc;
267}
268
269static struct security_operations yama_ops = {
270 .name = "yama",
271
272 .ptrace_access_check = yama_ptrace_access_check,
273 .task_prctl = yama_task_prctl,
274 .task_free = yama_task_free,
275};
276
277#ifdef CONFIG_SYSCTL
278static int zero;
279static int one = 1;
280
281struct ctl_path yama_sysctl_path[] = {
282 { .procname = "kernel", },
283 { .procname = "yama", },
284 { }
285};
286
287static struct ctl_table yama_sysctl_table[] = {
288 {
289 .procname = "ptrace_scope",
290 .data = &ptrace_scope,
291 .maxlen = sizeof(int),
292 .mode = 0644,
293 .proc_handler = proc_dointvec_minmax,
294 .extra1 = &zero,
295 .extra2 = &one,
296 },
297 { }
298};
299#endif /* CONFIG_SYSCTL */
300
301static __init int yama_init(void)
302{
303 if (!security_module_enable(&yama_ops))
304 return 0;
305
306 printk(KERN_INFO "Yama: becoming mindful.\n");
307
308 if (register_security(&yama_ops))
309 panic("Yama: kernel registration failed.\n");
310
311#ifdef CONFIG_SYSCTL
312 if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table))
313 panic("Yama: sysctl registration failed.\n");
314#endif
315
316 return 0;
317}
318
319security_initcall(yama_init);