domain.c 28.5 KB
Newer Older
K
Kentaro Takeda 已提交
1 2 3
/*
 * security/tomoyo/domain.c
 *
4
 * Domain transition functions for TOMOYO.
K
Kentaro Takeda 已提交
5
 *
6
 * Copyright (C) 2005-2010  NTT DATA CORPORATION
K
Kentaro Takeda 已提交
7 8 9 10
 */

#include "common.h"
#include <linux/binfmts.h>
11
#include <linux/slab.h>
K
Kentaro Takeda 已提交
12 13 14 15 16 17

/* Variables definitions.*/

/* The initial domain. */
struct tomoyo_domain_info tomoyo_kernel_domain;

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
/**
 * tomoyo_update_domain - Update an entry for domain policy.
 *
 * @new_entry:       Pointer to "struct tomoyo_acl_info".
 * @size:            Size of @new_entry in bytes.
 * @is_delete:       True if it is a delete request.
 * @domain:          Pointer to "struct tomoyo_domain_info".
 * @check_duplicate: Callback function to find duplicated entry.
 * @merge_duplicate: Callback function to merge duplicated entry.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
			 bool is_delete, struct tomoyo_domain_info *domain,
			 bool (*check_duplicate) (const struct tomoyo_acl_info
						  *,
						  const struct tomoyo_acl_info
						  *),
			 bool (*merge_duplicate) (struct tomoyo_acl_info *,
						  struct tomoyo_acl_info *,
						  const bool))
{
	int error = is_delete ? -ENOENT : -ENOMEM;
	struct tomoyo_acl_info *entry;

	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		return error;
	list_for_each_entry_rcu(entry, &domain->acl_info_list, list) {
		if (!check_duplicate(entry, new_entry))
			continue;
		if (merge_duplicate)
			entry->is_deleted = merge_duplicate(entry, new_entry,
							    is_delete);
		else
			entry->is_deleted = is_delete;
		error = 0;
		break;
	}
	if (error && !is_delete) {
		entry = tomoyo_commit_ok(new_entry, size);
		if (entry) {
			list_add_tail_rcu(&entry->list, &domain->acl_info_list);
			error = 0;
		}
	}
	mutex_unlock(&tomoyo_policy_lock);
	return error;
}

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
/*
 * tomoyo_domain_list is used for holding list of domains.
 * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding
 * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
 *
 * An entry is added by
 *
 * # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \
 *                                  /sys/kernel/security/tomoyo/domain_policy
 *
 * and is deleted by
 *
 * # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \
 *                                  /sys/kernel/security/tomoyo/domain_policy
 *
 * and all entries are retrieved by
 *
 * # cat /sys/kernel/security/tomoyo/domain_policy
 *
 * A domain is added by
 *
 * # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy
 *
 * and is deleted by
 *
 * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy
 *
 * and all domains are retrieved by
 *
 * # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy
 *
 * Normally, a domainname is monotonically getting longer because a domainname
 * which the process will belong to if an execve() operation succeeds is
 * defined as a concatenation of "current domainname" + "pathname passed to
 * execve()".
 * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for
 * exceptions.
 */
K
Kentaro Takeda 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
LIST_HEAD(tomoyo_domain_list);

/**
 * tomoyo_get_last_name - Get last component of a domainname.
 *
 * @domain: Pointer to "struct tomoyo_domain_info".
 *
 * Returns the last component of the domainname.
 */
const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
{
	const char *cp0 = domain->domainname->name;
	const char *cp1 = strrchr(cp0, ' ');

	if (cp1)
		return cp1 + 1;
	return cp0;
}

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
/*
 * tomoyo_domain_initializer_list is used for holding list of programs which
 * triggers reinitialization of domainname. Normally, a domainname is
 * monotonically getting longer. But sometimes, we restart daemon programs.
 * It would be convenient for us that "a daemon started upon system boot" and
 * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO
 * provides a way to shorten domainnames.
 *
 * An entry is added by
 *
 * # echo 'initialize_domain /usr/sbin/httpd' > \
 *                               /sys/kernel/security/tomoyo/exception_policy
 *
 * and is deleted by
 *
 * # echo 'delete initialize_domain /usr/sbin/httpd' > \
 *                               /sys/kernel/security/tomoyo/exception_policy
 *
 * and all entries are retrieved by
 *
 * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy
 *
 * In the example above, /usr/sbin/httpd will belong to
 * "<kernel> /usr/sbin/httpd" domain.
 *
 * You may specify a domainname using "from" keyword.
 * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
 * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd"
 * domain to belong to "<kernel> /usr/sbin/httpd" domain.
 *
 * You may add "no_" prefix to "initialize_domain".
 * "initialize_domain /usr/sbin/httpd" and
 * "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
 * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
 * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
 */
