]>
Commit | Line | Data |
---|---|---|
26a2a1c9 KT |
1 | /* |
2 | * security/tomoyo/domain.c | |
3 | * | |
c3ef1500 | 4 | * Domain transition functions for TOMOYO. |
26a2a1c9 | 5 | * |
c3ef1500 | 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION |
26a2a1c9 KT |
7 | */ |
8 | ||
9 | #include "common.h" | |
26a2a1c9 | 10 | #include <linux/binfmts.h> |
5a0e3ad6 | 11 | #include <linux/slab.h> |
26a2a1c9 KT |
12 | |
13 | /* Variables definitions.*/ | |
14 | ||
15 | /* The initial domain. */ | |
16 | struct tomoyo_domain_info tomoyo_kernel_domain; | |
17 | ||
237ab459 TH |
18 | /** |
19 | * tomoyo_update_domain - Update an entry for domain policy. | |
20 | * | |
21 | * @new_entry: Pointer to "struct tomoyo_acl_info". | |
22 | * @size: Size of @new_entry in bytes. | |
23 | * @is_delete: True if it is a delete request. | |
24 | * @domain: Pointer to "struct tomoyo_domain_info". | |
25 | * @check_duplicate: Callback function to find duplicated entry. | |
26 | * @merge_duplicate: Callback function to merge duplicated entry. | |
27 | * | |
28 | * Returns 0 on success, negative value otherwise. | |
29 | * | |
30 | * Caller holds tomoyo_read_lock(). | |
31 | */ | |
32 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | |
33 | bool is_delete, struct tomoyo_domain_info *domain, | |
34 | bool (*check_duplicate) (const struct tomoyo_acl_info | |
35 | *, | |
36 | const struct tomoyo_acl_info | |
37 | *), | |
38 | bool (*merge_duplicate) (struct tomoyo_acl_info *, | |
39 | struct tomoyo_acl_info *, | |
40 | const bool)) | |
41 | { | |
42 | int error = is_delete ? -ENOENT : -ENOMEM; | |
43 | struct tomoyo_acl_info *entry; | |
44 | ||
45 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | |
46 | return error; | |
47 | list_for_each_entry_rcu(entry, &domain->acl_info_list, list) { | |
48 | if (!check_duplicate(entry, new_entry)) | |
49 | continue; | |
50 | if (merge_duplicate) | |
51 | entry->is_deleted = merge_duplicate(entry, new_entry, | |
52 | is_delete); | |
53 | else | |
54 | entry->is_deleted = is_delete; | |
55 | error = 0; | |
56 | break; | |
57 | } | |
58 | if (error && !is_delete) { | |
59 | entry = tomoyo_commit_ok(new_entry, size); | |
60 | if (entry) { | |
61 | list_add_tail_rcu(&entry->list, &domain->acl_info_list); | |
62 | error = 0; | |
63 | } | |
64 | } | |
65 | mutex_unlock(&tomoyo_policy_lock); | |
66 | return error; | |
67 | } | |
68 | ||
c3fa109a TH |
69 | /* |
70 | * tomoyo_domain_list is used for holding list of domains. | |
71 | * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding | |
72 | * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain. | |
73 | * | |
74 | * An entry is added by | |
75 | * | |
76 | * # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \ | |
77 | * /sys/kernel/security/tomoyo/domain_policy | |
78 | * | |
79 | * and is deleted by | |
80 | * | |
81 | * # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \ | |
82 | * /sys/kernel/security/tomoyo/domain_policy | |
83 | * | |
84 | * and all entries are retrieved by | |
85 | * | |
86 | * # cat /sys/kernel/security/tomoyo/domain_policy | |
87 | * | |
88 | * A domain is added by | |
89 | * | |
90 | * # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy | |
91 | * | |
92 | * and is deleted by | |
93 | * | |
94 | * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy | |
95 | * | |
96 | * and all domains are retrieved by | |
97 | * | |
98 | * # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy | |
99 | * | |
100 | * Normally, a domainname is monotonically getting longer because a domainname | |
101 | * which the process will belong to if an execve() operation succeeds is | |
102 | * defined as a concatenation of "current domainname" + "pathname passed to | |
103 | * execve()". | |
104 | * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for | |
105 | * exceptions. | |
106 | */ | |
26a2a1c9 | 107 | LIST_HEAD(tomoyo_domain_list); |
26a2a1c9 | 108 | |
26a2a1c9 KT |
109 | /** |
110 | * tomoyo_get_last_name - Get last component of a domainname. | |
111 | * | |
112 | * @domain: Pointer to "struct tomoyo_domain_info". | |
113 | * | |
114 | * Returns the last component of the domainname. | |
115 | */ | |
116 | const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) | |
117 | { | |
118 | const char *cp0 = domain->domainname->name; | |
119 | const char *cp1 = strrchr(cp0, ' '); | |
120 | ||
121 | if (cp1) | |
122 | return cp1 + 1; | |
123 | return cp0; | |
124 | } | |
125 | ||
c3fa109a TH |
126 | /* |
127 | * tomoyo_domain_initializer_list is used for holding list of programs which | |
128 | * triggers reinitialization of domainname. Normally, a domainname is | |
129 | * monotonically getting longer. But sometimes, we restart daemon programs. | |
130 | * It would be convenient for us that "a daemon started upon system boot" and | |
131 | * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO | |
132 | * provides a way to shorten domainnames. | |
133 | * | |
134 | * An entry is added by | |
135 | * | |
136 | * # echo 'initialize_domain /usr/sbin/httpd' > \ | |
137 | * /sys/kernel/security/tomoyo/exception_policy | |
138 | * | |
139 | * and is deleted by | |
140 | * | |
141 | * # echo 'delete initialize_domain /usr/sbin/httpd' > \ | |
142 | * /sys/kernel/security/tomoyo/exception_policy | |
143 | * | |
144 | * and all entries are retrieved by | |
145 | * | |
146 | * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy | |
147 | * | |
148 | * In the example above, /usr/sbin/httpd will belong to | |
149 | * "<kernel> /usr/sbin/httpd" domain. | |
150 | * | |
151 | * You may specify a domainname using "from" keyword. | |
152 | * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" | |
153 | * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd" | |
154 | * domain to belong to "<kernel> /usr/sbin/httpd" domain. | |
155 | * | |
156 | * You may add "no_" prefix to "initialize_domain". | |
157 | * "initialize_domain /usr/sbin/httpd" and | |
158 | * "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" | |
159 | * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain | |
160 | * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain. | |
161 | */ | |
847b173e | 162 | LIST_HEAD(tomoyo_domain_initializer_list); |
26a2a1c9 KT |
163 | |
164 | /** | |
165 | * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. | |
166 | * | |
167 | * @domainname: The name of domain. May be NULL. | |
168 | * @program: The name of program. | |
169 | * @is_not: True if it is "no_initialize_domain" entry. | |
170 | * @is_delete: True if it is a delete request. | |
171 | * | |
172 | * Returns 0 on success, negative value otherwise. | |
fdb8ebb7 TH |
173 | * |
174 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
175 | */ |
176 | static int tomoyo_update_domain_initializer_entry(const char *domainname, | |
177 | const char *program, | |
178 | const bool is_not, | |
179 | const bool is_delete) | |
180 | { | |
26a2a1c9 | 181 | struct tomoyo_domain_initializer_entry *ptr; |
9e4b50e9 | 182 | struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; |
ca0b7df3 | 183 | int error = is_delete ? -ENOENT : -ENOMEM; |
26a2a1c9 | 184 | |
3f629636 TH |
185 | if (!tomoyo_is_correct_path(program)) |
186 | return -EINVAL; | |
26a2a1c9 KT |
187 | if (domainname) { |
188 | if (!tomoyo_is_domain_def(domainname) && | |
3f629636 | 189 | tomoyo_is_correct_path(domainname)) |
9e4b50e9 | 190 | e.is_last_name = true; |
17080008 | 191 | else if (!tomoyo_is_correct_domain(domainname)) |
26a2a1c9 | 192 | return -EINVAL; |
9e4b50e9 TH |
193 | e.domainname = tomoyo_get_name(domainname); |
194 | if (!e.domainname) | |
ca0b7df3 | 195 | goto out; |
26a2a1c9 | 196 | } |
9e4b50e9 TH |
197 | e.program = tomoyo_get_name(program); |
198 | if (!e.program) | |
ca0b7df3 | 199 | goto out; |
29282381 TH |
200 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
201 | goto out; | |
82e0f001 TH |
202 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, |
203 | head.list) { | |
9e4b50e9 | 204 | if (!tomoyo_is_same_domain_initializer_entry(ptr, &e)) |
26a2a1c9 | 205 | continue; |
82e0f001 | 206 | ptr->head.is_deleted = is_delete; |
26a2a1c9 | 207 | error = 0; |
ca0b7df3 | 208 | break; |
26a2a1c9 | 209 | } |
9e4b50e9 TH |
210 | if (!is_delete && error) { |
211 | struct tomoyo_domain_initializer_entry *entry = | |
212 | tomoyo_commit_ok(&e, sizeof(e)); | |
213 | if (entry) { | |
82e0f001 | 214 | list_add_tail_rcu(&entry->head.list, |
9e4b50e9 TH |
215 | &tomoyo_domain_initializer_list); |
216 | error = 0; | |
217 | } | |
26a2a1c9 | 218 | } |
f737d95d | 219 | mutex_unlock(&tomoyo_policy_lock); |
ca0b7df3 | 220 | out: |
9e4b50e9 TH |
221 | tomoyo_put_name(e.domainname); |
222 | tomoyo_put_name(e.program); | |
26a2a1c9 KT |
223 | return error; |
224 | } | |
225 | ||
226 | /** | |
227 | * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list. | |
228 | * | |
229 | * @head: Pointer to "struct tomoyo_io_buffer". | |
230 | * | |
231 | * Returns true on success, false otherwise. | |
fdb8ebb7 TH |
232 | * |
233 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
234 | */ |
235 | bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) | |
236 | { | |
237 | struct list_head *pos; | |
238 | bool done = true; | |
239 | ||
26a2a1c9 KT |
240 | list_for_each_cookie(pos, head->read_var2, |
241 | &tomoyo_domain_initializer_list) { | |
242 | const char *no; | |
243 | const char *from = ""; | |
244 | const char *domain = ""; | |
245 | struct tomoyo_domain_initializer_entry *ptr; | |
246 | ptr = list_entry(pos, struct tomoyo_domain_initializer_entry, | |
82e0f001 TH |
247 | head.list); |
248 | if (ptr->head.is_deleted) | |
26a2a1c9 KT |
249 | continue; |
250 | no = ptr->is_not ? "no_" : ""; | |
251 | if (ptr->domainname) { | |
252 | from = " from "; | |
253 | domain = ptr->domainname->name; | |
254 | } | |
7d2948b1 TH |
255 | done = tomoyo_io_printf(head, |
256 | "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN | |
257 | "%s%s%s\n", no, ptr->program->name, | |
258 | from, domain); | |
259 | if (!done) | |
26a2a1c9 | 260 | break; |
26a2a1c9 | 261 | } |
26a2a1c9 KT |
262 | return done; |
263 | } | |
264 | ||
265 | /** | |
266 | * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list. | |
267 | * | |
268 | * @data: String to parse. | |
269 | * @is_not: True if it is "no_initialize_domain" entry. | |
270 | * @is_delete: True if it is a delete request. | |
271 | * | |
272 | * Returns 0 on success, negative value otherwise. | |
fdb8ebb7 TH |
273 | * |
274 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
275 | */ |
276 | int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, | |
277 | const bool is_delete) | |
278 | { | |
279 | char *cp = strstr(data, " from "); | |
280 | ||
281 | if (cp) { | |
282 | *cp = '\0'; | |
283 | return tomoyo_update_domain_initializer_entry(cp + 6, data, | |
284 | is_not, | |
285 | is_delete); | |
286 | } | |
287 | return tomoyo_update_domain_initializer_entry(NULL, data, is_not, | |
288 | is_delete); | |
289 | } | |
290 | ||
291 | /** | |
292 | * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization. | |
293 | * | |
294 | * @domainname: The name of domain. | |
295 | * @program: The name of program. | |
296 | * @last_name: The last component of @domainname. | |
297 | * | |
298 | * Returns true if executing @program reinitializes domain transition, | |
299 | * false otherwise. | |
fdb8ebb7 TH |
300 | * |
301 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
302 | */ |
303 | static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * | |
304 | domainname, | |
305 | const struct tomoyo_path_info *program, | |
306 | const struct tomoyo_path_info * | |
307 | last_name) | |
308 | { | |
309 | struct tomoyo_domain_initializer_entry *ptr; | |
310 | bool flag = false; | |
311 | ||
82e0f001 TH |
312 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, |
313 | head.list) { | |
314 | if (ptr->head.is_deleted) | |
26a2a1c9 KT |
315 | continue; |
316 | if (ptr->domainname) { | |
317 | if (!ptr->is_last_name) { | |
318 | if (ptr->domainname != domainname) | |
319 | continue; | |
320 | } else { | |
321 | if (tomoyo_pathcmp(ptr->domainname, last_name)) | |
322 | continue; | |
323 | } | |
324 | } | |
325 | if (tomoyo_pathcmp(ptr->program, program)) | |
326 | continue; | |
327 | if (ptr->is_not) { | |
328 | flag = false; | |
329 | break; | |
330 | } | |
331 | flag = true; | |
332 | } | |
26a2a1c9 KT |
333 | return flag; |
334 | } | |
335 | ||
c3fa109a TH |
336 | /* |
337 | * tomoyo_domain_keeper_list is used for holding list of domainnames which | |
338 | * suppresses domain transition. Normally, a domainname is monotonically | |
339 | * getting longer. But sometimes, we want to suppress domain transition. | |
340 | * It would be convenient for us that programs executed from a login session | |
341 | * belong to the same domain. Thus, TOMOYO provides a way to suppress domain | |
342 | * transition. | |
343 | * | |
344 | * An entry is added by | |
345 | * | |
346 | * # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \ | |
347 | * /sys/kernel/security/tomoyo/exception_policy | |
348 | * | |
349 | * and is deleted by | |
350 | * | |
351 | * # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \ | |
352 | * /sys/kernel/security/tomoyo/exception_policy | |
353 | * | |
354 | * and all entries are retrieved by | |
355 | * | |
356 | * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy | |
357 | * | |
358 | * In the example above, any process which belongs to | |
359 | * "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain, | |
360 | * unless explicitly specified by "initialize_domain" or "no_keep_domain". | |
361 | * | |
362 | * You may specify a program using "from" keyword. | |
363 | * "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash" | |
364 | * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash" | |
365 | * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain. | |
366 | * | |
367 | * You may add "no_" prefix to "keep_domain". | |
368 | * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and | |
369 | * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will | |
370 | * cause "/usr/bin/passwd" to belong to | |
371 | * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless | |
372 | * explicitly specified by "initialize_domain". | |
373 | */ | |
847b173e | 374 | LIST_HEAD(tomoyo_domain_keeper_list); |
26a2a1c9 KT |
375 | |
376 | /** | |
377 | * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. | |
378 | * | |
379 | * @domainname: The name of domain. | |
380 | * @program: The name of program. May be NULL. | |
381 | * @is_not: True if it is "no_keep_domain" entry. | |
382 | * @is_delete: True if it is a delete request. | |
383 | * | |
384 | * Returns 0 on success, negative value otherwise. | |
fdb8ebb7 TH |
385 | * |
386 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
387 | */ |
388 | static int tomoyo_update_domain_keeper_entry(const char *domainname, | |
389 | const char *program, | |
390 | const bool is_not, | |
391 | const bool is_delete) | |
392 | { | |
26a2a1c9 | 393 | struct tomoyo_domain_keeper_entry *ptr; |
9e4b50e9 | 394 | struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; |
ca0b7df3 | 395 | int error = is_delete ? -ENOENT : -ENOMEM; |
26a2a1c9 KT |
396 | |
397 | if (!tomoyo_is_domain_def(domainname) && | |
3f629636 | 398 | tomoyo_is_correct_path(domainname)) |
9e4b50e9 | 399 | e.is_last_name = true; |
17080008 | 400 | else if (!tomoyo_is_correct_domain(domainname)) |
26a2a1c9 KT |
401 | return -EINVAL; |
402 | if (program) { | |
3f629636 | 403 | if (!tomoyo_is_correct_path(program)) |
26a2a1c9 | 404 | return -EINVAL; |
9e4b50e9 TH |
405 | e.program = tomoyo_get_name(program); |
406 | if (!e.program) | |
ca0b7df3 | 407 | goto out; |
26a2a1c9 | 408 | } |
9e4b50e9 TH |
409 | e.domainname = tomoyo_get_name(domainname); |
410 | if (!e.domainname) | |
ca0b7df3 | 411 | goto out; |
29282381 TH |
412 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
413 | goto out; | |
82e0f001 | 414 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, head.list) { |
9e4b50e9 | 415 | if (!tomoyo_is_same_domain_keeper_entry(ptr, &e)) |
26a2a1c9 | 416 | continue; |
82e0f001 | 417 | ptr->head.is_deleted = is_delete; |
26a2a1c9 | 418 | error = 0; |
ca0b7df3 | 419 | break; |
26a2a1c9 | 420 | } |
9e4b50e9 TH |
421 | if (!is_delete && error) { |
422 | struct tomoyo_domain_keeper_entry *entry = | |
423 | tomoyo_commit_ok(&e, sizeof(e)); | |
424 | if (entry) { | |
82e0f001 | 425 | list_add_tail_rcu(&entry->head.list, |
9e4b50e9 TH |
426 | &tomoyo_domain_keeper_list); |
427 | error = 0; | |
428 | } | |
26a2a1c9 | 429 | } |
f737d95d | 430 | mutex_unlock(&tomoyo_policy_lock); |
ca0b7df3 | 431 | out: |
9e4b50e9 TH |
432 | tomoyo_put_name(e.domainname); |
433 | tomoyo_put_name(e.program); | |
26a2a1c9 KT |
434 | return error; |
435 | } | |
436 | ||
437 | /** | |
438 | * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list. | |
439 | * | |
440 | * @data: String to parse. | |
441 | * @is_not: True if it is "no_keep_domain" entry. | |
442 | * @is_delete: True if it is a delete request. | |
443 | * | |
fdb8ebb7 | 444 | * Caller holds tomoyo_read_lock(). |
26a2a1c9 KT |
445 | */ |
446 | int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, | |
447 | const bool is_delete) | |
448 | { | |
449 | char *cp = strstr(data, " from "); | |
450 | ||
451 | if (cp) { | |
452 | *cp = '\0'; | |
453 | return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not, | |
454 | is_delete); | |
455 | } | |
456 | return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete); | |
457 | } | |
458 | ||
459 | /** | |
460 | * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list. | |
461 | * | |
462 | * @head: Pointer to "struct tomoyo_io_buffer". | |
463 | * | |
464 | * Returns true on success, false otherwise. | |
fdb8ebb7 TH |
465 | * |
466 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
467 | */ |
468 | bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) | |
469 | { | |
470 | struct list_head *pos; | |
33043cbb | 471 | bool done = true; |
26a2a1c9 | 472 | |
26a2a1c9 KT |
473 | list_for_each_cookie(pos, head->read_var2, |
474 | &tomoyo_domain_keeper_list) { | |
475 | struct tomoyo_domain_keeper_entry *ptr; | |
476 | const char *no; | |
477 | const char *from = ""; | |
478 | const char *program = ""; | |
479 | ||
82e0f001 TH |
480 | ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, |
481 | head.list); | |
482 | if (ptr->head.is_deleted) | |
26a2a1c9 KT |
483 | continue; |
484 | no = ptr->is_not ? "no_" : ""; | |
485 | if (ptr->program) { | |
486 | from = " from "; | |
487 | program = ptr->program->name; | |
488 | } | |
7d2948b1 TH |
489 | done = tomoyo_io_printf(head, |
490 | "%s" TOMOYO_KEYWORD_KEEP_DOMAIN | |
491 | "%s%s%s\n", no, program, from, | |
492 | ptr->domainname->name); | |
493 | if (!done) | |
26a2a1c9 | 494 | break; |
26a2a1c9 | 495 | } |
26a2a1c9 KT |
496 | return done; |
497 | } | |
498 | ||
499 | /** | |
500 | * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression. | |
501 | * | |
502 | * @domainname: The name of domain. | |
503 | * @program: The name of program. | |
504 | * @last_name: The last component of @domainname. | |
505 | * | |
506 | * Returns true if executing @program supresses domain transition, | |
507 | * false otherwise. | |
fdb8ebb7 TH |
508 | * |
509 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
510 | */ |
511 | static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, | |
512 | const struct tomoyo_path_info *program, | |
513 | const struct tomoyo_path_info *last_name) | |
514 | { | |
515 | struct tomoyo_domain_keeper_entry *ptr; | |
516 | bool flag = false; | |
517 | ||
82e0f001 TH |
518 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, head.list) { |
519 | if (ptr->head.is_deleted) | |
26a2a1c9 KT |
520 | continue; |
521 | if (!ptr->is_last_name) { | |
522 | if (ptr->domainname != domainname) | |
523 | continue; | |
524 | } else { | |
525 | if (tomoyo_pathcmp(ptr->domainname, last_name)) | |
526 | continue; | |
527 | } | |
528 | if (ptr->program && tomoyo_pathcmp(ptr->program, program)) | |
529 | continue; | |
530 | if (ptr->is_not) { | |
531 | flag = false; | |
532 | break; | |
533 | } | |
534 | flag = true; | |
535 | } | |
26a2a1c9 KT |
536 | return flag; |
537 | } | |
538 | ||
1084307c TH |
539 | /* |
540 | * tomoyo_aggregator_list is used for holding list of rewrite table for | |
541 | * execve() request. Some programs provides similar functionality. This keyword | |
542 | * allows users to aggregate such programs. | |
543 | * | |
544 | * Entries are added by | |
545 | * | |
546 | * # echo 'aggregator /usr/bin/vi /./editor' > \ | |
547 | * /sys/kernel/security/tomoyo/exception_policy | |
548 | * # echo 'aggregator /usr/bin/emacs /./editor' > \ | |
549 | * /sys/kernel/security/tomoyo/exception_policy | |
550 | * | |
551 | * and are deleted by | |
552 | * | |
553 | * # echo 'delete aggregator /usr/bin/vi /./editor' > \ | |
554 | * /sys/kernel/security/tomoyo/exception_policy | |
555 | * # echo 'delete aggregator /usr/bin/emacs /./editor' > \ | |
556 | * /sys/kernel/security/tomoyo/exception_policy | |
557 | * | |
558 | * and all entries are retrieved by | |
559 | * | |
560 | * # grep ^aggregator /sys/kernel/security/tomoyo/exception_policy | |
561 | * | |
562 | * In the example above, if /usr/bin/vi or /usr/bin/emacs are executed, | |
563 | * permission is checked for /./editor and domainname which the current process | |
564 | * will belong to after execve() succeeds is calculated using /./editor . | |
565 | */ | |
566 | LIST_HEAD(tomoyo_aggregator_list); | |
567 | ||
568 | /** | |
569 | * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator_entry" list. | |
570 | * | |
571 | * @original_name: The original program's name. | |
572 | * @aggregated_name: The program name to use. | |
573 | * @is_delete: True if it is a delete request. | |
574 | * | |
575 | * Returns 0 on success, negative value otherwise. | |
576 | * | |
577 | * Caller holds tomoyo_read_lock(). | |
578 | */ | |
579 | static int tomoyo_update_aggregator_entry(const char *original_name, | |
580 | const char *aggregated_name, | |
581 | const bool is_delete) | |
582 | { | |
583 | struct tomoyo_aggregator_entry *ptr; | |
584 | struct tomoyo_aggregator_entry e = { }; | |
585 | int error = is_delete ? -ENOENT : -ENOMEM; | |
586 | ||
587 | if (!tomoyo_is_correct_path(original_name) || | |
588 | !tomoyo_is_correct_path(aggregated_name)) | |
589 | return -EINVAL; | |
590 | e.original_name = tomoyo_get_name(original_name); | |
591 | e.aggregated_name = tomoyo_get_name(aggregated_name); | |
592 | if (!e.original_name || !e.aggregated_name || | |
593 | e.aggregated_name->is_patterned) /* No patterns allowed. */ | |
594 | goto out; | |
595 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | |
596 | goto out; | |
82e0f001 | 597 | list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, head.list) { |
1084307c TH |
598 | if (!tomoyo_is_same_aggregator_entry(ptr, &e)) |
599 | continue; | |
82e0f001 | 600 | ptr->head.is_deleted = is_delete; |
1084307c TH |
601 | error = 0; |
602 | break; | |
603 | } | |
604 | if (!is_delete && error) { | |
605 | struct tomoyo_aggregator_entry *entry = | |
606 | tomoyo_commit_ok(&e, sizeof(e)); | |
607 | if (entry) { | |
82e0f001 | 608 | list_add_tail_rcu(&entry->head.list, |
1084307c TH |
609 | &tomoyo_aggregator_list); |
610 | error = 0; | |
611 | } | |
612 | } | |
613 | mutex_unlock(&tomoyo_policy_lock); | |
614 | out: | |
615 | tomoyo_put_name(e.original_name); | |
616 | tomoyo_put_name(e.aggregated_name); | |
617 | return error; | |
618 | } | |
619 | ||
620 | /** | |
621 | * tomoyo_read_aggregator_policy - Read "struct tomoyo_aggregator_entry" list. | |
622 | * | |
623 | * @head: Pointer to "struct tomoyo_io_buffer". | |
624 | * | |
625 | * Returns true on success, false otherwise. | |
626 | * | |
627 | * Caller holds tomoyo_read_lock(). | |
628 | */ | |
629 | bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head) | |
630 | { | |
631 | struct list_head *pos; | |
632 | bool done = true; | |
633 | ||
634 | list_for_each_cookie(pos, head->read_var2, &tomoyo_aggregator_list) { | |
635 | struct tomoyo_aggregator_entry *ptr; | |
636 | ||
82e0f001 TH |
637 | ptr = list_entry(pos, struct tomoyo_aggregator_entry, |
638 | head.list); | |
639 | if (ptr->head.is_deleted) | |
1084307c TH |
640 | continue; |
641 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_AGGREGATOR | |
642 | "%s %s\n", ptr->original_name->name, | |
643 | ptr->aggregated_name->name); | |
644 | if (!done) | |
645 | break; | |
646 | } | |
647 | return done; | |
648 | } | |
649 | ||
650 | /** | |
651 | * tomoyo_write_aggregator_policy - Write "struct tomoyo_aggregator_entry" list. | |
652 | * | |
653 | * @data: String to parse. | |
654 | * @is_delete: True if it is a delete request. | |
655 | * | |
656 | * Returns 0 on success, negative value otherwise. | |
657 | * | |
658 | * Caller holds tomoyo_read_lock(). | |
659 | */ | |
660 | int tomoyo_write_aggregator_policy(char *data, const bool is_delete) | |
661 | { | |
662 | char *cp = strchr(data, ' '); | |
663 | ||
664 | if (!cp) | |
665 | return -EINVAL; | |
666 | *cp++ = '\0'; | |
667 | return tomoyo_update_aggregator_entry(data, cp, is_delete); | |
668 | } | |
669 | ||
c3fa109a TH |
670 | /* |
671 | * tomoyo_alias_list is used for holding list of symlink's pathnames which are | |
672 | * allowed to be passed to an execve() request. Normally, the domainname which | |
673 | * the current process will belong to after execve() succeeds is calculated | |
674 | * using dereferenced pathnames. But some programs behave differently depending | |
675 | * on the name passed to argv[0]. For busybox, calculating domainname using | |
676 | * dereferenced pathnames will cause all programs in the busybox to belong to | |
677 | * the same domain. Thus, TOMOYO provides a way to allow use of symlink's | |
678 | * pathname for checking execve()'s permission and calculating domainname which | |
679 | * the current process will belong to after execve() succeeds. | |
680 | * | |
681 | * An entry is added by | |
682 | * | |
683 | * # echo 'alias /bin/busybox /bin/cat' > \ | |
684 | * /sys/kernel/security/tomoyo/exception_policy | |
685 | * | |
686 | * and is deleted by | |
687 | * | |
688 | * # echo 'delete alias /bin/busybox /bin/cat' > \ | |
689 | * /sys/kernel/security/tomoyo/exception_policy | |
690 | * | |
691 | * and all entries are retrieved by | |
692 | * | |
693 | * # grep ^alias /sys/kernel/security/tomoyo/exception_policy | |
694 | * | |
695 | * In the example above, if /bin/cat is a symlink to /bin/busybox and execution | |
696 | * of /bin/cat is requested, permission is checked for /bin/cat rather than | |
697 | * /bin/busybox and domainname which the current process will belong to after | |
698 | * execve() succeeds is calculated using /bin/cat rather than /bin/busybox . | |
699 | */ | |
847b173e | 700 | LIST_HEAD(tomoyo_alias_list); |
26a2a1c9 KT |
701 | |
702 | /** | |
703 | * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list. | |
704 | * | |
705 | * @original_name: The original program's real name. | |
706 | * @aliased_name: The symbolic program's symbolic link's name. | |
707 | * @is_delete: True if it is a delete request. | |
708 | * | |
709 | * Returns 0 on success, negative value otherwise. | |
fdb8ebb7 TH |
710 | * |
711 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
712 | */ |
713 | static int tomoyo_update_alias_entry(const char *original_name, | |
714 | const char *aliased_name, | |
715 | const bool is_delete) | |
716 | { | |
26a2a1c9 | 717 | struct tomoyo_alias_entry *ptr; |
9e4b50e9 | 718 | struct tomoyo_alias_entry e = { }; |
ca0b7df3 | 719 | int error = is_delete ? -ENOENT : -ENOMEM; |
26a2a1c9 | 720 | |
3f629636 TH |
721 | if (!tomoyo_is_correct_path(original_name) || |
722 | !tomoyo_is_correct_path(aliased_name)) | |
723 | return -EINVAL; | |
9e4b50e9 TH |
724 | e.original_name = tomoyo_get_name(original_name); |
725 | e.aliased_name = tomoyo_get_name(aliased_name); | |
3f629636 TH |
726 | if (!e.original_name || !e.aliased_name || |
727 | e.original_name->is_patterned || e.aliased_name->is_patterned) | |
728 | goto out; /* No patterns allowed. */ | |
29282381 TH |
729 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
730 | goto out; | |
82e0f001 | 731 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) { |
9e4b50e9 | 732 | if (!tomoyo_is_same_alias_entry(ptr, &e)) |
26a2a1c9 | 733 | continue; |
82e0f001 | 734 | ptr->head.is_deleted = is_delete; |
26a2a1c9 | 735 | error = 0; |
ca0b7df3 | 736 | break; |
26a2a1c9 | 737 | } |
9e4b50e9 TH |
738 | if (!is_delete && error) { |
739 | struct tomoyo_alias_entry *entry = | |
740 | tomoyo_commit_ok(&e, sizeof(e)); | |
741 | if (entry) { | |
82e0f001 TH |
742 | list_add_tail_rcu(&entry->head.list, |
743 | &tomoyo_alias_list); | |
9e4b50e9 TH |
744 | error = 0; |
745 | } | |
26a2a1c9 | 746 | } |
f737d95d | 747 | mutex_unlock(&tomoyo_policy_lock); |
ca0b7df3 | 748 | out: |
9e4b50e9 TH |
749 | tomoyo_put_name(e.original_name); |
750 | tomoyo_put_name(e.aliased_name); | |
26a2a1c9 KT |
751 | return error; |
752 | } | |
753 | ||
754 | /** | |
755 | * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list. | |
756 | * | |
757 | * @head: Pointer to "struct tomoyo_io_buffer". | |
758 | * | |
759 | * Returns true on success, false otherwise. | |
fdb8ebb7 TH |
760 | * |
761 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
762 | */ |
763 | bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) | |
764 | { | |
765 | struct list_head *pos; | |
766 | bool done = true; | |
767 | ||
26a2a1c9 KT |
768 | list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) { |
769 | struct tomoyo_alias_entry *ptr; | |
770 | ||
82e0f001 TH |
771 | ptr = list_entry(pos, struct tomoyo_alias_entry, head.list); |
772 | if (ptr->head.is_deleted) | |
26a2a1c9 | 773 | continue; |
7d2948b1 TH |
774 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n", |
775 | ptr->original_name->name, | |
776 | ptr->aliased_name->name); | |
777 | if (!done) | |
26a2a1c9 | 778 | break; |
26a2a1c9 | 779 | } |
26a2a1c9 KT |
780 | return done; |
781 | } | |
782 | ||
783 | /** | |
784 | * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list. | |
785 | * | |
786 | * @data: String to parse. | |
787 | * @is_delete: True if it is a delete request. | |
788 | * | |
789 | * Returns 0 on success, negative value otherwise. | |
fdb8ebb7 TH |
790 | * |
791 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
792 | */ |
793 | int tomoyo_write_alias_policy(char *data, const bool is_delete) | |
794 | { | |
795 | char *cp = strchr(data, ' '); | |
796 | ||
797 | if (!cp) | |
798 | return -EINVAL; | |
799 | *cp++ = '\0'; | |
800 | return tomoyo_update_alias_entry(data, cp, is_delete); | |
801 | } | |
802 | ||
26a2a1c9 KT |
803 | /** |
804 | * tomoyo_find_or_assign_new_domain - Create a domain. | |
805 | * | |
806 | * @domainname: The name of domain. | |
807 | * @profile: Profile number to assign if the domain was newly created. | |
808 | * | |
809 | * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise. | |
fdb8ebb7 TH |
810 | * |
811 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 KT |
812 | */ |
813 | struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * | |
814 | domainname, | |
815 | const u8 profile) | |
816 | { | |
ca0b7df3 | 817 | struct tomoyo_domain_info *entry; |
29282381 | 818 | struct tomoyo_domain_info *domain = NULL; |
26a2a1c9 | 819 | const struct tomoyo_path_info *saved_domainname; |
ca0b7df3 | 820 | bool found = false; |
26a2a1c9 | 821 | |
17080008 | 822 | if (!tomoyo_is_correct_domain(domainname)) |
ca0b7df3 | 823 | return NULL; |
bf24fb01 | 824 | saved_domainname = tomoyo_get_name(domainname); |
26a2a1c9 | 825 | if (!saved_domainname) |
ca0b7df3 | 826 | return NULL; |
4e5d6f7e | 827 | entry = kzalloc(sizeof(*entry), GFP_NOFS); |
29282381 TH |
828 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
829 | goto out; | |
ca0b7df3 TH |
830 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { |
831 | if (domain->is_deleted || | |
832 | tomoyo_pathcmp(saved_domainname, domain->domainname)) | |
833 | continue; | |
834 | found = true; | |
835 | break; | |
836 | } | |
837 | if (!found && tomoyo_memory_ok(entry)) { | |
838 | INIT_LIST_HEAD(&entry->acl_info_list); | |
839 | entry->domainname = saved_domainname; | |
bf24fb01 | 840 | saved_domainname = NULL; |
ca0b7df3 TH |
841 | entry->profile = profile; |
842 | list_add_tail_rcu(&entry->list, &tomoyo_domain_list); | |
843 | domain = entry; | |
844 | entry = NULL; | |
845 | found = true; | |
26a2a1c9 | 846 | } |
f737d95d | 847 | mutex_unlock(&tomoyo_policy_lock); |
29282381 | 848 | out: |
bf24fb01 | 849 | tomoyo_put_name(saved_domainname); |
ca0b7df3 TH |
850 | kfree(entry); |
851 | return found ? domain : NULL; | |
26a2a1c9 KT |
852 | } |
853 | ||
854 | /** | |
855 | * tomoyo_find_next_domain - Find a domain. | |
856 | * | |
56f8c9bc | 857 | * @bprm: Pointer to "struct linux_binprm". |
26a2a1c9 KT |
858 | * |
859 | * Returns 0 on success, negative value otherwise. | |
fdb8ebb7 TH |
860 | * |
861 | * Caller holds tomoyo_read_lock(). | |
26a2a1c9 | 862 | */ |
56f8c9bc | 863 | int tomoyo_find_next_domain(struct linux_binprm *bprm) |
26a2a1c9 | 864 | { |
17fcfbd9 | 865 | struct tomoyo_request_info r; |
c8c57e84 | 866 | char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); |
26a2a1c9 KT |
867 | struct tomoyo_domain_info *old_domain = tomoyo_domain(); |
868 | struct tomoyo_domain_info *domain = NULL; | |
869 | const char *old_domain_name = old_domain->domainname->name; | |
870 | const char *original_name = bprm->filename; | |
57c2590f TH |
871 | u8 mode; |
872 | bool is_enforce; | |
26a2a1c9 | 873 | int retval = -ENOMEM; |
c8c57e84 TH |
874 | bool need_kfree = false; |
875 | struct tomoyo_path_info rn = { }; /* real name */ | |
876 | struct tomoyo_path_info sn = { }; /* symlink name */ | |
17fcfbd9 | 877 | struct tomoyo_path_info ln; /* last name */ |
26a2a1c9 | 878 | |
c8c57e84 TH |
879 | ln.name = tomoyo_get_last_name(old_domain); |
880 | tomoyo_fill_path_info(&ln); | |
57c2590f TH |
881 | mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); |
882 | is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); | |
26a2a1c9 KT |
883 | if (!tmp) |
884 | goto out; | |
885 | ||
17fcfbd9 | 886 | retry: |
c8c57e84 TH |
887 | if (need_kfree) { |
888 | kfree(rn.name); | |
889 | need_kfree = false; | |
890 | } | |
26a2a1c9 KT |
891 | /* Get tomoyo_realpath of program. */ |
892 | retval = -ENOENT; | |
c8c57e84 TH |
893 | rn.name = tomoyo_realpath(original_name); |
894 | if (!rn.name) | |
26a2a1c9 | 895 | goto out; |
c8c57e84 TH |
896 | tomoyo_fill_path_info(&rn); |
897 | need_kfree = true; | |
898 | ||
26a2a1c9 | 899 | /* Get tomoyo_realpath of symbolic link. */ |
c8c57e84 TH |
900 | sn.name = tomoyo_realpath_nofollow(original_name); |
901 | if (!sn.name) | |
26a2a1c9 | 902 | goto out; |
17fcfbd9 | 903 | tomoyo_fill_path_info(&sn); |
26a2a1c9 KT |
904 | |
905 | /* Check 'alias' directive. */ | |
17fcfbd9 | 906 | if (tomoyo_pathcmp(&rn, &sn)) { |
26a2a1c9 KT |
907 | struct tomoyo_alias_entry *ptr; |
908 | /* Is this program allowed to be called via symbolic links? */ | |
82e0f001 TH |
909 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) { |
910 | if (ptr->head.is_deleted || | |
17fcfbd9 TH |
911 | tomoyo_pathcmp(&rn, ptr->original_name) || |
912 | tomoyo_pathcmp(&sn, ptr->aliased_name)) | |
26a2a1c9 | 913 | continue; |
c8c57e84 TH |
914 | kfree(rn.name); |
915 | need_kfree = false; | |
916 | /* This is OK because it is read only. */ | |
917 | rn = *ptr->aliased_name; | |
26a2a1c9 KT |
918 | break; |
919 | } | |
26a2a1c9 KT |
920 | } |
921 | ||
1084307c TH |
922 | /* Check 'aggregator' directive. */ |
923 | { | |
924 | struct tomoyo_aggregator_entry *ptr; | |
82e0f001 TH |
925 | list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, |
926 | head.list) { | |
927 | if (ptr->head.is_deleted || | |
1084307c TH |
928 | !tomoyo_path_matches_pattern(&rn, |
929 | ptr->original_name)) | |
930 | continue; | |
931 | if (need_kfree) | |
932 | kfree(rn.name); | |
933 | need_kfree = false; | |
934 | /* This is OK because it is read only. */ | |
935 | rn = *ptr->aggregated_name; | |
936 | break; | |
937 | } | |
938 | } | |
939 | ||
26a2a1c9 | 940 | /* Check execute permission. */ |
57c2590f | 941 | retval = tomoyo_check_exec_perm(&r, &rn); |
17fcfbd9 TH |
942 | if (retval == TOMOYO_RETRY_REQUEST) |
943 | goto retry; | |
26a2a1c9 KT |
944 | if (retval < 0) |
945 | goto out; | |
946 | ||
17fcfbd9 | 947 | if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) { |
26a2a1c9 | 948 | /* Transit to the child of tomoyo_kernel_domain domain. */ |
c8c57e84 TH |
949 | snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, |
950 | TOMOYO_ROOT_NAME " " "%s", rn.name); | |
26a2a1c9 KT |
951 | } else if (old_domain == &tomoyo_kernel_domain && |
952 | !tomoyo_policy_loaded) { | |
953 | /* | |
954 | * Needn't to transit from kernel domain before starting | |
955 | * /sbin/init. But transit from kernel domain if executing | |
956 | * initializers because they might start before /sbin/init. | |
957 | */ | |
958 | domain = old_domain; | |
17fcfbd9 | 959 | } else if (tomoyo_is_domain_keeper(old_domain->domainname, &rn, &ln)) { |
26a2a1c9 KT |
960 | /* Keep current domain. */ |
961 | domain = old_domain; | |
962 | } else { | |
963 | /* Normal domain transition. */ | |
c8c57e84 TH |
964 | snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, |
965 | "%s %s", old_domain_name, rn.name); | |
26a2a1c9 | 966 | } |
c8c57e84 | 967 | if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) |
26a2a1c9 | 968 | goto done; |
c8c57e84 | 969 | domain = tomoyo_find_domain(tmp); |
26a2a1c9 KT |
970 | if (domain) |
971 | goto done; | |
17fcfbd9 TH |
972 | if (is_enforce) { |
973 | int error = tomoyo_supervisor(&r, "# wants to create domain\n" | |
c8c57e84 | 974 | "%s\n", tmp); |
17fcfbd9 TH |
975 | if (error == TOMOYO_RETRY_REQUEST) |
976 | goto retry; | |
977 | if (error < 0) | |
978 | goto done; | |
979 | } | |
c8c57e84 | 980 | domain = tomoyo_find_or_assign_new_domain(tmp, old_domain->profile); |
26a2a1c9 KT |
981 | done: |
982 | if (domain) | |
983 | goto out; | |
c8c57e84 | 984 | printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp); |
26a2a1c9 KT |
985 | if (is_enforce) |
986 | retval = -EPERM; | |
987 | else | |
ea13ddba | 988 | old_domain->transition_failed = true; |
26a2a1c9 | 989 | out: |
56f8c9bc TH |
990 | if (!domain) |
991 | domain = old_domain; | |
ec8e6a4e TH |
992 | /* Update reference count on "struct tomoyo_domain_info". */ |
993 | atomic_inc(&domain->users); | |
56f8c9bc | 994 | bprm->cred->security = domain; |
c8c57e84 TH |
995 | if (need_kfree) |
996 | kfree(rn.name); | |
997 | kfree(sn.name); | |
8e2d39a1 | 998 | kfree(tmp); |
26a2a1c9 KT |
999 | return retval; |
1000 | } |