diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2010-02-10 19:43:54 -0500 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2010-02-14 17:00:24 -0500 |
commit | 847b173ea3d6f50936823d07f2245059bf44713b (patch) | |
tree | b53c6d0536af73a078bcff0375f9f4d837f79bba /security/tomoyo/gc.c | |
parent | ec8e6a4e062e2edebef91e930c20572c9f4c0dda (diff) |
TOMOYO: Add garbage collector.
This patch adds garbage collector support to TOMOYO.
Elements are protected by "struct srcu_struct tomoyo_ss".
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo/gc.c')
-rw-r--r-- | security/tomoyo/gc.c | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c new file mode 100644 index 000000000000..ee15da39387d --- /dev/null +++ b/security/tomoyo/gc.c | |||
@@ -0,0 +1,370 @@ | |||
1 | /* | ||
2 | * security/tomoyo/gc.c | ||
3 | * | ||
4 | * Implementation of the Domain-Based Mandatory Access Control. | ||
5 | * | ||
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include "common.h" | ||
11 | #include <linux/kthread.h> | ||
12 | |||
13 | enum tomoyo_gc_id { | ||
14 | TOMOYO_ID_DOMAIN_INITIALIZER, | ||
15 | TOMOYO_ID_DOMAIN_KEEPER, | ||
16 | TOMOYO_ID_ALIAS, | ||
17 | TOMOYO_ID_GLOBALLY_READABLE, | ||
18 | TOMOYO_ID_PATTERN, | ||
19 | TOMOYO_ID_NO_REWRITE, | ||
20 | TOMOYO_ID_MANAGER, | ||
21 | TOMOYO_ID_NAME, | ||
22 | TOMOYO_ID_ACL, | ||
23 | TOMOYO_ID_DOMAIN | ||
24 | }; | ||
25 | |||
26 | struct tomoyo_gc_entry { | ||
27 | struct list_head list; | ||
28 | int type; | ||
29 | void *element; | ||
30 | }; | ||
31 | static LIST_HEAD(tomoyo_gc_queue); | ||
32 | static DEFINE_MUTEX(tomoyo_gc_mutex); | ||
33 | |||
34 | /* Caller holds tomoyo_policy_lock mutex. */ | ||
35 | static bool tomoyo_add_to_gc(const int type, void *element) | ||
36 | { | ||
37 | struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | ||
38 | if (!entry) | ||
39 | return false; | ||
40 | entry->type = type; | ||
41 | entry->element = element; | ||
42 | list_add(&entry->list, &tomoyo_gc_queue); | ||
43 | return true; | ||
44 | } | ||
45 | |||
46 | static void tomoyo_del_allow_read | ||
47 | (struct tomoyo_globally_readable_file_entry *ptr) | ||
48 | { | ||
49 | tomoyo_put_name(ptr->filename); | ||
50 | } | ||
51 | |||
52 | static void tomoyo_del_file_pattern(struct tomoyo_pattern_entry *ptr) | ||
53 | { | ||
54 | tomoyo_put_name(ptr->pattern); | ||
55 | } | ||
56 | |||
57 | static void tomoyo_del_no_rewrite(struct tomoyo_no_rewrite_entry *ptr) | ||
58 | { | ||
59 | tomoyo_put_name(ptr->pattern); | ||
60 | } | ||
61 | |||
62 | static void tomoyo_del_domain_initializer | ||
63 | (struct tomoyo_domain_initializer_entry *ptr) | ||
64 | { | ||
65 | tomoyo_put_name(ptr->domainname); | ||
66 | tomoyo_put_name(ptr->program); | ||
67 | } | ||
68 | |||
69 | static void tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr) | ||
70 | { | ||
71 | tomoyo_put_name(ptr->domainname); | ||
72 | tomoyo_put_name(ptr->program); | ||
73 | } | ||
74 | |||
75 | static void tomoyo_del_alias(struct tomoyo_alias_entry *ptr) | ||
76 | { | ||
77 | tomoyo_put_name(ptr->original_name); | ||
78 | tomoyo_put_name(ptr->aliased_name); | ||
79 | } | ||
80 | |||
81 | static void tomoyo_del_manager(struct tomoyo_policy_manager_entry *ptr) | ||
82 | { | ||
83 | tomoyo_put_name(ptr->manager); | ||
84 | } | ||
85 | |||
86 | static void tomoyo_del_acl(struct tomoyo_acl_info *acl) | ||
87 | { | ||
88 | switch (acl->type) { | ||
89 | case TOMOYO_TYPE_SINGLE_PATH_ACL: | ||
90 | { | ||
91 | struct tomoyo_single_path_acl_record *entry | ||
92 | = container_of(acl, typeof(*entry), head); | ||
93 | tomoyo_put_name(entry->filename); | ||
94 | } | ||
95 | break; | ||
96 | case TOMOYO_TYPE_DOUBLE_PATH_ACL: | ||
97 | { | ||
98 | struct tomoyo_double_path_acl_record *entry | ||
99 | = container_of(acl, typeof(*entry), head); | ||
100 | tomoyo_put_name(entry->filename1); | ||
101 | tomoyo_put_name(entry->filename2); | ||
102 | } | ||
103 | break; | ||
104 | default: | ||
105 | printk(KERN_WARNING "Unknown type\n"); | ||
106 | break; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) | ||
111 | { | ||
112 | struct tomoyo_acl_info *acl; | ||
113 | struct tomoyo_acl_info *tmp; | ||
114 | /* | ||
115 | * Since we don't protect whole execve() operation using SRCU, | ||
116 | * we need to recheck domain->users at this point. | ||
117 | * | ||
118 | * (1) Reader starts SRCU section upon execve(). | ||
119 | * (2) Reader traverses tomoyo_domain_list and finds this domain. | ||
120 | * (3) Writer marks this domain as deleted. | ||
121 | * (4) Garbage collector removes this domain from tomoyo_domain_list | ||
122 | * because this domain is marked as deleted and used by nobody. | ||
123 | * (5) Reader saves reference to this domain into | ||
124 | * "struct linux_binprm"->cred->security . | ||
125 | * (6) Reader finishes SRCU section, although execve() operation has | ||
126 | * not finished yet. | ||
127 | * (7) Garbage collector waits for SRCU synchronization. | ||
128 | * (8) Garbage collector kfree() this domain because this domain is | ||
129 | * used by nobody. | ||
130 | * (9) Reader finishes execve() operation and restores this domain from | ||
131 | * "struct linux_binprm"->cred->security. | ||
132 | * | ||
133 | * By updating domain->users at (5), we can solve this race problem | ||
134 | * by rechecking domain->users at (8). | ||
135 | */ | ||
136 | if (atomic_read(&domain->users)) | ||
137 | return false; | ||
138 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { | ||
139 | tomoyo_del_acl(acl); | ||
140 | tomoyo_memory_free(acl); | ||
141 | } | ||
142 | tomoyo_put_name(domain->domainname); | ||
143 | return true; | ||
144 | } | ||
145 | |||
146 | |||
147 | static void tomoyo_del_name(const struct tomoyo_name_entry *ptr) | ||
148 | { | ||
149 | } | ||
150 | |||
151 | static void tomoyo_collect_entry(void) | ||
152 | { | ||
153 | mutex_lock(&tomoyo_policy_lock); | ||
154 | { | ||
155 | struct tomoyo_globally_readable_file_entry *ptr; | ||
156 | list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, | ||
157 | list) { | ||
158 | if (!ptr->is_deleted) | ||
159 | continue; | ||
160 | if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr)) | ||
161 | list_del_rcu(&ptr->list); | ||
162 | else | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | { | ||
167 | struct tomoyo_pattern_entry *ptr; | ||
168 | list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { | ||
169 | if (!ptr->is_deleted) | ||
170 | continue; | ||
171 | if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr)) | ||
172 | list_del_rcu(&ptr->list); | ||
173 | else | ||
174 | break; | ||
175 | } | ||
176 | } | ||
177 | { | ||
178 | struct tomoyo_no_rewrite_entry *ptr; | ||
179 | list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { | ||
180 | if (!ptr->is_deleted) | ||
181 | continue; | ||
182 | if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr)) | ||
183 | list_del_rcu(&ptr->list); | ||
184 | else | ||
185 | break; | ||
186 | } | ||
187 | } | ||
188 | { | ||
189 | struct tomoyo_domain_initializer_entry *ptr; | ||
190 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, | ||
191 | list) { | ||
192 | if (!ptr->is_deleted) | ||
193 | continue; | ||
194 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, ptr)) | ||
195 | list_del_rcu(&ptr->list); | ||
196 | else | ||
197 | break; | ||
198 | } | ||
199 | } | ||
200 | { | ||
201 | struct tomoyo_domain_keeper_entry *ptr; | ||
202 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { | ||
203 | if (!ptr->is_deleted) | ||
204 | continue; | ||
205 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr)) | ||
206 | list_del_rcu(&ptr->list); | ||
207 | else | ||
208 | break; | ||
209 | } | ||
210 | } | ||
211 | { | ||
212 | struct tomoyo_alias_entry *ptr; | ||
213 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { | ||
214 | if (!ptr->is_deleted) | ||
215 | continue; | ||
216 | if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr)) | ||
217 | list_del_rcu(&ptr->list); | ||
218 | else | ||
219 | break; | ||
220 | } | ||
221 | } | ||
222 | { | ||
223 | struct tomoyo_policy_manager_entry *ptr; | ||
224 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, | ||
225 | list) { | ||
226 | if (!ptr->is_deleted) | ||
227 | continue; | ||
228 | if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr)) | ||
229 | list_del_rcu(&ptr->list); | ||
230 | else | ||
231 | break; | ||
232 | } | ||
233 | } | ||
234 | { | ||
235 | struct tomoyo_domain_info *domain; | ||
236 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | ||
237 | struct tomoyo_acl_info *acl; | ||
238 | list_for_each_entry_rcu(acl, &domain->acl_info_list, | ||
239 | list) { | ||
240 | switch (acl->type) { | ||
241 | case TOMOYO_TYPE_SINGLE_PATH_ACL: | ||
242 | if (container_of(acl, | ||
243 | struct tomoyo_single_path_acl_record, | ||
244 | head)->perm || | ||
245 | container_of(acl, | ||
246 | struct tomoyo_single_path_acl_record, | ||
247 | head)->perm_high) | ||
248 | continue; | ||
249 | break; | ||
250 | case TOMOYO_TYPE_DOUBLE_PATH_ACL: | ||
251 | if (container_of(acl, | ||
252 | struct tomoyo_double_path_acl_record, | ||
253 | head)->perm) | ||
254 | continue; | ||
255 | break; | ||
256 | default: | ||
257 | continue; | ||
258 | } | ||
259 | if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl)) | ||
260 | list_del_rcu(&acl->list); | ||
261 | else | ||
262 | break; | ||
263 | } | ||
264 | if (!domain->is_deleted || atomic_read(&domain->users)) | ||
265 | continue; | ||
266 | /* | ||
267 | * Nobody is referring this domain. But somebody may | ||
268 | * refer this domain after successful execve(). | ||
269 | * We recheck domain->users after SRCU synchronization. | ||
270 | */ | ||
271 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain)) | ||
272 | list_del_rcu(&domain->list); | ||
273 | else | ||
274 | break; | ||
275 | } | ||
276 | } | ||
277 | mutex_unlock(&tomoyo_policy_lock); | ||
278 | mutex_lock(&tomoyo_name_list_lock); | ||
279 | { | ||
280 | int i; | ||
281 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { | ||
282 | struct tomoyo_name_entry *ptr; | ||
283 | list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], | ||
284 | list) { | ||
285 | if (atomic_read(&ptr->users)) | ||
286 | continue; | ||
287 | if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr)) | ||
288 | list_del_rcu(&ptr->list); | ||
289 | else { | ||
290 | i = TOMOYO_MAX_HASH; | ||
291 | break; | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | mutex_unlock(&tomoyo_name_list_lock); | ||
297 | } | ||
298 | |||
299 | static void tomoyo_kfree_entry(void) | ||
300 | { | ||
301 | struct tomoyo_gc_entry *p; | ||
302 | struct tomoyo_gc_entry *tmp; | ||
303 | |||
304 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { | ||
305 | switch (p->type) { | ||
306 | case TOMOYO_ID_DOMAIN_INITIALIZER: | ||
307 | tomoyo_del_domain_initializer(p->element); | ||
308 | break; | ||
309 | case TOMOYO_ID_DOMAIN_KEEPER: | ||
310 | tomoyo_del_domain_keeper(p->element); | ||
311 | break; | ||
312 | case TOMOYO_ID_ALIAS: | ||
313 | tomoyo_del_alias(p->element); | ||
314 | break; | ||
315 | case TOMOYO_ID_GLOBALLY_READABLE: | ||
316 | tomoyo_del_allow_read(p->element); | ||
317 | break; | ||
318 | case TOMOYO_ID_PATTERN: | ||
319 | tomoyo_del_file_pattern(p->element); | ||
320 | break; | ||
321 | case TOMOYO_ID_NO_REWRITE: | ||
322 | tomoyo_del_no_rewrite(p->element); | ||
323 | break; | ||
324 | case TOMOYO_ID_MANAGER: | ||
325 | tomoyo_del_manager(p->element); | ||
326 | break; | ||
327 | case TOMOYO_ID_NAME: | ||
328 | tomoyo_del_name(p->element); | ||
329 | break; | ||
330 | case TOMOYO_ID_ACL: | ||
331 | tomoyo_del_acl(p->element); | ||
332 | break; | ||
333 | case TOMOYO_ID_DOMAIN: | ||
334 | if (!tomoyo_del_domain(p->element)) | ||
335 | continue; | ||
336 | break; | ||
337 | default: | ||
338 | printk(KERN_WARNING "Unknown type\n"); | ||
339 | break; | ||
340 | } | ||
341 | tomoyo_memory_free(p->element); | ||
342 | list_del(&p->list); | ||
343 | kfree(p); | ||
344 | } | ||
345 | } | ||
346 | |||
347 | static int tomoyo_gc_thread(void *unused) | ||
348 | { | ||
349 | daemonize("GC for TOMOYO"); | ||
350 | if (mutex_trylock(&tomoyo_gc_mutex)) { | ||
351 | int i; | ||
352 | for (i = 0; i < 10; i++) { | ||
353 | tomoyo_collect_entry(); | ||
354 | if (list_empty(&tomoyo_gc_queue)) | ||
355 | break; | ||
356 | synchronize_srcu(&tomoyo_ss); | ||
357 | tomoyo_kfree_entry(); | ||
358 | } | ||
359 | mutex_unlock(&tomoyo_gc_mutex); | ||
360 | } | ||
361 | do_exit(0); | ||
362 | } | ||
363 | |||
364 | void tomoyo_run_gc(void) | ||
365 | { | ||
366 | struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL, | ||
367 | "GC for TOMOYO"); | ||
368 | if (!IS_ERR(task)) | ||
369 | wake_up_process(task); | ||
370 | } | ||