file.c 38.8 KB
Newer Older
1 2 3
/*
 * security/tomoyo/file.c
 *
4
 * Pathname restriction functions.
5
 *
6
 * Copyright (C) 2005-2010  NTT DATA CORPORATION
7 8 9
 */

#include "common.h"
10
#include <linux/slab.h>
11

12
/* Keyword array for operations with one pathname. */
13
const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
T
Tetsuo Handa 已提交
14 15 16 17 18 19 20 21 22 23 24
	[TOMOYO_TYPE_READ_WRITE] = "read/write",
	[TOMOYO_TYPE_EXECUTE]    = "execute",
	[TOMOYO_TYPE_READ]       = "read",
	[TOMOYO_TYPE_WRITE]      = "write",
	[TOMOYO_TYPE_UNLINK]     = "unlink",
	[TOMOYO_TYPE_RMDIR]      = "rmdir",
	[TOMOYO_TYPE_TRUNCATE]   = "truncate",
	[TOMOYO_TYPE_SYMLINK]    = "symlink",
	[TOMOYO_TYPE_REWRITE]    = "rewrite",
	[TOMOYO_TYPE_CHROOT]     = "chroot",
	[TOMOYO_TYPE_UMOUNT]     = "unmount",
25 26
};

27
/* Keyword array for operations with one pathname and three numbers. */
28
const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = {
29 30 31 32 33
	[TOMOYO_TYPE_MKBLOCK]    = "mkblock",
	[TOMOYO_TYPE_MKCHAR]     = "mkchar",
};

/* Keyword array for operations with two pathnames. */
34
const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
35 36
	[TOMOYO_TYPE_LINK]       = "link",
	[TOMOYO_TYPE_RENAME]     = "rename",
T
Tetsuo Handa 已提交
37
	[TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
38 39
};

40
/* Keyword array for operations with one pathname and one number. */
41
const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
42 43 44 45 46 47 48 49 50 51
	[TOMOYO_TYPE_CREATE]     = "create",
	[TOMOYO_TYPE_MKDIR]      = "mkdir",
	[TOMOYO_TYPE_MKFIFO]     = "mkfifo",
	[TOMOYO_TYPE_MKSOCK]     = "mksock",
	[TOMOYO_TYPE_IOCTL]      = "ioctl",
	[TOMOYO_TYPE_CHMOD]      = "chmod",
	[TOMOYO_TYPE_CHOWN]      = "chown",
	[TOMOYO_TYPE_CHGRP]      = "chgrp",
};

T
Tetsuo Handa 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65
static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = {
	[TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN,
	[TOMOYO_TYPE_EXECUTE]    = TOMOYO_MAC_FILE_EXECUTE,
	[TOMOYO_TYPE_READ]       = TOMOYO_MAC_FILE_OPEN,
	[TOMOYO_TYPE_WRITE]      = TOMOYO_MAC_FILE_OPEN,
	[TOMOYO_TYPE_UNLINK]     = TOMOYO_MAC_FILE_UNLINK,
	[TOMOYO_TYPE_RMDIR]      = TOMOYO_MAC_FILE_RMDIR,
	[TOMOYO_TYPE_TRUNCATE]   = TOMOYO_MAC_FILE_TRUNCATE,
	[TOMOYO_TYPE_SYMLINK]    = TOMOYO_MAC_FILE_SYMLINK,
	[TOMOYO_TYPE_REWRITE]    = TOMOYO_MAC_FILE_REWRITE,
	[TOMOYO_TYPE_CHROOT]     = TOMOYO_MAC_FILE_CHROOT,
	[TOMOYO_TYPE_UMOUNT]     = TOMOYO_MAC_FILE_UMOUNT,
};

T
Tetsuo Handa 已提交
66
static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {
T
Tetsuo Handa 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	[TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,
	[TOMOYO_TYPE_MKCHAR]  = TOMOYO_MAC_FILE_MKCHAR,
};

static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
	[TOMOYO_TYPE_LINK]       = TOMOYO_MAC_FILE_LINK,
	[TOMOYO_TYPE_RENAME]     = TOMOYO_MAC_FILE_RENAME,
	[TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT,
};

static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
	[TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE,
	[TOMOYO_TYPE_MKDIR]  = TOMOYO_MAC_FILE_MKDIR,
	[TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO,
	[TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK,
	[TOMOYO_TYPE_IOCTL]  = TOMOYO_MAC_FILE_IOCTL,
	[TOMOYO_TYPE_CHMOD]  = TOMOYO_MAC_FILE_CHMOD,
	[TOMOYO_TYPE_CHOWN]  = TOMOYO_MAC_FILE_CHOWN,
	[TOMOYO_TYPE_CHGRP]  = TOMOYO_MAC_FILE_CHGRP,
};

88 89 90 91 92 93 94 95 96 97 98 99 100 101
void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
{
	if (!ptr)
		return;
	if (ptr->is_group)
		tomoyo_put_path_group(ptr->group);
	else
		tomoyo_put_name(ptr->filename);
}

bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
			       const struct tomoyo_name_union *ptr)
{
	if (ptr->is_group)
102
		return tomoyo_path_matches_group(name, ptr->group);
103 104 105
	return tomoyo_path_matches_pattern(name, ptr->filename);
}

106 107 108 109 110 111 112 113 114 115 116 117 118 119
void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
{
	if (ptr && ptr->is_group)
		tomoyo_put_number_group(ptr->group);
}

bool tomoyo_compare_number_union(const unsigned long value,
				 const struct tomoyo_number_union *ptr)
{
	if (ptr->is_group)
		return tomoyo_number_matches_group(value, value, ptr->group);
	return value >= ptr->values[0] && value <= ptr->values[1];
}

T
Tetsuo Handa 已提交
120 121 122 123 124 125 126 127 128 129 130
static void tomoyo_add_slash(struct tomoyo_path_info *buf)
{
	if (buf->is_dir)
		return;
	/*
	 * This is OK because tomoyo_encode() reserves space for appending "/".
	 */
	strcat((char *) buf->name, "/");
	tomoyo_fill_path_info(buf);
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
/**
 * tomoyo_strendswith - Check whether the token ends with the given token.
 *
 * @name: The token to check.
 * @tail: The token to find.
 *
 * Returns true if @name ends with @tail, false otherwise.
 */
static bool tomoyo_strendswith(const char *name, const char *tail)
{
	int len;

	if (!name || !tail)
		return false;
	len = strlen(name) - strlen(tail);
	return len >= 0 && !strcmp(name + len, tail);
}

/**
T
Tetsuo Handa 已提交
150
 * tomoyo_get_realpath - Get realpath.
151
 *
T
Tetsuo Handa 已提交
152
 * @buf:  Pointer to "struct tomoyo_path_info".
153 154
 * @path: Pointer to "struct path".
 *
T
Tetsuo Handa 已提交
155
 * Returns true on success, false otherwise.
156
 */
T
Tetsuo Handa 已提交
157
static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)
158
{
T
Tetsuo Handa 已提交
159 160 161 162
	buf->name = tomoyo_realpath_from_path(path);
	if (buf->name) {
		tomoyo_fill_path_info(buf);
		return true;
163
	}
T
Tetsuo Handa 已提交
164
        return false;
165 166
}

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
/**
 * tomoyo_audit_path_log - Audit path request log.
 *
 * @r: Pointer to "struct tomoyo_request_info".
 *
 * Returns 0 on success, negative value otherwise.
 */
static int tomoyo_audit_path_log(struct tomoyo_request_info *r)
{
	const char *operation = tomoyo_path_keyword[r->param.path.operation];
	const struct tomoyo_path_info *filename = r->param.path.filename;
	if (r->granted)
		return 0;
	tomoyo_warn_log(r, "%s %s", operation, filename->name);
	return tomoyo_supervisor(r, "allow_%s %s\n", operation,
				 tomoyo_file_pattern(filename));
}

/**
 * tomoyo_audit_path2_log - Audit path/path request log.
 *
 * @r: Pointer to "struct tomoyo_request_info".
 *
 * Returns 0 on success, negative value otherwise.
 */
static int tomoyo_audit_path2_log(struct tomoyo_request_info *r)
{
	const char *operation = tomoyo_path2_keyword[r->param.path2.operation];
	const struct tomoyo_path_info *filename1 = r->param.path2.filename1;
	const struct tomoyo_path_info *filename2 = r->param.path2.filename2;
	if (r->granted)
		return 0;
	tomoyo_warn_log(r, "%s %s %s", operation, filename1->name,
			filename2->name);
	return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
				 tomoyo_file_pattern(filename1),
				 tomoyo_file_pattern(filename2));
}

/**
 * tomoyo_audit_mkdev_log - Audit path/number/number/number request log.
 *
 * @r: Pointer to "struct tomoyo_request_info".
 *
 * Returns 0 on success, negative value otherwise.
 */
static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r)
{
215
	const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation];
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	const struct tomoyo_path_info *filename = r->param.mkdev.filename;
	const unsigned int major = r->param.mkdev.major;
	const unsigned int minor = r->param.mkdev.minor;
	const unsigned int mode = r->param.mkdev.mode;
	if (r->granted)
		return 0;
	tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode,
			major, minor);
	return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation,
				 tomoyo_file_pattern(filename), mode, major,
				 minor);
}

