1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * AppArmor security module |
4 | * |
5 | * This file contains AppArmor mediation of files |
6 | * |
7 | * Copyright (C) 1998-2008 Novell/SUSE |
8 | * Copyright 2009-2010 Canonical Ltd. |
9 | */ |
10 | |
11 | #include <linux/tty.h> |
12 | #include <linux/fdtable.h> |
13 | #include <linux/file.h> |
14 | #include <linux/fs.h> |
15 | #include <linux/mount.h> |
16 | |
17 | #include "include/apparmor.h" |
18 | #include "include/audit.h" |
19 | #include "include/cred.h" |
20 | #include "include/file.h" |
21 | #include "include/match.h" |
22 | #include "include/net.h" |
23 | #include "include/path.h" |
24 | #include "include/policy.h" |
25 | #include "include/label.h" |
26 | |
27 | static u32 map_mask_to_chr_mask(u32 mask) |
28 | { |
29 | u32 m = mask & PERMS_CHRS_MASK; |
30 | |
31 | if (mask & AA_MAY_GETATTR) |
32 | m |= MAY_READ; |
33 | if (mask & (AA_MAY_SETATTR | AA_MAY_CHMOD | AA_MAY_CHOWN)) |
34 | m |= MAY_WRITE; |
35 | |
36 | return m; |
37 | } |
38 | |
39 | /** |
40 | * file_audit_cb - call back for file specific audit fields |
41 | * @ab: audit_buffer (NOT NULL) |
42 | * @va: audit struct to audit values of (NOT NULL) |
43 | */ |
44 | static void file_audit_cb(struct audit_buffer *ab, void *va) |
45 | { |
46 | struct common_audit_data *sa = va; |
47 | struct apparmor_audit_data *ad = aad(sa); |
48 | kuid_t fsuid = ad->subj_cred ? ad->subj_cred->fsuid : current_fsuid(); |
49 | char str[10]; |
50 | |
51 | if (ad->request & AA_AUDIT_FILE_MASK) { |
52 | aa_perm_mask_to_str(str, str_size: sizeof(str), chrs: aa_file_perm_chrs, |
53 | mask: map_mask_to_chr_mask(mask: ad->request)); |
54 | audit_log_format(ab, fmt: " requested_mask=\"%s\"" , str); |
55 | } |
56 | if (ad->denied & AA_AUDIT_FILE_MASK) { |
57 | aa_perm_mask_to_str(str, str_size: sizeof(str), chrs: aa_file_perm_chrs, |
58 | mask: map_mask_to_chr_mask(mask: ad->denied)); |
59 | audit_log_format(ab, fmt: " denied_mask=\"%s\"" , str); |
60 | } |
61 | if (ad->request & AA_AUDIT_FILE_MASK) { |
62 | audit_log_format(ab, fmt: " fsuid=%d" , |
63 | from_kuid(to: &init_user_ns, uid: fsuid)); |
64 | audit_log_format(ab, fmt: " ouid=%d" , |
65 | from_kuid(to: &init_user_ns, uid: ad->fs.ouid)); |
66 | } |
67 | |
68 | if (ad->peer) { |
69 | audit_log_format(ab, fmt: " target=" ); |
70 | aa_label_xaudit(ab, labels_ns(ad->subj_label), label: ad->peer, |
71 | FLAG_VIEW_SUBNS, GFP_KERNEL); |
72 | } else if (ad->fs.target) { |
73 | audit_log_format(ab, fmt: " target=" ); |
74 | audit_log_untrustedstring(ab, string: ad->fs.target); |
75 | } |
76 | } |
77 | |
78 | /** |
79 | * aa_audit_file - handle the auditing of file operations |
80 | * @subj_cred: cred of the subject |
81 | * @profile: the profile being enforced (NOT NULL) |
82 | * @perms: the permissions computed for the request (NOT NULL) |
83 | * @op: operation being mediated |
84 | * @request: permissions requested |
85 | * @name: name of object being mediated (MAYBE NULL) |
86 | * @target: name of target (MAYBE NULL) |
87 | * @tlabel: target label (MAY BE NULL) |
88 | * @ouid: object uid |
89 | * @info: extra information message (MAYBE NULL) |
90 | * @error: 0 if operation allowed else failure error code |
91 | * |
92 | * Returns: %0 or error on failure |
93 | */ |
94 | int aa_audit_file(const struct cred *subj_cred, |
95 | struct aa_profile *profile, struct aa_perms *perms, |
96 | const char *op, u32 request, const char *name, |
97 | const char *target, struct aa_label *tlabel, |
98 | kuid_t ouid, const char *info, int error) |
99 | { |
100 | int type = AUDIT_APPARMOR_AUTO; |
101 | DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_TASK, AA_CLASS_FILE, op); |
102 | |
103 | ad.subj_cred = subj_cred; |
104 | ad.request = request; |
105 | ad.name = name; |
106 | ad.fs.target = target; |
107 | ad.peer = tlabel; |
108 | ad.fs.ouid = ouid; |
109 | ad.info = info; |
110 | ad.error = error; |
111 | ad.common.u.tsk = NULL; |
112 | |
113 | if (likely(!ad.error)) { |
114 | u32 mask = perms->audit; |
115 | |
116 | if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) |
117 | mask = 0xffff; |
118 | |
119 | /* mask off perms that are not being force audited */ |
120 | ad.request &= mask; |
121 | |
122 | if (likely(!ad.request)) |
123 | return 0; |
124 | type = AUDIT_APPARMOR_AUDIT; |
125 | } else { |
126 | /* only report permissions that were denied */ |
127 | ad.request = ad.request & ~perms->allow; |
128 | AA_BUG(!ad.request); |
129 | |
130 | if (ad.request & perms->kill) |
131 | type = AUDIT_APPARMOR_KILL; |
132 | |
133 | /* quiet known rejects, assumes quiet and kill do not overlap */ |
134 | if ((ad.request & perms->quiet) && |
135 | AUDIT_MODE(profile) != AUDIT_NOQUIET && |
136 | AUDIT_MODE(profile) != AUDIT_ALL) |
137 | ad.request &= ~perms->quiet; |
138 | |
139 | if (!ad.request) |
140 | return ad.error; |
141 | } |
142 | |
143 | ad.denied = ad.request & ~perms->allow; |
144 | return aa_audit(type, profile, ad: &ad, cb: file_audit_cb); |
145 | } |
146 | |
147 | /** |
148 | * is_deleted - test if a file has been completely unlinked |
149 | * @dentry: dentry of file to test for deletion (NOT NULL) |
150 | * |
151 | * Returns: true if deleted else false |
152 | */ |
153 | static inline bool is_deleted(struct dentry *dentry) |
154 | { |
155 | if (d_unlinked(dentry) && d_backing_inode(upper: dentry)->i_nlink == 0) |
156 | return true; |
157 | return false; |
158 | } |
159 | |
160 | static int path_name(const char *op, const struct cred *subj_cred, |
161 | struct aa_label *label, |
162 | const struct path *path, int flags, char *buffer, |
163 | const char **name, struct path_cond *cond, u32 request) |
164 | { |
165 | struct aa_profile *profile; |
166 | const char *info = NULL; |
167 | int error; |
168 | |
169 | error = aa_path_name(path, flags, buffer, name, info: &info, |
170 | labels_profile(label)->disconnected); |
171 | if (error) { |
172 | fn_for_each_confined(label, profile, |
173 | aa_audit_file(subj_cred, |
174 | profile, &nullperms, op, request, *name, |
175 | NULL, NULL, cond->uid, info, error)); |
176 | return error; |
177 | } |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | struct aa_perms default_perms = {}; |
183 | /** |
184 | * aa_lookup_fperms - convert dfa compressed perms to internal perms |
185 | * @file_rules: the aa_policydb to lookup perms for (NOT NULL) |
186 | * @state: state in dfa |
187 | * @cond: conditions to consider (NOT NULL) |
188 | * |
189 | * TODO: convert from dfa + state to permission entry |
190 | * |
191 | * Returns: a pointer to a file permission set |
192 | */ |
193 | struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules, |
194 | aa_state_t state, struct path_cond *cond) |
195 | { |
196 | unsigned int index = ACCEPT_TABLE(file_rules->dfa)[state]; |
197 | |
198 | if (!(file_rules->perms)) |
199 | return &default_perms; |
200 | |
201 | if (uid_eq(current_fsuid(), right: cond->uid)) |
202 | return &(file_rules->perms[index]); |
203 | |
204 | return &(file_rules->perms[index + 1]); |
205 | } |
206 | |
207 | /** |
208 | * aa_str_perms - find permission that match @name |
209 | * @file_rules: the aa_policydb to match against (NOT NULL) |
210 | * @start: state to start matching in |
211 | * @name: string to match against dfa (NOT NULL) |
212 | * @cond: conditions to consider for permission set computation (NOT NULL) |
213 | * @perms: Returns - the permissions found when matching @name |
214 | * |
215 | * Returns: the final state in @dfa when beginning @start and walking @name |
216 | */ |
217 | aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start, |
218 | const char *name, struct path_cond *cond, |
219 | struct aa_perms *perms) |
220 | { |
221 | aa_state_t state; |
222 | state = aa_dfa_match(dfa: file_rules->dfa, start, str: name); |
223 | *perms = *(aa_lookup_fperms(file_rules, state, cond)); |
224 | |
225 | return state; |
226 | } |
227 | |
228 | static int __aa_path_perm(const char *op, const struct cred *subj_cred, |
229 | struct aa_profile *profile, const char *name, |
230 | u32 request, struct path_cond *cond, int flags, |
231 | struct aa_perms *perms) |
232 | { |
233 | struct aa_ruleset *rules = list_first_entry(&profile->rules, |
234 | typeof(*rules), list); |
235 | int e = 0; |
236 | |
237 | if (profile_unconfined(profile)) |
238 | return 0; |
239 | aa_str_perms(file_rules: rules->file, start: rules->file->start[AA_CLASS_FILE], |
240 | name, cond, perms); |
241 | if (request & ~perms->allow) |
242 | e = -EACCES; |
243 | return aa_audit_file(subj_cred, |
244 | profile, perms, op, request, name, NULL, NULL, |
245 | ouid: cond->uid, NULL, error: e); |
246 | } |
247 | |
248 | |
249 | static int profile_path_perm(const char *op, const struct cred *subj_cred, |
250 | struct aa_profile *profile, |
251 | const struct path *path, char *buffer, u32 request, |
252 | struct path_cond *cond, int flags, |
253 | struct aa_perms *perms) |
254 | { |
255 | const char *name; |
256 | int error; |
257 | |
258 | if (profile_unconfined(profile)) |
259 | return 0; |
260 | |
261 | error = path_name(op, subj_cred, label: &profile->label, path, |
262 | flags: flags | profile->path_flags, buffer, name: &name, cond, |
263 | request); |
264 | if (error) |
265 | return error; |
266 | return __aa_path_perm(op, subj_cred, profile, name, request, cond, |
267 | flags, perms); |
268 | } |
269 | |
270 | /** |
271 | * aa_path_perm - do permissions check & audit for @path |
272 | * @op: operation being checked |
273 | * @subj_cred: subject cred |
274 | * @label: profile being enforced (NOT NULL) |
275 | * @path: path to check permissions of (NOT NULL) |
276 | * @flags: any additional path flags beyond what the profile specifies |
277 | * @request: requested permissions |
278 | * @cond: conditional info for this request (NOT NULL) |
279 | * |
280 | * Returns: %0 else error if access denied or other error |
281 | */ |
282 | int aa_path_perm(const char *op, const struct cred *subj_cred, |
283 | struct aa_label *label, |
284 | const struct path *path, int flags, u32 request, |
285 | struct path_cond *cond) |
286 | { |
287 | struct aa_perms perms = {}; |
288 | struct aa_profile *profile; |
289 | char *buffer = NULL; |
290 | int error; |
291 | |
292 | flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR : |
293 | 0); |
294 | buffer = aa_get_buffer(in_atomic: false); |
295 | if (!buffer) |
296 | return -ENOMEM; |
297 | error = fn_for_each_confined(label, profile, |
298 | profile_path_perm(op, subj_cred, profile, path, buffer, |
299 | request, cond, flags, &perms)); |
300 | |
301 | aa_put_buffer(buf: buffer); |
302 | |
303 | return error; |
304 | } |
305 | |
306 | /** |
307 | * xindex_is_subset - helper for aa_path_link |
308 | * @link: link permission set |
309 | * @target: target permission set |
310 | * |
311 | * test target x permissions are equal OR a subset of link x permissions |
312 | * this is done as part of the subset test, where a hardlink must have |
313 | * a subset of permissions that the target has. |
314 | * |
315 | * Returns: true if subset else false |
316 | */ |
317 | static inline bool xindex_is_subset(u32 link, u32 target) |
318 | { |
319 | if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || |
320 | ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) |
321 | return false; |
322 | |
323 | return true; |
324 | } |
325 | |
326 | static int profile_path_link(const struct cred *subj_cred, |
327 | struct aa_profile *profile, |
328 | const struct path *link, char *buffer, |
329 | const struct path *target, char *buffer2, |
330 | struct path_cond *cond) |
331 | { |
332 | struct aa_ruleset *rules = list_first_entry(&profile->rules, |
333 | typeof(*rules), list); |
334 | const char *lname, *tname = NULL; |
335 | struct aa_perms lperms = {}, perms; |
336 | const char *info = NULL; |
337 | u32 request = AA_MAY_LINK; |
338 | aa_state_t state; |
339 | int error; |
340 | |
341 | error = path_name(OP_LINK, subj_cred, label: &profile->label, path: link, |
342 | flags: profile->path_flags, |
343 | buffer, name: &lname, cond, AA_MAY_LINK); |
344 | if (error) |
345 | goto audit; |
346 | |
347 | /* buffer2 freed below, tname is pointer in buffer2 */ |
348 | error = path_name(OP_LINK, subj_cred, label: &profile->label, path: target, |
349 | flags: profile->path_flags, |
350 | buffer: buffer2, name: &tname, cond, AA_MAY_LINK); |
351 | if (error) |
352 | goto audit; |
353 | |
354 | error = -EACCES; |
355 | /* aa_str_perms - handles the case of the dfa being NULL */ |
356 | state = aa_str_perms(file_rules: rules->file, |
357 | start: rules->file->start[AA_CLASS_FILE], name: lname, |
358 | cond, perms: &lperms); |
359 | |
360 | if (!(lperms.allow & AA_MAY_LINK)) |
361 | goto audit; |
362 | |
363 | /* test to see if target can be paired with link */ |
364 | state = aa_dfa_null_transition(dfa: rules->file->dfa, start: state); |
365 | aa_str_perms(file_rules: rules->file, start: state, name: tname, cond, perms: &perms); |
366 | |
367 | /* force audit/quiet masks for link are stored in the second entry |
368 | * in the link pair. |
369 | */ |
370 | lperms.audit = perms.audit; |
371 | lperms.quiet = perms.quiet; |
372 | lperms.kill = perms.kill; |
373 | |
374 | if (!(perms.allow & AA_MAY_LINK)) { |
375 | info = "target restricted" ; |
376 | lperms = perms; |
377 | goto audit; |
378 | } |
379 | |
380 | /* done if link subset test is not required */ |
381 | if (!(perms.allow & AA_LINK_SUBSET)) |
382 | goto done_tests; |
383 | |
384 | /* Do link perm subset test requiring allowed permission on link are |
385 | * a subset of the allowed permissions on target. |
386 | */ |
387 | aa_str_perms(file_rules: rules->file, start: rules->file->start[AA_CLASS_FILE], |
388 | name: tname, cond, perms: &perms); |
389 | |
390 | /* AA_MAY_LINK is not considered in the subset test */ |
391 | request = lperms.allow & ~AA_MAY_LINK; |
392 | lperms.allow &= perms.allow | AA_MAY_LINK; |
393 | |
394 | request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow); |
395 | if (request & ~lperms.allow) { |
396 | goto audit; |
397 | } else if ((lperms.allow & MAY_EXEC) && |
398 | !xindex_is_subset(link: lperms.xindex, target: perms.xindex)) { |
399 | lperms.allow &= ~MAY_EXEC; |
400 | request |= MAY_EXEC; |
401 | info = "link not subset of target" ; |
402 | goto audit; |
403 | } |
404 | |
405 | done_tests: |
406 | error = 0; |
407 | |
408 | audit: |
409 | return aa_audit_file(subj_cred, |
410 | profile, perms: &lperms, OP_LINK, request, name: lname, target: tname, |
411 | NULL, ouid: cond->uid, info, error); |
412 | } |
413 | |
414 | /** |
415 | * aa_path_link - Handle hard link permission check |
416 | * @subj_cred: subject cred |
417 | * @label: the label being enforced (NOT NULL) |
418 | * @old_dentry: the target dentry (NOT NULL) |
419 | * @new_dir: directory the new link will be created in (NOT NULL) |
420 | * @new_dentry: the link being created (NOT NULL) |
421 | * |
422 | * Handle the permission test for a link & target pair. Permission |
423 | * is encoded as a pair where the link permission is determined |
424 | * first, and if allowed, the target is tested. The target test |
425 | * is done from the point of the link match (not start of DFA) |
426 | * making the target permission dependent on the link permission match. |
427 | * |
428 | * The subset test if required forces that permissions granted |
429 | * on link are a subset of the permission granted to target. |
430 | * |
431 | * Returns: %0 if allowed else error |
432 | */ |
433 | int aa_path_link(const struct cred *subj_cred, |
434 | struct aa_label *label, struct dentry *old_dentry, |
435 | const struct path *new_dir, struct dentry *new_dentry) |
436 | { |
437 | struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry }; |
438 | struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry }; |
439 | struct path_cond cond = { |
440 | d_backing_inode(upper: old_dentry)->i_uid, |
441 | d_backing_inode(upper: old_dentry)->i_mode |
442 | }; |
443 | char *buffer = NULL, *buffer2 = NULL; |
444 | struct aa_profile *profile; |
445 | int error; |
446 | |
447 | /* buffer freed below, lname is pointer in buffer */ |
448 | buffer = aa_get_buffer(in_atomic: false); |
449 | buffer2 = aa_get_buffer(in_atomic: false); |
450 | error = -ENOMEM; |
451 | if (!buffer || !buffer2) |
452 | goto out; |
453 | |
454 | error = fn_for_each_confined(label, profile, |
455 | profile_path_link(subj_cred, profile, &link, buffer, |
456 | &target, buffer2, &cond)); |
457 | out: |
458 | aa_put_buffer(buf: buffer); |
459 | aa_put_buffer(buf: buffer2); |
460 | return error; |
461 | } |
462 | |
463 | static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label, |
464 | u32 request) |
465 | { |
466 | struct aa_label *l, *old; |
467 | |
468 | /* update caching of label on file_ctx */ |
469 | spin_lock(lock: &fctx->lock); |
470 | old = rcu_dereference_protected(fctx->label, |
471 | lockdep_is_held(&fctx->lock)); |
472 | l = aa_label_merge(a: old, b: label, GFP_ATOMIC); |
473 | if (l) { |
474 | if (l != old) { |
475 | rcu_assign_pointer(fctx->label, l); |
476 | aa_put_label(l: old); |
477 | } else |
478 | aa_put_label(l); |
479 | fctx->allow |= request; |
480 | } |
481 | spin_unlock(lock: &fctx->lock); |
482 | } |
483 | |
484 | static int __file_path_perm(const char *op, const struct cred *subj_cred, |
485 | struct aa_label *label, |
486 | struct aa_label *flabel, struct file *file, |
487 | u32 request, u32 denied, bool in_atomic) |
488 | { |
489 | struct aa_profile *profile; |
490 | struct aa_perms perms = {}; |
491 | vfsuid_t vfsuid = i_uid_into_vfsuid(idmap: file_mnt_idmap(file), |
492 | inode: file_inode(f: file)); |
493 | struct path_cond cond = { |
494 | .uid = vfsuid_into_kuid(vfsuid), |
495 | .mode = file_inode(f: file)->i_mode |
496 | }; |
497 | char *buffer; |
498 | int flags, error; |
499 | |
500 | /* revalidation due to label out of date. No revocation at this time */ |
501 | if (!denied && aa_label_is_subset(set: flabel, sub: label)) |
502 | /* TODO: check for revocation on stale profiles */ |
503 | return 0; |
504 | |
505 | flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0); |
506 | buffer = aa_get_buffer(in_atomic); |
507 | if (!buffer) |
508 | return -ENOMEM; |
509 | |
510 | /* check every profile in task label not in current cache */ |
511 | error = fn_for_each_not_in_set(flabel, label, profile, |
512 | profile_path_perm(op, subj_cred, profile, |
513 | &file->f_path, buffer, |
514 | request, &cond, flags, &perms)); |
515 | if (denied && !error) { |
516 | /* |
517 | * check every profile in file label that was not tested |
518 | * in the initial check above. |
519 | * |
520 | * TODO: cache full perms so this only happens because of |
521 | * conditionals |
522 | * TODO: don't audit here |
523 | */ |
524 | if (label == flabel) |
525 | error = fn_for_each(label, profile, |
526 | profile_path_perm(op, subj_cred, |
527 | profile, &file->f_path, |
528 | buffer, request, &cond, flags, |
529 | &perms)); |
530 | else |
531 | error = fn_for_each_not_in_set(label, flabel, profile, |
532 | profile_path_perm(op, subj_cred, |
533 | profile, &file->f_path, |
534 | buffer, request, &cond, flags, |
535 | &perms)); |
536 | } |
537 | if (!error) |
538 | update_file_ctx(fctx: file_ctx(file), label, request); |
539 | |
540 | aa_put_buffer(buf: buffer); |
541 | |
542 | return error; |
543 | } |
544 | |
545 | static int __file_sock_perm(const char *op, const struct cred *subj_cred, |
546 | struct aa_label *label, |
547 | struct aa_label *flabel, struct file *file, |
548 | u32 request, u32 denied) |
549 | { |
550 | struct socket *sock = (struct socket *) file->private_data; |
551 | int error; |
552 | |
553 | AA_BUG(!sock); |
554 | |
555 | /* revalidation due to label out of date. No revocation at this time */ |
556 | if (!denied && aa_label_is_subset(set: flabel, sub: label)) |
557 | return 0; |
558 | |
559 | /* TODO: improve to skip profiles cached in flabel */ |
560 | error = aa_sock_file_perm(subj_cred, label, op, request, sock); |
561 | if (denied) { |
562 | /* TODO: improve to skip profiles checked above */ |
563 | /* check every profile in file label to is cached */ |
564 | last_error(error, aa_sock_file_perm(subj_cred, flabel, op, |
565 | request, sock)); |
566 | } |
567 | if (!error) |
568 | update_file_ctx(fctx: file_ctx(file), label, request); |
569 | |
570 | return error; |
571 | } |
572 | |
573 | /** |
574 | * aa_file_perm - do permission revalidation check & audit for @file |
575 | * @op: operation being checked |
576 | * @subj_cred: subject cred |
577 | * @label: label being enforced (NOT NULL) |
578 | * @file: file to revalidate access permissions on (NOT NULL) |
579 | * @request: requested permissions |
580 | * @in_atomic: whether allocations need to be done in atomic context |
581 | * |
582 | * Returns: %0 if access allowed else error |
583 | */ |
584 | int aa_file_perm(const char *op, const struct cred *subj_cred, |
585 | struct aa_label *label, struct file *file, |
586 | u32 request, bool in_atomic) |
587 | { |
588 | struct aa_file_ctx *fctx; |
589 | struct aa_label *flabel; |
590 | u32 denied; |
591 | int error = 0; |
592 | |
593 | AA_BUG(!label); |
594 | AA_BUG(!file); |
595 | |
596 | fctx = file_ctx(file); |
597 | |
598 | rcu_read_lock(); |
599 | flabel = rcu_dereference(fctx->label); |
600 | AA_BUG(!flabel); |
601 | |
602 | /* revalidate access, if task is unconfined, or the cached cred |
603 | * doesn't match or if the request is for more permissions than |
604 | * was granted. |
605 | * |
606 | * Note: the test for !unconfined(flabel) is to handle file |
607 | * delegation from unconfined tasks |
608 | */ |
609 | denied = request & ~fctx->allow; |
610 | if (unconfined(label) || unconfined(flabel) || |
611 | (!denied && aa_label_is_subset(set: flabel, sub: label))) { |
612 | rcu_read_unlock(); |
613 | goto done; |
614 | } |
615 | |
616 | flabel = aa_get_newest_label(l: flabel); |
617 | rcu_read_unlock(); |
618 | /* TODO: label cross check */ |
619 | |
620 | if (file->f_path.mnt && path_mediated_fs(dentry: file->f_path.dentry)) |
621 | error = __file_path_perm(op, subj_cred, label, flabel, file, |
622 | request, denied, in_atomic); |
623 | |
624 | else if (S_ISSOCK(file_inode(file)->i_mode)) |
625 | error = __file_sock_perm(op, subj_cred, label, flabel, file, |
626 | request, denied); |
627 | aa_put_label(l: flabel); |
628 | |
629 | done: |
630 | return error; |
631 | } |
632 | |
633 | static void revalidate_tty(const struct cred *subj_cred, struct aa_label *label) |
634 | { |
635 | struct tty_struct *tty; |
636 | int drop_tty = 0; |
637 | |
638 | tty = get_current_tty(); |
639 | if (!tty) |
640 | return; |
641 | |
642 | spin_lock(lock: &tty->files_lock); |
643 | if (!list_empty(head: &tty->tty_files)) { |
644 | struct tty_file_private *file_priv; |
645 | struct file *file; |
646 | /* TODO: Revalidate access to controlling tty. */ |
647 | file_priv = list_first_entry(&tty->tty_files, |
648 | struct tty_file_private, list); |
649 | file = file_priv->file; |
650 | |
651 | if (aa_file_perm(OP_INHERIT, subj_cred, label, file, |
652 | MAY_READ | MAY_WRITE, IN_ATOMIC)) |
653 | drop_tty = 1; |
654 | } |
655 | spin_unlock(lock: &tty->files_lock); |
656 | tty_kref_put(tty); |
657 | |
658 | if (drop_tty) |
659 | no_tty(); |
660 | } |
661 | |
662 | struct cred_label { |
663 | const struct cred *cred; |
664 | struct aa_label *label; |
665 | }; |
666 | |
667 | static int match_file(const void *p, struct file *file, unsigned int fd) |
668 | { |
669 | struct cred_label *cl = (struct cred_label *)p; |
670 | |
671 | if (aa_file_perm(OP_INHERIT, subj_cred: cl->cred, label: cl->label, file, |
672 | request: aa_map_file_to_perms(file), IN_ATOMIC)) |
673 | return fd + 1; |
674 | return 0; |
675 | } |
676 | |
677 | |
678 | /* based on selinux's flush_unauthorized_files */ |
679 | void aa_inherit_files(const struct cred *cred, struct files_struct *files) |
680 | { |
681 | struct aa_label *label = aa_get_newest_cred_label(cred); |
682 | struct cred_label cl = { |
683 | .cred = cred, |
684 | .label = label, |
685 | }; |
686 | struct file *devnull = NULL; |
687 | unsigned int n; |
688 | |
689 | revalidate_tty(subj_cred: cred, label); |
690 | |
691 | /* Revalidate access to inherited open files. */ |
692 | n = iterate_fd(files, 0, match_file, &cl); |
693 | if (!n) /* none found? */ |
694 | goto out; |
695 | |
696 | devnull = dentry_open(path: &aa_null, O_RDWR, creds: cred); |
697 | if (IS_ERR(ptr: devnull)) |
698 | devnull = NULL; |
699 | /* replace all the matching ones with this */ |
700 | do { |
701 | replace_fd(fd: n - 1, file: devnull, flags: 0); |
702 | } while ((n = iterate_fd(files, n, match_file, &cl)) != 0); |
703 | if (devnull) |
704 | fput(devnull); |
705 | out: |
706 | aa_put_label(l: label); |
707 | } |
708 | |