file.c 35.6 KB
Newer Older
1
2
3
4
5
6
7
/*
 * security/tomoyo/file.c
 *
 * Implementation of the Domain-Based Mandatory Access Control.
 *
 * Copyright (C) 2005-2009  NTT DATA CORPORATION
 *
Tetsuo Handa's avatar
Tetsuo Handa committed
8
 * Version: 2.2.0   2009/04/01
9
10
11
12
 *
 */

#include "common.h"
13
#include <linux/slab.h>
14
15

/* Keyword array for single path operations. */
Tetsuo Handa's avatar
Tetsuo Handa committed
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
	[TOMOYO_TYPE_READ_WRITE] = "read/write",
	[TOMOYO_TYPE_EXECUTE]    = "execute",
	[TOMOYO_TYPE_READ]       = "read",
	[TOMOYO_TYPE_WRITE]      = "write",
	[TOMOYO_TYPE_CREATE]     = "create",
	[TOMOYO_TYPE_UNLINK]     = "unlink",
	[TOMOYO_TYPE_MKDIR]      = "mkdir",
	[TOMOYO_TYPE_RMDIR]      = "rmdir",
	[TOMOYO_TYPE_MKFIFO]     = "mkfifo",
	[TOMOYO_TYPE_MKSOCK]     = "mksock",
	[TOMOYO_TYPE_MKBLOCK]    = "mkblock",
	[TOMOYO_TYPE_MKCHAR]     = "mkchar",
	[TOMOYO_TYPE_TRUNCATE]   = "truncate",
	[TOMOYO_TYPE_SYMLINK]    = "symlink",
	[TOMOYO_TYPE_REWRITE]    = "rewrite",
	[TOMOYO_TYPE_IOCTL]      = "ioctl",
	[TOMOYO_TYPE_CHMOD]      = "chmod",
	[TOMOYO_TYPE_CHOWN]      = "chown",
	[TOMOYO_TYPE_CHGRP]      = "chgrp",
	[TOMOYO_TYPE_CHROOT]     = "chroot",
	[TOMOYO_TYPE_MOUNT]      = "mount",
	[TOMOYO_TYPE_UMOUNT]     = "unmount",
39
40
41
};

/* Keyword array for double path operations. */
Tetsuo Handa's avatar
Tetsuo Handa committed
42
43
44
45
static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
	[TOMOYO_TYPE_LINK]    = "link",
	[TOMOYO_TYPE_RENAME]  = "rename",
	[TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
46
47
};

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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)
		return tomoyo_path_matches_group(name, ptr->group, 1);
	return tomoyo_path_matches_pattern(name, ptr->filename);
}

static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info
					      *name,
					      const struct tomoyo_name_union
					      *ptr, const bool may_use_pattern)
{
	if (ptr->is_group)
		return tomoyo_path_matches_group(name, ptr->group,
						 may_use_pattern);
	if (may_use_pattern || !ptr->filename->is_patterned)
		return tomoyo_path_matches_pattern(name, ptr->filename);
	return false;
}

79
80
81
82
83
84
85
86
87
88
89
90
91
92
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];
}

93
/**
Tetsuo Handa's avatar
Tetsuo Handa committed
94
 * tomoyo_path2keyword - Get the name of single path operation.
95
96
97
98
99
 *
 * @operation: Type of operation.
 *
 * Returns the name of single path operation.
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
100
const char *tomoyo_path2keyword(const u8 operation)
101
{
Tetsuo Handa's avatar
Tetsuo Handa committed
102
103
	return (operation < TOMOYO_MAX_PATH_OPERATION)
		? tomoyo_path_keyword[operation] : NULL;
104
105
106
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
107
 * tomoyo_path22keyword - Get the name of double path operation.
108
109
110
111
112
 *
 * @operation: Type of operation.
 *
 * Returns the name of double path operation.
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
113
const char *tomoyo_path22keyword(const u8 operation)
114
{
Tetsuo Handa's avatar
Tetsuo Handa committed
115
116
	return (operation < TOMOYO_MAX_PATH2_OPERATION)
		? tomoyo_path2_keyword[operation] : NULL;
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
}

/**
 * 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);
}

/**
 * tomoyo_get_path - Get realpath.
 *
 * @path: Pointer to "struct path".
 *
 * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
 */
static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
{
	int error;
147
	struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf),
148
							 GFP_NOFS);