/**
 * tomoyo_audit_path_number_log - Audit path/number request log.
 *
 * @r:     Pointer to "struct tomoyo_request_info".
 * @error: Error code.
 *
 * Returns 0 on success, negative value otherwise.
 */
static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r)
{
	const u8 type = r->param.path_number.operation;
	u8 radix;
	const struct tomoyo_path_info *filename = r->param.path_number.filename;
	const char *operation = tomoyo_path_number_keyword[type];
	char buffer[64];
	if (r->granted)
		return 0;
	switch (type) {
	case TOMOYO_TYPE_CREATE:
	case TOMOYO_TYPE_MKDIR:
	case TOMOYO_TYPE_MKFIFO:
	case TOMOYO_TYPE_MKSOCK:
	case TOMOYO_TYPE_CHMOD:
		radix = TOMOYO_VALUE_TYPE_OCTAL;
		break;
	case TOMOYO_TYPE_IOCTL:
		radix = TOMOYO_VALUE_TYPE_HEXADECIMAL;
		break;
	default:
		radix = TOMOYO_VALUE_TYPE_DECIMAL;
		break;
	}
	tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number,
			   radix);
	tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer);
	return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
				 tomoyo_file_pattern(filename), buffer);
}

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
/*
 * tomoyo_globally_readable_list is used for holding list of pathnames which
 * are by default allowed to be open()ed for reading by any process.
 *
 * An entry is added by
 *
 * # echo 'allow_read /lib/libc-2.5.so' > \
 *                               /sys/kernel/security/tomoyo/exception_policy
 *
 * and is deleted by
 *
 * # echo 'delete allow_read /lib/libc-2.5.so' > \
 *                               /sys/kernel/security/tomoyo/exception_policy
 *
 * and all entries are retrieved by
 *
 * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy
 *
 * In the example above, any process is allowed to
 * open("/lib/libc-2.5.so", O_RDONLY).
 * One exception is, if the domain which current process belongs to is marked
 * as "ignore_global_allow_read", current process can't do so unless explicitly
 * given "allow_read /lib/libc-2.5.so" to the domain which current process
 * belongs to.
 */
T
Tetsuo Handa 已提交
293
LIST_HEAD(tomoyo_globally_readable_list);
294

295 296 297 298 299 300 301 302 303
static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a,
					  const struct tomoyo_acl_head *b)
{
	return container_of(a, struct tomoyo_globally_readable_file_entry,
			    head)->filename ==
		container_of(b, struct tomoyo_globally_readable_file_entry,
			     head)->filename;
}

