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 | |
d2f8b234 | 14 | enum tomoyo_policy_id { |
a98aa4de | 15 | TOMOYO_ID_GROUP, |
7762fbff | 16 | TOMOYO_ID_PATH_GROUP, |
4c3e9e2d | 17 | TOMOYO_ID_NUMBER_GROUP, |
847b173e TH |
18 | TOMOYO_ID_DOMAIN_INITIALIZER, |
19 | TOMOYO_ID_DOMAIN_KEEPER, | |
1084307c | 20 | TOMOYO_ID_AGGREGATOR, |
847b173e TH |
21 | TOMOYO_ID_ALIAS, |
22 | TOMOYO_ID_GLOBALLY_READABLE, | |
23 | TOMOYO_ID_PATTERN, | |
24 | TOMOYO_ID_NO_REWRITE, | |
25 | TOMOYO_ID_MANAGER, | |
26 | TOMOYO_ID_NAME, | |
27 | TOMOYO_ID_ACL, | |
d2f8b234 TH |
28 | TOMOYO_ID_DOMAIN, |
29 | TOMOYO_MAX_POLICY | |
847b173e TH |
30 | }; |
31 | ||
32 | struct tomoyo_gc_entry { | |
33 | struct list_head list; | |
34 | int type; | |
e79acf0e | 35 | struct list_head *element; |
847b173e TH |
36 | }; |
37 | static LIST_HEAD(tomoyo_gc_queue); | |
38 | static DEFINE_MUTEX(tomoyo_gc_mutex); | |
39 | ||
40 | /* Caller holds tomoyo_policy_lock mutex. */ | |
e79acf0e | 41 | static bool tomoyo_add_to_gc(const int type, struct list_head *element) |
847b173e TH |
42 | { |
43 | struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | |
44 | if (!entry) | |
45 | return false; | |
46 | entry->type = type; | |
47 | entry->element = element; | |
48 | list_add(&entry->list, &tomoyo_gc_queue); | |
e79acf0e | 49 | list_del_rcu(element); |
847b173e TH |
50 | return true; |
51 | } | |
52 | ||
e79acf0e | 53 | static void tomoyo_del_allow_read(struct list_head *element) |
847b173e | 54 | { |
e79acf0e TH |
55 | struct tomoyo_globally_readable_file_entry *ptr = |
56 | container_of(element, typeof(*ptr), head.list); | |
847b173e TH |
57 | tomoyo_put_name(ptr->filename); |
58 | } | |
59 | ||
e79acf0e | 60 | static void tomoyo_del_file_pattern(struct list_head *element) |
847b173e | 61 | { |
e79acf0e TH |
62 | struct tomoyo_pattern_entry *ptr = |
63 | container_of(element, typeof(*ptr), head.list); | |
847b173e TH |
64 | tomoyo_put_name(ptr->pattern); |
65 | } | |
66 | ||
e79acf0e | 67 | static void tomoyo_del_no_rewrite(struct list_head *element) |
847b173e | 68 | { |
e79acf0e TH |
69 | struct tomoyo_no_rewrite_entry *ptr = |
70 | container_of(element, typeof(*ptr), head.list); | |
847b173e TH |
71 | tomoyo_put_name(ptr->pattern); |
72 | } | |
73 | ||
e79acf0e | 74 | static void tomoyo_del_domain_initializer(struct list_head *element) |
847b173e | 75 | { |
e79acf0e TH |
76 | struct tomoyo_domain_initializer_entry *ptr = |
77 | container_of(element, typeof(*ptr), head.list); | |
847b173e TH |
78 | tomoyo_put_name(ptr->domainname); |
79 | tomoyo_put_name(ptr->program); | |
80 | } | |
81 | ||
e79acf0e | 82 | static void tomoyo_del_domain_keeper(struct list_head *element) |
847b173e | 83 | { |
e79acf0e TH |
84 | struct tomoyo_domain_keeper_entry *ptr = |
85 | container_of(element, typeof(*ptr), head.list); | |
847b173e TH |
86 | tomoyo_put_name(ptr->domainname); |
87 | tomoyo_put_name(ptr->program); | |
88 | } | |
89 | ||
e79acf0e | 90 | static void tomoyo_del_aggregator(struct list_head *element) |
1084307c | 91 | { |
e79acf0e TH |
92 | struct tomoyo_aggregator_entry *ptr = |
93 | container_of(element, typeof(*ptr), head.list); | |
1084307c TH |
94 | tomoyo_put_name(ptr->original_name); |
95 | tomoyo_put_name(ptr->aggregated_name); | |
96 | } | |
97 | ||
e79acf0e | 98 | static void tomoyo_del_alias(struct list_head *element) |
847b173e | 99 | { |
e79acf0e TH |
100 | struct tomoyo_alias_entry *ptr = |
101 | container_of(element, typeof(*ptr), head.list); | |
847b173e TH |
102 | tomoyo_put_name(ptr->original_name); |
103 | tomoyo_put_name(ptr->aliased_name); | |
104 | } | |
105 | ||
e79acf0e | 106 | static void tomoyo_del_manager(struct list_head *element) |
847b173e | 107 | { |
e79acf0e TH |
108 | struct tomoyo_policy_manager_entry *ptr = |
109 | container_of(element, typeof(*ptr), head.list); | |
847b173e TH |
110 | tomoyo_put_name(ptr->manager); |
111 | } | |
112 | ||
e79acf0e | 113 | static void tomoyo_del_acl(struct list_head *element) |
847b173e | 114 | { |
e79acf0e TH |
115 | struct tomoyo_acl_info *acl = |
116 | container_of(element, typeof(*acl), list); | |
847b173e | 117 | switch (acl->type) { |
7ef61233 | 118 | case TOMOYO_TYPE_PATH_ACL: |
847b173e | 119 | { |
7ef61233 | 120 | struct tomoyo_path_acl *entry |
847b173e | 121 | = container_of(acl, typeof(*entry), head); |
7762fbff | 122 | tomoyo_put_name_union(&entry->name); |
847b173e TH |
123 | } |
124 | break; | |
7ef61233 | 125 | case TOMOYO_TYPE_PATH2_ACL: |
847b173e | 126 | { |
7ef61233 | 127 | struct tomoyo_path2_acl *entry |
847b173e | 128 | = container_of(acl, typeof(*entry), head); |
7762fbff TH |
129 | tomoyo_put_name_union(&entry->name1); |
130 | tomoyo_put_name_union(&entry->name2); | |
847b173e TH |
131 | } |
132 | break; | |
a1f9bb6a TH |
133 | case TOMOYO_TYPE_PATH_NUMBER_ACL: |
134 | { | |
135 | struct tomoyo_path_number_acl *entry | |
136 | = container_of(acl, typeof(*entry), head); | |
137 | tomoyo_put_name_union(&entry->name); | |
138 | tomoyo_put_number_union(&entry->number); | |
139 | } | |
140 | break; | |
75093152 | 141 | case TOMOYO_TYPE_MKDEV_ACL: |
a1f9bb6a | 142 | { |
75093152 | 143 | struct tomoyo_mkdev_acl *entry |
a1f9bb6a TH |
144 | = container_of(acl, typeof(*entry), head); |
145 | tomoyo_put_name_union(&entry->name); | |
146 | tomoyo_put_number_union(&entry->mode); | |
147 | tomoyo_put_number_union(&entry->major); | |
148 | tomoyo_put_number_union(&entry->minor); | |
149 | } | |
150 | break; | |
2106ccd9 TH |
151 | case TOMOYO_TYPE_MOUNT_ACL: |
152 | { | |
153 | struct tomoyo_mount_acl *entry | |
154 | = container_of(acl, typeof(*entry), head); | |
155 | tomoyo_put_name_union(&entry->dev_name); | |
156 | tomoyo_put_name_union(&entry->dir_name); | |
157 | tomoyo_put_name_union(&entry->fs_type); | |
158 | tomoyo_put_number_union(&entry->flags); | |
159 | } | |
160 | break; | |
847b173e TH |
161 | } |
162 | } | |
163 | ||
e79acf0e | 164 | static bool tomoyo_del_domain(struct list_head *element) |
847b173e | 165 | { |
e79acf0e TH |
166 | struct tomoyo_domain_info *domain = |
167 | container_of(element, typeof(*domain), list); | |
847b173e TH |
168 | struct tomoyo_acl_info *acl; |
169 | struct tomoyo_acl_info *tmp; | |
170 | /* | |
171 | * Since we don't protect whole execve() operation using SRCU, | |
172 | * we need to recheck domain->users at this point. | |
173 | * | |
174 | * (1) Reader starts SRCU section upon execve(). | |
175 | * (2) Reader traverses tomoyo_domain_list and finds this domain. | |
176 | * (3) Writer marks this domain as deleted. | |
177 | * (4) Garbage collector removes this domain from tomoyo_domain_list | |
178 | * because this domain is marked as deleted and used by nobody. | |
179 | * (5) Reader saves reference to this domain into | |
180 | * "struct linux_binprm"->cred->security . | |
181 | * (6) Reader finishes SRCU section, although execve() operation has | |
182 | * not finished yet. | |
183 | * (7) Garbage collector waits for SRCU synchronization. | |
184 | * (8) Garbage collector kfree() this domain because this domain is | |
185 | * used by nobody. | |
186 | * (9) Reader finishes execve() operation and restores this domain from | |
187 | * "struct linux_binprm"->cred->security. | |
188 | * | |
189 | * By updating domain->users at (5), we can solve this race problem | |
190 | * by rechecking domain->users at (8). | |
191 | */ | |
192 | if (atomic_read(&domain->users)) | |
193 | return false; | |
194 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { | |
e79acf0e | 195 | tomoyo_del_acl(&acl->list); |
847b173e TH |
196 | tomoyo_memory_free(acl); |
197 | } | |
198 | tomoyo_put_name(domain->domainname); | |
199 | return true; | |
200 | } | |
201 | ||
202 | ||
e79acf0e | 203 | static void tomoyo_del_name(struct list_head *element) |
847b173e | 204 | { |
e79acf0e TH |
205 | const struct tomoyo_name_entry *ptr = |
206 | container_of(element, typeof(*ptr), list); | |
847b173e TH |
207 | } |
208 | ||
a98aa4de | 209 | static void tomoyo_del_path_group(struct list_head *element) |
7762fbff | 210 | { |
a98aa4de | 211 | struct tomoyo_path_group *member = |
e79acf0e | 212 | container_of(element, typeof(*member), head.list); |
7762fbff TH |
213 | tomoyo_put_name(member->member_name); |
214 | } | |
215 | ||
a98aa4de | 216 | static void tomoyo_del_group(struct list_head *element) |
7762fbff | 217 | { |
a98aa4de | 218 | struct tomoyo_group *group = |
e79acf0e | 219 | container_of(element, typeof(*group), list); |
7762fbff TH |
220 | tomoyo_put_name(group->group_name); |
221 | } | |
222 | ||
e79acf0e | 223 | static void tomoyo_del_number_group(struct list_head *element) |
4c3e9e2d | 224 | { |
a98aa4de TH |
225 | struct tomoyo_number_group *member = |
226 | container_of(element, typeof(*member), head.list); | |
4c3e9e2d TH |
227 | } |
228 | ||
d2f8b234 TH |
229 | static struct list_head *tomoyo_policy_list[TOMOYO_MAX_POLICY] = { |
230 | [TOMOYO_ID_GLOBALLY_READABLE] = &tomoyo_globally_readable_list, | |
231 | [TOMOYO_ID_PATTERN] = &tomoyo_pattern_list, | |
232 | [TOMOYO_ID_NO_REWRITE] = &tomoyo_no_rewrite_list, | |
233 | [TOMOYO_ID_DOMAIN_INITIALIZER] = &tomoyo_domain_initializer_list, | |
234 | [TOMOYO_ID_DOMAIN_KEEPER] = &tomoyo_domain_keeper_list, | |
235 | [TOMOYO_ID_AGGREGATOR] = &tomoyo_aggregator_list, | |
236 | [TOMOYO_ID_ALIAS] = &tomoyo_alias_list, | |
237 | [TOMOYO_ID_MANAGER] = &tomoyo_policy_manager_list, | |
238 | }; | |
239 | ||
240 | static bool tomoyo_collect_member(struct list_head *member_list, int id) | |
241 | { | |
242 | struct tomoyo_acl_head *member; | |
243 | list_for_each_entry(member, member_list, list) { | |
244 | if (!member->is_deleted) | |
245 | continue; | |
246 | if (!tomoyo_add_to_gc(id, &member->list)) | |
247 | return false; | |
d2f8b234 TH |
248 | } |
249 | return true; | |
250 | } | |
251 | ||
252 | static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain) | |
253 | { | |
254 | struct tomoyo_acl_info *acl; | |
255 | list_for_each_entry(acl, &domain->acl_info_list, list) { | |
256 | if (!acl->is_deleted) | |
257 | continue; | |
258 | if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) | |
259 | return false; | |
d2f8b234 TH |
260 | } |
261 | return true; | |
262 | } | |
263 | ||
847b173e TH |
264 | static void tomoyo_collect_entry(void) |
265 | { | |
d2f8b234 | 266 | int i; |
29282381 TH |
267 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
268 | return; | |
d2f8b234 TH |
269 | for (i = 0; i < TOMOYO_MAX_POLICY; i++) { |
270 | if (tomoyo_policy_list[i]) | |
271 | if (!tomoyo_collect_member(tomoyo_policy_list[i], i)) | |
272 | goto unlock; | |
847b173e TH |
273 | } |
274 | { | |
275 | struct tomoyo_domain_info *domain; | |
276 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | |
d2f8b234 TH |
277 | if (!tomoyo_collect_acl(domain)) |
278 | goto unlock; | |
847b173e TH |
279 | if (!domain->is_deleted || atomic_read(&domain->users)) |
280 | continue; | |
281 | /* | |
282 | * Nobody is referring this domain. But somebody may | |
283 | * refer this domain after successful execve(). | |
284 | * We recheck domain->users after SRCU synchronization. | |
285 | */ | |
e79acf0e | 286 | if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) |
d2f8b234 | 287 | goto unlock; |
847b173e TH |
288 | } |
289 | } | |
d2f8b234 TH |
290 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { |
291 | struct tomoyo_name_entry *ptr; | |
292 | list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) { | |
293 | if (atomic_read(&ptr->users)) | |
294 | continue; | |
e79acf0e | 295 | if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list)) |
d2f8b234 | 296 | goto unlock; |
847b173e TH |
297 | } |
298 | } | |
7762fbff | 299 | { |
a98aa4de | 300 | struct tomoyo_group *group; |
7762fbff | 301 | list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { |
d2f8b234 | 302 | tomoyo_collect_member(&group->member_list, |
a98aa4de | 303 | TOMOYO_ID_PATH_GROUP); |
7762fbff TH |
304 | if (!list_empty(&group->member_list) || |
305 | atomic_read(&group->users)) | |
306 | continue; | |
a98aa4de | 307 | if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, |
e79acf0e | 308 | &group->list)) |
d2f8b234 | 309 | goto unlock; |
7762fbff TH |
310 | } |
311 | } | |
4c3e9e2d | 312 | { |
a98aa4de | 313 | struct tomoyo_group *group; |
d2f8b234 TH |
314 | list_for_each_entry_rcu(group, &tomoyo_number_group_list, |
315 | list) { | |
316 | tomoyo_collect_member(&group->member_list, | |
a98aa4de | 317 | TOMOYO_ID_NUMBER_GROUP); |
4c3e9e2d TH |
318 | if (!list_empty(&group->member_list) || |
319 | atomic_read(&group->users)) | |
320 | continue; | |
a98aa4de | 321 | if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, |
e79acf0e | 322 | &group->list)) |
d2f8b234 | 323 | goto unlock; |
4c3e9e2d TH |
324 | } |
325 | } | |
d2f8b234 | 326 | unlock: |
29282381 | 327 | mutex_unlock(&tomoyo_policy_lock); |
847b173e TH |
328 | } |
329 | ||
330 | static void tomoyo_kfree_entry(void) | |
331 | { | |
332 | struct tomoyo_gc_entry *p; | |
333 | struct tomoyo_gc_entry *tmp; | |
334 | ||
335 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { | |
e79acf0e | 336 | struct list_head *element = p->element; |
847b173e TH |
337 | switch (p->type) { |
338 | case TOMOYO_ID_DOMAIN_INITIALIZER: | |
e79acf0e | 339 | tomoyo_del_domain_initializer(element); |
847b173e TH |
340 | break; |
341 | case TOMOYO_ID_DOMAIN_KEEPER: | |
e79acf0e | 342 | tomoyo_del_domain_keeper(element); |
847b173e | 343 | break; |
1084307c | 344 | case TOMOYO_ID_AGGREGATOR: |
e79acf0e | 345 | tomoyo_del_aggregator(element); |
1084307c | 346 | break; |
847b173e | 347 | case TOMOYO_ID_ALIAS: |
e79acf0e | 348 | tomoyo_del_alias(element); |
847b173e TH |
349 | break; |
350 | case TOMOYO_ID_GLOBALLY_READABLE: | |
e79acf0e | 351 | tomoyo_del_allow_read(element); |
847b173e TH |
352 | break; |
353 | case TOMOYO_ID_PATTERN: | |
e79acf0e | 354 | tomoyo_del_file_pattern(element); |
847b173e TH |
355 | break; |
356 | case TOMOYO_ID_NO_REWRITE: | |
e79acf0e | 357 | tomoyo_del_no_rewrite(element); |
847b173e TH |
358 | break; |
359 | case TOMOYO_ID_MANAGER: | |
e79acf0e | 360 | tomoyo_del_manager(element); |
847b173e TH |
361 | break; |
362 | case TOMOYO_ID_NAME: | |
e79acf0e | 363 | tomoyo_del_name(element); |
847b173e TH |
364 | break; |
365 | case TOMOYO_ID_ACL: | |
e79acf0e | 366 | tomoyo_del_acl(element); |
847b173e TH |
367 | break; |
368 | case TOMOYO_ID_DOMAIN: | |
e79acf0e | 369 | if (!tomoyo_del_domain(element)) |
847b173e TH |
370 | continue; |
371 | break; | |
7762fbff | 372 | case TOMOYO_ID_PATH_GROUP: |
e79acf0e | 373 | tomoyo_del_path_group(element); |
7762fbff | 374 | break; |
a98aa4de TH |
375 | case TOMOYO_ID_GROUP: |
376 | tomoyo_del_group(element); | |
4c3e9e2d TH |
377 | break; |
378 | case TOMOYO_ID_NUMBER_GROUP: | |
e79acf0e | 379 | tomoyo_del_number_group(element); |
847b173e TH |
380 | break; |
381 | } | |
e79acf0e | 382 | tomoyo_memory_free(element); |
847b173e TH |
383 | list_del(&p->list); |
384 | kfree(p); | |
385 | } | |
386 | } | |
387 | ||
388 | static int tomoyo_gc_thread(void *unused) | |
389 | { | |
390 | daemonize("GC for TOMOYO"); | |
391 | if (mutex_trylock(&tomoyo_gc_mutex)) { | |
392 | int i; | |
393 | for (i = 0; i < 10; i++) { | |
394 | tomoyo_collect_entry(); | |
395 | if (list_empty(&tomoyo_gc_queue)) | |
396 | break; | |
397 | synchronize_srcu(&tomoyo_ss); | |
398 | tomoyo_kfree_entry(); | |
399 | } | |
400 | mutex_unlock(&tomoyo_gc_mutex); | |
401 | } | |
402 | do_exit(0); | |
403 | } | |
404 | ||
405 | void tomoyo_run_gc(void) | |
406 | { | |
407 | struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL, | |
408 | "GC for TOMOYO"); | |
409 | if (!IS_ERR(task)) | |
410 | wake_up_process(task); | |
411 | } |