.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * AppArmor security module |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * |
---|
6 | 7 | * Copyright (C) 1998-2008 Novell/SUSE |
---|
7 | 8 | * Copyright 2009-2010 Canonical Ltd. |
---|
8 | | - * |
---|
9 | | - * This program is free software; you can redistribute it and/or |
---|
10 | | - * modify it under the terms of the GNU General Public License as |
---|
11 | | - * published by the Free Software Foundation, version 2 of the |
---|
12 | | - * License. |
---|
13 | 9 | */ |
---|
14 | 10 | |
---|
15 | 11 | #include <linux/tty.h> |
---|
.. | .. |
---|
39 | 35 | } |
---|
40 | 36 | |
---|
41 | 37 | /** |
---|
42 | | - * audit_file_mask - convert mask to permission string |
---|
43 | | - * @buffer: buffer to write string to (NOT NULL) |
---|
44 | | - * @mask: permission mask to convert |
---|
45 | | - */ |
---|
46 | | -static void audit_file_mask(struct audit_buffer *ab, u32 mask) |
---|
47 | | -{ |
---|
48 | | - char str[10]; |
---|
49 | | - |
---|
50 | | - aa_perm_mask_to_str(str, sizeof(str), aa_file_perm_chrs, |
---|
51 | | - map_mask_to_chr_mask(mask)); |
---|
52 | | - audit_log_string(ab, str); |
---|
53 | | -} |
---|
54 | | - |
---|
55 | | -/** |
---|
56 | 38 | * file_audit_cb - call back for file specific audit fields |
---|
57 | 39 | * @ab: audit_buffer (NOT NULL) |
---|
58 | 40 | * @va: audit struct to audit values of (NOT NULL) |
---|
.. | .. |
---|
61 | 43 | { |
---|
62 | 44 | struct common_audit_data *sa = va; |
---|
63 | 45 | kuid_t fsuid = current_fsuid(); |
---|
| 46 | + char str[10]; |
---|
64 | 47 | |
---|
65 | 48 | if (aad(sa)->request & AA_AUDIT_FILE_MASK) { |
---|
66 | | - audit_log_format(ab, " requested_mask="); |
---|
67 | | - audit_file_mask(ab, aad(sa)->request); |
---|
| 49 | + aa_perm_mask_to_str(str, sizeof(str), aa_file_perm_chrs, |
---|
| 50 | + map_mask_to_chr_mask(aad(sa)->request)); |
---|
| 51 | + audit_log_format(ab, " requested_mask=\"%s\"", str); |
---|
68 | 52 | } |
---|
69 | 53 | if (aad(sa)->denied & AA_AUDIT_FILE_MASK) { |
---|
70 | | - audit_log_format(ab, " denied_mask="); |
---|
71 | | - audit_file_mask(ab, aad(sa)->denied); |
---|
| 54 | + aa_perm_mask_to_str(str, sizeof(str), aa_file_perm_chrs, |
---|
| 55 | + map_mask_to_chr_mask(aad(sa)->denied)); |
---|
| 56 | + audit_log_format(ab, " denied_mask=\"%s\"", str); |
---|
72 | 57 | } |
---|
73 | 58 | if (aad(sa)->request & AA_AUDIT_FILE_MASK) { |
---|
74 | 59 | audit_log_format(ab, " fsuid=%d", |
---|
.. | .. |
---|
80 | 65 | if (aad(sa)->peer) { |
---|
81 | 66 | audit_log_format(ab, " target="); |
---|
82 | 67 | aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, |
---|
83 | | - FLAG_VIEW_SUBNS, GFP_ATOMIC); |
---|
| 68 | + FLAG_VIEW_SUBNS, GFP_KERNEL); |
---|
84 | 69 | } else if (aad(sa)->fs.target) { |
---|
85 | 70 | audit_log_format(ab, " target="); |
---|
86 | 71 | audit_log_untrustedstring(ab, aad(sa)->fs.target); |
---|
.. | .. |
---|
158 | 143 | * is_deleted - test if a file has been completely unlinked |
---|
159 | 144 | * @dentry: dentry of file to test for deletion (NOT NULL) |
---|
160 | 145 | * |
---|
161 | | - * Returns: %1 if deleted else %0 |
---|
| 146 | + * Returns: true if deleted else false |
---|
162 | 147 | */ |
---|
163 | 148 | static inline bool is_deleted(struct dentry *dentry) |
---|
164 | 149 | { |
---|
165 | 150 | if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0) |
---|
166 | | - return 1; |
---|
167 | | - return 0; |
---|
| 151 | + return true; |
---|
| 152 | + return false; |
---|
168 | 153 | } |
---|
169 | 154 | |
---|
170 | 155 | static int path_name(const char *op, struct aa_label *label, |
---|
.. | .. |
---|
336 | 321 | |
---|
337 | 322 | flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR : |
---|
338 | 323 | 0); |
---|
339 | | - get_buffers(buffer); |
---|
| 324 | + buffer = aa_get_buffer(false); |
---|
| 325 | + if (!buffer) |
---|
| 326 | + return -ENOMEM; |
---|
340 | 327 | error = fn_for_each_confined(label, profile, |
---|
341 | 328 | profile_path_perm(op, profile, path, buffer, request, |
---|
342 | 329 | cond, flags, &perms)); |
---|
343 | 330 | |
---|
344 | | - put_buffers(buffer); |
---|
| 331 | + aa_put_buffer(buffer); |
---|
345 | 332 | |
---|
346 | 333 | return error; |
---|
347 | 334 | } |
---|
.. | .. |
---|
355 | 342 | * this is done as part of the subset test, where a hardlink must have |
---|
356 | 343 | * a subset of permissions that the target has. |
---|
357 | 344 | * |
---|
358 | | - * Returns: %1 if subset else %0 |
---|
| 345 | + * Returns: true if subset else false |
---|
359 | 346 | */ |
---|
360 | 347 | static inline bool xindex_is_subset(u32 link, u32 target) |
---|
361 | 348 | { |
---|
362 | 349 | if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || |
---|
363 | 350 | ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) |
---|
364 | | - return 0; |
---|
| 351 | + return false; |
---|
365 | 352 | |
---|
366 | | - return 1; |
---|
| 353 | + return true; |
---|
367 | 354 | } |
---|
368 | 355 | |
---|
369 | 356 | static int profile_path_link(struct aa_profile *profile, |
---|
.. | .. |
---|
479 | 466 | int error; |
---|
480 | 467 | |
---|
481 | 468 | /* buffer freed below, lname is pointer in buffer */ |
---|
482 | | - get_buffers(buffer, buffer2); |
---|
| 469 | + buffer = aa_get_buffer(false); |
---|
| 470 | + buffer2 = aa_get_buffer(false); |
---|
| 471 | + error = -ENOMEM; |
---|
| 472 | + if (!buffer || !buffer2) |
---|
| 473 | + goto out; |
---|
| 474 | + |
---|
483 | 475 | error = fn_for_each_confined(label, profile, |
---|
484 | 476 | profile_path_link(profile, &link, buffer, &target, |
---|
485 | 477 | buffer2, &cond)); |
---|
486 | | - put_buffers(buffer, buffer2); |
---|
487 | | - |
---|
| 478 | +out: |
---|
| 479 | + aa_put_buffer(buffer); |
---|
| 480 | + aa_put_buffer(buffer2); |
---|
488 | 481 | return error; |
---|
489 | 482 | } |
---|
490 | 483 | |
---|
.. | .. |
---|
496 | 489 | /* update caching of label on file_ctx */ |
---|
497 | 490 | spin_lock(&fctx->lock); |
---|
498 | 491 | old = rcu_dereference_protected(fctx->label, |
---|
499 | | - spin_is_locked(&fctx->lock)); |
---|
| 492 | + lockdep_is_held(&fctx->lock)); |
---|
500 | 493 | l = aa_label_merge(old, label, GFP_ATOMIC); |
---|
501 | 494 | if (l) { |
---|
502 | 495 | if (l != old) { |
---|
.. | .. |
---|
511 | 504 | |
---|
512 | 505 | static int __file_path_perm(const char *op, struct aa_label *label, |
---|
513 | 506 | struct aa_label *flabel, struct file *file, |
---|
514 | | - u32 request, u32 denied) |
---|
| 507 | + u32 request, u32 denied, bool in_atomic) |
---|
515 | 508 | { |
---|
516 | 509 | struct aa_profile *profile; |
---|
517 | 510 | struct aa_perms perms = {}; |
---|
.. | .. |
---|
528 | 521 | return 0; |
---|
529 | 522 | |
---|
530 | 523 | flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0); |
---|
531 | | - get_buffers(buffer); |
---|
| 524 | + buffer = aa_get_buffer(in_atomic); |
---|
| 525 | + if (!buffer) |
---|
| 526 | + return -ENOMEM; |
---|
532 | 527 | |
---|
533 | 528 | /* check every profile in task label not in current cache */ |
---|
534 | 529 | error = fn_for_each_not_in_set(flabel, label, profile, |
---|
.. | .. |
---|
557 | 552 | if (!error) |
---|
558 | 553 | update_file_ctx(file_ctx(file), label, request); |
---|
559 | 554 | |
---|
560 | | - put_buffers(buffer); |
---|
| 555 | + aa_put_buffer(buffer); |
---|
561 | 556 | |
---|
562 | 557 | return error; |
---|
563 | 558 | } |
---|
.. | .. |
---|
594 | 589 | * @label: label being enforced (NOT NULL) |
---|
595 | 590 | * @file: file to revalidate access permissions on (NOT NULL) |
---|
596 | 591 | * @request: requested permissions |
---|
| 592 | + * @in_atomic: whether allocations need to be done in atomic context |
---|
597 | 593 | * |
---|
598 | 594 | * Returns: %0 if access allowed else error |
---|
599 | 595 | */ |
---|
600 | 596 | int aa_file_perm(const char *op, struct aa_label *label, struct file *file, |
---|
601 | | - u32 request) |
---|
| 597 | + u32 request, bool in_atomic) |
---|
602 | 598 | { |
---|
603 | 599 | struct aa_file_ctx *fctx; |
---|
604 | 600 | struct aa_label *flabel; |
---|
.. | .. |
---|
623 | 619 | */ |
---|
624 | 620 | denied = request & ~fctx->allow; |
---|
625 | 621 | if (unconfined(label) || unconfined(flabel) || |
---|
626 | | - (!denied && aa_label_is_subset(flabel, label))) |
---|
| 622 | + (!denied && aa_label_is_subset(flabel, label))) { |
---|
| 623 | + rcu_read_unlock(); |
---|
627 | 624 | goto done; |
---|
| 625 | + } |
---|
628 | 626 | |
---|
| 627 | + flabel = aa_get_newest_label(flabel); |
---|
| 628 | + rcu_read_unlock(); |
---|
629 | 629 | /* TODO: label cross check */ |
---|
630 | 630 | |
---|
631 | 631 | if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry)) |
---|
632 | 632 | error = __file_path_perm(op, label, flabel, file, request, |
---|
633 | | - denied); |
---|
| 633 | + denied, in_atomic); |
---|
634 | 634 | |
---|
635 | 635 | else if (S_ISSOCK(file_inode(file)->i_mode)) |
---|
636 | 636 | error = __file_sock_perm(op, label, flabel, file, request, |
---|
637 | 637 | denied); |
---|
638 | | -done: |
---|
639 | | - rcu_read_unlock(); |
---|
| 638 | + aa_put_label(flabel); |
---|
640 | 639 | |
---|
| 640 | +done: |
---|
641 | 641 | return error; |
---|
642 | 642 | } |
---|
643 | 643 | |
---|
.. | .. |
---|
659 | 659 | struct tty_file_private, list); |
---|
660 | 660 | file = file_priv->file; |
---|
661 | 661 | |
---|
662 | | - if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE)) |
---|
| 662 | + if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE, |
---|
| 663 | + IN_ATOMIC)) |
---|
663 | 664 | drop_tty = 1; |
---|
664 | 665 | } |
---|
665 | 666 | spin_unlock(&tty->files_lock); |
---|
.. | .. |
---|
673 | 674 | { |
---|
674 | 675 | struct aa_label *label = (struct aa_label *)p; |
---|
675 | 676 | |
---|
676 | | - if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file))) |
---|
| 677 | + if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file), |
---|
| 678 | + IN_ATOMIC)) |
---|
677 | 679 | return fd + 1; |
---|
678 | 680 | return 0; |
---|
679 | 681 | } |
---|