304 305 306 307 308 309 310
/**
 * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
 *
 * @filename:  Filename unconditionally permitted to open() for reading.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
311 312
 *
 * Caller holds tomoyo_read_lock().
313 314 315 316
 */
static int tomoyo_update_globally_readable_entry(const char *filename,
						 const bool is_delete)
{
317
	struct tomoyo_globally_readable_file_entry e = { };
318
	int error;
319

T
Tetsuo Handa 已提交
320
	if (!tomoyo_correct_word(filename))
321
		return -EINVAL;
322 323
	e.filename = tomoyo_get_name(filename);
	if (!e.filename)
324
		return -ENOMEM;
325 326 327
	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
				     &tomoyo_globally_readable_list,
				     tomoyo_same_globally_readable);
328
	tomoyo_put_name(e.filename);
329 330 331 332
	return error;
}

/**
T
Tetsuo Handa 已提交
333
 * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
334 335 336 337
 *
 * @filename: The filename to check.
 *
 * Returns true if any domain can open @filename for reading, false otherwise.
338 339
 *
 * Caller holds tomoyo_read_lock().
340
 */
T
Tetsuo Handa 已提交
341
static bool tomoyo_globally_readable_file(const struct tomoyo_path_info *
342 343 344 345
					     filename)
{
	struct tomoyo_globally_readable_file_entry *ptr;
	bool found = false;
346

347 348 349
	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list,
				head.list) {
		if (!ptr->head.is_deleted &&
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
		    tomoyo_path_matches_pattern(filename, ptr->filename)) {
			found = true;
			break;
		}
	}
	return found;
}

/**
 * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
 *
 * @data:      String to parse.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
365 366
 *
 * Caller holds tomoyo_read_lock().
367 368 369 370 371 372 373 374 375 376 377 378
 */
int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
{
	return tomoyo_update_globally_readable_entry(data, is_delete);
}

/**
 * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns true on success, false otherwise.
379 380
 *
 * Caller holds tomoyo_read_lock().
381 382 383 384 385 386 387 388 389 390 391
 */
bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
{
	struct list_head *pos;
	bool done = true;

	list_for_each_cookie(pos, head->read_var2,
			     &tomoyo_globally_readable_list) {
		struct tomoyo_globally_readable_file_entry *ptr;
		ptr = list_entry(pos,
				 struct tomoyo_globally_readable_file_entry,
392 393
				 head.list);
		if (ptr->head.is_deleted)
394
			continue;
T
Tetsuo Handa 已提交
395 396 397
		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
					ptr->filename->name);
		if (!done)
398 399 400 401 402
			break;
	}
	return done;
}

403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
/* tomoyo_pattern_list is used for holding list of pathnames which are used for
 * converting pathnames to pathname patterns during learning mode.
 *
 * An entry is added by
 *
 * # echo 'file_pattern /proc/\$/mounts' > \
 *                             /sys/kernel/security/tomoyo/exception_policy
 *
 * and is deleted by
 *
 * # echo 'delete file_pattern /proc/\$/mounts' > \
 *                             /sys/kernel/security/tomoyo/exception_policy
 *
 * and all entries are retrieved by
 *
 * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy
 *
 * In the example above, if a process which belongs to a domain which is in
 * learning mode requested open("/proc/1/mounts", O_RDONLY),
 * "allow_read /proc/\$/mounts" is automatically added to the domain which that
 * process belongs to.
 *
 * It is not a desirable behavior that we have to use /proc/\$/ instead of
 * /proc/self/ when current process needs to access only current process's
 * information. As of now, LSM version of TOMOYO is using __d_path() for
 * calculating pathname. Non LSM version of TOMOYO is using its own function
 * which pretends as if /proc/self/ is not a symlink; so that we can forbid
 * current process from accessing other process's information.
 */
T
Tetsuo Handa 已提交
432
LIST_HEAD(tomoyo_pattern_list);
433

434 435 436 437 438 439 440
static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a,
				const struct tomoyo_acl_head *b)
{
	return container_of(a, struct tomoyo_pattern_entry, head)->pattern ==
		container_of(b, struct tomoyo_pattern_entry, head)->pattern;
}

441 442 443 444 445 446 447
/**
 * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
 *
 * @pattern:   Pathname pattern.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
448 449
 *
 * Caller holds tomoyo_read_lock().
450 451 452 453
 */
static int tomoyo_update_file_pattern_entry(const char *pattern,
					    const bool is_delete)
{
454
	struct tomoyo_pattern_entry e = { };
455
	int error;
456

T
Tetsuo Handa 已提交
457
	if (!tomoyo_correct_word(pattern))
458 459
		return -EINVAL;
	e.pattern = tomoyo_get_name(pattern);
460
	if (!e.pattern)
461 462 463 464
		return -ENOMEM;
	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
				     &tomoyo_pattern_list,
				     tomoyo_same_pattern);
465
	tomoyo_put_name(e.pattern);
466 467 468 469
	return error;
}

/**
470
 * tomoyo_file_pattern - Get patterned pathname.
471 472 473 474
 *
 * @filename: The filename to find patterned pathname.
 *
 * Returns pointer to pathname pattern if matched, @filename otherwise.
475 476
 *
 * Caller holds tomoyo_read_lock().
477
 */
478
const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename)
479 480 481 482
{
	struct tomoyo_pattern_entry *ptr;
	const struct tomoyo_path_info *pattern = NULL;

483 484
	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) {
		if (ptr->head.is_deleted)
485 486 487 488 489 490 491 492 493 494 495 496 497
			continue;
		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
			continue;
		pattern = ptr->pattern;
		if (tomoyo_strendswith(pattern->name, "/\\*")) {
			/* Do nothing. Try to find the better match. */
		} else {
			/* This would be the better match. Use this. */
			break;
		}
	}
	if (pattern)
		filename = pattern;
498
	return filename->name;
499 500 501 502 503 504 505 506 507
}

/**
 * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
 *
 * @data:      String to parse.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
508 509
 *
 * Caller holds tomoyo_read_lock().
510 511 512 513 514 515 516 517 518 519 520 521
 */
