]> bbs.cooldavid.org Git - net-next-2.6.git/blame_incremental - security/tomoyo/domain.c
TOMOYO: Add refcounter on domain structure.
[net-next-2.6.git] / security / tomoyo / domain.c
... / ...
CommitLineData
1/*
2 * security/tomoyo/domain.c
3 *
4 * Implementation of the Domain-Based Mandatory Access Control.
5 *
6 * Copyright (C) 2005-2009 NTT DATA CORPORATION
7 *
8 * Version: 2.2.0 2009/04/01
9 *
10 */
11
12#include "common.h"
13#include <linux/binfmts.h>
14
15/* Variables definitions.*/
16
17/* The initial domain. */
18struct tomoyo_domain_info tomoyo_kernel_domain;
19
20/*
21 * tomoyo_domain_list is used for holding list of domains.
22 * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding
23 * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
24 *
25 * An entry is added by
26 *
27 * # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \
28 * /sys/kernel/security/tomoyo/domain_policy
29 *
30 * and is deleted by
31 *
32 * # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \
33 * /sys/kernel/security/tomoyo/domain_policy
34 *
35 * and all entries are retrieved by
36 *
37 * # cat /sys/kernel/security/tomoyo/domain_policy
38 *
39 * A domain is added by
40 *
41 * # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy
42 *
43 * and is deleted by
44 *
45 * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy
46 *
47 * and all domains are retrieved by
48 *
49 * # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy
50 *
51 * Normally, a domainname is monotonically getting longer because a domainname
52 * which the process will belong to if an execve() operation succeeds is
53 * defined as a concatenation of "current domainname" + "pathname passed to
54 * execve()".
55 * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for
56 * exceptions.
57 */
58LIST_HEAD(tomoyo_domain_list);
59
60/**
61 * tomoyo_get_last_name - Get last component of a domainname.
62 *
63 * @domain: Pointer to "struct tomoyo_domain_info".
64 *
65 * Returns the last component of the domainname.
66 */
67const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
68{
69 const char *cp0 = domain->domainname->name;
70 const char *cp1 = strrchr(cp0, ' ');
71
72 if (cp1)
73 return cp1 + 1;
74 return cp0;
75}
76
77/*
78 * tomoyo_domain_initializer_list is used for holding list of programs which
79 * triggers reinitialization of domainname. Normally, a domainname is
80 * monotonically getting longer. But sometimes, we restart daemon programs.
81 * It would be convenient for us that "a daemon started upon system boot" and
82 * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO
83 * provides a way to shorten domainnames.
84 *
85 * An entry is added by
86 *
87 * # echo 'initialize_domain /usr/sbin/httpd' > \
88 * /sys/kernel/security/tomoyo/exception_policy
89 *
90 * and is deleted by
91 *
92 * # echo 'delete initialize_domain /usr/sbin/httpd' > \
93 * /sys/kernel/security/tomoyo/exception_policy
94 *
95 * and all entries are retrieved by
96 *
97 * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy
98 *
99 * In the example above, /usr/sbin/httpd will belong to
100 * "<kernel> /usr/sbin/httpd" domain.
101 *
102 * You may specify a domainname using "from" keyword.
103 * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
104 * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd"
105 * domain to belong to "<kernel> /usr/sbin/httpd" domain.
106 *
107 * You may add "no_" prefix to "initialize_domain".
108 * "initialize_domain /usr/sbin/httpd" and
109 * "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
110 * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
111 * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
112 */
113static LIST_HEAD(tomoyo_domain_initializer_list);
114
115/**
116 * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
117 *
118 * @domainname: The name of domain. May be NULL.
119 * @program: The name of program.
120 * @is_not: True if it is "no_initialize_domain" entry.
121 * @is_delete: True if it is a delete request.
122 *
123 * Returns 0 on success, negative value otherwise.
124 *
125 * Caller holds tomoyo_read_lock().
126 */
127static int tomoyo_update_domain_initializer_entry(const char *domainname,
128 const char *program,
129 const bool is_not,
130 const bool is_delete)
131{
132 struct tomoyo_domain_initializer_entry *entry = NULL;
133 struct tomoyo_domain_initializer_entry *ptr;
134 const struct tomoyo_path_info *saved_program = NULL;
135 const struct tomoyo_path_info *saved_domainname = NULL;
136 int error = is_delete ? -ENOENT : -ENOMEM;
137 bool is_last_name = false;
138
139 if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
140 return -EINVAL; /* No patterns allowed. */
141 if (domainname) {
142 if (!tomoyo_is_domain_def(domainname) &&
143 tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
144 is_last_name = true;
145 else if (!tomoyo_is_correct_domain(domainname, __func__))
146 return -EINVAL;
147 saved_domainname = tomoyo_get_name(domainname);
148 if (!saved_domainname)
149 goto out;
150 }
151 saved_program = tomoyo_get_name(program);
152 if (!saved_program)
153 goto out;
154 if (!is_delete)
155 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
156 mutex_lock(&tomoyo_policy_lock);
157 list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
158 if (ptr->is_not != is_not ||
159 ptr->domainname != saved_domainname ||
160 ptr->program != saved_program)
161 continue;
162 ptr->is_deleted = is_delete;
163 error = 0;
164 break;
165 }
166 if (!is_delete && error && tomoyo_memory_ok(entry)) {
167 entry->domainname = saved_domainname;
168 saved_domainname = NULL;
169 entry->program = saved_program;
170 saved_program = NULL;
171 entry->is_not = is_not;
172 entry->is_last_name = is_last_name;
173 list_add_tail_rcu(&entry->list,
174 &tomoyo_domain_initializer_list);
175 entry = NULL;
176 error = 0;
177 }
178 mutex_unlock(&tomoyo_policy_lock);
179 out:
180 tomoyo_put_name(saved_domainname);
181 tomoyo_put_name(saved_program);
182 kfree(entry);
183 return error;
184}
185
186/**
187 * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
188 *
189 * @head: Pointer to "struct tomoyo_io_buffer".
190 *
191 * Returns true on success, false otherwise.
192 *
193 * Caller holds tomoyo_read_lock().
194 */
195bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
196{
197 struct list_head *pos;
198 bool done = true;
199
200 list_for_each_cookie(pos, head->read_var2,
201 &tomoyo_domain_initializer_list) {
202 const char *no;
203 const char *from = "";
204 const char *domain = "";
205 struct tomoyo_domain_initializer_entry *ptr;
206 ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
207 list);
208 if (ptr->is_deleted)
209 continue;
210 no = ptr->is_not ? "no_" : "";
211 if (ptr->domainname) {
212 from = " from ";
213 domain = ptr->domainname->name;
214 }
215 done = tomoyo_io_printf(head,
216 "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
217 "%s%s%s\n", no, ptr->program->name,
218 from, domain);
219 if (!done)
220 break;
221 }
222 return done;
223}
224
225/**
226 * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list.
227 *
228 * @data: String to parse.
229 * @is_not: True if it is "no_initialize_domain" entry.
230 * @is_delete: True if it is a delete request.
231 *
232 * Returns 0 on success, negative value otherwise.
233 *
234 * Caller holds tomoyo_read_lock().
235 */
236int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
237 const bool is_delete)
238{
239 char *cp = strstr(data, " from ");
240
241 if (cp) {
242 *cp = '\0';
243 return tomoyo_update_domain_initializer_entry(cp + 6, data,
244 is_not,
245 is_delete);
246 }
247 return tomoyo_update_domain_initializer_entry(NULL, data, is_not,
248 is_delete);
249}
250
251/**
252 * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization.
253 *
254 * @domainname: The name of domain.
255 * @program: The name of program.
256 * @last_name: The last component of @domainname.
257 *
258 * Returns true if executing @program reinitializes domain transition,
259 * false otherwise.
260 *
261 * Caller holds tomoyo_read_lock().
262 */
263static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
264 domainname,
265 const struct tomoyo_path_info *program,
266 const struct tomoyo_path_info *
267 last_name)
268{
269 struct tomoyo_domain_initializer_entry *ptr;
270 bool flag = false;
271
272 list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
273 if (ptr->is_deleted)
274 continue;
275 if (ptr->domainname) {
276 if (!ptr->is_last_name) {
277 if (ptr->domainname != domainname)
278 continue;
279 } else {
280 if (tomoyo_pathcmp(ptr->domainname, last_name))
281 continue;
282 }
283 }
284 if (tomoyo_pathcmp(ptr->program, program))
285 continue;
286 if (ptr->is_not) {
287 flag = false;
288 break;
289 }
290 flag = true;
291 }
292 return flag;
293}
294
295/*
296 * tomoyo_domain_keeper_list is used for holding list of domainnames which
297 * suppresses domain transition. Normally, a domainname is monotonically
298 * getting longer. But sometimes, we want to suppress domain transition.
299 * It would be convenient for us that programs executed from a login session
300 * belong to the same domain. Thus, TOMOYO provides a way to suppress domain
301 * transition.
302 *
303 * An entry is added by
304 *
305 * # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
306 * /sys/kernel/security/tomoyo/exception_policy
307 *
308 * and is deleted by
309 *
310 * # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
311 * /sys/kernel/security/tomoyo/exception_policy
312 *
313 * and all entries are retrieved by
314 *
315 * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy
316 *
317 * In the example above, any process which belongs to
318 * "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain,
319 * unless explicitly specified by "initialize_domain" or "no_keep_domain".
320 *
321 * You may specify a program using "from" keyword.
322 * "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash"
323 * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash"
324 * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain.
325 *
326 * You may add "no_" prefix to "keep_domain".
327 * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and
328 * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will
329 * cause "/usr/bin/passwd" to belong to
330 * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
331 * explicitly specified by "initialize_domain".
332 */
333static LIST_HEAD(tomoyo_domain_keeper_list);
334
335/**
336 * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
337 *
338 * @domainname: The name of domain.
339 * @program: The name of program. May be NULL.
340 * @is_not: True if it is "no_keep_domain" entry.
341 * @is_delete: True if it is a delete request.
342 *
343 * Returns 0 on success, negative value otherwise.
344 *
345 * Caller holds tomoyo_read_lock().
346 */
347static int tomoyo_update_domain_keeper_entry(const char *domainname,
348 const char *program,
349 const bool is_not,
350 const bool is_delete)
351{
352 struct tomoyo_domain_keeper_entry *entry = NULL;
353 struct tomoyo_domain_keeper_entry *ptr;
354 const struct tomoyo_path_info *saved_domainname = NULL;
355 const struct tomoyo_path_info *saved_program = NULL;
356 int error = is_delete ? -ENOENT : -ENOMEM;
357 bool is_last_name = false;
358
359 if (!tomoyo_is_domain_def(domainname) &&
360 tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
361 is_last_name = true;
362 else if (!tomoyo_is_correct_domain(domainname, __func__))
363 return -EINVAL;
364 if (program) {
365 if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
366 return -EINVAL;
367 saved_program = tomoyo_get_name(program);
368 if (!saved_program)
369 goto out;
370 }
371 saved_domainname = tomoyo_get_name(domainname);
372 if (!saved_domainname)
373 goto out;
374 if (!is_delete)
375 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
376 mutex_lock(&tomoyo_policy_lock);
377 list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
378 if (ptr->is_not != is_not ||
379 ptr->domainname != saved_domainname ||
380 ptr->program != saved_program)
381 continue;
382 ptr->is_deleted = is_delete;
383 error = 0;
384 break;
385 }
386 if (!is_delete && error && tomoyo_memory_ok(entry)) {
387 entry->domainname = saved_domainname;
388 saved_domainname = NULL;
389 entry->program = saved_program;
390 saved_program = NULL;
391 entry->is_not = is_not;
392 entry->is_last_name = is_last_name;
393 list_add_tail_rcu(&entry->list, &tomoyo_domain_keeper_list);
394 entry = NULL;
395 error = 0;
396 }
397 mutex_unlock(&tomoyo_policy_lock);
398 out:
399 tomoyo_put_name(saved_domainname);
400 tomoyo_put_name(saved_program);
401 kfree(entry);
402 return error;
403}
404
405/**
406 * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
407 *
408 * @data: String to parse.
409 * @is_not: True if it is "no_keep_domain" entry.
410 * @is_delete: True if it is a delete request.
411 *
412 * Caller holds tomoyo_read_lock().
413 */
414int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
415 const bool is_delete)
416{
417 char *cp = strstr(data, " from ");
418
419 if (cp) {
420 *cp = '\0';
421 return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not,
422 is_delete);
423 }
424 return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete);
425}
426
427/**
428 * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
429 *
430 * @head: Pointer to "struct tomoyo_io_buffer".
431 *
432 * Returns true on success, false otherwise.
433 *
434 * Caller holds tomoyo_read_lock().
435 */
436bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
437{
438 struct list_head *pos;
439 bool done = true;
440
441 list_for_each_cookie(pos, head->read_var2,
442 &tomoyo_domain_keeper_list) {
443 struct tomoyo_domain_keeper_entry *ptr;
444 const char *no;
445 const char *from = "";
446 const char *program = "";
447
448 ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list);
449 if (ptr->is_deleted)
450 continue;
451 no = ptr->is_not ? "no_" : "";
452 if (ptr->program) {
453 from = " from ";
454 program = ptr->program->name;
455 }
456 done = tomoyo_io_printf(head,
457 "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
458 "%s%s%s\n", no, program, from,
459 ptr->domainname->name);
460 if (!done)
461 break;
462 }
463 return done;
464}
465
466/**
467 * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression.
468 *
469 * @domainname: The name of domain.
470 * @program: The name of program.
471 * @last_name: The last component of @domainname.
472 *
473 * Returns true if executing @program supresses domain transition,
474 * false otherwise.
475 *
476 * Caller holds tomoyo_read_lock().
477 */
478static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
479 const struct tomoyo_path_info *program,
480 const struct tomoyo_path_info *last_name)
481{
482 struct tomoyo_domain_keeper_entry *ptr;
483 bool flag = false;
484
485 list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
486 if (ptr->is_deleted)
487 continue;
488 if (!ptr->is_last_name) {
489 if (ptr->domainname != domainname)
490 continue;
491 } else {
492 if (tomoyo_pathcmp(ptr->domainname, last_name))
493 continue;
494 }
495 if (ptr->program && tomoyo_pathcmp(ptr->program, program))
496 continue;
497 if (ptr->is_not) {
498 flag = false;
499 break;
500 }
501 flag = true;
502 }
503 return flag;
504}
505
506/*
507 * tomoyo_alias_list is used for holding list of symlink's pathnames which are
508 * allowed to be passed to an execve() request. Normally, the domainname which
509 * the current process will belong to after execve() succeeds is calculated
510 * using dereferenced pathnames. But some programs behave differently depending
511 * on the name passed to argv[0]. For busybox, calculating domainname using
512 * dereferenced pathnames will cause all programs in the busybox to belong to
513 * the same domain. Thus, TOMOYO provides a way to allow use of symlink's
514 * pathname for checking execve()'s permission and calculating domainname which
515 * the current process will belong to after execve() succeeds.
516 *
517 * An entry is added by
518 *
519 * # echo 'alias /bin/busybox /bin/cat' > \
520 * /sys/kernel/security/tomoyo/exception_policy
521 *
522 * and is deleted by
523 *
524 * # echo 'delete alias /bin/busybox /bin/cat' > \
525 * /sys/kernel/security/tomoyo/exception_policy
526 *
527 * and all entries are retrieved by
528 *
529 * # grep ^alias /sys/kernel/security/tomoyo/exception_policy
530 *
531 * In the example above, if /bin/cat is a symlink to /bin/busybox and execution
532 * of /bin/cat is requested, permission is checked for /bin/cat rather than
533 * /bin/busybox and domainname which the current process will belong to after
534 * execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
535 */
536static LIST_HEAD(tomoyo_alias_list);
537
538/**
539 * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
540 *
541 * @original_name: The original program's real name.
542 * @aliased_name: The symbolic program's symbolic link's name.
543 * @is_delete: True if it is a delete request.
544 *
545 * Returns 0 on success, negative value otherwise.
546 *
547 * Caller holds tomoyo_read_lock().
548 */
549static int tomoyo_update_alias_entry(const char *original_name,
550 const char *aliased_name,
551 const bool is_delete)
552{
553 struct tomoyo_alias_entry *entry = NULL;
554 struct tomoyo_alias_entry *ptr;
555 const struct tomoyo_path_info *saved_original_name;
556 const struct tomoyo_path_info *saved_aliased_name;
557 int error = is_delete ? -ENOENT : -ENOMEM;
558
559 if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) ||
560 !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__))
561 return -EINVAL; /* No patterns allowed. */
562 saved_original_name = tomoyo_get_name(original_name);
563 saved_aliased_name = tomoyo_get_name(aliased_name);
564 if (!saved_original_name || !saved_aliased_name)
565 goto out;
566 if (!is_delete)
567 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
568 mutex_lock(&tomoyo_policy_lock);
569 list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
570 if (ptr->original_name != saved_original_name ||
571 ptr->aliased_name != saved_aliased_name)
572 continue;
573 ptr->is_deleted = is_delete;
574 error = 0;
575 break;
576 }
577 if (!is_delete && error && tomoyo_memory_ok(entry)) {
578 entry->original_name = saved_original_name;
579 saved_original_name = NULL;
580 entry->aliased_name = saved_aliased_name;
581 saved_aliased_name = NULL;
582 list_add_tail_rcu(&entry->list, &tomoyo_alias_list);
583 entry = NULL;
584 error = 0;
585 }
586 mutex_unlock(&tomoyo_policy_lock);
587 out:
588 tomoyo_put_name(saved_original_name);
589 tomoyo_put_name(saved_aliased_name);
590 kfree(entry);
591 return error;
592}
593
594/**
595 * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list.
596 *
597 * @head: Pointer to "struct tomoyo_io_buffer".
598 *
599 * Returns true on success, false otherwise.
600 *
601 * Caller holds tomoyo_read_lock().
602 */
603bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
604{
605 struct list_head *pos;
606 bool done = true;
607
608 list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
609 struct tomoyo_alias_entry *ptr;
610
611 ptr = list_entry(pos, struct tomoyo_alias_entry, list);
612 if (ptr->is_deleted)
613 continue;
614 done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
615 ptr->original_name->name,
616 ptr->aliased_name->name);
617 if (!done)
618 break;
619 }
620 return done;
621}
622
623/**
624 * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list.
625 *
626 * @data: String to parse.
627 * @is_delete: True if it is a delete request.
628 *
629 * Returns 0 on success, negative value otherwise.
630 *
631 * Caller holds tomoyo_read_lock().
632 */
633int tomoyo_write_alias_policy(char *data, const bool is_delete)
634{
635 char *cp = strchr(data, ' ');
636
637 if (!cp)
638 return -EINVAL;
639 *cp++ = '\0';
640 return tomoyo_update_alias_entry(data, cp, is_delete);
641}
642
643/**
644 * tomoyo_find_or_assign_new_domain - Create a domain.
645 *
646 * @domainname: The name of domain.
647 * @profile: Profile number to assign if the domain was newly created.
648 *
649 * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
650 *
651 * Caller holds tomoyo_read_lock().
652 */
653struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
654 domainname,
655 const u8 profile)
656{
657 struct tomoyo_domain_info *entry;
658 struct tomoyo_domain_info *domain;
659 const struct tomoyo_path_info *saved_domainname;
660 bool found = false;
661
662 if (!tomoyo_is_correct_domain(domainname, __func__))
663 return NULL;
664 saved_domainname = tomoyo_get_name(domainname);
665 if (!saved_domainname)
666 return NULL;
667 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
668 mutex_lock(&tomoyo_policy_lock);
669 list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
670 if (domain->is_deleted ||
671 tomoyo_pathcmp(saved_domainname, domain->domainname))
672 continue;
673 found = true;
674 break;
675 }
676 if (!found && tomoyo_memory_ok(entry)) {
677 INIT_LIST_HEAD(&entry->acl_info_list);
678 entry->domainname = saved_domainname;
679 saved_domainname = NULL;
680 entry->profile = profile;
681 list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
682 domain = entry;
683 entry = NULL;
684 found = true;
685 }
686 mutex_unlock(&tomoyo_policy_lock);
687 tomoyo_put_name(saved_domainname);
688 kfree(entry);
689 return found ? domain : NULL;
690}
691
692/**
693 * tomoyo_find_next_domain - Find a domain.
694 *
695 * @bprm: Pointer to "struct linux_binprm".
696 *
697 * Returns 0 on success, negative value otherwise.
698 *
699 * Caller holds tomoyo_read_lock().
700 */
701int tomoyo_find_next_domain(struct linux_binprm *bprm)
702{
703 /*
704 * This function assumes that the size of buffer returned by
705 * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
706 */
707 struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
708 struct tomoyo_domain_info *old_domain = tomoyo_domain();
709 struct tomoyo_domain_info *domain = NULL;
710 const char *old_domain_name = old_domain->domainname->name;
711 const char *original_name = bprm->filename;
712 char *new_domain_name = NULL;
713 char *real_program_name = NULL;
714 char *symlink_program_name = NULL;
715 const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE);
716 const bool is_enforce = (mode == 3);
717 int retval = -ENOMEM;
718 struct tomoyo_path_info r; /* real name */
719 struct tomoyo_path_info s; /* symlink name */
720 struct tomoyo_path_info l; /* last name */
721 static bool initialized;
722
723 if (!tmp)
724 goto out;
725
726 if (!initialized) {
727 /*
728 * Built-in initializers. This is needed because policies are
729 * not loaded until starting /sbin/init.
730 */
731 tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug",
732 false, false);
733 tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe",
734 false, false);
735 initialized = true;
736 }
737
738 /* Get tomoyo_realpath of program. */
739 retval = -ENOENT;
740 /* I hope tomoyo_realpath() won't fail with -ENOMEM. */
741 real_program_name = tomoyo_realpath(original_name);
742 if (!real_program_name)
743 goto out;
744 /* Get tomoyo_realpath of symbolic link. */
745 symlink_program_name = tomoyo_realpath_nofollow(original_name);
746 if (!symlink_program_name)
747 goto out;
748
749 r.name = real_program_name;
750 tomoyo_fill_path_info(&r);
751 s.name = symlink_program_name;
752 tomoyo_fill_path_info(&s);
753 l.name = tomoyo_get_last_name(old_domain);
754 tomoyo_fill_path_info(&l);
755
756 /* Check 'alias' directive. */
757 if (tomoyo_pathcmp(&r, &s)) {
758 struct tomoyo_alias_entry *ptr;
759 /* Is this program allowed to be called via symbolic links? */
760 list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
761 if (ptr->is_deleted ||
762 tomoyo_pathcmp(&r, ptr->original_name) ||
763 tomoyo_pathcmp(&s, ptr->aliased_name))
764 continue;
765 memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN);
766 strncpy(real_program_name, ptr->aliased_name->name,
767 TOMOYO_MAX_PATHNAME_LEN - 1);
768 tomoyo_fill_path_info(&r);
769 break;
770 }
771 }
772
773 /* Check execute permission. */
774 retval = tomoyo_check_exec_perm(old_domain, &r);
775 if (retval < 0)
776 goto out;
777
778 new_domain_name = tmp->buffer;
779 if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) {
780 /* Transit to the child of tomoyo_kernel_domain domain. */
781 snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
782 TOMOYO_ROOT_NAME " " "%s", real_program_name);
783 } else if (old_domain == &tomoyo_kernel_domain &&
784 !tomoyo_policy_loaded) {
785 /*
786 * Needn't to transit from kernel domain before starting
787 * /sbin/init. But transit from kernel domain if executing
788 * initializers because they might start before /sbin/init.
789 */
790 domain = old_domain;
791 } else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) {
792 /* Keep current domain. */
793 domain = old_domain;
794 } else {
795 /* Normal domain transition. */
796 snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
797 "%s %s", old_domain_name, real_program_name);
798 }
799 if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
800 goto done;
801 domain = tomoyo_find_domain(new_domain_name);
802 if (domain)
803 goto done;
804 if (is_enforce)
805 goto done;
806 domain = tomoyo_find_or_assign_new_domain(new_domain_name,
807 old_domain->profile);
808 done:
809 if (domain)
810 goto out;
811 printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
812 new_domain_name);
813 if (is_enforce)
814 retval = -EPERM;
815 else
816 old_domain->transition_failed = true;
817 out:
818 if (!domain)
819 domain = old_domain;
820 /* Update reference count on "struct tomoyo_domain_info". */
821 atomic_inc(&domain->users);
822 bprm->cred->security = domain;
823 kfree(real_program_name);
824 kfree(symlink_program_name);
825 kfree(tmp);
826 return retval;
827}