xref: /openbmc/linux/security/tomoyo/file.c (revision 2106ccd9)
1b69a54eeSKentaro Takeda /*
2b69a54eeSKentaro Takeda  * security/tomoyo/file.c
3b69a54eeSKentaro Takeda  *
4b69a54eeSKentaro Takeda  * Implementation of the Domain-Based Mandatory Access Control.
5b69a54eeSKentaro Takeda  *
6b69a54eeSKentaro Takeda  * Copyright (C) 2005-2009  NTT DATA CORPORATION
7b69a54eeSKentaro Takeda  *
839826a1eSTetsuo Handa  * Version: 2.2.0   2009/04/01
9b69a54eeSKentaro Takeda  *
10b69a54eeSKentaro Takeda  */
11b69a54eeSKentaro Takeda 
12b69a54eeSKentaro Takeda #include "common.h"
135a0e3ad6STejun Heo #include <linux/slab.h>
14b69a54eeSKentaro Takeda 
15a1f9bb6aSTetsuo Handa /* Keyword array for operations with one pathname. */
167ef61233STetsuo Handa static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
177ef61233STetsuo Handa 	[TOMOYO_TYPE_READ_WRITE] = "read/write",
187ef61233STetsuo Handa 	[TOMOYO_TYPE_EXECUTE]    = "execute",
197ef61233STetsuo Handa 	[TOMOYO_TYPE_READ]       = "read",
207ef61233STetsuo Handa 	[TOMOYO_TYPE_WRITE]      = "write",
217ef61233STetsuo Handa 	[TOMOYO_TYPE_UNLINK]     = "unlink",
227ef61233STetsuo Handa 	[TOMOYO_TYPE_RMDIR]      = "rmdir",
237ef61233STetsuo Handa 	[TOMOYO_TYPE_TRUNCATE]   = "truncate",
247ef61233STetsuo Handa 	[TOMOYO_TYPE_SYMLINK]    = "symlink",
257ef61233STetsuo Handa 	[TOMOYO_TYPE_REWRITE]    = "rewrite",
267ef61233STetsuo Handa 	[TOMOYO_TYPE_CHROOT]     = "chroot",
277ef61233STetsuo Handa 	[TOMOYO_TYPE_UMOUNT]     = "unmount",
28b69a54eeSKentaro Takeda };
29b69a54eeSKentaro Takeda 
30a1f9bb6aSTetsuo Handa /* Keyword array for operations with one pathname and three numbers. */
31a1f9bb6aSTetsuo Handa static const char *tomoyo_path_number3_keyword
32a1f9bb6aSTetsuo Handa [TOMOYO_MAX_PATH_NUMBER3_OPERATION] = {
33a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKBLOCK]    = "mkblock",
34a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKCHAR]     = "mkchar",
35a1f9bb6aSTetsuo Handa };
36a1f9bb6aSTetsuo Handa 
37a1f9bb6aSTetsuo Handa /* Keyword array for operations with two pathnames. */
387ef61233STetsuo Handa static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
397ef61233STetsuo Handa 	[TOMOYO_TYPE_LINK]       = "link",
407ef61233STetsuo Handa 	[TOMOYO_TYPE_RENAME]     = "rename",
417ef61233STetsuo Handa 	[TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
42b69a54eeSKentaro Takeda };
43b69a54eeSKentaro Takeda 
44a1f9bb6aSTetsuo Handa /* Keyword array for operations with one pathname and one number. */
45a1f9bb6aSTetsuo Handa static const char *tomoyo_path_number_keyword
46a1f9bb6aSTetsuo Handa [TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
47a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_CREATE]     = "create",
48a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKDIR]      = "mkdir",
49a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKFIFO]     = "mkfifo",
50a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKSOCK]     = "mksock",
51a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_IOCTL]      = "ioctl",
52a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_CHMOD]      = "chmod",
53a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_CHOWN]      = "chown",
54a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_CHGRP]      = "chgrp",
55a1f9bb6aSTetsuo Handa };
56a1f9bb6aSTetsuo Handa 
577762fbffSTetsuo Handa void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
587762fbffSTetsuo Handa {
597762fbffSTetsuo Handa 	if (!ptr)
607762fbffSTetsuo Handa 		return;
617762fbffSTetsuo Handa 	if (ptr->is_group)
627762fbffSTetsuo Handa 		tomoyo_put_path_group(ptr->group);
637762fbffSTetsuo Handa 	else
647762fbffSTetsuo Handa 		tomoyo_put_name(ptr->filename);
657762fbffSTetsuo Handa }
667762fbffSTetsuo Handa 
677762fbffSTetsuo Handa bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
687762fbffSTetsuo Handa 			       const struct tomoyo_name_union *ptr)
697762fbffSTetsuo Handa {
707762fbffSTetsuo Handa 	if (ptr->is_group)
717762fbffSTetsuo Handa 		return tomoyo_path_matches_group(name, ptr->group, 1);
727762fbffSTetsuo Handa 	return tomoyo_path_matches_pattern(name, ptr->filename);
737762fbffSTetsuo Handa }
747762fbffSTetsuo Handa 
757762fbffSTetsuo Handa static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info
767762fbffSTetsuo Handa 					      *name,
777762fbffSTetsuo Handa 					      const struct tomoyo_name_union
787762fbffSTetsuo Handa 					      *ptr, const bool may_use_pattern)
797762fbffSTetsuo Handa {
807762fbffSTetsuo Handa 	if (ptr->is_group)
817762fbffSTetsuo Handa 		return tomoyo_path_matches_group(name, ptr->group,
827762fbffSTetsuo Handa 						 may_use_pattern);
837762fbffSTetsuo Handa 	if (may_use_pattern || !ptr->filename->is_patterned)
847762fbffSTetsuo Handa 		return tomoyo_path_matches_pattern(name, ptr->filename);
857762fbffSTetsuo Handa 	return false;
867762fbffSTetsuo Handa }
877762fbffSTetsuo Handa 
884c3e9e2dSTetsuo Handa void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
894c3e9e2dSTetsuo Handa {
904c3e9e2dSTetsuo Handa 	if (ptr && ptr->is_group)
914c3e9e2dSTetsuo Handa 		tomoyo_put_number_group(ptr->group);
924c3e9e2dSTetsuo Handa }
934c3e9e2dSTetsuo Handa 
944c3e9e2dSTetsuo Handa bool tomoyo_compare_number_union(const unsigned long value,
954c3e9e2dSTetsuo Handa 				 const struct tomoyo_number_union *ptr)
964c3e9e2dSTetsuo Handa {
974c3e9e2dSTetsuo Handa 	if (ptr->is_group)
984c3e9e2dSTetsuo Handa 		return tomoyo_number_matches_group(value, value, ptr->group);
994c3e9e2dSTetsuo Handa 	return value >= ptr->values[0] && value <= ptr->values[1];
1004c3e9e2dSTetsuo Handa }
1014c3e9e2dSTetsuo Handa 
102b69a54eeSKentaro Takeda /**
103cb0abe6aSTetsuo Handa  * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members.
104cb0abe6aSTetsuo Handa  *
105cb0abe6aSTetsuo Handa  * @r:      Pointer to "struct tomoyo_request_info" to initialize.
106cb0abe6aSTetsuo Handa  * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain().
107cb0abe6aSTetsuo Handa  *
108cb0abe6aSTetsuo Handa  * Returns mode.
109cb0abe6aSTetsuo Handa  */
1102106ccd9STetsuo Handa int tomoyo_init_request_info(struct tomoyo_request_info *r,
111cb0abe6aSTetsuo Handa 			     struct tomoyo_domain_info *domain)
112cb0abe6aSTetsuo Handa {
113cb0abe6aSTetsuo Handa 	memset(r, 0, sizeof(*r));
114cb0abe6aSTetsuo Handa 	if (!domain)
115cb0abe6aSTetsuo Handa 		domain = tomoyo_domain();
116cb0abe6aSTetsuo Handa 	r->domain = domain;
117cb0abe6aSTetsuo Handa 	r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
118cb0abe6aSTetsuo Handa 	return r->mode;
119cb0abe6aSTetsuo Handa }
120cb0abe6aSTetsuo Handa 
121cb0abe6aSTetsuo Handa static void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
122cb0abe6aSTetsuo Handa      __attribute__ ((format(printf, 2, 3)));
123cb0abe6aSTetsuo Handa /**
124cb0abe6aSTetsuo Handa  * tomoyo_warn_log - Print warning or error message on console.
125cb0abe6aSTetsuo Handa  *
126cb0abe6aSTetsuo Handa  * @r:   Pointer to "struct tomoyo_request_info".
127cb0abe6aSTetsuo Handa  * @fmt: The printf()'s format string, followed by parameters.
128cb0abe6aSTetsuo Handa  */
129cb0abe6aSTetsuo Handa static void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
130cb0abe6aSTetsuo Handa {
131cb0abe6aSTetsuo Handa 	int len = PAGE_SIZE;
132cb0abe6aSTetsuo Handa 	va_list args;
133cb0abe6aSTetsuo Handa 	char *buffer;
134cb0abe6aSTetsuo Handa 	if (!tomoyo_verbose_mode(r->domain))
135cb0abe6aSTetsuo Handa 		return;
136cb0abe6aSTetsuo Handa 	while (1) {
137cb0abe6aSTetsuo Handa 		int len2;
138cb0abe6aSTetsuo Handa 		buffer = kmalloc(len, GFP_NOFS);
139cb0abe6aSTetsuo Handa 		if (!buffer)
140cb0abe6aSTetsuo Handa 			return;
141cb0abe6aSTetsuo Handa 		va_start(args, fmt);
142cb0abe6aSTetsuo Handa 		len2 = vsnprintf(buffer, len - 1, fmt, args);
143cb0abe6aSTetsuo Handa 		va_end(args);
144cb0abe6aSTetsuo Handa 		if (len2 <= len - 1) {
145cb0abe6aSTetsuo Handa 			buffer[len2] = '\0';
146cb0abe6aSTetsuo Handa 			break;
147cb0abe6aSTetsuo Handa 		}
148cb0abe6aSTetsuo Handa 		len = len2 + 1;
149cb0abe6aSTetsuo Handa 		kfree(buffer);
150cb0abe6aSTetsuo Handa 	}
151cb0abe6aSTetsuo Handa 	printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n",
152cb0abe6aSTetsuo Handa 	       r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING",
153cb0abe6aSTetsuo Handa 	       buffer, tomoyo_get_last_name(r->domain));
154cb0abe6aSTetsuo Handa 	kfree(buffer);
155cb0abe6aSTetsuo Handa }
156cb0abe6aSTetsuo Handa 
157cb0abe6aSTetsuo Handa /**
1587ef61233STetsuo Handa  * tomoyo_path2keyword - Get the name of single path operation.
159b69a54eeSKentaro Takeda  *
160b69a54eeSKentaro Takeda  * @operation: Type of operation.
161b69a54eeSKentaro Takeda  *
162b69a54eeSKentaro Takeda  * Returns the name of single path operation.
163b69a54eeSKentaro Takeda  */
1647ef61233STetsuo Handa const char *tomoyo_path2keyword(const u8 operation)
165b69a54eeSKentaro Takeda {
1667ef61233STetsuo Handa 	return (operation < TOMOYO_MAX_PATH_OPERATION)
1677ef61233STetsuo Handa 		? tomoyo_path_keyword[operation] : NULL;
168b69a54eeSKentaro Takeda }
169b69a54eeSKentaro Takeda 
170b69a54eeSKentaro Takeda /**
171a1f9bb6aSTetsuo Handa  * tomoyo_path_number32keyword - Get the name of path/number/number/number operations.
172a1f9bb6aSTetsuo Handa  *
173a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
174a1f9bb6aSTetsuo Handa  *
175a1f9bb6aSTetsuo Handa  * Returns the name of path/number/number/number operation.
176a1f9bb6aSTetsuo Handa  */
177a1f9bb6aSTetsuo Handa const char *tomoyo_path_number32keyword(const u8 operation)
178a1f9bb6aSTetsuo Handa {
179a1f9bb6aSTetsuo Handa 	return (operation < TOMOYO_MAX_PATH_NUMBER3_OPERATION)
180a1f9bb6aSTetsuo Handa 		? tomoyo_path_number3_keyword[operation] : NULL;
181a1f9bb6aSTetsuo Handa }
182a1f9bb6aSTetsuo Handa 
183a1f9bb6aSTetsuo Handa /**
1847ef61233STetsuo Handa  * tomoyo_path22keyword - Get the name of double path operation.
185b69a54eeSKentaro Takeda  *
186b69a54eeSKentaro Takeda  * @operation: Type of operation.
187b69a54eeSKentaro Takeda  *
188b69a54eeSKentaro Takeda  * Returns the name of double path operation.
189b69a54eeSKentaro Takeda  */
1907ef61233STetsuo Handa const char *tomoyo_path22keyword(const u8 operation)
191b69a54eeSKentaro Takeda {
1927ef61233STetsuo Handa 	return (operation < TOMOYO_MAX_PATH2_OPERATION)
1937ef61233STetsuo Handa 		? tomoyo_path2_keyword[operation] : NULL;
194b69a54eeSKentaro Takeda }
195b69a54eeSKentaro Takeda 
196b69a54eeSKentaro Takeda /**
197a1f9bb6aSTetsuo Handa  * tomoyo_path_number2keyword - Get the name of path/number operations.
198a1f9bb6aSTetsuo Handa  *
199a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
200a1f9bb6aSTetsuo Handa  *
201a1f9bb6aSTetsuo Handa  * Returns the name of path/number operation.
202a1f9bb6aSTetsuo Handa  */
203a1f9bb6aSTetsuo Handa const char *tomoyo_path_number2keyword(const u8 operation)
204a1f9bb6aSTetsuo Handa {
205a1f9bb6aSTetsuo Handa 	return (operation < TOMOYO_MAX_PATH_NUMBER_OPERATION)
206a1f9bb6aSTetsuo Handa 		? tomoyo_path_number_keyword[operation] : NULL;
207a1f9bb6aSTetsuo Handa }
208a1f9bb6aSTetsuo Handa 
209a1f9bb6aSTetsuo Handa /**
210b69a54eeSKentaro Takeda  * tomoyo_strendswith - Check whether the token ends with the given token.
211b69a54eeSKentaro Takeda  *
212b69a54eeSKentaro Takeda  * @name: The token to check.
213b69a54eeSKentaro Takeda  * @tail: The token to find.
214b69a54eeSKentaro Takeda  *
215b69a54eeSKentaro Takeda  * Returns true if @name ends with @tail, false otherwise.
216b69a54eeSKentaro Takeda  */
217b69a54eeSKentaro Takeda static bool tomoyo_strendswith(const char *name, const char *tail)
218b69a54eeSKentaro Takeda {
219b69a54eeSKentaro Takeda 	int len;
220b69a54eeSKentaro Takeda 
221b69a54eeSKentaro Takeda 	if (!name || !tail)
222b69a54eeSKentaro Takeda 		return false;
223b69a54eeSKentaro Takeda 	len = strlen(name) - strlen(tail);
224b69a54eeSKentaro Takeda 	return len >= 0 && !strcmp(name + len, tail);
225b69a54eeSKentaro Takeda }
226b69a54eeSKentaro Takeda 
227b69a54eeSKentaro Takeda /**
228b69a54eeSKentaro Takeda  * tomoyo_get_path - Get realpath.
229b69a54eeSKentaro Takeda  *
230b69a54eeSKentaro Takeda  * @path: Pointer to "struct path".
231b69a54eeSKentaro Takeda  *
232b69a54eeSKentaro Takeda  * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
233b69a54eeSKentaro Takeda  */
234b69a54eeSKentaro Takeda static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
235b69a54eeSKentaro Takeda {
236b69a54eeSKentaro Takeda 	int error;
2378e2d39a1STetsuo Handa 	struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf),
2384e5d6f7eSTetsuo Handa 							 GFP_NOFS);
239b69a54eeSKentaro Takeda 
240b69a54eeSKentaro Takeda 	if (!buf)
241b69a54eeSKentaro Takeda 		return NULL;
242b69a54eeSKentaro Takeda 	/* Reserve one byte for appending "/". */
243b69a54eeSKentaro Takeda 	error = tomoyo_realpath_from_path2(path, buf->body,
244b69a54eeSKentaro Takeda 					   sizeof(buf->body) - 2);
245b69a54eeSKentaro Takeda 	if (!error) {
246b69a54eeSKentaro Takeda 		buf->head.name = buf->body;
247b69a54eeSKentaro Takeda 		tomoyo_fill_path_info(&buf->head);
248b69a54eeSKentaro Takeda 		return &buf->head;
249b69a54eeSKentaro Takeda 	}
2508e2d39a1STetsuo Handa 	kfree(buf);
251b69a54eeSKentaro Takeda 	return NULL;
252b69a54eeSKentaro Takeda }
253b69a54eeSKentaro Takeda 
2547ef61233STetsuo Handa static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
255b69a54eeSKentaro Takeda 				   const char *filename2,
2567ef61233STetsuo Handa 				   struct tomoyo_domain_info *const domain,
2577ef61233STetsuo Handa 				   const bool is_delete);
2587ef61233STetsuo Handa static int tomoyo_update_path_acl(const u8 type, const char *filename,
2597ef61233STetsuo Handa 				  struct tomoyo_domain_info *const domain,
2607ef61233STetsuo Handa 				  const bool is_delete);
261b69a54eeSKentaro Takeda 
262c3fa109aSTetsuo Handa /*
263c3fa109aSTetsuo Handa  * tomoyo_globally_readable_list is used for holding list of pathnames which
264c3fa109aSTetsuo Handa  * are by default allowed to be open()ed for reading by any process.
265c3fa109aSTetsuo Handa  *
266c3fa109aSTetsuo Handa  * An entry is added by
267c3fa109aSTetsuo Handa  *
268c3fa109aSTetsuo Handa  * # echo 'allow_read /lib/libc-2.5.so' > \
269c3fa109aSTetsuo Handa  *                               /sys/kernel/security/tomoyo/exception_policy
270c3fa109aSTetsuo Handa  *
271c3fa109aSTetsuo Handa  * and is deleted by
272c3fa109aSTetsuo Handa  *
273c3fa109aSTetsuo Handa  * # echo 'delete allow_read /lib/libc-2.5.so' > \
274c3fa109aSTetsuo Handa  *                               /sys/kernel/security/tomoyo/exception_policy
275c3fa109aSTetsuo Handa  *
276c3fa109aSTetsuo Handa  * and all entries are retrieved by
277c3fa109aSTetsuo Handa  *
278c3fa109aSTetsuo Handa  * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy
279c3fa109aSTetsuo Handa  *
280c3fa109aSTetsuo Handa  * In the example above, any process is allowed to
281c3fa109aSTetsuo Handa  * open("/lib/libc-2.5.so", O_RDONLY).
282c3fa109aSTetsuo Handa  * One exception is, if the domain which current process belongs to is marked
283c3fa109aSTetsuo Handa  * as "ignore_global_allow_read", current process can't do so unless explicitly
284c3fa109aSTetsuo Handa  * given "allow_read /lib/libc-2.5.so" to the domain which current process
285c3fa109aSTetsuo Handa  * belongs to.
286c3fa109aSTetsuo Handa  */
287847b173eSTetsuo Handa LIST_HEAD(tomoyo_globally_readable_list);
288b69a54eeSKentaro Takeda 
289b69a54eeSKentaro Takeda /**
290b69a54eeSKentaro Takeda  * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
291b69a54eeSKentaro Takeda  *
292b69a54eeSKentaro Takeda  * @filename:  Filename unconditionally permitted to open() for reading.
293b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
294b69a54eeSKentaro Takeda  *
295b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
296fdb8ebb7STetsuo Handa  *
297fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
298b69a54eeSKentaro Takeda  */
299b69a54eeSKentaro Takeda static int tomoyo_update_globally_readable_entry(const char *filename,
300b69a54eeSKentaro Takeda 						 const bool is_delete)
301b69a54eeSKentaro Takeda {
302b69a54eeSKentaro Takeda 	struct tomoyo_globally_readable_file_entry *ptr;
3039e4b50e9STetsuo Handa 	struct tomoyo_globally_readable_file_entry e = { };
304ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
305b69a54eeSKentaro Takeda 
30617080008STetsuo Handa 	if (!tomoyo_is_correct_path(filename, 1, 0, -1))
307b69a54eeSKentaro Takeda 		return -EINVAL;
3089e4b50e9STetsuo Handa 	e.filename = tomoyo_get_name(filename);
3099e4b50e9STetsuo Handa 	if (!e.filename)
310b69a54eeSKentaro Takeda 		return -ENOMEM;
31129282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
31229282381STetsuo Handa 		goto out;
313fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
3149e4b50e9STetsuo Handa 		if (ptr->filename != e.filename)
315b69a54eeSKentaro Takeda 			continue;
316b69a54eeSKentaro Takeda 		ptr->is_deleted = is_delete;
317b69a54eeSKentaro Takeda 		error = 0;
318ca0b7df3STetsuo Handa 		break;
319b69a54eeSKentaro Takeda 	}
3209e4b50e9STetsuo Handa 	if (!is_delete && error) {
3219e4b50e9STetsuo Handa 		struct tomoyo_globally_readable_file_entry *entry =
3229e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
3239e4b50e9STetsuo Handa 		if (entry) {
3249e4b50e9STetsuo Handa 			list_add_tail_rcu(&entry->list,
3259e4b50e9STetsuo Handa 					  &tomoyo_globally_readable_list);
326b69a54eeSKentaro Takeda 			error = 0;
327ca0b7df3STetsuo Handa 		}
3289e4b50e9STetsuo Handa 	}
329f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
33029282381STetsuo Handa  out:
3319e4b50e9STetsuo Handa 	tomoyo_put_name(e.filename);
332b69a54eeSKentaro Takeda 	return error;
333b69a54eeSKentaro Takeda }
334b69a54eeSKentaro Takeda 
335b69a54eeSKentaro Takeda /**
336b69a54eeSKentaro Takeda  * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
337b69a54eeSKentaro Takeda  *
338b69a54eeSKentaro Takeda  * @filename: The filename to check.
339b69a54eeSKentaro Takeda  *
340b69a54eeSKentaro Takeda  * Returns true if any domain can open @filename for reading, false otherwise.
341fdb8ebb7STetsuo Handa  *
342fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
343b69a54eeSKentaro Takeda  */
344b69a54eeSKentaro Takeda static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
345b69a54eeSKentaro Takeda 					     filename)
346b69a54eeSKentaro Takeda {
347b69a54eeSKentaro Takeda 	struct tomoyo_globally_readable_file_entry *ptr;
348b69a54eeSKentaro Takeda 	bool found = false;
349fdb8ebb7STetsuo Handa 
350fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
351b69a54eeSKentaro Takeda 		if (!ptr->is_deleted &&
352b69a54eeSKentaro Takeda 		    tomoyo_path_matches_pattern(filename, ptr->filename)) {
353b69a54eeSKentaro Takeda 			found = true;
354b69a54eeSKentaro Takeda 			break;
355b69a54eeSKentaro Takeda 		}
356b69a54eeSKentaro Takeda 	}
357b69a54eeSKentaro Takeda 	return found;
358b69a54eeSKentaro Takeda }
359b69a54eeSKentaro Takeda 
360b69a54eeSKentaro Takeda /**
361b69a54eeSKentaro Takeda  * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
362b69a54eeSKentaro Takeda  *
363b69a54eeSKentaro Takeda  * @data:      String to parse.
364b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
365b69a54eeSKentaro Takeda  *
366b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
367fdb8ebb7STetsuo Handa  *
368fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
369b69a54eeSKentaro Takeda  */
370b69a54eeSKentaro Takeda int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
371b69a54eeSKentaro Takeda {
372b69a54eeSKentaro Takeda 	return tomoyo_update_globally_readable_entry(data, is_delete);
373b69a54eeSKentaro Takeda }
374b69a54eeSKentaro Takeda 
375b69a54eeSKentaro Takeda /**
376b69a54eeSKentaro Takeda  * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
377b69a54eeSKentaro Takeda  *
378b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
379b69a54eeSKentaro Takeda  *
380b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
381fdb8ebb7STetsuo Handa  *
382fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
383b69a54eeSKentaro Takeda  */
384b69a54eeSKentaro Takeda bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
385b69a54eeSKentaro Takeda {
386b69a54eeSKentaro Takeda 	struct list_head *pos;
387b69a54eeSKentaro Takeda 	bool done = true;
388b69a54eeSKentaro Takeda 
389b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2,
390b69a54eeSKentaro Takeda 			     &tomoyo_globally_readable_list) {
391b69a54eeSKentaro Takeda 		struct tomoyo_globally_readable_file_entry *ptr;
392b69a54eeSKentaro Takeda 		ptr = list_entry(pos,
393b69a54eeSKentaro Takeda 				 struct tomoyo_globally_readable_file_entry,
394b69a54eeSKentaro Takeda 				 list);
395b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
396b69a54eeSKentaro Takeda 			continue;
3977d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
3987d2948b1STetsuo Handa 					ptr->filename->name);
3997d2948b1STetsuo Handa 		if (!done)
400b69a54eeSKentaro Takeda 			break;
401b69a54eeSKentaro Takeda 	}
402b69a54eeSKentaro Takeda 	return done;
403b69a54eeSKentaro Takeda }
404b69a54eeSKentaro Takeda 
405c3fa109aSTetsuo Handa /* tomoyo_pattern_list is used for holding list of pathnames which are used for
406c3fa109aSTetsuo Handa  * converting pathnames to pathname patterns during learning mode.
407c3fa109aSTetsuo Handa  *
408c3fa109aSTetsuo Handa  * An entry is added by
409c3fa109aSTetsuo Handa  *
410c3fa109aSTetsuo Handa  * # echo 'file_pattern /proc/\$/mounts' > \
411c3fa109aSTetsuo Handa  *                             /sys/kernel/security/tomoyo/exception_policy
412c3fa109aSTetsuo Handa  *
413c3fa109aSTetsuo Handa  * and is deleted by
414c3fa109aSTetsuo Handa  *
415c3fa109aSTetsuo Handa  * # echo 'delete file_pattern /proc/\$/mounts' > \
416c3fa109aSTetsuo Handa  *                             /sys/kernel/security/tomoyo/exception_policy
417c3fa109aSTetsuo Handa  *
418c3fa109aSTetsuo Handa  * and all entries are retrieved by
419c3fa109aSTetsuo Handa  *
420c3fa109aSTetsuo Handa  * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy
421c3fa109aSTetsuo Handa  *
422c3fa109aSTetsuo Handa  * In the example above, if a process which belongs to a domain which is in
423c3fa109aSTetsuo Handa  * learning mode requested open("/proc/1/mounts", O_RDONLY),
424c3fa109aSTetsuo Handa  * "allow_read /proc/\$/mounts" is automatically added to the domain which that
425c3fa109aSTetsuo Handa  * process belongs to.
426c3fa109aSTetsuo Handa  *
427c3fa109aSTetsuo Handa  * It is not a desirable behavior that we have to use /proc/\$/ instead of
428c3fa109aSTetsuo Handa  * /proc/self/ when current process needs to access only current process's
429c3fa109aSTetsuo Handa  * information. As of now, LSM version of TOMOYO is using __d_path() for
430c3fa109aSTetsuo Handa  * calculating pathname. Non LSM version of TOMOYO is using its own function
431c3fa109aSTetsuo Handa  * which pretends as if /proc/self/ is not a symlink; so that we can forbid
432c3fa109aSTetsuo Handa  * current process from accessing other process's information.
433c3fa109aSTetsuo Handa  */
434847b173eSTetsuo Handa LIST_HEAD(tomoyo_pattern_list);
435b69a54eeSKentaro Takeda 
436b69a54eeSKentaro Takeda /**
437b69a54eeSKentaro Takeda  * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
438b69a54eeSKentaro Takeda  *
439b69a54eeSKentaro Takeda  * @pattern:   Pathname pattern.
440b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
441b69a54eeSKentaro Takeda  *
442b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
443fdb8ebb7STetsuo Handa  *
444fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
445b69a54eeSKentaro Takeda  */
446b69a54eeSKentaro Takeda static int tomoyo_update_file_pattern_entry(const char *pattern,
447b69a54eeSKentaro Takeda 					    const bool is_delete)
448b69a54eeSKentaro Takeda {
449b69a54eeSKentaro Takeda 	struct tomoyo_pattern_entry *ptr;
4509e4b50e9STetsuo Handa 	struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) };
451ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
452b69a54eeSKentaro Takeda 
4539e4b50e9STetsuo Handa 	if (!e.pattern)
454ca0b7df3STetsuo Handa 		return error;
4559e4b50e9STetsuo Handa 	if (!e.pattern->is_patterned)
456ca0b7df3STetsuo Handa 		goto out;
45729282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
45829282381STetsuo Handa 		goto out;
459fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
4609e4b50e9STetsuo Handa 		if (e.pattern != ptr->pattern)
461b69a54eeSKentaro Takeda 			continue;
462b69a54eeSKentaro Takeda 		ptr->is_deleted = is_delete;
463b69a54eeSKentaro Takeda 		error = 0;
464ca0b7df3STetsuo Handa 		break;
465b69a54eeSKentaro Takeda 	}
4669e4b50e9STetsuo Handa 	if (!is_delete && error) {
4679e4b50e9STetsuo Handa 		struct tomoyo_pattern_entry *entry =
4689e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
4699e4b50e9STetsuo Handa 		if (entry) {
470ca0b7df3STetsuo Handa 			list_add_tail_rcu(&entry->list, &tomoyo_pattern_list);
471b69a54eeSKentaro Takeda 			error = 0;
472ca0b7df3STetsuo Handa 		}
4739e4b50e9STetsuo Handa 	}
474f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
475ca0b7df3STetsuo Handa  out:
4769e4b50e9STetsuo Handa 	tomoyo_put_name(e.pattern);
477b69a54eeSKentaro Takeda 	return error;
478b69a54eeSKentaro Takeda }
479b69a54eeSKentaro Takeda 
480b69a54eeSKentaro Takeda /**
481b69a54eeSKentaro Takeda  * tomoyo_get_file_pattern - Get patterned pathname.
482b69a54eeSKentaro Takeda  *
483b69a54eeSKentaro Takeda  * @filename: The filename to find patterned pathname.
484b69a54eeSKentaro Takeda  *
485b69a54eeSKentaro Takeda  * Returns pointer to pathname pattern if matched, @filename otherwise.
486fdb8ebb7STetsuo Handa  *
487fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
488b69a54eeSKentaro Takeda  */
4892106ccd9STetsuo Handa const struct tomoyo_path_info *
490b69a54eeSKentaro Takeda tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
491b69a54eeSKentaro Takeda {
492b69a54eeSKentaro Takeda 	struct tomoyo_pattern_entry *ptr;
493b69a54eeSKentaro Takeda 	const struct tomoyo_path_info *pattern = NULL;
494b69a54eeSKentaro Takeda 
495fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
496b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
497b69a54eeSKentaro Takeda 			continue;
498b69a54eeSKentaro Takeda 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
499b69a54eeSKentaro Takeda 			continue;
500b69a54eeSKentaro Takeda 		pattern = ptr->pattern;
501b69a54eeSKentaro Takeda 		if (tomoyo_strendswith(pattern->name, "/\\*")) {
502b69a54eeSKentaro Takeda 			/* Do nothing. Try to find the better match. */
503b69a54eeSKentaro Takeda 		} else {
504b69a54eeSKentaro Takeda 			/* This would be the better match. Use this. */
505b69a54eeSKentaro Takeda 			break;
506b69a54eeSKentaro Takeda 		}
507b69a54eeSKentaro Takeda 	}
508b69a54eeSKentaro Takeda 	if (pattern)
509b69a54eeSKentaro Takeda 		filename = pattern;
510b69a54eeSKentaro Takeda 	return filename;
511b69a54eeSKentaro Takeda }
512b69a54eeSKentaro Takeda 
513b69a54eeSKentaro Takeda /**
514b69a54eeSKentaro Takeda  * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
515b69a54eeSKentaro Takeda  *
516b69a54eeSKentaro Takeda  * @data:      String to parse.
517b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
518b69a54eeSKentaro Takeda  *
519b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
520fdb8ebb7STetsuo Handa  *
521fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
522b69a54eeSKentaro Takeda  */
523b69a54eeSKentaro Takeda int tomoyo_write_pattern_policy(char *data, const bool is_delete)
524b69a54eeSKentaro Takeda {
525b69a54eeSKentaro Takeda 	return tomoyo_update_file_pattern_entry(data, is_delete);
526b69a54eeSKentaro Takeda }
527b69a54eeSKentaro Takeda 
528b69a54eeSKentaro Takeda /**
529b69a54eeSKentaro Takeda  * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
530b69a54eeSKentaro Takeda  *
531b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
532b69a54eeSKentaro Takeda  *
533b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
534fdb8ebb7STetsuo Handa  *
535fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
536b69a54eeSKentaro Takeda  */
537b69a54eeSKentaro Takeda bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
538b69a54eeSKentaro Takeda {
539b69a54eeSKentaro Takeda 	struct list_head *pos;
540b69a54eeSKentaro Takeda 	bool done = true;
541b69a54eeSKentaro Takeda 
542b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
543b69a54eeSKentaro Takeda 		struct tomoyo_pattern_entry *ptr;
544b69a54eeSKentaro Takeda 		ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
545b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
546b69a54eeSKentaro Takeda 			continue;
5477d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
5487d2948b1STetsuo Handa 					"%s\n", ptr->pattern->name);
5497d2948b1STetsuo Handa 		if (!done)
550b69a54eeSKentaro Takeda 			break;
551b69a54eeSKentaro Takeda 	}
552b69a54eeSKentaro Takeda 	return done;
553b69a54eeSKentaro Takeda }
554b69a54eeSKentaro Takeda 
555c3fa109aSTetsuo Handa /*
556c3fa109aSTetsuo Handa  * tomoyo_no_rewrite_list is used for holding list of pathnames which are by
557c3fa109aSTetsuo Handa  * default forbidden to modify already written content of a file.
558c3fa109aSTetsuo Handa  *
559c3fa109aSTetsuo Handa  * An entry is added by
560c3fa109aSTetsuo Handa  *
561c3fa109aSTetsuo Handa  * # echo 'deny_rewrite /var/log/messages' > \
562c3fa109aSTetsuo Handa  *                              /sys/kernel/security/tomoyo/exception_policy
563c3fa109aSTetsuo Handa  *
564c3fa109aSTetsuo Handa  * and is deleted by
565c3fa109aSTetsuo Handa  *
566c3fa109aSTetsuo Handa  * # echo 'delete deny_rewrite /var/log/messages' > \
567c3fa109aSTetsuo Handa  *                              /sys/kernel/security/tomoyo/exception_policy
568c3fa109aSTetsuo Handa  *
569c3fa109aSTetsuo Handa  * and all entries are retrieved by
570c3fa109aSTetsuo Handa  *
571c3fa109aSTetsuo Handa  * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy
572c3fa109aSTetsuo Handa  *
573c3fa109aSTetsuo Handa  * In the example above, if a process requested to rewrite /var/log/messages ,
574c3fa109aSTetsuo Handa  * the process can't rewrite unless the domain which that process belongs to
575c3fa109aSTetsuo Handa  * has "allow_rewrite /var/log/messages" entry.
576c3fa109aSTetsuo Handa  *
577c3fa109aSTetsuo Handa  * It is not a desirable behavior that we have to add "\040(deleted)" suffix
578c3fa109aSTetsuo Handa  * when we want to allow rewriting already unlink()ed file. As of now,
579c3fa109aSTetsuo Handa  * LSM version of TOMOYO is using __d_path() for calculating pathname.
580c3fa109aSTetsuo Handa  * Non LSM version of TOMOYO is using its own function which doesn't append
581c3fa109aSTetsuo Handa  * " (deleted)" suffix if the file is already unlink()ed; so that we don't
582c3fa109aSTetsuo Handa  * need to worry whether the file is already unlink()ed or not.
583c3fa109aSTetsuo Handa  */
584847b173eSTetsuo Handa LIST_HEAD(tomoyo_no_rewrite_list);
585b69a54eeSKentaro Takeda 
586b69a54eeSKentaro Takeda /**
587b69a54eeSKentaro Takeda  * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
588b69a54eeSKentaro Takeda  *
589b69a54eeSKentaro Takeda  * @pattern:   Pathname pattern that are not rewritable by default.
590b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
591b69a54eeSKentaro Takeda  *
592b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
593fdb8ebb7STetsuo Handa  *
594fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
595b69a54eeSKentaro Takeda  */
596b69a54eeSKentaro Takeda static int tomoyo_update_no_rewrite_entry(const char *pattern,
597b69a54eeSKentaro Takeda 					  const bool is_delete)
598b69a54eeSKentaro Takeda {
599ca0b7df3STetsuo Handa 	struct tomoyo_no_rewrite_entry *ptr;
6009e4b50e9STetsuo Handa 	struct tomoyo_no_rewrite_entry e = { };
601ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
602b69a54eeSKentaro Takeda 
60317080008STetsuo Handa 	if (!tomoyo_is_correct_path(pattern, 0, 0, 0))
604b69a54eeSKentaro Takeda 		return -EINVAL;
6059e4b50e9STetsuo Handa 	e.pattern = tomoyo_get_name(pattern);
6069e4b50e9STetsuo Handa 	if (!e.pattern)
607ca0b7df3STetsuo Handa 		return error;
60829282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
60929282381STetsuo Handa 		goto out;
610fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
6119e4b50e9STetsuo Handa 		if (ptr->pattern != e.pattern)
612b69a54eeSKentaro Takeda 			continue;
613b69a54eeSKentaro Takeda 		ptr->is_deleted = is_delete;
614b69a54eeSKentaro Takeda 		error = 0;
615ca0b7df3STetsuo Handa 		break;
616b69a54eeSKentaro Takeda 	}
6179e4b50e9STetsuo Handa 	if (!is_delete && error) {
6189e4b50e9STetsuo Handa 		struct tomoyo_no_rewrite_entry *entry =
6199e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
6209e4b50e9STetsuo Handa 		if (entry) {
6219e4b50e9STetsuo Handa 			list_add_tail_rcu(&entry->list,
6229e4b50e9STetsuo Handa 					  &tomoyo_no_rewrite_list);
623b69a54eeSKentaro Takeda 			error = 0;
624ca0b7df3STetsuo Handa 		}
6259e4b50e9STetsuo Handa 	}
626f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
62729282381STetsuo Handa  out:
6289e4b50e9STetsuo Handa 	tomoyo_put_name(e.pattern);
629b69a54eeSKentaro Takeda 	return error;
630b69a54eeSKentaro Takeda }
631b69a54eeSKentaro Takeda 
632b69a54eeSKentaro Takeda /**
633b69a54eeSKentaro Takeda  * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
634b69a54eeSKentaro Takeda  *
635b69a54eeSKentaro Takeda  * @filename: Filename to check.
636b69a54eeSKentaro Takeda  *
637b69a54eeSKentaro Takeda  * Returns true if @filename is specified by "deny_rewrite" directive,
638b69a54eeSKentaro Takeda  * false otherwise.
639fdb8ebb7STetsuo Handa  *
640fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
641b69a54eeSKentaro Takeda  */
642b69a54eeSKentaro Takeda static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
643b69a54eeSKentaro Takeda {
644b69a54eeSKentaro Takeda 	struct tomoyo_no_rewrite_entry *ptr;
645b69a54eeSKentaro Takeda 	bool found = false;
646b69a54eeSKentaro Takeda 
647fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
648b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
649b69a54eeSKentaro Takeda 			continue;
650b69a54eeSKentaro Takeda 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
651b69a54eeSKentaro Takeda 			continue;
652b69a54eeSKentaro Takeda 		found = true;
653b69a54eeSKentaro Takeda 		break;
654b69a54eeSKentaro Takeda 	}
655b69a54eeSKentaro Takeda 	return found;
656b69a54eeSKentaro Takeda }
657b69a54eeSKentaro Takeda 
658b69a54eeSKentaro Takeda /**
659b69a54eeSKentaro Takeda  * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
660b69a54eeSKentaro Takeda  *
661b69a54eeSKentaro Takeda  * @data:      String to parse.
662b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
663b69a54eeSKentaro Takeda  *
664b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
665fdb8ebb7STetsuo Handa  *
666fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
667b69a54eeSKentaro Takeda  */
668b69a54eeSKentaro Takeda int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
669b69a54eeSKentaro Takeda {
670b69a54eeSKentaro Takeda 	return tomoyo_update_no_rewrite_entry(data, is_delete);
671b69a54eeSKentaro Takeda }
672b69a54eeSKentaro Takeda 
673b69a54eeSKentaro Takeda /**
674b69a54eeSKentaro Takeda  * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
675b69a54eeSKentaro Takeda  *
676b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
677b69a54eeSKentaro Takeda  *
678b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
679fdb8ebb7STetsuo Handa  *
680fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
681b69a54eeSKentaro Takeda  */
682b69a54eeSKentaro Takeda bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
683b69a54eeSKentaro Takeda {
684b69a54eeSKentaro Takeda 	struct list_head *pos;
685b69a54eeSKentaro Takeda 	bool done = true;
686b69a54eeSKentaro Takeda 
687b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
688b69a54eeSKentaro Takeda 		struct tomoyo_no_rewrite_entry *ptr;
689b69a54eeSKentaro Takeda 		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
690b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
691b69a54eeSKentaro Takeda 			continue;
6927d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
6937d2948b1STetsuo Handa 					"%s\n", ptr->pattern->name);
6947d2948b1STetsuo Handa 		if (!done)
695b69a54eeSKentaro Takeda 			break;
696b69a54eeSKentaro Takeda 	}
697b69a54eeSKentaro Takeda 	return done;
698b69a54eeSKentaro Takeda }
699b69a54eeSKentaro Takeda 
700b69a54eeSKentaro Takeda /**
701b69a54eeSKentaro Takeda  * tomoyo_update_file_acl - Update file's read/write/execute ACL.
702b69a54eeSKentaro Takeda  *
703b69a54eeSKentaro Takeda  * @perm:      Permission (between 1 to 7).
704a1f9bb6aSTetsuo Handa  * @filename:  Filename.
705b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
706b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
707b69a54eeSKentaro Takeda  *
708b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
709b69a54eeSKentaro Takeda  *
710b69a54eeSKentaro Takeda  * This is legacy support interface for older policy syntax.
711b69a54eeSKentaro Takeda  * Current policy syntax uses "allow_read/write" instead of "6",
712b69a54eeSKentaro Takeda  * "allow_read" instead of "4", "allow_write" instead of "2",
713b69a54eeSKentaro Takeda  * "allow_execute" instead of "1".
714fdb8ebb7STetsuo Handa  *
715fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
716b69a54eeSKentaro Takeda  */
717a1f9bb6aSTetsuo Handa static int tomoyo_update_file_acl(u8 perm, const char *filename,
718b69a54eeSKentaro Takeda 				  struct tomoyo_domain_info * const domain,
719b69a54eeSKentaro Takeda 				  const bool is_delete)
720b69a54eeSKentaro Takeda {
721b69a54eeSKentaro Takeda 	if (perm > 7 || !perm) {
722b69a54eeSKentaro Takeda 		printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n",
723b69a54eeSKentaro Takeda 		       __func__, perm, filename);
724b69a54eeSKentaro Takeda 		return -EINVAL;
725b69a54eeSKentaro Takeda 	}
726b69a54eeSKentaro Takeda 	if (filename[0] != '@' && tomoyo_strendswith(filename, "/"))
727b69a54eeSKentaro Takeda 		/*
728b69a54eeSKentaro Takeda 		 * Only 'allow_mkdir' and 'allow_rmdir' are valid for
729b69a54eeSKentaro Takeda 		 * directory permissions.
730b69a54eeSKentaro Takeda 		 */
731b69a54eeSKentaro Takeda 		return 0;
732b69a54eeSKentaro Takeda 	if (perm & 4)
7337ef61233STetsuo Handa 		tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain,
7347ef61233STetsuo Handa 				       is_delete);
735b69a54eeSKentaro Takeda 	if (perm & 2)
7367ef61233STetsuo Handa 		tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain,
7377ef61233STetsuo Handa 				       is_delete);
738b69a54eeSKentaro Takeda 	if (perm & 1)
7397ef61233STetsuo Handa 		tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain,
7407ef61233STetsuo Handa 				       is_delete);
741b69a54eeSKentaro Takeda 	return 0;
742b69a54eeSKentaro Takeda }
743b69a54eeSKentaro Takeda 
744b69a54eeSKentaro Takeda /**
745cb0abe6aSTetsuo Handa  * tomoyo_path_acl - Check permission for single path operation.
746b69a54eeSKentaro Takeda  *
747cb0abe6aSTetsuo Handa  * @r:               Pointer to "struct tomoyo_request_info".
748b69a54eeSKentaro Takeda  * @filename:        Filename to check.
749b69a54eeSKentaro Takeda  * @perm:            Permission.
750b69a54eeSKentaro Takeda  * @may_use_pattern: True if patterned ACL is permitted.
751b69a54eeSKentaro Takeda  *
752b69a54eeSKentaro Takeda  * Returns 0 on success, -EPERM otherwise.
753fdb8ebb7STetsuo Handa  *
754fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
755b69a54eeSKentaro Takeda  */
756cb0abe6aSTetsuo Handa static int tomoyo_path_acl(const struct tomoyo_request_info *r,
7577ef61233STetsuo Handa 			   const struct tomoyo_path_info *filename,
7587ef61233STetsuo Handa 			   const u32 perm, const bool may_use_pattern)
759b69a54eeSKentaro Takeda {
760cb0abe6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
761b69a54eeSKentaro Takeda 	struct tomoyo_acl_info *ptr;
762b69a54eeSKentaro Takeda 	int error = -EPERM;
763b69a54eeSKentaro Takeda 
764fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
7657ef61233STetsuo Handa 		struct tomoyo_path_acl *acl;
7667ef61233STetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_ACL)
767b69a54eeSKentaro Takeda 			continue;
7687ef61233STetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_acl, head);
769a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm) ||
770a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_name_union_pattern(filename, &acl->name,
7717762fbffSTetsuo Handa                                                        may_use_pattern))
772b69a54eeSKentaro Takeda 			continue;
773b69a54eeSKentaro Takeda 		error = 0;
774b69a54eeSKentaro Takeda 		break;
775b69a54eeSKentaro Takeda 	}
776b69a54eeSKentaro Takeda 	return error;
777b69a54eeSKentaro Takeda }
778b69a54eeSKentaro Takeda 
779b69a54eeSKentaro Takeda /**
780cb0abe6aSTetsuo Handa  * tomoyo_file_perm - Check permission for opening files.
781b69a54eeSKentaro Takeda  *
782cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
783b69a54eeSKentaro Takeda  * @filename:  Filename to check.
784cb0abe6aSTetsuo Handa  * @mode:      Mode ("read" or "write" or "read/write" or "execute").
785b69a54eeSKentaro Takeda  *
786b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
787fdb8ebb7STetsuo Handa  *
788fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
789b69a54eeSKentaro Takeda  */
790cb0abe6aSTetsuo Handa static int tomoyo_file_perm(struct tomoyo_request_info *r,
791b69a54eeSKentaro Takeda 			    const struct tomoyo_path_info *filename,
792b69a54eeSKentaro Takeda 			    const u8 mode)
793b69a54eeSKentaro Takeda {
794b69a54eeSKentaro Takeda 	const char *msg = "<unknown>";
795b69a54eeSKentaro Takeda 	int error = 0;
796cb0abe6aSTetsuo Handa 	u32 perm = 0;
797b69a54eeSKentaro Takeda 
798b69a54eeSKentaro Takeda 	if (!filename)
799b69a54eeSKentaro Takeda 		return 0;
800cb0abe6aSTetsuo Handa 
801cb0abe6aSTetsuo Handa 	if (mode == 6) {
802cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE);
803cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_READ_WRITE;
804cb0abe6aSTetsuo Handa 	} else if (mode == 4) {
805cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ);
806cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_READ;
807cb0abe6aSTetsuo Handa 	} else if (mode == 2) {
808cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE);
809cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_WRITE;
810cb0abe6aSTetsuo Handa 	} else if (mode == 1) {
811cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE);
812cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_EXECUTE;
813cb0abe6aSTetsuo Handa 	} else
814cb0abe6aSTetsuo Handa 		BUG();
815cb0abe6aSTetsuo Handa 	error = tomoyo_path_acl(r, filename, perm, mode != 1);
816cb0abe6aSTetsuo Handa 	if (error && mode == 4 && !r->domain->ignore_global_allow_read
817b69a54eeSKentaro Takeda 	    && tomoyo_is_globally_readable_file(filename))
818b69a54eeSKentaro Takeda 		error = 0;
819b69a54eeSKentaro Takeda 	if (!error)
820b69a54eeSKentaro Takeda 		return 0;
821cb0abe6aSTetsuo Handa 	tomoyo_warn_log(r, "%s %s", msg, filename->name);
822cb0abe6aSTetsuo Handa 	if (r->mode == TOMOYO_CONFIG_ENFORCING)
823b69a54eeSKentaro Takeda 		return error;
824cb0abe6aSTetsuo Handa 	if (tomoyo_domain_quota_is_ok(r)) {
825b69a54eeSKentaro Takeda 		/* Don't use patterns for execute permission. */
826cb0abe6aSTetsuo Handa 		const struct tomoyo_path_info *patterned_file = (mode != 1) ?
827b69a54eeSKentaro Takeda 			tomoyo_get_file_pattern(filename) : filename;
828a1f9bb6aSTetsuo Handa 		tomoyo_update_file_acl(mode, patterned_file->name, r->domain,
829a1f9bb6aSTetsuo Handa 				       false);
830b69a54eeSKentaro Takeda 	}
831b69a54eeSKentaro Takeda 	return 0;
832b69a54eeSKentaro Takeda }
833b69a54eeSKentaro Takeda 
834b69a54eeSKentaro Takeda /**
8357ef61233STetsuo Handa  * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
836b69a54eeSKentaro Takeda  *
837b69a54eeSKentaro Takeda  * @type:      Type of operation.
838b69a54eeSKentaro Takeda  * @filename:  Filename.
839b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
840b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
841b69a54eeSKentaro Takeda  *
842b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
843fdb8ebb7STetsuo Handa  *
844fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
845b69a54eeSKentaro Takeda  */
8467ef61233STetsuo Handa static int tomoyo_update_path_acl(const u8 type, const char *filename,
8477ef61233STetsuo Handa 				  struct tomoyo_domain_info *const domain,
8487ef61233STetsuo Handa 				  const bool is_delete)
849b69a54eeSKentaro Takeda {
850a1f9bb6aSTetsuo Handa 	static const u16 tomoyo_rw_mask =
8517ef61233STetsuo Handa 		(1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE);
852a1f9bb6aSTetsuo Handa 	const u16 perm = 1 << type;
8539e4b50e9STetsuo Handa 	struct tomoyo_acl_info *ptr;
8549e4b50e9STetsuo Handa 	struct tomoyo_path_acl e = {
8559e4b50e9STetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_ACL,
8569e4b50e9STetsuo Handa 		.perm = perm
8579e4b50e9STetsuo Handa 	};
8589e4b50e9STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
859b69a54eeSKentaro Takeda 
8609e4b50e9STetsuo Handa 	if (type == TOMOYO_TYPE_READ_WRITE)
8619e4b50e9STetsuo Handa 		e.perm |= tomoyo_rw_mask;
862b69a54eeSKentaro Takeda 	if (!domain)
863b69a54eeSKentaro Takeda 		return -EINVAL;
8647762fbffSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name))
865b69a54eeSKentaro Takeda 		return -EINVAL;
86629282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
86729282381STetsuo Handa 		goto out;
868fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
8697ef61233STetsuo Handa 		struct tomoyo_path_acl *acl =
8707ef61233STetsuo Handa 			container_of(ptr, struct tomoyo_path_acl, head);
8717762fbffSTetsuo Handa 		if (!tomoyo_is_same_path_acl(acl, &e))
872b69a54eeSKentaro Takeda 			continue;
873ca0b7df3STetsuo Handa 		if (is_delete) {
874ca0b7df3STetsuo Handa 			acl->perm &= ~perm;
8759e4b50e9STetsuo Handa 			if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask)
8767ef61233STetsuo Handa 				acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
8777ef61233STetsuo Handa 			else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)))
8789e4b50e9STetsuo Handa 				acl->perm &= ~tomoyo_rw_mask;
879ca0b7df3STetsuo Handa 		} else {
880b69a54eeSKentaro Takeda 			acl->perm |= perm;
8819e4b50e9STetsuo Handa 			if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask)
8827ef61233STetsuo Handa 				acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE;
8837ef61233STetsuo Handa 			else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))
8849e4b50e9STetsuo Handa 				acl->perm |= tomoyo_rw_mask;
885b69a54eeSKentaro Takeda 		}
886b69a54eeSKentaro Takeda 		error = 0;
887b69a54eeSKentaro Takeda 		break;
888b69a54eeSKentaro Takeda 	}
8899e4b50e9STetsuo Handa 	if (!is_delete && error) {
8909e4b50e9STetsuo Handa 		struct tomoyo_path_acl *entry =
8919e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
8929e4b50e9STetsuo Handa 		if (entry) {
8939e4b50e9STetsuo Handa 			list_add_tail_rcu(&entry->head.list,
8949e4b50e9STetsuo Handa 					  &domain->acl_info_list);
895ca0b7df3STetsuo Handa 			error = 0;
896ca0b7df3STetsuo Handa 		}
8979e4b50e9STetsuo Handa 	}
898f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
89929282381STetsuo Handa  out:
9007762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name);
901b69a54eeSKentaro Takeda 	return error;
902b69a54eeSKentaro Takeda }
903b69a54eeSKentaro Takeda 
904b69a54eeSKentaro Takeda /**
905a1f9bb6aSTetsuo Handa  * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list.
906a1f9bb6aSTetsuo Handa  *
907a1f9bb6aSTetsuo Handa  * @type:      Type of operation.
908a1f9bb6aSTetsuo Handa  * @filename:  Filename.
909a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
910a1f9bb6aSTetsuo Handa  * @major:     Device major number.
911a1f9bb6aSTetsuo Handa  * @minor:     Device minor number.
912a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
913a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
914a1f9bb6aSTetsuo Handa  *
915a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
916a1f9bb6aSTetsuo Handa  */
917a1f9bb6aSTetsuo Handa static inline int tomoyo_update_path_number3_acl(const u8 type,
918a1f9bb6aSTetsuo Handa 						 const char *filename,
919a1f9bb6aSTetsuo Handa 						 char *mode,
920a1f9bb6aSTetsuo Handa 						 char *major, char *minor,
921a1f9bb6aSTetsuo Handa 						 struct tomoyo_domain_info *
922a1f9bb6aSTetsuo Handa 						 const domain,
923a1f9bb6aSTetsuo Handa 						 const bool is_delete)
924a1f9bb6aSTetsuo Handa {
925a1f9bb6aSTetsuo Handa 	const u8 perm = 1 << type;
926a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
927a1f9bb6aSTetsuo Handa 	struct tomoyo_path_number3_acl e = {
928a1f9bb6aSTetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL,
929a1f9bb6aSTetsuo Handa 		.perm = perm
930a1f9bb6aSTetsuo Handa 	};
931a1f9bb6aSTetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
932a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name) ||
933a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(mode, &e.mode) ||
934a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(major, &e.major) ||
935a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(minor, &e.minor))
936a1f9bb6aSTetsuo Handa 		goto out;
937a1f9bb6aSTetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
938a1f9bb6aSTetsuo Handa 		goto out;
939a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
940a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number3_acl *acl =
941a1f9bb6aSTetsuo Handa 			container_of(ptr, struct tomoyo_path_number3_acl, head);
942a1f9bb6aSTetsuo Handa 		if (!tomoyo_is_same_path_number3_acl(acl, &e))
943a1f9bb6aSTetsuo Handa 			continue;
944a1f9bb6aSTetsuo Handa 		if (is_delete)
945a1f9bb6aSTetsuo Handa 			acl->perm &= ~perm;
946a1f9bb6aSTetsuo Handa 		else
947a1f9bb6aSTetsuo Handa 			acl->perm |= perm;
948a1f9bb6aSTetsuo Handa 		error = 0;
949a1f9bb6aSTetsuo Handa 		break;
950a1f9bb6aSTetsuo Handa 	}
951a1f9bb6aSTetsuo Handa 	if (!is_delete && error) {
952a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number3_acl *entry =
953a1f9bb6aSTetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
954a1f9bb6aSTetsuo Handa 		if (entry) {
955a1f9bb6aSTetsuo Handa 			list_add_tail_rcu(&entry->head.list,
956a1f9bb6aSTetsuo Handa 					  &domain->acl_info_list);
957a1f9bb6aSTetsuo Handa 			error = 0;
958a1f9bb6aSTetsuo Handa 		}
959a1f9bb6aSTetsuo Handa 	}
960a1f9bb6aSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
961a1f9bb6aSTetsuo Handa  out:
962a1f9bb6aSTetsuo Handa 	tomoyo_put_name_union(&e.name);
963a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.mode);
964a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.major);
965a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.minor);
966a1f9bb6aSTetsuo Handa 	return error;
967a1f9bb6aSTetsuo Handa }
968a1f9bb6aSTetsuo Handa 
969a1f9bb6aSTetsuo Handa /**
9707ef61233STetsuo Handa  * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
971b69a54eeSKentaro Takeda  *
972b69a54eeSKentaro Takeda  * @type:      Type of operation.
973b69a54eeSKentaro Takeda  * @filename1: First filename.
974b69a54eeSKentaro Takeda  * @filename2: Second filename.
975b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
976b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
977b69a54eeSKentaro Takeda  *
978b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
979fdb8ebb7STetsuo Handa  *
980fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
981b69a54eeSKentaro Takeda  */
9827ef61233STetsuo Handa static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
983b69a54eeSKentaro Takeda 				   const char *filename2,
9847ef61233STetsuo Handa 				   struct tomoyo_domain_info *const domain,
9857ef61233STetsuo Handa 				   const bool is_delete)
986b69a54eeSKentaro Takeda {
987b69a54eeSKentaro Takeda 	const u8 perm = 1 << type;
9889e4b50e9STetsuo Handa 	struct tomoyo_path2_acl e = {
9899e4b50e9STetsuo Handa 		.head.type = TOMOYO_TYPE_PATH2_ACL,
9909e4b50e9STetsuo Handa 		.perm = perm
9919e4b50e9STetsuo Handa 	};
9929e4b50e9STetsuo Handa 	struct tomoyo_acl_info *ptr;
9939e4b50e9STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
994b69a54eeSKentaro Takeda 
995b69a54eeSKentaro Takeda 	if (!domain)
996b69a54eeSKentaro Takeda 		return -EINVAL;
9977762fbffSTetsuo Handa 	if (!tomoyo_parse_name_union(filename1, &e.name1) ||
9987762fbffSTetsuo Handa 	    !tomoyo_parse_name_union(filename2, &e.name2))
999ca0b7df3STetsuo Handa 		goto out;
100029282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
100129282381STetsuo Handa 		goto out;
1002ca0b7df3STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
10037ef61233STetsuo Handa 		struct tomoyo_path2_acl *acl =
10047ef61233STetsuo Handa 			container_of(ptr, struct tomoyo_path2_acl, head);
10057762fbffSTetsuo Handa 		if (!tomoyo_is_same_path2_acl(acl, &e))
1006ca0b7df3STetsuo Handa 			continue;
1007b69a54eeSKentaro Takeda 		if (is_delete)
1008b69a54eeSKentaro Takeda 			acl->perm &= ~perm;
1009ca0b7df3STetsuo Handa 		else
1010ca0b7df3STetsuo Handa 			acl->perm |= perm;
1011b69a54eeSKentaro Takeda 		error = 0;
1012b69a54eeSKentaro Takeda 		break;
1013b69a54eeSKentaro Takeda 	}
10149e4b50e9STetsuo Handa 	if (!is_delete && error) {
10159e4b50e9STetsuo Handa 		struct tomoyo_path2_acl *entry =
10169e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
10179e4b50e9STetsuo Handa 		if (entry) {
10189e4b50e9STetsuo Handa 			list_add_tail_rcu(&entry->head.list,
10199e4b50e9STetsuo Handa 					  &domain->acl_info_list);
1020ca0b7df3STetsuo Handa 			error = 0;
1021ca0b7df3STetsuo Handa 		}
10229e4b50e9STetsuo Handa 	}
1023f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
1024ca0b7df3STetsuo Handa  out:
10257762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name1);
10267762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name2);
1027b69a54eeSKentaro Takeda 	return error;
1028b69a54eeSKentaro Takeda }
1029b69a54eeSKentaro Takeda 
1030b69a54eeSKentaro Takeda /**
1031a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_acl - Check permission for path/number/number/number operation.
1032a1f9bb6aSTetsuo Handa  *
1033a1f9bb6aSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
1034a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1035a1f9bb6aSTetsuo Handa  * @perm:     Permission.
1036a1f9bb6aSTetsuo Handa  * @mode:     Create mode.
1037a1f9bb6aSTetsuo Handa  * @major:    Device major number.
1038a1f9bb6aSTetsuo Handa  * @minor:    Device minor number.
1039a1f9bb6aSTetsuo Handa  *
1040a1f9bb6aSTetsuo Handa  * Returns 0 on success, -EPERM otherwise.
1041a1f9bb6aSTetsuo Handa  *
1042a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1043a1f9bb6aSTetsuo Handa  */
1044a1f9bb6aSTetsuo Handa static int tomoyo_path_number3_acl(struct tomoyo_request_info *r,
1045a1f9bb6aSTetsuo Handa 				   const struct tomoyo_path_info *filename,
1046a1f9bb6aSTetsuo Handa 				   const u16 perm, const unsigned int mode,
1047a1f9bb6aSTetsuo Handa 				   const unsigned int major,
1048a1f9bb6aSTetsuo Handa 				   const unsigned int minor)
1049a1f9bb6aSTetsuo Handa {
1050a1f9bb6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
1051a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
1052a1f9bb6aSTetsuo Handa 	int error = -EPERM;
1053a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1054a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number3_acl *acl;
1055a1f9bb6aSTetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL)
1056a1f9bb6aSTetsuo Handa 			continue;
1057a1f9bb6aSTetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_number3_acl, head);
1058a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(mode, &acl->mode))
1059a1f9bb6aSTetsuo Handa 			continue;
1060a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(major, &acl->major))
1061a1f9bb6aSTetsuo Handa 			continue;
1062a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(minor, &acl->minor))
1063a1f9bb6aSTetsuo Handa 			continue;
1064a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm))
1065a1f9bb6aSTetsuo Handa 			continue;
1066a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_name_union(filename, &acl->name))
1067a1f9bb6aSTetsuo Handa 			continue;
1068a1f9bb6aSTetsuo Handa 		error = 0;
1069a1f9bb6aSTetsuo Handa 		break;
1070a1f9bb6aSTetsuo Handa 	}
1071a1f9bb6aSTetsuo Handa 	return error;
1072a1f9bb6aSTetsuo Handa }
1073a1f9bb6aSTetsuo Handa 
1074a1f9bb6aSTetsuo Handa /**
10757ef61233STetsuo Handa  * tomoyo_path2_acl - Check permission for double path operation.
1076b69a54eeSKentaro Takeda  *
1077cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1078b69a54eeSKentaro Takeda  * @type:      Type of operation.
1079b69a54eeSKentaro Takeda  * @filename1: First filename to check.
1080b69a54eeSKentaro Takeda  * @filename2: Second filename to check.
1081b69a54eeSKentaro Takeda  *
1082b69a54eeSKentaro Takeda  * Returns 0 on success, -EPERM otherwise.
1083fdb8ebb7STetsuo Handa  *
1084fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1085b69a54eeSKentaro Takeda  */
1086cb0abe6aSTetsuo Handa static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type,
10877ef61233STetsuo Handa 			    const struct tomoyo_path_info *filename1,
10887ef61233STetsuo Handa 			    const struct tomoyo_path_info *filename2)
1089b69a54eeSKentaro Takeda {
1090cb0abe6aSTetsuo Handa 	const struct tomoyo_domain_info *domain = r->domain;
1091b69a54eeSKentaro Takeda 	struct tomoyo_acl_info *ptr;
1092b69a54eeSKentaro Takeda 	const u8 perm = 1 << type;
1093b69a54eeSKentaro Takeda 	int error = -EPERM;
1094b69a54eeSKentaro Takeda 
1095fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
10967ef61233STetsuo Handa 		struct tomoyo_path2_acl *acl;
10977ef61233STetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
1098b69a54eeSKentaro Takeda 			continue;
10997ef61233STetsuo Handa 		acl = container_of(ptr, struct tomoyo_path2_acl, head);
1100b69a54eeSKentaro Takeda 		if (!(acl->perm & perm))
1101b69a54eeSKentaro Takeda 			continue;
11027762fbffSTetsuo Handa 		if (!tomoyo_compare_name_union(filename1, &acl->name1))
1103b69a54eeSKentaro Takeda 			continue;
11047762fbffSTetsuo Handa 		if (!tomoyo_compare_name_union(filename2, &acl->name2))
1105b69a54eeSKentaro Takeda 			continue;
1106b69a54eeSKentaro Takeda 		error = 0;
1107b69a54eeSKentaro Takeda 		break;
1108b69a54eeSKentaro Takeda 	}
1109b69a54eeSKentaro Takeda 	return error;
1110b69a54eeSKentaro Takeda }
1111b69a54eeSKentaro Takeda 
1112b69a54eeSKentaro Takeda /**
1113cb0abe6aSTetsuo Handa  * tomoyo_path_permission - Check permission for single path operation.
1114b69a54eeSKentaro Takeda  *
1115cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1116b69a54eeSKentaro Takeda  * @operation: Type of operation.
1117b69a54eeSKentaro Takeda  * @filename:  Filename to check.
1118b69a54eeSKentaro Takeda  *
1119b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1120fdb8ebb7STetsuo Handa  *
1121fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1122b69a54eeSKentaro Takeda  */
1123cb0abe6aSTetsuo Handa static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
1124cb0abe6aSTetsuo Handa 				  const struct tomoyo_path_info *filename)
1125b69a54eeSKentaro Takeda {
1126b69a54eeSKentaro Takeda 	int error;
1127b69a54eeSKentaro Takeda 
1128b69a54eeSKentaro Takeda  next:
1129cb0abe6aSTetsuo Handa 	error = tomoyo_path_acl(r, filename, 1 << operation, 1);
1130b69a54eeSKentaro Takeda 	if (!error)
1131b69a54eeSKentaro Takeda 		goto ok;
1132cb0abe6aSTetsuo Handa 	tomoyo_warn_log(r, "%s %s", tomoyo_path2keyword(operation),
1133cb0abe6aSTetsuo Handa 			filename->name);
1134cb0abe6aSTetsuo Handa 	if (tomoyo_domain_quota_is_ok(r)) {
1135b69a54eeSKentaro Takeda 		const char *name = tomoyo_get_file_pattern(filename)->name;
1136cb0abe6aSTetsuo Handa 		tomoyo_update_path_acl(operation, name, r->domain, false);
1137b69a54eeSKentaro Takeda 	}
1138cb0abe6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1139b69a54eeSKentaro Takeda 		error = 0;
1140b69a54eeSKentaro Takeda  ok:
1141b69a54eeSKentaro Takeda 	/*
1142b69a54eeSKentaro Takeda 	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
1143b69a54eeSKentaro Takeda 	 * we need to check "allow_rewrite" permission if the filename is
1144b69a54eeSKentaro Takeda 	 * specified by "deny_rewrite" keyword.
1145b69a54eeSKentaro Takeda 	 */
11467ef61233STetsuo Handa 	if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
1147b69a54eeSKentaro Takeda 	    tomoyo_is_no_rewrite_file(filename)) {
11487ef61233STetsuo Handa 		operation = TOMOYO_TYPE_REWRITE;
1149b69a54eeSKentaro Takeda 		goto next;
1150b69a54eeSKentaro Takeda 	}
1151b69a54eeSKentaro Takeda 	return error;
1152b69a54eeSKentaro Takeda }
1153b69a54eeSKentaro Takeda 
1154b69a54eeSKentaro Takeda /**
1155a1f9bb6aSTetsuo Handa  * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation.
1156a1f9bb6aSTetsuo Handa  *
1157a1f9bb6aSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
1158a1f9bb6aSTetsuo Handa  * @type:     Operation.
1159a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1160a1f9bb6aSTetsuo Handa  * @number:   Number.
1161a1f9bb6aSTetsuo Handa  *
1162a1f9bb6aSTetsuo Handa  * Returns 0 on success, -EPERM otherwise.
1163a1f9bb6aSTetsuo Handa  *
1164a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1165a1f9bb6aSTetsuo Handa  */
1166a1f9bb6aSTetsuo Handa static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type,
1167a1f9bb6aSTetsuo Handa 				  const struct tomoyo_path_info *filename,
1168a1f9bb6aSTetsuo Handa 				  const unsigned long number)
1169a1f9bb6aSTetsuo Handa {
1170a1f9bb6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
1171a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
1172a1f9bb6aSTetsuo Handa 	const u8 perm = 1 << type;
1173a1f9bb6aSTetsuo Handa 	int error = -EPERM;
1174a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1175a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number_acl *acl;
1176a1f9bb6aSTetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL)
1177a1f9bb6aSTetsuo Handa 			continue;
1178a1f9bb6aSTetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_number_acl,
1179a1f9bb6aSTetsuo Handa 				   head);
1180a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm) ||
1181a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_number_union(number, &acl->number) ||
1182a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_name_union(filename, &acl->name))
1183a1f9bb6aSTetsuo Handa 			continue;
1184a1f9bb6aSTetsuo Handa 		error = 0;
1185a1f9bb6aSTetsuo Handa 		break;
1186a1f9bb6aSTetsuo Handa 	}
1187a1f9bb6aSTetsuo Handa 	return error;
1188a1f9bb6aSTetsuo Handa }
1189a1f9bb6aSTetsuo Handa 
1190a1f9bb6aSTetsuo Handa /**
1191a1f9bb6aSTetsuo Handa  * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
1192a1f9bb6aSTetsuo Handa  *
1193a1f9bb6aSTetsuo Handa  * @type:      Type of operation.
1194a1f9bb6aSTetsuo Handa  * @filename:  Filename.
1195a1f9bb6aSTetsuo Handa  * @number:    Number.
1196a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
1197a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
1198a1f9bb6aSTetsuo Handa  *
1199a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1200a1f9bb6aSTetsuo Handa  */
1201a1f9bb6aSTetsuo Handa static inline int tomoyo_update_path_number_acl(const u8 type,
1202a1f9bb6aSTetsuo Handa 						const char *filename,
1203a1f9bb6aSTetsuo Handa 						char *number,
1204a1f9bb6aSTetsuo Handa 						struct tomoyo_domain_info *
1205a1f9bb6aSTetsuo Handa 						const domain,
1206a1f9bb6aSTetsuo Handa 						const bool is_delete)
1207a1f9bb6aSTetsuo Handa {
1208a1f9bb6aSTetsuo Handa 	const u8 perm = 1 << type;
1209a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
1210a1f9bb6aSTetsuo Handa 	struct tomoyo_path_number_acl e = {
1211a1f9bb6aSTetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
1212a1f9bb6aSTetsuo Handa 		.perm = perm
1213a1f9bb6aSTetsuo Handa 	};
1214a1f9bb6aSTetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
1215a1f9bb6aSTetsuo Handa 	if (!domain)
1216a1f9bb6aSTetsuo Handa 		return -EINVAL;
1217a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name))
1218a1f9bb6aSTetsuo Handa 		return -EINVAL;
1219a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_number_union(number, &e.number))
1220a1f9bb6aSTetsuo Handa 		goto out;
1221a1f9bb6aSTetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
1222a1f9bb6aSTetsuo Handa 		goto out;
1223a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1224a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number_acl *acl =
1225a1f9bb6aSTetsuo Handa 			container_of(ptr, struct tomoyo_path_number_acl, head);
1226a1f9bb6aSTetsuo Handa 		if (!tomoyo_is_same_path_number_acl(acl, &e))
1227a1f9bb6aSTetsuo Handa 			continue;
1228a1f9bb6aSTetsuo Handa 		if (is_delete)
1229a1f9bb6aSTetsuo Handa 			acl->perm &= ~perm;
1230a1f9bb6aSTetsuo Handa 		else
1231a1f9bb6aSTetsuo Handa 			acl->perm |= perm;
1232a1f9bb6aSTetsuo Handa 		error = 0;
1233a1f9bb6aSTetsuo Handa 		break;
1234a1f9bb6aSTetsuo Handa 	}
1235a1f9bb6aSTetsuo Handa 	if (!is_delete && error) {
1236a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number_acl *entry =
1237a1f9bb6aSTetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
1238a1f9bb6aSTetsuo Handa 		if (entry) {
1239a1f9bb6aSTetsuo Handa 			list_add_tail_rcu(&entry->head.list,
1240a1f9bb6aSTetsuo Handa 					  &domain->acl_info_list);
1241a1f9bb6aSTetsuo Handa 			error = 0;
1242a1f9bb6aSTetsuo Handa 		}
1243a1f9bb6aSTetsuo Handa 	}
1244a1f9bb6aSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
1245a1f9bb6aSTetsuo Handa  out:
1246a1f9bb6aSTetsuo Handa 	tomoyo_put_name_union(&e.name);
1247a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.number);
1248a1f9bb6aSTetsuo Handa 	return error;
1249a1f9bb6aSTetsuo Handa }
1250a1f9bb6aSTetsuo Handa 
1251a1f9bb6aSTetsuo Handa /**
1252a1f9bb6aSTetsuo Handa  * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
1253a1f9bb6aSTetsuo Handa  *
1254a1f9bb6aSTetsuo Handa  * @r:        Pointer to "strct tomoyo_request_info".
1255a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1256a1f9bb6aSTetsuo Handa  * @number:   Number.
1257a1f9bb6aSTetsuo Handa  *
1258a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1259a1f9bb6aSTetsuo Handa  *
1260a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1261a1f9bb6aSTetsuo Handa  */
1262a1f9bb6aSTetsuo Handa static int tomoyo_path_number_perm2(struct tomoyo_request_info *r,
1263a1f9bb6aSTetsuo Handa 				    const u8 type,
1264a1f9bb6aSTetsuo Handa 				    const struct tomoyo_path_info *filename,
1265a1f9bb6aSTetsuo Handa 				    const unsigned long number)
1266a1f9bb6aSTetsuo Handa {
1267a1f9bb6aSTetsuo Handa 	char buffer[64];
1268a1f9bb6aSTetsuo Handa 	int error;
1269a1f9bb6aSTetsuo Handa 	u8 radix;
1270a1f9bb6aSTetsuo Handa 
1271a1f9bb6aSTetsuo Handa 	if (!filename)
1272a1f9bb6aSTetsuo Handa 		return 0;
1273a1f9bb6aSTetsuo Handa 	switch (type) {
1274a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_CREATE:
1275a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKDIR:
1276a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKFIFO:
1277a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKSOCK:
1278a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_CHMOD:
1279a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_OCTAL;
1280a1f9bb6aSTetsuo Handa 		break;
1281a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_IOCTL:
1282a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_HEXADECIMAL;
1283a1f9bb6aSTetsuo Handa 		break;
1284a1f9bb6aSTetsuo Handa 	default:
1285a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_DECIMAL;
1286a1f9bb6aSTetsuo Handa 		break;
1287a1f9bb6aSTetsuo Handa 	}
1288a1f9bb6aSTetsuo Handa 	tomoyo_print_ulong(buffer, sizeof(buffer), number, radix);
1289a1f9bb6aSTetsuo Handa 	error = tomoyo_path_number_acl(r, type, filename, number);
1290a1f9bb6aSTetsuo Handa 	if (!error)
1291a1f9bb6aSTetsuo Handa 		return 0;
1292a1f9bb6aSTetsuo Handa 	tomoyo_warn_log(r, "%s %s %s", tomoyo_path_number2keyword(type),
1293a1f9bb6aSTetsuo Handa 			filename->name, buffer);
1294a1f9bb6aSTetsuo Handa 	if (tomoyo_domain_quota_is_ok(r))
1295a1f9bb6aSTetsuo Handa 		tomoyo_update_path_number_acl(type,
1296a1f9bb6aSTetsuo Handa 					      tomoyo_get_file_pattern(filename)
1297a1f9bb6aSTetsuo Handa 					      ->name, buffer, r->domain, false);
1298a1f9bb6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1299a1f9bb6aSTetsuo Handa 		error = 0;
1300a1f9bb6aSTetsuo Handa 	return error;
1301a1f9bb6aSTetsuo Handa }
1302a1f9bb6aSTetsuo Handa 
1303a1f9bb6aSTetsuo Handa /**
1304a1f9bb6aSTetsuo Handa  * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
1305a1f9bb6aSTetsuo Handa  *
1306a1f9bb6aSTetsuo Handa  * @type:   Type of operation.
1307a1f9bb6aSTetsuo Handa  * @path:   Pointer to "struct path".
1308a1f9bb6aSTetsuo Handa  * @number: Number.
1309a1f9bb6aSTetsuo Handa  *
1310a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1311a1f9bb6aSTetsuo Handa  */
1312a1f9bb6aSTetsuo Handa int tomoyo_path_number_perm(const u8 type, struct path *path,
1313a1f9bb6aSTetsuo Handa 			    unsigned long number)
1314a1f9bb6aSTetsuo Handa {
1315a1f9bb6aSTetsuo Handa 	struct tomoyo_request_info r;
1316a1f9bb6aSTetsuo Handa 	int error = -ENOMEM;
1317a1f9bb6aSTetsuo Handa 	struct tomoyo_path_info *buf;
1318a1f9bb6aSTetsuo Handa 	int idx;
1319a1f9bb6aSTetsuo Handa 
1320a1f9bb6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
1321a1f9bb6aSTetsuo Handa 	    !path->mnt || !path->dentry)
1322a1f9bb6aSTetsuo Handa 		return 0;
1323a1f9bb6aSTetsuo Handa 	idx = tomoyo_read_lock();
1324a1f9bb6aSTetsuo Handa 	buf = tomoyo_get_path(path);
1325a1f9bb6aSTetsuo Handa 	if (!buf)
1326a1f9bb6aSTetsuo Handa 		goto out;
1327a1f9bb6aSTetsuo Handa 	if (type == TOMOYO_TYPE_MKDIR && !buf->is_dir) {
1328a1f9bb6aSTetsuo Handa 		/*
1329a1f9bb6aSTetsuo Handa 		 * tomoyo_get_path() reserves space for appending "/."
1330a1f9bb6aSTetsuo Handa 		 */
1331a1f9bb6aSTetsuo Handa 		strcat((char *) buf->name, "/");
1332a1f9bb6aSTetsuo Handa 		tomoyo_fill_path_info(buf);
1333a1f9bb6aSTetsuo Handa 	}
1334a1f9bb6aSTetsuo Handa 	error = tomoyo_path_number_perm2(&r, type, buf, number);
1335a1f9bb6aSTetsuo Handa  out:
1336a1f9bb6aSTetsuo Handa 	kfree(buf);
1337a1f9bb6aSTetsuo Handa 	tomoyo_read_unlock(idx);
1338a1f9bb6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1339a1f9bb6aSTetsuo Handa 		error = 0;
1340a1f9bb6aSTetsuo Handa 	return error;
1341a1f9bb6aSTetsuo Handa }
1342a1f9bb6aSTetsuo Handa 
1343a1f9bb6aSTetsuo Handa /**
1344b69a54eeSKentaro Takeda  * tomoyo_check_exec_perm - Check permission for "execute".
1345b69a54eeSKentaro Takeda  *
1346b69a54eeSKentaro Takeda  * @domain:   Pointer to "struct tomoyo_domain_info".
1347b69a54eeSKentaro Takeda  * @filename: Check permission for "execute".
1348b69a54eeSKentaro Takeda  *
1349b69a54eeSKentaro Takeda  * Returns 0 on success, negativevalue otherwise.
1350fdb8ebb7STetsuo Handa  *
1351fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1352b69a54eeSKentaro Takeda  */
1353b69a54eeSKentaro Takeda int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
1354bcb86975STetsuo Handa 			   const struct tomoyo_path_info *filename)
1355b69a54eeSKentaro Takeda {
1356cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1357b69a54eeSKentaro Takeda 
1358cb0abe6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED)
1359b69a54eeSKentaro Takeda 		return 0;
1360cb0abe6aSTetsuo Handa 	return tomoyo_file_perm(&r, filename, 1);
1361b69a54eeSKentaro Takeda }
1362b69a54eeSKentaro Takeda 
1363b69a54eeSKentaro Takeda /**
1364b69a54eeSKentaro Takeda  * tomoyo_check_open_permission - Check permission for "read" and "write".
1365b69a54eeSKentaro Takeda  *
1366b69a54eeSKentaro Takeda  * @domain: Pointer to "struct tomoyo_domain_info".
1367b69a54eeSKentaro Takeda  * @path:   Pointer to "struct path".
1368b69a54eeSKentaro Takeda  * @flag:   Flags for open().
1369b69a54eeSKentaro Takeda  *
1370b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1371b69a54eeSKentaro Takeda  */
1372b69a54eeSKentaro Takeda int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
1373b69a54eeSKentaro Takeda 				 struct path *path, const int flag)
1374b69a54eeSKentaro Takeda {
1375b69a54eeSKentaro Takeda 	const u8 acc_mode = ACC_MODE(flag);
1376b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1377b69a54eeSKentaro Takeda 	struct tomoyo_path_info *buf;
1378cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1379fdb8ebb7STetsuo Handa 	int idx;
1380b69a54eeSKentaro Takeda 
1381cb0abe6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, domain) == TOMOYO_CONFIG_DISABLED ||
1382cb0abe6aSTetsuo Handa 	    !path->mnt)
1383b69a54eeSKentaro Takeda 		return 0;
1384b69a54eeSKentaro Takeda 	if (acc_mode == 0)
1385b69a54eeSKentaro Takeda 		return 0;
1386b69a54eeSKentaro Takeda 	if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
1387b69a54eeSKentaro Takeda 		/*
1388b69a54eeSKentaro Takeda 		 * I don't check directories here because mkdir() and rmdir()
1389b69a54eeSKentaro Takeda 		 * don't call me.
1390b69a54eeSKentaro Takeda 		 */
1391b69a54eeSKentaro Takeda 		return 0;
1392fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1393b69a54eeSKentaro Takeda 	buf = tomoyo_get_path(path);
1394b69a54eeSKentaro Takeda 	if (!buf)
1395b69a54eeSKentaro Takeda 		goto out;
1396b69a54eeSKentaro Takeda 	error = 0;
1397b69a54eeSKentaro Takeda 	/*
1398b69a54eeSKentaro Takeda 	 * If the filename is specified by "deny_rewrite" keyword,
1399b69a54eeSKentaro Takeda 	 * we need to check "allow_rewrite" permission when the filename is not
1400b69a54eeSKentaro Takeda 	 * opened for append mode or the filename is truncated at open time.
1401b69a54eeSKentaro Takeda 	 */
1402b69a54eeSKentaro Takeda 	if ((acc_mode & MAY_WRITE) &&
1403b69a54eeSKentaro Takeda 	    ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
1404b69a54eeSKentaro Takeda 	    (tomoyo_is_no_rewrite_file(buf))) {
1405cb0abe6aSTetsuo Handa 		error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, buf);
1406b69a54eeSKentaro Takeda 	}
1407b69a54eeSKentaro Takeda 	if (!error)
1408cb0abe6aSTetsuo Handa 		error = tomoyo_file_perm(&r, buf, acc_mode);
1409b69a54eeSKentaro Takeda 	if (!error && (flag & O_TRUNC))
1410cb0abe6aSTetsuo Handa 		error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, buf);
1411b69a54eeSKentaro Takeda  out:
14128e2d39a1STetsuo Handa 	kfree(buf);
1413fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1414cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1415b69a54eeSKentaro Takeda 		error = 0;
1416b69a54eeSKentaro Takeda 	return error;
1417b69a54eeSKentaro Takeda }
1418b69a54eeSKentaro Takeda 
1419b69a54eeSKentaro Takeda /**
14202106ccd9STetsuo Handa  * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
1421b69a54eeSKentaro Takeda  *
1422b69a54eeSKentaro Takeda  * @operation: Type of operation.
1423b69a54eeSKentaro Takeda  * @path:      Pointer to "struct path".
1424b69a54eeSKentaro Takeda  *
1425b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1426b69a54eeSKentaro Takeda  */
142797d6931eSTetsuo Handa int tomoyo_path_perm(const u8 operation, struct path *path)
1428b69a54eeSKentaro Takeda {
1429b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1430b69a54eeSKentaro Takeda 	struct tomoyo_path_info *buf;
1431cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1432fdb8ebb7STetsuo Handa 	int idx;
1433b69a54eeSKentaro Takeda 
1434cb0abe6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
1435cb0abe6aSTetsuo Handa 	    !path->mnt)
1436b69a54eeSKentaro Takeda 		return 0;
1437fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1438b69a54eeSKentaro Takeda 	buf = tomoyo_get_path(path);
1439b69a54eeSKentaro Takeda 	if (!buf)
1440b69a54eeSKentaro Takeda 		goto out;
1441b69a54eeSKentaro Takeda 	switch (operation) {
1442cb0abe6aSTetsuo Handa 	case TOMOYO_TYPE_REWRITE:
1443cb0abe6aSTetsuo Handa 		if (!tomoyo_is_no_rewrite_file(buf)) {
1444cb0abe6aSTetsuo Handa 			error = 0;
1445cb0abe6aSTetsuo Handa 			goto out;
1446cb0abe6aSTetsuo Handa 		}
1447cb0abe6aSTetsuo Handa 		break;
14487ef61233STetsuo Handa 	case TOMOYO_TYPE_RMDIR:
14497ef61233STetsuo Handa 	case TOMOYO_TYPE_CHROOT:
1450b69a54eeSKentaro Takeda 		if (!buf->is_dir) {
1451b69a54eeSKentaro Takeda 			/*
1452b69a54eeSKentaro Takeda 			 * tomoyo_get_path() reserves space for appending "/."
1453b69a54eeSKentaro Takeda 			 */
1454b69a54eeSKentaro Takeda 			strcat((char *) buf->name, "/");
1455b69a54eeSKentaro Takeda 			tomoyo_fill_path_info(buf);
1456b69a54eeSKentaro Takeda 		}
1457b69a54eeSKentaro Takeda 	}
1458cb0abe6aSTetsuo Handa 	error = tomoyo_path_permission(&r, operation, buf);
1459b69a54eeSKentaro Takeda  out:
14608e2d39a1STetsuo Handa 	kfree(buf);
1461fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1462cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1463b69a54eeSKentaro Takeda 		error = 0;
1464b69a54eeSKentaro Takeda 	return error;
1465b69a54eeSKentaro Takeda }
1466b69a54eeSKentaro Takeda 
1467b69a54eeSKentaro Takeda /**
1468a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_perm2 - Check permission for path/number/number/number operation.
1469a1f9bb6aSTetsuo Handa  *
1470a1f9bb6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1471a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
1472a1f9bb6aSTetsuo Handa  * @filename:  Filename to check.
1473a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
1474a1f9bb6aSTetsuo Handa  * @dev:       Device number.
1475a1f9bb6aSTetsuo Handa  *
1476a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1477a1f9bb6aSTetsuo Handa  *
1478a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1479a1f9bb6aSTetsuo Handa  */
1480a1f9bb6aSTetsuo Handa static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r,
1481a1f9bb6aSTetsuo Handa 				     const u8 operation,
1482a1f9bb6aSTetsuo Handa 				     const struct tomoyo_path_info *filename,
1483a1f9bb6aSTetsuo Handa 				     const unsigned int mode,
1484a1f9bb6aSTetsuo Handa 				     const unsigned int dev)
1485a1f9bb6aSTetsuo Handa {
1486a1f9bb6aSTetsuo Handa 	int error;
1487a1f9bb6aSTetsuo Handa 	const unsigned int major = MAJOR(dev);
1488a1f9bb6aSTetsuo Handa 	const unsigned int minor = MINOR(dev);
1489a1f9bb6aSTetsuo Handa 
1490a1f9bb6aSTetsuo Handa 	error = tomoyo_path_number3_acl(r, filename, 1 << operation, mode,
1491a1f9bb6aSTetsuo Handa 					major, minor);
1492a1f9bb6aSTetsuo Handa 	if (!error)
1493a1f9bb6aSTetsuo Handa 		return 0;
1494a1f9bb6aSTetsuo Handa 	tomoyo_warn_log(r, "%s %s 0%o %u %u",
1495a1f9bb6aSTetsuo Handa 			tomoyo_path_number32keyword(operation),
1496a1f9bb6aSTetsuo Handa 			filename->name, mode, major, minor);
1497a1f9bb6aSTetsuo Handa 	if (tomoyo_domain_quota_is_ok(r)) {
1498a1f9bb6aSTetsuo Handa 		char mode_buf[64];
1499a1f9bb6aSTetsuo Handa 		char major_buf[64];
1500a1f9bb6aSTetsuo Handa 		char minor_buf[64];
1501a1f9bb6aSTetsuo Handa 		memset(mode_buf, 0, sizeof(mode_buf));
1502a1f9bb6aSTetsuo Handa 		memset(major_buf, 0, sizeof(major_buf));
1503a1f9bb6aSTetsuo Handa 		memset(minor_buf, 0, sizeof(minor_buf));
1504a1f9bb6aSTetsuo Handa 		snprintf(mode_buf, sizeof(mode_buf) - 1, "0%o", mode);
1505a1f9bb6aSTetsuo Handa 		snprintf(major_buf, sizeof(major_buf) - 1, "%u", major);
1506a1f9bb6aSTetsuo Handa 		snprintf(minor_buf, sizeof(minor_buf) - 1, "%u", minor);
1507a1f9bb6aSTetsuo Handa 		tomoyo_update_path_number3_acl(operation,
1508a1f9bb6aSTetsuo Handa 					       tomoyo_get_file_pattern(filename)
1509a1f9bb6aSTetsuo Handa 					       ->name, mode_buf, major_buf,
1510a1f9bb6aSTetsuo Handa 					       minor_buf, r->domain, false);
1511a1f9bb6aSTetsuo Handa 	}
1512a1f9bb6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1513a1f9bb6aSTetsuo Handa 		error = 0;
1514a1f9bb6aSTetsuo Handa 	return error;
1515a1f9bb6aSTetsuo Handa }
1516a1f9bb6aSTetsuo Handa 
1517a1f9bb6aSTetsuo Handa /**
1518a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar".
1519a1f9bb6aSTetsuo Handa  *
1520a1f9bb6aSTetsuo Handa  * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK)
1521a1f9bb6aSTetsuo Handa  * @path:      Pointer to "struct path".
1522a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
1523a1f9bb6aSTetsuo Handa  * @dev:       Device number.
1524a1f9bb6aSTetsuo Handa  *
1525a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1526a1f9bb6aSTetsuo Handa  */
1527a1f9bb6aSTetsuo Handa int tomoyo_path_number3_perm(const u8 operation, struct path *path,
1528a1f9bb6aSTetsuo Handa 			     const unsigned int mode, unsigned int dev)
1529a1f9bb6aSTetsuo Handa {
1530a1f9bb6aSTetsuo Handa 	struct tomoyo_request_info r;
1531a1f9bb6aSTetsuo Handa 	int error = -ENOMEM;
1532a1f9bb6aSTetsuo Handa 	struct tomoyo_path_info *buf;
1533a1f9bb6aSTetsuo Handa 	int idx;
1534a1f9bb6aSTetsuo Handa 
1535a1f9bb6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
1536a1f9bb6aSTetsuo Handa 	    !path->mnt)
1537a1f9bb6aSTetsuo Handa 		return 0;
1538a1f9bb6aSTetsuo Handa 	idx = tomoyo_read_lock();
1539a1f9bb6aSTetsuo Handa 	error = -ENOMEM;
1540a1f9bb6aSTetsuo Handa 	buf = tomoyo_get_path(path);
1541a1f9bb6aSTetsuo Handa 	if (buf) {
1542a1f9bb6aSTetsuo Handa 		error = tomoyo_path_number3_perm2(&r, operation, buf, mode,
1543a1f9bb6aSTetsuo Handa 						  new_decode_dev(dev));
1544a1f9bb6aSTetsuo Handa 		kfree(buf);
1545a1f9bb6aSTetsuo Handa 	}
1546a1f9bb6aSTetsuo Handa 	tomoyo_read_unlock(idx);
1547a1f9bb6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1548a1f9bb6aSTetsuo Handa 		error = 0;
1549a1f9bb6aSTetsuo Handa 	return error;
1550a1f9bb6aSTetsuo Handa }
1551a1f9bb6aSTetsuo Handa 
1552a1f9bb6aSTetsuo Handa /**
15537ef61233STetsuo Handa  * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
1554b69a54eeSKentaro Takeda  *
1555b69a54eeSKentaro Takeda  * @operation: Type of operation.
1556b69a54eeSKentaro Takeda  * @path1:      Pointer to "struct path".
1557b69a54eeSKentaro Takeda  * @path2:      Pointer to "struct path".
1558b69a54eeSKentaro Takeda  *
1559b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1560b69a54eeSKentaro Takeda  */
156197d6931eSTetsuo Handa int tomoyo_path2_perm(const u8 operation, struct path *path1,
1562b69a54eeSKentaro Takeda 		      struct path *path2)
1563b69a54eeSKentaro Takeda {
1564b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1565cb0abe6aSTetsuo Handa 	struct tomoyo_path_info *buf1;
1566cb0abe6aSTetsuo Handa 	struct tomoyo_path_info *buf2;
1567cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1568fdb8ebb7STetsuo Handa 	int idx;
1569b69a54eeSKentaro Takeda 
1570cb0abe6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
1571cb0abe6aSTetsuo Handa 	    !path1->mnt || !path2->mnt)
1572b69a54eeSKentaro Takeda 		return 0;
1573fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1574b69a54eeSKentaro Takeda 	buf1 = tomoyo_get_path(path1);
1575b69a54eeSKentaro Takeda 	buf2 = tomoyo_get_path(path2);
1576b69a54eeSKentaro Takeda 	if (!buf1 || !buf2)
1577b69a54eeSKentaro Takeda 		goto out;
1578b69a54eeSKentaro Takeda 	{
1579b69a54eeSKentaro Takeda 		struct dentry *dentry = path1->dentry;
1580b69a54eeSKentaro Takeda 		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
1581b69a54eeSKentaro Takeda 			/*
1582b69a54eeSKentaro Takeda 			 * tomoyo_get_path() reserves space for appending "/."
1583b69a54eeSKentaro Takeda 			 */
1584b69a54eeSKentaro Takeda 			if (!buf1->is_dir) {
1585b69a54eeSKentaro Takeda 				strcat((char *) buf1->name, "/");
1586b69a54eeSKentaro Takeda 				tomoyo_fill_path_info(buf1);
1587b69a54eeSKentaro Takeda 			}
1588b69a54eeSKentaro Takeda 			if (!buf2->is_dir) {
1589b69a54eeSKentaro Takeda 				strcat((char *) buf2->name, "/");
1590b69a54eeSKentaro Takeda 				tomoyo_fill_path_info(buf2);
1591b69a54eeSKentaro Takeda 			}
1592b69a54eeSKentaro Takeda 		}
1593b69a54eeSKentaro Takeda 	}
1594cb0abe6aSTetsuo Handa 	error = tomoyo_path2_acl(&r, operation, buf1, buf2);
1595b69a54eeSKentaro Takeda 	if (!error)
1596b69a54eeSKentaro Takeda 		goto out;
1597cb0abe6aSTetsuo Handa 	tomoyo_warn_log(&r, "%s %s %s", tomoyo_path22keyword(operation),
1598cb0abe6aSTetsuo Handa 			buf1->name, buf2->name);
1599cb0abe6aSTetsuo Handa 	if (tomoyo_domain_quota_is_ok(&r)) {
1600b69a54eeSKentaro Takeda 		const char *name1 = tomoyo_get_file_pattern(buf1)->name;
1601b69a54eeSKentaro Takeda 		const char *name2 = tomoyo_get_file_pattern(buf2)->name;
1602cb0abe6aSTetsuo Handa 		tomoyo_update_path2_acl(operation, name1, name2, r.domain,
1603b69a54eeSKentaro Takeda 					false);
1604b69a54eeSKentaro Takeda 	}
1605b69a54eeSKentaro Takeda  out:
16068e2d39a1STetsuo Handa 	kfree(buf1);
16078e2d39a1STetsuo Handa 	kfree(buf2);
1608fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1609cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1610b69a54eeSKentaro Takeda 		error = 0;
1611b69a54eeSKentaro Takeda 	return error;
1612b69a54eeSKentaro Takeda }
1613a1f9bb6aSTetsuo Handa 
1614a1f9bb6aSTetsuo Handa /**
1615a1f9bb6aSTetsuo Handa  * tomoyo_write_file_policy - Update file related list.
1616a1f9bb6aSTetsuo Handa  *
1617a1f9bb6aSTetsuo Handa  * @data:      String to parse.
1618a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
1619a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
1620a1f9bb6aSTetsuo Handa  *
1621a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1622a1f9bb6aSTetsuo Handa  *
1623a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1624a1f9bb6aSTetsuo Handa  */
1625a1f9bb6aSTetsuo Handa int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
1626a1f9bb6aSTetsuo Handa 			     const bool is_delete)
1627a1f9bb6aSTetsuo Handa {
1628a1f9bb6aSTetsuo Handa 	char *w[5];
1629a1f9bb6aSTetsuo Handa 	u8 type;
1630a1f9bb6aSTetsuo Handa 	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
1631a1f9bb6aSTetsuo Handa 		return -EINVAL;
1632a1f9bb6aSTetsuo Handa 	if (strncmp(w[0], "allow_", 6)) {
1633a1f9bb6aSTetsuo Handa 		unsigned int perm;
1634a1f9bb6aSTetsuo Handa 		if (sscanf(w[0], "%u", &perm) == 1)
1635a1f9bb6aSTetsuo Handa 			return tomoyo_update_file_acl((u8) perm, w[1], domain,
1636a1f9bb6aSTetsuo Handa 						      is_delete);
1637a1f9bb6aSTetsuo Handa 		goto out;
1638a1f9bb6aSTetsuo Handa 	}
1639a1f9bb6aSTetsuo Handa 	w[0] += 6;
1640a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
1641a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_keyword[type]))
1642a1f9bb6aSTetsuo Handa 			continue;
1643a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_acl(type, w[1], domain, is_delete);
1644a1f9bb6aSTetsuo Handa 	}
1645a1f9bb6aSTetsuo Handa 	if (!w[2][0])
1646a1f9bb6aSTetsuo Handa 		goto out;
1647a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
1648a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path2_keyword[type]))
1649a1f9bb6aSTetsuo Handa 			continue;
1650a1f9bb6aSTetsuo Handa 		return tomoyo_update_path2_acl(type, w[1], w[2], domain,
1651a1f9bb6aSTetsuo Handa 					       is_delete);
1652a1f9bb6aSTetsuo Handa 	}
1653a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
1654a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_number_keyword[type]))
1655a1f9bb6aSTetsuo Handa 			continue;
1656a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
1657a1f9bb6aSTetsuo Handa 						     is_delete);
1658a1f9bb6aSTetsuo Handa 	}
1659a1f9bb6aSTetsuo Handa 	if (!w[3][0] || !w[4][0])
1660a1f9bb6aSTetsuo Handa 		goto out;
1661a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER3_OPERATION; type++) {
1662a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_number3_keyword[type]))
1663a1f9bb6aSTetsuo Handa 			continue;
1664a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_number3_acl(type, w[1], w[2], w[3],
1665a1f9bb6aSTetsuo Handa 						      w[4], domain, is_delete);
1666a1f9bb6aSTetsuo Handa 	}
1667a1f9bb6aSTetsuo Handa  out:
1668a1f9bb6aSTetsuo Handa 	return -EINVAL;
1669a1f9bb6aSTetsuo Handa }
1670