int tomoyo_write_pattern_policy(char *data, const bool is_delete)
{
	return tomoyo_update_file_pattern_entry(data, is_delete);
}

/**
 * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns true on success, false otherwise.
522 523
 *
 * Caller holds tomoyo_read_lock().
524 525 526 527 528 529 530 531
 */
bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
{
	struct list_head *pos;
	bool done = true;

	list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
		struct tomoyo_pattern_entry *ptr;
532 533
		ptr = list_entry(pos, struct tomoyo_pattern_entry, head.list);
		if (ptr->head.is_deleted)
534
			continue;
T
Tetsuo Handa 已提交
535 536 537
		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
					"%s\n", ptr->pattern->name);
		if (!done)
538 539 540 541 542
			break;
	}
	return done;
}

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
/*
 * tomoyo_no_rewrite_list is used for holding list of pathnames which are by
 * default forbidden to modify already written content of a file.
 *
 * An entry is added by
 *
 * # echo 'deny_rewrite /var/log/messages' > \
 *                              /sys/kernel/security/tomoyo/exception_policy
 *
 * and is deleted by
 *
 * # echo 'delete deny_rewrite /var/log/messages' > \
 *                              /sys/kernel/security/tomoyo/exception_policy
 *
 * and all entries are retrieved by
 *
 * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy
 *
 * In the example above, if a process requested to rewrite /var/log/messages ,
 * the process can't rewrite unless the domain which that process belongs to
 * has "allow_rewrite /var/log/messages" entry.
 *
 * It is not a desirable behavior that we have to add "\040(deleted)" suffix
 * when we want to allow rewriting already unlink()ed file. As of now,
 * LSM version of TOMOYO is using __d_path() for calculating pathname.
 * Non LSM version of TOMOYO is using its own function which doesn't append
 * " (deleted)" suffix if the file is already unlink()ed; so that we don't
 * need to worry whether the file is already unlink()ed or not.
 */
T
Tetsuo Handa 已提交
572
LIST_HEAD(tomoyo_no_rewrite_list);
573

574 575 576 577 578 579 580 581
static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a,
				   const struct tomoyo_acl_head *b)
{
	return container_of(a, struct tomoyo_no_rewrite_entry, head)->pattern
		== container_of(b, struct tomoyo_no_rewrite_entry, head)
		->pattern;
}

582 583 584 585 586 587 588
/**
 * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
 *
 * @pattern:   Pathname pattern that are not rewritable by default.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
589 590
 *
 * Caller holds tomoyo_read_lock().
591 592 593 594
 */
static int tomoyo_update_no_rewrite_entry(const char *pattern,
					  const bool is_delete)
{
595
	struct tomoyo_no_rewrite_entry e = { };
596
	int error;
597

T
Tetsuo Handa 已提交
598
	if (!tomoyo_correct_word(pattern))
599
		return -EINVAL;
600 601
	e.pattern = tomoyo_get_name(pattern);
	if (!e.pattern)
602 603 604 605
		return -ENOMEM;
	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
				     &tomoyo_no_rewrite_list,
				     tomoyo_same_no_rewrite);
606
	tomoyo_put_name(e.pattern);
607 608 609 610
	return error;
}

/**
T
Tetsuo Handa 已提交
611
 * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
612 613 614 615 616
 *
 * @filename: Filename to check.
 *
 * Returns true if @filename is specified by "deny_rewrite" directive,
 * false otherwise.
617 618
 *
 * Caller holds tomoyo_read_lock().
619
 */
T
Tetsuo Handa 已提交
620
static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename)
621 622 623 624
{
	struct tomoyo_no_rewrite_entry *ptr;
	bool found = false;

625 626
	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, head.list) {
		if (ptr->head.is_deleted)
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
			continue;
		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
			continue;
		found = true;
		break;
	}
	return found;
}

/**
 * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
 *
 * @data:      String to parse.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
643 644
 *
 * Caller holds tomoyo_read_lock().
645 646 647 648 649 650 651 652 653 654 655 656
 */
int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
{
	return tomoyo_update_no_rewrite_entry(data, is_delete);
}

/**
 * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns true on success, false otherwise.
657 658
 *
 * Caller holds tomoyo_read_lock().
659 660 661 662 663 664 665 666
 */
bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
{
	struct list_head *pos;
	bool done = true;

	list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
		struct tomoyo_no_rewrite_entry *ptr;
667 668 669
		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry,
				 head.list);
		if (ptr->head.is_deleted)
670
			continue;
T
Tetsuo Handa 已提交
671 672 673
		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
					"%s\n", ptr->pattern->name);
		if (!done)
674 675 676 677 678
			break;
	}
	return done;
}

679 680
static bool tomoyo_check_path_acl(const struct tomoyo_request_info *r,
				  const struct tomoyo_acl_info *ptr)
681
{
682 683 684 685 686
	const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl),
							 head);
	return (acl->perm & (1 << r->param.path.operation)) &&
		tomoyo_compare_name_union(r->param.path.filename, &acl->name);
}
687

688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
static bool tomoyo_check_path_number_acl(const struct tomoyo_request_info *r,
					 const struct tomoyo_acl_info *ptr)
{
	const struct tomoyo_path_number_acl *acl =
		container_of(ptr, typeof(*acl), head);
	return (acl->perm & (1 << r->param.path_number.operation)) &&
		tomoyo_compare_number_union(r->param.path_number.number,
					    &acl->number) &&
		tomoyo_compare_name_union(r->param.path_number.filename,
					  &acl->name);
}

static bool tomoyo_check_path2_acl(const struct tomoyo_request_info *r,
				   const struct tomoyo_acl_info *ptr)
{
	const struct tomoyo_path2_acl *acl =
		container_of(ptr, typeof(*acl), head);
	return (acl->perm & (1 << r->param.path2.operation)) &&
		tomoyo_compare_name_union(r->param.path2.filename1, &acl->name1)
		&& tomoyo_compare_name_union(r->param.path2.filename2,
					     &acl->name2);
}