149
150
151
152
153
154
155
156
157
158
159

	if (!buf)
		return NULL;
	/* Reserve one byte for appending "/". */
	error = tomoyo_realpath_from_path2(path, buf->body,
					   sizeof(buf->body) - 2);
	if (!error) {
		buf->head.name = buf->body;
		tomoyo_fill_path_info(&buf->head);
		return &buf->head;
	}
160
	kfree(buf);
161
162
163
	return NULL;
}

Tetsuo Handa's avatar
Tetsuo Handa committed
164
165
166
167
168
169
170
static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
				   const char *filename2,
				   struct tomoyo_domain_info *const domain,
				   const bool is_delete);
static int tomoyo_update_path_acl(const u8 type, const char *filename,
				  struct tomoyo_domain_info *const domain,
				  const bool is_delete);
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
/*
 * 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.
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
197
LIST_HEAD(tomoyo_globally_readable_list);
198
199
200
201
202
203
204
205

/**
 * 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.
206
207
 *
 * Caller holds tomoyo_read_lock().
208
209
210
211
212
 */
static int tomoyo_update_globally_readable_entry(const char *filename,
						 const bool is_delete)
{
	struct tomoyo_globally_readable_file_entry *ptr;
213
	struct tomoyo_globally_readable_file_entry e = { };
214
	int error = is_delete ? -ENOENT : -ENOMEM;
215

216
	if (!tomoyo_is_correct_path(filename, 1, 0, -1))
217
		return -EINVAL;
218
219
	e.filename = tomoyo_get_name(filename);
	if (!e.filename)
220
		return -ENOMEM;
221
222
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
223
	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
224
		if (ptr->filename != e.filename)
225
226
227
			continue;
		ptr->is_deleted = is_delete;
		error = 0;
228
		break;
229
	}
230
231
232
233
234
235
236
237
	if (!is_delete && error) {
		struct tomoyo_globally_readable_file_entry *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
			list_add_tail_rcu(&entry->list,
					  &tomoyo_globally_readable_list);
			error = 0;
		}
238
	}
239
	mutex_unlock(&tomoyo_policy_lock);
240
 out:
241
	tomoyo_put_name(e.filename);
242
243
244
245
246
247
248
249
250
	return error;
}

/**
 * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
 *
 * @filename: The filename to check.
 *
 * Returns true if any domain can open @filename for reading, false otherwise.
251
252
 *
 * Caller holds tomoyo_read_lock().
253
254
255
256
257
258
 */
static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
					     filename)
{
	struct tomoyo_globally_readable_file_entry *ptr;
	bool found = false;
259
260

	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
		if (!ptr->is_deleted &&
		    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.
277
278
 *
 * Caller holds tomoyo_read_lock().
279
280
281
282
283
284
285
286
287
288
289
290
 */
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.
291
292
 *
 * Caller holds tomoyo_read_lock().
293
294
295
296
297
298
299
300
301
302
303
304
305
306
 */
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,
				 list);
		if (ptr->is_deleted)
			continue;
Tetsuo Handa's avatar
Tetsuo Handa committed
307
308
309
		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
					ptr->filename->name);
		if (!done)
310
311
312
313
314
			break;
	}
	return done;
}

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/* 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.
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
344
LIST_HEAD(tomoyo_pattern_list);
345
346
347
348
349
350
351
352

/**
 * 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.
353
354
 *
 * Caller holds tomoyo_read_lock().
355
356
357
358
359
 */
static int tomoyo_update_file_pattern_entry(const char *pattern,
					    const bool is_delete)
{
	struct tomoyo_pattern_entry *ptr;
360
	struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) };
361
	int error = is_delete ? -ENOENT : -ENOMEM;
362

363
	if (!e.pattern)
364
		return error;
365
	if (!e.pattern->is_patterned)
366
		goto out;
367
368
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
369
	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
370
		if (e.pattern != ptr->pattern)
371
372
373
			continue;
		ptr->is_deleted = is_delete;
		error = 0;
374
		break;
375
	}
376
377
378
379
380
381
382
	if (!is_delete && error) {
		struct tomoyo_pattern_entry *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
			list_add_tail_rcu(&entry->list, &tomoyo_pattern_list);
			error = 0;
		}
383
	}
384
	mutex_unlock(&tomoyo_policy_lock);
385
 out:
386
	tomoyo_put_name(e.pattern);
387
388
389
390
391
392
393
394
395
	return error;
}

/**
 * tomoyo_get_file_pattern - Get patterned pathname.
 *
 * @filename: The filename to find patterned pathname.
 *
 * Returns pointer to pathname pattern if matched, @filename otherwise.
396
397
 *
 * Caller holds tomoyo_read_lock().
398
399
400
401
402
403
404
 */