T
Tetsuo Handa 已提交
162
LIST_HEAD(tomoyo_domain_initializer_list);
K
Kentaro Takeda 已提交
163 164 165 166 167 168 169 170 171 172

/**
 * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
 *
 * @domainname: The name of domain. May be NULL.
 * @program:    The name of program.
 * @is_not:     True if it is "no_initialize_domain" entry.
 * @is_delete:  True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
173 174
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
175 176 177 178 179 180 181
 */
static int tomoyo_update_domain_initializer_entry(const char *domainname,
						  const char *program,
						  const bool is_not,
						  const bool is_delete)
{
	struct tomoyo_domain_initializer_entry *ptr;
182
	struct tomoyo_domain_initializer_entry e = { .is_not = is_not };
183
	int error = is_delete ? -ENOENT : -ENOMEM;
K
Kentaro Takeda 已提交
184

185 186
	if (!tomoyo_is_correct_path(program))
		return -EINVAL;
K
Kentaro Takeda 已提交
187 188
	if (domainname) {
		if (!tomoyo_is_domain_def(domainname) &&
189
		    tomoyo_is_correct_path(domainname))
190
			e.is_last_name = true;
191
		else if (!tomoyo_is_correct_domain(domainname))
K
Kentaro Takeda 已提交
192
			return -EINVAL;
193 194
		e.domainname = tomoyo_get_name(domainname);
		if (!e.domainname)
195
			goto out;
K
Kentaro Takeda 已提交
196
	}
197 198
	e.program = tomoyo_get_name(program);
	if (!e.program)
199
		goto out;
200 201
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
202 203
	list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list,
				head.list) {
204
		if (!tomoyo_is_same_domain_initializer_entry(ptr, &e))
K
Kentaro Takeda 已提交
205
			continue;
206
		ptr->head.is_deleted = is_delete;
K
Kentaro Takeda 已提交
207
		error = 0;
208
		break;
K
Kentaro Takeda 已提交
209
	}
210 211 212 213
	if (!is_delete && error) {
		struct tomoyo_domain_initializer_entry *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
214
			list_add_tail_rcu(&entry->head.list,
215 216 217
					  &tomoyo_domain_initializer_list);
			error = 0;
		}
K
Kentaro Takeda 已提交
218
	}
219
	mutex_unlock(&tomoyo_policy_lock);
220
 out:
221 222
	tomoyo_put_name(e.domainname);
	tomoyo_put_name(e.program);
K
Kentaro Takeda 已提交
223 224 225 226 227 228 229 230 231
	return error;
}

/**
 * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns true on success, false otherwise.
232 233
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
234 235 236 237 238 239 240 241 242 243 244 245 246
 */
bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
{
	struct list_head *pos;
	bool done = true;

	list_for_each_cookie(pos, head->read_var2,
			     &tomoyo_domain_initializer_list) {
		const char *no;
		const char *from = "";
		const char *domain = "";
		struct tomoyo_domain_initializer_entry *ptr;
		ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
247 248
				 head.list);
		if (ptr->head.is_deleted)
K
Kentaro Takeda 已提交
249 250 251 252 253 254
			continue;
		no = ptr->is_not ? "no_" : "";
		if (ptr->domainname) {
			from = " from ";
			domain = ptr->domainname->name;
		}
T
Tetsuo Handa 已提交
255 256 257 258 259
		done = tomoyo_io_printf(head,
					"%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
					"%s%s%s\n", no, ptr->program->name,
					from, domain);
		if (!done)
K
Kentaro Takeda 已提交
260 261 262 263 264 265 266 267 268 269 270 271 272
			break;
	}
	return done;
}