static bool tomoyo_check_mkdev_acl(const struct tomoyo_request_info *r,
				const struct tomoyo_acl_info *ptr)
{
T
Tetsuo Handa 已提交
714
	const struct tomoyo_mkdev_acl *acl =
715 716 717 718 719 720 721 722 723 724
		container_of(ptr, typeof(*acl), head);
	return (acl->perm & (1 << r->param.mkdev.operation)) &&
		tomoyo_compare_number_union(r->param.mkdev.mode,
					    &acl->mode) &&
		tomoyo_compare_number_union(r->param.mkdev.major,
					    &acl->major) &&
		tomoyo_compare_number_union(r->param.mkdev.minor,
					    &acl->minor) &&
		tomoyo_compare_name_union(r->param.mkdev.filename,
					  &acl->name);
725 726
}

727 728 729 730 731
static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,
				 const struct tomoyo_acl_info *b)
{
	const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
	const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
T
Tetsuo Handa 已提交
732 733
	return tomoyo_same_acl_head(&p1->head, &p2->head) &&
		tomoyo_same_name_union(&p1->name, &p2->name);
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
}

static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
				  struct tomoyo_acl_info *b,
				  const bool is_delete)
{
	u16 * const a_perm = &container_of(a, struct tomoyo_path_acl, head)
		->perm;
	u16 perm = *a_perm;
	const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
	if (is_delete) {
		perm &= ~b_perm;
		if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK)
			perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
		else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE)))
			perm &= ~TOMOYO_RW_MASK;
	} else {
		perm |= b_perm;
		if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK)
			perm |= (1 << TOMOYO_TYPE_READ_WRITE);
		else if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
			perm |= TOMOYO_RW_MASK;
	}
	*a_perm = perm;
	return !perm;
}

761
/**
T
Tetsuo Handa 已提交
762
 * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
763 764 765 766 767 768 769
 *
 * @type:      Type of operation.
 * @filename:  Filename.
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
770 771
 *
 * Caller holds tomoyo_read_lock().
772
 */
T
Tetsuo Handa 已提交
773
static int tomoyo_update_path_acl(const u8 type, const char *filename,
774
				  struct tomoyo_domain_info * const domain,
T
Tetsuo Handa 已提交
775
				  const bool is_delete)
776
{
777 778
	struct tomoyo_path_acl e = {
		.head.type = TOMOYO_TYPE_PATH_ACL,
779
		.perm = 1 << type
780
	};
781 782 783
	int error;
	if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE))
		e.perm |= TOMOYO_RW_MASK;
784
	if (!tomoyo_parse_name_union(filename, &e.name))
785
		return -EINVAL;
786 787 788
	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
				     tomoyo_same_path_acl,
				     tomoyo_merge_path_acl);
789
	tomoyo_put_name_union(&e.name);
790 791 792
	return error;
}

T
Tetsuo Handa 已提交
793
static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a,
794 795
					 const struct tomoyo_acl_info *b)
{
T
Tetsuo Handa 已提交
796
	const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1),
797
								head);
T
Tetsuo Handa 已提交
798
	const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2),
799
								head);
T
Tetsuo Handa 已提交
800 801 802 803 804
	return tomoyo_same_acl_head(&p1->head, &p2->head)
		&& tomoyo_same_name_union(&p1->name, &p2->name)
		&& tomoyo_same_number_union(&p1->mode, &p2->mode)
		&& tomoyo_same_number_union(&p1->major, &p2->major)
		&& tomoyo_same_number_union(&p1->minor, &p2->minor);
805 806
}

T
Tetsuo Handa 已提交
807
static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a,
808 809 810
					  struct tomoyo_acl_info *b,
					  const bool is_delete)
{
T
Tetsuo Handa 已提交
811
	u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl,
812 813
					 head)->perm;
	u8 perm = *a_perm;
T
Tetsuo Handa 已提交
814
	const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head)
815 816 817 818 819 820 821 822 823
		->perm;
	if (is_delete)
		perm &= ~b_perm;
	else
		perm |= b_perm;
	*a_perm = perm;
	return !perm;
}

824
/**
T
Tetsuo Handa 已提交
825
 * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list.
826 827 828 829 830 831 832 833 834 835
 *
 * @type:      Type of operation.
 * @filename:  Filename.
 * @mode:      Create mode.
 * @major:     Device major number.
 * @minor:     Device minor number.
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
836 837
 *
 * Caller holds tomoyo_read_lock().
838
 */
T
Tetsuo Handa 已提交
839
static int tomoyo_update_mkdev_acl(const u8 type, const char *filename,
840 841 842
					  char *mode, char *major, char *minor,
					  struct tomoyo_domain_info * const
					  domain, const bool is_delete)
843
{
T
Tetsuo Handa 已提交
844 845
	struct tomoyo_mkdev_acl e = {
		.head.type = TOMOYO_TYPE_MKDEV_ACL,
846
		.perm = 1 << type
847 848 849 850 851 852 853
	};
	int error = is_delete ? -ENOENT : -ENOMEM;
	if (!tomoyo_parse_name_union(filename, &e.name) ||
	    !tomoyo_parse_number_union(mode, &e.mode) ||
	    !tomoyo_parse_number_union(major, &e.major) ||
	    !tomoyo_parse_number_union(minor, &e.minor))
		goto out;
854
	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
T
Tetsuo Handa 已提交
855 856
				     tomoyo_same_mkdev_acl,
				     tomoyo_merge_mkdev_acl);
857 858 859 860 861 862 863 864
 out:
	tomoyo_put_name_union(&e.name);
	tomoyo_put_number_union(&e.mode);
	tomoyo_put_number_union(&e.major);
	tomoyo_put_number_union(&e.minor);
	return error;
}