static const struct tomoyo_path_info *
tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
{
	struct tomoyo_pattern_entry *ptr;
	const struct tomoyo_path_info *pattern = NULL;

405
	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
		if (ptr->is_deleted)
			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;
	return filename;
}

/**
 * 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.
430
431
 *
 * Caller holds tomoyo_read_lock().
432
433
434
435
436
437
438
439
440
441
442
443
 */
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.
444
445
 *
 * Caller holds tomoyo_read_lock().
446
447
448
449
450
451
452
453
454
455
456
 */
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;
		ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
		if (ptr->is_deleted)
			continue;
Tetsuo Handa's avatar
Tetsuo Handa committed
457
458
459
		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
					"%s\n", ptr->pattern->name);
		if (!done)
460
461
462
463
464
			break;
	}
	return done;
}

465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/*
 * 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.
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
494
LIST_HEAD(tomoyo_no_rewrite_list);
495
496
497
498
499
500
501
502

/**
 * 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.
503
504
 *
 * Caller holds tomoyo_read_lock().
505
506
507
508
 */
static int tomoyo_update_no_rewrite_entry(const char *pattern,
					  const bool is_delete)
{
509
	struct tomoyo_no_rewrite_entry *ptr;
510
	struct tomoyo_no_rewrite_entry e = { };
511
	int error = is_delete ? -ENOENT : -ENOMEM;
512

513
	if (!tomoyo_is_correct_path(pattern, 0, 0, 0))
514
		return -EINVAL;
515
516
	e.pattern = tomoyo_get_name(pattern);
	if (!e.pattern)
517
		return error;
518
519
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
520
	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
521
		if (ptr->pattern != e.pattern)
522
523
524
			continue;
		ptr->is_deleted = is_delete;
		error = 0;
525
		break;
526
	}
527
528
529
530
531
532
533
534
	if (!is_delete && error) {
		struct tomoyo_no_rewrite_entry *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
			list_add_tail_rcu(&entry->list,
					  &tomoyo_no_rewrite_list);
			error = 0;
		}
535
	}
536
	mutex_unlock(&tomoyo_policy_lock);
537
 out:
538
	tomoyo_put_name(e.pattern);
539
540
541
542
543
544
545
546
547
548
	return error;
}

/**
 * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
 *
 * @filename: Filename to check.
 *
 * Returns true if @filename is specified by "deny_rewrite" directive,
 * false otherwise.
549
550
 *
 * Caller holds tomoyo_read_lock().
551
552
553
554
555
556
 */
static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
{
	struct tomoyo_no_rewrite_entry *ptr;
	bool found = false;

557
	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
		if (ptr->is_deleted)
			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.
575
576
 *
 * Caller holds tomoyo_read_lock().
577
578
579
580
581
582
583
584
585
586
587
588
 */
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.
589
590
 *
 * Caller holds tomoyo_read_lock().
591
592
593
594
595
596
597
598
599
600
601
 */
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;
		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
		if (ptr->is_deleted)
			continue;
Tetsuo Handa's avatar
Tetsuo Handa committed
602
603
604
		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
					"%s\n", ptr->pattern->name);
		if (!done)
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
			break;
	}
	return done;
}

/**
 * tomoyo_update_file_acl - Update file's read/write/execute ACL.
 *
 * @filename:  Filename.
 * @perm:      Permission (between 1 to 7).
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * This is legacy support interface for older policy syntax.
 * Current policy syntax uses "allow_read/write" instead of "6",
 * "allow_read" instead of "4", "allow_write" instead of "2",
 * "allow_execute" instead of "1".
624
625
 *
 * Caller holds tomoyo_read_lock().
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
 */
static int tomoyo_update_file_acl(const char *filename, u8 perm,
				  struct tomoyo_domain_info * const domain,
				  const bool is_delete)
{
	if (perm > 7 || !perm) {
		printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n",
		       __func__, perm, filename);
		return -EINVAL;
	}
	if (filename[0] != '@' && tomoyo_strendswith(filename, "/"))
		/*
		 * Only 'allow_mkdir' and 'allow_rmdir' are valid for
		 * directory permissions.
		 */
		return 0;
	if (perm & 4)
Tetsuo Handa's avatar
Tetsuo Handa committed
643
644
		tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain,
				       is_delete);
645
	if (perm & 2)
Tetsuo Handa's avatar
Tetsuo Handa committed
646
647
		tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain,
				       is_delete);
648
	if (perm & 1)
