apparmor: compute policydb permission on profile load
authorJohn Johansen <john.johansen@canonical.com>
Tue, 17 Nov 2020 09:38:16 +0000 (01:38 -0800)
committerJohn Johansen <john.johansen@canonical.com>
Mon, 3 Oct 2022 21:49:03 +0000 (14:49 -0700)
Rather than computing policydb permissions for each access
permissions can be computed once on profile load and stored for lookup.

Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/apparmorfs.c
security/apparmor/include/perms.h
security/apparmor/include/policy.h
security/apparmor/label.c
security/apparmor/lib.c
security/apparmor/mount.c
security/apparmor/net.c
security/apparmor/policy.c
security/apparmor/policy_unpack.c

index 825b309..1177837 100644 (file)
@@ -633,7 +633,7 @@ static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
                state = aa_dfa_match_len(dfa, profile->policy.start[0],
                                         match_str, match_len);
                if (state)
-                       aa_compute_perms(dfa, state, &tmp);
+                       tmp = *aa_lookup_perms(profile->policy.perms, state);
        }
        aa_apply_modes_to_perms(profile, &tmp);
        aa_perms_accum_raw(perms, &tmp);
index 13f20c5..de9631e 100644 (file)
@@ -133,6 +133,17 @@ extern struct aa_perms allperms;
        xcheck(fn_for_each((L1), (P), (FN1)), fn_for_each((L2), (P), (FN2)))
 
 
+extern struct aa_perms default_perms;
+
+static inline struct aa_perms *aa_lookup_perms(struct aa_perms *perms,
+                                              unsigned int state)
+{
+       if (!(perms))
+               return &default_perms;
+
+       return &(perms[state]);
+}
+
 void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs,
                         u32 mask);
 void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
@@ -141,8 +152,6 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
                        u32 chrsmask, const char * const *names, u32 namesmask);
 void aa_apply_modes_to_perms(struct aa_profile *profile,
                             struct aa_perms *perms);
-void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
-                     struct aa_perms *perms);
 void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend);
 void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
 void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
index 7882d5e..0dec18c 100644 (file)
@@ -77,6 +77,7 @@ enum profile_mode {
 struct aa_policydb {
        /* Generic policy DFA specific rule types will be subsections of it */
        struct aa_dfa *dfa;
+       struct aa_perms *perms;
        unsigned int start[AA_CLASS_LAST + 1];
 
 };
index 0f36ee9..ddb0441 100644 (file)
@@ -1328,7 +1328,7 @@ next:
                if (!state)
                        goto fail;
        }
-       aa_compute_perms(profile->policy.dfa, state, perms);
+       *perms = *aa_lookup_perms(profile->policy.perms, state);
        aa_apply_modes_to_perms(profile, perms);
        if ((perms->allow & request) != request)
                return -EACCES;
@@ -1379,7 +1379,7 @@ static int label_components_match(struct aa_profile *profile,
        return 0;
 
 next:
-       aa_compute_perms(profile->policy.dfa, state, &tmp);
+       tmp = *aa_lookup_perms(profile->policy.perms, state);
        aa_apply_modes_to_perms(profile, &tmp);
        aa_perms_accum(perms, &tmp);
        label_for_each_cont(i, label, tp) {
@@ -1388,7 +1388,7 @@ next:
                state = match_component(profile, tp, start);
                if (!state)
                        goto fail;
-               aa_compute_perms(profile->policy.dfa, state, &tmp);
+               tmp = *aa_lookup_perms(profile->policy.perms, state);
                aa_apply_modes_to_perms(profile, &tmp);
                aa_perms_accum(perms, &tmp);
        }
index 1c72a61..505ef58 100644 (file)
@@ -315,48 +315,6 @@ void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
  */
 }
 