865 866 867 868 869
static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,
				  const struct tomoyo_acl_info *b)
{
	const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
	const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
T
Tetsuo Handa 已提交
870 871 872
	return tomoyo_same_acl_head(&p1->head, &p2->head)
		&& tomoyo_same_name_union(&p1->name1, &p2->name1)
		&& tomoyo_same_name_union(&p1->name2, &p2->name2);
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
}

static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
				   struct tomoyo_acl_info *b,
				   const bool is_delete)
{
	u8 * const a_perm = &container_of(a, struct tomoyo_path2_acl, head)
		->perm;
	u8 perm = *a_perm;
	const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm;
	if (is_delete)
		perm &= ~b_perm;
	else
		perm |= b_perm;
	*a_perm = perm;
	return !perm;
}

891
/**
T
Tetsuo Handa 已提交
892
 * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
893 894 895 896 897 898 899 900
 *
 * @type:      Type of operation.
 * @filename1: First filename.
 * @filename2: Second filename.
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
901 902
 *
 * Caller holds tomoyo_read_lock().
903
 */
T
Tetsuo Handa 已提交
904 905
static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
				   const char *filename2,
906
				   struct tomoyo_domain_info * const domain,
T
Tetsuo Handa 已提交
907
				   const bool is_delete)
908
{
909 910
	struct tomoyo_path2_acl e = {
		.head.type = TOMOYO_TYPE_PATH2_ACL,
911
		.perm = 1 << type
912
	};
913
	int error = is_delete ? -ENOENT : -ENOMEM;
914 915
	if (!tomoyo_parse_name_union(filename1, &e.name1) ||
	    !tomoyo_parse_name_union(filename2, &e.name2))
916
		goto out;
917 918 919
	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
				     tomoyo_same_path2_acl,
				     tomoyo_merge_path2_acl);
920
 out:
921 922
	tomoyo_put_name_union(&e.name1);
	tomoyo_put_name_union(&e.name2);
923 924 925 926
	return error;
}

/**
927
 * tomoyo_path_permission - Check permission for single path operation.
928
 *
929
 * @r:         Pointer to "struct tomoyo_request_info".
930 931 932 933
 * @operation: Type of operation.
 * @filename:  Filename to check.
 *
 * Returns 0 on success, negative value otherwise.
934 935
 *
 * Caller holds tomoyo_read_lock().
936
 */
937 938
int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
			   const struct tomoyo_path_info *filename)
939 940 941 942
{
	int error;

 next:
T
Tetsuo Handa 已提交
943 944 945 946
	r->type = tomoyo_p2mac[operation];
	r->mode = tomoyo_get_mode(r->profile, r->type);
	if (r->mode == TOMOYO_CONFIG_DISABLED)
		return 0;
947 948 949
	r->param_type = TOMOYO_TYPE_PATH_ACL;
	r->param.path.filename = filename;
	r->param.path.operation = operation;
950
	do {
951 952
		tomoyo_check_acl(r, tomoyo_check_path_acl);
		if (!r->granted && operation == TOMOYO_TYPE_READ &&
953
		    !r->domain->ignore_global_allow_read &&
T
Tetsuo Handa 已提交
954
		    tomoyo_globally_readable_file(filename))
955 956
			r->granted = true;
		error = tomoyo_audit_path_log(r);
957 958 959 960 961 962
		/*
		 * Do not retry for execute request, for alias may have
		 * changed.
		 */
	} while (error == TOMOYO_RETRY_REQUEST &&
		 operation != TOMOYO_TYPE_EXECUTE);
963 964 965 966 967
	/*
	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
	 * we need to check "allow_rewrite" permission if the filename is
	 * specified by "deny_rewrite" keyword.
	 */
T
Tetsuo Handa 已提交
968
	if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
T
Tetsuo Handa 已提交
969
	    tomoyo_no_rewrite_file(filename)) {
T
Tetsuo Handa 已提交
970
		operation = TOMOYO_TYPE_REWRITE;
971 972 973 974 975
		goto next;
	}
	return error;
}

976 977 978 979 980 981 982
static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
					const struct tomoyo_acl_info *b)
{
	const struct tomoyo_path_number_acl *p1 = container_of(a, typeof(*p1),
							       head);
	const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
							       head);
T
Tetsuo Handa 已提交
983 984 985
	return tomoyo_same_acl_head(&p1->head, &p2->head)
		&& tomoyo_same_name_union(&p1->name, &p2->name)
		&& tomoyo_same_number_union(&p1->number, &p2->number);
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
}

static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
					 struct tomoyo_acl_info *b,
					 const bool is_delete)
{
	u8 * const a_perm = &container_of(a, struct tomoyo_path_number_acl,
					  head)->perm;
	u8 perm = *a_perm;
	const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head)
		->perm;
	if (is_delete)
		perm &= ~b_perm;
	else
		perm |= b_perm;
	*a_perm = perm;
	return !perm;
}

1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
/**
 * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
 *
 * @type:      Type of operation.
 * @filename:  Filename.
 * @number:    Number.
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 */
1016 1017 1018 1019 1020
static int tomoyo_update_path_number_acl(const u8 type, const char *filename,
					 char *number,
					 struct tomoyo_domain_info * const
					 domain,
					 const bool is_delete)
1021 1022 1023
{
	struct tomoyo_path_number_acl e = {
		.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
1024
		.perm = 1 << type
1025 1026 1027 1028 1029 1030
	};
	int error = is_delete ? -ENOENT : -ENOMEM;
	if (!tomoyo_parse_name_union(filename, &e.name))
		return -EINVAL;
	if (!tomoyo_parse_number_union(number, &e.number))
		goto out;
1031 1032 1033
	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
				     tomoyo_same_path_number_acl,
				     tomoyo_merge_path_number_acl);
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
 out:
	tomoyo_put_name_union(&e.name);
	tomoyo_put_number_union(&e.number);
	return error;
}

/**
 * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
 *
 * @type:   Type of operation.
 * @path:   Pointer to "struct path".
 * @number: Number.
 *
 * Returns 0 on success, negative value otherwise.
 */