Tetsuo Handa's avatar
Tetsuo Handa committed
649
650
		tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain,
				       is_delete);
651
652
653
654
	return 0;
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
655
 * tomoyo_path_acl2 - Check permission for single path operation.
656
657
658
659
660
661
662
 *
 * @domain:          Pointer to "struct tomoyo_domain_info".
 * @filename:        Filename to check.
 * @perm:            Permission.
 * @may_use_pattern: True if patterned ACL is permitted.
 *
 * Returns 0 on success, -EPERM otherwise.
663
664
 *
 * Caller holds tomoyo_read_lock().
665
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
666
667
668
static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain,
			    const struct tomoyo_path_info *filename,
			    const u32 perm, const bool may_use_pattern)
669
670
671
672
{
	struct tomoyo_acl_info *ptr;
	int error = -EPERM;

673
	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
Tetsuo Handa's avatar
Tetsuo Handa committed
674
675
		struct tomoyo_path_acl *acl;
		if (ptr->type != TOMOYO_TYPE_PATH_ACL)
676
			continue;
Tetsuo Handa's avatar
Tetsuo Handa committed
677
		acl = container_of(ptr, struct tomoyo_path_acl, head);
678
679
680
681
682
683
684
		if (perm <= 0xFFFF) {
			if (!(acl->perm & perm))
				continue;
		} else {
			if (!(acl->perm_high & (perm >> 16)))
				continue;
		}
685
686
		if (!tomoyo_compare_name_union_pattern(filename, &acl->name,
                                                       may_use_pattern))
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
			continue;
		error = 0;
		break;
	}
	return error;
}

/**
 * tomoyo_check_file_acl - Check permission for opening files.
 *
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @filename:  Filename to check.
 * @operation: Mode ("read" or "write" or "read/write" or "execute").
 *
 * Returns 0 on success, -EPERM otherwise.
702
703
 *
 * Caller holds tomoyo_read_lock().
704
705
706
707
708
 */
static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
				 const struct tomoyo_path_info *filename,
				 const u8 operation)
{
709
	u32 perm = 0;
710
711
712
713

	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
		return 0;
	if (operation == 6)
Tetsuo Handa's avatar
Tetsuo Handa committed
714
		perm = 1 << TOMOYO_TYPE_READ_WRITE;
715
	else if (operation == 4)
Tetsuo Handa's avatar
Tetsuo Handa committed
716
		perm = 1 << TOMOYO_TYPE_READ;
717
	else if (operation == 2)
Tetsuo Handa's avatar
Tetsuo Handa committed
718
		perm = 1 << TOMOYO_TYPE_WRITE;
719
	else if (operation == 1)
Tetsuo Handa's avatar
Tetsuo Handa committed
720
		perm = 1 << TOMOYO_TYPE_EXECUTE;
721
722
	else
		BUG();
Tetsuo Handa's avatar
Tetsuo Handa committed
723
	return tomoyo_path_acl2(domain, filename, perm, operation != 1);
724
725
726
727
728
729
730
731
732
733
734
735
}

/**
 * tomoyo_check_file_perm2 - Check permission for opening files.
 *
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @filename:  Filename to check.
 * @perm:      Mode ("read" or "write" or "read/write" or "execute").
 * @operation: Operation name passed used for verbose mode.
 * @mode:      Access control mode.
 *
 * Returns 0 on success, negative value otherwise.
736
737
 *
 * Caller holds tomoyo_read_lock().
738
739
740
741
742
743
744
745
746
747
748
749
750
 */
static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
				   const struct tomoyo_path_info *filename,
				   const u8 perm, const char *operation,
				   const u8 mode)
{
	const bool is_enforce = (mode == 3);
	const char *msg = "<unknown>";
	int error = 0;

	if (!filename)
		return 0;
	error = tomoyo_check_file_acl(domain, filename, perm);
Tetsuo Handa's avatar
Tetsuo Handa committed
751
	if (error && perm == 4 && !domain->ignore_global_allow_read
752
753
754
	    && tomoyo_is_globally_readable_file(filename))
		error = 0;
	if (perm == 6)
Tetsuo Handa's avatar
Tetsuo Handa committed
755
		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE);
756
	else if (perm == 4)
Tetsuo Handa's avatar
Tetsuo Handa committed
757
		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ);
758
	else if (perm == 2)
Tetsuo Handa's avatar
Tetsuo Handa committed
759
		msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE);
760
	else if (perm == 1)
