xref: /openbmc/linux/security/tomoyo/file.c (revision 82e0f001)
1b69a54eeSKentaro Takeda /*
2b69a54eeSKentaro Takeda  * security/tomoyo/file.c
3b69a54eeSKentaro Takeda  *
4c3ef1500STetsuo Handa  * Pathname restriction functions.
5b69a54eeSKentaro Takeda  *
6c3ef1500STetsuo Handa  * Copyright (C) 2005-2010  NTT DATA CORPORATION
7b69a54eeSKentaro Takeda  */
8b69a54eeSKentaro Takeda 
9b69a54eeSKentaro Takeda #include "common.h"
105a0e3ad6STejun Heo #include <linux/slab.h>
11b69a54eeSKentaro Takeda 
12a1f9bb6aSTetsuo Handa /* Keyword array for operations with one pathname. */
137ef61233STetsuo Handa static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
147ef61233STetsuo Handa 	[TOMOYO_TYPE_READ_WRITE] = "read/write",
157ef61233STetsuo Handa 	[TOMOYO_TYPE_EXECUTE]    = "execute",
167ef61233STetsuo Handa 	[TOMOYO_TYPE_READ]       = "read",
177ef61233STetsuo Handa 	[TOMOYO_TYPE_WRITE]      = "write",
187ef61233STetsuo Handa 	[TOMOYO_TYPE_UNLINK]     = "unlink",
197ef61233STetsuo Handa 	[TOMOYO_TYPE_RMDIR]      = "rmdir",
207ef61233STetsuo Handa 	[TOMOYO_TYPE_TRUNCATE]   = "truncate",
217ef61233STetsuo Handa 	[TOMOYO_TYPE_SYMLINK]    = "symlink",
227ef61233STetsuo Handa 	[TOMOYO_TYPE_REWRITE]    = "rewrite",
237ef61233STetsuo Handa 	[TOMOYO_TYPE_CHROOT]     = "chroot",
247ef61233STetsuo Handa 	[TOMOYO_TYPE_UMOUNT]     = "unmount",
25b69a54eeSKentaro Takeda };
26b69a54eeSKentaro Takeda 
27a1f9bb6aSTetsuo Handa /* Keyword array for operations with one pathname and three numbers. */
28a1f9bb6aSTetsuo Handa static const char *tomoyo_path_number3_keyword
29a1f9bb6aSTetsuo Handa [TOMOYO_MAX_PATH_NUMBER3_OPERATION] = {
30a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKBLOCK]    = "mkblock",
31a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKCHAR]     = "mkchar",
32a1f9bb6aSTetsuo Handa };
33a1f9bb6aSTetsuo Handa 
34a1f9bb6aSTetsuo Handa /* Keyword array for operations with two pathnames. */
357ef61233STetsuo Handa static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
367ef61233STetsuo Handa 	[TOMOYO_TYPE_LINK]       = "link",
377ef61233STetsuo Handa 	[TOMOYO_TYPE_RENAME]     = "rename",
387ef61233STetsuo Handa 	[TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
39b69a54eeSKentaro Takeda };
40b69a54eeSKentaro Takeda 
41a1f9bb6aSTetsuo Handa /* Keyword array for operations with one pathname and one number. */
42a1f9bb6aSTetsuo Handa static const char *tomoyo_path_number_keyword
43a1f9bb6aSTetsuo Handa [TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
44a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_CREATE]     = "create",
45a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKDIR]      = "mkdir",
46a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKFIFO]     = "mkfifo",
47a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_MKSOCK]     = "mksock",
48a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_IOCTL]      = "ioctl",
49a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_CHMOD]      = "chmod",
50a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_CHOWN]      = "chown",
51a1f9bb6aSTetsuo Handa 	[TOMOYO_TYPE_CHGRP]      = "chgrp",
52a1f9bb6aSTetsuo Handa };
53a1f9bb6aSTetsuo Handa 
5457c2590fSTetsuo Handa static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = {
5557c2590fSTetsuo Handa 	[TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN,
5657c2590fSTetsuo Handa 	[TOMOYO_TYPE_EXECUTE]    = TOMOYO_MAC_FILE_EXECUTE,
5757c2590fSTetsuo Handa 	[TOMOYO_TYPE_READ]       = TOMOYO_MAC_FILE_OPEN,
5857c2590fSTetsuo Handa 	[TOMOYO_TYPE_WRITE]      = TOMOYO_MAC_FILE_OPEN,
5957c2590fSTetsuo Handa 	[TOMOYO_TYPE_UNLINK]     = TOMOYO_MAC_FILE_UNLINK,
6057c2590fSTetsuo Handa 	[TOMOYO_TYPE_RMDIR]      = TOMOYO_MAC_FILE_RMDIR,
6157c2590fSTetsuo Handa 	[TOMOYO_TYPE_TRUNCATE]   = TOMOYO_MAC_FILE_TRUNCATE,
6257c2590fSTetsuo Handa 	[TOMOYO_TYPE_SYMLINK]    = TOMOYO_MAC_FILE_SYMLINK,
6357c2590fSTetsuo Handa 	[TOMOYO_TYPE_REWRITE]    = TOMOYO_MAC_FILE_REWRITE,
6457c2590fSTetsuo Handa 	[TOMOYO_TYPE_CHROOT]     = TOMOYO_MAC_FILE_CHROOT,
6557c2590fSTetsuo Handa 	[TOMOYO_TYPE_UMOUNT]     = TOMOYO_MAC_FILE_UMOUNT,
6657c2590fSTetsuo Handa };
6757c2590fSTetsuo Handa 
6857c2590fSTetsuo Handa static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = {
6957c2590fSTetsuo Handa 	[TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,
7057c2590fSTetsuo Handa 	[TOMOYO_TYPE_MKCHAR]  = TOMOYO_MAC_FILE_MKCHAR,
7157c2590fSTetsuo Handa };
7257c2590fSTetsuo Handa 
7357c2590fSTetsuo Handa static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
7457c2590fSTetsuo Handa 	[TOMOYO_TYPE_LINK]       = TOMOYO_MAC_FILE_LINK,
7557c2590fSTetsuo Handa 	[TOMOYO_TYPE_RENAME]     = TOMOYO_MAC_FILE_RENAME,
7657c2590fSTetsuo Handa 	[TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT,
7757c2590fSTetsuo Handa };
7857c2590fSTetsuo Handa 
7957c2590fSTetsuo Handa static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
8057c2590fSTetsuo Handa 	[TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE,
8157c2590fSTetsuo Handa 	[TOMOYO_TYPE_MKDIR]  = TOMOYO_MAC_FILE_MKDIR,
8257c2590fSTetsuo Handa 	[TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO,
8357c2590fSTetsuo Handa 	[TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK,
8457c2590fSTetsuo Handa 	[TOMOYO_TYPE_IOCTL]  = TOMOYO_MAC_FILE_IOCTL,
8557c2590fSTetsuo Handa 	[TOMOYO_TYPE_CHMOD]  = TOMOYO_MAC_FILE_CHMOD,
8657c2590fSTetsuo Handa 	[TOMOYO_TYPE_CHOWN]  = TOMOYO_MAC_FILE_CHOWN,
8757c2590fSTetsuo Handa 	[TOMOYO_TYPE_CHGRP]  = TOMOYO_MAC_FILE_CHGRP,
8857c2590fSTetsuo Handa };
8957c2590fSTetsuo Handa 
907762fbffSTetsuo Handa void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
917762fbffSTetsuo Handa {
927762fbffSTetsuo Handa 	if (!ptr)
937762fbffSTetsuo Handa 		return;
947762fbffSTetsuo Handa 	if (ptr->is_group)
957762fbffSTetsuo Handa 		tomoyo_put_path_group(ptr->group);
967762fbffSTetsuo Handa 	else
977762fbffSTetsuo Handa 		tomoyo_put_name(ptr->filename);
987762fbffSTetsuo Handa }
997762fbffSTetsuo Handa 
1007762fbffSTetsuo Handa bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
1017762fbffSTetsuo Handa 			       const struct tomoyo_name_union *ptr)
1027762fbffSTetsuo Handa {
1037762fbffSTetsuo Handa 	if (ptr->is_group)
1043f629636STetsuo Handa 		return tomoyo_path_matches_group(name, ptr->group);
1057762fbffSTetsuo Handa 	return tomoyo_path_matches_pattern(name, ptr->filename);
1067762fbffSTetsuo Handa }
1077762fbffSTetsuo Handa 
1084c3e9e2dSTetsuo Handa void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
1094c3e9e2dSTetsuo Handa {
1104c3e9e2dSTetsuo Handa 	if (ptr && ptr->is_group)
1114c3e9e2dSTetsuo Handa 		tomoyo_put_number_group(ptr->group);
1124c3e9e2dSTetsuo Handa }
1134c3e9e2dSTetsuo Handa 
1144c3e9e2dSTetsuo Handa bool tomoyo_compare_number_union(const unsigned long value,
1154c3e9e2dSTetsuo Handa 				 const struct tomoyo_number_union *ptr)
1164c3e9e2dSTetsuo Handa {
1174c3e9e2dSTetsuo Handa 	if (ptr->is_group)
1184c3e9e2dSTetsuo Handa 		return tomoyo_number_matches_group(value, value, ptr->group);
1194c3e9e2dSTetsuo Handa 	return value >= ptr->values[0] && value <= ptr->values[1];
1204c3e9e2dSTetsuo Handa }
1214c3e9e2dSTetsuo Handa 
122b69a54eeSKentaro Takeda /**
1237ef61233STetsuo Handa  * tomoyo_path2keyword - Get the name of single path operation.
124b69a54eeSKentaro Takeda  *
125b69a54eeSKentaro Takeda  * @operation: Type of operation.
126b69a54eeSKentaro Takeda  *
127b69a54eeSKentaro Takeda  * Returns the name of single path operation.
128b69a54eeSKentaro Takeda  */
1297ef61233STetsuo Handa const char *tomoyo_path2keyword(const u8 operation)
130b69a54eeSKentaro Takeda {
1317ef61233STetsuo Handa 	return (operation < TOMOYO_MAX_PATH_OPERATION)
1327ef61233STetsuo Handa 		? tomoyo_path_keyword[operation] : NULL;
133b69a54eeSKentaro Takeda }
134b69a54eeSKentaro Takeda 
135b69a54eeSKentaro Takeda /**
136a1f9bb6aSTetsuo Handa  * tomoyo_path_number32keyword - Get the name of path/number/number/number operations.
137a1f9bb6aSTetsuo Handa  *
138a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
139a1f9bb6aSTetsuo Handa  *
140a1f9bb6aSTetsuo Handa  * Returns the name of path/number/number/number operation.
141a1f9bb6aSTetsuo Handa  */
142a1f9bb6aSTetsuo Handa const char *tomoyo_path_number32keyword(const u8 operation)
143a1f9bb6aSTetsuo Handa {
144a1f9bb6aSTetsuo Handa 	return (operation < TOMOYO_MAX_PATH_NUMBER3_OPERATION)
145a1f9bb6aSTetsuo Handa 		? tomoyo_path_number3_keyword[operation] : NULL;
146a1f9bb6aSTetsuo Handa }
147a1f9bb6aSTetsuo Handa 
148a1f9bb6aSTetsuo Handa /**
1497ef61233STetsuo Handa  * tomoyo_path22keyword - Get the name of double path operation.
150b69a54eeSKentaro Takeda  *
151b69a54eeSKentaro Takeda  * @operation: Type of operation.
152b69a54eeSKentaro Takeda  *
153b69a54eeSKentaro Takeda  * Returns the name of double path operation.
154b69a54eeSKentaro Takeda  */
1557ef61233STetsuo Handa const char *tomoyo_path22keyword(const u8 operation)
156b69a54eeSKentaro Takeda {
1577ef61233STetsuo Handa 	return (operation < TOMOYO_MAX_PATH2_OPERATION)
1587ef61233STetsuo Handa 		? tomoyo_path2_keyword[operation] : NULL;
159b69a54eeSKentaro Takeda }
160b69a54eeSKentaro Takeda 
161b69a54eeSKentaro Takeda /**
162a1f9bb6aSTetsuo Handa  * tomoyo_path_number2keyword - Get the name of path/number operations.
163a1f9bb6aSTetsuo Handa  *
164a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
165a1f9bb6aSTetsuo Handa  *
166a1f9bb6aSTetsuo Handa  * Returns the name of path/number operation.
167a1f9bb6aSTetsuo Handa  */
168a1f9bb6aSTetsuo Handa const char *tomoyo_path_number2keyword(const u8 operation)
169a1f9bb6aSTetsuo Handa {
170a1f9bb6aSTetsuo Handa 	return (operation < TOMOYO_MAX_PATH_NUMBER_OPERATION)
171a1f9bb6aSTetsuo Handa 		? tomoyo_path_number_keyword[operation] : NULL;
172a1f9bb6aSTetsuo Handa }
173a1f9bb6aSTetsuo Handa 
174c8c57e84STetsuo Handa static void tomoyo_add_slash(struct tomoyo_path_info *buf)
175c8c57e84STetsuo Handa {
176c8c57e84STetsuo Handa 	if (buf->is_dir)
177c8c57e84STetsuo Handa 		return;
178c8c57e84STetsuo Handa 	/*
179c8c57e84STetsuo Handa 	 * This is OK because tomoyo_encode() reserves space for appending "/".
180c8c57e84STetsuo Handa 	 */
181c8c57e84STetsuo Handa 	strcat((char *) buf->name, "/");
182c8c57e84STetsuo Handa 	tomoyo_fill_path_info(buf);
183c8c57e84STetsuo Handa }
184c8c57e84STetsuo Handa 
185a1f9bb6aSTetsuo Handa /**
186b69a54eeSKentaro Takeda  * tomoyo_strendswith - Check whether the token ends with the given token.
187b69a54eeSKentaro Takeda  *
188b69a54eeSKentaro Takeda  * @name: The token to check.
189b69a54eeSKentaro Takeda  * @tail: The token to find.
190b69a54eeSKentaro Takeda  *
191b69a54eeSKentaro Takeda  * Returns true if @name ends with @tail, false otherwise.
192b69a54eeSKentaro Takeda  */
193b69a54eeSKentaro Takeda static bool tomoyo_strendswith(const char *name, const char *tail)
194b69a54eeSKentaro Takeda {
195b69a54eeSKentaro Takeda 	int len;
196b69a54eeSKentaro Takeda 
197b69a54eeSKentaro Takeda 	if (!name || !tail)
198b69a54eeSKentaro Takeda 		return false;
199b69a54eeSKentaro Takeda 	len = strlen(name) - strlen(tail);
200b69a54eeSKentaro Takeda 	return len >= 0 && !strcmp(name + len, tail);
201b69a54eeSKentaro Takeda }
202b69a54eeSKentaro Takeda 
203b69a54eeSKentaro Takeda /**
204c8c57e84STetsuo Handa  * tomoyo_get_realpath - Get realpath.
205b69a54eeSKentaro Takeda  *
206c8c57e84STetsuo Handa  * @buf:  Pointer to "struct tomoyo_path_info".
207b69a54eeSKentaro Takeda  * @path: Pointer to "struct path".
208b69a54eeSKentaro Takeda  *
209c8c57e84STetsuo Handa  * Returns true on success, false otherwise.
210b69a54eeSKentaro Takeda  */
211c8c57e84STetsuo Handa static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)
212b69a54eeSKentaro Takeda {
213c8c57e84STetsuo Handa 	buf->name = tomoyo_realpath_from_path(path);
214c8c57e84STetsuo Handa 	if (buf->name) {
215c8c57e84STetsuo Handa 		tomoyo_fill_path_info(buf);
216c8c57e84STetsuo Handa 		return true;
217b69a54eeSKentaro Takeda 	}
218c8c57e84STetsuo Handa         return false;
219b69a54eeSKentaro Takeda }
220b69a54eeSKentaro Takeda 
2217ef61233STetsuo Handa static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
222b69a54eeSKentaro Takeda 				   const char *filename2,
2237ef61233STetsuo Handa 				   struct tomoyo_domain_info *const domain,
2247ef61233STetsuo Handa 				   const bool is_delete);
2257ef61233STetsuo Handa static int tomoyo_update_path_acl(const u8 type, const char *filename,
2267ef61233STetsuo Handa 				  struct tomoyo_domain_info *const domain,
2277ef61233STetsuo Handa 				  const bool is_delete);
228b69a54eeSKentaro Takeda 
229c3fa109aSTetsuo Handa /*
230c3fa109aSTetsuo Handa  * tomoyo_globally_readable_list is used for holding list of pathnames which
231c3fa109aSTetsuo Handa  * are by default allowed to be open()ed for reading by any process.
232c3fa109aSTetsuo Handa  *
233c3fa109aSTetsuo Handa  * An entry is added by
234c3fa109aSTetsuo Handa  *
235c3fa109aSTetsuo Handa  * # echo 'allow_read /lib/libc-2.5.so' > \
236c3fa109aSTetsuo Handa  *                               /sys/kernel/security/tomoyo/exception_policy
237c3fa109aSTetsuo Handa  *
238c3fa109aSTetsuo Handa  * and is deleted by
239c3fa109aSTetsuo Handa  *
240c3fa109aSTetsuo Handa  * # echo 'delete allow_read /lib/libc-2.5.so' > \
241c3fa109aSTetsuo Handa  *                               /sys/kernel/security/tomoyo/exception_policy
242c3fa109aSTetsuo Handa  *
243c3fa109aSTetsuo Handa  * and all entries are retrieved by
244c3fa109aSTetsuo Handa  *
245c3fa109aSTetsuo Handa  * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy
246c3fa109aSTetsuo Handa  *
247c3fa109aSTetsuo Handa  * In the example above, any process is allowed to
248c3fa109aSTetsuo Handa  * open("/lib/libc-2.5.so", O_RDONLY).
249c3fa109aSTetsuo Handa  * One exception is, if the domain which current process belongs to is marked
250c3fa109aSTetsuo Handa  * as "ignore_global_allow_read", current process can't do so unless explicitly
251c3fa109aSTetsuo Handa  * given "allow_read /lib/libc-2.5.so" to the domain which current process
252c3fa109aSTetsuo Handa  * belongs to.
253c3fa109aSTetsuo Handa  */
254847b173eSTetsuo Handa LIST_HEAD(tomoyo_globally_readable_list);
255b69a54eeSKentaro Takeda 
256b69a54eeSKentaro Takeda /**
257b69a54eeSKentaro Takeda  * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
258b69a54eeSKentaro Takeda  *
259b69a54eeSKentaro Takeda  * @filename:  Filename unconditionally permitted to open() for reading.
260b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
261b69a54eeSKentaro Takeda  *
262b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
263fdb8ebb7STetsuo Handa  *
264fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
265b69a54eeSKentaro Takeda  */
266b69a54eeSKentaro Takeda static int tomoyo_update_globally_readable_entry(const char *filename,
267b69a54eeSKentaro Takeda 						 const bool is_delete)
268b69a54eeSKentaro Takeda {
269b69a54eeSKentaro Takeda 	struct tomoyo_globally_readable_file_entry *ptr;
2709e4b50e9STetsuo Handa 	struct tomoyo_globally_readable_file_entry e = { };
271ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
272b69a54eeSKentaro Takeda 
2733f629636STetsuo Handa 	if (!tomoyo_is_correct_word(filename))
274b69a54eeSKentaro Takeda 		return -EINVAL;
2759e4b50e9STetsuo Handa 	e.filename = tomoyo_get_name(filename);
2769e4b50e9STetsuo Handa 	if (!e.filename)
277b69a54eeSKentaro Takeda 		return -ENOMEM;
27829282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
27929282381STetsuo Handa 		goto out;
28082e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list,
28182e0f001STetsuo Handa 				head.list) {
2829e4b50e9STetsuo Handa 		if (ptr->filename != e.filename)
283b69a54eeSKentaro Takeda 			continue;
28482e0f001STetsuo Handa 		ptr->head.is_deleted = is_delete;
285b69a54eeSKentaro Takeda 		error = 0;
286ca0b7df3STetsuo Handa 		break;
287b69a54eeSKentaro Takeda 	}
2889e4b50e9STetsuo Handa 	if (!is_delete && error) {
2899e4b50e9STetsuo Handa 		struct tomoyo_globally_readable_file_entry *entry =
2909e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
2919e4b50e9STetsuo Handa 		if (entry) {
29282e0f001STetsuo Handa 			list_add_tail_rcu(&entry->head.list,
2939e4b50e9STetsuo Handa 					  &tomoyo_globally_readable_list);
294b69a54eeSKentaro Takeda 			error = 0;
295ca0b7df3STetsuo Handa 		}
2969e4b50e9STetsuo Handa 	}
297f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
29829282381STetsuo Handa  out:
2999e4b50e9STetsuo Handa 	tomoyo_put_name(e.filename);
300b69a54eeSKentaro Takeda 	return error;
301b69a54eeSKentaro Takeda }
302b69a54eeSKentaro Takeda 
303b69a54eeSKentaro Takeda /**
304b69a54eeSKentaro Takeda  * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
305b69a54eeSKentaro Takeda  *
306b69a54eeSKentaro Takeda  * @filename: The filename to check.
307b69a54eeSKentaro Takeda  *
308b69a54eeSKentaro Takeda  * Returns true if any domain can open @filename for reading, false otherwise.
309fdb8ebb7STetsuo Handa  *
310fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
311b69a54eeSKentaro Takeda  */
312b69a54eeSKentaro Takeda static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
313b69a54eeSKentaro Takeda 					     filename)
314b69a54eeSKentaro Takeda {
315b69a54eeSKentaro Takeda 	struct tomoyo_globally_readable_file_entry *ptr;
316b69a54eeSKentaro Takeda 	bool found = false;
317fdb8ebb7STetsuo Handa 
31882e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list,
31982e0f001STetsuo Handa 				head.list) {
32082e0f001STetsuo Handa 		if (!ptr->head.is_deleted &&
321b69a54eeSKentaro Takeda 		    tomoyo_path_matches_pattern(filename, ptr->filename)) {
322b69a54eeSKentaro Takeda 			found = true;
323b69a54eeSKentaro Takeda 			break;
324b69a54eeSKentaro Takeda 		}
325b69a54eeSKentaro Takeda 	}
326b69a54eeSKentaro Takeda 	return found;
327b69a54eeSKentaro Takeda }
328b69a54eeSKentaro Takeda 
329b69a54eeSKentaro Takeda /**
330b69a54eeSKentaro Takeda  * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
331b69a54eeSKentaro Takeda  *
332b69a54eeSKentaro Takeda  * @data:      String to parse.
333b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
334b69a54eeSKentaro Takeda  *
335b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
336fdb8ebb7STetsuo Handa  *
337fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
338b69a54eeSKentaro Takeda  */
339b69a54eeSKentaro Takeda int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
340b69a54eeSKentaro Takeda {
341b69a54eeSKentaro Takeda 	return tomoyo_update_globally_readable_entry(data, is_delete);
342b69a54eeSKentaro Takeda }
343b69a54eeSKentaro Takeda 
344b69a54eeSKentaro Takeda /**
345b69a54eeSKentaro Takeda  * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
346b69a54eeSKentaro Takeda  *
347b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
348b69a54eeSKentaro Takeda  *
349b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
350fdb8ebb7STetsuo Handa  *
351fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
352b69a54eeSKentaro Takeda  */
353b69a54eeSKentaro Takeda bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
354b69a54eeSKentaro Takeda {
355b69a54eeSKentaro Takeda 	struct list_head *pos;
356b69a54eeSKentaro Takeda 	bool done = true;
357b69a54eeSKentaro Takeda 
358b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2,
359b69a54eeSKentaro Takeda 			     &tomoyo_globally_readable_list) {
360b69a54eeSKentaro Takeda 		struct tomoyo_globally_readable_file_entry *ptr;
361b69a54eeSKentaro Takeda 		ptr = list_entry(pos,
362b69a54eeSKentaro Takeda 				 struct tomoyo_globally_readable_file_entry,
36382e0f001STetsuo Handa 				 head.list);
36482e0f001STetsuo Handa 		if (ptr->head.is_deleted)
365b69a54eeSKentaro Takeda 			continue;
3667d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
3677d2948b1STetsuo Handa 					ptr->filename->name);
3687d2948b1STetsuo Handa 		if (!done)
369b69a54eeSKentaro Takeda 			break;
370b69a54eeSKentaro Takeda 	}
371b69a54eeSKentaro Takeda 	return done;
372b69a54eeSKentaro Takeda }
373b69a54eeSKentaro Takeda 
374c3fa109aSTetsuo Handa /* tomoyo_pattern_list is used for holding list of pathnames which are used for
375c3fa109aSTetsuo Handa  * converting pathnames to pathname patterns during learning mode.
376c3fa109aSTetsuo Handa  *
377c3fa109aSTetsuo Handa  * An entry is added by
378c3fa109aSTetsuo Handa  *
379c3fa109aSTetsuo Handa  * # echo 'file_pattern /proc/\$/mounts' > \
380c3fa109aSTetsuo Handa  *                             /sys/kernel/security/tomoyo/exception_policy
381c3fa109aSTetsuo Handa  *
382c3fa109aSTetsuo Handa  * and is deleted by
383c3fa109aSTetsuo Handa  *
384c3fa109aSTetsuo Handa  * # echo 'delete file_pattern /proc/\$/mounts' > \
385c3fa109aSTetsuo Handa  *                             /sys/kernel/security/tomoyo/exception_policy
386c3fa109aSTetsuo Handa  *
387c3fa109aSTetsuo Handa  * and all entries are retrieved by
388c3fa109aSTetsuo Handa  *
389c3fa109aSTetsuo Handa  * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy
390c3fa109aSTetsuo Handa  *
391c3fa109aSTetsuo Handa  * In the example above, if a process which belongs to a domain which is in
392c3fa109aSTetsuo Handa  * learning mode requested open("/proc/1/mounts", O_RDONLY),
393c3fa109aSTetsuo Handa  * "allow_read /proc/\$/mounts" is automatically added to the domain which that
394c3fa109aSTetsuo Handa  * process belongs to.
395c3fa109aSTetsuo Handa  *
396c3fa109aSTetsuo Handa  * It is not a desirable behavior that we have to use /proc/\$/ instead of
397c3fa109aSTetsuo Handa  * /proc/self/ when current process needs to access only current process's
398c3fa109aSTetsuo Handa  * information. As of now, LSM version of TOMOYO is using __d_path() for
399c3fa109aSTetsuo Handa  * calculating pathname. Non LSM version of TOMOYO is using its own function
400c3fa109aSTetsuo Handa  * which pretends as if /proc/self/ is not a symlink; so that we can forbid
401c3fa109aSTetsuo Handa  * current process from accessing other process's information.
402c3fa109aSTetsuo Handa  */
403847b173eSTetsuo Handa LIST_HEAD(tomoyo_pattern_list);
404b69a54eeSKentaro Takeda 
405b69a54eeSKentaro Takeda /**
406b69a54eeSKentaro Takeda  * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
407b69a54eeSKentaro Takeda  *
408b69a54eeSKentaro Takeda  * @pattern:   Pathname pattern.
409b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
410b69a54eeSKentaro Takeda  *
411b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
412fdb8ebb7STetsuo Handa  *
413fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
414b69a54eeSKentaro Takeda  */
415b69a54eeSKentaro Takeda static int tomoyo_update_file_pattern_entry(const char *pattern,
416b69a54eeSKentaro Takeda 					    const bool is_delete)
417b69a54eeSKentaro Takeda {
418b69a54eeSKentaro Takeda 	struct tomoyo_pattern_entry *ptr;
4193f629636STetsuo Handa 	struct tomoyo_pattern_entry e = { };
420ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
421b69a54eeSKentaro Takeda 
4223f629636STetsuo Handa 	if (!tomoyo_is_correct_word(pattern))
4233f629636STetsuo Handa 		return -EINVAL;
4243f629636STetsuo Handa 	e.pattern = tomoyo_get_name(pattern);
4259e4b50e9STetsuo Handa 	if (!e.pattern)
426ca0b7df3STetsuo Handa 		return error;
42729282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
42829282381STetsuo Handa 		goto out;
42982e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) {
4309e4b50e9STetsuo Handa 		if (e.pattern != ptr->pattern)
431b69a54eeSKentaro Takeda 			continue;
43282e0f001STetsuo Handa 		ptr->head.is_deleted = is_delete;
433b69a54eeSKentaro Takeda 		error = 0;
434ca0b7df3STetsuo Handa 		break;
435b69a54eeSKentaro Takeda 	}
4369e4b50e9STetsuo Handa 	if (!is_delete && error) {
4379e4b50e9STetsuo Handa 		struct tomoyo_pattern_entry *entry =
4389e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
4399e4b50e9STetsuo Handa 		if (entry) {
44082e0f001STetsuo Handa 			list_add_tail_rcu(&entry->head.list,
44182e0f001STetsuo Handa 					  &tomoyo_pattern_list);
442b69a54eeSKentaro Takeda 			error = 0;
443ca0b7df3STetsuo Handa 		}
4449e4b50e9STetsuo Handa 	}
445f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
446ca0b7df3STetsuo Handa  out:
4479e4b50e9STetsuo Handa 	tomoyo_put_name(e.pattern);
448b69a54eeSKentaro Takeda 	return error;
449b69a54eeSKentaro Takeda }
450b69a54eeSKentaro Takeda 
451b69a54eeSKentaro Takeda /**
45217fcfbd9STetsuo Handa  * tomoyo_file_pattern - Get patterned pathname.
453b69a54eeSKentaro Takeda  *
454b69a54eeSKentaro Takeda  * @filename: The filename to find patterned pathname.
455b69a54eeSKentaro Takeda  *
456b69a54eeSKentaro Takeda  * Returns pointer to pathname pattern if matched, @filename otherwise.
457fdb8ebb7STetsuo Handa  *
458fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
459b69a54eeSKentaro Takeda  */
46017fcfbd9STetsuo Handa const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename)
461b69a54eeSKentaro Takeda {
462b69a54eeSKentaro Takeda 	struct tomoyo_pattern_entry *ptr;
463b69a54eeSKentaro Takeda 	const struct tomoyo_path_info *pattern = NULL;
464b69a54eeSKentaro Takeda 
46582e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) {
46682e0f001STetsuo Handa 		if (ptr->head.is_deleted)
467b69a54eeSKentaro Takeda 			continue;
468b69a54eeSKentaro Takeda 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
469b69a54eeSKentaro Takeda 			continue;
470b69a54eeSKentaro Takeda 		pattern = ptr->pattern;
471b69a54eeSKentaro Takeda 		if (tomoyo_strendswith(pattern->name, "/\\*")) {
472b69a54eeSKentaro Takeda 			/* Do nothing. Try to find the better match. */
473b69a54eeSKentaro Takeda 		} else {
474b69a54eeSKentaro Takeda 			/* This would be the better match. Use this. */
475b69a54eeSKentaro Takeda 			break;
476b69a54eeSKentaro Takeda 		}
477b69a54eeSKentaro Takeda 	}
478b69a54eeSKentaro Takeda 	if (pattern)
479b69a54eeSKentaro Takeda 		filename = pattern;
48017fcfbd9STetsuo Handa 	return filename->name;
481b69a54eeSKentaro Takeda }
482b69a54eeSKentaro Takeda 
483b69a54eeSKentaro Takeda /**
484b69a54eeSKentaro Takeda  * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
485b69a54eeSKentaro Takeda  *
486b69a54eeSKentaro Takeda  * @data:      String to parse.
487b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
488b69a54eeSKentaro Takeda  *
489b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
490fdb8ebb7STetsuo Handa  *
491fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
492b69a54eeSKentaro Takeda  */
493b69a54eeSKentaro Takeda int tomoyo_write_pattern_policy(char *data, const bool is_delete)
494b69a54eeSKentaro Takeda {
495b69a54eeSKentaro Takeda 	return tomoyo_update_file_pattern_entry(data, is_delete);
496b69a54eeSKentaro Takeda }
497b69a54eeSKentaro Takeda 
498b69a54eeSKentaro Takeda /**
499b69a54eeSKentaro Takeda  * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
500b69a54eeSKentaro Takeda  *
501b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
502b69a54eeSKentaro Takeda  *
503b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
504fdb8ebb7STetsuo Handa  *
505fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
506b69a54eeSKentaro Takeda  */
507b69a54eeSKentaro Takeda bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
508b69a54eeSKentaro Takeda {
509b69a54eeSKentaro Takeda 	struct list_head *pos;
510b69a54eeSKentaro Takeda 	bool done = true;
511b69a54eeSKentaro Takeda 
512b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
513b69a54eeSKentaro Takeda 		struct tomoyo_pattern_entry *ptr;
51482e0f001STetsuo Handa 		ptr = list_entry(pos, struct tomoyo_pattern_entry, head.list);
51582e0f001STetsuo Handa 		if (ptr->head.is_deleted)
516b69a54eeSKentaro Takeda 			continue;
5177d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
5187d2948b1STetsuo Handa 					"%s\n", ptr->pattern->name);
5197d2948b1STetsuo Handa 		if (!done)
520b69a54eeSKentaro Takeda 			break;
521b69a54eeSKentaro Takeda 	}
522b69a54eeSKentaro Takeda 	return done;
523b69a54eeSKentaro Takeda }
524b69a54eeSKentaro Takeda 
525c3fa109aSTetsuo Handa /*
526c3fa109aSTetsuo Handa  * tomoyo_no_rewrite_list is used for holding list of pathnames which are by
527c3fa109aSTetsuo Handa  * default forbidden to modify already written content of a file.
528c3fa109aSTetsuo Handa  *
529c3fa109aSTetsuo Handa  * An entry is added by
530c3fa109aSTetsuo Handa  *
531c3fa109aSTetsuo Handa  * # echo 'deny_rewrite /var/log/messages' > \
532c3fa109aSTetsuo Handa  *                              /sys/kernel/security/tomoyo/exception_policy
533c3fa109aSTetsuo Handa  *
534c3fa109aSTetsuo Handa  * and is deleted by
535c3fa109aSTetsuo Handa  *
536c3fa109aSTetsuo Handa  * # echo 'delete deny_rewrite /var/log/messages' > \
537c3fa109aSTetsuo Handa  *                              /sys/kernel/security/tomoyo/exception_policy
538c3fa109aSTetsuo Handa  *
539c3fa109aSTetsuo Handa  * and all entries are retrieved by
540c3fa109aSTetsuo Handa  *
541c3fa109aSTetsuo Handa  * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy
542c3fa109aSTetsuo Handa  *
543c3fa109aSTetsuo Handa  * In the example above, if a process requested to rewrite /var/log/messages ,
544c3fa109aSTetsuo Handa  * the process can't rewrite unless the domain which that process belongs to
545c3fa109aSTetsuo Handa  * has "allow_rewrite /var/log/messages" entry.
546c3fa109aSTetsuo Handa  *
547c3fa109aSTetsuo Handa  * It is not a desirable behavior that we have to add "\040(deleted)" suffix
548c3fa109aSTetsuo Handa  * when we want to allow rewriting already unlink()ed file. As of now,
549c3fa109aSTetsuo Handa  * LSM version of TOMOYO is using __d_path() for calculating pathname.
550c3fa109aSTetsuo Handa  * Non LSM version of TOMOYO is using its own function which doesn't append
551c3fa109aSTetsuo Handa  * " (deleted)" suffix if the file is already unlink()ed; so that we don't
552c3fa109aSTetsuo Handa  * need to worry whether the file is already unlink()ed or not.
553c3fa109aSTetsuo Handa  */
554847b173eSTetsuo Handa LIST_HEAD(tomoyo_no_rewrite_list);
555b69a54eeSKentaro Takeda 
556b69a54eeSKentaro Takeda /**
557b69a54eeSKentaro Takeda  * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
558b69a54eeSKentaro Takeda  *
559b69a54eeSKentaro Takeda  * @pattern:   Pathname pattern that are not rewritable by default.
560b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
561b69a54eeSKentaro Takeda  *
562b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
563fdb8ebb7STetsuo Handa  *
564fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
565b69a54eeSKentaro Takeda  */
566b69a54eeSKentaro Takeda static int tomoyo_update_no_rewrite_entry(const char *pattern,
567b69a54eeSKentaro Takeda 					  const bool is_delete)
568b69a54eeSKentaro Takeda {
569ca0b7df3STetsuo Handa 	struct tomoyo_no_rewrite_entry *ptr;
5709e4b50e9STetsuo Handa 	struct tomoyo_no_rewrite_entry e = { };
571ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
572b69a54eeSKentaro Takeda 
5733f629636STetsuo Handa 	if (!tomoyo_is_correct_word(pattern))
574b69a54eeSKentaro Takeda 		return -EINVAL;
5759e4b50e9STetsuo Handa 	e.pattern = tomoyo_get_name(pattern);
5769e4b50e9STetsuo Handa 	if (!e.pattern)
577ca0b7df3STetsuo Handa 		return error;
57829282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
57929282381STetsuo Handa 		goto out;
58082e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, head.list) {
5819e4b50e9STetsuo Handa 		if (ptr->pattern != e.pattern)
582b69a54eeSKentaro Takeda 			continue;
58382e0f001STetsuo Handa 		ptr->head.is_deleted = is_delete;
584b69a54eeSKentaro Takeda 		error = 0;
585ca0b7df3STetsuo Handa 		break;
586b69a54eeSKentaro Takeda 	}
5879e4b50e9STetsuo Handa 	if (!is_delete && error) {
5889e4b50e9STetsuo Handa 		struct tomoyo_no_rewrite_entry *entry =
5899e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
5909e4b50e9STetsuo Handa 		if (entry) {
59182e0f001STetsuo Handa 			list_add_tail_rcu(&entry->head.list,
5929e4b50e9STetsuo Handa 					  &tomoyo_no_rewrite_list);
593b69a54eeSKentaro Takeda 			error = 0;
594ca0b7df3STetsuo Handa 		}
5959e4b50e9STetsuo Handa 	}
596f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
59729282381STetsuo Handa  out:
5989e4b50e9STetsuo Handa 	tomoyo_put_name(e.pattern);
599b69a54eeSKentaro Takeda 	return error;
600b69a54eeSKentaro Takeda }
601b69a54eeSKentaro Takeda 
602b69a54eeSKentaro Takeda /**
603b69a54eeSKentaro Takeda  * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
604b69a54eeSKentaro Takeda  *
605b69a54eeSKentaro Takeda  * @filename: Filename to check.
606b69a54eeSKentaro Takeda  *
607b69a54eeSKentaro Takeda  * Returns true if @filename is specified by "deny_rewrite" directive,
608b69a54eeSKentaro Takeda  * false otherwise.
609fdb8ebb7STetsuo Handa  *
610fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
611b69a54eeSKentaro Takeda  */
612b69a54eeSKentaro Takeda static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
613b69a54eeSKentaro Takeda {
614b69a54eeSKentaro Takeda 	struct tomoyo_no_rewrite_entry *ptr;
615b69a54eeSKentaro Takeda 	bool found = false;
616b69a54eeSKentaro Takeda 
61782e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, head.list) {
61882e0f001STetsuo Handa 		if (ptr->head.is_deleted)
619b69a54eeSKentaro Takeda 			continue;
620b69a54eeSKentaro Takeda 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
621b69a54eeSKentaro Takeda 			continue;
622b69a54eeSKentaro Takeda 		found = true;
623b69a54eeSKentaro Takeda 		break;
624b69a54eeSKentaro Takeda 	}
625b69a54eeSKentaro Takeda 	return found;
626b69a54eeSKentaro Takeda }
627b69a54eeSKentaro Takeda 
628b69a54eeSKentaro Takeda /**
629b69a54eeSKentaro Takeda  * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
630b69a54eeSKentaro Takeda  *
631b69a54eeSKentaro Takeda  * @data:      String to parse.
632b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
633b69a54eeSKentaro Takeda  *
634b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
635fdb8ebb7STetsuo Handa  *
636fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
637b69a54eeSKentaro Takeda  */
638b69a54eeSKentaro Takeda int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
639b69a54eeSKentaro Takeda {
640b69a54eeSKentaro Takeda 	return tomoyo_update_no_rewrite_entry(data, is_delete);
641b69a54eeSKentaro Takeda }
642b69a54eeSKentaro Takeda 
643b69a54eeSKentaro Takeda /**
644b69a54eeSKentaro Takeda  * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
645b69a54eeSKentaro Takeda  *
646b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
647b69a54eeSKentaro Takeda  *
648b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
649fdb8ebb7STetsuo Handa  *
650fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
651b69a54eeSKentaro Takeda  */
652b69a54eeSKentaro Takeda bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
653b69a54eeSKentaro Takeda {
654b69a54eeSKentaro Takeda 	struct list_head *pos;
655b69a54eeSKentaro Takeda 	bool done = true;
656b69a54eeSKentaro Takeda 
657b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
658b69a54eeSKentaro Takeda 		struct tomoyo_no_rewrite_entry *ptr;
65982e0f001STetsuo Handa 		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry,
66082e0f001STetsuo Handa 				 head.list);
66182e0f001STetsuo Handa 		if (ptr->head.is_deleted)
662b69a54eeSKentaro Takeda 			continue;
6637d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
6647d2948b1STetsuo Handa 					"%s\n", ptr->pattern->name);
6657d2948b1STetsuo Handa 		if (!done)
666b69a54eeSKentaro Takeda 			break;
667b69a54eeSKentaro Takeda 	}
668b69a54eeSKentaro Takeda 	return done;
669b69a54eeSKentaro Takeda }
670b69a54eeSKentaro Takeda 
671b69a54eeSKentaro Takeda /**
672cb0abe6aSTetsuo Handa  * tomoyo_path_acl - Check permission for single path operation.
673b69a54eeSKentaro Takeda  *
674cb0abe6aSTetsuo Handa  * @r:               Pointer to "struct tomoyo_request_info".
675b69a54eeSKentaro Takeda  * @filename:        Filename to check.
676b69a54eeSKentaro Takeda  * @perm:            Permission.
677b69a54eeSKentaro Takeda  *
678b69a54eeSKentaro Takeda  * Returns 0 on success, -EPERM otherwise.
679fdb8ebb7STetsuo Handa  *
680fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
681b69a54eeSKentaro Takeda  */
682cb0abe6aSTetsuo Handa static int tomoyo_path_acl(const struct tomoyo_request_info *r,
6837ef61233STetsuo Handa 			   const struct tomoyo_path_info *filename,
6843f629636STetsuo Handa 			   const u32 perm)
685b69a54eeSKentaro Takeda {
686cb0abe6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
687b69a54eeSKentaro Takeda 	struct tomoyo_acl_info *ptr;
688b69a54eeSKentaro Takeda 	int error = -EPERM;
689b69a54eeSKentaro Takeda 
690fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
6917ef61233STetsuo Handa 		struct tomoyo_path_acl *acl;
6927ef61233STetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_ACL)
693b69a54eeSKentaro Takeda 			continue;
6947ef61233STetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_acl, head);
695a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm) ||
6963f629636STetsuo Handa 		    !tomoyo_compare_name_union(filename, &acl->name))
697b69a54eeSKentaro Takeda 			continue;
698b69a54eeSKentaro Takeda 		error = 0;
699b69a54eeSKentaro Takeda 		break;
700b69a54eeSKentaro Takeda 	}
701b69a54eeSKentaro Takeda 	return error;
702b69a54eeSKentaro Takeda }
703b69a54eeSKentaro Takeda 
704b69a54eeSKentaro Takeda /**
705cb0abe6aSTetsuo Handa  * tomoyo_file_perm - Check permission for opening files.
706b69a54eeSKentaro Takeda  *
707cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
708b69a54eeSKentaro Takeda  * @filename:  Filename to check.
709cb0abe6aSTetsuo Handa  * @mode:      Mode ("read" or "write" or "read/write" or "execute").
710b69a54eeSKentaro Takeda  *
711b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
712fdb8ebb7STetsuo Handa  *
713fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
714b69a54eeSKentaro Takeda  */
715cb0abe6aSTetsuo Handa static int tomoyo_file_perm(struct tomoyo_request_info *r,
716b69a54eeSKentaro Takeda 			    const struct tomoyo_path_info *filename,
717b69a54eeSKentaro Takeda 			    const u8 mode)
718b69a54eeSKentaro Takeda {
719b69a54eeSKentaro Takeda 	const char *msg = "<unknown>";
720b69a54eeSKentaro Takeda 	int error = 0;
721cb0abe6aSTetsuo Handa 	u32 perm = 0;
722b69a54eeSKentaro Takeda 
723b69a54eeSKentaro Takeda 	if (!filename)
724b69a54eeSKentaro Takeda 		return 0;
725cb0abe6aSTetsuo Handa 
726cb0abe6aSTetsuo Handa 	if (mode == 6) {
727cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE);
728cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_READ_WRITE;
729cb0abe6aSTetsuo Handa 	} else if (mode == 4) {
730cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ);
731cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_READ;
732cb0abe6aSTetsuo Handa 	} else if (mode == 2) {
733cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE);
734cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_WRITE;
735cb0abe6aSTetsuo Handa 	} else if (mode == 1) {
736cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE);
737cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_EXECUTE;
738cb0abe6aSTetsuo Handa 	} else
739cb0abe6aSTetsuo Handa 		BUG();
74017fcfbd9STetsuo Handa 	do {
7413f629636STetsuo Handa 		error = tomoyo_path_acl(r, filename, perm);
742cb0abe6aSTetsuo Handa 		if (error && mode == 4 && !r->domain->ignore_global_allow_read
743b69a54eeSKentaro Takeda 		    && tomoyo_is_globally_readable_file(filename))
744b69a54eeSKentaro Takeda 			error = 0;
745b69a54eeSKentaro Takeda 		if (!error)
74617fcfbd9STetsuo Handa 			break;
747cb0abe6aSTetsuo Handa 		tomoyo_warn_log(r, "%s %s", msg, filename->name);
74817fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
74917fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename));
75017fcfbd9STetsuo Handa 		/*
75117fcfbd9STetsuo Handa                  * Do not retry for execute request, for alias may have
75217fcfbd9STetsuo Handa 		 * changed.
75317fcfbd9STetsuo Handa                  */
75417fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST && mode != 1);
75517fcfbd9STetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
75617fcfbd9STetsuo Handa 		error = 0;
757b69a54eeSKentaro Takeda 	return error;
758b69a54eeSKentaro Takeda }
759b69a54eeSKentaro Takeda 
760237ab459STetsuo Handa static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,
761237ab459STetsuo Handa 				 const struct tomoyo_acl_info *b)
762237ab459STetsuo Handa {
763237ab459STetsuo Handa 	const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
764237ab459STetsuo Handa 	const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
765237ab459STetsuo Handa 	return tomoyo_is_same_acl_head(&p1->head, &p2->head) &&
766237ab459STetsuo Handa 		tomoyo_is_same_name_union(&p1->name, &p2->name);
767237ab459STetsuo Handa }
768237ab459STetsuo Handa 
769237ab459STetsuo Handa static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
770237ab459STetsuo Handa 				  struct tomoyo_acl_info *b,
771237ab459STetsuo Handa 				  const bool is_delete)
772237ab459STetsuo Handa {
773237ab459STetsuo Handa 	u16 * const a_perm = &container_of(a, struct tomoyo_path_acl, head)
774237ab459STetsuo Handa 		->perm;
775237ab459STetsuo Handa 	u16 perm = *a_perm;
776237ab459STetsuo Handa 	const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
777237ab459STetsuo Handa 	if (is_delete) {
778237ab459STetsuo Handa 		perm &= ~b_perm;
779237ab459STetsuo Handa 		if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK)
780237ab459STetsuo Handa 			perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
781237ab459STetsuo Handa 		else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE)))
782237ab459STetsuo Handa 			perm &= ~TOMOYO_RW_MASK;
783237ab459STetsuo Handa 	} else {
784237ab459STetsuo Handa 		perm |= b_perm;
785237ab459STetsuo Handa 		if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK)
786237ab459STetsuo Handa 			perm |= (1 << TOMOYO_TYPE_READ_WRITE);
787237ab459STetsuo Handa 		else if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
788237ab459STetsuo Handa 			perm |= TOMOYO_RW_MASK;
789237ab459STetsuo Handa 	}
790237ab459STetsuo Handa 	*a_perm = perm;
791237ab459STetsuo Handa 	return !perm;
792237ab459STetsuo Handa }
793237ab459STetsuo Handa 
794b69a54eeSKentaro Takeda /**
7957ef61233STetsuo Handa  * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
796b69a54eeSKentaro Takeda  *
797b69a54eeSKentaro Takeda  * @type:      Type of operation.
798b69a54eeSKentaro Takeda  * @filename:  Filename.
799b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
800b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
801b69a54eeSKentaro Takeda  *
802b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
803fdb8ebb7STetsuo Handa  *
804fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
805b69a54eeSKentaro Takeda  */
8067ef61233STetsuo Handa static int tomoyo_update_path_acl(const u8 type, const char *filename,
8077ef61233STetsuo Handa 				  struct tomoyo_domain_info * const domain,
8087ef61233STetsuo Handa 				  const bool is_delete)
809b69a54eeSKentaro Takeda {
8109e4b50e9STetsuo Handa 	struct tomoyo_path_acl e = {
8119e4b50e9STetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_ACL,
812237ab459STetsuo Handa 		.perm = 1 << type
8139e4b50e9STetsuo Handa 	};
814237ab459STetsuo Handa 	int error;
815237ab459STetsuo Handa 	if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE))
816237ab459STetsuo Handa 		e.perm |= TOMOYO_RW_MASK;
8177762fbffSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name))
818b69a54eeSKentaro Takeda 		return -EINVAL;
819237ab459STetsuo Handa 	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
820237ab459STetsuo Handa 				     tomoyo_same_path_acl,
821237ab459STetsuo Handa 				     tomoyo_merge_path_acl);
8227762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name);
823b69a54eeSKentaro Takeda 	return error;
824b69a54eeSKentaro Takeda }
825b69a54eeSKentaro Takeda 
826237ab459STetsuo Handa static bool tomoyo_same_path_number3_acl(const struct tomoyo_acl_info *a,
827237ab459STetsuo Handa 					 const struct tomoyo_acl_info *b)
828237ab459STetsuo Handa {
829237ab459STetsuo Handa 	const struct tomoyo_path_number3_acl *p1 = container_of(a, typeof(*p1),
830237ab459STetsuo Handa 								head);
831237ab459STetsuo Handa 	const struct tomoyo_path_number3_acl *p2 = container_of(b, typeof(*p2),
832237ab459STetsuo Handa 								head);
833237ab459STetsuo Handa 	return tomoyo_is_same_acl_head(&p1->head, &p2->head)
834237ab459STetsuo Handa 		&& tomoyo_is_same_name_union(&p1->name, &p2->name)
835237ab459STetsuo Handa 		&& tomoyo_is_same_number_union(&p1->mode, &p2->mode)
836237ab459STetsuo Handa 		&& tomoyo_is_same_number_union(&p1->major, &p2->major)
837237ab459STetsuo Handa 		&& tomoyo_is_same_number_union(&p1->minor, &p2->minor);
838237ab459STetsuo Handa }
839237ab459STetsuo Handa 
840237ab459STetsuo Handa static bool tomoyo_merge_path_number3_acl(struct tomoyo_acl_info *a,
841237ab459STetsuo Handa 					  struct tomoyo_acl_info *b,
842237ab459STetsuo Handa 					  const bool is_delete)
843237ab459STetsuo Handa {
844237ab459STetsuo Handa 	u8 *const a_perm = &container_of(a, struct tomoyo_path_number3_acl,
845237ab459STetsuo Handa 					 head)->perm;
846237ab459STetsuo Handa 	u8 perm = *a_perm;
847237ab459STetsuo Handa 	const u8 b_perm = container_of(b, struct tomoyo_path_number3_acl, head)
848237ab459STetsuo Handa 		->perm;
849237ab459STetsuo Handa 	if (is_delete)
850237ab459STetsuo Handa 		perm &= ~b_perm;
851237ab459STetsuo Handa 	else
852237ab459STetsuo Handa 		perm |= b_perm;
853237ab459STetsuo Handa 	*a_perm = perm;
854237ab459STetsuo Handa 	return !perm;
855237ab459STetsuo Handa }
856237ab459STetsuo Handa 
857b69a54eeSKentaro Takeda /**
858a1f9bb6aSTetsuo Handa  * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list.
859a1f9bb6aSTetsuo Handa  *
860a1f9bb6aSTetsuo Handa  * @type:      Type of operation.
861a1f9bb6aSTetsuo Handa  * @filename:  Filename.
862a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
863a1f9bb6aSTetsuo Handa  * @major:     Device major number.
864a1f9bb6aSTetsuo Handa  * @minor:     Device minor number.
865a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
866a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
867a1f9bb6aSTetsuo Handa  *
868a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
869237ab459STetsuo Handa  *
870237ab459STetsuo Handa  * Caller holds tomoyo_read_lock().
871a1f9bb6aSTetsuo Handa  */
872237ab459STetsuo Handa static int tomoyo_update_path_number3_acl(const u8 type, const char *filename,
873237ab459STetsuo Handa 					  char *mode, char *major, char *minor,
874237ab459STetsuo Handa 					  struct tomoyo_domain_info * const
875237ab459STetsuo Handa 					  domain, const bool is_delete)
876a1f9bb6aSTetsuo Handa {
877a1f9bb6aSTetsuo Handa 	struct tomoyo_path_number3_acl e = {
878a1f9bb6aSTetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL,
879237ab459STetsuo Handa 		.perm = 1 << type
880a1f9bb6aSTetsuo Handa 	};
881a1f9bb6aSTetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
882a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name) ||
883a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(mode, &e.mode) ||
884a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(major, &e.major) ||
885a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(minor, &e.minor))
886a1f9bb6aSTetsuo Handa 		goto out;
887237ab459STetsuo Handa 	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
888237ab459STetsuo Handa 				     tomoyo_same_path_number3_acl,
889237ab459STetsuo Handa 				     tomoyo_merge_path_number3_acl);
890a1f9bb6aSTetsuo Handa  out:
891a1f9bb6aSTetsuo Handa 	tomoyo_put_name_union(&e.name);
892a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.mode);
893a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.major);
894a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.minor);
895a1f9bb6aSTetsuo Handa 	return error;
896a1f9bb6aSTetsuo Handa }
897a1f9bb6aSTetsuo Handa 
898237ab459STetsuo Handa static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,
899237ab459STetsuo Handa 				  const struct tomoyo_acl_info *b)
900237ab459STetsuo Handa {
901237ab459STetsuo Handa 	const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
902237ab459STetsuo Handa 	const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
903237ab459STetsuo Handa 	return tomoyo_is_same_acl_head(&p1->head, &p2->head)
904237ab459STetsuo Handa 		&& tomoyo_is_same_name_union(&p1->name1, &p2->name1)
905237ab459STetsuo Handa 		&& tomoyo_is_same_name_union(&p1->name2, &p2->name2);
906237ab459STetsuo Handa }
907237ab459STetsuo Handa 
908237ab459STetsuo Handa static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
909237ab459STetsuo Handa 				   struct tomoyo_acl_info *b,
910237ab459STetsuo Handa 				   const bool is_delete)
911237ab459STetsuo Handa {
912237ab459STetsuo Handa 	u8 * const a_perm = &container_of(a, struct tomoyo_path2_acl, head)
913237ab459STetsuo Handa 		->perm;
914237ab459STetsuo Handa 	u8 perm = *a_perm;
915237ab459STetsuo Handa 	const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm;
916237ab459STetsuo Handa 	if (is_delete)
917237ab459STetsuo Handa 		perm &= ~b_perm;
918237ab459STetsuo Handa 	else
919237ab459STetsuo Handa 		perm |= b_perm;
920237ab459STetsuo Handa 	*a_perm = perm;
921237ab459STetsuo Handa 	return !perm;
922237ab459STetsuo Handa }
923237ab459STetsuo Handa 
924a1f9bb6aSTetsuo Handa /**
9257ef61233STetsuo Handa  * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
926b69a54eeSKentaro Takeda  *
927b69a54eeSKentaro Takeda  * @type:      Type of operation.
928b69a54eeSKentaro Takeda  * @filename1: First filename.
929b69a54eeSKentaro Takeda  * @filename2: Second filename.
930b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
931b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
932b69a54eeSKentaro Takeda  *
933b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
934fdb8ebb7STetsuo Handa  *
935fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
936b69a54eeSKentaro Takeda  */
9377ef61233STetsuo Handa static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
938b69a54eeSKentaro Takeda 				   const char *filename2,
9397ef61233STetsuo Handa 				   struct tomoyo_domain_info * const domain,
9407ef61233STetsuo Handa 				   const bool is_delete)
941b69a54eeSKentaro Takeda {
9429e4b50e9STetsuo Handa 	struct tomoyo_path2_acl e = {
9439e4b50e9STetsuo Handa 		.head.type = TOMOYO_TYPE_PATH2_ACL,
944237ab459STetsuo Handa 		.perm = 1 << type
9459e4b50e9STetsuo Handa 	};
9469e4b50e9STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
9477762fbffSTetsuo Handa 	if (!tomoyo_parse_name_union(filename1, &e.name1) ||
9487762fbffSTetsuo Handa 	    !tomoyo_parse_name_union(filename2, &e.name2))
949ca0b7df3STetsuo Handa 		goto out;
950237ab459STetsuo Handa 	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
951237ab459STetsuo Handa 				     tomoyo_same_path2_acl,
952237ab459STetsuo Handa 				     tomoyo_merge_path2_acl);
953ca0b7df3STetsuo Handa  out:
9547762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name1);
9557762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name2);
956b69a54eeSKentaro Takeda 	return error;
957b69a54eeSKentaro Takeda }
958b69a54eeSKentaro Takeda 
959b69a54eeSKentaro Takeda /**
960a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_acl - Check permission for path/number/number/number operation.
961a1f9bb6aSTetsuo Handa  *
962a1f9bb6aSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
963a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
964a1f9bb6aSTetsuo Handa  * @perm:     Permission.
965a1f9bb6aSTetsuo Handa  * @mode:     Create mode.
966a1f9bb6aSTetsuo Handa  * @major:    Device major number.
967a1f9bb6aSTetsuo Handa  * @minor:    Device minor number.
968a1f9bb6aSTetsuo Handa  *
969a1f9bb6aSTetsuo Handa  * Returns 0 on success, -EPERM otherwise.
970a1f9bb6aSTetsuo Handa  *
971a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
972a1f9bb6aSTetsuo Handa  */
973a1f9bb6aSTetsuo Handa static int tomoyo_path_number3_acl(struct tomoyo_request_info *r,
974a1f9bb6aSTetsuo Handa 				   const struct tomoyo_path_info *filename,
975a1f9bb6aSTetsuo Handa 				   const u16 perm, const unsigned int mode,
976a1f9bb6aSTetsuo Handa 				   const unsigned int major,
977a1f9bb6aSTetsuo Handa 				   const unsigned int minor)
978a1f9bb6aSTetsuo Handa {
979a1f9bb6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
980a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
981a1f9bb6aSTetsuo Handa 	int error = -EPERM;
982a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
983a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number3_acl *acl;
984a1f9bb6aSTetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL)
985a1f9bb6aSTetsuo Handa 			continue;
986a1f9bb6aSTetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_number3_acl, head);
987a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(mode, &acl->mode))
988a1f9bb6aSTetsuo Handa 			continue;
989a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(major, &acl->major))
990a1f9bb6aSTetsuo Handa 			continue;
991a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(minor, &acl->minor))
992a1f9bb6aSTetsuo Handa 			continue;
993a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm))
994a1f9bb6aSTetsuo Handa 			continue;
995a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_name_union(filename, &acl->name))
996a1f9bb6aSTetsuo Handa 			continue;
997a1f9bb6aSTetsuo Handa 		error = 0;
998a1f9bb6aSTetsuo Handa 		break;
999a1f9bb6aSTetsuo Handa 	}
1000a1f9bb6aSTetsuo Handa 	return error;
1001a1f9bb6aSTetsuo Handa }
1002a1f9bb6aSTetsuo Handa 
1003a1f9bb6aSTetsuo Handa /**
10047ef61233STetsuo Handa  * tomoyo_path2_acl - Check permission for double path operation.
1005b69a54eeSKentaro Takeda  *
1006cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1007b69a54eeSKentaro Takeda  * @type:      Type of operation.
1008b69a54eeSKentaro Takeda  * @filename1: First filename to check.
1009b69a54eeSKentaro Takeda  * @filename2: Second filename to check.
1010b69a54eeSKentaro Takeda  *
1011b69a54eeSKentaro Takeda  * Returns 0 on success, -EPERM otherwise.
1012fdb8ebb7STetsuo Handa  *
1013fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1014b69a54eeSKentaro Takeda  */
1015cb0abe6aSTetsuo Handa static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type,
10167ef61233STetsuo Handa 			    const struct tomoyo_path_info *filename1,
10177ef61233STetsuo Handa 			    const struct tomoyo_path_info *filename2)
1018b69a54eeSKentaro Takeda {
1019cb0abe6aSTetsuo Handa 	const struct tomoyo_domain_info *domain = r->domain;
1020b69a54eeSKentaro Takeda 	struct tomoyo_acl_info *ptr;
1021b69a54eeSKentaro Takeda 	const u8 perm = 1 << type;
1022b69a54eeSKentaro Takeda 	int error = -EPERM;
1023b69a54eeSKentaro Takeda 
1024fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
10257ef61233STetsuo Handa 		struct tomoyo_path2_acl *acl;
10267ef61233STetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
1027b69a54eeSKentaro Takeda 			continue;
10287ef61233STetsuo Handa 		acl = container_of(ptr, struct tomoyo_path2_acl, head);
1029b69a54eeSKentaro Takeda 		if (!(acl->perm & perm))
1030b69a54eeSKentaro Takeda 			continue;
10317762fbffSTetsuo Handa 		if (!tomoyo_compare_name_union(filename1, &acl->name1))
1032b69a54eeSKentaro Takeda 			continue;
10337762fbffSTetsuo Handa 		if (!tomoyo_compare_name_union(filename2, &acl->name2))
1034b69a54eeSKentaro Takeda 			continue;
1035b69a54eeSKentaro Takeda 		error = 0;
1036b69a54eeSKentaro Takeda 		break;
1037b69a54eeSKentaro Takeda 	}
1038b69a54eeSKentaro Takeda 	return error;
1039b69a54eeSKentaro Takeda }
1040b69a54eeSKentaro Takeda 
1041b69a54eeSKentaro Takeda /**
1042cb0abe6aSTetsuo Handa  * tomoyo_path_permission - Check permission for single path operation.
1043b69a54eeSKentaro Takeda  *
1044cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1045b69a54eeSKentaro Takeda  * @operation: Type of operation.
1046b69a54eeSKentaro Takeda  * @filename:  Filename to check.
1047b69a54eeSKentaro Takeda  *
1048b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1049fdb8ebb7STetsuo Handa  *
1050fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1051b69a54eeSKentaro Takeda  */
1052cb0abe6aSTetsuo Handa static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
1053cb0abe6aSTetsuo Handa 				  const struct tomoyo_path_info *filename)
1054b69a54eeSKentaro Takeda {
105517fcfbd9STetsuo Handa 	const char *msg;
1056b69a54eeSKentaro Takeda 	int error;
1057b69a54eeSKentaro Takeda 
1058b69a54eeSKentaro Takeda  next:
105957c2590fSTetsuo Handa 	r->type = tomoyo_p2mac[operation];
106057c2590fSTetsuo Handa 	r->mode = tomoyo_get_mode(r->profile, r->type);
106157c2590fSTetsuo Handa 	if (r->mode == TOMOYO_CONFIG_DISABLED)
106257c2590fSTetsuo Handa 		return 0;
106317fcfbd9STetsuo Handa 	do {
10643f629636STetsuo Handa 		error = tomoyo_path_acl(r, filename, 1 << operation);
1065b69a54eeSKentaro Takeda 		if (!error)
106617fcfbd9STetsuo Handa 			break;
106717fcfbd9STetsuo Handa 		msg = tomoyo_path2keyword(operation);
106817fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s", msg, filename->name);
106917fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
107017fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename));
107117fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1072cb0abe6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1073b69a54eeSKentaro Takeda 		error = 0;
1074b69a54eeSKentaro Takeda 	/*
1075b69a54eeSKentaro Takeda 	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
1076b69a54eeSKentaro Takeda 	 * we need to check "allow_rewrite" permission if the filename is
1077b69a54eeSKentaro Takeda 	 * specified by "deny_rewrite" keyword.
1078b69a54eeSKentaro Takeda 	 */
10797ef61233STetsuo Handa 	if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
1080b69a54eeSKentaro Takeda 	    tomoyo_is_no_rewrite_file(filename)) {
10817ef61233STetsuo Handa 		operation = TOMOYO_TYPE_REWRITE;
1082b69a54eeSKentaro Takeda 		goto next;
1083b69a54eeSKentaro Takeda 	}
1084b69a54eeSKentaro Takeda 	return error;
1085b69a54eeSKentaro Takeda }
1086b69a54eeSKentaro Takeda 
1087b69a54eeSKentaro Takeda /**
1088a1f9bb6aSTetsuo Handa  * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation.
1089a1f9bb6aSTetsuo Handa  *
1090a1f9bb6aSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
1091a1f9bb6aSTetsuo Handa  * @type:     Operation.
1092a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1093a1f9bb6aSTetsuo Handa  * @number:   Number.
1094a1f9bb6aSTetsuo Handa  *
1095a1f9bb6aSTetsuo Handa  * Returns 0 on success, -EPERM otherwise.
1096a1f9bb6aSTetsuo Handa  *
1097a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1098a1f9bb6aSTetsuo Handa  */
1099a1f9bb6aSTetsuo Handa static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type,
1100a1f9bb6aSTetsuo Handa 				  const struct tomoyo_path_info *filename,
1101a1f9bb6aSTetsuo Handa 				  const unsigned long number)
1102a1f9bb6aSTetsuo Handa {
1103a1f9bb6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
1104a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
1105a1f9bb6aSTetsuo Handa 	const u8 perm = 1 << type;
1106a1f9bb6aSTetsuo Handa 	int error = -EPERM;
1107a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1108a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number_acl *acl;
1109a1f9bb6aSTetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL)
1110a1f9bb6aSTetsuo Handa 			continue;
1111a1f9bb6aSTetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_number_acl,
1112a1f9bb6aSTetsuo Handa 				   head);
1113a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm) ||
1114a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_number_union(number, &acl->number) ||
1115a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_name_union(filename, &acl->name))
1116a1f9bb6aSTetsuo Handa 			continue;
1117a1f9bb6aSTetsuo Handa 		error = 0;
1118a1f9bb6aSTetsuo Handa 		break;
1119a1f9bb6aSTetsuo Handa 	}
1120a1f9bb6aSTetsuo Handa 	return error;
1121a1f9bb6aSTetsuo Handa }
1122a1f9bb6aSTetsuo Handa 
1123237ab459STetsuo Handa static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
1124237ab459STetsuo Handa 					const struct tomoyo_acl_info *b)
1125237ab459STetsuo Handa {
1126237ab459STetsuo Handa 	const struct tomoyo_path_number_acl *p1 = container_of(a, typeof(*p1),
1127237ab459STetsuo Handa 							       head);
1128237ab459STetsuo Handa 	const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
1129237ab459STetsuo Handa 							       head);
1130237ab459STetsuo Handa 	return tomoyo_is_same_acl_head(&p1->head, &p2->head)
1131237ab459STetsuo Handa 		&& tomoyo_is_same_name_union(&p1->name, &p2->name)
1132237ab459STetsuo Handa 		&& tomoyo_is_same_number_union(&p1->number, &p2->number);
1133237ab459STetsuo Handa }
1134237ab459STetsuo Handa 
1135237ab459STetsuo Handa static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
1136237ab459STetsuo Handa 					 struct tomoyo_acl_info *b,
1137237ab459STetsuo Handa 					 const bool is_delete)
1138237ab459STetsuo Handa {
1139237ab459STetsuo Handa 	u8 * const a_perm = &container_of(a, struct tomoyo_path_number_acl,
1140237ab459STetsuo Handa 					  head)->perm;
1141237ab459STetsuo Handa 	u8 perm = *a_perm;
1142237ab459STetsuo Handa 	const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head)
1143237ab459STetsuo Handa 		->perm;
1144237ab459STetsuo Handa 	if (is_delete)
1145237ab459STetsuo Handa 		perm &= ~b_perm;
1146237ab459STetsuo Handa 	else
1147237ab459STetsuo Handa 		perm |= b_perm;
1148237ab459STetsuo Handa 	*a_perm = perm;
1149237ab459STetsuo Handa 	return !perm;
1150237ab459STetsuo Handa }
1151237ab459STetsuo Handa 
1152a1f9bb6aSTetsuo Handa /**
1153a1f9bb6aSTetsuo Handa  * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
1154a1f9bb6aSTetsuo Handa  *
1155a1f9bb6aSTetsuo Handa  * @type:      Type of operation.
1156a1f9bb6aSTetsuo Handa  * @filename:  Filename.
1157a1f9bb6aSTetsuo Handa  * @number:    Number.
1158a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
1159a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
1160a1f9bb6aSTetsuo Handa  *
1161a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1162a1f9bb6aSTetsuo Handa  */
1163237ab459STetsuo Handa static int tomoyo_update_path_number_acl(const u8 type, const char *filename,
1164a1f9bb6aSTetsuo Handa 					 char *number,
1165237ab459STetsuo Handa 					 struct tomoyo_domain_info * const
1166237ab459STetsuo Handa 					 domain,
1167a1f9bb6aSTetsuo Handa 					 const bool is_delete)
1168a1f9bb6aSTetsuo Handa {
1169a1f9bb6aSTetsuo Handa 	struct tomoyo_path_number_acl e = {
1170a1f9bb6aSTetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
1171237ab459STetsuo Handa 		.perm = 1 << type
1172a1f9bb6aSTetsuo Handa 	};
1173a1f9bb6aSTetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
1174a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name))
1175a1f9bb6aSTetsuo Handa 		return -EINVAL;
1176a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_number_union(number, &e.number))
1177a1f9bb6aSTetsuo Handa 		goto out;
1178237ab459STetsuo Handa 	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
1179237ab459STetsuo Handa 				     tomoyo_same_path_number_acl,
1180237ab459STetsuo Handa 				     tomoyo_merge_path_number_acl);
1181a1f9bb6aSTetsuo Handa  out:
1182a1f9bb6aSTetsuo Handa 	tomoyo_put_name_union(&e.name);
1183a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.number);
1184a1f9bb6aSTetsuo Handa 	return error;
1185a1f9bb6aSTetsuo Handa }
1186a1f9bb6aSTetsuo Handa 
1187a1f9bb6aSTetsuo Handa /**
1188a1f9bb6aSTetsuo Handa  * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
1189a1f9bb6aSTetsuo Handa  *
1190a1f9bb6aSTetsuo Handa  * @r:        Pointer to "strct tomoyo_request_info".
1191a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1192a1f9bb6aSTetsuo Handa  * @number:   Number.
1193a1f9bb6aSTetsuo Handa  *
1194a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1195a1f9bb6aSTetsuo Handa  *
1196a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1197a1f9bb6aSTetsuo Handa  */
1198a1f9bb6aSTetsuo Handa static int tomoyo_path_number_perm2(struct tomoyo_request_info *r,
1199a1f9bb6aSTetsuo Handa 				    const u8 type,
1200a1f9bb6aSTetsuo Handa 				    const struct tomoyo_path_info *filename,
1201a1f9bb6aSTetsuo Handa 				    const unsigned long number)
1202a1f9bb6aSTetsuo Handa {
1203a1f9bb6aSTetsuo Handa 	char buffer[64];
1204a1f9bb6aSTetsuo Handa 	int error;
1205a1f9bb6aSTetsuo Handa 	u8 radix;
120617fcfbd9STetsuo Handa 	const char *msg;
1207a1f9bb6aSTetsuo Handa 
1208a1f9bb6aSTetsuo Handa 	if (!filename)
1209a1f9bb6aSTetsuo Handa 		return 0;
1210a1f9bb6aSTetsuo Handa 	switch (type) {
1211a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_CREATE:
1212a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKDIR:
1213a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKFIFO:
1214a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKSOCK:
1215a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_CHMOD:
1216a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_OCTAL;
1217a1f9bb6aSTetsuo Handa 		break;
1218a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_IOCTL:
1219a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_HEXADECIMAL;
1220a1f9bb6aSTetsuo Handa 		break;
1221a1f9bb6aSTetsuo Handa 	default:
1222a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_DECIMAL;
1223a1f9bb6aSTetsuo Handa 		break;
1224a1f9bb6aSTetsuo Handa 	}
1225a1f9bb6aSTetsuo Handa 	tomoyo_print_ulong(buffer, sizeof(buffer), number, radix);
122617fcfbd9STetsuo Handa 	do {
1227a1f9bb6aSTetsuo Handa 		error = tomoyo_path_number_acl(r, type, filename, number);
1228a1f9bb6aSTetsuo Handa 		if (!error)
122917fcfbd9STetsuo Handa 			break;
123017fcfbd9STetsuo Handa 		msg = tomoyo_path_number2keyword(type);
123117fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s %s", msg, filename->name, buffer);
123217fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg,
123317fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename),
123417fcfbd9STetsuo Handa 					  buffer);
123517fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1236a1f9bb6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1237a1f9bb6aSTetsuo Handa 		error = 0;
1238a1f9bb6aSTetsuo Handa 	return error;
1239a1f9bb6aSTetsuo Handa }
1240a1f9bb6aSTetsuo Handa 
1241a1f9bb6aSTetsuo Handa /**
1242a1f9bb6aSTetsuo Handa  * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
1243a1f9bb6aSTetsuo Handa  *
1244a1f9bb6aSTetsuo Handa  * @type:   Type of operation.
1245a1f9bb6aSTetsuo Handa  * @path:   Pointer to "struct path".
1246a1f9bb6aSTetsuo Handa  * @number: Number.
1247a1f9bb6aSTetsuo Handa  *
1248a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1249a1f9bb6aSTetsuo Handa  */
1250a1f9bb6aSTetsuo Handa int tomoyo_path_number_perm(const u8 type, struct path *path,
1251a1f9bb6aSTetsuo Handa 			    unsigned long number)
1252a1f9bb6aSTetsuo Handa {
1253a1f9bb6aSTetsuo Handa 	struct tomoyo_request_info r;
1254a1f9bb6aSTetsuo Handa 	int error = -ENOMEM;
1255c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1256a1f9bb6aSTetsuo Handa 	int idx;
1257a1f9bb6aSTetsuo Handa 
125857c2590fSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
125957c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry)
1260a1f9bb6aSTetsuo Handa 		return 0;
1261a1f9bb6aSTetsuo Handa 	idx = tomoyo_read_lock();
1262c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1263a1f9bb6aSTetsuo Handa 		goto out;
1264c8c57e84STetsuo Handa 	if (type == TOMOYO_TYPE_MKDIR)
1265c8c57e84STetsuo Handa 		tomoyo_add_slash(&buf);
1266c8c57e84STetsuo Handa 	error = tomoyo_path_number_perm2(&r, type, &buf, number);
1267a1f9bb6aSTetsuo Handa  out:
1268c8c57e84STetsuo Handa 	kfree(buf.name);
1269a1f9bb6aSTetsuo Handa 	tomoyo_read_unlock(idx);
1270a1f9bb6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1271a1f9bb6aSTetsuo Handa 		error = 0;
1272a1f9bb6aSTetsuo Handa 	return error;
1273a1f9bb6aSTetsuo Handa }
1274a1f9bb6aSTetsuo Handa 
1275a1f9bb6aSTetsuo Handa /**
1276b69a54eeSKentaro Takeda  * tomoyo_check_exec_perm - Check permission for "execute".
1277b69a54eeSKentaro Takeda  *
127857c2590fSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
1279b69a54eeSKentaro Takeda  * @filename: Check permission for "execute".
1280b69a54eeSKentaro Takeda  *
1281b69a54eeSKentaro Takeda  * Returns 0 on success, negativevalue otherwise.
1282fdb8ebb7STetsuo Handa  *
1283fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1284b69a54eeSKentaro Takeda  */
128557c2590fSTetsuo Handa int tomoyo_check_exec_perm(struct tomoyo_request_info *r,
1286bcb86975STetsuo Handa 			   const struct tomoyo_path_info *filename)
1287b69a54eeSKentaro Takeda {
128857c2590fSTetsuo Handa 	if (r->mode == TOMOYO_CONFIG_DISABLED)
1289b69a54eeSKentaro Takeda 		return 0;
129057c2590fSTetsuo Handa 	return tomoyo_file_perm(r, filename, 1);
1291b69a54eeSKentaro Takeda }
1292b69a54eeSKentaro Takeda 
1293b69a54eeSKentaro Takeda /**
1294b69a54eeSKentaro Takeda  * tomoyo_check_open_permission - Check permission for "read" and "write".
1295b69a54eeSKentaro Takeda  *
1296b69a54eeSKentaro Takeda  * @domain: Pointer to "struct tomoyo_domain_info".
1297b69a54eeSKentaro Takeda  * @path:   Pointer to "struct path".
1298b69a54eeSKentaro Takeda  * @flag:   Flags for open().
1299b69a54eeSKentaro Takeda  *
1300b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1301b69a54eeSKentaro Takeda  */
1302b69a54eeSKentaro Takeda int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
1303b69a54eeSKentaro Takeda 				 struct path *path, const int flag)
1304b69a54eeSKentaro Takeda {
1305b69a54eeSKentaro Takeda 	const u8 acc_mode = ACC_MODE(flag);
1306b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1307c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1308cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1309fdb8ebb7STetsuo Handa 	int idx;
1310b69a54eeSKentaro Takeda 
131157c2590fSTetsuo Handa 	if (!path->mnt ||
131257c2590fSTetsuo Handa 	    (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)))
1313b69a54eeSKentaro Takeda 		return 0;
131457c2590fSTetsuo Handa 	buf.name = NULL;
131557c2590fSTetsuo Handa 	r.mode = TOMOYO_CONFIG_DISABLED;
1316fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1317c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1318b69a54eeSKentaro Takeda 		goto out;
1319b69a54eeSKentaro Takeda 	error = 0;
1320b69a54eeSKentaro Takeda 	/*
1321b69a54eeSKentaro Takeda 	 * If the filename is specified by "deny_rewrite" keyword,
1322b69a54eeSKentaro Takeda 	 * we need to check "allow_rewrite" permission when the filename is not
1323b69a54eeSKentaro Takeda 	 * opened for append mode or the filename is truncated at open time.
1324b69a54eeSKentaro Takeda 	 */
132557c2590fSTetsuo Handa 	if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
132657c2590fSTetsuo Handa 	    && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE)
132757c2590fSTetsuo Handa 	    != TOMOYO_CONFIG_DISABLED) {
132857c2590fSTetsuo Handa 		if (!tomoyo_get_realpath(&buf, path)) {
132957c2590fSTetsuo Handa 			error = -ENOMEM;
133057c2590fSTetsuo Handa 			goto out;
1331b69a54eeSKentaro Takeda 		}
133257c2590fSTetsuo Handa 		if (tomoyo_is_no_rewrite_file(&buf))
133357c2590fSTetsuo Handa 			error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
133457c2590fSTetsuo Handa 						       &buf);
133557c2590fSTetsuo Handa 	}
133657c2590fSTetsuo Handa 	if (!error && acc_mode &&
133757c2590fSTetsuo Handa 	    tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
133857c2590fSTetsuo Handa 	    != TOMOYO_CONFIG_DISABLED) {
133957c2590fSTetsuo Handa 		if (!buf.name && !tomoyo_get_realpath(&buf, path)) {
134057c2590fSTetsuo Handa 			error = -ENOMEM;
134157c2590fSTetsuo Handa 			goto out;
134257c2590fSTetsuo Handa 		}
1343c8c57e84STetsuo Handa 		error = tomoyo_file_perm(&r, &buf, acc_mode);
134457c2590fSTetsuo Handa 	}
1345b69a54eeSKentaro Takeda  out:
1346c8c57e84STetsuo Handa 	kfree(buf.name);
1347fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1348cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1349b69a54eeSKentaro Takeda 		error = 0;
1350b69a54eeSKentaro Takeda 	return error;
1351b69a54eeSKentaro Takeda }
1352b69a54eeSKentaro Takeda 
1353b69a54eeSKentaro Takeda /**
13542106ccd9STetsuo Handa  * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
1355b69a54eeSKentaro Takeda  *
1356b69a54eeSKentaro Takeda  * @operation: Type of operation.
1357b69a54eeSKentaro Takeda  * @path:      Pointer to "struct path".
1358b69a54eeSKentaro Takeda  *
1359b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1360b69a54eeSKentaro Takeda  */
136197d6931eSTetsuo Handa int tomoyo_path_perm(const u8 operation, struct path *path)
1362b69a54eeSKentaro Takeda {
1363b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1364c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1365cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1366fdb8ebb7STetsuo Handa 	int idx;
1367b69a54eeSKentaro Takeda 
136857c2590fSTetsuo Handa 	if (!path->mnt)
1369b69a54eeSKentaro Takeda 		return 0;
137057c2590fSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
137157c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED)
137257c2590fSTetsuo Handa 		return 0;
137357c2590fSTetsuo Handa 	buf.name = NULL;
1374fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1375c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1376b69a54eeSKentaro Takeda 		goto out;
1377b69a54eeSKentaro Takeda 	switch (operation) {
1378cb0abe6aSTetsuo Handa 	case TOMOYO_TYPE_REWRITE:
1379c8c57e84STetsuo Handa 		if (!tomoyo_is_no_rewrite_file(&buf)) {
1380cb0abe6aSTetsuo Handa 			error = 0;
1381cb0abe6aSTetsuo Handa 			goto out;
1382cb0abe6aSTetsuo Handa 		}
1383cb0abe6aSTetsuo Handa 		break;
13847ef61233STetsuo Handa 	case TOMOYO_TYPE_RMDIR:
13857ef61233STetsuo Handa 	case TOMOYO_TYPE_CHROOT:
138657c2590fSTetsuo Handa 	case TOMOYO_TYPE_UMOUNT:
1387c8c57e84STetsuo Handa 		tomoyo_add_slash(&buf);
1388c8c57e84STetsuo Handa 		break;
1389b69a54eeSKentaro Takeda 	}
1390c8c57e84STetsuo Handa 	error = tomoyo_path_permission(&r, operation, &buf);
1391b69a54eeSKentaro Takeda  out:
1392c8c57e84STetsuo Handa 	kfree(buf.name);
1393fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1394cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1395b69a54eeSKentaro Takeda 		error = 0;
1396b69a54eeSKentaro Takeda 	return error;
1397b69a54eeSKentaro Takeda }
1398b69a54eeSKentaro Takeda 
1399b69a54eeSKentaro Takeda /**
1400a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_perm2 - Check permission for path/number/number/number operation.
1401a1f9bb6aSTetsuo Handa  *
1402a1f9bb6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1403a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
1404a1f9bb6aSTetsuo Handa  * @filename:  Filename to check.
1405a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
1406a1f9bb6aSTetsuo Handa  * @dev:       Device number.
1407a1f9bb6aSTetsuo Handa  *
1408a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1409a1f9bb6aSTetsuo Handa  *
1410a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1411a1f9bb6aSTetsuo Handa  */
1412a1f9bb6aSTetsuo Handa static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r,
1413a1f9bb6aSTetsuo Handa 				     const u8 operation,
1414a1f9bb6aSTetsuo Handa 				     const struct tomoyo_path_info *filename,
1415a1f9bb6aSTetsuo Handa 				     const unsigned int mode,
1416a1f9bb6aSTetsuo Handa 				     const unsigned int dev)
1417a1f9bb6aSTetsuo Handa {
1418a1f9bb6aSTetsuo Handa 	int error;
141917fcfbd9STetsuo Handa 	const char *msg;
1420a1f9bb6aSTetsuo Handa 	const unsigned int major = MAJOR(dev);
1421a1f9bb6aSTetsuo Handa 	const unsigned int minor = MINOR(dev);
1422a1f9bb6aSTetsuo Handa 
142317fcfbd9STetsuo Handa 	do {
142417fcfbd9STetsuo Handa 		error = tomoyo_path_number3_acl(r, filename, 1 << operation,
142517fcfbd9STetsuo Handa 						mode, major, minor);
1426a1f9bb6aSTetsuo Handa 		if (!error)
142717fcfbd9STetsuo Handa 			break;
142817fcfbd9STetsuo Handa 		msg = tomoyo_path_number32keyword(operation);
142917fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s 0%o %u %u", msg, filename->name,
143017fcfbd9STetsuo Handa 				mode, major, minor);
143117fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg,
143217fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename), mode,
143317fcfbd9STetsuo Handa 					  major, minor);
143417fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1435a1f9bb6aSTetsuo Handa         if (r->mode != TOMOYO_CONFIG_ENFORCING)
1436a1f9bb6aSTetsuo Handa 		error = 0;
1437a1f9bb6aSTetsuo Handa 	return error;
1438a1f9bb6aSTetsuo Handa }
1439a1f9bb6aSTetsuo Handa 
1440a1f9bb6aSTetsuo Handa /**
1441a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar".
1442a1f9bb6aSTetsuo Handa  *
1443a1f9bb6aSTetsuo Handa  * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK)
1444a1f9bb6aSTetsuo Handa  * @path:      Pointer to "struct path".
1445a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
1446a1f9bb6aSTetsuo Handa  * @dev:       Device number.
1447a1f9bb6aSTetsuo Handa  *
1448a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1449a1f9bb6aSTetsuo Handa  */
1450a1f9bb6aSTetsuo Handa int tomoyo_path_number3_perm(const u8 operation, struct path *path,
1451a1f9bb6aSTetsuo Handa 			     const unsigned int mode, unsigned int dev)
1452a1f9bb6aSTetsuo Handa {
1453a1f9bb6aSTetsuo Handa 	struct tomoyo_request_info r;
1454a1f9bb6aSTetsuo Handa 	int error = -ENOMEM;
1455c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1456a1f9bb6aSTetsuo Handa 	int idx;
1457a1f9bb6aSTetsuo Handa 
145857c2590fSTetsuo Handa 	if (!path->mnt ||
145957c2590fSTetsuo Handa 	    tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
146057c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED)
1461a1f9bb6aSTetsuo Handa 		return 0;
1462a1f9bb6aSTetsuo Handa 	idx = tomoyo_read_lock();
1463a1f9bb6aSTetsuo Handa 	error = -ENOMEM;
1464c8c57e84STetsuo Handa 	if (tomoyo_get_realpath(&buf, path)) {
1465c8c57e84STetsuo Handa 		error = tomoyo_path_number3_perm2(&r, operation, &buf, mode,
1466a1f9bb6aSTetsuo Handa 						  new_decode_dev(dev));
1467c8c57e84STetsuo Handa 		kfree(buf.name);
1468a1f9bb6aSTetsuo Handa 	}
1469a1f9bb6aSTetsuo Handa 	tomoyo_read_unlock(idx);
1470a1f9bb6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1471a1f9bb6aSTetsuo Handa 		error = 0;
1472a1f9bb6aSTetsuo Handa 	return error;
1473a1f9bb6aSTetsuo Handa }
1474a1f9bb6aSTetsuo Handa 
1475a1f9bb6aSTetsuo Handa /**
14767ef61233STetsuo Handa  * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
1477b69a54eeSKentaro Takeda  *
1478b69a54eeSKentaro Takeda  * @operation: Type of operation.
1479b69a54eeSKentaro Takeda  * @path1:      Pointer to "struct path".
1480b69a54eeSKentaro Takeda  * @path2:      Pointer to "struct path".
1481b69a54eeSKentaro Takeda  *
1482b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1483b69a54eeSKentaro Takeda  */
148497d6931eSTetsuo Handa int tomoyo_path2_perm(const u8 operation, struct path *path1,
1485b69a54eeSKentaro Takeda 		      struct path *path2)
1486b69a54eeSKentaro Takeda {
1487b69a54eeSKentaro Takeda 	int error = -ENOMEM;
148817fcfbd9STetsuo Handa 	const char *msg;
1489c8c57e84STetsuo Handa 	struct tomoyo_path_info buf1;
1490c8c57e84STetsuo Handa 	struct tomoyo_path_info buf2;
1491cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1492fdb8ebb7STetsuo Handa 	int idx;
1493b69a54eeSKentaro Takeda 
149457c2590fSTetsuo Handa 	if (!path1->mnt || !path2->mnt ||
149557c2590fSTetsuo Handa 	    tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
149657c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED)
1497b69a54eeSKentaro Takeda 		return 0;
1498c8c57e84STetsuo Handa 	buf1.name = NULL;
1499c8c57e84STetsuo Handa 	buf2.name = NULL;
1500fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1501c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf1, path1) ||
1502c8c57e84STetsuo Handa 	    !tomoyo_get_realpath(&buf2, path2))
1503b69a54eeSKentaro Takeda 		goto out;
150457c2590fSTetsuo Handa 	switch (operation) {
150557c2590fSTetsuo Handa 		struct dentry *dentry;
150657c2590fSTetsuo Handa 	case TOMOYO_TYPE_RENAME:
150757c2590fSTetsuo Handa         case TOMOYO_TYPE_LINK:
150857c2590fSTetsuo Handa 		dentry = path1->dentry;
150957c2590fSTetsuo Handa 	        if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
151057c2590fSTetsuo Handa                         break;
151157c2590fSTetsuo Handa                 /* fall through */
151257c2590fSTetsuo Handa         case TOMOYO_TYPE_PIVOT_ROOT:
1513c8c57e84STetsuo Handa                 tomoyo_add_slash(&buf1);
1514c8c57e84STetsuo Handa                 tomoyo_add_slash(&buf2);
151557c2590fSTetsuo Handa 		break;
1516b69a54eeSKentaro Takeda         }
151717fcfbd9STetsuo Handa 	do {
1518c8c57e84STetsuo Handa 		error = tomoyo_path2_acl(&r, operation, &buf1, &buf2);
1519b69a54eeSKentaro Takeda 		if (!error)
152017fcfbd9STetsuo Handa 			break;
152117fcfbd9STetsuo Handa 		msg = tomoyo_path22keyword(operation);
1522c8c57e84STetsuo Handa 		tomoyo_warn_log(&r, "%s %s %s", msg, buf1.name, buf2.name);
152317fcfbd9STetsuo Handa 		error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg,
1524c8c57e84STetsuo Handa 					  tomoyo_file_pattern(&buf1),
1525c8c57e84STetsuo Handa 					  tomoyo_file_pattern(&buf2));
152617fcfbd9STetsuo Handa         } while (error == TOMOYO_RETRY_REQUEST);
1527b69a54eeSKentaro Takeda  out:
1528c8c57e84STetsuo Handa 	kfree(buf1.name);
1529c8c57e84STetsuo Handa 	kfree(buf2.name);
1530fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1531cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1532b69a54eeSKentaro Takeda 		error = 0;
1533b69a54eeSKentaro Takeda 	return error;
1534b69a54eeSKentaro Takeda }
1535a1f9bb6aSTetsuo Handa 
1536a1f9bb6aSTetsuo Handa /**
1537a1f9bb6aSTetsuo Handa  * tomoyo_write_file_policy - Update file related list.
1538a1f9bb6aSTetsuo Handa  *
1539a1f9bb6aSTetsuo Handa  * @data:      String to parse.
1540a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
1541a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
1542a1f9bb6aSTetsuo Handa  *
1543a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1544a1f9bb6aSTetsuo Handa  *
1545a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1546a1f9bb6aSTetsuo Handa  */
1547a1f9bb6aSTetsuo Handa int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
1548a1f9bb6aSTetsuo Handa 			     const bool is_delete)
1549a1f9bb6aSTetsuo Handa {
1550a1f9bb6aSTetsuo Handa 	char *w[5];
1551a1f9bb6aSTetsuo Handa 	u8 type;
1552a1f9bb6aSTetsuo Handa 	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
1553a1f9bb6aSTetsuo Handa 		return -EINVAL;
1554237ab459STetsuo Handa 	if (strncmp(w[0], "allow_", 6))
1555a1f9bb6aSTetsuo Handa 		goto out;
1556a1f9bb6aSTetsuo Handa 	w[0] += 6;
1557a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
1558a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_keyword[type]))
1559a1f9bb6aSTetsuo Handa 			continue;
1560a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_acl(type, w[1], domain, is_delete);
1561a1f9bb6aSTetsuo Handa 	}
1562a1f9bb6aSTetsuo Handa 	if (!w[2][0])
1563a1f9bb6aSTetsuo Handa 		goto out;
1564a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
1565a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path2_keyword[type]))
1566a1f9bb6aSTetsuo Handa 			continue;
1567a1f9bb6aSTetsuo Handa 		return tomoyo_update_path2_acl(type, w[1], w[2], domain,
1568a1f9bb6aSTetsuo Handa 					       is_delete);
1569a1f9bb6aSTetsuo Handa 	}
1570a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
1571a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_number_keyword[type]))
1572a1f9bb6aSTetsuo Handa 			continue;
1573a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
1574a1f9bb6aSTetsuo Handa 						     is_delete);
1575a1f9bb6aSTetsuo Handa 	}
1576a1f9bb6aSTetsuo Handa 	if (!w[3][0] || !w[4][0])
1577a1f9bb6aSTetsuo Handa 		goto out;
1578a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER3_OPERATION; type++) {
1579a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_number3_keyword[type]))
1580a1f9bb6aSTetsuo Handa 			continue;
1581a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_number3_acl(type, w[1], w[2], w[3],
1582a1f9bb6aSTetsuo Handa 						      w[4], domain, is_delete);
1583a1f9bb6aSTetsuo Handa 	}
1584a1f9bb6aSTetsuo Handa  out:
1585a1f9bb6aSTetsuo Handa 	return -EINVAL;
1586a1f9bb6aSTetsuo Handa }
1587