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