Tetsuo Handa's avatar
Tetsuo Handa committed
761
		msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE);
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
	else
		BUG();
	if (!error)
		return 0;
	if (tomoyo_verbose_mode(domain))
		printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied "
		       "for %s\n", tomoyo_get_msg(is_enforce), msg, operation,
		       filename->name, tomoyo_get_last_name(domain));
	if (is_enforce)
		return error;
	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
		/* Don't use patterns for execute permission. */
		const struct tomoyo_path_info *patterned_file = (perm != 1) ?
			tomoyo_get_file_pattern(filename) : filename;
		tomoyo_update_file_acl(patterned_file->name, perm,
				       domain, false);
	}
	return 0;
}

/**
 * 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.
790
791
 *
 * Caller holds tomoyo_read_lock().
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
 */
int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
			     const bool is_delete)
{
	char *filename = strchr(data, ' ');
	char *filename2;
	unsigned int perm;
	u8 type;

	if (!filename)
		return -EINVAL;
	*filename++ = '\0';
	if (sscanf(data, "%u", &perm) == 1)
		return tomoyo_update_file_acl(filename, (u8) perm, domain,
					      is_delete);
	if (strncmp(data, "allow_", 6))
		goto out;
	data += 6;
Tetsuo Handa's avatar
Tetsuo Handa committed
810
811
	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
		if (strcmp(data, tomoyo_path_keyword[type]))
812
			continue;
Tetsuo Handa's avatar
Tetsuo Handa committed
813
814
		return tomoyo_update_path_acl(type, filename, domain,
					      is_delete);
815
816
817
818
819
	}
	filename2 = strchr(filename, ' ');
	if (!filename2)
		goto out;
	*filename2++ = '\0';
Tetsuo Handa's avatar
Tetsuo Handa committed
820
821
	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
		if (strcmp(data, tomoyo_path2_keyword[type]))
822
			continue;
Tetsuo Handa's avatar
Tetsuo Handa committed
823
824
		return tomoyo_update_path2_acl(type, filename, filename2,
					       domain, is_delete);
825
826
827
828
829
830
	}
 out:
	return -EINVAL;
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
831
 * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
832
833
834
835
836
837
838
 *
 * @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.
839
840
 *
 * Caller holds tomoyo_read_lock().
841
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
842
843
844
static int tomoyo_update_path_acl(const u8 type, const char *filename,
				  struct tomoyo_domain_info *const domain,
				  const bool is_delete)
845
{
846
	static const u32 tomoyo_rw_mask =
Tetsuo Handa's avatar
Tetsuo Handa committed
847
		(1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE);
848
	const u32 perm = 1 << type;
849
	struct tomoyo_acl_info *ptr;
850
851
852
853
854
	struct tomoyo_path_acl e = {
		.head.type = TOMOYO_TYPE_PATH_ACL,
		.perm_high = perm >> 16,
		.perm = perm
	};
855
	int error = is_delete ? -ENOENT : -ENOMEM;
856

857
858
	if (type == TOMOYO_TYPE_READ_WRITE)
		e.perm |= tomoyo_rw_mask;
859
860
	if (!domain)
		return -EINVAL;
861
	if (!tomoyo_parse_name_union(filename, &e.name))
862
		return -EINVAL;
863
864
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
865
	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
Tetsuo Handa's avatar
Tetsuo Handa committed
866
867
		struct tomoyo_path_acl *acl =
			container_of(ptr, struct tomoyo_path_acl, head);
868
		if (!tomoyo_is_same_path_acl(acl, &e))
869
			continue;
870
871
872
873
874
		if (is_delete) {
			if (perm <= 0xFFFF)
				acl->perm &= ~perm;
			else
				acl->perm_high &= ~(perm >> 16);
875
			if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask)
Tetsuo Handa's avatar
Tetsuo Handa committed
876
877
				acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
			else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)))
878
				acl->perm &= ~tomoyo_rw_mask;
879
880
881
882
883
		} else {
			if (perm <= 0xFFFF)
				acl->perm |= perm;
			else
				acl->perm_high |= (perm >> 16);
884
			if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask)
Tetsuo Handa's avatar
Tetsuo Handa committed
885
886
				acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE;
			else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))
887
				acl->perm |= tomoyo_rw_mask;
888
		}
889
		error = 0;
890
		break;
891
	}
892
893
894
895
896
897
898
899
	if (!is_delete && error) {
		struct tomoyo_path_acl *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
			list_add_tail_rcu(&entry->head.list,
					  &domain->acl_info_list);
			error = 0;
		}
900
	}