int tomoyo_path_number_perm(const u8 type, struct path *path,
			    unsigned long number)
{
	struct tomoyo_request_info r;
	int error = -ENOMEM;
T
Tetsuo Handa 已提交
1054
	struct tomoyo_path_info buf;
1055 1056
	int idx;

T
Tetsuo Handa 已提交
1057 1058
	if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
	    == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry)
1059 1060
		return 0;
	idx = tomoyo_read_lock();
T
Tetsuo Handa 已提交
1061
	if (!tomoyo_get_realpath(&buf, path))
1062
		goto out;
T
Tetsuo Handa 已提交
1063 1064
	if (type == TOMOYO_TYPE_MKDIR)
		tomoyo_add_slash(&buf);
T
Tetsuo Handa 已提交
1065 1066 1067 1068 1069 1070 1071 1072
	r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL;
	r.param.path_number.operation = type;
	r.param.path_number.filename = &buf;
	r.param.path_number.number = number;
	do {
		tomoyo_check_acl(&r, tomoyo_check_path_number_acl);
		error = tomoyo_audit_path_number_log(&r);
	} while (error == TOMOYO_RETRY_REQUEST);
T
Tetsuo Handa 已提交
1073
	kfree(buf.name);
T
Tetsuo Handa 已提交
1074
 out:
1075 1076 1077 1078 1079 1080
	tomoyo_read_unlock(idx);
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
		error = 0;
	return error;
}

1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
/**
 * tomoyo_check_open_permission - Check permission for "read" and "write".
 *
 * @domain: Pointer to "struct tomoyo_domain_info".
 * @path:   Pointer to "struct path".
 * @flag:   Flags for open().
 *
 * Returns 0 on success, negative value otherwise.
 */
int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
				 struct path *path, const int flag)
{
	const u8 acc_mode = ACC_MODE(flag);
	int error = -ENOMEM;
T
Tetsuo Handa 已提交
1095
	struct tomoyo_path_info buf;
1096
	struct tomoyo_request_info r;
1097
	int idx;
1098

T
Tetsuo Handa 已提交
1099 1100
	if (!path->mnt ||
	    (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)))
1101
		return 0;
T
Tetsuo Handa 已提交
1102 1103
	buf.name = NULL;
	r.mode = TOMOYO_CONFIG_DISABLED;
1104
	idx = tomoyo_read_lock();
T
Tetsuo Handa 已提交
1105
	if (!tomoyo_get_realpath(&buf, path))
1106 1107 1108 1109 1110 1111 1112
		goto out;
	error = 0;
	/*
	 * If the filename is specified by "deny_rewrite" keyword,
	 * we need to check "allow_rewrite" permission when the filename is not
	 * opened for append mode or the filename is truncated at open time.
	 */
T
Tetsuo Handa 已提交
1113 1114 1115 1116 1117 1118 1119
	if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
	    && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE)
	    != TOMOYO_CONFIG_DISABLED) {
		if (!tomoyo_get_realpath(&buf, path)) {
			error = -ENOMEM;
			goto out;
		}
T
Tetsuo Handa 已提交
1120
		if (tomoyo_no_rewrite_file(&buf))
T
Tetsuo Handa 已提交
1121 1122
			error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
						       &buf);
1123
	}
T
Tetsuo Handa 已提交
1124 1125 1126
	if (!error && acc_mode &&
	    tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
	    != TOMOYO_CONFIG_DISABLED) {
1127
		u8 operation;
T
Tetsuo Handa 已提交
1128 1129 1130 1131
		if (!buf.name && !tomoyo_get_realpath(&buf, path)) {
			error = -ENOMEM;
			goto out;
		}
1132 1133 1134 1135 1136 1137 1138
		if (acc_mode == (MAY_READ | MAY_WRITE))
			operation = TOMOYO_TYPE_READ_WRITE;
		else if (acc_mode == MAY_READ)
			operation = TOMOYO_TYPE_READ;
		else
			operation = TOMOYO_TYPE_WRITE;
		error = tomoyo_path_permission(&r, operation, &buf);
T
Tetsuo Handa 已提交
1139
	}
1140
 out:
T
Tetsuo Handa 已提交
1141
	kfree(buf.name);
1142
	tomoyo_read_unlock(idx);
1143
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1144 1145 1146 1147 1148
		error = 0;
	return error;
}

/**
T
Tetsuo Handa 已提交
1149
 * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
1150 1151 1152 1153 1154 1155
 *
 * @operation: Type of operation.
 * @path:      Pointer to "struct path".
 *
 * Returns 0 on success, negative value otherwise.
 */
1156
int tomoyo_path_perm(const u8 operation, struct path *path)
1157 1158
{
	int error = -ENOMEM;
T
Tetsuo Handa 已提交
1159
	struct tomoyo_path_info buf;
1160
	struct tomoyo_request_info r;
1161
	int idx;
1162

T
Tetsuo Handa 已提交
1163 1164 1165 1166
	if (!path->mnt)
		return 0;
	if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
	    == TOMOYO_CONFIG_DISABLED)
1167
		return 0;
T
Tetsuo Handa 已提交
1168
	buf.name = NULL;
1169
	idx = tomoyo_read_lock();
T
Tetsuo Handa 已提交
1170
	if (!tomoyo_get_realpath(&buf, path))
1171 1172
		goto out;
	switch (operation) {
1173
	case TOMOYO_TYPE_REWRITE:
T
Tetsuo Handa 已提交
1174
		if (!tomoyo_no_rewrite_file(&buf)) {
1175 1176 1177 1178
			error = 0;
			goto out;
		}
		break;
T
Tetsuo Handa 已提交
1179 1180
	case TOMOYO_TYPE_RMDIR:
	case TOMOYO_TYPE_CHROOT:
T
Tetsuo Handa 已提交
1181
	case TOMOYO_TYPE_UMOUNT:
T
Tetsuo Handa 已提交
1182 1183
		tomoyo_add_slash(&buf);
		break;
1184
	}
T
Tetsuo Handa 已提交
1185
	error = tomoyo_path_permission(&r, operation, &buf);
