1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * security/tomoyo/file.c |
4 | * |
5 | * Copyright (C) 2005-2011 NTT DATA CORPORATION |
6 | */ |
7 | |
8 | #include "common.h" |
9 | #include <linux/slab.h> |
10 | |
11 | /* |
12 | * Mapping table from "enum tomoyo_path_acl_index" to "enum tomoyo_mac_index". |
13 | */ |
14 | static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = { |
15 | [TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE, |
16 | [TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN, |
17 | [TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN, |
18 | [TOMOYO_TYPE_APPEND] = TOMOYO_MAC_FILE_OPEN, |
19 | [TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK, |
20 | [TOMOYO_TYPE_GETATTR] = TOMOYO_MAC_FILE_GETATTR, |
21 | [TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR, |
22 | [TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE, |
23 | [TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK, |
24 | [TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT, |
25 | [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT, |
26 | }; |
27 | |
28 | /* |
29 | * Mapping table from "enum tomoyo_mkdev_acl_index" to "enum tomoyo_mac_index". |
30 | */ |
31 | const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = { |
32 | [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK, |
33 | [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR, |
34 | }; |
35 | |
36 | /* |
37 | * Mapping table from "enum tomoyo_path2_acl_index" to "enum tomoyo_mac_index". |
38 | */ |
39 | const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = { |
40 | [TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK, |
41 | [TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME, |
42 | [TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT, |
43 | }; |
44 | |
45 | /* |
46 | * Mapping table from "enum tomoyo_path_number_acl_index" to |
47 | * "enum tomoyo_mac_index". |
48 | */ |
49 | const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { |
50 | [TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE, |
51 | [TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR, |
52 | [TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO, |
53 | [TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK, |
54 | [TOMOYO_TYPE_IOCTL] = TOMOYO_MAC_FILE_IOCTL, |
55 | [TOMOYO_TYPE_CHMOD] = TOMOYO_MAC_FILE_CHMOD, |
56 | [TOMOYO_TYPE_CHOWN] = TOMOYO_MAC_FILE_CHOWN, |
57 | [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP, |
58 | }; |
59 | |
60 | /** |
61 | * tomoyo_put_name_union - Drop reference on "struct tomoyo_name_union". |
62 | * |
63 | * @ptr: Pointer to "struct tomoyo_name_union". |
64 | * |
65 | * Returns nothing. |
66 | */ |
67 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr) |
68 | { |
69 | tomoyo_put_group(group: ptr->group); |
70 | tomoyo_put_name(name: ptr->filename); |
71 | } |
72 | |
73 | /** |
74 | * tomoyo_compare_name_union - Check whether a name matches "struct tomoyo_name_union" or not. |
75 | * |
76 | * @name: Pointer to "struct tomoyo_path_info". |
77 | * @ptr: Pointer to "struct tomoyo_name_union". |
78 | * |
79 | * Returns "struct tomoyo_path_info" if @name matches @ptr, NULL otherwise. |
80 | */ |
81 | const struct tomoyo_path_info * |
82 | tomoyo_compare_name_union(const struct tomoyo_path_info *name, |
83 | const struct tomoyo_name_union *ptr) |
84 | { |
85 | if (ptr->group) |
86 | return tomoyo_path_matches_group(pathname: name, group: ptr->group); |
87 | if (tomoyo_path_matches_pattern(filename: name, pattern: ptr->filename)) |
88 | return ptr->filename; |
89 | return NULL; |
90 | } |
91 | |
92 | /** |
93 | * tomoyo_put_number_union - Drop reference on "struct tomoyo_number_union". |
94 | * |
95 | * @ptr: Pointer to "struct tomoyo_number_union". |
96 | * |
97 | * Returns nothing. |
98 | */ |
99 | void tomoyo_put_number_union(struct tomoyo_number_union *ptr) |
100 | { |
101 | tomoyo_put_group(group: ptr->group); |
102 | } |
103 | |
104 | /** |
105 | * tomoyo_compare_number_union - Check whether a value matches "struct tomoyo_number_union" or not. |
106 | * |
107 | * @value: Number to check. |
108 | * @ptr: Pointer to "struct tomoyo_number_union". |
109 | * |
110 | * Returns true if @value matches @ptr, false otherwise. |
111 | */ |
112 | bool tomoyo_compare_number_union(const unsigned long value, |
113 | const struct tomoyo_number_union *ptr) |
114 | { |
115 | if (ptr->group) |
116 | return tomoyo_number_matches_group(min: value, max: value, group: ptr->group); |
117 | return value >= ptr->values[0] && value <= ptr->values[1]; |
118 | } |
119 | |
120 | /** |
121 | * tomoyo_add_slash - Add trailing '/' if needed. |
122 | * |
123 | * @buf: Pointer to "struct tomoyo_path_info". |
124 | * |
125 | * Returns nothing. |
126 | * |
127 | * @buf must be generated by tomoyo_encode() because this function does not |
128 | * allocate memory for adding '/'. |
129 | */ |
130 | static void tomoyo_add_slash(struct tomoyo_path_info *buf) |
131 | { |
132 | if (buf->is_dir) |
133 | return; |
134 | /* |
135 | * This is OK because tomoyo_encode() reserves space for appending "/". |
136 | */ |
137 | strcat(p: (char *) buf->name, q: "/" ); |
138 | tomoyo_fill_path_info(ptr: buf); |
139 | } |
140 | |
141 | /** |
142 | * tomoyo_get_realpath - Get realpath. |
143 | * |
144 | * @buf: Pointer to "struct tomoyo_path_info". |
145 | * @path: Pointer to "struct path". |
146 | * |
147 | * Returns true on success, false otherwise. |
148 | */ |
149 | static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, const struct path *path) |
150 | { |
151 | buf->name = tomoyo_realpath_from_path(path); |
152 | if (buf->name) { |
153 | tomoyo_fill_path_info(ptr: buf); |
154 | return true; |
155 | } |
156 | return false; |
157 | } |
158 | |
159 | /** |
160 | * tomoyo_audit_path_log - Audit path request log. |
161 | * |
162 | * @r: Pointer to "struct tomoyo_request_info". |
163 | * |
164 | * Returns 0 on success, negative value otherwise. |
165 | */ |
166 | static int tomoyo_audit_path_log(struct tomoyo_request_info *r) |
167 | { |
168 | return tomoyo_supervisor(r, fmt: "file %s %s\n" , tomoyo_path_keyword |
169 | [r->param.path.operation], |
170 | r->param.path.filename->name); |
171 | } |
172 | |
173 | /** |
174 | * tomoyo_audit_path2_log - Audit path/path request log. |
175 | * |
176 | * @r: Pointer to "struct tomoyo_request_info". |
177 | * |
178 | * Returns 0 on success, negative value otherwise. |
179 | */ |
180 | static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) |
181 | { |
182 | return tomoyo_supervisor(r, fmt: "file %s %s %s\n" , tomoyo_mac_keywords |
183 | [tomoyo_pp2mac[r->param.path2.operation]], |
184 | r->param.path2.filename1->name, |
185 | r->param.path2.filename2->name); |
186 | } |
187 | |
188 | /** |
189 | * tomoyo_audit_mkdev_log - Audit path/number/number/number request log. |
190 | * |
191 | * @r: Pointer to "struct tomoyo_request_info". |
192 | * |
193 | * Returns 0 on success, negative value otherwise. |
194 | */ |
195 | static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r) |
196 | { |
197 | return tomoyo_supervisor(r, fmt: "file %s %s 0%o %u %u\n" , |
198 | tomoyo_mac_keywords |
199 | [tomoyo_pnnn2mac[r->param.mkdev.operation]], |
200 | r->param.mkdev.filename->name, |
201 | r->param.mkdev.mode, r->param.mkdev.major, |
202 | r->param.mkdev.minor); |
203 | } |
204 | |
205 | /** |
206 | * tomoyo_audit_path_number_log - Audit path/number request log. |
207 | * |
208 | * @r: Pointer to "struct tomoyo_request_info". |
209 | * |
210 | * Returns 0 on success, negative value otherwise. |
211 | */ |
212 | static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) |
213 | { |
214 | const u8 type = r->param.path_number.operation; |
215 | u8 radix; |
216 | char buffer[64]; |
217 | |
218 | switch (type) { |
219 | case TOMOYO_TYPE_CREATE: |
220 | case TOMOYO_TYPE_MKDIR: |
221 | case TOMOYO_TYPE_MKFIFO: |
222 | case TOMOYO_TYPE_MKSOCK: |
223 | case TOMOYO_TYPE_CHMOD: |
224 | radix = TOMOYO_VALUE_TYPE_OCTAL; |
225 | break; |
226 | case TOMOYO_TYPE_IOCTL: |
227 | radix = TOMOYO_VALUE_TYPE_HEXADECIMAL; |
228 | break; |
229 | default: |
230 | radix = TOMOYO_VALUE_TYPE_DECIMAL; |
231 | break; |
232 | } |
233 | tomoyo_print_ulong(buffer, buffer_len: sizeof(buffer), value: r->param.path_number.number, |
234 | type: radix); |
235 | return tomoyo_supervisor(r, fmt: "file %s %s %s\n" , tomoyo_mac_keywords |
236 | [tomoyo_pn2mac[type]], |
237 | r->param.path_number.filename->name, buffer); |
238 | } |
239 | |
240 | /** |
241 | * tomoyo_check_path_acl - Check permission for path operation. |
242 | * |
243 | * @r: Pointer to "struct tomoyo_request_info". |
244 | * @ptr: Pointer to "struct tomoyo_acl_info". |
245 | * |
246 | * Returns true if granted, false otherwise. |
247 | * |
248 | * To be able to use wildcard for domain transition, this function sets |
249 | * matching entry on success. Since the caller holds tomoyo_read_lock(), |
250 | * it is safe to set matching entry. |
251 | */ |
252 | static bool tomoyo_check_path_acl(struct tomoyo_request_info *r, |
253 | const struct tomoyo_acl_info *ptr) |
254 | { |
255 | const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl), |
256 | head); |
257 | |
258 | if (acl->perm & (1 << r->param.path.operation)) { |
259 | r->param.path.matched_path = |
260 | tomoyo_compare_name_union(name: r->param.path.filename, |
261 | ptr: &acl->name); |
262 | return r->param.path.matched_path != NULL; |
263 | } |
264 | return false; |
265 | } |
266 | |
267 | /** |
268 | * tomoyo_check_path_number_acl - Check permission for path number operation. |
269 | * |
270 | * @r: Pointer to "struct tomoyo_request_info". |
271 | * @ptr: Pointer to "struct tomoyo_acl_info". |
272 | * |
273 | * Returns true if granted, false otherwise. |
274 | */ |
275 | static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r, |
276 | const struct tomoyo_acl_info *ptr) |
277 | { |
278 | const struct tomoyo_path_number_acl *acl = |
279 | container_of(ptr, typeof(*acl), head); |
280 | |
281 | return (acl->perm & (1 << r->param.path_number.operation)) && |
282 | tomoyo_compare_number_union(value: r->param.path_number.number, |
283 | ptr: &acl->number) && |
284 | tomoyo_compare_name_union(name: r->param.path_number.filename, |
285 | ptr: &acl->name); |
286 | } |
287 | |
288 | /** |
289 | * tomoyo_check_path2_acl - Check permission for path path operation. |
290 | * |
291 | * @r: Pointer to "struct tomoyo_request_info". |
292 | * @ptr: Pointer to "struct tomoyo_acl_info". |
293 | * |
294 | * Returns true if granted, false otherwise. |
295 | */ |
296 | static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r, |
297 | const struct tomoyo_acl_info *ptr) |
298 | { |
299 | const struct tomoyo_path2_acl *acl = |
300 | container_of(ptr, typeof(*acl), head); |
301 | |
302 | return (acl->perm & (1 << r->param.path2.operation)) && |
303 | tomoyo_compare_name_union(name: r->param.path2.filename1, ptr: &acl->name1) |
304 | && tomoyo_compare_name_union(name: r->param.path2.filename2, |
305 | ptr: &acl->name2); |
306 | } |
307 | |
308 | /** |
309 | * tomoyo_check_mkdev_acl - Check permission for path number number number operation. |
310 | * |
311 | * @r: Pointer to "struct tomoyo_request_info". |
312 | * @ptr: Pointer to "struct tomoyo_acl_info". |
313 | * |
314 | * Returns true if granted, false otherwise. |
315 | */ |
316 | static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r, |
317 | const struct tomoyo_acl_info *ptr) |
318 | { |
319 | const struct tomoyo_mkdev_acl *acl = |
320 | container_of(ptr, typeof(*acl), head); |
321 | |
322 | return (acl->perm & (1 << r->param.mkdev.operation)) && |
323 | tomoyo_compare_number_union(value: r->param.mkdev.mode, |
324 | ptr: &acl->mode) && |
325 | tomoyo_compare_number_union(value: r->param.mkdev.major, |
326 | ptr: &acl->major) && |
327 | tomoyo_compare_number_union(value: r->param.mkdev.minor, |
328 | ptr: &acl->minor) && |
329 | tomoyo_compare_name_union(name: r->param.mkdev.filename, |
330 | ptr: &acl->name); |
331 | } |
332 | |
333 | /** |
334 | * tomoyo_same_path_acl - Check for duplicated "struct tomoyo_path_acl" entry. |
335 | * |
336 | * @a: Pointer to "struct tomoyo_acl_info". |
337 | * @b: Pointer to "struct tomoyo_acl_info". |
338 | * |
339 | * Returns true if @a == @b except permission bits, false otherwise. |
340 | */ |
341 | static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, |
342 | const struct tomoyo_acl_info *b) |
343 | { |
344 | const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head); |
345 | const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head); |
346 | |
347 | return tomoyo_same_name_union(a: &p1->name, b: &p2->name); |
348 | } |
349 | |
350 | /** |
351 | * tomoyo_merge_path_acl - Merge duplicated "struct tomoyo_path_acl" entry. |
352 | * |
353 | * @a: Pointer to "struct tomoyo_acl_info". |
354 | * @b: Pointer to "struct tomoyo_acl_info". |
355 | * @is_delete: True for @a &= ~@b, false for @a |= @b. |
356 | * |
357 | * Returns true if @a is empty, false otherwise. |
358 | */ |
359 | static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, |
360 | struct tomoyo_acl_info *b, |
361 | const bool is_delete) |
362 | { |
363 | u16 * const a_perm = &container_of(a, struct tomoyo_path_acl, head) |
364 | ->perm; |
365 | u16 perm = READ_ONCE(*a_perm); |
366 | const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm; |
367 | |
368 | if (is_delete) |
369 | perm &= ~b_perm; |
370 | else |
371 | perm |= b_perm; |
372 | WRITE_ONCE(*a_perm, perm); |
373 | return !perm; |
374 | } |
375 | |
376 | /** |
377 | * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. |
378 | * |
379 | * @perm: Permission. |
380 | * @param: Pointer to "struct tomoyo_acl_param". |
381 | * |
382 | * Returns 0 on success, negative value otherwise. |
383 | * |
384 | * Caller holds tomoyo_read_lock(). |
385 | */ |
386 | static int tomoyo_update_path_acl(const u16 perm, |
387 | struct tomoyo_acl_param *param) |
388 | { |
389 | struct tomoyo_path_acl e = { |
390 | .head.type = TOMOYO_TYPE_PATH_ACL, |
391 | .perm = perm |
392 | }; |
393 | int error; |
394 | |
395 | if (!tomoyo_parse_name_union(param, ptr: &e.name)) |
396 | error = -EINVAL; |
397 | else |
398 | error = tomoyo_update_domain(new_entry: &e.head, size: sizeof(e), param, |
399 | check_duplicate: tomoyo_same_path_acl, |
400 | merge_duplicate: tomoyo_merge_path_acl); |
401 | tomoyo_put_name_union(ptr: &e.name); |
402 | return error; |
403 | } |
404 | |
405 | /** |
406 | * tomoyo_same_mkdev_acl - Check for duplicated "struct tomoyo_mkdev_acl" entry. |
407 | * |
408 | * @a: Pointer to "struct tomoyo_acl_info". |
409 | * @b: Pointer to "struct tomoyo_acl_info". |
410 | * |
411 | * Returns true if @a == @b except permission bits, false otherwise. |
412 | */ |
413 | static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a, |
414 | const struct tomoyo_acl_info *b) |
415 | { |
416 | const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head); |
417 | const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head); |
418 | |
419 | return tomoyo_same_name_union(a: &p1->name, b: &p2->name) && |
420 | tomoyo_same_number_union(a: &p1->mode, b: &p2->mode) && |
421 | tomoyo_same_number_union(a: &p1->major, b: &p2->major) && |
422 | tomoyo_same_number_union(a: &p1->minor, b: &p2->minor); |
423 | } |
424 | |
425 | /** |
426 | * tomoyo_merge_mkdev_acl - Merge duplicated "struct tomoyo_mkdev_acl" entry. |
427 | * |
428 | * @a: Pointer to "struct tomoyo_acl_info". |
429 | * @b: Pointer to "struct tomoyo_acl_info". |
430 | * @is_delete: True for @a &= ~@b, false for @a |= @b. |
431 | * |
432 | * Returns true if @a is empty, false otherwise. |
433 | */ |
434 | static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a, |
435 | struct tomoyo_acl_info *b, |
436 | const bool is_delete) |
437 | { |
438 | u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl, |
439 | head)->perm; |
440 | u8 perm = READ_ONCE(*a_perm); |
441 | const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head) |
442 | ->perm; |
443 | |
444 | if (is_delete) |
445 | perm &= ~b_perm; |
446 | else |
447 | perm |= b_perm; |
448 | WRITE_ONCE(*a_perm, perm); |
449 | return !perm; |
450 | } |
451 | |
452 | /** |
453 | * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list. |
454 | * |
455 | * @perm: Permission. |
456 | * @param: Pointer to "struct tomoyo_acl_param". |
457 | * |
458 | * Returns 0 on success, negative value otherwise. |
459 | * |
460 | * Caller holds tomoyo_read_lock(). |
461 | */ |
462 | static int tomoyo_update_mkdev_acl(const u8 perm, |
463 | struct tomoyo_acl_param *param) |
464 | { |
465 | struct tomoyo_mkdev_acl e = { |
466 | .head.type = TOMOYO_TYPE_MKDEV_ACL, |
467 | .perm = perm |
468 | }; |
469 | int error; |
470 | |
471 | if (!tomoyo_parse_name_union(param, ptr: &e.name) || |
472 | !tomoyo_parse_number_union(param, ptr: &e.mode) || |
473 | !tomoyo_parse_number_union(param, ptr: &e.major) || |
474 | !tomoyo_parse_number_union(param, ptr: &e.minor)) |
475 | error = -EINVAL; |
476 | else |
477 | error = tomoyo_update_domain(new_entry: &e.head, size: sizeof(e), param, |
478 | check_duplicate: tomoyo_same_mkdev_acl, |
479 | merge_duplicate: tomoyo_merge_mkdev_acl); |
480 | tomoyo_put_name_union(ptr: &e.name); |
481 | tomoyo_put_number_union(ptr: &e.mode); |
482 | tomoyo_put_number_union(ptr: &e.major); |
483 | tomoyo_put_number_union(ptr: &e.minor); |
484 | return error; |
485 | } |
486 | |
487 | /** |
488 | * tomoyo_same_path2_acl - Check for duplicated "struct tomoyo_path2_acl" entry. |
489 | * |
490 | * @a: Pointer to "struct tomoyo_acl_info". |
491 | * @b: Pointer to "struct tomoyo_acl_info". |
492 | * |
493 | * Returns true if @a == @b except permission bits, false otherwise. |
494 | */ |
495 | static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a, |
496 | const struct tomoyo_acl_info *b) |
497 | { |
498 | const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head); |
499 | const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head); |
500 | |
501 | return tomoyo_same_name_union(a: &p1->name1, b: &p2->name1) && |
502 | tomoyo_same_name_union(a: &p1->name2, b: &p2->name2); |
503 | } |
504 | |
505 | /** |
506 | * tomoyo_merge_path2_acl - Merge duplicated "struct tomoyo_path2_acl" entry. |
507 | * |
508 | * @a: Pointer to "struct tomoyo_acl_info". |
509 | * @b: Pointer to "struct tomoyo_acl_info". |
510 | * @is_delete: True for @a &= ~@b, false for @a |= @b. |
511 | * |
512 | * Returns true if @a is empty, false otherwise. |
513 | */ |
514 | static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a, |
515 | struct tomoyo_acl_info *b, |
516 | const bool is_delete) |
517 | { |
518 | u8 * const a_perm = &container_of(a, struct tomoyo_path2_acl, head) |
519 | ->perm; |
520 | u8 perm = READ_ONCE(*a_perm); |
521 | const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm; |
522 | |
523 | if (is_delete) |
524 | perm &= ~b_perm; |
525 | else |
526 | perm |= b_perm; |
527 | WRITE_ONCE(*a_perm, perm); |
528 | return !perm; |
529 | } |
530 | |
531 | /** |
532 | * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. |
533 | * |
534 | * @perm: Permission. |
535 | * @param: Pointer to "struct tomoyo_acl_param". |
536 | * |
537 | * Returns 0 on success, negative value otherwise. |
538 | * |
539 | * Caller holds tomoyo_read_lock(). |
540 | */ |
541 | static int tomoyo_update_path2_acl(const u8 perm, |
542 | struct tomoyo_acl_param *param) |
543 | { |
544 | struct tomoyo_path2_acl e = { |
545 | .head.type = TOMOYO_TYPE_PATH2_ACL, |
546 | .perm = perm |
547 | }; |
548 | int error; |
549 | |
550 | if (!tomoyo_parse_name_union(param, ptr: &e.name1) || |
551 | !tomoyo_parse_name_union(param, ptr: &e.name2)) |
552 | error = -EINVAL; |
553 | else |
554 | error = tomoyo_update_domain(new_entry: &e.head, size: sizeof(e), param, |
555 | check_duplicate: tomoyo_same_path2_acl, |
556 | merge_duplicate: tomoyo_merge_path2_acl); |
557 | tomoyo_put_name_union(ptr: &e.name1); |
558 | tomoyo_put_name_union(ptr: &e.name2); |
559 | return error; |
560 | } |
561 | |
562 | /** |
563 | * tomoyo_path_permission - Check permission for single path operation. |
564 | * |
565 | * @r: Pointer to "struct tomoyo_request_info". |
566 | * @operation: Type of operation. |
567 | * @filename: Filename to check. |
568 | * |
569 | * Returns 0 on success, negative value otherwise. |
570 | * |
571 | * Caller holds tomoyo_read_lock(). |
572 | */ |
573 | static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, |
574 | const struct tomoyo_path_info *filename) |
575 | { |
576 | int error; |
577 | |
578 | r->type = tomoyo_p2mac[operation]; |
579 | r->mode = tomoyo_get_mode(ns: r->domain->ns, profile: r->profile, index: r->type); |
580 | if (r->mode == TOMOYO_CONFIG_DISABLED) |
581 | return 0; |
582 | r->param_type = TOMOYO_TYPE_PATH_ACL; |
583 | r->param.path.filename = filename; |
584 | r->param.path.operation = operation; |
585 | do { |
586 | tomoyo_check_acl(r, check_entry: tomoyo_check_path_acl); |
587 | error = tomoyo_audit_path_log(r); |
588 | } while (error == TOMOYO_RETRY_REQUEST); |
589 | return error; |
590 | } |
591 | |
592 | /** |
593 | * tomoyo_execute_permission - Check permission for execute operation. |
594 | * |
595 | * @r: Pointer to "struct tomoyo_request_info". |
596 | * @filename: Filename to check. |
597 | * |
598 | * Returns 0 on success, negative value otherwise. |
599 | * |
600 | * Caller holds tomoyo_read_lock(). |
601 | */ |
602 | int tomoyo_execute_permission(struct tomoyo_request_info *r, |
603 | const struct tomoyo_path_info *filename) |
604 | { |
605 | /* |
606 | * Unlike other permission checks, this check is done regardless of |
607 | * profile mode settings in order to check for domain transition |
608 | * preference. |
609 | */ |
610 | r->type = TOMOYO_MAC_FILE_EXECUTE; |
611 | r->mode = tomoyo_get_mode(ns: r->domain->ns, profile: r->profile, index: r->type); |
612 | r->param_type = TOMOYO_TYPE_PATH_ACL; |
613 | r->param.path.filename = filename; |
614 | r->param.path.operation = TOMOYO_TYPE_EXECUTE; |
615 | tomoyo_check_acl(r, check_entry: tomoyo_check_path_acl); |
616 | r->ee->transition = r->matched_acl && r->matched_acl->cond ? |
617 | r->matched_acl->cond->transit : NULL; |
618 | if (r->mode != TOMOYO_CONFIG_DISABLED) |
619 | return tomoyo_audit_path_log(r); |
620 | return 0; |
621 | } |
622 | |
623 | /** |
624 | * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry. |
625 | * |
626 | * @a: Pointer to "struct tomoyo_acl_info". |
627 | * @b: Pointer to "struct tomoyo_acl_info". |
628 | * |
629 | * Returns true if @a == @b except permission bits, false otherwise. |
630 | */ |
631 | static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, |
632 | const struct tomoyo_acl_info *b) |
633 | { |
634 | const struct tomoyo_path_number_acl *p1 = container_of(a, typeof(*p1), |
635 | head); |
636 | const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2), |
637 | head); |
638 | |
639 | return tomoyo_same_name_union(a: &p1->name, b: &p2->name) && |
640 | tomoyo_same_number_union(a: &p1->number, b: &p2->number); |
641 | } |
642 | |
643 | /** |
644 | * tomoyo_merge_path_number_acl - Merge duplicated "struct tomoyo_path_number_acl" entry. |
645 | * |
646 | * @a: Pointer to "struct tomoyo_acl_info". |
647 | * @b: Pointer to "struct tomoyo_acl_info". |
648 | * @is_delete: True for @a &= ~@b, false for @a |= @b. |
649 | * |
650 | * Returns true if @a is empty, false otherwise. |
651 | */ |
652 | static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a, |
653 | struct tomoyo_acl_info *b, |
654 | const bool is_delete) |
655 | { |
656 | u8 * const a_perm = &container_of(a, struct tomoyo_path_number_acl, |
657 | head)->perm; |
658 | u8 perm = READ_ONCE(*a_perm); |
659 | const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head) |
660 | ->perm; |
661 | |
662 | if (is_delete) |
663 | perm &= ~b_perm; |
664 | else |
665 | perm |= b_perm; |
666 | WRITE_ONCE(*a_perm, perm); |
667 | return !perm; |
668 | } |
669 | |
670 | /** |
671 | * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL. |
672 | * |
673 | * @perm: Permission. |
674 | * @param: Pointer to "struct tomoyo_acl_param". |
675 | * |
676 | * Returns 0 on success, negative value otherwise. |
677 | */ |
678 | static int tomoyo_update_path_number_acl(const u8 perm, |
679 | struct tomoyo_acl_param *param) |
680 | { |
681 | struct tomoyo_path_number_acl e = { |
682 | .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, |
683 | .perm = perm |
684 | }; |
685 | int error; |
686 | |
687 | if (!tomoyo_parse_name_union(param, ptr: &e.name) || |
688 | !tomoyo_parse_number_union(param, ptr: &e.number)) |
689 | error = -EINVAL; |
690 | else |
691 | error = tomoyo_update_domain(new_entry: &e.head, size: sizeof(e), param, |
692 | check_duplicate: tomoyo_same_path_number_acl, |
693 | merge_duplicate: tomoyo_merge_path_number_acl); |
694 | tomoyo_put_name_union(ptr: &e.name); |
695 | tomoyo_put_number_union(ptr: &e.number); |
696 | return error; |
697 | } |
698 | |
699 | /** |
700 | * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". |
701 | * |
702 | * @type: Type of operation. |
703 | * @path: Pointer to "struct path". |
704 | * @number: Number. |
705 | * |
706 | * Returns 0 on success, negative value otherwise. |
707 | */ |
708 | int tomoyo_path_number_perm(const u8 type, const struct path *path, |
709 | unsigned long number) |
710 | { |
711 | struct tomoyo_request_info r; |
712 | struct tomoyo_obj_info obj = { |
713 | .path1 = { .mnt = path->mnt, .dentry = path->dentry }, |
714 | }; |
715 | int error = -ENOMEM; |
716 | struct tomoyo_path_info buf; |
717 | int idx; |
718 | |
719 | if (tomoyo_init_request_info(r: &r, NULL, index: tomoyo_pn2mac[type]) |
720 | == TOMOYO_CONFIG_DISABLED) |
721 | return 0; |
722 | idx = tomoyo_read_lock(); |
723 | if (!tomoyo_get_realpath(buf: &buf, path)) |
724 | goto out; |
725 | r.obj = &obj; |
726 | if (type == TOMOYO_TYPE_MKDIR) |
727 | tomoyo_add_slash(buf: &buf); |
728 | r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; |
729 | r.param.path_number.operation = type; |
730 | r.param.path_number.filename = &buf; |
731 | r.param.path_number.number = number; |
732 | do { |
733 | tomoyo_check_acl(r: &r, check_entry: tomoyo_check_path_number_acl); |
734 | error = tomoyo_audit_path_number_log(r: &r); |
735 | } while (error == TOMOYO_RETRY_REQUEST); |
736 | kfree(objp: buf.name); |
737 | out: |
738 | tomoyo_read_unlock(idx); |
739 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
740 | error = 0; |
741 | return error; |
742 | } |
743 | |
744 | /** |
745 | * tomoyo_check_open_permission - Check permission for "read" and "write". |
746 | * |
747 | * @domain: Pointer to "struct tomoyo_domain_info". |
748 | * @path: Pointer to "struct path". |
749 | * @flag: Flags for open(). |
750 | * |
751 | * Returns 0 on success, negative value otherwise. |
752 | */ |
753 | int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, |
754 | const struct path *path, const int flag) |
755 | { |
756 | const u8 acc_mode = ACC_MODE(flag); |
757 | int error = 0; |
758 | struct tomoyo_path_info buf; |
759 | struct tomoyo_request_info r; |
760 | struct tomoyo_obj_info obj = { |
761 | .path1 = { .mnt = path->mnt, .dentry = path->dentry }, |
762 | }; |
763 | int idx; |
764 | |
765 | buf.name = NULL; |
766 | r.mode = TOMOYO_CONFIG_DISABLED; |
767 | idx = tomoyo_read_lock(); |
768 | if (acc_mode && |
769 | tomoyo_init_request_info(r: &r, domain, index: TOMOYO_MAC_FILE_OPEN) |
770 | != TOMOYO_CONFIG_DISABLED) { |
771 | if (!tomoyo_get_realpath(buf: &buf, path)) { |
772 | error = -ENOMEM; |
773 | goto out; |
774 | } |
775 | r.obj = &obj; |
776 | if (acc_mode & MAY_READ) |
777 | error = tomoyo_path_permission(r: &r, operation: TOMOYO_TYPE_READ, |
778 | filename: &buf); |
779 | if (!error && (acc_mode & MAY_WRITE)) |
780 | error = tomoyo_path_permission(r: &r, operation: (flag & O_APPEND) ? |
781 | TOMOYO_TYPE_APPEND : |
782 | TOMOYO_TYPE_WRITE, |
783 | filename: &buf); |
784 | } |
785 | out: |
786 | kfree(objp: buf.name); |
787 | tomoyo_read_unlock(idx); |
788 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
789 | error = 0; |
790 | return error; |
791 | } |
792 | |
793 | /** |
794 | * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "chroot" and "unmount". |
795 | * |
796 | * @operation: Type of operation. |
797 | * @path: Pointer to "struct path". |
798 | * @target: Symlink's target if @operation is TOMOYO_TYPE_SYMLINK, |
799 | * NULL otherwise. |
800 | * |
801 | * Returns 0 on success, negative value otherwise. |
802 | */ |
803 | int tomoyo_path_perm(const u8 operation, const struct path *path, const char *target) |
804 | { |
805 | struct tomoyo_request_info r; |
806 | struct tomoyo_obj_info obj = { |
807 | .path1 = { .mnt = path->mnt, .dentry = path->dentry }, |
808 | }; |
809 | int error; |
810 | struct tomoyo_path_info buf; |
811 | bool is_enforce; |
812 | struct tomoyo_path_info symlink_target; |
813 | int idx; |
814 | |
815 | if (tomoyo_init_request_info(r: &r, NULL, index: tomoyo_p2mac[operation]) |
816 | == TOMOYO_CONFIG_DISABLED) |
817 | return 0; |
818 | is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING); |
819 | error = -ENOMEM; |
820 | buf.name = NULL; |
821 | idx = tomoyo_read_lock(); |
822 | if (!tomoyo_get_realpath(buf: &buf, path)) |
823 | goto out; |
824 | r.obj = &obj; |
825 | switch (operation) { |
826 | case TOMOYO_TYPE_RMDIR: |
827 | case TOMOYO_TYPE_CHROOT: |
828 | tomoyo_add_slash(buf: &buf); |
829 | break; |
830 | case TOMOYO_TYPE_SYMLINK: |
831 | symlink_target.name = tomoyo_encode(str: target); |
832 | if (!symlink_target.name) |
833 | goto out; |
834 | tomoyo_fill_path_info(ptr: &symlink_target); |
835 | obj.symlink_target = &symlink_target; |
836 | break; |
837 | } |
838 | error = tomoyo_path_permission(r: &r, operation, filename: &buf); |
839 | if (operation == TOMOYO_TYPE_SYMLINK) |
840 | kfree(objp: symlink_target.name); |
841 | out: |
842 | kfree(objp: buf.name); |
843 | tomoyo_read_unlock(idx); |
844 | if (!is_enforce) |
845 | error = 0; |
846 | return error; |
847 | } |
848 | |
849 | /** |
850 | * tomoyo_mkdev_perm - Check permission for "mkblock" and "mkchar". |
851 | * |
852 | * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK) |
853 | * @path: Pointer to "struct path". |
854 | * @mode: Create mode. |
855 | * @dev: Device number. |
856 | * |
857 | * Returns 0 on success, negative value otherwise. |
858 | */ |
859 | int tomoyo_mkdev_perm(const u8 operation, const struct path *path, |
860 | const unsigned int mode, unsigned int dev) |
861 | { |
862 | struct tomoyo_request_info r; |
863 | struct tomoyo_obj_info obj = { |
864 | .path1 = { .mnt = path->mnt, .dentry = path->dentry }, |
865 | }; |
866 | int error = -ENOMEM; |
867 | struct tomoyo_path_info buf; |
868 | int idx; |
869 | |
870 | if (tomoyo_init_request_info(r: &r, NULL, index: tomoyo_pnnn2mac[operation]) |
871 | == TOMOYO_CONFIG_DISABLED) |
872 | return 0; |
873 | idx = tomoyo_read_lock(); |
874 | error = -ENOMEM; |
875 | if (tomoyo_get_realpath(buf: &buf, path)) { |
876 | r.obj = &obj; |
877 | dev = new_decode_dev(dev); |
878 | r.param_type = TOMOYO_TYPE_MKDEV_ACL; |
879 | r.param.mkdev.filename = &buf; |
880 | r.param.mkdev.operation = operation; |
881 | r.param.mkdev.mode = mode; |
882 | r.param.mkdev.major = MAJOR(dev); |
883 | r.param.mkdev.minor = MINOR(dev); |
884 | tomoyo_check_acl(r: &r, check_entry: tomoyo_check_mkdev_acl); |
885 | error = tomoyo_audit_mkdev_log(r: &r); |
886 | kfree(objp: buf.name); |
887 | } |
888 | tomoyo_read_unlock(idx); |
889 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
890 | error = 0; |
891 | return error; |
892 | } |
893 | |
894 | /** |
895 | * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root". |
896 | * |
897 | * @operation: Type of operation. |
898 | * @path1: Pointer to "struct path". |
899 | * @path2: Pointer to "struct path". |
900 | * |
901 | * Returns 0 on success, negative value otherwise. |
902 | */ |
903 | int tomoyo_path2_perm(const u8 operation, const struct path *path1, |
904 | const struct path *path2) |
905 | { |
906 | int error = -ENOMEM; |
907 | struct tomoyo_path_info buf1; |
908 | struct tomoyo_path_info buf2; |
909 | struct tomoyo_request_info r; |
910 | struct tomoyo_obj_info obj = { |
911 | .path1 = { .mnt = path1->mnt, .dentry = path1->dentry }, |
912 | .path2 = { .mnt = path2->mnt, .dentry = path2->dentry } |
913 | }; |
914 | int idx; |
915 | |
916 | if (tomoyo_init_request_info(r: &r, NULL, index: tomoyo_pp2mac[operation]) |
917 | == TOMOYO_CONFIG_DISABLED) |
918 | return 0; |
919 | buf1.name = NULL; |
920 | buf2.name = NULL; |
921 | idx = tomoyo_read_lock(); |
922 | if (!tomoyo_get_realpath(buf: &buf1, path: path1) || |
923 | !tomoyo_get_realpath(buf: &buf2, path: path2)) |
924 | goto out; |
925 | switch (operation) { |
926 | case TOMOYO_TYPE_RENAME: |
927 | case TOMOYO_TYPE_LINK: |
928 | if (!d_is_dir(dentry: path1->dentry)) |
929 | break; |
930 | fallthrough; |
931 | case TOMOYO_TYPE_PIVOT_ROOT: |
932 | tomoyo_add_slash(buf: &buf1); |
933 | tomoyo_add_slash(buf: &buf2); |
934 | break; |
935 | } |
936 | r.obj = &obj; |
937 | r.param_type = TOMOYO_TYPE_PATH2_ACL; |
938 | r.param.path2.operation = operation; |
939 | r.param.path2.filename1 = &buf1; |
940 | r.param.path2.filename2 = &buf2; |
941 | do { |
942 | tomoyo_check_acl(r: &r, check_entry: tomoyo_check_path2_acl); |
943 | error = tomoyo_audit_path2_log(r: &r); |
944 | } while (error == TOMOYO_RETRY_REQUEST); |
945 | out: |
946 | kfree(objp: buf1.name); |
947 | kfree(objp: buf2.name); |
948 | tomoyo_read_unlock(idx); |
949 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
950 | error = 0; |
951 | return error; |
952 | } |
953 | |
954 | /** |
955 | * tomoyo_same_mount_acl - Check for duplicated "struct tomoyo_mount_acl" entry. |
956 | * |
957 | * @a: Pointer to "struct tomoyo_acl_info". |
958 | * @b: Pointer to "struct tomoyo_acl_info". |
959 | * |
960 | * Returns true if @a == @b, false otherwise. |
961 | */ |
962 | static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, |
963 | const struct tomoyo_acl_info *b) |
964 | { |
965 | const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); |
966 | const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); |
967 | |
968 | return tomoyo_same_name_union(a: &p1->dev_name, b: &p2->dev_name) && |
969 | tomoyo_same_name_union(a: &p1->dir_name, b: &p2->dir_name) && |
970 | tomoyo_same_name_union(a: &p1->fs_type, b: &p2->fs_type) && |
971 | tomoyo_same_number_union(a: &p1->flags, b: &p2->flags); |
972 | } |
973 | |
974 | /** |
975 | * tomoyo_update_mount_acl - Write "struct tomoyo_mount_acl" list. |
976 | * |
977 | * @param: Pointer to "struct tomoyo_acl_param". |
978 | * |
979 | * Returns 0 on success, negative value otherwise. |
980 | * |
981 | * Caller holds tomoyo_read_lock(). |
982 | */ |
983 | static int tomoyo_update_mount_acl(struct tomoyo_acl_param *param) |
984 | { |
985 | struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; |
986 | int error; |
987 | |
988 | if (!tomoyo_parse_name_union(param, ptr: &e.dev_name) || |
989 | !tomoyo_parse_name_union(param, ptr: &e.dir_name) || |
990 | !tomoyo_parse_name_union(param, ptr: &e.fs_type) || |
991 | !tomoyo_parse_number_union(param, ptr: &e.flags)) |
992 | error = -EINVAL; |
993 | else |
994 | error = tomoyo_update_domain(new_entry: &e.head, size: sizeof(e), param, |
995 | check_duplicate: tomoyo_same_mount_acl, NULL); |
996 | tomoyo_put_name_union(ptr: &e.dev_name); |
997 | tomoyo_put_name_union(ptr: &e.dir_name); |
998 | tomoyo_put_name_union(ptr: &e.fs_type); |
999 | tomoyo_put_number_union(ptr: &e.flags); |
1000 | return error; |
1001 | } |
1002 | |
1003 | /** |
1004 | * tomoyo_write_file - Update file related list. |
1005 | * |
1006 | * @param: Pointer to "struct tomoyo_acl_param". |
1007 | * |
1008 | * Returns 0 on success, negative value otherwise. |
1009 | * |
1010 | * Caller holds tomoyo_read_lock(). |
1011 | */ |
1012 | int tomoyo_write_file(struct tomoyo_acl_param *param) |
1013 | { |
1014 | u16 perm = 0; |
1015 | u8 type; |
1016 | const char *operation = tomoyo_read_token(param); |
1017 | |
1018 | for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) |
1019 | if (tomoyo_permstr(string: operation, keyword: tomoyo_path_keyword[type])) |
1020 | perm |= 1 << type; |
1021 | if (perm) |
1022 | return tomoyo_update_path_acl(perm, param); |
1023 | for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) |
1024 | if (tomoyo_permstr(string: operation, |
1025 | keyword: tomoyo_mac_keywords[tomoyo_pp2mac[type]])) |
1026 | perm |= 1 << type; |
1027 | if (perm) |
1028 | return tomoyo_update_path2_acl(perm, param); |
1029 | for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) |
1030 | if (tomoyo_permstr(string: operation, |
1031 | keyword: tomoyo_mac_keywords[tomoyo_pn2mac[type]])) |
1032 | perm |= 1 << type; |
1033 | if (perm) |
1034 | return tomoyo_update_path_number_acl(perm, param); |
1035 | for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) |
1036 | if (tomoyo_permstr(string: operation, |
1037 | keyword: tomoyo_mac_keywords[tomoyo_pnnn2mac[type]])) |
1038 | perm |= 1 << type; |
1039 | if (perm) |
1040 | return tomoyo_update_mkdev_acl(perm, param); |
1041 | if (tomoyo_permstr(string: operation, |
1042 | keyword: tomoyo_mac_keywords[TOMOYO_MAC_FILE_MOUNT])) |
1043 | return tomoyo_update_mount_acl(param); |
1044 | return -EINVAL; |
1045 | } |
1046 | |