901
	mutex_unlock(&tomoyo_policy_lock);
902
 out:
903
	tomoyo_put_name_union(&e.name);
904
905
906
907
	return error;
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
908
 * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
909
910
911
912
913
914
915
916
 *
 * @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.
917
918
 *
 * Caller holds tomoyo_read_lock().
919
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
920
921
922
923
static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
				   const char *filename2,
				   struct tomoyo_domain_info *const domain,
				   const bool is_delete)
924
{
925
926
927
928
929
	const u8 perm = 1 << type;
	struct tomoyo_path2_acl e = {
		.head.type = TOMOYO_TYPE_PATH2_ACL,
		.perm = perm
	};
930
	struct tomoyo_acl_info *ptr;
931
	int error = is_delete ? -ENOENT : -ENOMEM;
932
933
934

	if (!domain)
		return -EINVAL;
935
936
	if (!tomoyo_parse_name_union(filename1, &e.name1) ||
	    !tomoyo_parse_name_union(filename2, &e.name2))
937
		goto out;
938
939
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
940
	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
Tetsuo Handa's avatar
Tetsuo Handa committed
941
942
		struct tomoyo_path2_acl *acl =
			container_of(ptr, struct tomoyo_path2_acl, head);
943
		if (!tomoyo_is_same_path2_acl(acl, &e))
944
			continue;
945
946
947
948
		if (is_delete)
			acl->perm &= ~perm;
		else
			acl->perm |= perm;
949
		error = 0;
950
		break;
951
	}
952
953
954
955
956
957
958
959
	if (!is_delete && error) {
		struct tomoyo_path2_acl *entry =
			tomoyo_commit_ok(&e, sizeof(e));
		if (entry) {
			list_add_tail_rcu(&entry->head.list,
					  &domain->acl_info_list);
			error = 0;
		}
960
	}
961
	mutex_unlock(&tomoyo_policy_lock);
962
 out:
963
964
	tomoyo_put_name_union(&e.name1);
	tomoyo_put_name_union(&e.name2);
965
966
967
968
	return error;
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
969
 * tomoyo_path_acl - Check permission for single path operation.
970
971
972
973
974
975
 *
 * @domain:   Pointer to "struct tomoyo_domain_info".
 * @type:     Type of operation.
 * @filename: Filename to check.
 *
 * Returns 0 on success, negative value otherwise.
976
977
 *
 * Caller holds tomoyo_read_lock().
978
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
979
980
static int tomoyo_path_acl(struct tomoyo_domain_info *domain, const u8 type,
			   const struct tomoyo_path_info *filename)
981
982
983
{
	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
		return 0;
Tetsuo Handa's avatar
Tetsuo Handa committed
984
	return tomoyo_path_acl2(domain, filename, 1 << type, 1);
985
986
987
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
988
 * tomoyo_path2_acl - Check permission for double path operation.
989
990
991
992
993
994
995
 *
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @type:      Type of operation.
 * @filename1: First filename to check.
 * @filename2: Second filename to check.
 *
 * Returns 0 on success, -EPERM otherwise.
996
997
 *
 * Caller holds tomoyo_read_lock().
998
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
999
1000
1001
1002
static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain,
			    const u8 type,
			    const struct tomoyo_path_info *filename1,
			    const struct tomoyo_path_info *filename2)
1003
1004
1005
1006
1007
1008
1009
{
	struct tomoyo_acl_info *ptr;
	const u8 perm = 1 << type;
	int error = -EPERM;

	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
		return 0;
1010
	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
Tetsuo Handa's avatar
Tetsuo Handa committed
1011
1012
		struct tomoyo_path2_acl *acl;
		if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
1013
			continue;
Tetsuo Handa's avatar
Tetsuo Handa committed
1014
		acl = container_of(ptr, struct tomoyo_path2_acl, head);
1015
1016
		if (!(acl->perm & perm))
			continue;
1017
		if (!tomoyo_compare_name_union(filename1, &acl->name1))
1018
			continue;
1019
		if (!tomoyo_compare_name_union(filename2, &acl->name2))
1020
1021
1022
1023
1024
1025
1026
1027
			continue;
		error = 0;
		break;
	}
	return error;
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
1028
 * tomoyo_path_permission2 - Check permission for single path operation.
1029
1030
1031
1032
1033
1034
1035
 *
 * @domain:    Pointer to "struct tomoyo_domain_info".
 * @operation: Type of operation.
 * @filename:  Filename to check.
 * @mode:      Access control mode.
 *
 * Returns 0 on success, negative value otherwise.
