xref: /openbmc/linux/security/tomoyo/file.c (revision c8c57e84)
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 
547762fbffSTetsuo Handa void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
557762fbffSTetsuo Handa {
567762fbffSTetsuo Handa 	if (!ptr)
577762fbffSTetsuo Handa 		return;
587762fbffSTetsuo Handa 	if (ptr->is_group)
597762fbffSTetsuo Handa 		tomoyo_put_path_group(ptr->group);
607762fbffSTetsuo Handa 	else
617762fbffSTetsuo Handa 		tomoyo_put_name(ptr->filename);
627762fbffSTetsuo Handa }
637762fbffSTetsuo Handa 
647762fbffSTetsuo Handa bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
657762fbffSTetsuo Handa 			       const struct tomoyo_name_union *ptr)
667762fbffSTetsuo Handa {
677762fbffSTetsuo Handa 	if (ptr->is_group)
687762fbffSTetsuo Handa 		return tomoyo_path_matches_group(name, ptr->group, 1);
697762fbffSTetsuo Handa 	return tomoyo_path_matches_pattern(name, ptr->filename);
707762fbffSTetsuo Handa }
717762fbffSTetsuo Handa 
727762fbffSTetsuo Handa static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info
737762fbffSTetsuo Handa 					      *name,
747762fbffSTetsuo Handa 					      const struct tomoyo_name_union
757762fbffSTetsuo Handa 					      *ptr, const bool may_use_pattern)
767762fbffSTetsuo Handa {
777762fbffSTetsuo Handa 	if (ptr->is_group)
787762fbffSTetsuo Handa 		return tomoyo_path_matches_group(name, ptr->group,
797762fbffSTetsuo Handa 						 may_use_pattern);
807762fbffSTetsuo Handa 	if (may_use_pattern || !ptr->filename->is_patterned)
817762fbffSTetsuo Handa 		return tomoyo_path_matches_pattern(name, ptr->filename);
827762fbffSTetsuo Handa 	return false;
837762fbffSTetsuo Handa }
847762fbffSTetsuo Handa 
854c3e9e2dSTetsuo Handa void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
864c3e9e2dSTetsuo Handa {
874c3e9e2dSTetsuo Handa 	if (ptr && ptr->is_group)
884c3e9e2dSTetsuo Handa 		tomoyo_put_number_group(ptr->group);
894c3e9e2dSTetsuo Handa }
904c3e9e2dSTetsuo Handa 
914c3e9e2dSTetsuo Handa bool tomoyo_compare_number_union(const unsigned long value,
924c3e9e2dSTetsuo Handa 				 const struct tomoyo_number_union *ptr)
934c3e9e2dSTetsuo Handa {
944c3e9e2dSTetsuo Handa 	if (ptr->is_group)
954c3e9e2dSTetsuo Handa 		return tomoyo_number_matches_group(value, value, ptr->group);
964c3e9e2dSTetsuo Handa 	return value >= ptr->values[0] && value <= ptr->values[1];
974c3e9e2dSTetsuo Handa }
984c3e9e2dSTetsuo Handa 
99b69a54eeSKentaro Takeda /**
1007ef61233STetsuo Handa  * tomoyo_path2keyword - Get the name of single path operation.
101b69a54eeSKentaro Takeda  *
102b69a54eeSKentaro Takeda  * @operation: Type of operation.
103b69a54eeSKentaro Takeda  *
104b69a54eeSKentaro Takeda  * Returns the name of single path operation.
105b69a54eeSKentaro Takeda  */
1067ef61233STetsuo Handa const char *tomoyo_path2keyword(const u8 operation)
107b69a54eeSKentaro Takeda {
1087ef61233STetsuo Handa 	return (operation < TOMOYO_MAX_PATH_OPERATION)
1097ef61233STetsuo Handa 		? tomoyo_path_keyword[operation] : NULL;
110b69a54eeSKentaro Takeda }
111b69a54eeSKentaro Takeda 
112b69a54eeSKentaro Takeda /**
113a1f9bb6aSTetsuo Handa  * tomoyo_path_number32keyword - Get the name of path/number/number/number operations.
114a1f9bb6aSTetsuo Handa  *
115a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
116a1f9bb6aSTetsuo Handa  *
117a1f9bb6aSTetsuo Handa  * Returns the name of path/number/number/number operation.
118a1f9bb6aSTetsuo Handa  */
119a1f9bb6aSTetsuo Handa const char *tomoyo_path_number32keyword(const u8 operation)
120a1f9bb6aSTetsuo Handa {
121a1f9bb6aSTetsuo Handa 	return (operation < TOMOYO_MAX_PATH_NUMBER3_OPERATION)
122a1f9bb6aSTetsuo Handa 		? tomoyo_path_number3_keyword[operation] : NULL;
123a1f9bb6aSTetsuo Handa }
124a1f9bb6aSTetsuo Handa 
125a1f9bb6aSTetsuo Handa /**
1267ef61233STetsuo Handa  * tomoyo_path22keyword - Get the name of double path operation.
127b69a54eeSKentaro Takeda  *
128b69a54eeSKentaro Takeda  * @operation: Type of operation.
129b69a54eeSKentaro Takeda  *
130b69a54eeSKentaro Takeda  * Returns the name of double path operation.
131b69a54eeSKentaro Takeda  */
1327ef61233STetsuo Handa const char *tomoyo_path22keyword(const u8 operation)
133b69a54eeSKentaro Takeda {
1347ef61233STetsuo Handa 	return (operation < TOMOYO_MAX_PATH2_OPERATION)
1357ef61233STetsuo Handa 		? tomoyo_path2_keyword[operation] : NULL;
136b69a54eeSKentaro Takeda }
137b69a54eeSKentaro Takeda 
138b69a54eeSKentaro Takeda /**
139a1f9bb6aSTetsuo Handa  * tomoyo_path_number2keyword - Get the name of path/number operations.
140a1f9bb6aSTetsuo Handa  *
141a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
142a1f9bb6aSTetsuo Handa  *
143a1f9bb6aSTetsuo Handa  * Returns the name of path/number operation.
144a1f9bb6aSTetsuo Handa  */
145a1f9bb6aSTetsuo Handa const char *tomoyo_path_number2keyword(const u8 operation)
146a1f9bb6aSTetsuo Handa {
147a1f9bb6aSTetsuo Handa 	return (operation < TOMOYO_MAX_PATH_NUMBER_OPERATION)
148a1f9bb6aSTetsuo Handa 		? tomoyo_path_number_keyword[operation] : NULL;
149a1f9bb6aSTetsuo Handa }
150a1f9bb6aSTetsuo Handa 
151c8c57e84STetsuo Handa static void tomoyo_add_slash(struct tomoyo_path_info *buf)
152c8c57e84STetsuo Handa {
153c8c57e84STetsuo Handa 	if (buf->is_dir)
154c8c57e84STetsuo Handa 		return;
155c8c57e84STetsuo Handa 	/*
156c8c57e84STetsuo Handa 	 * This is OK because tomoyo_encode() reserves space for appending "/".
157c8c57e84STetsuo Handa 	 */
158c8c57e84STetsuo Handa 	strcat((char *) buf->name, "/");
159c8c57e84STetsuo Handa 	tomoyo_fill_path_info(buf);
160c8c57e84STetsuo Handa }
161c8c57e84STetsuo Handa 
162a1f9bb6aSTetsuo Handa /**
163b69a54eeSKentaro Takeda  * tomoyo_strendswith - Check whether the token ends with the given token.
164b69a54eeSKentaro Takeda  *
165b69a54eeSKentaro Takeda  * @name: The token to check.
166b69a54eeSKentaro Takeda  * @tail: The token to find.
167b69a54eeSKentaro Takeda  *
168b69a54eeSKentaro Takeda  * Returns true if @name ends with @tail, false otherwise.
169b69a54eeSKentaro Takeda  */
170b69a54eeSKentaro Takeda static bool tomoyo_strendswith(const char *name, const char *tail)
171b69a54eeSKentaro Takeda {
172b69a54eeSKentaro Takeda 	int len;
173b69a54eeSKentaro Takeda 
174b69a54eeSKentaro Takeda 	if (!name || !tail)
175b69a54eeSKentaro Takeda 		return false;
176b69a54eeSKentaro Takeda 	len = strlen(name) - strlen(tail);
177b69a54eeSKentaro Takeda 	return len >= 0 && !strcmp(name + len, tail);
178b69a54eeSKentaro Takeda }
179b69a54eeSKentaro Takeda 
180b69a54eeSKentaro Takeda /**
181c8c57e84STetsuo Handa  * tomoyo_get_realpath - Get realpath.
182b69a54eeSKentaro Takeda  *
183c8c57e84STetsuo Handa  * @buf:  Pointer to "struct tomoyo_path_info".
184b69a54eeSKentaro Takeda  * @path: Pointer to "struct path".
185b69a54eeSKentaro Takeda  *
186c8c57e84STetsuo Handa  * Returns true on success, false otherwise.
187b69a54eeSKentaro Takeda  */
188c8c57e84STetsuo Handa static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)
189b69a54eeSKentaro Takeda {
190c8c57e84STetsuo Handa 	buf->name = tomoyo_realpath_from_path(path);
191c8c57e84STetsuo Handa 	if (buf->name) {
192c8c57e84STetsuo Handa 		tomoyo_fill_path_info(buf);
193c8c57e84STetsuo Handa 		return true;
194b69a54eeSKentaro Takeda 	}
195c8c57e84STetsuo Handa         return false;
196b69a54eeSKentaro Takeda }
197b69a54eeSKentaro Takeda 
1987ef61233STetsuo Handa static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
199b69a54eeSKentaro Takeda 				   const char *filename2,
2007ef61233STetsuo Handa 				   struct tomoyo_domain_info *const domain,
2017ef61233STetsuo Handa 				   const bool is_delete);
2027ef61233STetsuo Handa static int tomoyo_update_path_acl(const u8 type, const char *filename,
2037ef61233STetsuo Handa 				  struct tomoyo_domain_info *const domain,
2047ef61233STetsuo Handa 				  const bool is_delete);
205b69a54eeSKentaro Takeda 
206c3fa109aSTetsuo Handa /*
207c3fa109aSTetsuo Handa  * tomoyo_globally_readable_list is used for holding list of pathnames which
208c3fa109aSTetsuo Handa  * are by default allowed to be open()ed for reading by any process.
209c3fa109aSTetsuo Handa  *
210c3fa109aSTetsuo Handa  * An entry is added by
211c3fa109aSTetsuo Handa  *
212c3fa109aSTetsuo Handa  * # echo 'allow_read /lib/libc-2.5.so' > \
213c3fa109aSTetsuo Handa  *                               /sys/kernel/security/tomoyo/exception_policy
214c3fa109aSTetsuo Handa  *
215c3fa109aSTetsuo Handa  * and is deleted by
216c3fa109aSTetsuo Handa  *
217c3fa109aSTetsuo Handa  * # echo 'delete allow_read /lib/libc-2.5.so' > \
218c3fa109aSTetsuo Handa  *                               /sys/kernel/security/tomoyo/exception_policy
219c3fa109aSTetsuo Handa  *
220c3fa109aSTetsuo Handa  * and all entries are retrieved by
221c3fa109aSTetsuo Handa  *
222c3fa109aSTetsuo Handa  * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy
223c3fa109aSTetsuo Handa  *
224c3fa109aSTetsuo Handa  * In the example above, any process is allowed to
225c3fa109aSTetsuo Handa  * open("/lib/libc-2.5.so", O_RDONLY).
226c3fa109aSTetsuo Handa  * One exception is, if the domain which current process belongs to is marked
227c3fa109aSTetsuo Handa  * as "ignore_global_allow_read", current process can't do so unless explicitly
228c3fa109aSTetsuo Handa  * given "allow_read /lib/libc-2.5.so" to the domain which current process
229c3fa109aSTetsuo Handa  * belongs to.
230c3fa109aSTetsuo Handa  */
231847b173eSTetsuo Handa LIST_HEAD(tomoyo_globally_readable_list);
232b69a54eeSKentaro Takeda 
233b69a54eeSKentaro Takeda /**
234b69a54eeSKentaro Takeda  * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
235b69a54eeSKentaro Takeda  *
236b69a54eeSKentaro Takeda  * @filename:  Filename unconditionally permitted to open() for reading.
237b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
238b69a54eeSKentaro Takeda  *
239b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
240fdb8ebb7STetsuo Handa  *
241fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
242b69a54eeSKentaro Takeda  */
243b69a54eeSKentaro Takeda static int tomoyo_update_globally_readable_entry(const char *filename,
244b69a54eeSKentaro Takeda 						 const bool is_delete)
245b69a54eeSKentaro Takeda {
246b69a54eeSKentaro Takeda 	struct tomoyo_globally_readable_file_entry *ptr;
2479e4b50e9STetsuo Handa 	struct tomoyo_globally_readable_file_entry e = { };
248ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
249b69a54eeSKentaro Takeda 
25017080008STetsuo Handa 	if (!tomoyo_is_correct_path(filename, 1, 0, -1))
251b69a54eeSKentaro Takeda 		return -EINVAL;
2529e4b50e9STetsuo Handa 	e.filename = tomoyo_get_name(filename);
2539e4b50e9STetsuo Handa 	if (!e.filename)
254b69a54eeSKentaro Takeda 		return -ENOMEM;
25529282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
25629282381STetsuo Handa 		goto out;
257fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
2589e4b50e9STetsuo Handa 		if (ptr->filename != e.filename)
259b69a54eeSKentaro Takeda 			continue;
260b69a54eeSKentaro Takeda 		ptr->is_deleted = is_delete;
261b69a54eeSKentaro Takeda 		error = 0;
262ca0b7df3STetsuo Handa 		break;
263b69a54eeSKentaro Takeda 	}
2649e4b50e9STetsuo Handa 	if (!is_delete && error) {
2659e4b50e9STetsuo Handa 		struct tomoyo_globally_readable_file_entry *entry =
2669e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
2679e4b50e9STetsuo Handa 		if (entry) {
2689e4b50e9STetsuo Handa 			list_add_tail_rcu(&entry->list,
2699e4b50e9STetsuo Handa 					  &tomoyo_globally_readable_list);
270b69a54eeSKentaro Takeda 			error = 0;
271ca0b7df3STetsuo Handa 		}
2729e4b50e9STetsuo Handa 	}
273f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
27429282381STetsuo Handa  out:
2759e4b50e9STetsuo Handa 	tomoyo_put_name(e.filename);
276b69a54eeSKentaro Takeda 	return error;
277b69a54eeSKentaro Takeda }
278b69a54eeSKentaro Takeda 
279b69a54eeSKentaro Takeda /**
280b69a54eeSKentaro Takeda  * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
281b69a54eeSKentaro Takeda  *
282b69a54eeSKentaro Takeda  * @filename: The filename to check.
283b69a54eeSKentaro Takeda  *
284b69a54eeSKentaro Takeda  * Returns true if any domain can open @filename for reading, false otherwise.
285fdb8ebb7STetsuo Handa  *
286fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
287b69a54eeSKentaro Takeda  */
288b69a54eeSKentaro Takeda static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
289b69a54eeSKentaro Takeda 					     filename)
290b69a54eeSKentaro Takeda {
291b69a54eeSKentaro Takeda 	struct tomoyo_globally_readable_file_entry *ptr;
292b69a54eeSKentaro Takeda 	bool found = false;
293fdb8ebb7STetsuo Handa 
294fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
295b69a54eeSKentaro Takeda 		if (!ptr->is_deleted &&
296b69a54eeSKentaro Takeda 		    tomoyo_path_matches_pattern(filename, ptr->filename)) {
297b69a54eeSKentaro Takeda 			found = true;
298b69a54eeSKentaro Takeda 			break;
299b69a54eeSKentaro Takeda 		}
300b69a54eeSKentaro Takeda 	}
301b69a54eeSKentaro Takeda 	return found;
302b69a54eeSKentaro Takeda }
303b69a54eeSKentaro Takeda 
304b69a54eeSKentaro Takeda /**
305b69a54eeSKentaro Takeda  * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
306b69a54eeSKentaro Takeda  *
307b69a54eeSKentaro Takeda  * @data:      String to parse.
308b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
309b69a54eeSKentaro Takeda  *
310b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
311fdb8ebb7STetsuo Handa  *
312fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
313b69a54eeSKentaro Takeda  */
314b69a54eeSKentaro Takeda int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
315b69a54eeSKentaro Takeda {
316b69a54eeSKentaro Takeda 	return tomoyo_update_globally_readable_entry(data, is_delete);
317b69a54eeSKentaro Takeda }
318b69a54eeSKentaro Takeda 
319b69a54eeSKentaro Takeda /**
320b69a54eeSKentaro Takeda  * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
321b69a54eeSKentaro Takeda  *
322b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
323b69a54eeSKentaro Takeda  *
324b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
325fdb8ebb7STetsuo Handa  *
326fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
327b69a54eeSKentaro Takeda  */
328b69a54eeSKentaro Takeda bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
329b69a54eeSKentaro Takeda {
330b69a54eeSKentaro Takeda 	struct list_head *pos;
331b69a54eeSKentaro Takeda 	bool done = true;
332b69a54eeSKentaro Takeda 
333b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2,
334b69a54eeSKentaro Takeda 			     &tomoyo_globally_readable_list) {
335b69a54eeSKentaro Takeda 		struct tomoyo_globally_readable_file_entry *ptr;
336b69a54eeSKentaro Takeda 		ptr = list_entry(pos,
337b69a54eeSKentaro Takeda 				 struct tomoyo_globally_readable_file_entry,
338b69a54eeSKentaro Takeda 				 list);
339b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
340b69a54eeSKentaro Takeda 			continue;
3417d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
3427d2948b1STetsuo Handa 					ptr->filename->name);
3437d2948b1STetsuo Handa 		if (!done)
344b69a54eeSKentaro Takeda 			break;
345b69a54eeSKentaro Takeda 	}
346b69a54eeSKentaro Takeda 	return done;
347b69a54eeSKentaro Takeda }
348b69a54eeSKentaro Takeda 
349c3fa109aSTetsuo Handa /* tomoyo_pattern_list is used for holding list of pathnames which are used for
350c3fa109aSTetsuo Handa  * converting pathnames to pathname patterns during learning mode.
351c3fa109aSTetsuo Handa  *
352c3fa109aSTetsuo Handa  * An entry is added by
353c3fa109aSTetsuo Handa  *
354c3fa109aSTetsuo Handa  * # echo 'file_pattern /proc/\$/mounts' > \
355c3fa109aSTetsuo Handa  *                             /sys/kernel/security/tomoyo/exception_policy
356c3fa109aSTetsuo Handa  *
357c3fa109aSTetsuo Handa  * and is deleted by
358c3fa109aSTetsuo Handa  *
359c3fa109aSTetsuo Handa  * # echo 'delete file_pattern /proc/\$/mounts' > \
360c3fa109aSTetsuo Handa  *                             /sys/kernel/security/tomoyo/exception_policy
361c3fa109aSTetsuo Handa  *
362c3fa109aSTetsuo Handa  * and all entries are retrieved by
363c3fa109aSTetsuo Handa  *
364c3fa109aSTetsuo Handa  * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy
365c3fa109aSTetsuo Handa  *
366c3fa109aSTetsuo Handa  * In the example above, if a process which belongs to a domain which is in
367c3fa109aSTetsuo Handa  * learning mode requested open("/proc/1/mounts", O_RDONLY),
368c3fa109aSTetsuo Handa  * "allow_read /proc/\$/mounts" is automatically added to the domain which that
369c3fa109aSTetsuo Handa  * process belongs to.
370c3fa109aSTetsuo Handa  *
371c3fa109aSTetsuo Handa  * It is not a desirable behavior that we have to use /proc/\$/ instead of
372c3fa109aSTetsuo Handa  * /proc/self/ when current process needs to access only current process's
373c3fa109aSTetsuo Handa  * information. As of now, LSM version of TOMOYO is using __d_path() for
374c3fa109aSTetsuo Handa  * calculating pathname. Non LSM version of TOMOYO is using its own function
375c3fa109aSTetsuo Handa  * which pretends as if /proc/self/ is not a symlink; so that we can forbid
376c3fa109aSTetsuo Handa  * current process from accessing other process's information.
377c3fa109aSTetsuo Handa  */
378847b173eSTetsuo Handa LIST_HEAD(tomoyo_pattern_list);
379b69a54eeSKentaro Takeda 
380b69a54eeSKentaro Takeda /**
381b69a54eeSKentaro Takeda  * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
382b69a54eeSKentaro Takeda  *
383b69a54eeSKentaro Takeda  * @pattern:   Pathname pattern.
384b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
385b69a54eeSKentaro Takeda  *
386b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
387fdb8ebb7STetsuo Handa  *
388fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
389b69a54eeSKentaro Takeda  */
390b69a54eeSKentaro Takeda static int tomoyo_update_file_pattern_entry(const char *pattern,
391b69a54eeSKentaro Takeda 					    const bool is_delete)
392b69a54eeSKentaro Takeda {
393b69a54eeSKentaro Takeda 	struct tomoyo_pattern_entry *ptr;
3949e4b50e9STetsuo Handa 	struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) };
395ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
396b69a54eeSKentaro Takeda 
3979e4b50e9STetsuo Handa 	if (!e.pattern)
398ca0b7df3STetsuo Handa 		return error;
3999e4b50e9STetsuo Handa 	if (!e.pattern->is_patterned)
400ca0b7df3STetsuo Handa 		goto out;
40129282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
40229282381STetsuo Handa 		goto out;
403fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
4049e4b50e9STetsuo Handa 		if (e.pattern != ptr->pattern)
405b69a54eeSKentaro Takeda 			continue;
406b69a54eeSKentaro Takeda 		ptr->is_deleted = is_delete;
407b69a54eeSKentaro Takeda 		error = 0;
408ca0b7df3STetsuo Handa 		break;
409b69a54eeSKentaro Takeda 	}
4109e4b50e9STetsuo Handa 	if (!is_delete && error) {
4119e4b50e9STetsuo Handa 		struct tomoyo_pattern_entry *entry =
4129e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
4139e4b50e9STetsuo Handa 		if (entry) {
414ca0b7df3STetsuo Handa 			list_add_tail_rcu(&entry->list, &tomoyo_pattern_list);
415b69a54eeSKentaro Takeda 			error = 0;
416ca0b7df3STetsuo Handa 		}
4179e4b50e9STetsuo Handa 	}
418f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
419ca0b7df3STetsuo Handa  out:
4209e4b50e9STetsuo Handa 	tomoyo_put_name(e.pattern);
421b69a54eeSKentaro Takeda 	return error;
422b69a54eeSKentaro Takeda }
423b69a54eeSKentaro Takeda 
424b69a54eeSKentaro Takeda /**
42517fcfbd9STetsuo Handa  * tomoyo_file_pattern - Get patterned pathname.
426b69a54eeSKentaro Takeda  *
427b69a54eeSKentaro Takeda  * @filename: The filename to find patterned pathname.
428b69a54eeSKentaro Takeda  *
429b69a54eeSKentaro Takeda  * Returns pointer to pathname pattern if matched, @filename otherwise.
430fdb8ebb7STetsuo Handa  *
431fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
432b69a54eeSKentaro Takeda  */
43317fcfbd9STetsuo Handa const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename)
434b69a54eeSKentaro Takeda {
435b69a54eeSKentaro Takeda 	struct tomoyo_pattern_entry *ptr;
436b69a54eeSKentaro Takeda 	const struct tomoyo_path_info *pattern = NULL;
437b69a54eeSKentaro Takeda 
438fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
439b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
440b69a54eeSKentaro Takeda 			continue;
441b69a54eeSKentaro Takeda 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
442b69a54eeSKentaro Takeda 			continue;
443b69a54eeSKentaro Takeda 		pattern = ptr->pattern;
444b69a54eeSKentaro Takeda 		if (tomoyo_strendswith(pattern->name, "/\\*")) {
445b69a54eeSKentaro Takeda 			/* Do nothing. Try to find the better match. */
446b69a54eeSKentaro Takeda 		} else {
447b69a54eeSKentaro Takeda 			/* This would be the better match. Use this. */
448b69a54eeSKentaro Takeda 			break;
449b69a54eeSKentaro Takeda 		}
450b69a54eeSKentaro Takeda 	}
451b69a54eeSKentaro Takeda 	if (pattern)
452b69a54eeSKentaro Takeda 		filename = pattern;
45317fcfbd9STetsuo Handa 	return filename->name;
454b69a54eeSKentaro Takeda }
455b69a54eeSKentaro Takeda 
456b69a54eeSKentaro Takeda /**
457b69a54eeSKentaro Takeda  * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
458b69a54eeSKentaro Takeda  *
459b69a54eeSKentaro Takeda  * @data:      String to parse.
460b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
461b69a54eeSKentaro Takeda  *
462b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
463fdb8ebb7STetsuo Handa  *
464fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
465b69a54eeSKentaro Takeda  */
466b69a54eeSKentaro Takeda int tomoyo_write_pattern_policy(char *data, const bool is_delete)
467b69a54eeSKentaro Takeda {
468b69a54eeSKentaro Takeda 	return tomoyo_update_file_pattern_entry(data, is_delete);
469b69a54eeSKentaro Takeda }
470b69a54eeSKentaro Takeda 
471b69a54eeSKentaro Takeda /**
472b69a54eeSKentaro Takeda  * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
473b69a54eeSKentaro Takeda  *
474b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
475b69a54eeSKentaro Takeda  *
476b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
477fdb8ebb7STetsuo Handa  *
478fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
479b69a54eeSKentaro Takeda  */
480b69a54eeSKentaro Takeda bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
481b69a54eeSKentaro Takeda {
482b69a54eeSKentaro Takeda 	struct list_head *pos;
483b69a54eeSKentaro Takeda 	bool done = true;
484b69a54eeSKentaro Takeda 
485b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
486b69a54eeSKentaro Takeda 		struct tomoyo_pattern_entry *ptr;
487b69a54eeSKentaro Takeda 		ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
488b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
489b69a54eeSKentaro Takeda 			continue;
4907d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
4917d2948b1STetsuo Handa 					"%s\n", ptr->pattern->name);
4927d2948b1STetsuo Handa 		if (!done)
493b69a54eeSKentaro Takeda 			break;
494b69a54eeSKentaro Takeda 	}
495b69a54eeSKentaro Takeda 	return done;
496b69a54eeSKentaro Takeda }
497b69a54eeSKentaro Takeda 
498c3fa109aSTetsuo Handa /*
499c3fa109aSTetsuo Handa  * tomoyo_no_rewrite_list is used for holding list of pathnames which are by
500c3fa109aSTetsuo Handa  * default forbidden to modify already written content of a file.
501c3fa109aSTetsuo Handa  *
502c3fa109aSTetsuo Handa  * An entry is added by
503c3fa109aSTetsuo Handa  *
504c3fa109aSTetsuo Handa  * # echo 'deny_rewrite /var/log/messages' > \
505c3fa109aSTetsuo Handa  *                              /sys/kernel/security/tomoyo/exception_policy
506c3fa109aSTetsuo Handa  *
507c3fa109aSTetsuo Handa  * and is deleted by
508c3fa109aSTetsuo Handa  *
509c3fa109aSTetsuo Handa  * # echo 'delete deny_rewrite /var/log/messages' > \
510c3fa109aSTetsuo Handa  *                              /sys/kernel/security/tomoyo/exception_policy
511c3fa109aSTetsuo Handa  *
512c3fa109aSTetsuo Handa  * and all entries are retrieved by
513c3fa109aSTetsuo Handa  *
514c3fa109aSTetsuo Handa  * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy
515c3fa109aSTetsuo Handa  *
516c3fa109aSTetsuo Handa  * In the example above, if a process requested to rewrite /var/log/messages ,
517c3fa109aSTetsuo Handa  * the process can't rewrite unless the domain which that process belongs to
518c3fa109aSTetsuo Handa  * has "allow_rewrite /var/log/messages" entry.
519c3fa109aSTetsuo Handa  *
520c3fa109aSTetsuo Handa  * It is not a desirable behavior that we have to add "\040(deleted)" suffix
521c3fa109aSTetsuo Handa  * when we want to allow rewriting already unlink()ed file. As of now,
522c3fa109aSTetsuo Handa  * LSM version of TOMOYO is using __d_path() for calculating pathname.
523c3fa109aSTetsuo Handa  * Non LSM version of TOMOYO is using its own function which doesn't append
524c3fa109aSTetsuo Handa  * " (deleted)" suffix if the file is already unlink()ed; so that we don't
525c3fa109aSTetsuo Handa  * need to worry whether the file is already unlink()ed or not.
526c3fa109aSTetsuo Handa  */
527847b173eSTetsuo Handa LIST_HEAD(tomoyo_no_rewrite_list);
528b69a54eeSKentaro Takeda 
529b69a54eeSKentaro Takeda /**
530b69a54eeSKentaro Takeda  * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
531b69a54eeSKentaro Takeda  *
532b69a54eeSKentaro Takeda  * @pattern:   Pathname pattern that are not rewritable by default.
533b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
534b69a54eeSKentaro Takeda  *
535b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
536fdb8ebb7STetsuo Handa  *
537fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
538b69a54eeSKentaro Takeda  */
539b69a54eeSKentaro Takeda static int tomoyo_update_no_rewrite_entry(const char *pattern,
540b69a54eeSKentaro Takeda 					  const bool is_delete)
541b69a54eeSKentaro Takeda {
542ca0b7df3STetsuo Handa 	struct tomoyo_no_rewrite_entry *ptr;
5439e4b50e9STetsuo Handa 	struct tomoyo_no_rewrite_entry e = { };
544ca0b7df3STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
545b69a54eeSKentaro Takeda 
54617080008STetsuo Handa 	if (!tomoyo_is_correct_path(pattern, 0, 0, 0))
547b69a54eeSKentaro Takeda 		return -EINVAL;
5489e4b50e9STetsuo Handa 	e.pattern = tomoyo_get_name(pattern);
5499e4b50e9STetsuo Handa 	if (!e.pattern)
550ca0b7df3STetsuo Handa 		return error;
55129282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
55229282381STetsuo Handa 		goto out;
553fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
5549e4b50e9STetsuo Handa 		if (ptr->pattern != e.pattern)
555b69a54eeSKentaro Takeda 			continue;
556b69a54eeSKentaro Takeda 		ptr->is_deleted = is_delete;
557b69a54eeSKentaro Takeda 		error = 0;
558ca0b7df3STetsuo Handa 		break;
559b69a54eeSKentaro Takeda 	}
5609e4b50e9STetsuo Handa 	if (!is_delete && error) {
5619e4b50e9STetsuo Handa 		struct tomoyo_no_rewrite_entry *entry =
5629e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
5639e4b50e9STetsuo Handa 		if (entry) {
5649e4b50e9STetsuo Handa 			list_add_tail_rcu(&entry->list,
5659e4b50e9STetsuo Handa 					  &tomoyo_no_rewrite_list);
566b69a54eeSKentaro Takeda 			error = 0;
567ca0b7df3STetsuo Handa 		}
5689e4b50e9STetsuo Handa 	}
569f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
57029282381STetsuo Handa  out:
5719e4b50e9STetsuo Handa 	tomoyo_put_name(e.pattern);
572b69a54eeSKentaro Takeda 	return error;
573b69a54eeSKentaro Takeda }
574b69a54eeSKentaro Takeda 
575b69a54eeSKentaro Takeda /**
576b69a54eeSKentaro Takeda  * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
577b69a54eeSKentaro Takeda  *
578b69a54eeSKentaro Takeda  * @filename: Filename to check.
579b69a54eeSKentaro Takeda  *
580b69a54eeSKentaro Takeda  * Returns true if @filename is specified by "deny_rewrite" directive,
581b69a54eeSKentaro Takeda  * false otherwise.
582fdb8ebb7STetsuo Handa  *
583fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
584b69a54eeSKentaro Takeda  */
585b69a54eeSKentaro Takeda static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
586b69a54eeSKentaro Takeda {
587b69a54eeSKentaro Takeda 	struct tomoyo_no_rewrite_entry *ptr;
588b69a54eeSKentaro Takeda 	bool found = false;
589b69a54eeSKentaro Takeda 
590fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
591b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
592b69a54eeSKentaro Takeda 			continue;
593b69a54eeSKentaro Takeda 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
594b69a54eeSKentaro Takeda 			continue;
595b69a54eeSKentaro Takeda 		found = true;
596b69a54eeSKentaro Takeda 		break;
597b69a54eeSKentaro Takeda 	}
598b69a54eeSKentaro Takeda 	return found;
599b69a54eeSKentaro Takeda }
600b69a54eeSKentaro Takeda 
601b69a54eeSKentaro Takeda /**
602b69a54eeSKentaro Takeda  * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
603b69a54eeSKentaro Takeda  *
604b69a54eeSKentaro Takeda  * @data:      String to parse.
605b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
606b69a54eeSKentaro Takeda  *
607b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
608fdb8ebb7STetsuo Handa  *
609fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
610b69a54eeSKentaro Takeda  */
611b69a54eeSKentaro Takeda int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
612b69a54eeSKentaro Takeda {
613b69a54eeSKentaro Takeda 	return tomoyo_update_no_rewrite_entry(data, is_delete);
614b69a54eeSKentaro Takeda }
615b69a54eeSKentaro Takeda 
616b69a54eeSKentaro Takeda /**
617b69a54eeSKentaro Takeda  * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
618b69a54eeSKentaro Takeda  *
619b69a54eeSKentaro Takeda  * @head: Pointer to "struct tomoyo_io_buffer".
620b69a54eeSKentaro Takeda  *
621b69a54eeSKentaro Takeda  * Returns true on success, false otherwise.
622fdb8ebb7STetsuo Handa  *
623fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
624b69a54eeSKentaro Takeda  */
625b69a54eeSKentaro Takeda bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
626b69a54eeSKentaro Takeda {
627b69a54eeSKentaro Takeda 	struct list_head *pos;
628b69a54eeSKentaro Takeda 	bool done = true;
629b69a54eeSKentaro Takeda 
630b69a54eeSKentaro Takeda 	list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
631b69a54eeSKentaro Takeda 		struct tomoyo_no_rewrite_entry *ptr;
632b69a54eeSKentaro Takeda 		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
633b69a54eeSKentaro Takeda 		if (ptr->is_deleted)
634b69a54eeSKentaro Takeda 			continue;
6357d2948b1STetsuo Handa 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
6367d2948b1STetsuo Handa 					"%s\n", ptr->pattern->name);
6377d2948b1STetsuo Handa 		if (!done)
638b69a54eeSKentaro Takeda 			break;
639b69a54eeSKentaro Takeda 	}
640b69a54eeSKentaro Takeda 	return done;
641b69a54eeSKentaro Takeda }
642b69a54eeSKentaro Takeda 
643b69a54eeSKentaro Takeda /**
644b69a54eeSKentaro Takeda  * tomoyo_update_file_acl - Update file's read/write/execute ACL.
645b69a54eeSKentaro Takeda  *
646b69a54eeSKentaro Takeda  * @perm:      Permission (between 1 to 7).
647a1f9bb6aSTetsuo Handa  * @filename:  Filename.
648b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
649b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
650b69a54eeSKentaro Takeda  *
651b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
652b69a54eeSKentaro Takeda  *
653b69a54eeSKentaro Takeda  * This is legacy support interface for older policy syntax.
654b69a54eeSKentaro Takeda  * Current policy syntax uses "allow_read/write" instead of "6",
655b69a54eeSKentaro Takeda  * "allow_read" instead of "4", "allow_write" instead of "2",
656b69a54eeSKentaro Takeda  * "allow_execute" instead of "1".
657fdb8ebb7STetsuo Handa  *
658fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
659b69a54eeSKentaro Takeda  */
660a1f9bb6aSTetsuo Handa static int tomoyo_update_file_acl(u8 perm, const char *filename,
661b69a54eeSKentaro Takeda 				  struct tomoyo_domain_info * const domain,
662b69a54eeSKentaro Takeda 				  const bool is_delete)
663b69a54eeSKentaro Takeda {
664b69a54eeSKentaro Takeda 	if (perm > 7 || !perm) {
665b69a54eeSKentaro Takeda 		printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n",
666b69a54eeSKentaro Takeda 		       __func__, perm, filename);
667b69a54eeSKentaro Takeda 		return -EINVAL;
668b69a54eeSKentaro Takeda 	}
669b69a54eeSKentaro Takeda 	if (filename[0] != '@' && tomoyo_strendswith(filename, "/"))
670b69a54eeSKentaro Takeda 		/*
671b69a54eeSKentaro Takeda 		 * Only 'allow_mkdir' and 'allow_rmdir' are valid for
672b69a54eeSKentaro Takeda 		 * directory permissions.
673b69a54eeSKentaro Takeda 		 */
674b69a54eeSKentaro Takeda 		return 0;
675b69a54eeSKentaro Takeda 	if (perm & 4)
6767ef61233STetsuo Handa 		tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain,
6777ef61233STetsuo Handa 				       is_delete);
678b69a54eeSKentaro Takeda 	if (perm & 2)
6797ef61233STetsuo Handa 		tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain,
6807ef61233STetsuo Handa 				       is_delete);
681b69a54eeSKentaro Takeda 	if (perm & 1)
6827ef61233STetsuo Handa 		tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain,
6837ef61233STetsuo Handa 				       is_delete);
684b69a54eeSKentaro Takeda 	return 0;
685b69a54eeSKentaro Takeda }
686b69a54eeSKentaro Takeda 
687b69a54eeSKentaro Takeda /**
688cb0abe6aSTetsuo Handa  * tomoyo_path_acl - Check permission for single path operation.
689b69a54eeSKentaro Takeda  *
690cb0abe6aSTetsuo Handa  * @r:               Pointer to "struct tomoyo_request_info".
691b69a54eeSKentaro Takeda  * @filename:        Filename to check.
692b69a54eeSKentaro Takeda  * @perm:            Permission.
693b69a54eeSKentaro Takeda  * @may_use_pattern: True if patterned ACL is permitted.
694b69a54eeSKentaro Takeda  *
695b69a54eeSKentaro Takeda  * Returns 0 on success, -EPERM otherwise.
696fdb8ebb7STetsuo Handa  *
697fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
698b69a54eeSKentaro Takeda  */
699cb0abe6aSTetsuo Handa static int tomoyo_path_acl(const struct tomoyo_request_info *r,
7007ef61233STetsuo Handa 			   const struct tomoyo_path_info *filename,
7017ef61233STetsuo Handa 			   const u32 perm, const bool may_use_pattern)
702b69a54eeSKentaro Takeda {
703cb0abe6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
704b69a54eeSKentaro Takeda 	struct tomoyo_acl_info *ptr;
705b69a54eeSKentaro Takeda 	int error = -EPERM;
706b69a54eeSKentaro Takeda 
707fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
7087ef61233STetsuo Handa 		struct tomoyo_path_acl *acl;
7097ef61233STetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_ACL)
710b69a54eeSKentaro Takeda 			continue;
7117ef61233STetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_acl, head);
712a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm) ||
713a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_name_union_pattern(filename, &acl->name,
7147762fbffSTetsuo Handa                                                        may_use_pattern))
715b69a54eeSKentaro Takeda 			continue;
716b69a54eeSKentaro Takeda 		error = 0;
717b69a54eeSKentaro Takeda 		break;
718b69a54eeSKentaro Takeda 	}
719b69a54eeSKentaro Takeda 	return error;
720b69a54eeSKentaro Takeda }
721b69a54eeSKentaro Takeda 
722b69a54eeSKentaro Takeda /**
723cb0abe6aSTetsuo Handa  * tomoyo_file_perm - Check permission for opening files.
724b69a54eeSKentaro Takeda  *
725cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
726b69a54eeSKentaro Takeda  * @filename:  Filename to check.
727cb0abe6aSTetsuo Handa  * @mode:      Mode ("read" or "write" or "read/write" or "execute").
728b69a54eeSKentaro Takeda  *
729b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
730fdb8ebb7STetsuo Handa  *
731fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
732b69a54eeSKentaro Takeda  */
733cb0abe6aSTetsuo Handa static int tomoyo_file_perm(struct tomoyo_request_info *r,
734b69a54eeSKentaro Takeda 			    const struct tomoyo_path_info *filename,
735b69a54eeSKentaro Takeda 			    const u8 mode)
736b69a54eeSKentaro Takeda {
737b69a54eeSKentaro Takeda 	const char *msg = "<unknown>";
738b69a54eeSKentaro Takeda 	int error = 0;
739cb0abe6aSTetsuo Handa 	u32 perm = 0;
740b69a54eeSKentaro Takeda 
741b69a54eeSKentaro Takeda 	if (!filename)
742b69a54eeSKentaro Takeda 		return 0;
743cb0abe6aSTetsuo Handa 
744cb0abe6aSTetsuo Handa 	if (mode == 6) {
745cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE);
746cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_READ_WRITE;
747cb0abe6aSTetsuo Handa 	} else if (mode == 4) {
748cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_READ);
749cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_READ;
750cb0abe6aSTetsuo Handa 	} else if (mode == 2) {
751cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE);
752cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_WRITE;
753cb0abe6aSTetsuo Handa 	} else if (mode == 1) {
754cb0abe6aSTetsuo Handa 		msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE);
755cb0abe6aSTetsuo Handa 		perm = 1 << TOMOYO_TYPE_EXECUTE;
756cb0abe6aSTetsuo Handa 	} else
757cb0abe6aSTetsuo Handa 		BUG();
75817fcfbd9STetsuo Handa 	do {
759cb0abe6aSTetsuo Handa 		error = tomoyo_path_acl(r, filename, perm, mode != 1);
760cb0abe6aSTetsuo Handa 		if (error && mode == 4 && !r->domain->ignore_global_allow_read
761b69a54eeSKentaro Takeda 		    && tomoyo_is_globally_readable_file(filename))
762b69a54eeSKentaro Takeda 			error = 0;
763b69a54eeSKentaro Takeda 		if (!error)
76417fcfbd9STetsuo Handa 			break;
765cb0abe6aSTetsuo Handa 		tomoyo_warn_log(r, "%s %s", msg, filename->name);
76617fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
76717fcfbd9STetsuo Handa 					  mode == 1 ? filename->name :
76817fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename));
76917fcfbd9STetsuo Handa 		/*
77017fcfbd9STetsuo Handa                  * Do not retry for execute request, for alias may have
77117fcfbd9STetsuo Handa 		 * changed.
77217fcfbd9STetsuo Handa                  */
77317fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST && mode != 1);
77417fcfbd9STetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
77517fcfbd9STetsuo Handa 		error = 0;
776b69a54eeSKentaro Takeda 	return error;
777b69a54eeSKentaro Takeda }
778b69a54eeSKentaro Takeda 
779b69a54eeSKentaro Takeda /**
7807ef61233STetsuo Handa  * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
781b69a54eeSKentaro Takeda  *
782b69a54eeSKentaro Takeda  * @type:      Type of operation.
783b69a54eeSKentaro Takeda  * @filename:  Filename.
784b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
785b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
786b69a54eeSKentaro Takeda  *
787b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
788fdb8ebb7STetsuo Handa  *
789fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
790b69a54eeSKentaro Takeda  */
7917ef61233STetsuo Handa static int tomoyo_update_path_acl(const u8 type, const char *filename,
7927ef61233STetsuo Handa 				  struct tomoyo_domain_info *const domain,
7937ef61233STetsuo Handa 				  const bool is_delete)
794b69a54eeSKentaro Takeda {
795a1f9bb6aSTetsuo Handa 	static const u16 tomoyo_rw_mask =
7967ef61233STetsuo Handa 		(1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE);
797a1f9bb6aSTetsuo Handa 	const u16 perm = 1 << type;
7989e4b50e9STetsuo Handa 	struct tomoyo_acl_info *ptr;
7999e4b50e9STetsuo Handa 	struct tomoyo_path_acl e = {
8009e4b50e9STetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_ACL,
8019e4b50e9STetsuo Handa 		.perm = perm
8029e4b50e9STetsuo Handa 	};
8039e4b50e9STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
804b69a54eeSKentaro Takeda 
8059e4b50e9STetsuo Handa 	if (type == TOMOYO_TYPE_READ_WRITE)
8069e4b50e9STetsuo Handa 		e.perm |= tomoyo_rw_mask;
807b69a54eeSKentaro Takeda 	if (!domain)
808b69a54eeSKentaro Takeda 		return -EINVAL;
8097762fbffSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name))
810b69a54eeSKentaro Takeda 		return -EINVAL;
81129282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
81229282381STetsuo Handa 		goto out;
813fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
8147ef61233STetsuo Handa 		struct tomoyo_path_acl *acl =
8157ef61233STetsuo Handa 			container_of(ptr, struct tomoyo_path_acl, head);
8167762fbffSTetsuo Handa 		if (!tomoyo_is_same_path_acl(acl, &e))
817b69a54eeSKentaro Takeda 			continue;
818ca0b7df3STetsuo Handa 		if (is_delete) {
819ca0b7df3STetsuo Handa 			acl->perm &= ~perm;
8209e4b50e9STetsuo Handa 			if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask)
8217ef61233STetsuo Handa 				acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
8227ef61233STetsuo Handa 			else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)))
8239e4b50e9STetsuo Handa 				acl->perm &= ~tomoyo_rw_mask;
824ca0b7df3STetsuo Handa 		} else {
825b69a54eeSKentaro Takeda 			acl->perm |= perm;
8269e4b50e9STetsuo Handa 			if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask)
8277ef61233STetsuo Handa 				acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE;
8287ef61233STetsuo Handa 			else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))
8299e4b50e9STetsuo Handa 				acl->perm |= tomoyo_rw_mask;
830b69a54eeSKentaro Takeda 		}
831b69a54eeSKentaro Takeda 		error = 0;
832b69a54eeSKentaro Takeda 		break;
833b69a54eeSKentaro Takeda 	}
8349e4b50e9STetsuo Handa 	if (!is_delete && error) {
8359e4b50e9STetsuo Handa 		struct tomoyo_path_acl *entry =
8369e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
8379e4b50e9STetsuo Handa 		if (entry) {
8389e4b50e9STetsuo Handa 			list_add_tail_rcu(&entry->head.list,
8399e4b50e9STetsuo Handa 					  &domain->acl_info_list);
840ca0b7df3STetsuo Handa 			error = 0;
841ca0b7df3STetsuo Handa 		}
8429e4b50e9STetsuo Handa 	}
843f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
84429282381STetsuo Handa  out:
8457762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name);
846b69a54eeSKentaro Takeda 	return error;
847b69a54eeSKentaro Takeda }
848b69a54eeSKentaro Takeda 
849b69a54eeSKentaro Takeda /**
850a1f9bb6aSTetsuo Handa  * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list.
851a1f9bb6aSTetsuo Handa  *
852a1f9bb6aSTetsuo Handa  * @type:      Type of operation.
853a1f9bb6aSTetsuo Handa  * @filename:  Filename.
854a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
855a1f9bb6aSTetsuo Handa  * @major:     Device major number.
856a1f9bb6aSTetsuo Handa  * @minor:     Device minor number.
857a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
858a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
859a1f9bb6aSTetsuo Handa  *
860a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
861a1f9bb6aSTetsuo Handa  */
862a1f9bb6aSTetsuo Handa static inline int tomoyo_update_path_number3_acl(const u8 type,
863a1f9bb6aSTetsuo Handa 						 const char *filename,
864a1f9bb6aSTetsuo Handa 						 char *mode,
865a1f9bb6aSTetsuo Handa 						 char *major, char *minor,
866a1f9bb6aSTetsuo Handa 						 struct tomoyo_domain_info *
867a1f9bb6aSTetsuo Handa 						 const domain,
868a1f9bb6aSTetsuo Handa 						 const bool is_delete)
869a1f9bb6aSTetsuo Handa {
870a1f9bb6aSTetsuo Handa 	const u8 perm = 1 << type;
871a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
872a1f9bb6aSTetsuo Handa 	struct tomoyo_path_number3_acl e = {
873a1f9bb6aSTetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL,
874a1f9bb6aSTetsuo Handa 		.perm = perm
875a1f9bb6aSTetsuo Handa 	};
876a1f9bb6aSTetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
877a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name) ||
878a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(mode, &e.mode) ||
879a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(major, &e.major) ||
880a1f9bb6aSTetsuo Handa 	    !tomoyo_parse_number_union(minor, &e.minor))
881a1f9bb6aSTetsuo Handa 		goto out;
882a1f9bb6aSTetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
883a1f9bb6aSTetsuo Handa 		goto out;
884a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
885a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number3_acl *acl =
886a1f9bb6aSTetsuo Handa 			container_of(ptr, struct tomoyo_path_number3_acl, head);
887a1f9bb6aSTetsuo Handa 		if (!tomoyo_is_same_path_number3_acl(acl, &e))
888a1f9bb6aSTetsuo Handa 			continue;
889a1f9bb6aSTetsuo Handa 		if (is_delete)
890a1f9bb6aSTetsuo Handa 			acl->perm &= ~perm;
891a1f9bb6aSTetsuo Handa 		else
892a1f9bb6aSTetsuo Handa 			acl->perm |= perm;
893a1f9bb6aSTetsuo Handa 		error = 0;
894a1f9bb6aSTetsuo Handa 		break;
895a1f9bb6aSTetsuo Handa 	}
896a1f9bb6aSTetsuo Handa 	if (!is_delete && error) {
897a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number3_acl *entry =
898a1f9bb6aSTetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
899a1f9bb6aSTetsuo Handa 		if (entry) {
900a1f9bb6aSTetsuo Handa 			list_add_tail_rcu(&entry->head.list,
901a1f9bb6aSTetsuo Handa 					  &domain->acl_info_list);
902a1f9bb6aSTetsuo Handa 			error = 0;
903a1f9bb6aSTetsuo Handa 		}
904a1f9bb6aSTetsuo Handa 	}
905a1f9bb6aSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
906a1f9bb6aSTetsuo Handa  out:
907a1f9bb6aSTetsuo Handa 	tomoyo_put_name_union(&e.name);
908a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.mode);
909a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.major);
910a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.minor);
911a1f9bb6aSTetsuo Handa 	return error;
912a1f9bb6aSTetsuo Handa }
913a1f9bb6aSTetsuo Handa 
914a1f9bb6aSTetsuo Handa /**
9157ef61233STetsuo Handa  * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
916b69a54eeSKentaro Takeda  *
917b69a54eeSKentaro Takeda  * @type:      Type of operation.
918b69a54eeSKentaro Takeda  * @filename1: First filename.
919b69a54eeSKentaro Takeda  * @filename2: Second filename.
920b69a54eeSKentaro Takeda  * @domain:    Pointer to "struct tomoyo_domain_info".
921b69a54eeSKentaro Takeda  * @is_delete: True if it is a delete request.
922b69a54eeSKentaro Takeda  *
923b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
924fdb8ebb7STetsuo Handa  *
925fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
926b69a54eeSKentaro Takeda  */
9277ef61233STetsuo Handa static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
928b69a54eeSKentaro Takeda 				   const char *filename2,
9297ef61233STetsuo Handa 				   struct tomoyo_domain_info *const domain,
9307ef61233STetsuo Handa 				   const bool is_delete)
931b69a54eeSKentaro Takeda {
932b69a54eeSKentaro Takeda 	const u8 perm = 1 << type;
9339e4b50e9STetsuo Handa 	struct tomoyo_path2_acl e = {
9349e4b50e9STetsuo Handa 		.head.type = TOMOYO_TYPE_PATH2_ACL,
9359e4b50e9STetsuo Handa 		.perm = perm
9369e4b50e9STetsuo Handa 	};
9379e4b50e9STetsuo Handa 	struct tomoyo_acl_info *ptr;
9389e4b50e9STetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
939b69a54eeSKentaro Takeda 
940b69a54eeSKentaro Takeda 	if (!domain)
941b69a54eeSKentaro Takeda 		return -EINVAL;
9427762fbffSTetsuo Handa 	if (!tomoyo_parse_name_union(filename1, &e.name1) ||
9437762fbffSTetsuo Handa 	    !tomoyo_parse_name_union(filename2, &e.name2))
944ca0b7df3STetsuo Handa 		goto out;
94529282381STetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
94629282381STetsuo Handa 		goto out;
947ca0b7df3STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
9487ef61233STetsuo Handa 		struct tomoyo_path2_acl *acl =
9497ef61233STetsuo Handa 			container_of(ptr, struct tomoyo_path2_acl, head);
9507762fbffSTetsuo Handa 		if (!tomoyo_is_same_path2_acl(acl, &e))
951ca0b7df3STetsuo Handa 			continue;
952b69a54eeSKentaro Takeda 		if (is_delete)
953b69a54eeSKentaro Takeda 			acl->perm &= ~perm;
954ca0b7df3STetsuo Handa 		else
955ca0b7df3STetsuo Handa 			acl->perm |= perm;
956b69a54eeSKentaro Takeda 		error = 0;
957b69a54eeSKentaro Takeda 		break;
958b69a54eeSKentaro Takeda 	}
9599e4b50e9STetsuo Handa 	if (!is_delete && error) {
9609e4b50e9STetsuo Handa 		struct tomoyo_path2_acl *entry =
9619e4b50e9STetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
9629e4b50e9STetsuo Handa 		if (entry) {
9639e4b50e9STetsuo Handa 			list_add_tail_rcu(&entry->head.list,
9649e4b50e9STetsuo Handa 					  &domain->acl_info_list);
965ca0b7df3STetsuo Handa 			error = 0;
966ca0b7df3STetsuo Handa 		}
9679e4b50e9STetsuo Handa 	}
968f737d95dSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
969ca0b7df3STetsuo Handa  out:
9707762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name1);
9717762fbffSTetsuo Handa 	tomoyo_put_name_union(&e.name2);
972b69a54eeSKentaro Takeda 	return error;
973b69a54eeSKentaro Takeda }
974b69a54eeSKentaro Takeda 
975b69a54eeSKentaro Takeda /**
976a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_acl - Check permission for path/number/number/number operation.
977a1f9bb6aSTetsuo Handa  *
978a1f9bb6aSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
979a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
980a1f9bb6aSTetsuo Handa  * @perm:     Permission.
981a1f9bb6aSTetsuo Handa  * @mode:     Create mode.
982a1f9bb6aSTetsuo Handa  * @major:    Device major number.
983a1f9bb6aSTetsuo Handa  * @minor:    Device minor number.
984a1f9bb6aSTetsuo Handa  *
985a1f9bb6aSTetsuo Handa  * Returns 0 on success, -EPERM otherwise.
986a1f9bb6aSTetsuo Handa  *
987a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
988a1f9bb6aSTetsuo Handa  */
989a1f9bb6aSTetsuo Handa static int tomoyo_path_number3_acl(struct tomoyo_request_info *r,
990a1f9bb6aSTetsuo Handa 				   const struct tomoyo_path_info *filename,
991a1f9bb6aSTetsuo Handa 				   const u16 perm, const unsigned int mode,
992a1f9bb6aSTetsuo Handa 				   const unsigned int major,
993a1f9bb6aSTetsuo Handa 				   const unsigned int minor)
994a1f9bb6aSTetsuo Handa {
995a1f9bb6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
996a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
997a1f9bb6aSTetsuo Handa 	int error = -EPERM;
998a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
999a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number3_acl *acl;
1000a1f9bb6aSTetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL)
1001a1f9bb6aSTetsuo Handa 			continue;
1002a1f9bb6aSTetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_number3_acl, head);
1003a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(mode, &acl->mode))
1004a1f9bb6aSTetsuo Handa 			continue;
1005a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(major, &acl->major))
1006a1f9bb6aSTetsuo Handa 			continue;
1007a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_number_union(minor, &acl->minor))
1008a1f9bb6aSTetsuo Handa 			continue;
1009a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm))
1010a1f9bb6aSTetsuo Handa 			continue;
1011a1f9bb6aSTetsuo Handa 		if (!tomoyo_compare_name_union(filename, &acl->name))
1012a1f9bb6aSTetsuo Handa 			continue;
1013a1f9bb6aSTetsuo Handa 		error = 0;
1014a1f9bb6aSTetsuo Handa 		break;
1015a1f9bb6aSTetsuo Handa 	}
1016a1f9bb6aSTetsuo Handa 	return error;
1017a1f9bb6aSTetsuo Handa }
1018a1f9bb6aSTetsuo Handa 
1019a1f9bb6aSTetsuo Handa /**
10207ef61233STetsuo Handa  * tomoyo_path2_acl - Check permission for double path operation.
1021b69a54eeSKentaro Takeda  *
1022cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1023b69a54eeSKentaro Takeda  * @type:      Type of operation.
1024b69a54eeSKentaro Takeda  * @filename1: First filename to check.
1025b69a54eeSKentaro Takeda  * @filename2: Second filename to check.
1026b69a54eeSKentaro Takeda  *
1027b69a54eeSKentaro Takeda  * Returns 0 on success, -EPERM otherwise.
1028fdb8ebb7STetsuo Handa  *
1029fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1030b69a54eeSKentaro Takeda  */
1031cb0abe6aSTetsuo Handa static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type,
10327ef61233STetsuo Handa 			    const struct tomoyo_path_info *filename1,
10337ef61233STetsuo Handa 			    const struct tomoyo_path_info *filename2)
1034b69a54eeSKentaro Takeda {
1035cb0abe6aSTetsuo Handa 	const struct tomoyo_domain_info *domain = r->domain;
1036b69a54eeSKentaro Takeda 	struct tomoyo_acl_info *ptr;
1037b69a54eeSKentaro Takeda 	const u8 perm = 1 << type;
1038b69a54eeSKentaro Takeda 	int error = -EPERM;
1039b69a54eeSKentaro Takeda 
1040fdb8ebb7STetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
10417ef61233STetsuo Handa 		struct tomoyo_path2_acl *acl;
10427ef61233STetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
1043b69a54eeSKentaro Takeda 			continue;
10447ef61233STetsuo Handa 		acl = container_of(ptr, struct tomoyo_path2_acl, head);
1045b69a54eeSKentaro Takeda 		if (!(acl->perm & perm))
1046b69a54eeSKentaro Takeda 			continue;
10477762fbffSTetsuo Handa 		if (!tomoyo_compare_name_union(filename1, &acl->name1))
1048b69a54eeSKentaro Takeda 			continue;
10497762fbffSTetsuo Handa 		if (!tomoyo_compare_name_union(filename2, &acl->name2))
1050b69a54eeSKentaro Takeda 			continue;
1051b69a54eeSKentaro Takeda 		error = 0;
1052b69a54eeSKentaro Takeda 		break;
1053b69a54eeSKentaro Takeda 	}
1054b69a54eeSKentaro Takeda 	return error;
1055b69a54eeSKentaro Takeda }
1056b69a54eeSKentaro Takeda 
1057b69a54eeSKentaro Takeda /**
1058cb0abe6aSTetsuo Handa  * tomoyo_path_permission - Check permission for single path operation.
1059b69a54eeSKentaro Takeda  *
1060cb0abe6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1061b69a54eeSKentaro Takeda  * @operation: Type of operation.
1062b69a54eeSKentaro Takeda  * @filename:  Filename to check.
1063b69a54eeSKentaro Takeda  *
1064b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1065fdb8ebb7STetsuo Handa  *
1066fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1067b69a54eeSKentaro Takeda  */
1068cb0abe6aSTetsuo Handa static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
1069cb0abe6aSTetsuo Handa 				  const struct tomoyo_path_info *filename)
1070b69a54eeSKentaro Takeda {
107117fcfbd9STetsuo Handa 	const char *msg;
1072b69a54eeSKentaro Takeda 	int error;
1073b69a54eeSKentaro Takeda 
1074b69a54eeSKentaro Takeda  next:
107517fcfbd9STetsuo Handa 	do {
1076cb0abe6aSTetsuo Handa 		error = tomoyo_path_acl(r, filename, 1 << operation, 1);
1077b69a54eeSKentaro Takeda 		if (!error)
107817fcfbd9STetsuo Handa 			break;
107917fcfbd9STetsuo Handa 		msg = tomoyo_path2keyword(operation);
108017fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s", msg, filename->name);
108117fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
108217fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename));
108317fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1084cb0abe6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1085b69a54eeSKentaro Takeda 		error = 0;
1086b69a54eeSKentaro Takeda 	/*
1087b69a54eeSKentaro Takeda 	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
1088b69a54eeSKentaro Takeda 	 * we need to check "allow_rewrite" permission if the filename is
1089b69a54eeSKentaro Takeda 	 * specified by "deny_rewrite" keyword.
1090b69a54eeSKentaro Takeda 	 */
10917ef61233STetsuo Handa 	if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
1092b69a54eeSKentaro Takeda 	    tomoyo_is_no_rewrite_file(filename)) {
10937ef61233STetsuo Handa 		operation = TOMOYO_TYPE_REWRITE;
1094b69a54eeSKentaro Takeda 		goto next;
1095b69a54eeSKentaro Takeda 	}
1096b69a54eeSKentaro Takeda 	return error;
1097b69a54eeSKentaro Takeda }
1098b69a54eeSKentaro Takeda 
1099b69a54eeSKentaro Takeda /**
1100a1f9bb6aSTetsuo Handa  * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation.
1101a1f9bb6aSTetsuo Handa  *
1102a1f9bb6aSTetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
1103a1f9bb6aSTetsuo Handa  * @type:     Operation.
1104a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1105a1f9bb6aSTetsuo Handa  * @number:   Number.
1106a1f9bb6aSTetsuo Handa  *
1107a1f9bb6aSTetsuo Handa  * Returns 0 on success, -EPERM otherwise.
1108a1f9bb6aSTetsuo Handa  *
1109a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1110a1f9bb6aSTetsuo Handa  */
1111a1f9bb6aSTetsuo Handa static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type,
1112a1f9bb6aSTetsuo Handa 				  const struct tomoyo_path_info *filename,
1113a1f9bb6aSTetsuo Handa 				  const unsigned long number)
1114a1f9bb6aSTetsuo Handa {
1115a1f9bb6aSTetsuo Handa 	struct tomoyo_domain_info *domain = r->domain;
1116a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
1117a1f9bb6aSTetsuo Handa 	const u8 perm = 1 << type;
1118a1f9bb6aSTetsuo Handa 	int error = -EPERM;
1119a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1120a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number_acl *acl;
1121a1f9bb6aSTetsuo Handa 		if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL)
1122a1f9bb6aSTetsuo Handa 			continue;
1123a1f9bb6aSTetsuo Handa 		acl = container_of(ptr, struct tomoyo_path_number_acl,
1124a1f9bb6aSTetsuo Handa 				   head);
1125a1f9bb6aSTetsuo Handa 		if (!(acl->perm & perm) ||
1126a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_number_union(number, &acl->number) ||
1127a1f9bb6aSTetsuo Handa 		    !tomoyo_compare_name_union(filename, &acl->name))
1128a1f9bb6aSTetsuo Handa 			continue;
1129a1f9bb6aSTetsuo Handa 		error = 0;
1130a1f9bb6aSTetsuo Handa 		break;
1131a1f9bb6aSTetsuo Handa 	}
1132a1f9bb6aSTetsuo Handa 	return error;
1133a1f9bb6aSTetsuo Handa }
1134a1f9bb6aSTetsuo Handa 
1135a1f9bb6aSTetsuo Handa /**
1136a1f9bb6aSTetsuo Handa  * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
1137a1f9bb6aSTetsuo Handa  *
1138a1f9bb6aSTetsuo Handa  * @type:      Type of operation.
1139a1f9bb6aSTetsuo Handa  * @filename:  Filename.
1140a1f9bb6aSTetsuo Handa  * @number:    Number.
1141a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
1142a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
1143a1f9bb6aSTetsuo Handa  *
1144a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1145a1f9bb6aSTetsuo Handa  */
1146a1f9bb6aSTetsuo Handa static inline int tomoyo_update_path_number_acl(const u8 type,
1147a1f9bb6aSTetsuo Handa 						const char *filename,
1148a1f9bb6aSTetsuo Handa 						char *number,
1149a1f9bb6aSTetsuo Handa 						struct tomoyo_domain_info *
1150a1f9bb6aSTetsuo Handa 						const domain,
1151a1f9bb6aSTetsuo Handa 						const bool is_delete)
1152a1f9bb6aSTetsuo Handa {
1153a1f9bb6aSTetsuo Handa 	const u8 perm = 1 << type;
1154a1f9bb6aSTetsuo Handa 	struct tomoyo_acl_info *ptr;
1155a1f9bb6aSTetsuo Handa 	struct tomoyo_path_number_acl e = {
1156a1f9bb6aSTetsuo Handa 		.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
1157a1f9bb6aSTetsuo Handa 		.perm = perm
1158a1f9bb6aSTetsuo Handa 	};
1159a1f9bb6aSTetsuo Handa 	int error = is_delete ? -ENOENT : -ENOMEM;
1160a1f9bb6aSTetsuo Handa 	if (!domain)
1161a1f9bb6aSTetsuo Handa 		return -EINVAL;
1162a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_name_union(filename, &e.name))
1163a1f9bb6aSTetsuo Handa 		return -EINVAL;
1164a1f9bb6aSTetsuo Handa 	if (!tomoyo_parse_number_union(number, &e.number))
1165a1f9bb6aSTetsuo Handa 		goto out;
1166a1f9bb6aSTetsuo Handa 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
1167a1f9bb6aSTetsuo Handa 		goto out;
1168a1f9bb6aSTetsuo Handa 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1169a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number_acl *acl =
1170a1f9bb6aSTetsuo Handa 			container_of(ptr, struct tomoyo_path_number_acl, head);
1171a1f9bb6aSTetsuo Handa 		if (!tomoyo_is_same_path_number_acl(acl, &e))
1172a1f9bb6aSTetsuo Handa 			continue;
1173a1f9bb6aSTetsuo Handa 		if (is_delete)
1174a1f9bb6aSTetsuo Handa 			acl->perm &= ~perm;
1175a1f9bb6aSTetsuo Handa 		else
1176a1f9bb6aSTetsuo Handa 			acl->perm |= perm;
1177a1f9bb6aSTetsuo Handa 		error = 0;
1178a1f9bb6aSTetsuo Handa 		break;
1179a1f9bb6aSTetsuo Handa 	}
1180a1f9bb6aSTetsuo Handa 	if (!is_delete && error) {
1181a1f9bb6aSTetsuo Handa 		struct tomoyo_path_number_acl *entry =
1182a1f9bb6aSTetsuo Handa 			tomoyo_commit_ok(&e, sizeof(e));
1183a1f9bb6aSTetsuo Handa 		if (entry) {
1184a1f9bb6aSTetsuo Handa 			list_add_tail_rcu(&entry->head.list,
1185a1f9bb6aSTetsuo Handa 					  &domain->acl_info_list);
1186a1f9bb6aSTetsuo Handa 			error = 0;
1187a1f9bb6aSTetsuo Handa 		}
1188a1f9bb6aSTetsuo Handa 	}
1189a1f9bb6aSTetsuo Handa 	mutex_unlock(&tomoyo_policy_lock);
1190a1f9bb6aSTetsuo Handa  out:
1191a1f9bb6aSTetsuo Handa 	tomoyo_put_name_union(&e.name);
1192a1f9bb6aSTetsuo Handa 	tomoyo_put_number_union(&e.number);
1193a1f9bb6aSTetsuo Handa 	return error;
1194a1f9bb6aSTetsuo Handa }
1195a1f9bb6aSTetsuo Handa 
1196a1f9bb6aSTetsuo Handa /**
1197a1f9bb6aSTetsuo Handa  * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
1198a1f9bb6aSTetsuo Handa  *
1199a1f9bb6aSTetsuo Handa  * @r:        Pointer to "strct tomoyo_request_info".
1200a1f9bb6aSTetsuo Handa  * @filename: Filename to check.
1201a1f9bb6aSTetsuo Handa  * @number:   Number.
1202a1f9bb6aSTetsuo Handa  *
1203a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1204a1f9bb6aSTetsuo Handa  *
1205a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1206a1f9bb6aSTetsuo Handa  */
1207a1f9bb6aSTetsuo Handa static int tomoyo_path_number_perm2(struct tomoyo_request_info *r,
1208a1f9bb6aSTetsuo Handa 				    const u8 type,
1209a1f9bb6aSTetsuo Handa 				    const struct tomoyo_path_info *filename,
1210a1f9bb6aSTetsuo Handa 				    const unsigned long number)
1211a1f9bb6aSTetsuo Handa {
1212a1f9bb6aSTetsuo Handa 	char buffer[64];
1213a1f9bb6aSTetsuo Handa 	int error;
1214a1f9bb6aSTetsuo Handa 	u8 radix;
121517fcfbd9STetsuo Handa 	const char *msg;
1216a1f9bb6aSTetsuo Handa 
1217a1f9bb6aSTetsuo Handa 	if (!filename)
1218a1f9bb6aSTetsuo Handa 		return 0;
1219a1f9bb6aSTetsuo Handa 	switch (type) {
1220a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_CREATE:
1221a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKDIR:
1222a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKFIFO:
1223a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_MKSOCK:
1224a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_CHMOD:
1225a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_OCTAL;
1226a1f9bb6aSTetsuo Handa 		break;
1227a1f9bb6aSTetsuo Handa 	case TOMOYO_TYPE_IOCTL:
1228a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_HEXADECIMAL;
1229a1f9bb6aSTetsuo Handa 		break;
1230a1f9bb6aSTetsuo Handa 	default:
1231a1f9bb6aSTetsuo Handa 		radix = TOMOYO_VALUE_TYPE_DECIMAL;
1232a1f9bb6aSTetsuo Handa 		break;
1233a1f9bb6aSTetsuo Handa 	}
1234a1f9bb6aSTetsuo Handa 	tomoyo_print_ulong(buffer, sizeof(buffer), number, radix);
123517fcfbd9STetsuo Handa 	do {
1236a1f9bb6aSTetsuo Handa 		error = tomoyo_path_number_acl(r, type, filename, number);
1237a1f9bb6aSTetsuo Handa 		if (!error)
123817fcfbd9STetsuo Handa 			break;
123917fcfbd9STetsuo Handa 		msg = tomoyo_path_number2keyword(type);
124017fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s %s", msg, filename->name, buffer);
124117fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg,
124217fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename),
124317fcfbd9STetsuo Handa 					  buffer);
124417fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1245a1f9bb6aSTetsuo Handa 	if (r->mode != TOMOYO_CONFIG_ENFORCING)
1246a1f9bb6aSTetsuo Handa 		error = 0;
1247a1f9bb6aSTetsuo Handa 	return error;
1248a1f9bb6aSTetsuo Handa }
1249a1f9bb6aSTetsuo Handa 
1250a1f9bb6aSTetsuo Handa /**
1251a1f9bb6aSTetsuo Handa  * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
1252a1f9bb6aSTetsuo Handa  *
1253a1f9bb6aSTetsuo Handa  * @type:   Type of operation.
1254a1f9bb6aSTetsuo Handa  * @path:   Pointer to "struct path".
1255a1f9bb6aSTetsuo Handa  * @number: Number.
1256a1f9bb6aSTetsuo Handa  *
1257a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1258a1f9bb6aSTetsuo Handa  */
1259a1f9bb6aSTetsuo Handa int tomoyo_path_number_perm(const u8 type, struct path *path,
1260a1f9bb6aSTetsuo Handa 			    unsigned long number)
1261a1f9bb6aSTetsuo Handa {
1262a1f9bb6aSTetsuo Handa 	struct tomoyo_request_info r;
1263a1f9bb6aSTetsuo Handa 	int error = -ENOMEM;
1264c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1265a1f9bb6aSTetsuo Handa 	int idx;
1266a1f9bb6aSTetsuo Handa 
1267a1f9bb6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
1268a1f9bb6aSTetsuo Handa 	    !path->mnt || !path->dentry)
1269a1f9bb6aSTetsuo Handa 		return 0;
1270a1f9bb6aSTetsuo Handa 	idx = tomoyo_read_lock();
1271c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1272a1f9bb6aSTetsuo Handa 		goto out;
1273c8c57e84STetsuo Handa 	if (type == TOMOYO_TYPE_MKDIR)
1274c8c57e84STetsuo Handa 		tomoyo_add_slash(&buf);
1275c8c57e84STetsuo Handa 	error = tomoyo_path_number_perm2(&r, type, &buf, number);
1276a1f9bb6aSTetsuo Handa  out:
1277c8c57e84STetsuo Handa 	kfree(buf.name);
1278a1f9bb6aSTetsuo Handa 	tomoyo_read_unlock(idx);
1279a1f9bb6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1280a1f9bb6aSTetsuo Handa 		error = 0;
1281a1f9bb6aSTetsuo Handa 	return error;
1282a1f9bb6aSTetsuo Handa }
1283a1f9bb6aSTetsuo Handa 
1284a1f9bb6aSTetsuo Handa /**
1285b69a54eeSKentaro Takeda  * tomoyo_check_exec_perm - Check permission for "execute".
1286b69a54eeSKentaro Takeda  *
1287b69a54eeSKentaro Takeda  * @domain:   Pointer to "struct tomoyo_domain_info".
1288b69a54eeSKentaro Takeda  * @filename: Check permission for "execute".
1289b69a54eeSKentaro Takeda  *
1290b69a54eeSKentaro Takeda  * Returns 0 on success, negativevalue otherwise.
1291fdb8ebb7STetsuo Handa  *
1292fdb8ebb7STetsuo Handa  * Caller holds tomoyo_read_lock().
1293b69a54eeSKentaro Takeda  */
1294b69a54eeSKentaro Takeda int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
1295bcb86975STetsuo Handa 			   const struct tomoyo_path_info *filename)
1296b69a54eeSKentaro Takeda {
1297cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1298b69a54eeSKentaro Takeda 
1299cb0abe6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED)
1300b69a54eeSKentaro Takeda 		return 0;
1301cb0abe6aSTetsuo Handa 	return tomoyo_file_perm(&r, filename, 1);
1302b69a54eeSKentaro Takeda }
1303b69a54eeSKentaro Takeda 
1304b69a54eeSKentaro Takeda /**
1305b69a54eeSKentaro Takeda  * tomoyo_check_open_permission - Check permission for "read" and "write".
1306b69a54eeSKentaro Takeda  *
1307b69a54eeSKentaro Takeda  * @domain: Pointer to "struct tomoyo_domain_info".
1308b69a54eeSKentaro Takeda  * @path:   Pointer to "struct path".
1309b69a54eeSKentaro Takeda  * @flag:   Flags for open().
1310b69a54eeSKentaro Takeda  *
1311b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1312b69a54eeSKentaro Takeda  */
1313b69a54eeSKentaro Takeda int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
1314b69a54eeSKentaro Takeda 				 struct path *path, const int flag)
1315b69a54eeSKentaro Takeda {
1316b69a54eeSKentaro Takeda 	const u8 acc_mode = ACC_MODE(flag);
1317b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1318c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1319cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1320fdb8ebb7STetsuo Handa 	int idx;
1321b69a54eeSKentaro Takeda 
1322cb0abe6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, domain) == TOMOYO_CONFIG_DISABLED ||
1323cb0abe6aSTetsuo Handa 	    !path->mnt)
1324b69a54eeSKentaro Takeda 		return 0;
1325b69a54eeSKentaro Takeda 	if (acc_mode == 0)
1326b69a54eeSKentaro Takeda 		return 0;
1327b69a54eeSKentaro Takeda 	if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
1328b69a54eeSKentaro Takeda 		/*
1329b69a54eeSKentaro Takeda 		 * I don't check directories here because mkdir() and rmdir()
1330b69a54eeSKentaro Takeda 		 * don't call me.
1331b69a54eeSKentaro Takeda 		 */
1332b69a54eeSKentaro Takeda 		return 0;
1333fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1334c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1335b69a54eeSKentaro Takeda 		goto out;
1336b69a54eeSKentaro Takeda 	error = 0;
1337b69a54eeSKentaro Takeda 	/*
1338b69a54eeSKentaro Takeda 	 * If the filename is specified by "deny_rewrite" keyword,
1339b69a54eeSKentaro Takeda 	 * we need to check "allow_rewrite" permission when the filename is not
1340b69a54eeSKentaro Takeda 	 * opened for append mode or the filename is truncated at open time.
1341b69a54eeSKentaro Takeda 	 */
1342b69a54eeSKentaro Takeda 	if ((acc_mode & MAY_WRITE) &&
1343b69a54eeSKentaro Takeda 	    ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
1344c8c57e84STetsuo Handa 	    (tomoyo_is_no_rewrite_file(&buf))) {
1345c8c57e84STetsuo Handa 		error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf);
1346b69a54eeSKentaro Takeda 	}
1347b69a54eeSKentaro Takeda 	if (!error)
1348c8c57e84STetsuo Handa 		error = tomoyo_file_perm(&r, &buf, acc_mode);
1349b69a54eeSKentaro Takeda 	if (!error && (flag & O_TRUNC))
1350c8c57e84STetsuo Handa 		error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, &buf);
1351b69a54eeSKentaro Takeda  out:
1352c8c57e84STetsuo Handa 	kfree(buf.name);
1353fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1354cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1355b69a54eeSKentaro Takeda 		error = 0;
1356b69a54eeSKentaro Takeda 	return error;
1357b69a54eeSKentaro Takeda }
1358b69a54eeSKentaro Takeda 
1359b69a54eeSKentaro Takeda /**
13602106ccd9STetsuo Handa  * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
1361b69a54eeSKentaro Takeda  *
1362b69a54eeSKentaro Takeda  * @operation: Type of operation.
1363b69a54eeSKentaro Takeda  * @path:      Pointer to "struct path".
1364b69a54eeSKentaro Takeda  *
1365b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1366b69a54eeSKentaro Takeda  */
136797d6931eSTetsuo Handa int tomoyo_path_perm(const u8 operation, struct path *path)
1368b69a54eeSKentaro Takeda {
1369b69a54eeSKentaro Takeda 	int error = -ENOMEM;
1370c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1371cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1372fdb8ebb7STetsuo Handa 	int idx;
1373b69a54eeSKentaro Takeda 
1374cb0abe6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
1375cb0abe6aSTetsuo Handa 	    !path->mnt)
1376b69a54eeSKentaro Takeda 		return 0;
1377fdb8ebb7STetsuo Handa 	idx = tomoyo_read_lock();
1378c8c57e84STetsuo Handa 	if (!tomoyo_get_realpath(&buf, path))
1379b69a54eeSKentaro Takeda 		goto out;
1380b69a54eeSKentaro Takeda 	switch (operation) {
1381cb0abe6aSTetsuo Handa 	case TOMOYO_TYPE_REWRITE:
1382c8c57e84STetsuo Handa 		if (!tomoyo_is_no_rewrite_file(&buf)) {
1383cb0abe6aSTetsuo Handa 			error = 0;
1384cb0abe6aSTetsuo Handa 			goto out;
1385cb0abe6aSTetsuo Handa 		}
1386cb0abe6aSTetsuo Handa 		break;
13877ef61233STetsuo Handa 	case TOMOYO_TYPE_RMDIR:
13887ef61233STetsuo Handa 	case TOMOYO_TYPE_CHROOT:
1389c8c57e84STetsuo Handa 		tomoyo_add_slash(&buf);
1390c8c57e84STetsuo Handa 		break;
1391b69a54eeSKentaro Takeda 	}
1392c8c57e84STetsuo Handa 	error = tomoyo_path_permission(&r, operation, &buf);
1393b69a54eeSKentaro Takeda  out:
1394c8c57e84STetsuo Handa 	kfree(buf.name);
1395fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1396cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1397b69a54eeSKentaro Takeda 		error = 0;
1398b69a54eeSKentaro Takeda 	return error;
1399b69a54eeSKentaro Takeda }
1400b69a54eeSKentaro Takeda 
1401b69a54eeSKentaro Takeda /**
1402a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_perm2 - Check permission for path/number/number/number operation.
1403a1f9bb6aSTetsuo Handa  *
1404a1f9bb6aSTetsuo Handa  * @r:         Pointer to "struct tomoyo_request_info".
1405a1f9bb6aSTetsuo Handa  * @operation: Type of operation.
1406a1f9bb6aSTetsuo Handa  * @filename:  Filename to check.
1407a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
1408a1f9bb6aSTetsuo Handa  * @dev:       Device number.
1409a1f9bb6aSTetsuo Handa  *
1410a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1411a1f9bb6aSTetsuo Handa  *
1412a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1413a1f9bb6aSTetsuo Handa  */
1414a1f9bb6aSTetsuo Handa static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r,
1415a1f9bb6aSTetsuo Handa 				     const u8 operation,
1416a1f9bb6aSTetsuo Handa 				     const struct tomoyo_path_info *filename,
1417a1f9bb6aSTetsuo Handa 				     const unsigned int mode,
1418a1f9bb6aSTetsuo Handa 				     const unsigned int dev)
1419a1f9bb6aSTetsuo Handa {
1420a1f9bb6aSTetsuo Handa 	int error;
142117fcfbd9STetsuo Handa 	const char *msg;
1422a1f9bb6aSTetsuo Handa 	const unsigned int major = MAJOR(dev);
1423a1f9bb6aSTetsuo Handa 	const unsigned int minor = MINOR(dev);
1424a1f9bb6aSTetsuo Handa 
142517fcfbd9STetsuo Handa 	do {
142617fcfbd9STetsuo Handa 		error = tomoyo_path_number3_acl(r, filename, 1 << operation,
142717fcfbd9STetsuo Handa 						mode, major, minor);
1428a1f9bb6aSTetsuo Handa 		if (!error)
142917fcfbd9STetsuo Handa 			break;
143017fcfbd9STetsuo Handa 		msg = tomoyo_path_number32keyword(operation);
143117fcfbd9STetsuo Handa 		tomoyo_warn_log(r, "%s %s 0%o %u %u", msg, filename->name,
143217fcfbd9STetsuo Handa 				mode, major, minor);
143317fcfbd9STetsuo Handa 		error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg,
143417fcfbd9STetsuo Handa 					  tomoyo_file_pattern(filename), mode,
143517fcfbd9STetsuo Handa 					  major, minor);
143617fcfbd9STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1437a1f9bb6aSTetsuo Handa         if (r->mode != TOMOYO_CONFIG_ENFORCING)
1438a1f9bb6aSTetsuo Handa 		error = 0;
1439a1f9bb6aSTetsuo Handa 	return error;
1440a1f9bb6aSTetsuo Handa }
1441a1f9bb6aSTetsuo Handa 
1442a1f9bb6aSTetsuo Handa /**
1443a1f9bb6aSTetsuo Handa  * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar".
1444a1f9bb6aSTetsuo Handa  *
1445a1f9bb6aSTetsuo Handa  * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK)
1446a1f9bb6aSTetsuo Handa  * @path:      Pointer to "struct path".
1447a1f9bb6aSTetsuo Handa  * @mode:      Create mode.
1448a1f9bb6aSTetsuo Handa  * @dev:       Device number.
1449a1f9bb6aSTetsuo Handa  *
1450a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1451a1f9bb6aSTetsuo Handa  */
1452a1f9bb6aSTetsuo Handa int tomoyo_path_number3_perm(const u8 operation, struct path *path,
1453a1f9bb6aSTetsuo Handa 			     const unsigned int mode, unsigned int dev)
1454a1f9bb6aSTetsuo Handa {
1455a1f9bb6aSTetsuo Handa 	struct tomoyo_request_info r;
1456a1f9bb6aSTetsuo Handa 	int error = -ENOMEM;
1457c8c57e84STetsuo Handa 	struct tomoyo_path_info buf;
1458a1f9bb6aSTetsuo Handa 	int idx;
1459a1f9bb6aSTetsuo Handa 
1460a1f9bb6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
1461a1f9bb6aSTetsuo Handa 	    !path->mnt)
1462a1f9bb6aSTetsuo Handa 		return 0;
1463a1f9bb6aSTetsuo Handa 	idx = tomoyo_read_lock();
1464a1f9bb6aSTetsuo Handa 	error = -ENOMEM;
1465c8c57e84STetsuo Handa 	if (tomoyo_get_realpath(&buf, path)) {
1466c8c57e84STetsuo Handa 		error = tomoyo_path_number3_perm2(&r, operation, &buf, mode,
1467a1f9bb6aSTetsuo Handa 						  new_decode_dev(dev));
1468c8c57e84STetsuo Handa 		kfree(buf.name);
1469a1f9bb6aSTetsuo Handa 	}
1470a1f9bb6aSTetsuo Handa 	tomoyo_read_unlock(idx);
1471a1f9bb6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1472a1f9bb6aSTetsuo Handa 		error = 0;
1473a1f9bb6aSTetsuo Handa 	return error;
1474a1f9bb6aSTetsuo Handa }
1475a1f9bb6aSTetsuo Handa 
1476a1f9bb6aSTetsuo Handa /**
14777ef61233STetsuo Handa  * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
1478b69a54eeSKentaro Takeda  *
1479b69a54eeSKentaro Takeda  * @operation: Type of operation.
1480b69a54eeSKentaro Takeda  * @path1:      Pointer to "struct path".
1481b69a54eeSKentaro Takeda  * @path2:      Pointer to "struct path".
1482b69a54eeSKentaro Takeda  *
1483b69a54eeSKentaro Takeda  * Returns 0 on success, negative value otherwise.
1484b69a54eeSKentaro Takeda  */
148597d6931eSTetsuo Handa int tomoyo_path2_perm(const u8 operation, struct path *path1,
1486b69a54eeSKentaro Takeda 		      struct path *path2)
1487b69a54eeSKentaro Takeda {
1488b69a54eeSKentaro Takeda 	int error = -ENOMEM;
148917fcfbd9STetsuo Handa 	const char *msg;
1490c8c57e84STetsuo Handa 	struct tomoyo_path_info buf1;
1491c8c57e84STetsuo Handa 	struct tomoyo_path_info buf2;
1492cb0abe6aSTetsuo Handa 	struct tomoyo_request_info r;
1493fdb8ebb7STetsuo Handa 	int idx;
1494b69a54eeSKentaro Takeda 
1495cb0abe6aSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
1496cb0abe6aSTetsuo Handa 	    !path1->mnt || !path2->mnt)
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;
1504b69a54eeSKentaro Takeda 	{
1505b69a54eeSKentaro Takeda 		struct dentry *dentry = path1->dentry;
1506b69a54eeSKentaro Takeda 		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
1507c8c57e84STetsuo Handa 			tomoyo_add_slash(&buf1);
1508c8c57e84STetsuo Handa 			tomoyo_add_slash(&buf2);
1509b69a54eeSKentaro Takeda 		}
1510b69a54eeSKentaro Takeda 	}
151117fcfbd9STetsuo Handa 	do {
1512c8c57e84STetsuo Handa 		error = tomoyo_path2_acl(&r, operation, &buf1, &buf2);
1513b69a54eeSKentaro Takeda 		if (!error)
151417fcfbd9STetsuo Handa 			break;
151517fcfbd9STetsuo Handa 		msg = tomoyo_path22keyword(operation);
1516c8c57e84STetsuo Handa 		tomoyo_warn_log(&r, "%s %s %s", msg, buf1.name, buf2.name);
151717fcfbd9STetsuo Handa 		error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg,
1518c8c57e84STetsuo Handa 					  tomoyo_file_pattern(&buf1),
1519c8c57e84STetsuo Handa 					  tomoyo_file_pattern(&buf2));
152017fcfbd9STetsuo Handa         } while (error == TOMOYO_RETRY_REQUEST);
1521b69a54eeSKentaro Takeda  out:
1522c8c57e84STetsuo Handa 	kfree(buf1.name);
1523c8c57e84STetsuo Handa 	kfree(buf2.name);
1524fdb8ebb7STetsuo Handa 	tomoyo_read_unlock(idx);
1525cb0abe6aSTetsuo Handa 	if (r.mode != TOMOYO_CONFIG_ENFORCING)
1526b69a54eeSKentaro Takeda 		error = 0;
1527b69a54eeSKentaro Takeda 	return error;
1528b69a54eeSKentaro Takeda }
1529a1f9bb6aSTetsuo Handa 
1530a1f9bb6aSTetsuo Handa /**
1531a1f9bb6aSTetsuo Handa  * tomoyo_write_file_policy - Update file related list.
1532a1f9bb6aSTetsuo Handa  *
1533a1f9bb6aSTetsuo Handa  * @data:      String to parse.
1534a1f9bb6aSTetsuo Handa  * @domain:    Pointer to "struct tomoyo_domain_info".
1535a1f9bb6aSTetsuo Handa  * @is_delete: True if it is a delete request.
1536a1f9bb6aSTetsuo Handa  *
1537a1f9bb6aSTetsuo Handa  * Returns 0 on success, negative value otherwise.
1538a1f9bb6aSTetsuo Handa  *
1539a1f9bb6aSTetsuo Handa  * Caller holds tomoyo_read_lock().
1540a1f9bb6aSTetsuo Handa  */
1541a1f9bb6aSTetsuo Handa int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
1542a1f9bb6aSTetsuo Handa 			     const bool is_delete)
1543a1f9bb6aSTetsuo Handa {
1544a1f9bb6aSTetsuo Handa 	char *w[5];
1545a1f9bb6aSTetsuo Handa 	u8 type;
1546a1f9bb6aSTetsuo Handa 	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
1547a1f9bb6aSTetsuo Handa 		return -EINVAL;
1548a1f9bb6aSTetsuo Handa 	if (strncmp(w[0], "allow_", 6)) {
1549a1f9bb6aSTetsuo Handa 		unsigned int perm;
1550a1f9bb6aSTetsuo Handa 		if (sscanf(w[0], "%u", &perm) == 1)
1551a1f9bb6aSTetsuo Handa 			return tomoyo_update_file_acl((u8) perm, w[1], domain,
1552a1f9bb6aSTetsuo Handa 						      is_delete);
1553a1f9bb6aSTetsuo Handa 		goto out;
1554a1f9bb6aSTetsuo Handa 	}
1555a1f9bb6aSTetsuo Handa 	w[0] += 6;
1556a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
1557a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_keyword[type]))
1558a1f9bb6aSTetsuo Handa 			continue;
1559a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_acl(type, w[1], domain, is_delete);
1560a1f9bb6aSTetsuo Handa 	}
1561a1f9bb6aSTetsuo Handa 	if (!w[2][0])
1562a1f9bb6aSTetsuo Handa 		goto out;
1563a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
1564a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path2_keyword[type]))
1565a1f9bb6aSTetsuo Handa 			continue;
1566a1f9bb6aSTetsuo Handa 		return tomoyo_update_path2_acl(type, w[1], w[2], domain,
1567a1f9bb6aSTetsuo Handa 					       is_delete);
1568a1f9bb6aSTetsuo Handa 	}
1569a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
1570a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_number_keyword[type]))
1571a1f9bb6aSTetsuo Handa 			continue;
1572a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
1573a1f9bb6aSTetsuo Handa 						     is_delete);
1574a1f9bb6aSTetsuo Handa 	}
1575a1f9bb6aSTetsuo Handa 	if (!w[3][0] || !w[4][0])
1576a1f9bb6aSTetsuo Handa 		goto out;
1577a1f9bb6aSTetsuo Handa 	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER3_OPERATION; type++) {
1578a1f9bb6aSTetsuo Handa 		if (strcmp(w[0], tomoyo_path_number3_keyword[type]))
1579a1f9bb6aSTetsuo Handa 			continue;
1580a1f9bb6aSTetsuo Handa 		return tomoyo_update_path_number3_acl(type, w[1], w[2], w[3],
1581a1f9bb6aSTetsuo Handa 						      w[4], domain, is_delete);
1582a1f9bb6aSTetsuo Handa 	}
1583a1f9bb6aSTetsuo Handa  out:
1584a1f9bb6aSTetsuo Handa 	return -EINVAL;
1585a1f9bb6aSTetsuo Handa }
1586