/**
 * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list.
 *
 * @data:      String to parse.
 * @is_not:    True if it is "no_initialize_domain" entry.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
273 274
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
 */
int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
					   const bool is_delete)
{
	char *cp = strstr(data, " from ");

	if (cp) {
		*cp = '\0';
		return tomoyo_update_domain_initializer_entry(cp + 6, data,
							      is_not,
							      is_delete);
	}
	return tomoyo_update_domain_initializer_entry(NULL, data, is_not,
						      is_delete);
}

/**
 * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization.
 *
 * @domainname: The name of domain.
 * @program:    The name of program.
 * @last_name:  The last component of @domainname.
 *
 * Returns true if executing @program reinitializes domain transition,
 * false otherwise.
300 301
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
302 303 304 305 306 307 308 309 310 311
 */
static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
					 domainname,
					 const struct tomoyo_path_info *program,
					 const struct tomoyo_path_info *
					 last_name)
{
	struct tomoyo_domain_initializer_entry *ptr;
	bool flag = false;

312 313 314
	list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list,
				head.list) {
		if (ptr->head.is_deleted)
K
Kentaro Takeda 已提交
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
			continue;
		if (ptr->domainname) {
			if (!ptr->is_last_name) {
				if (ptr->domainname != domainname)
					continue;
			} else {
				if (tomoyo_pathcmp(ptr->domainname, last_name))
					continue;
			}
		}
		if (tomoyo_pathcmp(ptr->program, program))
			continue;
		if (ptr->is_not) {
			flag = false;
			break;
		}
		flag = true;
	}
	return flag;
}

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
/*
 * tomoyo_domain_keeper_list is used for holding list of domainnames which
 * suppresses domain transition. Normally, a domainname is monotonically
 * getting longer. But sometimes, we want to suppress domain transition.
 * It would be convenient for us that programs executed from a login session
 * belong to the same domain. Thus, TOMOYO provides a way to suppress domain
 * transition.
 *
 * An entry is added by
 *
 * # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
 *                              /sys/kernel/security/tomoyo/exception_policy
 *
 * and is deleted by
 *
 * # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
 *                              /sys/kernel/security/tomoyo/exception_policy
 *
 * and all entries are retrieved by
 *
 * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy
 *
 * In the example above, any process which belongs to
 * "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain,
 * unless explicitly specified by "initialize_domain" or "no_keep_domain".
 *
 * You may specify a program using "from" keyword.
 * "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash"
 * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash"
 * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain.
 *
 * You may add "no_" prefix to "keep_domain".
 * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and
 * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will
 * cause "/usr/bin/passwd" to belong to
 * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
 * explicitly specified by "initialize_domain".
 */
T
Tetsuo Handa 已提交
374
LIST_HEAD(tomoyo_domain_keeper_list);
K
Kentaro Takeda 已提交
375 376 377 378 379 380 381 382 383 384

/**
 * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
 *
 * @domainname: The name of domain.
 * @program:    The name of program. May be NULL.
 * @is_not:     True if it is "no_keep_domain" entry.
 * @is_delete:  True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
385 386
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
387 388 389 390 391 392 393
 */
static int tomoyo_update_domain_keeper_entry(const char *domainname,
					     const char *program,
					     const bool is_not,
					     const bool is_delete)
{
	struct tomoyo_domain_keeper_entry *ptr;
394
	struct tomoyo_domain_keeper_entry e = { .is_not = is_not };
395
	int error = is_delete ? -ENOENT : -ENOMEM;
K
Kentaro Takeda 已提交
396 397

	if (!tomoyo_is_domain_def(domainname) &&
398
	    tomoyo_is_correct_path(domainname))
399
		e.is_last_name = true;
400
	else if (!tomoyo_is_correct_domain(domainname))
K
Kentaro Takeda 已提交
401 402
		return -EINVAL;
	if (program) {
403
		if (!tomoyo_is_correct_path(program))
K
Kentaro Takeda 已提交
404
			return -EINVAL;
405 406
		e.program = tomoyo_get_name(program);
		if (!e.program)
407
			goto out;
K
Kentaro Takeda 已提交
408
	}
409 410
	e.domainname = tomoyo_get_name(domainname);
	if (!e.domainname)
411
		goto out;
412 413
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
414
	list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, head.list) {
415
		if (!tomoyo_is_same_domain_keeper_entry(ptr, &e))
K
Kentaro Takeda 已提交
416
			continue;
417
		ptr->head.is_deleted = is_delete;
K
Kentaro Takeda 已提交
418
		error = 0;
419
		break;
K
Kentaro Takeda 已提交
420
	}