1186
 out:
T
Tetsuo Handa 已提交
1187
	kfree(buf.name);
1188
	tomoyo_read_unlock(idx);
1189
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1190 1191 1192 1193
		error = 0;
	return error;
}

1194
/**
T
Tetsuo Handa 已提交
1195
 * tomoyo_mkdev_perm - Check permission for "mkblock" and "mkchar".
1196 1197 1198 1199 1200 1201 1202 1203
 *
 * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK)
 * @path:      Pointer to "struct path".
 * @mode:      Create mode.
 * @dev:       Device number.
 *
 * Returns 0 on success, negative value otherwise.
 */
T
Tetsuo Handa 已提交
1204
int tomoyo_mkdev_perm(const u8 operation, struct path *path,
1205 1206 1207 1208
			     const unsigned int mode, unsigned int dev)
{
	struct tomoyo_request_info r;
	int error = -ENOMEM;
T
Tetsuo Handa 已提交
1209
	struct tomoyo_path_info buf;
1210 1211
	int idx;

T
Tetsuo Handa 已提交
1212 1213 1214
	if (!path->mnt ||
	    tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
	    == TOMOYO_CONFIG_DISABLED)
1215 1216 1217
		return 0;
	idx = tomoyo_read_lock();
	error = -ENOMEM;
T
Tetsuo Handa 已提交
1218
	if (tomoyo_get_realpath(&buf, path)) {
1219
		dev = new_decode_dev(dev);
T
Tetsuo Handa 已提交
1220
		r.param_type = TOMOYO_TYPE_MKDEV_ACL;
1221 1222 1223 1224 1225
		r.param.mkdev.filename = &buf;
		r.param.mkdev.operation = operation;
		r.param.mkdev.mode = mode;
		r.param.mkdev.major = MAJOR(dev);
		r.param.mkdev.minor = MINOR(dev);
1226 1227
		tomoyo_check_acl(&r, tomoyo_check_mkdev_acl);
		error = tomoyo_audit_mkdev_log(&r);
T
Tetsuo Handa 已提交
1228
		kfree(buf.name);
1229 1230 1231 1232 1233 1234 1235
	}
	tomoyo_read_unlock(idx);
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
		error = 0;
	return error;
}

1236
/**
T
Tetsuo Handa 已提交
1237
 * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
1238 1239 1240 1241 1242 1243 1244
 *
 * @operation: Type of operation.
 * @path1:      Pointer to "struct path".
 * @path2:      Pointer to "struct path".
 *
 * Returns 0 on success, negative value otherwise.
 */
1245
int tomoyo_path2_perm(const u8 operation, struct path *path1,
T
Tetsuo Handa 已提交
1246
		      struct path *path2)
1247 1248
{
	int error = -ENOMEM;
T
Tetsuo Handa 已提交
1249 1250
	struct tomoyo_path_info buf1;
	struct tomoyo_path_info buf2;
1251
	struct tomoyo_request_info r;
1252
	int idx;
1253

T
Tetsuo Handa 已提交
1254 1255 1256
	if (!path1->mnt || !path2->mnt ||
	    tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
	    == TOMOYO_CONFIG_DISABLED)
1257
		return 0;
T
Tetsuo Handa 已提交
1258 1259
	buf1.name = NULL;
	buf2.name = NULL;
1260
	idx = tomoyo_read_lock();
T
Tetsuo Handa 已提交
1261 1262
	if (!tomoyo_get_realpath(&buf1, path1) ||
	    !tomoyo_get_realpath(&buf2, path2))
1263
		goto out;
T
Tetsuo Handa 已提交
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
	switch (operation) {
		struct dentry *dentry;
	case TOMOYO_TYPE_RENAME:
        case TOMOYO_TYPE_LINK:
		dentry = path1->dentry;
	        if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
                        break;
                /* fall through */
        case TOMOYO_TYPE_PIVOT_ROOT:
                tomoyo_add_slash(&buf1);
                tomoyo_add_slash(&buf2);
		break;
        }
1277 1278 1279 1280
	r.param_type = TOMOYO_TYPE_PATH2_ACL;
	r.param.path2.operation = operation;
	r.param.path2.filename1 = &buf1;
	r.param.path2.filename2 = &buf2;
1281
	do {
1282 1283 1284
		tomoyo_check_acl(&r, tomoyo_check_path2_acl);
		error = tomoyo_audit_path2_log(&r);
	} while (error == TOMOYO_RETRY_REQUEST);
1285
 out:
T
Tetsuo Handa 已提交
1286 1287
	kfree(buf1.name);
	kfree(buf2.name);
1288
	tomoyo_read_unlock(idx);
1289
	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1290 1291 1292
		error = 0;
	return error;
}
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311

/**
 * tomoyo_write_file_policy - Update file related list.
 *
 * @data:      String to parse.
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
			     const bool is_delete)
{
	char *w[5];
	u8 type;
	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
		return -EINVAL;
1312
	if (strncmp(w[0], "allow_", 6))
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
		goto out;
	w[0] += 6;
	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
		if (strcmp(w[0], tomoyo_path_keyword[type]))
			continue;
		return tomoyo_update_path_acl(type, w[1], domain, is_delete);
	}
	if (!w[2][0])
		goto out;
	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
		if (strcmp(w[0], tomoyo_path2_keyword[type]))
			continue;
		return tomoyo_update_path2_acl(type, w[1], w[2], domain,
					       is_delete);
	}
	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
		if (strcmp(w[0], tomoyo_path_number_keyword[type]))
			continue;
		return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
						     is_delete);
	}
	if (!w[3][0] || !w[4][0])
		goto out;
T
Tetsuo Handa 已提交
1336 1337
	for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) {
		if (strcmp(w[0], tomoyo_mkdev_keyword[type]))
1338
			continue;
T
Tetsuo Handa 已提交
1339 1340
		return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3],
					       w[4], domain, is_delete);
1341 1342 1343 1344
	}
 out:
	return -EINVAL;
}