| .. | .. | 
|---|
|  | 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 | } | 
|---|