-static u32 map_other(u32 x)
-{
-       return ((x & 0x3) << 8) |       /* SETATTR/GETATTR */
-               ((x & 0x1c) << 18) |    /* ACCEPT/BIND/LISTEN */
-               ((x & 0x60) << 19);     /* SETOPT/GETOPT */
-}
-
-static u32 map_xbits(u32 x)
-{
-       return ((x & 0x1) << 7) |
-               ((x & 0x7e) << 9);
-}
-
-void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
-                     struct aa_perms *perms)
-{
-       /* This mapping is convulated due to history.
-        * v1-v4: only file perms
-        * v5: added policydb which dropped in perm user conditional to
-        *     gain new perm bits, but had to map around the xbits because
-        *     the userspace compiler was still munging them.
-        * v9: adds using the xbits in policydb because the compiler now
-        *     supports treating policydb permission bits different.
-        *     Unfortunately there is not way to force auditing on the
-        *     perms represented by the xbits
-        */
-       *perms = (struct aa_perms) {
-               .allow = dfa_user_allow(dfa, state) |
-                        map_xbits(dfa_user_xbits(dfa, state)),
-               .audit = dfa_user_audit(dfa, state),
-               .quiet = dfa_user_quiet(dfa, state) |
-                        map_xbits(dfa_other_xbits(dfa, state)),
-       };
-
-       /* for v5-v9 perm mapping in the policydb, the other set is used
-        * to extend the general perm set
-        */
-       perms->allow |= map_other(dfa_other_allow(dfa, state));
-       perms->audit |= map_other(dfa_other_audit(dfa, state));
-       perms->quiet |= map_other(dfa_other_quiet(dfa, state));
-}
-
 /**
  * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
  * @accum - perms struct to accumulate into
index f612472..1e978c2 100644 (file)
@@ -203,25 +203,6 @@ static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
        return state;
 }
 
-/**
- * compute_mnt_perms - compute mount permission associated with @state
- * @dfa: dfa to match against (NOT NULL)
- * @state: state match finished in
- *
- * Returns: mount permissions
- */
-static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa,
-                                          unsigned int state)
-{
-       struct aa_perms perms = {
-               .allow = dfa_user_allow(dfa, state),
-               .audit = dfa_user_audit(dfa, state),
-               .quiet = dfa_user_quiet(dfa, state),
-       };
-
-       return perms;
-}
-
 static const char * const mnt_info_table[] = {
        "match succeeded",
        "failed mntpnt match",
@@ -236,50 +217,52 @@ static const char * const mnt_info_table[] = {
  * Returns 0 on success else element that match failed in, this is the
  * index into the mnt_info_table above
  */
-static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
+static int do_match_mnt(struct aa_policydb *policy, unsigned int start,
                        const char *mntpnt, const char *devname,
                        const char *type, unsigned long flags,
                        void *data, bool binary, struct aa_perms *perms)
 {
        unsigned int state;
 
-       AA_BUG(!dfa);
+       AA_BUG(!policy);
+       AA_BUG(!policy->dfa);
+       AA_BUG(!policy->perms);
        AA_BUG(!perms);
 
-       state = aa_dfa_match(dfa, start, mntpnt);
-       state = aa_dfa_null_transition(dfa, state);
+       state = aa_dfa_match(policy->dfa, start, mntpnt);
+       state = aa_dfa_null_transition(policy->dfa, state);
        if (!state)
                return 1;
 
        if (devname)
-               state = aa_dfa_match(dfa, state, devname);
-       state = aa_dfa_null_transition(dfa, state);
+               state = aa_dfa_match(policy->dfa, state, devname);
+       state = aa_dfa_null_transition(policy->dfa, state);
        if (!state)
                return 2;
 
        if (type)
-               state = aa_dfa_match(dfa, state, type);
-       state = aa_dfa_null_transition(dfa, state);
+               state = aa_dfa_match(policy->dfa, state, type);
+       state = aa_dfa_null_transition(policy->dfa, state);
        if (!state)
                return 3;
 
-       state = match_mnt_flags(dfa, state, flags);
+       state = match_mnt_flags(policy->dfa, state, flags);
        if (!state)
                return 4;
-       *perms = compute_mnt_perms(dfa, state);
+       *perms = *aa_lookup_perms(policy->perms, state);
        if (perms->allow & AA_MAY_MOUNT)
                return 0;
 
        /* only match data if not binary and the DFA flags data is expected */
        if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
-               state = aa_dfa_null_transition(dfa, state);
+               state = aa_dfa_null_transition(policy->dfa, state);
                if (!state)
                        return 4;
 
-               state = aa_dfa_match(dfa, state, data);
+               state = aa_dfa_match(policy->dfa, state, data);
                if (!state)
                        return 5;
-               *perms = compute_mnt_perms(dfa, state);
+               *perms = *aa_lookup_perms(policy->perms, state);
                if (perms->allow & AA_MAY_MOUNT)
                        return 0;
        }
@@ -341,7 +324,7 @@ static int match_mnt_path_str(struct aa_profile *profile,
        }
 
        error = -EACCES;
-       pos = do_match_mnt(profile->policy.dfa,
+       pos = do_match_mnt(&profile->policy,
                           profile->policy.start[AA_CLASS_MOUNT],
                           mntpnt, devname, type, flags, data, binary, &perms);
        if (pos) {
@@ -601,7 +584,7 @@ static int profile_umount(struct aa_profile *profile, const struct path *path,
        state = aa_dfa_match(profile->policy.dfa,
                             profile->policy.start[AA_CLASS_MOUNT],
                             name);
-       perms = compute_mnt_perms(profile->policy.dfa, state);
+       perms = *aa_lookup_perms(profile->policy.perms, state);
        if (AA_MAY_UMOUNT & ~perms.allow)
                error = -EACCES;
 
@@ -672,7 +655,7 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
                             new_name);
        state = aa_dfa_null_transition(profile->policy.dfa, state);
        state = aa_dfa_match(profile->policy.dfa, state, old_name);
-       perms = compute_mnt_perms(profile->policy.dfa, state);
+       perms = *aa_lookup_perms(profile->policy.perms, state);
 
        if (AA_MAY_PIVOTROOT & perms.allow)
                error = 0;
index 7efe4d1..88e8a7e 100644 (file)
@@ -125,7 +125,7 @@ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
        buffer[1] = cpu_to_be16((u16) type);
        state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer,
                                 4);
-       aa_compute_perms(profile->policy.dfa, state, &perms);
+       perms = *aa_lookup_perms(profile->policy.perms, state);
        aa_apply_modes_to_perms(profile, &perms);
 
        return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
index e2d23cd..6c3086e 100644 (file)
@@ -233,7 +233,7 @@ void aa_free_profile(struct aa_profile *profile)
        aa_put_dfa(profile->xmatch);
        kvfree(profile->xmatch_perms);
        aa_put_dfa(profile->policy.dfa);
-
+       kvfree(profile->policy.perms);
        if (profile->data) {
                rht = profile->data;
                profile->data = NULL;
index 44910c2..ed06338 100644 (file)
@@ -756,7 +756,7 @@ static struct aa_perms *compute_fperms(struct aa_dfa *dfa)
 
        state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
        /* DFAs are restricted from having a state_count of less than 2 */
-       table = kvzalloc(state_count * 2 * sizeof(struct aa_perms), GFP_KERNEL);
+       table = kvcalloc(state_count * 2, sizeof(struct aa_perms), GFP_KERNEL);
        if (!table)
                return NULL;
 
@@ -789,6 +789,54 @@ static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch)
        return perms_table;
 }
 
+static u32 map_other(u32 x)
+{
+       return ((x & 0x3) << 8) |       /* SETATTR/GETATTR */
+               ((x & 0x1c) << 18) |    /* ACCEPT/BIND/LISTEN */
+               ((x & 0x60) << 19);     /* SETOPT/GETOPT */
+}
+
+static struct aa_perms compute_perms_entry(struct aa_dfa *dfa,
+                                          unsigned int state)
+{
+       struct aa_perms perms = { };
+
+       perms.allow = dfa_user_allow(dfa, state);
+       perms.audit = dfa_user_audit(dfa, state);
+       perms.quiet = dfa_user_quiet(dfa, state);
+
+       /* for v5 perm mapping in the policydb, the other set is used
+        * to extend the general perm set
+        */
+
+       perms.allow |= map_other(dfa_other_allow(dfa, state));
+       perms.audit |= map_other(dfa_other_audit(dfa, state));
+       perms.quiet |= map_other(dfa_other_quiet(dfa, state));
+
+       return perms;
+}
+
+static struct aa_perms *compute_perms(struct aa_dfa *dfa)
+{
+       int state;
+       int state_count;
+       struct aa_perms *table;
+
+       AA_BUG(!dfa);
+
+       state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
+       /* DFAs are restricted from having a state_count of less than 2 */
+       table = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL);
+       if (!table)
+               return NULL;
+
+       /* zero init so skip the trap state (state == 0) */
+       for (state = 1; state < state_count; state++)
+               table[state] = compute_perms_entry(dfa, state);
+
+       return table;
+}
+
 /**
  * unpack_profile - unpack a serialized profile
  * @e: serialized data extent information (NOT NULL)
@@ -986,6 +1034,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                        goto fail;
        } else
                profile->policy.dfa = aa_get_dfa(nulldfa);
+       profile->policy.perms = compute_perms(profile->policy.dfa);
+       if (!profile->policy.perms) {
+               info = "failed to remap policydb permission table";
+               goto fail;
+       }
 
        /* get file rules */
        profile->file.dfa = unpack_dfa(e);