421 422 423 424
	if (!is_delete && error) {
		struct tomoyo_domain_keeper_entry *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
425
			list_add_tail_rcu(&entry->head.list,
426 427 428
					  &tomoyo_domain_keeper_list);
			error = 0;
		}
K
Kentaro Takeda 已提交
429
	}
430
	mutex_unlock(&tomoyo_policy_lock);
431
 out:
432 433
	tomoyo_put_name(e.domainname);
	tomoyo_put_name(e.program);
K
Kentaro Takeda 已提交
434 435 436 437 438 439 440 441 442 443
	return error;
}

/**
 * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
 *
 * @data:      String to parse.
 * @is_not:    True if it is "no_keep_domain" entry.
 * @is_delete: True if it is a delete request.
 *
444
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
 */
int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
				      const bool is_delete)
{
	char *cp = strstr(data, " from ");

	if (cp) {
		*cp = '\0';
		return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not,
							 is_delete);
	}
	return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete);
}

/**
 * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns true on success, false otherwise.
465 466
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
467 468 469 470
 */
bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
{
	struct list_head *pos;
471
	bool done = true;
K
Kentaro Takeda 已提交
472 473 474 475 476 477 478 479

	list_for_each_cookie(pos, head->read_var2,
			     &tomoyo_domain_keeper_list) {
		struct tomoyo_domain_keeper_entry *ptr;
		const char *no;
		const char *from = "";
		const char *program = "";

480 481 482
		ptr = list_entry(pos, struct tomoyo_domain_keeper_entry,
				 head.list);
		if (ptr->head.is_deleted)
K
Kentaro Takeda 已提交
483 484 485 486 487 488
			continue;
		no = ptr->is_not ? "no_" : "";
		if (ptr->program) {
			from = " from ";
			program = ptr->program->name;
		}
T
Tetsuo Handa 已提交
489 490 491 492 493
		done = tomoyo_io_printf(head,
					"%s" TOMOYO_KEYWORD_KEEP_DOMAIN
					"%s%s%s\n", no, program, from,
					ptr->domainname->name);
		if (!done)
K
Kentaro Takeda 已提交
494 495 496 497 498 499 500 501 502 503 504 505 506 507
			break;
	}
	return done;
}

/**
 * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression.
 *
 * @domainname: The name of domain.
 * @program:    The name of program.
 * @last_name:  The last component of @domainname.
 *
 * Returns true if executing @program supresses domain transition,
 * false otherwise.
508 509
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
510 511 512 513 514 515 516 517
 */
static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
				    const struct tomoyo_path_info *program,
				    const struct tomoyo_path_info *last_name)
{
	struct tomoyo_domain_keeper_entry *ptr;
	bool flag = false;

518 519
	list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, head.list) {
		if (ptr->head.is_deleted)
K
Kentaro Takeda 已提交
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
			continue;
		if (!ptr->is_last_name) {
			if (ptr->domainname != domainname)
				continue;
		} else {
			if (tomoyo_pathcmp(ptr->domainname, last_name))
				continue;
		}
		if (ptr->program && tomoyo_pathcmp(ptr->program, program))
			continue;
		if (ptr->is_not) {
			flag = false;
			break;
		}
		flag = true;
	}
	return flag;
}