1036
1037
 *
 * Caller holds tomoyo_read_lock().
1038
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
1039
1040
1041
1042
static int tomoyo_path_permission2(struct tomoyo_domain_info *const domain,
				   u8 operation,
				   const struct tomoyo_path_info *filename,
				   const u8 mode)
1043
1044
1045
1046
1047
1048
1049
1050
{
	const char *msg;
	int error;
	const bool is_enforce = (mode == 3);

	if (!mode)
		return 0;
 next:
Tetsuo Handa's avatar
Tetsuo Handa committed
1051
1052
	error = tomoyo_path_acl(domain, operation, filename);
	msg = tomoyo_path2keyword(operation);
1053
1054
1055
1056
1057
1058
1059
1060
	if (!error)
		goto ok;
	if (tomoyo_verbose_mode(domain))
		printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n",
		       tomoyo_get_msg(is_enforce), msg, filename->name,
		       tomoyo_get_last_name(domain));
	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
		const char *name = tomoyo_get_file_pattern(filename)->name;
Tetsuo Handa's avatar
Tetsuo Handa committed
1061
		tomoyo_update_path_acl(operation, name, domain, false);
1062
1063
1064
1065
1066
1067
1068
1069
1070
	}
	if (!is_enforce)
		error = 0;
 ok:
	/*
	 * 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.
	 */
Tetsuo Handa's avatar
Tetsuo Handa committed
1071
	if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
1072
	    tomoyo_is_no_rewrite_file(filename)) {
Tetsuo Handa's avatar
Tetsuo Handa committed
1073
		operation = TOMOYO_TYPE_REWRITE;
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
		goto next;
	}
	return error;
}

/**
 * tomoyo_check_exec_perm - Check permission for "execute".
 *
 * @domain:   Pointer to "struct tomoyo_domain_info".
 * @filename: Check permission for "execute".
 *
 * Returns 0 on success, negativevalue otherwise.
1086
1087
 *
 * Caller holds tomoyo_read_lock().
1088
1089
 */
int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
1090
			   const struct tomoyo_path_info *filename)
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
{
	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);

	if (!mode)
		return 0;
	return tomoyo_check_file_perm2(domain, filename, 1, "do_execve", mode);
}

/**
 * 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;
	struct tomoyo_path_info *buf;
	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
	const bool is_enforce = (mode == 3);
1116
	int idx;
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127

	if (!mode || !path->mnt)
		return 0;
	if (acc_mode == 0)
		return 0;
	if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
		/*
		 * I don't check directories here because mkdir() and rmdir()
		 * don't call me.
		 */
		return 0;
1128
	idx = tomoyo_read_lock();
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
	buf = tomoyo_get_path(path);
	if (!buf)
		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.
	 */
	if ((acc_mode & MAY_WRITE) &&
	    ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
	    (tomoyo_is_no_rewrite_file(buf))) {
Tetsuo Handa's avatar
Tetsuo Handa committed
1141
1142
		error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE,
						buf, mode);
1143
1144
1145
1146
1147
	}
	if (!error)
		error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open",
						mode);
	if (!error && (flag & O_TRUNC))
Tetsuo Handa's avatar
Tetsuo Handa committed
1148
1149
		error = tomoyo_path_permission2(domain, TOMOYO_TYPE_TRUNCATE,
						buf, mode);
1150
 out:
1151
	kfree(buf);
1152
	tomoyo_read_unlock(idx);
1153
1154
1155
1156
1157
1158
	if (!is_enforce)
		error = 0;
	return error;
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
1159
 * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount".
1160
1161
1162
1163
1164
1165
 *
 * @operation: Type of operation.
 * @path:      Pointer to "struct path".
 *
 * Returns 0 on success, negative value otherwise.
 */
1166
int tomoyo_path_perm(const u8 operation, struct path *path)
1167
1168
1169
{
	int error = -ENOMEM;
	struct tomoyo_path_info *buf;
1170
	struct tomoyo_domain_info *domain = tomoyo_domain();
1171
1172
	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
	const bool is_enforce = (mode == 3);
1173
	int idx;
1174
1175
1176

	if (!mode || !path->mnt)
		return 0;
1177
	idx = tomoyo_read_lock();
1178
1179
1180
1181
	buf = tomoyo_get_path(path);
	if (!buf)
		goto out;
	switch (operation) {
Tetsuo Handa's avatar
Tetsuo Handa committed
1182
1183
1184
	case TOMOYO_TYPE_MKDIR:
	case TOMOYO_TYPE_RMDIR:
	case TOMOYO_TYPE_CHROOT:
1185
1186
1187
1188
1189
1190
1191
1192
		if (!buf->is_dir) {
			/*
			 * tomoyo_get_path() reserves space for appending "/."
			 */
			strcat((char *) buf->name, "/");
			tomoyo_fill_path_info(buf);
		}
	}
Tetsuo Handa's avatar
Tetsuo Handa committed
1193
	error = tomoyo_path_permission2(domain, operation, buf, mode);
1194
 out:
1195
	kfree(buf);
1196
	tomoyo_read_unlock(idx);
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
	if (!is_enforce)
		error = 0;
	return error;
}

