policy_unpack.c (ae6d35ed0a481824a8730c39d5b319c8a76ea00e) | policy_unpack.c (caa9f579ca7255e9d6c25f072447d895c5928c97) |
---|---|
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * AppArmor security module 4 * 5 * This file contains AppArmor functions for unpacking policy loaded from 6 * userspace. 7 * 8 * Copyright (C) 1998-2008 Novell/SUSE --- 13 unchanged lines hidden (view full) --- 22#include "include/audit.h" 23#include "include/cred.h" 24#include "include/crypto.h" 25#include "include/file.h" 26#include "include/match.h" 27#include "include/path.h" 28#include "include/policy.h" 29#include "include/policy_unpack.h" | 1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * AppArmor security module 4 * 5 * This file contains AppArmor functions for unpacking policy loaded from 6 * userspace. 7 * 8 * Copyright (C) 1998-2008 Novell/SUSE --- 13 unchanged lines hidden (view full) --- 22#include "include/audit.h" 23#include "include/cred.h" 24#include "include/crypto.h" 25#include "include/file.h" 26#include "include/match.h" 27#include "include/path.h" 28#include "include/policy.h" 29#include "include/policy_unpack.h" |
30#include "include/policy_compat.h" |
|
30 | 31 |
31#define K_ABI_MASK 0x3ff 32#define FORCE_COMPLAIN_FLAG 0x800 33#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK)) 34#define VERSION_LE(X, Y) (((X) & K_ABI_MASK) <= ((Y) & K_ABI_MASK)) 35#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK)) | |
36 | 32 |
37#define v5 5 /* base version */ 38#define v6 6 /* per entry policydb mediation check */ 39#define v7 7 40#define v8 8 /* full network masking */ 41#define v9 9 /* xbits are used as permission bits in policydb */ 42 | |
43/* 44 * The AppArmor interface treats data as a type byte followed by the 45 * actual data. The interface has the notion of a named entry 46 * which has a name (AA_NAME typecode followed by name string) followed by 47 * the entries typecode and data. Named types allow for optional 48 * elements and extensions to be added and tested for without breaking 49 * backwards compatibility. 50 */ --- 615 unchanged lines hidden (view full) --- 666static int datacmp(struct rhashtable_compare_arg *arg, const void *obj) 667{ 668 const struct aa_data *data = obj; 669 const char * const *key = arg->key; 670 671 return strcmp(data->key, *key); 672} 673 | 33/* 34 * The AppArmor interface treats data as a type byte followed by the 35 * actual data. The interface has the notion of a named entry 36 * which has a name (AA_NAME typecode followed by name string) followed by 37 * the entries typecode and data. Named types allow for optional 38 * elements and extensions to be added and tested for without breaking 39 * backwards compatibility. 40 */ --- 615 unchanged lines hidden (view full) --- 656static int datacmp(struct rhashtable_compare_arg *arg, const void *obj) 657{ 658 const struct aa_data *data = obj; 659 const char * const *key = arg->key; 660 661 return strcmp(data->key, *key); 662} 663 |
674/* remap old accept table embedded permissions to separate permission table */ 675static u32 dfa_map_xindex(u16 mask) 676{ 677 u16 old_index = (mask >> 10) & 0xf; 678 u32 index = 0; 679 680 if (mask & 0x100) 681 index |= AA_X_UNSAFE; 682 if (mask & 0x200) 683 index |= AA_X_INHERIT; 684 if (mask & 0x80) 685 index |= AA_X_UNCONFINED; 686 687 if (old_index == 1) { 688 index |= AA_X_UNCONFINED; 689 } else if (old_index == 2) { 690 index |= AA_X_NAME; 691 } else if (old_index == 3) { 692 index |= AA_X_NAME | AA_X_CHILD; 693 } else if (old_index) { 694 index |= AA_X_TABLE; 695 index |= old_index - 4; 696 } 697 698 return index; 699} 700 701/* 702 * map old dfa inline permissions to new format 703 */ 704#define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ 705 ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) 706#define dfa_user_xbits(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 7) & 0x7f) 707#define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) 708#define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) 709#define dfa_user_xindex(dfa, state) \ 710 (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) 711 712#define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ 713 0x7f) | \ 714 ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) 715#define dfa_other_xbits(dfa, state) \ 716 ((((ACCEPT_TABLE(dfa)[state]) >> 7) >> 14) & 0x7f) 717#define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) 718#define dfa_other_quiet(dfa, state) \ 719 ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) 720#define dfa_other_xindex(dfa, state) \ 721 dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) 722 | |
723/** | 664/** |
724 * map_old_perms - map old file perms layout to the new layout 725 * @old: permission set in old mapping 726 * 727 * Returns: new permission mapping 728 */ 729static u32 map_old_perms(u32 old) 730{ 731 u32 new = old & 0xf; 732 733 if (old & MAY_READ) 734 new |= AA_MAY_GETATTR | AA_MAY_OPEN; 735 if (old & MAY_WRITE) 736 new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE | 737 AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN; 738 if (old & 0x10) 739 new |= AA_MAY_LINK; 740 /* the old mapping lock and link_subset flags where overlaid 741 * and use was determined by part of a pair that they were in 742 */ 743 if (old & 0x20) 744 new |= AA_MAY_LOCK | AA_LINK_SUBSET; 745 if (old & 0x40) /* AA_EXEC_MMAP */ 746 new |= AA_EXEC_MMAP; 747 748 return new; 749} 750 751static void compute_fperms_allow(struct aa_perms *perms, struct aa_dfa *dfa, 752 aa_state_t state) 753{ 754 perms->allow |= AA_MAY_GETATTR; 755 756 /* change_profile wasn't determined by ownership in old mapping */ 757 if (ACCEPT_TABLE(dfa)[state] & 0x80000000) 758 perms->allow |= AA_MAY_CHANGE_PROFILE; 759 if (ACCEPT_TABLE(dfa)[state] & 0x40000000) 760 perms->allow |= AA_MAY_ONEXEC; 761} 762 763static struct aa_perms compute_fperms_user(struct aa_dfa *dfa, 764 aa_state_t state) 765{ 766 struct aa_perms perms = { }; 767 768 perms.allow = map_old_perms(dfa_user_allow(dfa, state)); 769 perms.audit = map_old_perms(dfa_user_audit(dfa, state)); 770 perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); 771 perms.xindex = dfa_user_xindex(dfa, state); 772 773 compute_fperms_allow(&perms, dfa, state); 774 775 return perms; 776} 777 778static struct aa_perms compute_fperms_other(struct aa_dfa *dfa, 779 aa_state_t state) 780{ 781 struct aa_perms perms = { }; 782 783 perms.allow = map_old_perms(dfa_other_allow(dfa, state)); 784 perms.audit = map_old_perms(dfa_other_audit(dfa, state)); 785 perms.quiet = map_old_perms(dfa_other_quiet(dfa, state)); 786 perms.xindex = dfa_other_xindex(dfa, state); 787 788 compute_fperms_allow(&perms, dfa, state); 789 790 return perms; 791} 792 793/** 794 * aa_compute_fperms - convert dfa compressed perms to internal perms and store 795 * them so they can be retrieved later. 796 * @dfa: a dfa using fperms to remap to internal permissions 797 * 798 * Returns: remapped perm table 799 */ 800static struct aa_perms *compute_fperms(struct aa_dfa *dfa) 801{ 802 aa_state_t state; 803 unsigned int state_count; 804 struct aa_perms *table; 805 806 AA_BUG(!dfa); 807 808 state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; 809 /* DFAs are restricted from having a state_count of less than 2 */ 810 table = kvcalloc(state_count * 2, sizeof(struct aa_perms), GFP_KERNEL); 811 if (!table) 812 return NULL; 813 814 /* zero init so skip the trap state (state == 0) */ 815 for (state = 1; state < state_count; state++) { 816 table[state * 2] = compute_fperms_user(dfa, state); 817 table[state * 2 + 1] = compute_fperms_other(dfa, state); 818 } 819 820 return table; 821} 822 823static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch) 824{ 825 struct aa_perms *perms; 826 int state; 827 int state_count; 828 829 AA_BUG(!xmatch); 830 831 state_count = xmatch->tables[YYTD_ID_BASE]->td_lolen; 832 /* DFAs are restricted from having a state_count of less than 2 */ 833 perms = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL); 834 835 /* zero init so skip the trap state (state == 0) */ 836 for (state = 1; state < state_count; state++) 837 perms[state].allow = dfa_user_allow(xmatch, state); 838 839 return perms; 840} 841 842static u32 map_other(u32 x) 843{ 844 return ((x & 0x3) << 8) | /* SETATTR/GETATTR */ 845 ((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */ 846 ((x & 0x60) << 19); /* SETOPT/GETOPT */ 847} 848 849static u32 map_xbits(u32 x) 850{ 851 return ((x & 0x1) << 7) | 852 ((x & 0x7e) << 9); 853} 854 855static struct aa_perms compute_perms_entry(struct aa_dfa *dfa, 856 aa_state_t state, 857 u32 version) 858{ 859 struct aa_perms perms = { }; 860 861 perms.allow = dfa_user_allow(dfa, state); 862 perms.audit = dfa_user_audit(dfa, state); 863 perms.quiet = dfa_user_quiet(dfa, state); 864 865 /* 866 * This mapping is convulated due to history. 867 * v1-v4: only file perms, which are handled by compute_fperms 868 * v5: added policydb which dropped user conditional to gain new 869 * perm bits, but had to map around the xbits because the 870 * userspace compiler was still munging them. 871 * v9: adds using the xbits in policydb because the compiler now 872 * supports treating policydb permission bits different. 873 * Unfortunately there is no way to force auditing on the 874 * perms represented by the xbits 875 */ 876 perms.allow |= map_other(dfa_other_allow(dfa, state)); 877 if (VERSION_LE(version, v8)) 878 perms.allow |= AA_MAY_LOCK; 879 else 880 perms.allow |= map_xbits(dfa_user_xbits(dfa, state)); 881 882 /* 883 * for v5-v9 perm mapping in the policydb, the other set is used 884 * to extend the general perm set 885 */ 886 perms.audit |= map_other(dfa_other_audit(dfa, state)); 887 perms.quiet |= map_other(dfa_other_quiet(dfa, state)); 888 if (VERSION_GT(version, v8)) 889 perms.quiet |= map_xbits(dfa_other_xbits(dfa, state)); 890 891 return perms; 892} 893 894static struct aa_perms *compute_perms(struct aa_dfa *dfa, u32 version) 895{ 896 unsigned int state; 897 unsigned int state_count; 898 struct aa_perms *table; 899 900 AA_BUG(!dfa); 901 902 state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; 903 /* DFAs are restricted from having a state_count of less than 2 */ 904 table = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL); 905 if (!table) 906 return NULL; 907 908 /* zero init so skip the trap state (state == 0) */ 909 for (state = 1; state < state_count; state++) 910 table[state] = compute_perms_entry(dfa, state, version); 911 912 return table; 913} 914 915/** 916 * remap_dfa_accept - remap old dfa accept table to be an index 917 * @dfa: dfa to do the remapping on 918 * @factor: scaling factor for the index conversion. 919 * 920 * Used in conjunction with compute_Xperms, it converts old style perms 921 * that are encoded in the dfa accept tables to the new style where 922 * there is a permission table and the accept table is an index into 923 * the permission table. 924 */ 925static void remap_dfa_accept(struct aa_dfa *dfa, unsigned int factor) 926{ 927 unsigned int state; 928 unsigned int state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; 929 930 AA_BUG(!dfa); 931 932 for (state = 0; state < state_count; state++) 933 ACCEPT_TABLE(dfa)[state] = state * factor; 934 kvfree(dfa->tables[YYTD_ID_ACCEPT2]); 935 dfa->tables[YYTD_ID_ACCEPT2] = NULL; 936} 937 938/** | |
939 * unpack_profile - unpack a serialized profile 940 * @e: serialized data extent information (NOT NULL) 941 * @ns_name: pointer of newly allocated copy of %NULL in case of error 942 * 943 * NOTE: unpack profile sets audit struct if there is a failure 944 */ 945static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) 946{ --- 49 unchanged lines hidden (view full) --- 996 /* neither xmatch_len not xmatch_perms are optional if xmatch is set */ 997 if (profile->xmatch.dfa) { 998 if (!unpack_u32(e, &tmp, NULL)) { 999 info = "missing xmatch len"; 1000 goto fail; 1001 } 1002 profile->xmatch_len = tmp; 1003 profile->xmatch.start[AA_CLASS_XMATCH] = DFA_START; | 665 * unpack_profile - unpack a serialized profile 666 * @e: serialized data extent information (NOT NULL) 667 * @ns_name: pointer of newly allocated copy of %NULL in case of error 668 * 669 * NOTE: unpack profile sets audit struct if there is a failure 670 */ 671static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) 672{ --- 49 unchanged lines hidden (view full) --- 722 /* neither xmatch_len not xmatch_perms are optional if xmatch is set */ 723 if (profile->xmatch.dfa) { 724 if (!unpack_u32(e, &tmp, NULL)) { 725 info = "missing xmatch len"; 726 goto fail; 727 } 728 profile->xmatch_len = tmp; 729 profile->xmatch.start[AA_CLASS_XMATCH] = DFA_START; |
1004 profile->xmatch.perms = compute_xmatch_perms(profile->xmatch.dfa); 1005 if (!profile->xmatch.perms) { | 730 if (aa_compat_map_xmatch(&profile->xmatch)) { |
1006 info = "failed to convert xmatch permission table"; 1007 goto fail; 1008 } | 731 info = "failed to convert xmatch permission table"; 732 goto fail; 733 } |
1009 remap_dfa_accept(profile->xmatch.dfa, 1); | |
1010 } 1011 1012 /* disconnected attachment string is optional */ 1013 (void) unpack_str(e, &profile->disconnected, "disconnected"); 1014 1015 /* per profile debug flags (complain, audit) */ 1016 if (!unpack_nameX(e, AA_STRUCT, "flags")) { 1017 info = "profile missing flags"; --- 108 unchanged lines hidden (view full) --- 1126 for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) { 1127 profile->policy.start[i] = 1128 aa_dfa_next(profile->policy.dfa, 1129 profile->policy.start[0], 1130 i); 1131 } 1132 if (!unpack_nameX(e, AA_STRUCTEND, NULL)) 1133 goto fail; | 734 } 735 736 /* disconnected attachment string is optional */ 737 (void) unpack_str(e, &profile->disconnected, "disconnected"); 738 739 /* per profile debug flags (complain, audit) */ 740 if (!unpack_nameX(e, AA_STRUCT, "flags")) { 741 info = "profile missing flags"; --- 108 unchanged lines hidden (view full) --- 850 for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) { 851 profile->policy.start[i] = 852 aa_dfa_next(profile->policy.dfa, 853 profile->policy.start[0], 854 i); 855 } 856 if (!unpack_nameX(e, AA_STRUCTEND, NULL)) 857 goto fail; |
1134 profile->policy.perms = compute_perms(profile->policy.dfa, 1135 e->version); 1136 if (!profile->policy.perms) { | 858 if (aa_compat_map_policy(&profile->policy, e->version)) { |
1137 info = "failed to remap policydb permission table"; 1138 goto fail; 1139 } | 859 info = "failed to remap policydb permission table"; 860 goto fail; 861 } |
1140 /* Do not remap internal dfas */ 1141 remap_dfa_accept(profile->policy.dfa, 1); | |
1142 } else 1143 profile->policy.dfa = aa_get_dfa(nulldfa); 1144 1145 /* get file rules */ 1146 profile->file.dfa = unpack_dfa(e); 1147 if (IS_ERR(profile->file.dfa)) { 1148 error = PTR_ERR(profile->file.dfa); 1149 profile->file.dfa = NULL; 1150 info = "failed to unpack profile file rules"; 1151 goto fail; 1152 } else if (profile->file.dfa) { 1153 if (!unpack_u32(e, &profile->file.start[AA_CLASS_FILE], 1154 "dfa_start")) 1155 /* default start state */ 1156 profile->file.start[AA_CLASS_FILE] = DFA_START; | 862 } else 863 profile->policy.dfa = aa_get_dfa(nulldfa); 864 865 /* get file rules */ 866 profile->file.dfa = unpack_dfa(e); 867 if (IS_ERR(profile->file.dfa)) { 868 error = PTR_ERR(profile->file.dfa); 869 profile->file.dfa = NULL; 870 info = "failed to unpack profile file rules"; 871 goto fail; 872 } else if (profile->file.dfa) { 873 if (!unpack_u32(e, &profile->file.start[AA_CLASS_FILE], 874 "dfa_start")) 875 /* default start state */ 876 profile->file.start[AA_CLASS_FILE] = DFA_START; |
1157 profile->file.perms = compute_fperms(profile->file.dfa); 1158 if (!profile->file.perms) { | 877 if (aa_compat_map_file(&profile->file)) { |
1159 info = "failed to remap file permission table"; 1160 goto fail; 1161 } | 878 info = "failed to remap file permission table"; 879 goto fail; 880 } |
1162 remap_dfa_accept(profile->file.dfa, 2); | |
1163 if (!unpack_trans_table(e, profile)) { 1164 info = "failed to unpack profile transition table"; 1165 goto fail; 1166 } 1167 } else if (profile->policy.dfa && 1168 profile->policy.start[AA_CLASS_FILE]) { 1169 profile->file.dfa = aa_get_dfa(profile->policy.dfa); 1170 profile->file.start[AA_CLASS_FILE] = profile->policy.start[AA_CLASS_FILE]; --- 369 unchanged lines hidden --- | 881 if (!unpack_trans_table(e, profile)) { 882 info = "failed to unpack profile transition table"; 883 goto fail; 884 } 885 } else if (profile->policy.dfa && 886 profile->policy.start[AA_CLASS_FILE]) { 887 profile->file.dfa = aa_get_dfa(profile->policy.dfa); 888 profile->file.start[AA_CLASS_FILE] = profile->policy.start[AA_CLASS_FILE]; --- 369 unchanged lines hidden --- |