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

Configure Feed

Select the types of activity you want to include in your feed.

apparmor: add support loading per permission tagging

Add support for the per permission tag index for a given permission
set. This will be used by both meta-data tagging, to allow annotating
accept states with context and debug information. As well as by rule
tainting and triggers to specify the taint or trigger to be applied.

Since these are low frequency ancillary data items they are stored
in a tighter packed format to that allows for sharing and reuse of the
strings between permissions and accept states. Reducing the amount of
kernel memory use at the cost of having to go through a couple if
index based indirections.

The tags are just strings that has no meaning with out context. When
used as meta-data for auditing and debugging its entirely information
for userspace, but triggers, and tainting can be used to affect the
domain. However they all exist in the same packed data set and can
be shared between different uses.

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

+269 -18
+1
security/apparmor/file.c
··· 103 103 104 104 ad.subj_cred = subj_cred; 105 105 ad.request = request; 106 + ad.tags = perms->tag; 106 107 ad.name = name; 107 108 ad.fs.target = target; 108 109 ad.peer = tlabel;
+2
security/apparmor/include/audit.h
··· 119 119 const char *info; 120 120 u32 request; 121 121 u32 denied; 122 + u32 tags; 123 + 122 124 union { 123 125 /* these entries require a custom callback fn */ 124 126 struct {
+3 -2
security/apparmor/include/lib.h
··· 30 30 #define DEBUG_DOMAIN 4 31 31 #define DEBUG_POLICY 8 32 32 #define DEBUG_INTERFACE 0x10 33 - #define DEBUG_UNPACK 0x40 33 + #define DEBUG_UNPACK 0x20 34 + #define DEBUG_TAGS 0x40 34 35 35 - #define DEBUG_ALL 0x1f /* update if new DEBUG_X added */ 36 + #define DEBUG_ALL 0x7f /* update if new DEBUG_X added */ 36 37 #define DEBUG_PARSE_ERROR (-1) 37 38 38 39 #define DEBUG_ON (aa_g_debug != DEBUG_NONE)
+28 -4
security/apparmor/include/policy.h
··· 79 79 }; 80 80 81 81 82 + struct aa_tags_header { 83 + u32 mask; /* bit mask matching permissions */ 84 + u32 count; /* number of strings per entry */ 85 + u32 size; /* size of all strings covered by count */ 86 + u32 tags; /* index into string table */ 87 + }; 88 + 89 + struct aa_tags_struct { 90 + struct { 91 + u32 size; /* number of entries in tagsets */ 92 + u32 *table; /* indexes into headers & strs */ 93 + } sets; 94 + struct { 95 + u32 size; /* number of headers == num of strs */ 96 + struct aa_tags_header *table; 97 + } hdrs; 98 + struct aa_str_table strs; 99 + }; 100 + 82 101 /* struct aa_policydb - match engine for a policy 83 - * count: refcount for the pdb 84 - * dfa: dfa pattern match 85 - * perms: table of permissions 86 - * strs: table of strings, index by x 102 + * @count: refcount for the pdb 103 + * @dfa: dfa pattern match 104 + * @perms: table of permissions 105 + * @size: number of entries in @perms 106 + * @trans: table of strings, index by x 107 + * @tags: table of tags that perms->tag indexes 108 + * @start:_states to start in for each class 87 109 * start: set of start states for the different classes of data 88 110 */ 89 111 struct aa_policydb { ··· 116 94 u32 size; 117 95 }; 118 96 struct aa_str_table trans; 97 + struct aa_tags_struct tags; 119 98 aa_state_t start[AA_CLASS_LAST + 1]; 120 99 }; 121 100 122 101 extern struct aa_policydb *nullpdb; 123 102 103 + void aa_destroy_tags(struct aa_tags_struct *tags); 124 104 struct aa_policydb *aa_alloc_pdb(gfp_t gfp); 125 105 void aa_pdb_free_kref(struct kref *kref); 126 106
+2
security/apparmor/lib.c
··· 45 45 { "policy", DEBUG_POLICY }, 46 46 { "interface", DEBUG_INTERFACE }, 47 47 { "unpack", DEBUG_UNPACK }, 48 + { "tags", DEBUG_TAGS }, 48 49 { NULL, 0 } 49 50 }; 50 51 ··· 512 511 /* don't free name as its a subset of hname */ 513 512 aa_put_str(policy->hname); 514 513 } 514 +
+8
security/apparmor/policy.c
··· 98 98 "user", 99 99 }; 100 100 101 + void aa_destroy_tags(struct aa_tags_struct *tags) 102 + { 103 + kfree_sensitive(tags->hdrs.table); 104 + kfree_sensitive(tags->sets.table); 105 + aa_destroy_str_table(&tags->strs); 106 + memset(tags, 0, sizeof(*tags)); 107 + } 101 108 102 109 static void aa_free_pdb(struct aa_policydb *pdb) 103 110 { ··· 112 105 aa_put_dfa(pdb->dfa); 113 106 kvfree(pdb->perms); 114 107 aa_destroy_str_table(&pdb->trans); 108 + aa_destroy_tags(&pdb->tags); 115 109 kfree(pdb); 116 110 } 117 111 }
+8 -2
security/apparmor/policy_compat.c
··· 263 263 *size = state_count; 264 264 265 265 /* zero init so skip the trap state (state == 0) */ 266 - for (state = 1; state < state_count; state++) 266 + for (state = 1; state < state_count; state++) { 267 267 table[state] = compute_perms_entry(dfa, state, version); 268 - 268 + AA_DEBUG(DEBUG_UNPACK, 269 + "[%d]: (0x%x/0x%x/0x%x//0x%x/0x%x//0x%x), converted from accept1: 0x%x, accept2: 0x%x", 270 + state, table[state].allow, table[state].deny, 271 + table[state].prompt, table[state].audit, 272 + table[state].quiet, table[state].xindex, 273 + ACCEPT_TABLE(dfa)[state], ACCEPT_TABLE2(dfa)[state]); 274 + } 269 275 return table; 270 276 } 271 277
+217 -10
security/apparmor/policy_unpack.c
··· 506 506 * @multi: allow multiple strings on a single entry 507 507 * @strs: str table to unpack to (NOT NULL) 508 508 * 509 - * Returns: true if table successfully unpacked or not present 509 + * Returns: 0 if table successfully unpacked or not present, else error 510 510 */ 511 - static bool unpack_strs_table(struct aa_ext *e, const char *name, bool multi, 511 + static int unpack_strs_table(struct aa_ext *e, const char *name, bool multi, 512 512 struct aa_str_table *strs) 513 513 { 514 514 void *saved_pos = e->pos; 515 515 struct aa_str_table_ent *table = NULL; 516 + int error = -EPROTO; 516 517 517 518 /* exec table is optional */ 518 519 if (aa_unpack_nameX(e, AA_STRUCT, name)) { ··· 530 529 goto fail; 531 530 table = kcalloc(size, sizeof(struct aa_str_table_ent), 532 531 GFP_KERNEL); 533 - if (!table) 532 + if (!table) { 533 + error = -ENOMEM; 534 534 goto fail; 535 - 535 + } 536 536 strs->table = table; 537 537 strs->size = size; 538 538 for (i = 0; i < size; i++) { ··· 544 542 */ 545 543 c = process_strs_entry(str, size2, multi); 546 544 if (c <= 0) { 547 - AA_DEBUG(DEBUG_UNPACK, "process_strs %d", c); 545 + AA_DEBUG(DEBUG_UNPACK, "process_strs %d i %d pos %ld", 546 + c, i, e->pos - saved_pos); 548 547 goto fail; 549 548 } 550 549 if (!multi && c > 1) { ··· 562 559 if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) 563 560 goto fail; 564 561 } 565 - return true; 562 + return 0; 566 563 567 564 fail: 568 565 aa_destroy_str_table(strs); 569 566 e->pos = saved_pos; 570 - return false; 567 + return error; 571 568 } 572 569 573 570 static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) ··· 682 679 return false; 683 680 } 684 681 682 + 683 + static bool verify_tags(struct aa_tags_struct *tags, const char **info) 684 + { 685 + if ((tags->hdrs.size && !tags->hdrs.table) || 686 + (!tags->hdrs.size && tags->hdrs.table)) { 687 + *info = "failed verification tag.hdrs disagree"; 688 + return false; 689 + } 690 + if ((tags->sets.size && !tags->sets.table) || 691 + (!tags->sets.size && tags->sets.table)) { 692 + *info = "failed verification tag.sets disagree"; 693 + return false; 694 + } 695 + if ((tags->strs.size && !tags->strs.table) || 696 + (!tags->strs.size && tags->strs.table)) { 697 + *info = "failed verification tags->strs disagree"; 698 + return false; 699 + } 700 + /* no data present */ 701 + if (!tags->sets.size && !tags->hdrs.size && !tags->strs.size) { 702 + return true; 703 + } else if (!(tags->sets.size && tags->hdrs.size && tags->strs.size)) { 704 + /* some data present but not all */ 705 + *info = "failed verification tags partial data present"; 706 + return false; 707 + } 708 + 709 + u32 i; 710 + 711 + for (i = 0; i < tags->sets.size; i++) { 712 + /* count followed by count indexes into hdrs */ 713 + u32 cnt = tags->sets.table[i]; 714 + 715 + if (i+cnt >= tags->sets.size) { 716 + AA_DEBUG(DEBUG_UNPACK, 717 + "tagset too large %d+%d > sets.table[%d]", 718 + i, cnt, tags->sets.size); 719 + *info = "failed verification tagset too large"; 720 + return false; 721 + } 722 + for (; cnt; cnt--) { 723 + if (tags->sets.table[++i] >= tags->hdrs.size) { 724 + AA_DEBUG(DEBUG_UNPACK, 725 + "tagsets idx out of bounds cnt %d sets.table[%d] >= %d", 726 + cnt, i-1, tags->hdrs.size); 727 + *info = "failed verification tagsets idx out of bounds"; 728 + return false; 729 + } 730 + } 731 + } 732 + for (i = 0; i < tags->hdrs.size; i++) { 733 + u32 idx = tags->hdrs.table[i].tags; 734 + 735 + if (idx >= tags->strs.size) { 736 + AA_DEBUG(DEBUG_UNPACK, 737 + "tag.hdrs idx oob idx %d > tags->strs.size=%d", 738 + idx, tags->strs.size); 739 + *info = "failed verification tags.hdrs idx out of bounds"; 740 + return false; 741 + } 742 + if (tags->hdrs.table[i].count != tags->strs.table[idx].count) { 743 + AA_DEBUG(DEBUG_UNPACK, "hdrs.table[%d].count=%d != tags->strs.table[%d]=%d", 744 + i, tags->hdrs.table[i].count, idx, tags->strs.table[idx].count); 745 + *info = "failed verification tagd.hdrs[idx].count"; 746 + return false; 747 + } 748 + if (tags->hdrs.table[i].size != tags->strs.table[idx].size) { 749 + AA_DEBUG(DEBUG_UNPACK, "hdrs.table[%d].size=%d != strs.table[%d].size=%d", 750 + i, tags->hdrs.table[i].size, idx, tags->strs.table[idx].size); 751 + *info = "failed verification tagd.hdrs[idx].size"; 752 + return false; 753 + } 754 + } 755 + 756 + return true; 757 + } 758 + 759 + static int unpack_tagsets(struct aa_ext *e, struct aa_tags_struct *tags) 760 + { 761 + u32 *sets; 762 + u16 i, size; 763 + int error = -EPROTO; 764 + void *pos = e->pos; 765 + 766 + if (!aa_unpack_array(e, "sets", &size)) 767 + goto fail_reset; 768 + sets = kcalloc(size, sizeof(u32), GFP_KERNEL); 769 + if (!sets) { 770 + error = -ENOMEM; 771 + goto fail_reset; 772 + } 773 + for (i = 0; i < size; i++) { 774 + if (!aa_unpack_u32(e, &sets[i], NULL)) 775 + goto fail; 776 + } 777 + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) 778 + goto fail; 779 + 780 + tags->sets.size = size; 781 + tags->sets.table = sets; 782 + 783 + return 0; 784 + 785 + fail: 786 + kfree_sensitive(sets); 787 + fail_reset: 788 + e->pos = pos; 789 + return error; 790 + } 791 + 792 + static bool unpack_tag_header_ent(struct aa_ext *e, struct aa_tags_header *h) 793 + { 794 + return aa_unpack_u32(e, &h->mask, NULL) && 795 + aa_unpack_u32(e, &h->count, NULL) && 796 + aa_unpack_u32(e, &h->size, NULL) && 797 + aa_unpack_u32(e, &h->tags, NULL); 798 + } 799 + 800 + static int unpack_tag_headers(struct aa_ext *e, struct aa_tags_struct *tags) 801 + { 802 + struct aa_tags_header *hdrs; 803 + u16 i, size; 804 + int error = -EPROTO; 805 + void *pos = e->pos; 806 + 807 + if (!aa_unpack_array(e, "hdrs", &size)) 808 + goto fail_reset; 809 + hdrs = kcalloc(size, sizeof(struct aa_tags_header), GFP_KERNEL); 810 + if (!hdrs) { 811 + error = -ENOMEM; 812 + goto fail_reset; 813 + } 814 + for (i = 0; i < size; i++) { 815 + if (!unpack_tag_header_ent(e, &hdrs[i])) 816 + goto fail; 817 + } 818 + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) 819 + goto fail; 820 + 821 + tags->hdrs.size = size; 822 + tags->hdrs.table = hdrs; 823 + AA_DEBUG(DEBUG_UNPACK, "headers %ld size %d", (long) hdrs, size); 824 + return true; 825 + 826 + fail: 827 + kfree_sensitive(hdrs); 828 + fail_reset: 829 + e->pos = pos; 830 + return error; 831 + } 832 + 833 + 834 + static size_t unpack_tags(struct aa_ext *e, struct aa_tags_struct *tags, 835 + const char **info) 836 + { 837 + int error = -EPROTO; 838 + void *pos = e->pos; 839 + 840 + AA_BUG(!tags); 841 + /* policy tags are optional */ 842 + if (aa_unpack_nameX(e, AA_STRUCT, "tags")) { 843 + u32 version; 844 + 845 + if (!aa_unpack_u32(e, &version, "version") || version != 1) { 846 + *info = "invalid tags version"; 847 + goto fail_reset; 848 + } 849 + error = unpack_strs_table(e, "strs", true, &tags->strs); 850 + if (error) { 851 + *info = "failed to unpack profile tag.strs"; 852 + goto fail; 853 + } 854 + error = unpack_tag_headers(e, tags); 855 + if (error) { 856 + *info = "failed to unpack profile tag.headers"; 857 + goto fail; 858 + } 859 + error = unpack_tagsets(e, tags); 860 + if (error) { 861 + *info = "failed to unpack profile tag.sets"; 862 + goto fail; 863 + } 864 + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) 865 + goto fail; 866 + 867 + if (!verify_tags(tags, info)) 868 + goto fail; 869 + } 870 + 871 + return 0; 872 + 873 + fail: 874 + aa_destroy_tags(tags); 875 + fail_reset: 876 + e->pos = pos; 877 + return error; 878 + } 879 + 685 880 static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm) 686 881 { 687 882 u32 reserved; ··· 959 758 if (!pdb) 960 759 return -ENOMEM; 961 760 761 + AA_DEBUG(DEBUG_UNPACK, "unpacking tags"); 762 + if (unpack_tags(e, &pdb->tags, info) < 0) 763 + goto fail; 764 + AA_DEBUG(DEBUG_UNPACK, "done unpacking tags"); 765 + 962 766 size = unpack_perms_table(e, &pdb->perms); 963 767 if (size < 0) { 964 768 error = size; ··· 1036 830 * transition table may be present even when the dfa is 1037 831 * not. For compatibility reasons unpack and discard. 1038 832 */ 1039 - if (!unpack_strs_table(e, "xtable", false, &pdb->trans) && 1040 - required_trans) { 833 + error = unpack_strs_table(e, "xtable", false, &pdb->trans); 834 + if (error && required_trans) { 1041 835 *info = "failed to unpack profile transition table"; 1042 836 goto fail; 1043 837 } ··· 1300 1094 goto fail; 1301 1095 } else if (rules->file->dfa) { 1302 1096 if (!rules->file->perms) { 1097 + AA_DEBUG(DEBUG_UNPACK, "compat mapping perms"); 1303 1098 error = aa_compat_map_file(rules->file); 1304 1099 if (error) { 1305 1100 info = "failed to remap file permission table"; ··· 1503 1296 if (xmax < xidx) 1504 1297 xmax = xidx; 1505 1298 } 1506 - if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size) 1299 + if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->tags.sets.size) 1507 1300 return false; 1508 1301 if (pdb->perms[i].label && 1509 1302 pdb->perms[i].label >= pdb->trans.size)