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