539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
/*
 * tomoyo_aggregator_list is used for holding list of rewrite table for
 * execve() request. Some programs provides similar functionality. This keyword
 * allows users to aggregate such programs.
 *
 * Entries are added by
 *
 * # echo 'aggregator /usr/bin/vi /./editor' > \
 *                            /sys/kernel/security/tomoyo/exception_policy
 * # echo 'aggregator /usr/bin/emacs /./editor' > \
 *                            /sys/kernel/security/tomoyo/exception_policy
 *
 * and are deleted by
 *
 * # echo 'delete aggregator /usr/bin/vi /./editor' > \
 *                            /sys/kernel/security/tomoyo/exception_policy
 * # echo 'delete aggregator /usr/bin/emacs /./editor' > \
 *                            /sys/kernel/security/tomoyo/exception_policy
 *
 * and all entries are retrieved by
 *
 * # grep ^aggregator /sys/kernel/security/tomoyo/exception_policy
 *
 * In the example above, if /usr/bin/vi or /usr/bin/emacs are executed,
 * permission is checked for /./editor and domainname which the current process
 * will belong to after execve() succeeds is calculated using /./editor .
 */
LIST_HEAD(tomoyo_aggregator_list);

/**
 * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator_entry" list.
 *
 * @original_name:   The original program's name.
 * @aggregated_name: The program name to use.
 * @is_delete:       True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
static int tomoyo_update_aggregator_entry(const char *original_name,
					  const char *aggregated_name,
					  const bool is_delete)
{
	struct tomoyo_aggregator_entry *ptr;
	struct tomoyo_aggregator_entry e = { };
	int error = is_delete ? -ENOENT : -ENOMEM;

	if (!tomoyo_is_correct_path(original_name) ||
	    !tomoyo_is_correct_path(aggregated_name))
		return -EINVAL;
	e.original_name = tomoyo_get_name(original_name);
	e.aggregated_name = tomoyo_get_name(aggregated_name);
	if (!e.original_name || !e.aggregated_name ||
	    e.aggregated_name->is_patterned) /* No patterns allowed. */
		goto out;
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
597
	list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, head.list) {
598 599
		if (!tomoyo_is_same_aggregator_entry(ptr, &e))
			continue;
600
		ptr->head.is_deleted = is_delete;
601 602 603 604 605 606 607
		error = 0;
		break;
	}
	if (!is_delete && error) {
		struct tomoyo_aggregator_entry *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
608
			list_add_tail_rcu(&entry->head.list,
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
					  &tomoyo_aggregator_list);
			error = 0;
		}
	}
	mutex_unlock(&tomoyo_policy_lock);
 out:
	tomoyo_put_name(e.original_name);
	tomoyo_put_name(e.aggregated_name);
	return error;
}

/**
 * tomoyo_read_aggregator_policy - Read "struct tomoyo_aggregator_entry" list.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns true on success, false otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head)
{
	struct list_head *pos;
	bool done = true;

	list_for_each_cookie(pos, head->read_var2, &tomoyo_aggregator_list) {
		struct tomoyo_aggregator_entry *ptr;

637 638 639
		ptr = list_entry(pos, struct tomoyo_aggregator_entry,
				 head.list);
		if (ptr->head.is_deleted)
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
			continue;
		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_AGGREGATOR
					"%s %s\n", ptr->original_name->name,
					ptr->aggregated_name->name);
		if (!done)
			break;
	}
	return done;
}

/**
 * tomoyo_write_aggregator_policy - Write "struct tomoyo_aggregator_entry" list.
 *
 * @data:      String to parse.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
int tomoyo_write_aggregator_policy(char *data, const bool is_delete)
{
	char *cp = strchr(data, ' ');

	if (!cp)
		return -EINVAL;
	*cp++ = '\0';
	return tomoyo_update_aggregator_entry(data, cp, is_delete);
}

670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
/*
 * tomoyo_alias_list is used for holding list of symlink's pathnames which are
 * allowed to be passed to an execve() request. Normally, the domainname which
 * the current process will belong to after execve() succeeds is calculated
 * using dereferenced pathnames. But some programs behave differently depending
 * on the name passed to argv[0]. For busybox, calculating domainname using
 * dereferenced pathnames will cause all programs in the busybox to belong to
 * the same domain. Thus, TOMOYO provides a way to allow use of symlink's
 * pathname for checking execve()'s permission and calculating domainname which
 * the current process will belong to after execve() succeeds.
 *
 * An entry is added by
 *
 * # echo 'alias /bin/busybox /bin/cat' > \
 *                            /sys/kernel/security/tomoyo/exception_policy
 *
 * and is deleted by
 *
 * # echo 'delete alias /bin/busybox /bin/cat' > \
 *                            /sys/kernel/security/tomoyo/exception_policy
 *
 * and all entries are retrieved by
 *
 * # grep ^alias /sys/kernel/security/tomoyo/exception_policy
 *
 * In the example above, if /bin/cat is a symlink to /bin/busybox and execution
 * of /bin/cat is requested, permission is checked for /bin/cat rather than
 * /bin/busybox and domainname which the current process will belong to after
 * execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
 */
