xref: /openbmc/linux/security/tomoyo/file.c (revision 36f5e1ff)
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 
25636f5e1ffSTetsuo Handa static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a,
25736f5e1ffSTetsuo Handa 					  const struct tomoyo_acl_head *b)
25836f5e1ffSTetsuo Handa {
25936f5e1ffSTetsuo Handa 	return container_of(a, struct tomoyo_globally_readable_file_entry,
26036f5e1ffSTetsuo Handa 			    head)->filename ==
26136f5e1ffSTetsuo Handa 		container_of(b, struct tomoyo_globally_readable_file_entry,
26236f5e1ffSTetsuo Handa 			     head)->filename;
26336f5e1ffSTetsuo Handa }
26436f5e1ffSTetsuo Handa 
265b69a54eeSKentaro Takeda /**
266b69a54eeSKentaro Takeda  * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
267b69a54eeSKentaro Takeda  *
268b69a54eeSKentaro Takeda  * @filename:  Filename unconditionally permitted to open() for reading.
269b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
270b69a54eeSKentaro Takeda  *
271b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
272fdb8ebb7STetsuo Handa  *
273fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
274b69a54eeSKentaro Takeda  */
275b69a54eeSKentaro Takeda static int tomoyo_update_globally_readable_entry(const char *filename,
276b69a54eeSKentaro Takeda 						 const bool is_delete)
277b69a54eeSKentaro Takeda {
2789e4b50e9STetsuo Handa 	struct tomoyo_globally_readable_file_entry e = { };
27936f5e1ffSTetsuo Handa 	int error;
280b69a54eeSKentaro Takeda 
2813f629636STetsuo Handa 	if (!tomoyo_is_correct_word(filename))
282b69a54eeSKentaro Takeda 		return -EINVAL;
2839e4b50e9STetsuo Handa 	e.filename = tomoyo_get_name(filename);
2849e4b50e9STetsuo Handa 	if (!e.filename)
285b69a54eeSKentaro Takeda 		return -ENOMEM;
28636f5e1ffSTetsuo Handa 	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
28736f5e1ffSTetsuo Handa 				     &tomoyo_globally_readable_list,
28836f5e1ffSTetsuo Handa 				     tomoyo_same_globally_readable);
2899e4b50e9STetsuo Handa 	tomoyo_put_name(e.filename);
290b69a54eeSKentaro Takeda 	return error;
291b69a54eeSKentaro Takeda }
292b69a54eeSKentaro Takeda 
293b69a54eeSKentaro Takeda /**
294b69a54eeSKentaro Takeda  * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
295b69a54eeSKentaro Takeda  *
296b69a54eeSKentaro Takeda  * @filename: The filename to check.
297b69a54eeSKentaro Takeda  *
298b69a54eeSKentaro Takeda  * Returns true if any domain can open @filename for reading, false otherwise.
299fdb8ebb7STetsuo Handa  *
300fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
301b69a54eeSKentaro Takeda  */
302b69a54eeSKentaro Takeda static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
303b69a54eeSKentaro Takeda 					     filename)
304b69a54eeSKentaro Takeda {
305b69a54eeSKentaro Takeda 	struct tomoyo_globally_readable_file_entry *ptr;
306b69a54eeSKentaro Takeda 	bool found = false;
307fdb8ebb7STetsuo Handa 
30882e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list,
30982e0f001STetsuo Handa 				head.list) {
31082e0f001STetsuo Handa 		if (!ptr->head.is_deleted &&
311b69a54eeSKentaro Takeda 		    tomoyo_path_matches_pattern(filename, ptr->filename)) {
312b69a54eeSKentaro Takeda 			found = true;
313b69a54eeSKentaro Takeda 			break;
314b69a54eeSKentaro Takeda 		}
315b69a54eeSKentaro Takeda 	}
316b69a54eeSKentaro Takeda 	return found;
317b69a54eeSKentaro Takeda }
318b69a54eeSKentaro Takeda 
319b69a54eeSKentaro Takeda /**
320b69a54eeSKentaro Takeda  * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
321b69a54eeSKentaro Takeda  *
322b69a54eeSKentaro Takeda  * @data:      String to parse.
323b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
324b69a54eeSKentaro Takeda  *
325b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
326fdb8ebb7STetsuo Handa  *
327fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
328b69a54eeSKentaro Takeda  */
329b69a54eeSKentaro Takeda int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
330b69a54eeSKentaro Takeda {
331b69a54eeSKentaro Takeda 	return tomoyo_update_globally_readable_entry(data, is_delete);
332b69a54eeSKentaro Takeda }
333b69a54eeSKentaro Takeda 
334b69a54eeSKentaro Takeda /**
335b69a54eeSKentaro Takeda  * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
336b69a54eeSKentaro Takeda  *
337b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
338b69a54eeSKentaro Takeda  *
339b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
340fdb8ebb7STetsuo Handa  *
341fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
342b69a54eeSKentaro Takeda  */
343b69a54eeSKentaro Takeda bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
344b69a54eeSKentaro Takeda {
345b69a54eeSKentaro Takeda 	struct list_head *pos;
346b69a54eeSKentaro Takeda 	bool done = true;
347b69a54eeSKentaro Takeda 
348b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2,
349b69a54eeSKentaro Takeda 			     &tomoyo_globally_readable_list) {
350b69a54eeSKentaro Takeda 		struct tomoyo_globally_readable_file_entry *ptr;
351b69a54eeSKentaro Takeda 		ptr = list_entry(pos,
352b69a54eeSKentaro Takeda 				 struct tomoyo_globally_readable_file_entry,
35382e0f001STetsuo Handa 				 head.list);
35482e0f001STetsuo Handa 		if (ptr->head.is_deleted)
355b69a54eeSKentaro Takeda 			continue;
3567d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
3577d2948b1STetsuo Handa 					ptr->filename->name);
3587d2948b1STetsuo Handa 		if (!done)
359b69a54eeSKentaro Takeda 			break;
360b69a54eeSKentaro Takeda 	}
361b69a54eeSKentaro Takeda 	return done;
362b69a54eeSKentaro Takeda }
363b69a54eeSKentaro Takeda 
364c3fa109aSTetsuo Handa /* tomoyo_pattern_list is used for holding list of pathnames which are used for
365c3fa109aSTetsuo Handa  * converting pathnames to pathname patterns during learning mode.
366c3fa109aSTetsuo Handa  *
367c3fa109aSTetsuo Handa  * An entry is added by
368c3fa109aSTetsuo Handa  *
369c3fa109aSTetsuo Handa  * # echo 'file_pattern /proc/\$/mounts' > \
370c3fa109aSTetsuo Handa  *                             /sys/kernel/security/tomoyo/exception_policy
371c3fa109aSTetsuo Handa  *
372c3fa109aSTetsuo Handa  * and is deleted by
373c3fa109aSTetsuo Handa  *
374c3fa109aSTetsuo Handa  * # echo 'delete file_pattern /proc/\$/mounts' > \
375c3fa109aSTetsuo Handa  *                             /sys/kernel/security/tomoyo/exception_policy
376c3fa109aSTetsuo Handa  *
377c3fa109aSTetsuo Handa  * and all entries are retrieved by
378c3fa109aSTetsuo Handa  *
379c3fa109aSTetsuo Handa  * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy
380c3fa109aSTetsuo Handa  *
381c3fa109aSTetsuo Handa  * In the example above, if a process which belongs to a domain which is in
382c3fa109aSTetsuo Handa  * learning mode requested open("/proc/1/mounts", O_RDONLY),
383c3fa109aSTetsuo Handa  * "allow_read /proc/\$/mounts" is automatically added to the domain which that
384c3fa109aSTetsuo Handa  * process belongs to.
385c3fa109aSTetsuo Handa  *
386c3fa109aSTetsuo Handa  * It is not a desirable behavior that we have to use /proc/\$/ instead of
387c3fa109aSTetsuo Handa  * /proc/self/ when current process needs to access only current process's
388c3fa109aSTetsuo Handa  * information. As of now, LSM version of TOMOYO is using __d_path() for
389c3fa109aSTetsuo Handa  * calculating pathname. Non LSM version of TOMOYO is using its own function
390c3fa109aSTetsuo Handa  * which pretends as if /proc/self/ is not a symlink; so that we can forbid
391c3fa109aSTetsuo Handa  * current process from accessing other process's information.
392c3fa109aSTetsuo Handa  */
393847b173eSTetsuo Handa LIST_HEAD(tomoyo_pattern_list);
394b69a54eeSKentaro Takeda 
39536f5e1ffSTetsuo Handa static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a,
39636f5e1ffSTetsuo Handa 				const struct tomoyo_acl_head *b)
39736f5e1ffSTetsuo Handa {
39836f5e1ffSTetsuo Handa 	return container_of(a, struct tomoyo_pattern_entry, head)->pattern ==
39936f5e1ffSTetsuo Handa 		container_of(b, struct tomoyo_pattern_entry, head)->pattern;
40036f5e1ffSTetsuo Handa }
40136f5e1ffSTetsuo Handa 
402b69a54eeSKentaro Takeda /**
403b69a54eeSKentaro Takeda  * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
404b69a54eeSKentaro Takeda  *
405b69a54eeSKentaro Takeda  * @pattern:   Pathname pattern.
406b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
407b69a54eeSKentaro Takeda  *
408b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
409fdb8ebb7STetsuo Handa  *
410fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
411b69a54eeSKentaro Takeda  */
412b69a54eeSKentaro Takeda static int tomoyo_update_file_pattern_entry(const char *pattern,
413b69a54eeSKentaro Takeda 					    const bool is_delete)
414b69a54eeSKentaro Takeda {
4153f629636STetsuo Handa 	struct tomoyo_pattern_entry e = { };
41636f5e1ffSTetsuo Handa 	int error;
417b69a54eeSKentaro Takeda 
4183f629636STetsuo Handa 	if (!tomoyo_is_correct_word(pattern))
4193f629636STetsuo Handa 		return -EINVAL;
4203f629636STetsuo Handa 	e.pattern = tomoyo_get_name(pattern);
4219e4b50e9STetsuo Handa 	if (!e.pattern)
42236f5e1ffSTetsuo Handa 		return -ENOMEM;
42336f5e1ffSTetsuo Handa 	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
42436f5e1ffSTetsuo Handa 				     &tomoyo_pattern_list,
42536f5e1ffSTetsuo Handa 				     tomoyo_same_pattern);
4269e4b50e9STetsuo Handa 	tomoyo_put_name(e.pattern);
427b69a54eeSKentaro Takeda 	return error;
428b69a54eeSKentaro Takeda }
429b69a54eeSKentaro Takeda 
430b69a54eeSKentaro Takeda /**
43117fcfbd9STetsuo Handa  * tomoyo_file_pattern - Get patterned pathname.
432b69a54eeSKentaro Takeda  *
433b69a54eeSKentaro Takeda  * @filename: The filename to find patterned pathname.
434b69a54eeSKentaro Takeda  *
435b69a54eeSKentaro Takeda  * Returns pointer to pathname pattern if matched, @filename otherwise.
436fdb8ebb7STetsuo Handa  *
437fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
438b69a54eeSKentaro Takeda  */
43917fcfbd9STetsuo Handa const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename)
440b69a54eeSKentaro Takeda {
441b69a54eeSKentaro Takeda 	struct tomoyo_pattern_entry *ptr;
442b69a54eeSKentaro Takeda 	const struct tomoyo_path_info *pattern = NULL;
443b69a54eeSKentaro Takeda 
44482e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) {
44582e0f001STetsuo Handa 		if (ptr->head.is_deleted)
446b69a54eeSKentaro Takeda 			continue;
447b69a54eeSKentaro Takeda 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
448b69a54eeSKentaro Takeda 			continue;
449b69a54eeSKentaro Takeda 		pattern = ptr->pattern;
450b69a54eeSKentaro Takeda 		if (tomoyo_strendswith(pattern->name, "/\\*")) {
451b69a54eeSKentaro Takeda 			/* Do nothing. Try to find the better match. */
452b69a54eeSKentaro Takeda 		} else {
453b69a54eeSKentaro Takeda 			/* This would be the better match. Use this. */
454b69a54eeSKentaro Takeda 			break;
455b69a54eeSKentaro Takeda 		}
456b69a54eeSKentaro Takeda 	}
457b69a54eeSKentaro Takeda 	if (pattern)
458b69a54eeSKentaro Takeda 		filename = pattern;
45917fcfbd9STetsuo Handa 	return filename->name;
460b69a54eeSKentaro Takeda }
461b69a54eeSKentaro Takeda 
462b69a54eeSKentaro Takeda /**
463b69a54eeSKentaro Takeda  * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
464b69a54eeSKentaro Takeda  *
465b69a54eeSKentaro Takeda  * @data:      String to parse.
466b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
467b69a54eeSKentaro Takeda  *
468b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
469fdb8ebb7STetsuo Handa  *
470fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
471b69a54eeSKentaro Takeda  */
472b69a54eeSKentaro Takeda int tomoyo_write_pattern_policy(char *data, const bool is_delete)
473b69a54eeSKentaro Takeda {
474b69a54eeSKentaro Takeda 	return tomoyo_update_file_pattern_entry(data, is_delete);
475b69a54eeSKentaro Takeda }
476b69a54eeSKentaro Takeda 
477b69a54eeSKentaro Takeda /**
478b69a54eeSKentaro Takeda  * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
479b69a54eeSKentaro Takeda  *
480b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
481b69a54eeSKentaro Takeda  *
482b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
483fdb8ebb7STetsuo Handa  *
484fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
485b69a54eeSKentaro Takeda  */
486b69a54eeSKentaro Takeda bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
487b69a54eeSKentaro Takeda {
488b69a54eeSKentaro Takeda 	struct list_head *pos;
489b69a54eeSKentaro Takeda 	bool done = true;
490b69a54eeSKentaro Takeda 
491b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
492b69a54eeSKentaro Takeda 		struct tomoyo_pattern_entry *ptr;
49382e0f001STetsuo Handa 		ptr = list_entry(pos, struct tomoyo_pattern_entry, head.list);
49482e0f001STetsuo Handa 		if (ptr->head.is_deleted)
495b69a54eeSKentaro Takeda 			continue;
4967d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
4977d2948b1STetsuo Handa 					"%s\n", ptr->pattern->name);
4987d2948b1STetsuo Handa 		if (!done)
499b69a54eeSKentaro Takeda 			break;
500b69a54eeSKentaro Takeda 	}
501b69a54eeSKentaro Takeda 	return done;
502b69a54eeSKentaro Takeda }
503b69a54eeSKentaro Takeda 
504c3fa109aSTetsuo Handa /*
505c3fa109aSTetsuo Handa  * tomoyo_no_rewrite_list is used for holding list of pathnames which are by
506c3fa109aSTetsuo Handa  * default forbidden to modify already written content of a file.
507c3fa109aSTetsuo Handa  *
508c3fa109aSTetsuo Handa  * An entry is added by
509c3fa109aSTetsuo Handa  *
510c3fa109aSTetsuo Handa  * # echo 'deny_rewrite /var/log/messages' > \
511c3fa109aSTetsuo Handa  *                              /sys/kernel/security/tomoyo/exception_policy
512c3fa109aSTetsuo Handa  *
513c3fa109aSTetsuo Handa  * and is deleted by
514c3fa109aSTetsuo Handa  *
515c3fa109aSTetsuo Handa  * # echo 'delete deny_rewrite /var/log/messages' > \
516c3fa109aSTetsuo Handa  *                              /sys/kernel/security/tomoyo/exception_policy
517c3fa109aSTetsuo Handa  *
518c3fa109aSTetsuo Handa  * and all entries are retrieved by
519c3fa109aSTetsuo Handa  *
520c3fa109aSTetsuo Handa  * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy
521c3fa109aSTetsuo Handa  *
522c3fa109aSTetsuo Handa  * In the example above, if a process requested to rewrite /var/log/messages ,
523c3fa109aSTetsuo Handa  * the process can't rewrite unless the domain which that process belongs to
524c3fa109aSTetsuo Handa  * has "allow_rewrite /var/log/messages" entry.
525c3fa109aSTetsuo Handa  *
526c3fa109aSTetsuo Handa  * It is not a desirable behavior that we have to add "\040(deleted)" suffix
527c3fa109aSTetsuo Handa  * when we want to allow rewriting already unlink()ed file. As of now,
528c3fa109aSTetsuo Handa  * LSM version of TOMOYO is using __d_path() for calculating pathname.
529c3fa109aSTetsuo Handa  * Non LSM version of TOMOYO is using its own function which doesn't append
530c3fa109aSTetsuo Handa  * " (deleted)" suffix if the file is already unlink()ed; so that we don't
531c3fa109aSTetsuo Handa  * need to worry whether the file is already unlink()ed or not.
532c3fa109aSTetsuo Handa  */
533847b173eSTetsuo Handa LIST_HEAD(tomoyo_no_rewrite_list);
534b69a54eeSKentaro Takeda 
53536f5e1ffSTetsuo Handa static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a,
53636f5e1ffSTetsuo Handa 				   const struct tomoyo_acl_head *b)
53736f5e1ffSTetsuo Handa {
53836f5e1ffSTetsuo Handa 	return container_of(a, struct tomoyo_no_rewrite_entry, head)->pattern
53936f5e1ffSTetsuo Handa 		== container_of(b, struct tomoyo_no_rewrite_entry, head)
54036f5e1ffSTetsuo Handa 		->pattern;
54136f5e1ffSTetsuo Handa }
54236f5e1ffSTetsuo Handa 
543b69a54eeSKentaro Takeda /**
544b69a54eeSKentaro Takeda  * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
545b69a54eeSKentaro Takeda  *
546b69a54eeSKentaro Takeda  * @pattern:   Pathname pattern that are not rewritable by default.
547b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
548b69a54eeSKentaro Takeda  *
549b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
550fdb8ebb7STetsuo Handa  *
551fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
552b69a54eeSKentaro Takeda  */
553b69a54eeSKentaro Takeda static int tomoyo_update_no_rewrite_entry(const char *pattern,
554b69a54eeSKentaro Takeda 					  const bool is_delete)
555b69a54eeSKentaro Takeda {
5569e4b50e9STetsuo Handa 	struct tomoyo_no_rewrite_entry e = { };
55736f5e1ffSTetsuo Handa 	int error;
558b69a54eeSKentaro Takeda 
5593f629636STetsuo Handa 	if (!tomoyo_is_correct_word(pattern))
560b69a54eeSKentaro Takeda 		return -EINVAL;
5619e4b50e9STetsuo Handa 	e.pattern = tomoyo_get_name(pattern);
5629e4b50e9STetsuo Handa 	if (!e.pattern)
56336f5e1ffSTetsuo Handa 		return -ENOMEM;
56436f5e1ffSTetsuo Handa 	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
56536f5e1ffSTetsuo Handa 				     &tomoyo_no_rewrite_list,
56636f5e1ffSTetsuo Handa 				     tomoyo_same_no_rewrite);
5679e4b50e9STetsuo Handa 	tomoyo_put_name(e.pattern);
568b69a54eeSKentaro Takeda 	return error;
569b69a54eeSKentaro Takeda }
570b69a54eeSKentaro Takeda 
571b69a54eeSKentaro Takeda /**
572b69a54eeSKentaro Takeda  * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
573b69a54eeSKentaro Takeda  *
574b69a54eeSKentaro Takeda  * @filename: Filename to check.
575b69a54eeSKentaro Takeda  *
576b69a54eeSKentaro Takeda  * Returns true if @filename is specified by "deny_rewrite" directive,
577b69a54eeSKentaro Takeda  * false otherwise.
578fdb8ebb7STetsuo Handa  *
579fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
580b69a54eeSKentaro Takeda  */
581b69a54eeSKentaro Takeda static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
582b69a54eeSKentaro Takeda {
583b69a54eeSKentaro Takeda 	struct tomoyo_no_rewrite_entry *ptr;
584b69a54eeSKentaro Takeda 	bool found = false;
585b69a54eeSKentaro Takeda 
58682e0f001STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, head.list) {
58782e0f001STetsuo Handa 		if (ptr->head.is_deleted)
588b69a54eeSKentaro Takeda 			continue;
589b69a54eeSKentaro Takeda 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
590b69a54eeSKentaro Takeda 			continue;
591b69a54eeSKentaro Takeda 		found = true;
592b69a54eeSKentaro Takeda 		break;
593b69a54eeSKentaro Takeda 	}
594b69a54eeSKentaro Takeda 	return found;
595b69a54eeSKentaro Takeda }
596b69a54eeSKentaro Takeda 
597b69a54eeSKentaro Takeda /**
598b69a54eeSKentaro Takeda  * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
599b69a54eeSKentaro Takeda  *
600b69a54eeSKentaro Takeda  * @data:      String to parse.
601b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
602b69a54eeSKentaro Takeda  *
603b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
604fdb8ebb7STetsuo Handa  *
605fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
606b69a54eeSKentaro Takeda  */
607b69a54eeSKentaro Takeda int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
608b69a54eeSKentaro Takeda {
609b69a54eeSKentaro Takeda 	return tomoyo_update_no_rewrite_entry(data, is_delete);
610b69a54eeSKentaro Takeda }
611b69a54eeSKentaro Takeda 
612b69a54eeSKentaro Takeda /**
613b69a54eeSKentaro Takeda  * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
614b69a54eeSKentaro Takeda  *
615b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
616b69a54eeSKentaro Takeda  *
617b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
618fdb8ebb7STetsuo Handa  *
619fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
620b69a54eeSKentaro Takeda  */
621b69a54eeSKentaro Takeda bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
622b69a54eeSKentaro Takeda {
623b69a54eeSKentaro Takeda 	struct list_head *pos;
624b69a54eeSKentaro Takeda 	bool done = true;
625b69a54eeSKentaro Takeda 
626b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
627b69a54eeSKentaro Takeda 		struct tomoyo_no_rewrite_entry *ptr;
62882e0f001STetsuo Handa 		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry,
62982e0f001STetsuo Handa 				 head.list);
63082e0f001STetsuo Handa 		if (ptr->head.is_deleted)
631b69a54eeSKentaro Takeda 			continue;
6327d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
6337d2948b1STetsuo Handa 					"%s\n", ptr->pattern->name);
6347d2948b1STetsuo Handa 		if (!done)
635b69a54eeSKentaro Takeda 			break;
636b69a54eeSKentaro Takeda 	}
637b69a54eeSKentaro Takeda 	return done;
638b69a54eeSKentaro Takeda }
639b69a54eeSKentaro Takeda 
640b69a54eeSKentaro Takeda /**
641cb0abe6aSTetsuo Handa  * tomoyo_path_acl - Check permission for single path operation.
642b69a54eeSKentaro Takeda  *
643cb0abe6aSTetsuo Handa  * @r:               Pointer to "struct tomoyo_request_info".
644b69a54eeSKentaro Takeda  * @filename:        Filename to check.
645b69a54eeSKentaro Takeda  * @perm:            Permission.
646b69a54eeSKentaro Takeda  *
647b69a54eeSKentaro Takeda  * Returns 0 on success, -EPERM otherwise.
648fdb8ebb7STetsuo Handa  *
649fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
650b69a54eeSKentaro Takeda  */
651cb0abe6aSTetsuo Handa static int tomoyo_path_acl(const struct tomoyo_request_info *r,
6527ef61233STetsuo Handa 			   const struct tomoyo_path_info *filename,
6533f629636STetsuo Handa 			   const u32 perm)
654b69a54eeSKentaro Takeda {
655cb0abe6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
656b69a54eeSKentaro Takeda 	struct tomoyo_acl_info *ptr;
657b69a54eeSKentaro Takeda 	int error = -EPERM;
658b69a54eeSKentaro Takeda 
659fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
6607ef61233STetsuo Handa 		struct tomoyo_path_acl *acl;
6617ef61233STetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_ACL)
662b69a54eeSKentaro Takeda 			continue;
6637ef61233STetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_acl, head);
664a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm) ||
6653f629636STetsuo Handa 		    !tomoyo_compare_name_union(filename, &acl->name))
666b69a54eeSKentaro Takeda 			continue;
667b69a54eeSKentaro Takeda 		error = 0;
668b69a54eeSKentaro Takeda 		break;
669b69a54eeSKentaro Takeda 	}
670b69a54eeSKentaro Takeda 	return error;
671b69a54eeSKentaro Takeda }
672b69a54eeSKentaro Takeda 
673b69a54eeSKentaro Takeda /**
674cb0abe6aSTetsuo Handa  * tomoyo_file_perm - Check permission for opening files.
675b69a54eeSKentaro Takeda  *
676cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
677b69a54eeSKentaro Takeda  * @filename:  Filename to check.
678cb0abe6aSTetsuo Handa  * @mode:      Mode ("read" or "write" or "read/write" or "execute").
679b69a54eeSKentaro Takeda  *
680b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
681fdb8ebb7STetsuo Handa  *
682fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
683b69a54eeSKentaro Takeda  */
684cb0abe6aSTetsuo Handa static int tomoyo_file_perm(struct tomoyo_request_info *r,
685b69a54eeSKentaro Takeda 			    const struct tomoyo_path_info *filename,
686b69a54eeSKentaro Takeda 			    const u8 mode)
687b69a54eeSKentaro Takeda {
688b69a54eeSKentaro Takeda 	const char *msg = "<unknown>";
689b69a54eeSKentaro Takeda 	int error = 0;
690cb0abe6aSTetsuo Handa 	u32 perm = 0;
691b69a54eeSKentaro Takeda 
692b69a54eeSKentaro Takeda 	if (!filename)
693b69a54eeSKentaro Takeda 		return 0;
694cb0abe6aSTetsuo Handa 
695cb0abe6aSTetsuo Handa 	if (mode == 6) {
696cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE);
697cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_READ_WRITE;
698cb0abe6aSTetsuo Handa 	} else if (mode == 4) {
699cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ);
700cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_READ;
701cb0abe6aSTetsuo Handa 	} else if (mode == 2) {
702cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE);
703cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_WRITE;
704cb0abe6aSTetsuo Handa 	} else if (mode == 1) {
705cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE);
706cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_EXECUTE;
707cb0abe6aSTetsuo Handa 	} else
708cb0abe6aSTetsuo Handa 		BUG();
70917fcfbd9STetsuo Handa 	do {
7103f629636STetsuo Handa 		error = tomoyo_path_acl(r, filename, perm);
711cb0abe6aSTetsuo Handa 		if (error && mode == 4 && !r->domain->ignore_global_allow_read
712b69a54eeSKentaro Takeda 		    && tomoyo_is_globally_readable_file(filename))
713b69a54eeSKentaro Takeda 			error = 0;
714b69a54eeSKentaro Takeda 		if (!error)
71517fcfbd9STetsuo Handa 			break;
716cb0abe6aSTetsuo Handa 		tomoyo_warn_log(r, "%s %s", msg, filename->name);
71717fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
71817fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename));
71917fcfbd9STetsuo Handa 		/*
72017fcfbd9STetsuo Handa                  * Do not retry for execute request, for alias may have
72117fcfbd9STetsuo Handa 		 * changed.
72217fcfbd9STetsuo Handa                  */
72317fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST && mode != 1);
72417fcfbd9STetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
72517fcfbd9STetsuo Handa 		error = 0;
726b69a54eeSKentaro Takeda 	return error;
727b69a54eeSKentaro Takeda }
728b69a54eeSKentaro Takeda 
729237ab459STetsuo Handa static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,
730237ab459STetsuo Handa 				 const struct tomoyo_acl_info *b)
731237ab459STetsuo Handa {
732237ab459STetsuo Handa 	const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
733237ab459STetsuo Handa 	const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
734237ab459STetsuo Handa 	return tomoyo_is_same_acl_head(&p1->head, &p2->head) &&
735237ab459STetsuo Handa 		tomoyo_is_same_name_union(&p1->name, &p2->name);
736237ab459STetsuo Handa }
737237ab459STetsuo Handa 
738237ab459STetsuo Handa static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
739237ab459STetsuo Handa 				  struct tomoyo_acl_info *b,
740237ab459STetsuo Handa 				  const bool is_delete)
741237ab459STetsuo Handa {
742237ab459STetsuo Handa 	u16 * const a_perm = &container_of(a, struct tomoyo_path_acl, head)
743237ab459STetsuo Handa 		->perm;
744237ab459STetsuo Handa 	u16 perm = *a_perm;
745237ab459STetsuo Handa 	const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
746237ab459STetsuo Handa 	if (is_delete) {
747237ab459STetsuo Handa 		perm &= ~b_perm;
748237ab459STetsuo Handa 		if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK)
749237ab459STetsuo Handa 			perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
750237ab459STetsuo Handa 		else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE)))
751237ab459STetsuo Handa 			perm &= ~TOMOYO_RW_MASK;
752237ab459STetsuo Handa 	} else {
753237ab459STetsuo Handa 		perm |= b_perm;
754237ab459STetsuo Handa 		if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK)
755237ab459STetsuo Handa 			perm |= (1 << TOMOYO_TYPE_READ_WRITE);
756237ab459STetsuo Handa 		else if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
757237ab459STetsuo Handa 			perm |= TOMOYO_RW_MASK;
758237ab459STetsuo Handa 	}
759237ab459STetsuo Handa 	*a_perm = perm;
760237ab459STetsuo Handa 	return !perm;
761237ab459STetsuo Handa }
762237ab459STetsuo Handa 
763b69a54eeSKentaro Takeda /**
7647ef61233STetsuo Handa  * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
765b69a54eeSKentaro Takeda  *
766b69a54eeSKentaro Takeda  * @type:      Type of operation.
767b69a54eeSKentaro Takeda  * @filename:  Filename.
768b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
769b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
770b69a54eeSKentaro Takeda  *
771b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
772fdb8ebb7STetsuo Handa  *
773fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
774b69a54eeSKentaro Takeda  */
7757ef61233STetsuo Handa static int tomoyo_update_path_acl(const u8 type, const char *filename,
7767ef61233STetsuo Handa 				  struct tomoyo_domain_info * const domain,
7777ef61233STetsuo Handa 				  const bool is_delete)
778b69a54eeSKentaro Takeda {
7799e4b50e9STetsuo Handa 	struct tomoyo_path_acl e = {
7809e4b50e9STetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_ACL,
781237ab459STetsuo Handa 		.perm = 1 << type
7829e4b50e9STetsuo Handa 	};
783237ab459STetsuo Handa 	int error;
784237ab459STetsuo Handa 	if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE))
785237ab459STetsuo Handa 		e.perm |= TOMOYO_RW_MASK;
7867762fbffSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name))
787b69a54eeSKentaro Takeda 		return -EINVAL;
788237ab459STetsuo Handa 	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
789237ab459STetsuo Handa 				     tomoyo_same_path_acl,
790237ab459STetsuo Handa 				     tomoyo_merge_path_acl);
7917762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name);
792b69a54eeSKentaro Takeda 	return error;
793b69a54eeSKentaro Takeda }
794b69a54eeSKentaro Takeda 
795237ab459STetsuo Handa static bool tomoyo_same_path_number3_acl(const struct tomoyo_acl_info *a,
796237ab459STetsuo Handa 					 const struct tomoyo_acl_info *b)
797237ab459STetsuo Handa {
798237ab459STetsuo Handa 	const struct tomoyo_path_number3_acl *p1 = container_of(a, typeof(*p1),
799237ab459STetsuo Handa 								head);
800237ab459STetsuo Handa 	const struct tomoyo_path_number3_acl *p2 = container_of(b, typeof(*p2),
801237ab459STetsuo Handa 								head);
802237ab459STetsuo Handa 	return tomoyo_is_same_acl_head(&p1->head, &p2->head)
803237ab459STetsuo Handa 		&& tomoyo_is_same_name_union(&p1->name, &p2->name)
804237ab459STetsuo Handa 		&& tomoyo_is_same_number_union(&p1->mode, &p2->mode)
805237ab459STetsuo Handa 		&& tomoyo_is_same_number_union(&p1->major, &p2->major)
806237ab459STetsuo Handa 		&& tomoyo_is_same_number_union(&p1->minor, &p2->minor);
807237ab459STetsuo Handa }
808237ab459STetsuo Handa 
809237ab459STetsuo Handa static bool tomoyo_merge_path_number3_acl(struct tomoyo_acl_info *a,
810237ab459STetsuo Handa 					  struct tomoyo_acl_info *b,
811237ab459STetsuo Handa 					  const bool is_delete)
812237ab459STetsuo Handa {
813237ab459STetsuo Handa 	u8 *const a_perm = &container_of(a, struct tomoyo_path_number3_acl,
814237ab459STetsuo Handa 					 head)->perm;
815237ab459STetsuo Handa 	u8 perm = *a_perm;
816237ab459STetsuo Handa 	const u8 b_perm = container_of(b, struct tomoyo_path_number3_acl, head)
817237ab459STetsuo Handa 		->perm;
818237ab459STetsuo Handa 	if (is_delete)
819237ab459STetsuo Handa 		perm &= ~b_perm;
820237ab459STetsuo Handa 	else
821237ab459STetsuo Handa 		perm |= b_perm;
822237ab459STetsuo Handa 	*a_perm = perm;
823237ab459STetsuo Handa 	return !perm;
824237ab459STetsuo Handa }
825237ab459STetsuo Handa 
826b69a54eeSKentaro Takeda /**
827a1f9bb6aSTetsuo Handa  * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list.
828a1f9bb6aSTetsuo Handa  *
829a1f9bb6aSTetsuo Handa  * @type:      Type of operation.
830a1f9bb6aSTetsuo Handa  * @filename:  Filename.
831a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
832a1f9bb6aSTetsuo Handa  * @major:     Device major number.
833a1f9bb6aSTetsuo Handa  * @minor:     Device minor number.
834a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
835a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
836a1f9bb6aSTetsuo Handa  *
837a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
838237ab459STetsuo Handa  *
839237ab459STetsuo Handa  * Caller holds tomoyo_read_lock().
840a1f9bb6aSTetsuo Handa  */
841237ab459STetsuo Handa static int tomoyo_update_path_number3_acl(const u8 type, const char *filename,
842237ab459STetsuo Handa 					  char *mode, char *major, char *minor,
843237ab459STetsuo Handa 					  struct tomoyo_domain_info * const
844237ab459STetsuo Handa 					  domain, const bool is_delete)
845a1f9bb6aSTetsuo Handa {
846a1f9bb6aSTetsuo Handa 	struct tomoyo_path_number3_acl e = {
847a1f9bb6aSTetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL,
848237ab459STetsuo Handa 		.perm = 1 << type
849a1f9bb6aSTetsuo Handa 	};
850a1f9bb6aSTetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
851a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name) ||
852a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(mode, &e.mode) ||
853a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(major, &e.major) ||
854a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(minor, &e.minor))
855a1f9bb6aSTetsuo Handa 		goto out;
856237ab459STetsuo Handa 	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
857237ab459STetsuo Handa 				     tomoyo_same_path_number3_acl,
858237ab459STetsuo Handa 				     tomoyo_merge_path_number3_acl);
859a1f9bb6aSTetsuo Handa  out:
860a1f9bb6aSTetsuo Handa 	tomoyo_put_name_union(&e.name);
861a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.mode);
862a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.major);
863a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.minor);
864a1f9bb6aSTetsuo Handa 	return error;
865a1f9bb6aSTetsuo Handa }
866a1f9bb6aSTetsuo Handa 
867237ab459STetsuo Handa static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,
868237ab459STetsuo Handa 				  const struct tomoyo_acl_info *b)
869237ab459STetsuo Handa {
870237ab459STetsuo Handa 	const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
871237ab459STetsuo Handa 	const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
872237ab459STetsuo Handa 	return tomoyo_is_same_acl_head(&p1->head, &p2->head)
873237ab459STetsuo Handa 		&& tomoyo_is_same_name_union(&p1->name1, &p2->name1)
874237ab459STetsuo Handa 		&& tomoyo_is_same_name_union(&p1->name2, &p2->name2);
875237ab459STetsuo Handa }
876237ab459STetsuo Handa 
877237ab459STetsuo Handa static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
878237ab459STetsuo Handa 				   struct tomoyo_acl_info *b,
879237ab459STetsuo Handa 				   const bool is_delete)
880237ab459STetsuo Handa {
881237ab459STetsuo Handa 	u8 * const a_perm = &container_of(a, struct tomoyo_path2_acl, head)
882237ab459STetsuo Handa 		->perm;
883237ab459STetsuo Handa 	u8 perm = *a_perm;
884237ab459STetsuo Handa 	const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm;
885237ab459STetsuo Handa 	if (is_delete)
886237ab459STetsuo Handa 		perm &= ~b_perm;
887237ab459STetsuo Handa 	else
888237ab459STetsuo Handa 		perm |= b_perm;
889237ab459STetsuo Handa 	*a_perm = perm;
890237ab459STetsuo Handa 	return !perm;
891237ab459STetsuo Handa }
892237ab459STetsuo Handa 
893a1f9bb6aSTetsuo Handa /**
8947ef61233STetsuo Handa  * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
895b69a54eeSKentaro Takeda  *
896b69a54eeSKentaro Takeda  * @type:      Type of operation.
897b69a54eeSKentaro Takeda  * @filename1: First filename.
898b69a54eeSKentaro Takeda  * @filename2: Second filename.
899b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
900b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
901b69a54eeSKentaro Takeda  *
902b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
903fdb8ebb7STetsuo Handa  *
904fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
905b69a54eeSKentaro Takeda  */
9067ef61233STetsuo Handa static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
907b69a54eeSKentaro Takeda 				   const char *filename2,
9087ef61233STetsuo Handa 				   struct tomoyo_domain_info * const domain,
9097ef61233STetsuo Handa 				   const bool is_delete)
910b69a54eeSKentaro Takeda {
9119e4b50e9STetsuo Handa 	struct tomoyo_path2_acl e = {
9129e4b50e9STetsuo Handa 		.head.type = TOMOYO_TYPE_PATH2_ACL,
913237ab459STetsuo Handa 		.perm = 1 << type
9149e4b50e9STetsuo Handa 	};
9159e4b50e9STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
9167762fbffSTetsuo Handa 	if (!tomoyo_parse_name_union(filename1, &e.name1) ||
9177762fbffSTetsuo Handa 	    !tomoyo_parse_name_union(filename2, &e.name2))
918ca0b7df3STetsuo Handa 		goto out;
919237ab459STetsuo Handa 	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
920237ab459STetsuo Handa 				     tomoyo_same_path2_acl,
921237ab459STetsuo Handa 				     tomoyo_merge_path2_acl);
922ca0b7df3STetsuo Handa  out:
9237762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name1);
9247762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name2);
925b69a54eeSKentaro Takeda 	return error;
926b69a54eeSKentaro Takeda }
927b69a54eeSKentaro Takeda 
928b69a54eeSKentaro Takeda /**
929a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_acl - Check permission for path/number/number/number operation.
930a1f9bb6aSTetsuo Handa  *
931a1f9bb6aSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
932a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
933a1f9bb6aSTetsuo Handa  * @perm:     Permission.
934a1f9bb6aSTetsuo Handa  * @mode:     Create mode.
935a1f9bb6aSTetsuo Handa  * @major:    Device major number.
936a1f9bb6aSTetsuo Handa  * @minor:    Device minor number.
937a1f9bb6aSTetsuo Handa  *
938a1f9bb6aSTetsuo Handa  * Returns 0 on success, -EPERM otherwise.
939a1f9bb6aSTetsuo Handa  *
940a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
941a1f9bb6aSTetsuo Handa  */
942a1f9bb6aSTetsuo Handa static int tomoyo_path_number3_acl(struct tomoyo_request_info *r,
943a1f9bb6aSTetsuo Handa 				   const struct tomoyo_path_info *filename,
944a1f9bb6aSTetsuo Handa 				   const u16 perm, const unsigned int mode,
945a1f9bb6aSTetsuo Handa 				   const unsigned int major,
946a1f9bb6aSTetsuo Handa 				   const unsigned int minor)
947a1f9bb6aSTetsuo Handa {
948a1f9bb6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
949a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
950a1f9bb6aSTetsuo Handa 	int error = -EPERM;
951a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
952a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number3_acl *acl;
953a1f9bb6aSTetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL)
954a1f9bb6aSTetsuo Handa 			continue;
955a1f9bb6aSTetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_number3_acl, head);
956a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(mode, &acl->mode))
957a1f9bb6aSTetsuo Handa 			continue;
958a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(major, &acl->major))
959a1f9bb6aSTetsuo Handa 			continue;
960a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(minor, &acl->minor))
961a1f9bb6aSTetsuo Handa 			continue;
962a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm))
963a1f9bb6aSTetsuo Handa 			continue;
964a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_name_union(filename, &acl->name))
965a1f9bb6aSTetsuo Handa 			continue;
966a1f9bb6aSTetsuo Handa 		error = 0;
967a1f9bb6aSTetsuo Handa 		break;
968a1f9bb6aSTetsuo Handa 	}
969a1f9bb6aSTetsuo Handa 	return error;
970a1f9bb6aSTetsuo Handa }
971a1f9bb6aSTetsuo Handa 
972a1f9bb6aSTetsuo Handa /**
9737ef61233STetsuo Handa  * tomoyo_path2_acl - Check permission for double path operation.
974b69a54eeSKentaro Takeda  *
975cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
976b69a54eeSKentaro Takeda  * @type:      Type of operation.
977b69a54eeSKentaro Takeda  * @filename1: First filename to check.
978b69a54eeSKentaro Takeda  * @filename2: Second filename to check.
979b69a54eeSKentaro Takeda  *
980b69a54eeSKentaro Takeda  * Returns 0 on success, -EPERM otherwise.
981fdb8ebb7STetsuo Handa  *
982fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
983b69a54eeSKentaro Takeda  */
984cb0abe6aSTetsuo Handa static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type,
9857ef61233STetsuo Handa 			    const struct tomoyo_path_info *filename1,
9867ef61233STetsuo Handa 			    const struct tomoyo_path_info *filename2)
987b69a54eeSKentaro Takeda {
988cb0abe6aSTetsuo Handa 	const struct tomoyo_domain_info *domain = r->domain;
989b69a54eeSKentaro Takeda 	struct tomoyo_acl_info *ptr;
990b69a54eeSKentaro Takeda 	const u8 perm = 1 << type;
991b69a54eeSKentaro Takeda 	int error = -EPERM;
992b69a54eeSKentaro Takeda 
993fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
9947ef61233STetsuo Handa 		struct tomoyo_path2_acl *acl;
9957ef61233STetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
996b69a54eeSKentaro Takeda 			continue;
9977ef61233STetsuo Handa 		acl = container_of(ptr, struct tomoyo_path2_acl, head);
998b69a54eeSKentaro Takeda 		if (!(acl->perm & perm))
999b69a54eeSKentaro Takeda 			continue;
10007762fbffSTetsuo Handa 		if (!tomoyo_compare_name_union(filename1, &acl->name1))
1001b69a54eeSKentaro Takeda 			continue;
10027762fbffSTetsuo Handa 		if (!tomoyo_compare_name_union(filename2, &acl->name2))
1003b69a54eeSKentaro Takeda 			continue;
1004b69a54eeSKentaro Takeda 		error = 0;
1005b69a54eeSKentaro Takeda 		break;
1006b69a54eeSKentaro Takeda 	}
1007b69a54eeSKentaro Takeda 	return error;
1008b69a54eeSKentaro Takeda }
1009b69a54eeSKentaro Takeda 
1010b69a54eeSKentaro Takeda /**
1011cb0abe6aSTetsuo Handa  * tomoyo_path_permission - Check permission for single path operation.
1012b69a54eeSKentaro Takeda  *
1013cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1014b69a54eeSKentaro Takeda  * @operation: Type of operation.
1015b69a54eeSKentaro Takeda  * @filename:  Filename to check.
1016b69a54eeSKentaro Takeda  *
1017b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1018fdb8ebb7STetsuo Handa  *
1019fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1020b69a54eeSKentaro Takeda  */
1021cb0abe6aSTetsuo Handa static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
1022cb0abe6aSTetsuo Handa 				  const struct tomoyo_path_info *filename)
1023b69a54eeSKentaro Takeda {
102417fcfbd9STetsuo Handa 	const char *msg;
1025b69a54eeSKentaro Takeda 	int error;
1026b69a54eeSKentaro Takeda 
1027b69a54eeSKentaro Takeda  next:
102857c2590fSTetsuo Handa 	r->type = tomoyo_p2mac[operation];
102957c2590fSTetsuo Handa 	r->mode = tomoyo_get_mode(r->profile, r->type);
103057c2590fSTetsuo Handa 	if (r->mode == TOMOYO_CONFIG_DISABLED)
103157c2590fSTetsuo Handa 		return 0;
103217fcfbd9STetsuo Handa 	do {
10333f629636STetsuo Handa 		error = tomoyo_path_acl(r, filename, 1 << operation);
1034b69a54eeSKentaro Takeda 		if (!error)
103517fcfbd9STetsuo Handa 			break;
103617fcfbd9STetsuo Handa 		msg = tomoyo_path2keyword(operation);
103717fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s", msg, filename->name);
103817fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
103917fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename));
104017fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1041cb0abe6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1042b69a54eeSKentaro Takeda 		error = 0;
1043b69a54eeSKentaro Takeda 	/*
1044b69a54eeSKentaro Takeda 	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
1045b69a54eeSKentaro Takeda 	 * we need to check "allow_rewrite" permission if the filename is
1046b69a54eeSKentaro Takeda 	 * specified by "deny_rewrite" keyword.
1047b69a54eeSKentaro Takeda 	 */
10487ef61233STetsuo Handa 	if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
1049b69a54eeSKentaro Takeda 	    tomoyo_is_no_rewrite_file(filename)) {
10507ef61233STetsuo Handa 		operation = TOMOYO_TYPE_REWRITE;
1051b69a54eeSKentaro Takeda 		goto next;
1052b69a54eeSKentaro Takeda 	}
1053b69a54eeSKentaro Takeda 	return error;
1054b69a54eeSKentaro Takeda }
1055b69a54eeSKentaro Takeda 
1056b69a54eeSKentaro Takeda /**
1057a1f9bb6aSTetsuo Handa  * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation.
1058a1f9bb6aSTetsuo Handa  *
1059a1f9bb6aSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
1060a1f9bb6aSTetsuo Handa  * @type:     Operation.
1061a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1062a1f9bb6aSTetsuo Handa  * @number:   Number.
1063a1f9bb6aSTetsuo Handa  *
1064a1f9bb6aSTetsuo Handa  * Returns 0 on success, -EPERM otherwise.
1065a1f9bb6aSTetsuo Handa  *
1066a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1067a1f9bb6aSTetsuo Handa  */
1068a1f9bb6aSTetsuo Handa static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type,
1069a1f9bb6aSTetsuo Handa 				  const struct tomoyo_path_info *filename,
1070a1f9bb6aSTetsuo Handa 				  const unsigned long number)
1071a1f9bb6aSTetsuo Handa {
1072a1f9bb6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
1073a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
1074a1f9bb6aSTetsuo Handa 	const u8 perm = 1 << type;
1075a1f9bb6aSTetsuo Handa 	int error = -EPERM;
1076a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1077a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number_acl *acl;
1078a1f9bb6aSTetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL)
1079a1f9bb6aSTetsuo Handa 			continue;
1080a1f9bb6aSTetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_number_acl,
1081a1f9bb6aSTetsuo Handa 				   head);
1082a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm) ||
1083a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_number_union(number, &acl->number) ||
1084a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_name_union(filename, &acl->name))
1085a1f9bb6aSTetsuo Handa 			continue;
1086a1f9bb6aSTetsuo Handa 		error = 0;
1087a1f9bb6aSTetsuo Handa 		break;
1088a1f9bb6aSTetsuo Handa 	}
1089a1f9bb6aSTetsuo Handa 	return error;
1090a1f9bb6aSTetsuo Handa }
1091a1f9bb6aSTetsuo Handa 
1092237ab459STetsuo Handa static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
1093237ab459STetsuo Handa 					const struct tomoyo_acl_info *b)
1094237ab459STetsuo Handa {
1095237ab459STetsuo Handa 	const struct tomoyo_path_number_acl *p1 = container_of(a, typeof(*p1),
1096237ab459STetsuo Handa 							       head);
1097237ab459STetsuo Handa 	const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
1098237ab459STetsuo Handa 							       head);
1099237ab459STetsuo Handa 	return tomoyo_is_same_acl_head(&p1->head, &p2->head)
1100237ab459STetsuo Handa 		&& tomoyo_is_same_name_union(&p1->name, &p2->name)
1101237ab459STetsuo Handa 		&& tomoyo_is_same_number_union(&p1->number, &p2->number);
1102237ab459STetsuo Handa }
1103237ab459STetsuo Handa 
1104237ab459STetsuo Handa static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
1105237ab459STetsuo Handa 					 struct tomoyo_acl_info *b,
1106237ab459STetsuo Handa 					 const bool is_delete)
1107237ab459STetsuo Handa {
1108237ab459STetsuo Handa 	u8 * const a_perm = &container_of(a, struct tomoyo_path_number_acl,
1109237ab459STetsuo Handa 					  head)->perm;
1110237ab459STetsuo Handa 	u8 perm = *a_perm;
1111237ab459STetsuo Handa 	const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head)
1112237ab459STetsuo Handa 		->perm;
1113237ab459STetsuo Handa 	if (is_delete)
1114237ab459STetsuo Handa 		perm &= ~b_perm;
1115237ab459STetsuo Handa 	else
1116237ab459STetsuo Handa 		perm |= b_perm;
1117237ab459STetsuo Handa 	*a_perm = perm;
1118237ab459STetsuo Handa 	return !perm;
1119237ab459STetsuo Handa }
1120237ab459STetsuo Handa 
1121a1f9bb6aSTetsuo Handa /**
1122a1f9bb6aSTetsuo Handa  * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
1123a1f9bb6aSTetsuo Handa  *
1124a1f9bb6aSTetsuo Handa  * @type:      Type of operation.
1125a1f9bb6aSTetsuo Handa  * @filename:  Filename.
1126a1f9bb6aSTetsuo Handa  * @number:    Number.
1127a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
1128a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
1129a1f9bb6aSTetsuo Handa  *
1130a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1131a1f9bb6aSTetsuo Handa  */
1132237ab459STetsuo Handa static int tomoyo_update_path_number_acl(const u8 type, const char *filename,
1133a1f9bb6aSTetsuo Handa 					 char *number,
1134237ab459STetsuo Handa 					 struct tomoyo_domain_info * const
1135237ab459STetsuo Handa 					 domain,
1136a1f9bb6aSTetsuo Handa 					 const bool is_delete)
1137a1f9bb6aSTetsuo Handa {
1138a1f9bb6aSTetsuo Handa 	struct tomoyo_path_number_acl e = {
1139a1f9bb6aSTetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
1140237ab459STetsuo Handa 		.perm = 1 << type
1141a1f9bb6aSTetsuo Handa 	};
1142a1f9bb6aSTetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
1143a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name))
1144a1f9bb6aSTetsuo Handa 		return -EINVAL;
1145a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_number_union(number, &e.number))
1146a1f9bb6aSTetsuo Handa 		goto out;
1147237ab459STetsuo Handa 	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
1148237ab459STetsuo Handa 				     tomoyo_same_path_number_acl,
1149237ab459STetsuo Handa 				     tomoyo_merge_path_number_acl);
1150a1f9bb6aSTetsuo Handa  out:
1151a1f9bb6aSTetsuo Handa 	tomoyo_put_name_union(&e.name);
1152a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.number);
1153a1f9bb6aSTetsuo Handa 	return error;
1154a1f9bb6aSTetsuo Handa }
1155a1f9bb6aSTetsuo Handa 
1156a1f9bb6aSTetsuo Handa /**
1157a1f9bb6aSTetsuo Handa  * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
1158a1f9bb6aSTetsuo Handa  *
1159a1f9bb6aSTetsuo Handa  * @r:        Pointer to "strct tomoyo_request_info".
1160a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1161a1f9bb6aSTetsuo Handa  * @number:   Number.
1162a1f9bb6aSTetsuo Handa  *
1163a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1164a1f9bb6aSTetsuo Handa  *
1165a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1166a1f9bb6aSTetsuo Handa  */
1167a1f9bb6aSTetsuo Handa static int tomoyo_path_number_perm2(struct tomoyo_request_info *r,
1168a1f9bb6aSTetsuo Handa 				    const u8 type,
1169a1f9bb6aSTetsuo Handa 				    const struct tomoyo_path_info *filename,
1170a1f9bb6aSTetsuo Handa 				    const unsigned long number)
1171a1f9bb6aSTetsuo Handa {
1172a1f9bb6aSTetsuo Handa 	char buffer[64];
1173a1f9bb6aSTetsuo Handa 	int error;
1174a1f9bb6aSTetsuo Handa 	u8 radix;
117517fcfbd9STetsuo Handa 	const char *msg;
1176a1f9bb6aSTetsuo Handa 
1177a1f9bb6aSTetsuo Handa 	if (!filename)
1178a1f9bb6aSTetsuo Handa 		return 0;
1179a1f9bb6aSTetsuo Handa 	switch (type) {
1180a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_CREATE:
1181a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKDIR:
1182a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKFIFO:
1183a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKSOCK:
1184a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_CHMOD:
1185a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_OCTAL;
1186a1f9bb6aSTetsuo Handa 		break;
1187a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_IOCTL:
1188a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_HEXADECIMAL;
1189a1f9bb6aSTetsuo Handa 		break;
1190a1f9bb6aSTetsuo Handa 	default:
1191a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_DECIMAL;
1192a1f9bb6aSTetsuo Handa 		break;
1193a1f9bb6aSTetsuo Handa 	}
1194a1f9bb6aSTetsuo Handa 	tomoyo_print_ulong(buffer, sizeof(buffer), number, radix);
119517fcfbd9STetsuo Handa 	do {
1196a1f9bb6aSTetsuo Handa 		error = tomoyo_path_number_acl(r, type, filename, number);
1197a1f9bb6aSTetsuo Handa 		if (!error)
119817fcfbd9STetsuo Handa 			break;
119917fcfbd9STetsuo Handa 		msg = tomoyo_path_number2keyword(type);
120017fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s %s", msg, filename->name, buffer);
120117fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg,
120217fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename),
120317fcfbd9STetsuo Handa 					  buffer);
120417fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1205a1f9bb6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1206a1f9bb6aSTetsuo Handa 		error = 0;
1207a1f9bb6aSTetsuo Handa 	return error;
1208a1f9bb6aSTetsuo Handa }
1209a1f9bb6aSTetsuo Handa 
1210a1f9bb6aSTetsuo Handa /**
1211a1f9bb6aSTetsuo Handa  * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
1212a1f9bb6aSTetsuo Handa  *
1213a1f9bb6aSTetsuo Handa  * @type:   Type of operation.
1214a1f9bb6aSTetsuo Handa  * @path:   Pointer to "struct path".
1215a1f9bb6aSTetsuo Handa  * @number: Number.
1216a1f9bb6aSTetsuo Handa  *
1217a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1218a1f9bb6aSTetsuo Handa  */
1219a1f9bb6aSTetsuo Handa int tomoyo_path_number_perm(const u8 type, struct path *path,
1220a1f9bb6aSTetsuo Handa 			    unsigned long number)
1221a1f9bb6aSTetsuo Handa {
1222a1f9bb6aSTetsuo Handa 	struct tomoyo_request_info r;
1223a1f9bb6aSTetsuo Handa 	int error = -ENOMEM;
1224c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1225a1f9bb6aSTetsuo Handa 	int idx;
1226a1f9bb6aSTetsuo Handa 
122757c2590fSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
122857c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry)
1229a1f9bb6aSTetsuo Handa 		return 0;
1230a1f9bb6aSTetsuo Handa 	idx = tomoyo_read_lock();
1231c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1232a1f9bb6aSTetsuo Handa 		goto out;
1233c8c57e84STetsuo Handa 	if (type == TOMOYO_TYPE_MKDIR)
1234c8c57e84STetsuo Handa 		tomoyo_add_slash(&buf);
1235c8c57e84STetsuo Handa 	error = tomoyo_path_number_perm2(&r, type, &buf, number);
1236a1f9bb6aSTetsuo Handa  out:
1237c8c57e84STetsuo Handa 	kfree(buf.name);
1238a1f9bb6aSTetsuo Handa 	tomoyo_read_unlock(idx);
1239a1f9bb6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1240a1f9bb6aSTetsuo Handa 		error = 0;
1241a1f9bb6aSTetsuo Handa 	return error;
1242a1f9bb6aSTetsuo Handa }
1243a1f9bb6aSTetsuo Handa 
1244a1f9bb6aSTetsuo Handa /**
1245b69a54eeSKentaro Takeda  * tomoyo_check_exec_perm - Check permission for "execute".
1246b69a54eeSKentaro Takeda  *
124757c2590fSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
1248b69a54eeSKentaro Takeda  * @filename: Check permission for "execute".
1249b69a54eeSKentaro Takeda  *
1250b69a54eeSKentaro Takeda  * Returns 0 on success, negativevalue otherwise.
1251fdb8ebb7STetsuo Handa  *
1252fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1253b69a54eeSKentaro Takeda  */
125457c2590fSTetsuo Handa int tomoyo_check_exec_perm(struct tomoyo_request_info *r,
1255bcb86975STetsuo Handa 			   const struct tomoyo_path_info *filename)
1256b69a54eeSKentaro Takeda {
125757c2590fSTetsuo Handa 	if (r->mode == TOMOYO_CONFIG_DISABLED)
1258b69a54eeSKentaro Takeda 		return 0;
125957c2590fSTetsuo Handa 	return tomoyo_file_perm(r, filename, 1);
1260b69a54eeSKentaro Takeda }
1261b69a54eeSKentaro Takeda 
1262b69a54eeSKentaro Takeda /**
1263b69a54eeSKentaro Takeda  * tomoyo_check_open_permission - Check permission for "read" and "write".
1264b69a54eeSKentaro Takeda  *
1265b69a54eeSKentaro Takeda  * @domain: Pointer to "struct tomoyo_domain_info".
1266b69a54eeSKentaro Takeda  * @path:   Pointer to "struct path".
1267b69a54eeSKentaro Takeda  * @flag:   Flags for open().
1268b69a54eeSKentaro Takeda  *
1269b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1270b69a54eeSKentaro Takeda  */
1271b69a54eeSKentaro Takeda int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
1272b69a54eeSKentaro Takeda 				 struct path *path, const int flag)
1273b69a54eeSKentaro Takeda {
1274b69a54eeSKentaro Takeda 	const u8 acc_mode = ACC_MODE(flag);
1275b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1276c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1277cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1278fdb8ebb7STetsuo Handa 	int idx;
1279b69a54eeSKentaro Takeda 
128057c2590fSTetsuo Handa 	if (!path->mnt ||
128157c2590fSTetsuo Handa 	    (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)))
1282b69a54eeSKentaro Takeda 		return 0;
128357c2590fSTetsuo Handa 	buf.name = NULL;
128457c2590fSTetsuo Handa 	r.mode = TOMOYO_CONFIG_DISABLED;
1285fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1286c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1287b69a54eeSKentaro Takeda 		goto out;
1288b69a54eeSKentaro Takeda 	error = 0;
1289b69a54eeSKentaro Takeda 	/*
1290b69a54eeSKentaro Takeda 	 * If the filename is specified by "deny_rewrite" keyword,
1291b69a54eeSKentaro Takeda 	 * we need to check "allow_rewrite" permission when the filename is not
1292b69a54eeSKentaro Takeda 	 * opened for append mode or the filename is truncated at open time.
1293b69a54eeSKentaro Takeda 	 */
129457c2590fSTetsuo Handa 	if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
129557c2590fSTetsuo Handa 	    && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE)
129657c2590fSTetsuo Handa 	    != TOMOYO_CONFIG_DISABLED) {
129757c2590fSTetsuo Handa 		if (!tomoyo_get_realpath(&buf, path)) {
129857c2590fSTetsuo Handa 			error = -ENOMEM;
129957c2590fSTetsuo Handa 			goto out;
1300b69a54eeSKentaro Takeda 		}
130157c2590fSTetsuo Handa 		if (tomoyo_is_no_rewrite_file(&buf))
130257c2590fSTetsuo Handa 			error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
130357c2590fSTetsuo Handa 						       &buf);
130457c2590fSTetsuo Handa 	}
130557c2590fSTetsuo Handa 	if (!error && acc_mode &&
130657c2590fSTetsuo Handa 	    tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
130757c2590fSTetsuo Handa 	    != TOMOYO_CONFIG_DISABLED) {
130857c2590fSTetsuo Handa 		if (!buf.name && !tomoyo_get_realpath(&buf, path)) {
130957c2590fSTetsuo Handa 			error = -ENOMEM;
131057c2590fSTetsuo Handa 			goto out;
131157c2590fSTetsuo Handa 		}
1312c8c57e84STetsuo Handa 		error = tomoyo_file_perm(&r, &buf, acc_mode);
131357c2590fSTetsuo Handa 	}
1314b69a54eeSKentaro Takeda  out:
1315c8c57e84STetsuo Handa 	kfree(buf.name);
1316fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1317cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1318b69a54eeSKentaro Takeda 		error = 0;
1319b69a54eeSKentaro Takeda 	return error;
1320b69a54eeSKentaro Takeda }
1321b69a54eeSKentaro Takeda 
1322b69a54eeSKentaro Takeda /**
13232106ccd9STetsuo Handa  * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
1324b69a54eeSKentaro Takeda  *
1325b69a54eeSKentaro Takeda  * @operation: Type of operation.
1326b69a54eeSKentaro Takeda  * @path:      Pointer to "struct path".
1327b69a54eeSKentaro Takeda  *
1328b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1329b69a54eeSKentaro Takeda  */
133097d6931eSTetsuo Handa int tomoyo_path_perm(const u8 operation, struct path *path)
1331b69a54eeSKentaro Takeda {
1332b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1333c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1334cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1335fdb8ebb7STetsuo Handa 	int idx;
1336b69a54eeSKentaro Takeda 
133757c2590fSTetsuo Handa 	if (!path->mnt)
1338b69a54eeSKentaro Takeda 		return 0;
133957c2590fSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
134057c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED)
134157c2590fSTetsuo Handa 		return 0;
134257c2590fSTetsuo Handa 	buf.name = NULL;
1343fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1344c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1345b69a54eeSKentaro Takeda 		goto out;
1346b69a54eeSKentaro Takeda 	switch (operation) {
1347cb0abe6aSTetsuo Handa 	case TOMOYO_TYPE_REWRITE:
1348c8c57e84STetsuo Handa 		if (!tomoyo_is_no_rewrite_file(&buf)) {
1349cb0abe6aSTetsuo Handa 			error = 0;
1350cb0abe6aSTetsuo Handa 			goto out;
1351cb0abe6aSTetsuo Handa 		}
1352cb0abe6aSTetsuo Handa 		break;
13537ef61233STetsuo Handa 	case TOMOYO_TYPE_RMDIR:
13547ef61233STetsuo Handa 	case TOMOYO_TYPE_CHROOT:
135557c2590fSTetsuo Handa 	case TOMOYO_TYPE_UMOUNT:
1356c8c57e84STetsuo Handa 		tomoyo_add_slash(&buf);
1357c8c57e84STetsuo Handa 		break;
1358b69a54eeSKentaro Takeda 	}
1359c8c57e84STetsuo Handa 	error = tomoyo_path_permission(&r, operation, &buf);
1360b69a54eeSKentaro Takeda  out:
1361c8c57e84STetsuo Handa 	kfree(buf.name);
1362fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1363cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1364b69a54eeSKentaro Takeda 		error = 0;
1365b69a54eeSKentaro Takeda 	return error;
1366b69a54eeSKentaro Takeda }
1367b69a54eeSKentaro Takeda 
1368b69a54eeSKentaro Takeda /**
1369a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_perm2 - Check permission for path/number/number/number operation.
1370a1f9bb6aSTetsuo Handa  *
1371a1f9bb6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1372a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
1373a1f9bb6aSTetsuo Handa  * @filename:  Filename to check.
1374a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
1375a1f9bb6aSTetsuo Handa  * @dev:       Device number.
1376a1f9bb6aSTetsuo Handa  *
1377a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1378a1f9bb6aSTetsuo Handa  *
1379a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1380a1f9bb6aSTetsuo Handa  */
1381a1f9bb6aSTetsuo Handa static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r,
1382a1f9bb6aSTetsuo Handa 				     const u8 operation,
1383a1f9bb6aSTetsuo Handa 				     const struct tomoyo_path_info *filename,
1384a1f9bb6aSTetsuo Handa 				     const unsigned int mode,
1385a1f9bb6aSTetsuo Handa 				     const unsigned int dev)
1386a1f9bb6aSTetsuo Handa {
1387a1f9bb6aSTetsuo Handa 	int error;
138817fcfbd9STetsuo Handa 	const char *msg;
1389a1f9bb6aSTetsuo Handa 	const unsigned int major = MAJOR(dev);
1390a1f9bb6aSTetsuo Handa 	const unsigned int minor = MINOR(dev);
1391a1f9bb6aSTetsuo Handa 
139217fcfbd9STetsuo Handa 	do {
139317fcfbd9STetsuo Handa 		error = tomoyo_path_number3_acl(r, filename, 1 << operation,
139417fcfbd9STetsuo Handa 						mode, major, minor);
1395a1f9bb6aSTetsuo Handa 		if (!error)
139617fcfbd9STetsuo Handa 			break;
139717fcfbd9STetsuo Handa 		msg = tomoyo_path_number32keyword(operation);
139817fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s 0%o %u %u", msg, filename->name,
139917fcfbd9STetsuo Handa 				mode, major, minor);
140017fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg,
140117fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename), mode,
140217fcfbd9STetsuo Handa 					  major, minor);
140317fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1404a1f9bb6aSTetsuo Handa         if (r->mode != TOMOYO_CONFIG_ENFORCING)
1405a1f9bb6aSTetsuo Handa 		error = 0;
1406a1f9bb6aSTetsuo Handa 	return error;
1407a1f9bb6aSTetsuo Handa }
1408a1f9bb6aSTetsuo Handa 
1409a1f9bb6aSTetsuo Handa /**
1410a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar".
1411a1f9bb6aSTetsuo Handa  *
1412a1f9bb6aSTetsuo Handa  * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK)
1413a1f9bb6aSTetsuo Handa  * @path:      Pointer to "struct path".
1414a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
1415a1f9bb6aSTetsuo Handa  * @dev:       Device number.
1416a1f9bb6aSTetsuo Handa  *
1417a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1418a1f9bb6aSTetsuo Handa  */
1419a1f9bb6aSTetsuo Handa int tomoyo_path_number3_perm(const u8 operation, struct path *path,
1420a1f9bb6aSTetsuo Handa 			     const unsigned int mode, unsigned int dev)
1421a1f9bb6aSTetsuo Handa {
1422a1f9bb6aSTetsuo Handa 	struct tomoyo_request_info r;
1423a1f9bb6aSTetsuo Handa 	int error = -ENOMEM;
1424c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1425a1f9bb6aSTetsuo Handa 	int idx;
1426a1f9bb6aSTetsuo Handa 
142757c2590fSTetsuo Handa 	if (!path->mnt ||
142857c2590fSTetsuo Handa 	    tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
142957c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED)
1430a1f9bb6aSTetsuo Handa 		return 0;
1431a1f9bb6aSTetsuo Handa 	idx = tomoyo_read_lock();
1432a1f9bb6aSTetsuo Handa 	error = -ENOMEM;
1433c8c57e84STetsuo Handa 	if (tomoyo_get_realpath(&buf, path)) {
1434c8c57e84STetsuo Handa 		error = tomoyo_path_number3_perm2(&r, operation, &buf, mode,
1435a1f9bb6aSTetsuo Handa 						  new_decode_dev(dev));
1436c8c57e84STetsuo Handa 		kfree(buf.name);
1437a1f9bb6aSTetsuo Handa 	}
1438a1f9bb6aSTetsuo Handa 	tomoyo_read_unlock(idx);
1439a1f9bb6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1440a1f9bb6aSTetsuo Handa 		error = 0;
1441a1f9bb6aSTetsuo Handa 	return error;
1442a1f9bb6aSTetsuo Handa }
1443a1f9bb6aSTetsuo Handa 
1444a1f9bb6aSTetsuo Handa /**
14457ef61233STetsuo Handa  * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
1446b69a54eeSKentaro Takeda  *
1447b69a54eeSKentaro Takeda  * @operation: Type of operation.
1448b69a54eeSKentaro Takeda  * @path1:      Pointer to "struct path".
1449b69a54eeSKentaro Takeda  * @path2:      Pointer to "struct path".
1450b69a54eeSKentaro Takeda  *
1451b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1452b69a54eeSKentaro Takeda  */
145397d6931eSTetsuo Handa int tomoyo_path2_perm(const u8 operation, struct path *path1,
1454b69a54eeSKentaro Takeda 		      struct path *path2)
1455b69a54eeSKentaro Takeda {
1456b69a54eeSKentaro Takeda 	int error = -ENOMEM;
145717fcfbd9STetsuo Handa 	const char *msg;
1458c8c57e84STetsuo Handa 	struct tomoyo_path_info buf1;
1459c8c57e84STetsuo Handa 	struct tomoyo_path_info buf2;
1460cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1461fdb8ebb7STetsuo Handa 	int idx;
1462b69a54eeSKentaro Takeda 
146357c2590fSTetsuo Handa 	if (!path1->mnt || !path2->mnt ||
146457c2590fSTetsuo Handa 	    tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
146557c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED)
1466b69a54eeSKentaro Takeda 		return 0;
1467c8c57e84STetsuo Handa 	buf1.name = NULL;
1468c8c57e84STetsuo Handa 	buf2.name = NULL;
1469fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1470c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf1, path1) ||
1471c8c57e84STetsuo Handa 	    !tomoyo_get_realpath(&buf2, path2))
1472b69a54eeSKentaro Takeda 		goto out;
147357c2590fSTetsuo Handa 	switch (operation) {
147457c2590fSTetsuo Handa 		struct dentry *dentry;
147557c2590fSTetsuo Handa 	case TOMOYO_TYPE_RENAME:
147657c2590fSTetsuo Handa         case TOMOYO_TYPE_LINK:
147757c2590fSTetsuo Handa 		dentry = path1->dentry;
147857c2590fSTetsuo Handa 	        if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
147957c2590fSTetsuo Handa                         break;
148057c2590fSTetsuo Handa                 /* fall through */
148157c2590fSTetsuo Handa         case TOMOYO_TYPE_PIVOT_ROOT:
1482c8c57e84STetsuo Handa                 tomoyo_add_slash(&buf1);
1483c8c57e84STetsuo Handa                 tomoyo_add_slash(&buf2);
148457c2590fSTetsuo Handa 		break;
1485b69a54eeSKentaro Takeda         }
148617fcfbd9STetsuo Handa 	do {
1487c8c57e84STetsuo Handa 		error = tomoyo_path2_acl(&r, operation, &buf1, &buf2);
1488b69a54eeSKentaro Takeda 		if (!error)
148917fcfbd9STetsuo Handa 			break;
149017fcfbd9STetsuo Handa 		msg = tomoyo_path22keyword(operation);
1491c8c57e84STetsuo Handa 		tomoyo_warn_log(&r, "%s %s %s", msg, buf1.name, buf2.name);
149217fcfbd9STetsuo Handa 		error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg,
1493c8c57e84STetsuo Handa 					  tomoyo_file_pattern(&buf1),
1494c8c57e84STetsuo Handa 					  tomoyo_file_pattern(&buf2));
149517fcfbd9STetsuo Handa         } while (error == TOMOYO_RETRY_REQUEST);
1496b69a54eeSKentaro Takeda  out:
1497c8c57e84STetsuo Handa 	kfree(buf1.name);
1498c8c57e84STetsuo Handa 	kfree(buf2.name);
1499fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1500cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1501b69a54eeSKentaro Takeda 		error = 0;
1502b69a54eeSKentaro Takeda 	return error;
1503b69a54eeSKentaro Takeda }
1504a1f9bb6aSTetsuo Handa 
1505a1f9bb6aSTetsuo Handa /**
1506a1f9bb6aSTetsuo Handa  * tomoyo_write_file_policy - Update file related list.
1507a1f9bb6aSTetsuo Handa  *
1508a1f9bb6aSTetsuo Handa  * @data:      String to parse.
1509a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
1510a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
1511a1f9bb6aSTetsuo Handa  *
1512a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1513a1f9bb6aSTetsuo Handa  *
1514a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1515a1f9bb6aSTetsuo Handa  */
1516a1f9bb6aSTetsuo Handa int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
1517a1f9bb6aSTetsuo Handa 			     const bool is_delete)
1518a1f9bb6aSTetsuo Handa {
1519a1f9bb6aSTetsuo Handa 	char *w[5];
1520a1f9bb6aSTetsuo Handa 	u8 type;
1521a1f9bb6aSTetsuo Handa 	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
1522a1f9bb6aSTetsuo Handa 		return -EINVAL;
1523237ab459STetsuo Handa 	if (strncmp(w[0], "allow_", 6))
1524a1f9bb6aSTetsuo Handa 		goto out;
1525a1f9bb6aSTetsuo Handa 	w[0] += 6;
1526a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
1527a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_keyword[type]))
1528a1f9bb6aSTetsuo Handa 			continue;
1529a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_acl(type, w[1], domain, is_delete);
1530a1f9bb6aSTetsuo Handa 	}
1531a1f9bb6aSTetsuo Handa 	if (!w[2][0])
1532a1f9bb6aSTetsuo Handa 		goto out;
1533a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
1534a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path2_keyword[type]))
1535a1f9bb6aSTetsuo Handa 			continue;
1536a1f9bb6aSTetsuo Handa 		return tomoyo_update_path2_acl(type, w[1], w[2], domain,
1537a1f9bb6aSTetsuo Handa 					       is_delete);
1538a1f9bb6aSTetsuo Handa 	}
1539a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
1540a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_number_keyword[type]))
1541a1f9bb6aSTetsuo Handa 			continue;
1542a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
1543a1f9bb6aSTetsuo Handa 						     is_delete);
1544a1f9bb6aSTetsuo Handa 	}
1545a1f9bb6aSTetsuo Handa 	if (!w[3][0] || !w[4][0])
1546a1f9bb6aSTetsuo Handa 		goto out;
1547a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER3_OPERATION; type++) {
1548a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_number3_keyword[type]))
1549a1f9bb6aSTetsuo Handa 			continue;
1550a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_number3_acl(type, w[1], w[2], w[3],
1551a1f9bb6aSTetsuo Handa 						      w[4], domain, is_delete);
1552a1f9bb6aSTetsuo Handa 	}
1553a1f9bb6aSTetsuo Handa  out:
1554a1f9bb6aSTetsuo Handa 	return -EINVAL;
1555a1f9bb6aSTetsuo Handa }
1556