/**
 * tomoyo_check_rewrite_permission - Check permission for "rewrite".
 *
 * @filp: Pointer to "struct file".
 *
 * Returns 0 on success, negative value otherwise.
 */
1209
int tomoyo_check_rewrite_permission(struct file *filp)
1210
1211
{
	int error = -ENOMEM;
1212
	struct tomoyo_domain_info *domain = tomoyo_domain();
1213
1214
1215
	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
	const bool is_enforce = (mode == 3);
	struct tomoyo_path_info *buf;
1216
	int idx;
1217
1218
1219

	if (!mode || !filp->f_path.mnt)
		return 0;
1220
1221

	idx = tomoyo_read_lock();
1222
1223
1224
1225
1226
1227
1228
	buf = tomoyo_get_path(&filp->f_path);
	if (!buf)
		goto out;
	if (!tomoyo_is_no_rewrite_file(buf)) {
		error = 0;
		goto out;
	}
Tetsuo Handa's avatar
Tetsuo Handa committed
1229
	error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, buf, mode);
1230
 out:
1231
	kfree(buf);
1232
	tomoyo_read_unlock(idx);
1233
1234
1235
1236
1237
1238
	if (!is_enforce)
		error = 0;
	return error;
}

/**
Tetsuo Handa's avatar
Tetsuo Handa committed
1239
 * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
1240
1241
1242
1243
1244
1245
1246
 *
 * @operation: Type of operation.
 * @path1:      Pointer to "struct path".
 * @path2:      Pointer to "struct path".
 *
 * Returns 0 on success, negative value otherwise.
 */
1247
int tomoyo_path2_perm(const u8 operation, struct path *path1,
Tetsuo Handa's avatar
Tetsuo Handa committed
1248
		      struct path *path2)
1249
1250
1251
{
	int error = -ENOMEM;
	struct tomoyo_path_info *buf1, *buf2;
1252
	struct tomoyo_domain_info *domain = tomoyo_domain();
1253
1254
1255
	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
	const bool is_enforce = (mode == 3);
	const char *msg;
1256
	int idx;
1257
1258
1259

	if (!mode || !path1->mnt || !path2->mnt)
		return 0;
1260
	idx = tomoyo_read_lock();
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
	buf1 = tomoyo_get_path(path1);
	buf2 = tomoyo_get_path(path2);
	if (!buf1 || !buf2)
		goto out;
	{
		struct dentry *dentry = path1->dentry;
		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
			/*
			 * tomoyo_get_path() reserves space for appending "/."
			 */
			if (!buf1->is_dir) {
				strcat((char *) buf1->name, "/");
				tomoyo_fill_path_info(buf1);
			}
			if (!buf2->is_dir) {
				strcat((char *) buf2->name, "/");
				tomoyo_fill_path_info(buf2);
			}
		}
	}
Tetsuo Handa's avatar
Tetsuo Handa committed
1281
1282
	error = tomoyo_path2_acl(domain, operation, buf1, buf2);
	msg = tomoyo_path22keyword(operation);
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
	if (!error)
		goto out;
	if (tomoyo_verbose_mode(domain))
		printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' "
		       "denied for %s\n", tomoyo_get_msg(is_enforce),
		       msg, buf1->name, buf2->name,
		       tomoyo_get_last_name(domain));
	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
		const char *name1 = tomoyo_get_file_pattern(buf1)->name;
		const char *name2 = tomoyo_get_file_pattern(buf2)->name;
Tetsuo Handa's avatar
Tetsuo Handa committed
1293
1294
		tomoyo_update_path2_acl(operation, name1, name2, domain,
					false);
1295
1296
	}
 out:
1297
1298
	kfree(buf1);
	kfree(buf2);
1299
	tomoyo_read_unlock(idx);
1300
1301
1302
1303
	if (!is_enforce)
		error = 0;
	return error;
}