]> bbs.cooldavid.org Git - net-next-2.6.git/blame - security/tomoyo/domain.c
TOMOYO: Add refcounter on domain structure.
[net-next-2.6.git] / security / tomoyo / domain.c
CommitLineData
26a2a1c9
KT
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 *
39826a1e 8 * Version: 2.2.0 2009/04/01
26a2a1c9
KT
9 *
10 */
11
12#include "common.h"
26a2a1c9
KT
13#include <linux/binfmts.h>
14
15/* Variables definitions.*/
16
17/* The initial domain. */
18struct tomoyo_domain_info tomoyo_kernel_domain;
19
c3fa109a
TH
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 */
26a2a1c9 58LIST_HEAD(tomoyo_domain_list);
26a2a1c9 59
26a2a1c9
KT
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
c3fa109a
TH
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 */
26a2a1c9 113static LIST_HEAD(tomoyo_domain_initializer_list);
26a2a1c9
KT
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.
fdb8ebb7
TH
124 *
125 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
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{
ca0b7df3 132 struct tomoyo_domain_initializer_entry *entry = NULL;
26a2a1c9 133 struct tomoyo_domain_initializer_entry *ptr;
bf24fb01 134 const struct tomoyo_path_info *saved_program = NULL;
26a2a1c9 135 const struct tomoyo_path_info *saved_domainname = NULL;
ca0b7df3 136 int error = is_delete ? -ENOENT : -ENOMEM;
26a2a1c9
KT
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;
bf24fb01 147 saved_domainname = tomoyo_get_name(domainname);
26a2a1c9 148 if (!saved_domainname)
ca0b7df3 149 goto out;
26a2a1c9 150 }
bf24fb01 151 saved_program = tomoyo_get_name(program);
26a2a1c9 152 if (!saved_program)
ca0b7df3
TH
153 goto out;
154 if (!is_delete)
155 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
f737d95d 156 mutex_lock(&tomoyo_policy_lock);
fdb8ebb7 157 list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
26a2a1c9
KT
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;
ca0b7df3 164 break;
26a2a1c9 165 }
ca0b7df3
TH
166 if (!is_delete && error && tomoyo_memory_ok(entry)) {
167 entry->domainname = saved_domainname;
bf24fb01 168 saved_domainname = NULL;
ca0b7df3 169 entry->program = saved_program;
bf24fb01 170 saved_program = NULL;
ca0b7df3
TH
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;
26a2a1c9 177 }
f737d95d 178 mutex_unlock(&tomoyo_policy_lock);
ca0b7df3 179 out:
bf24fb01
TH
180 tomoyo_put_name(saved_domainname);
181 tomoyo_put_name(saved_program);
ca0b7df3 182 kfree(entry);
26a2a1c9
KT
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.
fdb8ebb7
TH
192 *
193 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
194 */
195bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
196{
197 struct list_head *pos;
198 bool done = true;
199
26a2a1c9
KT
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 }
7d2948b1
TH
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)
26a2a1c9 220 break;
26a2a1c9 221 }
26a2a1c9
KT
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.
fdb8ebb7
TH
233 *
234 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
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.
fdb8ebb7
TH
260 *
261 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
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
fdb8ebb7 272 list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
26a2a1c9
KT
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 }
26a2a1c9
KT
292 return flag;
293}
294
c3fa109a
TH
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 */
26a2a1c9 333static LIST_HEAD(tomoyo_domain_keeper_list);
26a2a1c9
KT
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.
fdb8ebb7
TH
344 *
345 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
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{
ca0b7df3 352 struct tomoyo_domain_keeper_entry *entry = NULL;
26a2a1c9 353 struct tomoyo_domain_keeper_entry *ptr;
bf24fb01 354 const struct tomoyo_path_info *saved_domainname = NULL;
26a2a1c9 355 const struct tomoyo_path_info *saved_program = NULL;
ca0b7df3 356 int error = is_delete ? -ENOENT : -ENOMEM;
26a2a1c9
KT
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;
bf24fb01 367 saved_program = tomoyo_get_name(program);
26a2a1c9 368 if (!saved_program)
ca0b7df3 369 goto out;
26a2a1c9 370 }
bf24fb01 371 saved_domainname = tomoyo_get_name(domainname);
26a2a1c9 372 if (!saved_domainname)
ca0b7df3
TH
373 goto out;
374 if (!is_delete)
375 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
f737d95d 376 mutex_lock(&tomoyo_policy_lock);
fdb8ebb7 377 list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
26a2a1c9
KT
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;
ca0b7df3 384 break;
26a2a1c9 385 }
ca0b7df3
TH
386 if (!is_delete && error && tomoyo_memory_ok(entry)) {
387 entry->domainname = saved_domainname;
bf24fb01 388 saved_domainname = NULL;
ca0b7df3 389 entry->program = saved_program;
bf24fb01 390 saved_program = NULL;
ca0b7df3
TH
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;
26a2a1c9 396 }
f737d95d 397 mutex_unlock(&tomoyo_policy_lock);
ca0b7df3 398 out:
bf24fb01
TH
399 tomoyo_put_name(saved_domainname);
400 tomoyo_put_name(saved_program);
ca0b7df3 401 kfree(entry);
26a2a1c9
KT
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 *
fdb8ebb7 412 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
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.
fdb8ebb7
TH
433 *
434 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
435 */
436bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
437{
438 struct list_head *pos;
33043cbb 439 bool done = true;
26a2a1c9 440
26a2a1c9
KT
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 }
7d2948b1
TH
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)
26a2a1c9 461 break;
26a2a1c9 462 }
26a2a1c9
KT
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.
fdb8ebb7
TH
475 *
476 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
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
fdb8ebb7 485 list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
26a2a1c9
KT
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 }
26a2a1c9
KT
503 return flag;
504}
505
c3fa109a
TH
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 */
26a2a1c9 536static LIST_HEAD(tomoyo_alias_list);
26a2a1c9
KT
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.
fdb8ebb7
TH
546 *
547 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
548 */
549static int tomoyo_update_alias_entry(const char *original_name,
550 const char *aliased_name,
551 const bool is_delete)
552{
ca0b7df3 553 struct tomoyo_alias_entry *entry = NULL;
26a2a1c9
KT
554 struct tomoyo_alias_entry *ptr;
555 const struct tomoyo_path_info *saved_original_name;
556 const struct tomoyo_path_info *saved_aliased_name;
ca0b7df3 557 int error = is_delete ? -ENOENT : -ENOMEM;
26a2a1c9
KT
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. */
bf24fb01
TH
562 saved_original_name = tomoyo_get_name(original_name);
563 saved_aliased_name = tomoyo_get_name(aliased_name);
26a2a1c9 564 if (!saved_original_name || !saved_aliased_name)
ca0b7df3
TH
565 goto out;
566 if (!is_delete)
567 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
f737d95d 568 mutex_lock(&tomoyo_policy_lock);
fdb8ebb7 569 list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
26a2a1c9
KT
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;
ca0b7df3 575 break;
26a2a1c9 576 }
ca0b7df3
TH
577 if (!is_delete && error && tomoyo_memory_ok(entry)) {
578 entry->original_name = saved_original_name;
bf24fb01 579 saved_original_name = NULL;
ca0b7df3 580 entry->aliased_name = saved_aliased_name;
bf24fb01 581 saved_aliased_name = NULL;
ca0b7df3
TH
582 list_add_tail_rcu(&entry->list, &tomoyo_alias_list);
583 entry = NULL;
584 error = 0;
26a2a1c9 585 }
f737d95d 586 mutex_unlock(&tomoyo_policy_lock);
ca0b7df3 587 out:
bf24fb01
TH
588 tomoyo_put_name(saved_original_name);
589 tomoyo_put_name(saved_aliased_name);
ca0b7df3 590 kfree(entry);
26a2a1c9
KT
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.
fdb8ebb7
TH
600 *
601 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
602 */
603bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
604{
605 struct list_head *pos;
606 bool done = true;
607
26a2a1c9
KT
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;
7d2948b1
TH
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)
26a2a1c9 618 break;
26a2a1c9 619 }
26a2a1c9
KT
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.
fdb8ebb7
TH
630 *
631 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
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
26a2a1c9
KT
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.
fdb8ebb7
TH
650 *
651 * Caller holds tomoyo_read_lock().
26a2a1c9
KT
652 */
653struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
654 domainname,
655 const u8 profile)
656{
ca0b7df3 657 struct tomoyo_domain_info *entry;
cd7bec6a 658 struct tomoyo_domain_info *domain;
26a2a1c9 659 const struct tomoyo_path_info *saved_domainname;
ca0b7df3 660 bool found = false;
26a2a1c9 661
26a2a1c9 662 if (!tomoyo_is_correct_domain(domainname, __func__))
ca0b7df3 663 return NULL;
bf24fb01 664 saved_domainname = tomoyo_get_name(domainname);
26a2a1c9 665 if (!saved_domainname)
ca0b7df3
TH
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;
bf24fb01 679 saved_domainname = NULL;
ca0b7df3
TH
680 entry->profile = profile;
681 list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
682 domain = entry;
683 entry = NULL;
684 found = true;
26a2a1c9 685 }
f737d95d 686 mutex_unlock(&tomoyo_policy_lock);
bf24fb01 687 tomoyo_put_name(saved_domainname);
ca0b7df3
TH
688 kfree(entry);
689 return found ? domain : NULL;
26a2a1c9
KT
690}
691
692/**
693 * tomoyo_find_next_domain - Find a domain.
694 *
56f8c9bc 695 * @bprm: Pointer to "struct linux_binprm".
26a2a1c9
KT
696 *
697 * Returns 0 on success, negative value otherwise.
fdb8ebb7
TH
698 *
699 * Caller holds tomoyo_read_lock().
26a2a1c9 700 */
56f8c9bc 701int tomoyo_find_next_domain(struct linux_binprm *bprm)
26a2a1c9
KT
702{
703 /*
704 * This function assumes that the size of buffer returned by
705 * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
706 */
8e2d39a1 707 struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
26a2a1c9
KT
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? */
fdb8ebb7 760 list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
26a2a1c9
KT
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 }
26a2a1c9
KT
771 }
772
773 /* Check execute permission. */
bcb86975 774 retval = tomoyo_check_exec_perm(old_domain, &r);
26a2a1c9
KT
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;
26a2a1c9 801 domain = tomoyo_find_domain(new_domain_name);
26a2a1c9
KT
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
ea13ddba 816 old_domain->transition_failed = true;
26a2a1c9 817 out:
56f8c9bc
TH
818 if (!domain)
819 domain = old_domain;
ec8e6a4e
TH
820 /* Update reference count on "struct tomoyo_domain_info". */
821 atomic_inc(&domain->users);
56f8c9bc 822 bprm->cred->security = domain;
8e2d39a1
TH
823 kfree(real_program_name);
824 kfree(symlink_program_name);
825 kfree(tmp);
26a2a1c9
KT
826 return retval;
827}