T
Tetsuo Handa 已提交
700
LIST_HEAD(tomoyo_alias_list);
K
Kentaro Takeda 已提交
701 702 703 704 705 706 707 708 709

/**
 * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
 *
 * @original_name: The original program's real name.
 * @aliased_name:  The symbolic program's symbolic link's name.
 * @is_delete:     True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
710 711
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
712 713 714 715 716 717
 */
static int tomoyo_update_alias_entry(const char *original_name,
				     const char *aliased_name,
				     const bool is_delete)
{
	struct tomoyo_alias_entry *ptr;
718
	struct tomoyo_alias_entry e = { };
719
	int error = is_delete ? -ENOENT : -ENOMEM;
K
Kentaro Takeda 已提交
720

721 722 723
	if (!tomoyo_is_correct_path(original_name) ||
	    !tomoyo_is_correct_path(aliased_name))
		return -EINVAL;
724 725
	e.original_name = tomoyo_get_name(original_name);
	e.aliased_name = tomoyo_get_name(aliased_name);
726 727 728
	if (!e.original_name || !e.aliased_name ||
	    e.original_name->is_patterned || e.aliased_name->is_patterned)
		goto out; /* No patterns allowed. */
729 730
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
731
	list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) {
732
		if (!tomoyo_is_same_alias_entry(ptr, &e))
K
Kentaro Takeda 已提交
733
			continue;
734
		ptr->head.is_deleted = is_delete;
K
Kentaro Takeda 已提交
735
		error = 0;
736
		break;
K
Kentaro Takeda 已提交
737
	}
738 739 740 741
	if (!is_delete && error) {
		struct tomoyo_alias_entry *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
742 743
			list_add_tail_rcu(&entry->head.list,
					  &tomoyo_alias_list);
744 745
			error = 0;
		}
K
Kentaro Takeda 已提交
746
	}
747
	mutex_unlock(&tomoyo_policy_lock);
748
 out:
749 750
	tomoyo_put_name(e.original_name);
	tomoyo_put_name(e.aliased_name);
K
Kentaro Takeda 已提交
751 752 753 754 755 756 757 758 759
	return error;
}

/**
 * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns true on success, false otherwise.
760 761
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
762 763 764 765 766 767 768 769 770
 */
bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
{
	struct list_head *pos;
	bool done = true;

	list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
		struct tomoyo_alias_entry *ptr;

771 772
		ptr = list_entry(pos, struct tomoyo_alias_entry, head.list);
		if (ptr->head.is_deleted)
K
Kentaro Takeda 已提交
773
			continue;
T
Tetsuo Handa 已提交
774 775 776 777
		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
					ptr->original_name->name,
					ptr->aliased_name->name);
		if (!done)
K
Kentaro Takeda 已提交
778 779 780 781 782 783 784 785 786 787 788 789
			break;
	}
	return done;
}

/**
 * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list.
 *
 * @data:      String to parse.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
790 791
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
 */
int tomoyo_write_alias_policy(char *data, const bool is_delete)
{
	char *cp = strchr(data, ' ');

	if (!cp)
		return -EINVAL;
	*cp++ = '\0';
	return tomoyo_update_alias_entry(data, cp, is_delete);
}

/**
 * tomoyo_find_or_assign_new_domain - Create a domain.
 *
 * @domainname: The name of domain.
 * @profile:    Profile number to assign if the domain was newly created.
 *
 * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
810 811
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
812 813 814 815 816
 */
struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
							    domainname,
							    const u8 profile)
{
817
	struct tomoyo_domain_info *entry;
818
	struct tomoyo_domain_info *domain = NULL;
K
Kentaro Takeda 已提交
819
	const struct tomoyo_path_info *saved_domainname;
820
	bool found = false;
K
Kentaro Takeda 已提交
821

822
	if (!tomoyo_is_correct_domain(domainname))
823
		return NULL;
824
	saved_domainname = tomoyo_get_name(domainname);
K
Kentaro Takeda 已提交
825
	if (!saved_domainname)
826
		return NULL;
827
	entry = kzalloc(sizeof(*entry), GFP_NOFS);
828 829
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
830 831 832 833 834 835 836 837 838 839
	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
		if (domain->is_deleted ||
		    tomoyo_pathcmp(saved_domainname, domain->domainname))
			continue;
		found = true;
		break;
	}
	if (!found && tomoyo_memory_ok(entry)) {
		INIT_LIST_HEAD(&entry->acl_info_list);
		entry->domainname = saved_domainname;
840
		saved_domainname = NULL;
841 842 843 844 845
		entry->profile = profile;
		list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
		domain = entry;
		entry = NULL;
		found = true;
K
Kentaro Takeda 已提交
846
	}
847
	mutex_unlock(&tomoyo_policy_lock);
848
 out:
849
	tomoyo_put_name(saved_domainname);
850 851
	kfree(entry);
	return found ? domain : NULL;
K
Kentaro Takeda 已提交
852 853 854 855 856
}

/**
 * tomoyo_find_next_domain - Find a domain.
 *
857
 * @bprm: Pointer to "struct linux_binprm".
K
Kentaro Takeda 已提交
858 859
 *
 * Returns 0 on success, negative value otherwise.
860 861
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
862
 */
863
int tomoyo_find_next_domain(struct linux_binprm *bprm)
K
Kentaro Takeda 已提交
864
{
865
	struct tomoyo_request_info r;
T
Tetsuo Handa 已提交
866
	char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
K
Kentaro Takeda 已提交
867 868 869 870
	struct tomoyo_domain_info *old_domain = tomoyo_domain();
	struct tomoyo_domain_info *domain = NULL;
	const char *old_domain_name = old_domain->domainname->name;
	const char *original_name = bprm->filename;
T
Tetsuo Handa 已提交
871 872
	u8 mode;
	bool is_enforce;
K
Kentaro Takeda 已提交
873
	int retval = -ENOMEM;
T
Tetsuo Handa 已提交
874 875 876
	bool need_kfree = false;
	struct tomoyo_path_info rn = { }; /* real name */
	struct tomoyo_path_info sn = { }; /* symlink name */
877
	struct tomoyo_path_info ln; /* last name */
K
Kentaro Takeda 已提交
878

T
Tetsuo Handa 已提交
879 880
	ln.name = tomoyo_get_last_name(old_domain);
	tomoyo_fill_path_info(&ln);
T
Tetsuo Handa 已提交
881 882
	mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
	is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
K
Kentaro Takeda 已提交
883 884 885
	if (!tmp)
		goto out;

886
 retry:
T
Tetsuo Handa 已提交
887 888 889 890
	if (need_kfree) {
		kfree(rn.name);
		need_kfree = false;
	}
K
Kentaro Takeda 已提交
891 892
	/* Get tomoyo_realpath of program. */
	retval = -ENOENT;
T
Tetsuo Handa 已提交
893 894
	rn.name = tomoyo_realpath(original_name);
	if (!rn.name)
K
Kentaro Takeda 已提交
895
		goto out;
T
Tetsuo Handa 已提交
896 897 898
	tomoyo_fill_path_info(&rn);
	need_kfree = true;

K
Kentaro Takeda 已提交
899
	/* Get tomoyo_realpath of symbolic link. */
T
Tetsuo Handa 已提交
900 901
	sn.name = tomoyo_realpath_nofollow(original_name);
	if (!sn.name)
K
Kentaro Takeda 已提交
902
		goto out;
903
	tomoyo_fill_path_info(&sn);
K
Kentaro Takeda 已提交
904 905

	/* Check 'alias' directive. */
906
	if (tomoyo_pathcmp(&rn, &sn)) {
K
Kentaro Takeda 已提交
907 908
		struct tomoyo_alias_entry *ptr;
		/* Is this program allowed to be called via symbolic links? */
909 910
		list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) {
			if (ptr->head.is_deleted ||
911 912
			    tomoyo_pathcmp(&rn, ptr->original_name) ||
			    tomoyo_pathcmp(&sn, ptr->aliased_name))
K
Kentaro Takeda 已提交
913
				continue;
T
Tetsuo Handa 已提交
914 915 916 917
			kfree(rn.name);
			need_kfree = false;
			/* This is OK because it is read only. */
			rn = *ptr->aliased_name;
K
Kentaro Takeda 已提交
918 919 920 921
			break;
		}
	}

