]>
Commit | Line | Data |
---|---|---|
847b173e TH |
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> | |
5a0e3ad6 | 12 | #include <linux/slab.h> |
847b173e | 13 | |
e2bf6907 | 14 | struct tomoyo_gc { |
847b173e | 15 | struct list_head list; |
0df7e8b8 | 16 | enum tomoyo_policy_id type; |
e79acf0e | 17 | struct list_head *element; |
847b173e TH |
18 | }; |
19 | static LIST_HEAD(tomoyo_gc_queue); | |
20 | static DEFINE_MUTEX(tomoyo_gc_mutex); | |
21 | ||
0df7e8b8 TH |
22 | /** |
23 | * tomoyo_add_to_gc - Add an entry to to be deleted list. | |
24 | * | |
25 | * @type: One of values in "enum tomoyo_policy_id". | |
26 | * @element: Pointer to "struct list_head". | |
27 | * | |
28 | * Returns true on success, false otherwise. | |
29 | * | |
30 | * Caller holds tomoyo_policy_lock mutex. | |
31 | * | |
32 | * Adding an entry needs kmalloc(). Thus, if we try to add thousands of | |
33 | * entries at once, it will take too long time. Thus, do not add more than 128 | |
34 | * entries per a scan. But to be able to handle worst case where all entries | |
35 | * are in-use, we accept one more entry per a scan. | |
36 | * | |
37 | * If we use singly linked list using "struct list_head"->prev (which is | |
38 | * LIST_POISON2), we can avoid kmalloc(). | |
39 | */ | |
e79acf0e | 40 | static bool tomoyo_add_to_gc(const int type, struct list_head *element) |
847b173e | 41 | { |
e2bf6907 | 42 | struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); |
847b173e TH |
43 | if (!entry) |
44 | return false; | |
45 | entry->type = type; | |
46 | entry->element = element; | |
47 | list_add(&entry->list, &tomoyo_gc_queue); | |
e79acf0e | 48 | list_del_rcu(element); |
847b173e TH |
49 | return true; |
50 | } | |
51 | ||
0df7e8b8 TH |
52 | /** |
53 | * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". | |
54 | * | |
55 | * @element: Pointer to "struct list_head". | |
56 | * | |
57 | * Returns nothing. | |
58 | */ | |
5448ec4f | 59 | static void tomoyo_del_transition_control(struct list_head *element) |
847b173e | 60 | { |
5448ec4f | 61 | struct tomoyo_transition_control *ptr = |
e79acf0e | 62 | container_of(element, typeof(*ptr), head.list); |
847b173e TH |
63 | tomoyo_put_name(ptr->domainname); |
64 | tomoyo_put_name(ptr->program); | |
65 | } | |
66 | ||
0df7e8b8 TH |
67 | /** |
68 | * tomoyo_del_aggregator - Delete members in "struct tomoyo_aggregator". | |
69 | * | |
70 | * @element: Pointer to "struct list_head". | |
71 | * | |
72 | * Returns nothing. | |
73 | */ | |
e79acf0e | 74 | static void tomoyo_del_aggregator(struct list_head *element) |
1084307c | 75 | { |
e2bf6907 | 76 | struct tomoyo_aggregator *ptr = |
e79acf0e | 77 | container_of(element, typeof(*ptr), head.list); |
1084307c TH |
78 | tomoyo_put_name(ptr->original_name); |
79 | tomoyo_put_name(ptr->aggregated_name); | |
80 | } | |
81 | ||
0df7e8b8 TH |
82 | /** |
83 | * tomoyo_del_manager - Delete members in "struct tomoyo_manager". | |
84 | * | |
85 | * @element: Pointer to "struct list_head". | |
86 | * | |
87 | * Returns nothing. | |
88 | */ | |
e79acf0e | 89 | static void tomoyo_del_manager(struct list_head *element) |
847b173e | 90 | { |
e2bf6907 | 91 | struct tomoyo_manager *ptr = |
e79acf0e | 92 | container_of(element, typeof(*ptr), head.list); |
847b173e TH |
93 | tomoyo_put_name(ptr->manager); |
94 | } | |
95 | ||
0df7e8b8 TH |
96 | /** |
97 | * tomoyo_del_acl - Delete members in "struct tomoyo_acl_info". | |
98 | * | |
99 | * @element: Pointer to "struct list_head". | |
100 | * | |
101 | * Returns nothing. | |
102 | */ | |
e79acf0e | 103 | static void tomoyo_del_acl(struct list_head *element) |
847b173e | 104 | { |
e79acf0e TH |
105 | struct tomoyo_acl_info *acl = |
106 | container_of(element, typeof(*acl), list); | |
847b173e | 107 | switch (acl->type) { |
7ef61233 | 108 | case TOMOYO_TYPE_PATH_ACL: |
847b173e | 109 | { |
7ef61233 | 110 | struct tomoyo_path_acl *entry |
847b173e | 111 | = container_of(acl, typeof(*entry), head); |
7762fbff | 112 | tomoyo_put_name_union(&entry->name); |
847b173e TH |
113 | } |
114 | break; | |
7ef61233 | 115 | case TOMOYO_TYPE_PATH2_ACL: |
847b173e | 116 | { |
7ef61233 | 117 | struct tomoyo_path2_acl *entry |
847b173e | 118 | = container_of(acl, typeof(*entry), head); |
7762fbff TH |
119 | tomoyo_put_name_union(&entry->name1); |
120 | tomoyo_put_name_union(&entry->name2); | |
847b173e TH |
121 | } |
122 | break; | |
a1f9bb6a TH |
123 | case TOMOYO_TYPE_PATH_NUMBER_ACL: |
124 | { | |
125 | struct tomoyo_path_number_acl *entry | |
126 | = container_of(acl, typeof(*entry), head); | |
127 | tomoyo_put_name_union(&entry->name); | |
128 | tomoyo_put_number_union(&entry->number); | |
129 | } | |
130 | break; | |
75093152 | 131 | case TOMOYO_TYPE_MKDEV_ACL: |
a1f9bb6a | 132 | { |
75093152 | 133 | struct tomoyo_mkdev_acl *entry |
a1f9bb6a TH |
134 | = container_of(acl, typeof(*entry), head); |
135 | tomoyo_put_name_union(&entry->name); | |
136 | tomoyo_put_number_union(&entry->mode); | |
137 | tomoyo_put_number_union(&entry->major); | |
138 | tomoyo_put_number_union(&entry->minor); | |
139 | } | |
140 | break; | |
2106ccd9 TH |
141 | case TOMOYO_TYPE_MOUNT_ACL: |
142 | { | |
143 | struct tomoyo_mount_acl *entry | |
144 | = container_of(acl, typeof(*entry), head); | |
145 | tomoyo_put_name_union(&entry->dev_name); | |
146 | tomoyo_put_name_union(&entry->dir_name); | |
147 | tomoyo_put_name_union(&entry->fs_type); | |
148 | tomoyo_put_number_union(&entry->flags); | |
149 | } | |
150 | break; | |
847b173e TH |
151 | } |
152 | } | |
153 | ||
e79acf0e | 154 | static bool tomoyo_del_domain(struct list_head *element) |
847b173e | 155 | { |
e79acf0e TH |
156 | struct tomoyo_domain_info *domain = |
157 | container_of(element, typeof(*domain), list); | |
847b173e TH |
158 | struct tomoyo_acl_info *acl; |
159 | struct tomoyo_acl_info *tmp; | |
160 | /* | |
161 | * Since we don't protect whole execve() operation using SRCU, | |
162 | * we need to recheck domain->users at this point. | |
163 | * | |
164 | * (1) Reader starts SRCU section upon execve(). | |
165 | * (2) Reader traverses tomoyo_domain_list and finds this domain. | |
166 | * (3) Writer marks this domain as deleted. | |
167 | * (4) Garbage collector removes this domain from tomoyo_domain_list | |
168 | * because this domain is marked as deleted and used by nobody. | |
169 | * (5) Reader saves reference to this domain into | |
170 | * "struct linux_binprm"->cred->security . | |
171 | * (6) Reader finishes SRCU section, although execve() operation has | |
172 | * not finished yet. | |
173 | * (7) Garbage collector waits for SRCU synchronization. | |
174 | * (8) Garbage collector kfree() this domain because this domain is | |
175 | * used by nobody. | |
176 | * (9) Reader finishes execve() operation and restores this domain from | |
177 | * "struct linux_binprm"->cred->security. | |
178 | * | |
179 | * By updating domain->users at (5), we can solve this race problem | |
180 | * by rechecking domain->users at (8). | |
181 | */ | |
182 | if (atomic_read(&domain->users)) | |
183 | return false; | |
184 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { | |
e79acf0e | 185 | tomoyo_del_acl(&acl->list); |
847b173e TH |
186 | tomoyo_memory_free(acl); |
187 | } | |
188 | tomoyo_put_name(domain->domainname); | |
189 | return true; | |
190 | } | |
191 | ||
192 | ||
0df7e8b8 TH |
193 | /** |
194 | * tomoyo_del_name - Delete members in "struct tomoyo_name". | |
195 | * | |
196 | * @element: Pointer to "struct list_head". | |
197 | * | |
198 | * Returns nothing. | |
199 | */ | |
e79acf0e | 200 | static void tomoyo_del_name(struct list_head *element) |
847b173e | 201 | { |
e2bf6907 | 202 | const struct tomoyo_name *ptr = |
0df7e8b8 | 203 | container_of(element, typeof(*ptr), head.list); |
847b173e TH |
204 | } |
205 | ||
0df7e8b8 TH |
206 | /** |
207 | * tomoyo_del_path_group - Delete members in "struct tomoyo_path_group". | |
208 | * | |
209 | * @element: Pointer to "struct list_head". | |
210 | * | |
211 | * Returns nothing. | |
212 | */ | |
a98aa4de | 213 | static void tomoyo_del_path_group(struct list_head *element) |
7762fbff | 214 | { |
a98aa4de | 215 | struct tomoyo_path_group *member = |
e79acf0e | 216 | container_of(element, typeof(*member), head.list); |
7762fbff TH |
217 | tomoyo_put_name(member->member_name); |
218 | } | |
219 | ||
0df7e8b8 TH |
220 | /** |
221 | * tomoyo_del_group - Delete "struct tomoyo_group". | |
222 | * | |
223 | * @element: Pointer to "struct list_head". | |
224 | * | |
225 | * Returns nothing. | |
226 | */ | |
a98aa4de | 227 | static void tomoyo_del_group(struct list_head *element) |
7762fbff | 228 | { |
a98aa4de | 229 | struct tomoyo_group *group = |
0df7e8b8 | 230 | container_of(element, typeof(*group), head.list); |
7762fbff TH |
231 | tomoyo_put_name(group->group_name); |
232 | } | |
233 | ||
0df7e8b8 TH |
234 | /** |
235 | * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group". | |
236 | * | |
237 | * @element: Pointer to "struct list_head". | |
238 | * | |
239 | * Returns nothing. | |
240 | */ | |
e79acf0e | 241 | static void tomoyo_del_number_group(struct list_head *element) |
4c3e9e2d | 242 | { |
a98aa4de TH |
243 | struct tomoyo_number_group *member = |
244 | container_of(element, typeof(*member), head.list); | |
4c3e9e2d TH |
245 | } |
246 | ||
0df7e8b8 TH |
247 | /** |
248 | * tomoyo_collect_member - Delete elements with "struct tomoyo_acl_head". | |
249 | * | |
250 | * @id: One of values in "enum tomoyo_policy_id". | |
251 | * @member_list: Pointer to "struct list_head". | |
252 | * | |
253 | * Returns true if some elements are deleted, false otherwise. | |
254 | */ | |
255 | static bool tomoyo_collect_member(const enum tomoyo_policy_id id, | |
256 | struct list_head *member_list) | |
d2f8b234 TH |
257 | { |
258 | struct tomoyo_acl_head *member; | |
259 | list_for_each_entry(member, member_list, list) { | |
260 | if (!member->is_deleted) | |
261 | continue; | |
262 | if (!tomoyo_add_to_gc(id, &member->list)) | |
263 | return false; | |
d2f8b234 TH |
264 | } |
265 | return true; | |
266 | } | |
267 | ||
32997144 TH |
268 | /** |
269 | * tomoyo_collect_acl - Delete elements in "struct tomoyo_domain_info". | |
270 | * | |
271 | * @list: Pointer to "struct list_head". | |
272 | * | |
273 | * Returns true if some elements are deleted, false otherwise. | |
274 | */ | |
275 | static bool tomoyo_collect_acl(struct list_head *list) | |
d2f8b234 TH |
276 | { |
277 | struct tomoyo_acl_info *acl; | |
32997144 | 278 | list_for_each_entry(acl, list, list) { |
d2f8b234 TH |
279 | if (!acl->is_deleted) |
280 | continue; | |
281 | if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) | |
282 | return false; | |
d2f8b234 TH |
283 | } |
284 | return true; | |
285 | } | |
286 | ||
0df7e8b8 TH |
287 | /** |
288 | * tomoyo_collect_entry - Scan lists for deleted elements. | |
289 | * | |
290 | * Returns nothing. | |
291 | */ | |
847b173e TH |
292 | static void tomoyo_collect_entry(void) |
293 | { | |
d2f8b234 | 294 | int i; |
29282381 TH |
295 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
296 | return; | |
d2f8b234 | 297 | for (i = 0; i < TOMOYO_MAX_POLICY; i++) { |
0df7e8b8 | 298 | if (!tomoyo_collect_member(i, &tomoyo_policy_list[i])) |
a230f9e7 | 299 | goto unlock; |
847b173e | 300 | } |
32997144 TH |
301 | for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) |
302 | if (!tomoyo_collect_acl(&tomoyo_acl_group[i])) | |
303 | goto unlock; | |
847b173e TH |
304 | { |
305 | struct tomoyo_domain_info *domain; | |
306 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | |
32997144 | 307 | if (!tomoyo_collect_acl(&domain->acl_info_list)) |
d2f8b234 | 308 | goto unlock; |
847b173e TH |
309 | if (!domain->is_deleted || atomic_read(&domain->users)) |
310 | continue; | |
311 | /* | |
312 | * Nobody is referring this domain. But somebody may | |
313 | * refer this domain after successful execve(). | |
314 | * We recheck domain->users after SRCU synchronization. | |
315 | */ | |
e79acf0e | 316 | if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) |
d2f8b234 | 317 | goto unlock; |
847b173e TH |
318 | } |
319 | } | |
d2f8b234 | 320 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { |
e2bf6907 | 321 | struct tomoyo_name *ptr; |
0df7e8b8 TH |
322 | list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], head.list) { |
323 | if (atomic_read(&ptr->head.users)) | |
d2f8b234 | 324 | continue; |
0df7e8b8 | 325 | if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->head.list)) |
d2f8b234 | 326 | goto unlock; |
847b173e TH |
327 | } |
328 | } | |
7c2ea22e TH |
329 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) { |
330 | struct list_head *list = &tomoyo_group_list[i]; | |
331 | int id; | |
a98aa4de | 332 | struct tomoyo_group *group; |
7c2ea22e TH |
333 | switch (i) { |
334 | case 0: | |
335 | id = TOMOYO_ID_PATH_GROUP; | |
336 | break; | |
337 | default: | |
338 | id = TOMOYO_ID_NUMBER_GROUP; | |
339 | break; | |
7762fbff | 340 | } |
0df7e8b8 TH |
341 | list_for_each_entry(group, list, head.list) { |
342 | if (!tomoyo_collect_member(id, &group->member_list)) | |
7c2ea22e | 343 | goto unlock; |
4c3e9e2d | 344 | if (!list_empty(&group->member_list) || |
0df7e8b8 | 345 | atomic_read(&group->head.users)) |
4c3e9e2d | 346 | continue; |
0df7e8b8 TH |
347 | if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, |
348 | &group->head.list)) | |
d2f8b234 | 349 | goto unlock; |
4c3e9e2d TH |
350 | } |
351 | } | |
d2f8b234 | 352 | unlock: |
29282381 | 353 | mutex_unlock(&tomoyo_policy_lock); |
847b173e TH |
354 | } |
355 | ||
356 | static void tomoyo_kfree_entry(void) | |
357 | { | |
e2bf6907 TH |
358 | struct tomoyo_gc *p; |
359 | struct tomoyo_gc *tmp; | |
847b173e TH |
360 | |
361 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { | |
e79acf0e | 362 | struct list_head *element = p->element; |
847b173e | 363 | switch (p->type) { |
5448ec4f TH |
364 | case TOMOYO_ID_TRANSITION_CONTROL: |
365 | tomoyo_del_transition_control(element); | |
847b173e | 366 | break; |
1084307c | 367 | case TOMOYO_ID_AGGREGATOR: |
e79acf0e | 368 | tomoyo_del_aggregator(element); |
1084307c | 369 | break; |
847b173e | 370 | case TOMOYO_ID_MANAGER: |
e79acf0e | 371 | tomoyo_del_manager(element); |
847b173e TH |
372 | break; |
373 | case TOMOYO_ID_NAME: | |
e79acf0e | 374 | tomoyo_del_name(element); |
847b173e TH |
375 | break; |
376 | case TOMOYO_ID_ACL: | |
e79acf0e | 377 | tomoyo_del_acl(element); |
847b173e TH |
378 | break; |
379 | case TOMOYO_ID_DOMAIN: | |
e79acf0e | 380 | if (!tomoyo_del_domain(element)) |
847b173e TH |
381 | continue; |
382 | break; | |
7762fbff | 383 | case TOMOYO_ID_PATH_GROUP: |
e79acf0e | 384 | tomoyo_del_path_group(element); |
7762fbff | 385 | break; |
a98aa4de TH |
386 | case TOMOYO_ID_GROUP: |
387 | tomoyo_del_group(element); | |
4c3e9e2d TH |
388 | break; |
389 | case TOMOYO_ID_NUMBER_GROUP: | |
e79acf0e | 390 | tomoyo_del_number_group(element); |
847b173e | 391 | break; |
0df7e8b8 TH |
392 | case TOMOYO_MAX_POLICY: |
393 | break; | |
847b173e | 394 | } |
e79acf0e | 395 | tomoyo_memory_free(element); |
847b173e TH |
396 | list_del(&p->list); |
397 | kfree(p); | |
398 | } | |
399 | } | |
400 | ||
0df7e8b8 TH |
401 | /** |
402 | * tomoyo_gc_thread - Garbage collector thread function. | |
403 | * | |
404 | * @unused: Unused. | |
405 | * | |
406 | * In case OOM-killer choose this thread for termination, we create this thread | |
407 | * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was | |
408 | * close()d. | |
409 | * | |
410 | * Returns 0. | |
411 | */ | |
847b173e TH |
412 | static int tomoyo_gc_thread(void *unused) |
413 | { | |
414 | daemonize("GC for TOMOYO"); | |
415 | if (mutex_trylock(&tomoyo_gc_mutex)) { | |
416 | int i; | |
417 | for (i = 0; i < 10; i++) { | |
418 | tomoyo_collect_entry(); | |
419 | if (list_empty(&tomoyo_gc_queue)) | |
420 | break; | |
421 | synchronize_srcu(&tomoyo_ss); | |
422 | tomoyo_kfree_entry(); | |
423 | } | |
424 | mutex_unlock(&tomoyo_gc_mutex); | |
425 | } | |
426 | do_exit(0); | |
427 | } | |
428 | ||
429 | void tomoyo_run_gc(void) | |
430 | { | |
431 | struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL, | |
432 | "GC for TOMOYO"); | |
433 | if (!IS_ERR(task)) | |
434 | wake_up_process(task); | |
435 | } |