Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

apparmor: add the ability for policy to specify a permission table

Currently permissions are encoded in the dfa accept entries that are
then mapped to an internal permission structure. This limits the
permissions that userspace can specify, so allow userspace to directly
specify the permission table.

Signed-off-by: John Johansen <john.johansen@canonical.com>

+98 -11
+4 -1
security/apparmor/include/policy.h
··· 81 81 */ 82 82 struct aa_policydb { 83 83 struct aa_dfa *dfa; 84 - struct aa_perms *perms; 84 + struct { 85 + struct aa_perms *perms; 86 + u32 size; 87 + }; 85 88 struct aa_str_table trans; 86 89 aa_state_t start[AA_CLASS_LAST + 1]; 87 90 };
+94 -10
security/apparmor/policy_unpack.c
··· 435 435 /** 436 436 * unpack_dfa - unpack a file rule dfa 437 437 * @e: serialized data extent information (NOT NULL) 438 + * @flags: dfa flags to check 438 439 * 439 440 * returns dfa or ERR_PTR or NULL if no dfa 440 441 */ 441 - static struct aa_dfa *unpack_dfa(struct aa_ext *e) 442 + static struct aa_dfa *unpack_dfa(struct aa_ext *e, int flags) 442 443 { 443 444 char *blob = NULL; 444 445 size_t size; ··· 455 454 size_t sz = blob - (char *) e->start - 456 455 ((e->pos - e->start) & 7); 457 456 size_t pad = ALIGN(sz, 8) - sz; 458 - int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | 459 - TO_ACCEPT2_FLAG(YYTD_DATA32); 460 457 if (aa_g_paranoid_load) 461 458 flags |= DFA_FLAG_VERIFY_STATES; 462 459 dfa = aa_dfa_unpack(blob + pad, size - pad, flags); ··· 658 659 return false; 659 660 } 660 661 662 + static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm) 663 + { 664 + bool res; 665 + 666 + if (version != 1) 667 + return false; 668 + 669 + res = unpack_u32(e, &perm->allow, NULL); 670 + res = res && unpack_u32(e, &perm->allow, NULL); 671 + res = res && unpack_u32(e, &perm->deny, NULL); 672 + res = res && unpack_u32(e, &perm->subtree, NULL); 673 + res = res && unpack_u32(e, &perm->cond, NULL); 674 + res = res && unpack_u32(e, &perm->kill, NULL); 675 + res = res && unpack_u32(e, &perm->complain, NULL); 676 + res = res && unpack_u32(e, &perm->prompt, NULL); 677 + res = res && unpack_u32(e, &perm->audit, NULL); 678 + res = res && unpack_u32(e, &perm->quiet, NULL); 679 + res = res && unpack_u32(e, &perm->hide, NULL); 680 + res = res && unpack_u32(e, &perm->xindex, NULL); 681 + res = res && unpack_u32(e, &perm->tag, NULL); 682 + res = res && unpack_u32(e, &perm->label, NULL); 683 + 684 + return res; 685 + } 686 + 687 + static ssize_t unpack_perms_table(struct aa_ext *e, struct aa_perms **perms) 688 + { 689 + void *pos = e->pos; 690 + u16 size = 0; 691 + 692 + AA_BUG(!perms); 693 + /* 694 + * policy perms are optional, in which case perms are embedded 695 + * in the dfa accept table 696 + */ 697 + if (unpack_nameX(e, AA_STRUCT, "perms")) { 698 + int i; 699 + u32 version; 700 + 701 + if (!unpack_u32(e, &version, "version")) 702 + goto fail_reset; 703 + if (unpack_array(e, NULL, &size) != TRI_TRUE) 704 + goto fail_reset; 705 + *perms = kcalloc(size, sizeof(struct aa_perms), GFP_KERNEL); 706 + if (!*perms) 707 + goto fail_reset; 708 + for (i = 0; i < size; i++) { 709 + if (!unpack_perm(e, version, &(*perms)[i])) 710 + goto fail; 711 + } 712 + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) 713 + goto fail; 714 + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) 715 + goto fail; 716 + } else 717 + *perms = NULL; 718 + 719 + return size; 720 + 721 + fail: 722 + kfree(*perms); 723 + fail_reset: 724 + e->pos = pos; 725 + return -EPROTO; 726 + } 727 + 661 728 static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy, 662 729 bool required_dfa, bool required_trans, 663 730 const char **info) 664 731 { 665 - int i; 732 + void *pos = e->pos; 733 + int i, flags, error = -EPROTO; 666 734 667 - policy->dfa = unpack_dfa(e); 735 + policy->size = unpack_perms_table(e, &policy->perms); 736 + if (policy->size < 0) { 737 + error = policy->size; 738 + policy->perms = NULL; 739 + *info = "failed to unpack - perms"; 740 + goto fail; 741 + } else if (policy->perms) { 742 + /* perms table present accept is index */ 743 + flags = TO_ACCEPT1_FLAG(YYTD_DATA32); 744 + } else { 745 + /* packed perms in accept1 and accept2 */ 746 + flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | 747 + TO_ACCEPT2_FLAG(YYTD_DATA32); 748 + } 749 + 750 + policy->dfa = unpack_dfa(e, flags); 668 751 if (IS_ERR(policy->dfa)) { 669 - int error = PTR_ERR(policy->dfa); 670 - 752 + error = PTR_ERR(policy->dfa); 671 753 policy->dfa = NULL; 672 754 *info = "failed to unpack - dfa"; 673 - return error; 755 + goto fail; 674 756 } else if (!policy->dfa) { 675 757 if (required_dfa) { 676 758 *info = "missing required dfa"; 677 - return -EPROTO; 759 + goto fail; 678 760 } 679 761 goto out; 680 762 } ··· 779 699 } 780 700 if (!unpack_trans_table(e, &policy->trans) && required_trans) { 781 701 *info = "failed to unpack profile transition table"; 782 - return -EPROTO; 702 + goto fail; 783 703 } 784 704 /* TODO: move compat mapping here, requires dfa merging first */ 785 705 786 706 out: 787 707 return 0; 708 + 709 + fail: 710 + e->pos = pos; 711 + return error; 788 712 } 789 713 790 714 static u32 strhash(const void *data, u32 len, u32 seed)