922 923 924
	/* Check 'aggregator' directive. */
	{
		struct tomoyo_aggregator_entry *ptr;
925 926 927
		list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list,
					head.list) {
			if (ptr->head.is_deleted ||
928 929 930 931 932 933 934 935 936 937 938 939
			    !tomoyo_path_matches_pattern(&rn,
							 ptr->original_name))
				continue;
			if (need_kfree)
				kfree(rn.name);
			need_kfree = false;
			/* This is OK because it is read only. */
			rn = *ptr->aggregated_name;
			break;
		}
	}

K
Kentaro Takeda 已提交
940
	/* Check execute permission. */
T
Tetsuo Handa 已提交
941
	retval = tomoyo_check_exec_perm(&r, &rn);
942 943
	if (retval == TOMOYO_RETRY_REQUEST)
		goto retry;
K
Kentaro Takeda 已提交
944 945 946
	if (retval < 0)
		goto out;

947
	if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) {
K
Kentaro Takeda 已提交
948
		/* Transit to the child of tomoyo_kernel_domain domain. */
T
Tetsuo Handa 已提交
949 950
		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1,
			 TOMOYO_ROOT_NAME " " "%s", rn.name);
K
Kentaro Takeda 已提交
951 952 953 954 955 956 957 958
	} else if (old_domain == &tomoyo_kernel_domain &&
		   !tomoyo_policy_loaded) {
		/*
		 * Needn't to transit from kernel domain before starting
		 * /sbin/init. But transit from kernel domain if executing
		 * initializers because they might start before /sbin/init.
		 */
		domain = old_domain;
959
	} else if (tomoyo_is_domain_keeper(old_domain->domainname, &rn, &ln)) {
K
Kentaro Takeda 已提交
960 961 962 963
		/* Keep current domain. */
		domain = old_domain;
	} else {
		/* Normal domain transition. */
T
Tetsuo Handa 已提交
964 965
		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1,
			 "%s %s", old_domain_name, rn.name);
K
Kentaro Takeda 已提交
966
	}
T
Tetsuo Handa 已提交
967
	if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
K
Kentaro Takeda 已提交
968
		goto done;
T
Tetsuo Handa 已提交
969
	domain = tomoyo_find_domain(tmp);
K
Kentaro Takeda 已提交
970 971
	if (domain)
		goto done;
972 973
	if (is_enforce) {
		int error = tomoyo_supervisor(&r, "# wants to create domain\n"
T
Tetsuo Handa 已提交
974
					      "%s\n", tmp);
975 976 977 978 979
		if (error == TOMOYO_RETRY_REQUEST)
			goto retry;
		if (error < 0)
			goto done;
	}
T
Tetsuo Handa 已提交
980
	domain = tomoyo_find_or_assign_new_domain(tmp, old_domain->profile);
K
Kentaro Takeda 已提交
981 982 983
 done:
	if (domain)
		goto out;
T
Tetsuo Handa 已提交
984
	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
K
Kentaro Takeda 已提交
985 986 987
	if (is_enforce)
		retval = -EPERM;
	else
T
Tetsuo Handa 已提交
988
		old_domain->transition_failed = true;
K
Kentaro Takeda 已提交
989
 out:
990 991
	if (!domain)
		domain = old_domain;
992 993
	/* Update reference count on "struct tomoyo_domain_info". */
	atomic_inc(&domain->users);
994
	bprm->cred->security = domain;
T
Tetsuo Handa 已提交
995 996 997
	if (need_kfree)
		kfree(rn.name);
	kfree(sn.name);
998
	kfree(tmp);
K
Kentaro Takeda 已提交
999 1000
	return retval;
}