apparmor: refcount the pdb
authorJohn Johansen <john.johansen@canonical.com>
Fri, 28 Apr 2023 12:32:52 +0000 (05:32 -0700)
committerJohn Johansen <john.johansen@canonical.com>
Wed, 18 Oct 2023 22:30:47 +0000 (15:30 -0700)
With the move to permission tables the dfa is no longer a stand
alone entity when used, needing a minimum of a permission table.
However it still could be shared among different pdbs each using
a different permission table.

Instead of duping the permission table when sharing a pdb, add a
refcount to the pdb so it can be easily shared.

Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
15 files changed:
security/apparmor/apparmorfs.c
security/apparmor/domain.c
security/apparmor/file.c
security/apparmor/include/lib.h
security/apparmor/include/match.h
security/apparmor/include/policy.h
security/apparmor/ipc.c
security/apparmor/label.c
security/apparmor/lib.c
security/apparmor/lsm.c
security/apparmor/match.c
security/apparmor/mount.c
security/apparmor/net.c
security/apparmor/policy.c
security/apparmor/policy_unpack.c

index f20d923..0bba46c 100644 (file)
@@ -619,23 +619,23 @@ static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
 
        if (profile_unconfined(profile))
                return;
-       if (rules->file.dfa && *match_str == AA_CLASS_FILE) {
-               state = aa_dfa_match_len(rules->file.dfa,
-                                        rules->file.start[AA_CLASS_FILE],
+       if (rules->file->dfa && *match_str == AA_CLASS_FILE) {
+               state = aa_dfa_match_len(rules->file->dfa,
+                                        rules->file->start[AA_CLASS_FILE],
                                         match_str + 1, match_len - 1);
                if (state) {
                        struct path_cond cond = { };
 
-                       tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
+                       tmp = *(aa_lookup_fperms(rules->file, state, &cond));
                }
-       } else if (rules->policy.dfa) {
+       } else if (rules->policy->dfa) {
                if (!RULE_MEDIATES(rules, *match_str))
                        return; /* no change to current perms */
-               state = aa_dfa_match_len(rules->policy.dfa,
-                                        rules->policy.start[0],
+               state = aa_dfa_match_len(rules->policy->dfa,
+                                        rules->policy->start[0],
                                         match_str, match_len);
                if (state)
-                       tmp = *aa_lookup_perms(&rules->policy, state);
+                       tmp = *aa_lookup_perms(rules->policy, state);
        }
        aa_apply_modes_to_perms(profile, &tmp);
        aa_perms_accum_raw(perms, &tmp);
@@ -1096,7 +1096,7 @@ static int seq_profile_attach_show(struct seq_file *seq, void *v)
        struct aa_profile *profile = labels_profile(label);
        if (profile->attach.xmatch_str)
                seq_printf(seq, "%s\n", profile->attach.xmatch_str);
-       else if (profile->attach.xmatch.dfa)
+       else if (profile->attach.xmatch->dfa)
                seq_puts(seq, "<unknown>\n");
        else
                seq_printf(seq, "%s\n", profile->base.name);
index d95292a..87dfa0e 100644 (file)
@@ -77,7 +77,7 @@ out:
 /**** TODO: dedup to aa_label_match - needs perm and dfa, merging
  * specifically this is an exact copy of aa_label_match except
  * aa_compute_perms is replaced with aa_compute_fperms
- * and policy.dfa with file.dfa
+ * and policy->dfa with file->dfa
  ****/
 /* match a profile and its associated ns component if needed
  * Assumes visibility test has already been done.
@@ -93,16 +93,16 @@ static inline aa_state_t match_component(struct aa_profile *profile,
        const char *ns_name;
 
        if (stack)
-               state = aa_dfa_match(rules->file.dfa, state, "&");
+               state = aa_dfa_match(rules->file->dfa, state, "&");
        if (profile->ns == tp->ns)
-               return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
+               return aa_dfa_match(rules->file->dfa, state, tp->base.hname);
 
        /* try matching with namespace name and then profile */
        ns_name = aa_ns_name(profile->ns, tp->ns, true);
-       state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
-       state = aa_dfa_match(rules->file.dfa, state, ns_name);
-       state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
-       return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
+       state = aa_dfa_match_len(rules->file->dfa, state, ":", 1);
+       state = aa_dfa_match(rules->file->dfa, state, ns_name);
+       state = aa_dfa_match_len(rules->file->dfa, state, ":", 1);
+       return aa_dfa_match(rules->file->dfa, state, tp->base.hname);
 }
 
 /**
@@ -150,12 +150,12 @@ next:
        label_for_each_cont(i, label, tp) {
                if (!aa_ns_visible(profile->ns, tp->ns, subns))
                        continue;
-               state = aa_dfa_match(rules->file.dfa, state, "//&");
+               state = aa_dfa_match(rules->file->dfa, state, "//&");
                state = match_component(profile, tp, false, state);
                if (!state)
                        goto fail;
        }
-       *perms = *(aa_lookup_fperms(&(rules->file), state, &cond));
+       *perms = *(aa_lookup_fperms(rules->file, state, &cond));
        aa_apply_modes_to_perms(profile, perms);
        if ((perms->allow & request) != request)
                return -EACCES;
@@ -210,7 +210,7 @@ static int label_components_match(struct aa_profile *profile,
        return 0;
 
 next:
-       tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
+       tmp = *(aa_lookup_fperms(rules->file, state, &cond));
        aa_apply_modes_to_perms(profile, &tmp);
        aa_perms_accum(perms, &tmp);
        label_for_each_cont(i, label, tp) {
@@ -219,7 +219,7 @@ next:
                state = match_component(profile, tp, stack, start);
                if (!state)
                        goto fail;
-               tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
+               tmp = *(aa_lookup_fperms(rules->file, state, &cond));
                aa_apply_modes_to_perms(profile, &tmp);
                aa_perms_accum(perms, &tmp);
        }
@@ -317,7 +317,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
        might_sleep();
 
        /* transition from exec match to xattr set */
-       state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
+       state = aa_dfa_outofband_transition(attach->xmatch->dfa, state);
        d = bprm->file->f_path.dentry;
 
        for (i = 0; i < attach->xattr_count; i++) {
@@ -331,20 +331,20 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
                         * that not present xattr can be distinguished from a 0
                         * length value or rule that matches any value
                         */
-                       state = aa_dfa_null_transition(attach->xmatch.dfa,
+                       state = aa_dfa_null_transition(attach->xmatch->dfa,
                                                       state);
                        /* Check xattr value */
-                       state = aa_dfa_match_len(attach->xmatch.dfa, state,
+                       state = aa_dfa_match_len(attach->xmatch->dfa, state,
                                                 value, size);
-                       index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
-                       perm = attach->xmatch.perms[index].allow;
+                       index = ACCEPT_TABLE(attach->xmatch->dfa)[state];
+                       perm = attach->xmatch->perms[index].allow;
                        if (!(perm & MAY_EXEC)) {
                                ret = -EINVAL;
                                goto out;
                        }
                }
                /* transition to next element */
-               state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
+               state = aa_dfa_outofband_transition(attach->xmatch->dfa, state);
                if (size < 0) {
                        /*
                         * No xattr match, so verify if transition to
@@ -413,16 +413,16 @@ restart:
                 * as another profile, signal a conflict and refuse to
                 * match.
                 */
-               if (attach->xmatch.dfa) {
+               if (attach->xmatch->dfa) {
                        unsigned int count;
                        aa_state_t state;
                        u32 index, perm;
 
-                       state = aa_dfa_leftmatch(attach->xmatch.dfa,
-                                       attach->xmatch.start[AA_CLASS_XMATCH],
+                       state = aa_dfa_leftmatch(attach->xmatch->dfa,
+                                       attach->xmatch->start[AA_CLASS_XMATCH],
                                        name, &count);
-                       index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
-                       perm = attach->xmatch.perms[index].allow;
+                       index = ACCEPT_TABLE(attach->xmatch->dfa)[state];
+                       perm = attach->xmatch->perms[index].allow;
                        /* any accepting state means a valid match. */
                        if (perm & MAY_EXEC) {
                                int ret = 0;
@@ -525,7 +525,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
        /* TODO: move lookup parsing to unpack time so this is a straight
         *       index into the resultant label
         */
-       for (*name = rules->file.trans.table[index]; !label && *name;
+       for (*name = rules->file->trans.table[index]; !label && *name;
             *name = next_name(xtype, *name)) {
                if (xindex & AA_X_CHILD) {
                        struct aa_profile *new_profile;
@@ -579,7 +579,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
                break;
        case AA_X_TABLE:
                /* TODO: fix when perm mapping done at unload */
-               stack = rules->file.trans.table[xindex & AA_X_INDEX_MASK];
+               stack = rules->file->trans.table[xindex & AA_X_INDEX_MASK];
                if (*stack != '&') {
                        /* released by caller */
                        new = x_table_lookup(profile, xindex, lookupname);
@@ -638,7 +638,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
                                                    typeof(*rules), list);
        struct aa_label *new = NULL;
        const char *info = NULL, *name = NULL, *target = NULL;
-       aa_state_t state = rules->file.start[AA_CLASS_FILE];
+       aa_state_t state = rules->file->start[AA_CLASS_FILE];
        struct aa_perms perms = {};
        bool nonewprivs = false;
        int error = 0;
@@ -672,7 +672,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
        }
 
        /* find exec permissions for name */
-       state = aa_str_perms(&(rules->file), state, name, cond, &perms);
+       state = aa_str_perms(rules->file, state, name, cond, &perms);
        if (perms.allow & MAY_EXEC) {
                /* exec permission determine how to transition */
                new = x_to_label(profile, bprm, name, perms.xindex, &target,
@@ -738,7 +738,7 @@ static int profile_onexec(const struct cred *subj_cred,
 {
        struct aa_ruleset *rules = list_first_entry(&profile->rules,
                                                    typeof(*rules), list);
-       aa_state_t state = rules->file.start[AA_CLASS_FILE];
+       aa_state_t state = rules->file->start[AA_CLASS_FILE];
        struct aa_perms perms = {};
        const char *xname = NULL, *info = "change_profile onexec";
        int error = -EACCES;
@@ -771,7 +771,7 @@ static int profile_onexec(const struct cred *subj_cred,
        }
 
        /* find exec permissions for name */
-       state = aa_str_perms(&(rules->file), state, xname, cond, &perms);
+       state = aa_str_perms(rules->file, state, xname, cond, &perms);
        if (!(perms.allow & AA_MAY_ONEXEC)) {
                info = "no change_onexec valid for executable";
                goto audit;
@@ -780,7 +780,7 @@ static int profile_onexec(const struct cred *subj_cred,
         * onexec permission is linked to exec with a standard pairing
         * exec\0change_profile
         */
-       state = aa_dfa_null_transition(rules->file.dfa, state);
+       state = aa_dfa_null_transition(rules->file->dfa, state);
        error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
                                     state, &perms);
        if (error) {
@@ -1300,7 +1300,7 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
 
        if (!error)
                error = change_profile_perms(profile, target, stack, request,
-                                            rules->file.start[AA_CLASS_FILE],
+                                            rules->file->start[AA_CLASS_FILE],
                                             perms);
        if (error)
                error = aa_audit_file(subj_cred, profile, perms, op, request,
index 48afcef..c03eb7c 100644 (file)
@@ -236,7 +236,7 @@ static int __aa_path_perm(const char *op, const struct cred *subj_cred,
 
        if (profile_unconfined(profile))
                return 0;
-       aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
+       aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE],
                     name, cond, perms);
        if (request & ~perms->allow)
                e = -EACCES;
@@ -353,16 +353,16 @@ static int profile_path_link(const struct cred *subj_cred,
 
        error = -EACCES;
        /* aa_str_perms - handles the case of the dfa being NULL */
-       state = aa_str_perms(&(rules->file),
-                            rules->file.start[AA_CLASS_FILE], lname,
+       state = aa_str_perms(rules->file,
+                            rules->file->start[AA_CLASS_FILE], lname,
                             cond, &lperms);
 
        if (!(lperms.allow & AA_MAY_LINK))
                goto audit;
 
        /* test to see if target can be paired with link */
-       state = aa_dfa_null_transition(rules->file.dfa, state);
-       aa_str_perms(&(rules->file), state, tname, cond, &perms);
+       state = aa_dfa_null_transition(rules->file->dfa, state);
+       aa_str_perms(rules->file, state, tname, cond, &perms);
 
        /* force audit/quiet masks for link are stored in the second entry
         * in the link pair.
@@ -384,7 +384,7 @@ static int profile_path_link(const struct cred *subj_cred,
        /* Do link perm subset test requiring allowed permission on link are
         * a subset of the allowed permissions on target.
         */
-       aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
+       aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE],
                     tname, cond, &perms);
 
        /* AA_MAY_LINK is not considered in the subset test */
index 73c8a32..d7a894b 100644 (file)
@@ -16,6 +16,8 @@
 
 #include "match.h"
 
+extern struct aa_dfa *stacksplitdfa;
+
 /*
  * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
  * which is not related to profile accesses.
index 58fbf67..4bb0405 100644 (file)
@@ -102,9 +102,6 @@ struct aa_dfa {
        struct table_header *tables[YYTD_ID_TSIZE];
 };
 
-extern struct aa_dfa *nulldfa;
-extern struct aa_dfa *stacksplitdfa;
-
 #define byte_to_byte(X) (X)
 
 #define UNPACK_ARRAY(TABLE, BLOB, LEN, TTYPE, BTYPE, NTOHX)    \
@@ -122,9 +119,6 @@ static inline size_t table_size(size_t len, size_t el_size)
        return ALIGN(sizeof(struct table_header) + len * el_size, 8);
 }
 
-int aa_setup_dfa_engine(void);
-void aa_teardown_dfa_engine(void);
-
 #define aa_state_t unsigned int
 
 struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags);
index 5572447..e69c916 100644 (file)
@@ -74,12 +74,14 @@ enum profile_mode {
 
 
 /* struct aa_policydb - match engine for a policy
+ * count: refcount for the pdb
  * dfa: dfa pattern match
  * perms: table of permissions
  * strs: table of strings, index by x
  * start: set of start states for the different classes of data
  */
 struct aa_policydb {
+       struct kref count;
        struct aa_dfa *dfa;
        struct {
                struct aa_perms *perms;
@@ -89,13 +91,36 @@ struct aa_policydb {
        aa_state_t start[AA_CLASS_LAST + 1];
 };
 
-static inline void aa_destroy_policydb(struct aa_policydb *policy)
+extern struct aa_policydb *nullpdb;
+
+struct aa_policydb *aa_alloc_pdb(gfp_t gfp);
+void aa_pdb_free_kref(struct kref *kref);
+
+/**
+ * aa_get_pdb - increment refcount on @pdb
+ * @pdb: policydb  (MAYBE NULL)
+ *
+ * Returns: pointer to @pdb if @pdb is NULL will return NULL
+ * Requires: @pdb must be held with valid refcount when called
+ */
+static inline struct aa_policydb *aa_get_pdb(struct aa_policydb *pdb)
 {
-       aa_put_dfa(policy->dfa);
-       if (policy->perms)
-               kvfree(policy->perms);
-       aa_free_str_table(&policy->trans);
+       if (pdb)
+               kref_get(&(pdb->count));
 
+       return pdb;
+}
+
+/**
+ * aa_put_pdb - put a pdb refcount
+ * @pdb: pdb to put refcount   (MAYBE NULL)
+ *
+ * Requires: if @pdb != NULL that a valid refcount be held
+ */
+static inline void aa_put_pdb(struct aa_policydb *pdb)
+{
+       if (pdb)
+               kref_put(&pdb->count, aa_pdb_free_kref);
 }
 
 static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy,
@@ -139,8 +164,8 @@ struct aa_ruleset {
        int size;
 
        /* TODO: merge policy and file */
-       struct aa_policydb policy;
-       struct aa_policydb file;
+       struct aa_policydb *policy;
+       struct aa_policydb *file;
        struct aa_caps caps;
 
        struct aa_rlimit rlimits;
@@ -159,7 +184,7 @@ struct aa_ruleset {
  */
 struct aa_attachment {
        const char *xmatch_str;
-       struct aa_policydb xmatch;
+       struct aa_policydb *xmatch;
        unsigned int xmatch_len;
        int xattr_count;
        char **xattrs;
@@ -267,10 +292,10 @@ static inline aa_state_t RULE_MEDIATES(struct aa_ruleset *rules,
                                       unsigned char class)
 {
        if (class <= AA_CLASS_LAST)
-               return rules->policy.start[class];
+               return rules->policy->start[class];
        else
-               return aa_dfa_match_len(rules->policy.dfa,
-                                       rules->policy.start[0], &class, 1);
+               return aa_dfa_match_len(rules->policy->dfa,
+                                       rules->policy->start[0], &class, 1);
 }
 
 static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF)
@@ -280,7 +305,7 @@ static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF)
 
        if (!state)
                return DFA_NOMATCH;
-       return aa_dfa_match_len(rules->policy.dfa, state, (char *) &be_af, 2);
+       return aa_dfa_match_len(rules->policy->dfa, state, (char *) &be_af, 2);
 }
 
 static inline aa_state_t ANY_RULE_MEDIATES(struct list_head *head,
index c0d0dbd..0cdf434 100644 (file)
@@ -92,8 +92,8 @@ static int profile_signal_perm(const struct cred *cred,
        ad->subj_cred = cred;
        ad->peer = peer;
        /* TODO: secondary cache check <profile, profile, perm> */
-       state = aa_dfa_next(rules->policy.dfa,
-                           rules->policy.start[AA_CLASS_SIGNAL],
+       state = aa_dfa_next(rules->policy->dfa,
+                           rules->policy->start[AA_CLASS_SIGNAL],
                            ad->signal);
        aa_label_match(profile, rules, peer, state, false, request, &perms);
        aa_apply_modes_to_perms(profile, &perms);
index 8a74e89..c71e461 100644 (file)
@@ -1270,14 +1270,14 @@ static inline aa_state_t match_component(struct aa_profile *profile,
        const char *ns_name;
 
        if (profile->ns == tp->ns)
-               return aa_dfa_match(rules->policy.dfa, state, tp->base.hname);
+               return aa_dfa_match(rules->policy->dfa, state, tp->base.hname);
 
        /* try matching with namespace name and then profile */
        ns_name = aa_ns_name(profile->ns, tp->ns, true);
-       state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
-       state = aa_dfa_match(rules->policy.dfa, state, ns_name);
-       state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
-       return aa_dfa_match(rules->policy.dfa, state, tp->base.hname);
+       state = aa_dfa_match_len(rules->policy->dfa, state, ":", 1);
+       state = aa_dfa_match(rules->policy->dfa, state, ns_name);
+       state = aa_dfa_match_len(rules->policy->dfa, state, ":", 1);
+       return aa_dfa_match(rules->policy->dfa, state, tp->base.hname);
 }
 
 /**
@@ -1323,12 +1323,12 @@ next:
        label_for_each_cont(i, label, tp) {
                if (!aa_ns_visible(profile->ns, tp->ns, subns))
                        continue;
-               state = aa_dfa_match(rules->policy.dfa, state, "//&");
+               state = aa_dfa_match(rules->policy->dfa, state, "//&");
                state = match_component(profile, rules, tp, state);
                if (!state)
                        goto fail;
        }
-       *perms = *aa_lookup_perms(&rules->policy, state);
+       *perms = *aa_lookup_perms(rules->policy, state);
        aa_apply_modes_to_perms(profile, perms);
        if ((perms->allow & request) != request)
                return -EACCES;
@@ -1381,7 +1381,7 @@ static int label_components_match(struct aa_profile *profile,
        return 0;
 
 next:
-       tmp = *aa_lookup_perms(&rules->policy, state);
+       tmp = *aa_lookup_perms(rules->policy, state);
        aa_apply_modes_to_perms(profile, &tmp);
        aa_perms_accum(perms, &tmp);
        label_for_each_cont(i, label, tp) {
@@ -1390,7 +1390,7 @@ next:
                state = match_component(profile, rules, tp, start);
                if (!state)
                        goto fail;
-               tmp = *aa_lookup_perms(&rules->policy, state);
+               tmp = *aa_lookup_perms(rules->policy, state);
                aa_apply_modes_to_perms(profile, &tmp);
                aa_perms_accum(perms, &tmp);
        }
index c87bcca..4c198d2 100644 (file)
@@ -341,8 +341,8 @@ void aa_profile_match_label(struct aa_profile *profile,
        /* TODO: doesn't yet handle extended types */
        aa_state_t state;
 
-       state = aa_dfa_next(rules->policy.dfa,
-                           rules->policy.start[AA_CLASS_LABEL],
+       state = aa_dfa_next(rules->policy->dfa,
+                           rules->policy->start[AA_CLASS_LABEL],
                            type);
        aa_label_match(profile, rules, label, state, false, request, perms);
 }
index 9e31a93..bcfe8b9 100644 (file)
@@ -1887,6 +1887,69 @@ static int __init apparmor_nf_ip_init(void)
 __initcall(apparmor_nf_ip_init);
 #endif
 
+static char nulldfa_src[] = {
+       #include "nulldfa.in"
+};
+struct aa_dfa *nulldfa;
+
+static char stacksplitdfa_src[] = {
+       #include "stacksplitdfa.in"
+};
+struct aa_dfa *stacksplitdfa;
+struct aa_policydb *nullpdb;
+
+static int __init aa_setup_dfa_engine(void)
+{
+       int error = -ENOMEM;
+
+       nullpdb = aa_alloc_pdb(GFP_KERNEL);
+       if (!nullpdb)
+               return -ENOMEM;
+
+       nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
+                           TO_ACCEPT1_FLAG(YYTD_DATA32) |
+                           TO_ACCEPT2_FLAG(YYTD_DATA32));
+       if (IS_ERR(nulldfa)) {
+               error = PTR_ERR(nulldfa);
+               goto fail;
+       }
+       nullpdb->dfa = aa_get_dfa(nulldfa);
+       nullpdb->perms = kcalloc(2, sizeof(struct aa_perms), GFP_KERNEL);
+       if (!nullpdb->perms)
+               goto fail;
+       nullpdb->size = 2;
+
+       stacksplitdfa = aa_dfa_unpack(stacksplitdfa_src,
+                                     sizeof(stacksplitdfa_src),
+                                     TO_ACCEPT1_FLAG(YYTD_DATA32) |
+                                     TO_ACCEPT2_FLAG(YYTD_DATA32));
+       if (IS_ERR(stacksplitdfa)) {
+               error = PTR_ERR(stacksplitdfa);
+               goto fail;
+       }
+
+       return 0;
+
+fail:
+       aa_put_pdb(nullpdb);
+       aa_put_dfa(nulldfa);
+       nullpdb = NULL;
+       nulldfa = NULL;
+       stacksplitdfa = NULL;
+
+       return error;
+}
+
+static void __init aa_teardown_dfa_engine(void)
+{
+       aa_put_dfa(stacksplitdfa);
+       aa_put_dfa(nulldfa);
+       aa_put_pdb(nullpdb);
+       nullpdb = NULL;
+       stacksplitdfa = NULL;
+       nulldfa = NULL;
+}
+
 static int __init apparmor_init(void)
 {
        int error;
index 7bdcca2..517d77d 100644 (file)
 
 #define base_idx(X) ((X) & 0xffffff)
 
-static char nulldfa_src[] = {
-       #include "nulldfa.in"
-};
-struct aa_dfa *nulldfa;
-
-static char stacksplitdfa_src[] = {
-       #include "stacksplitdfa.in"
-};
-struct aa_dfa *stacksplitdfa;
-
-int __init aa_setup_dfa_engine(void)
-{
-       int error;
-
-       nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
-                               TO_ACCEPT1_FLAG(YYTD_DATA32) |
-                               TO_ACCEPT2_FLAG(YYTD_DATA32));
-       if (IS_ERR(nulldfa)) {
-               error = PTR_ERR(nulldfa);
-               nulldfa = NULL;
-               return error;
-       }
-
-       stacksplitdfa = aa_dfa_unpack(stacksplitdfa_src,
-                                     sizeof(stacksplitdfa_src),
-                                     TO_ACCEPT1_FLAG(YYTD_DATA32) |
-                                     TO_ACCEPT2_FLAG(YYTD_DATA32));
-       if (IS_ERR(stacksplitdfa)) {
-               aa_put_dfa(nulldfa);
-               nulldfa = NULL;
-               error = PTR_ERR(stacksplitdfa);
-               stacksplitdfa = NULL;
-               return error;
-       }
-
-       return 0;
-}
-
-void __init aa_teardown_dfa_engine(void)
-{
-       aa_put_dfa(stacksplitdfa);
-       aa_put_dfa(nulldfa);
-}
-
 /**
  * unpack_table - unpack a dfa table (one of accept, default, base, next check)
  * @blob: data to unpack (NOT NULL)
index 2bb77aa..3455dd4 100644 (file)
@@ -332,8 +332,8 @@ static int match_mnt_path_str(const struct cred *subj_cred,
        }
 
        error = -EACCES;
-       pos = do_match_mnt(&rules->policy,
-                          rules->policy.start[AA_CLASS_MOUNT],
+       pos = do_match_mnt(rules->policy,
+                          rules->policy->start[AA_CLASS_MOUNT],
                           mntpnt, devname, type, flags, data, binary, &perms);
        if (pos) {
                info = mnt_info_table[pos];
@@ -606,10 +606,10 @@ static int profile_umount(const struct cred *subj_cred,
        if (error)
                goto audit;
 
-       state = aa_dfa_match(rules->policy.dfa,
-                            rules->policy.start[AA_CLASS_MOUNT],
+       state = aa_dfa_match(rules->policy->dfa,
+                            rules->policy->start[AA_CLASS_MOUNT],
                             name);
-       perms = *aa_lookup_perms(&rules->policy, state);
+       perms = *aa_lookup_perms(rules->policy, state);
        if (AA_MAY_UMOUNT & ~perms.allow)
                error = -EACCES;
 
@@ -680,12 +680,12 @@ static struct aa_label *build_pivotroot(const struct cred *subj_cred,
                goto audit;
 
        error = -EACCES;
-       state = aa_dfa_match(rules->policy.dfa,
-                            rules->policy.start[AA_CLASS_MOUNT],
+       state = aa_dfa_match(rules->policy->dfa,
+                            rules->policy->start[AA_CLASS_MOUNT],
                             new_name);
-       state = aa_dfa_null_transition(rules->policy.dfa, state);
-       state = aa_dfa_match(rules->policy.dfa, state, old_name);
-       perms = *aa_lookup_perms(&rules->policy, state);
+       state = aa_dfa_null_transition(rules->policy->dfa, state);
+       state = aa_dfa_match(rules->policy->dfa, state, old_name);
+       perms = *aa_lookup_perms(rules->policy, state);
 
        if (AA_MAY_PIVOTROOT & perms.allow)
                error = 0;
index 704c171..87e934b 100644 (file)
@@ -127,9 +127,9 @@ int aa_profile_af_perm(struct aa_profile *profile,
 
        buffer[0] = cpu_to_be16(family);
        buffer[1] = cpu_to_be16((u16) type);
-       state = aa_dfa_match_len(rules->policy.dfa, state, (char *) &buffer,
+       state = aa_dfa_match_len(rules->policy->dfa, state, (char *) &buffer,
                                 4);
-       perms = *aa_lookup_perms(&rules->policy, state);
+       perms = *aa_lookup_perms(rules->policy, state);
        aa_apply_modes_to_perms(profile, &perms);
 
        return aa_check_perms(profile, &perms, request, ad, audit_net_cb);
index 6f80e12..0b36bd6 100644 (file)
@@ -98,6 +98,41 @@ const char *const aa_profile_mode_names[] = {
 };
 
 
+static void aa_free_pdb(struct aa_policydb *policy)
+{
+       if (policy) {
+               aa_put_dfa(policy->dfa);
+               if (policy->perms)
+                       kvfree(policy->perms);
+               aa_free_str_table(&policy->trans);
+       }
+}
+
+/**
+ * aa_pdb_free_kref - free aa_policydb by kref (called by aa_put_pdb)
+ * @kr: kref callback for freeing of a dfa  (NOT NULL)
+ */
+void aa_pdb_free_kref(struct kref *kref)
+{
+       struct aa_policydb *pdb = container_of(kref, struct aa_policydb, count);
+
+       aa_free_pdb(pdb);
+}
+
+
+struct aa_policydb *aa_alloc_pdb(gfp_t gfp)
+{
+       struct aa_policydb *pdb = kzalloc(sizeof(struct aa_policydb), gfp);
+
+       if (!pdb)
+               return NULL;
+
+       kref_init(&pdb->count);
+
+       return pdb;
+}
+
+
 /**
  * __add_profile - add a profiles to list and label tree
  * @list: list to add it to  (NOT NULL)
@@ -200,15 +235,15 @@ static void free_attachment(struct aa_attachment *attach)
        for (i = 0; i < attach->xattr_count; i++)
                kfree_sensitive(attach->xattrs[i]);
        kfree_sensitive(attach->xattrs);
-       aa_destroy_policydb(&attach->xmatch);
+       aa_put_pdb(attach->xmatch);
 }
 
 static void free_ruleset(struct aa_ruleset *rules)
 {
        int i;
 
-       aa_destroy_policydb(&rules->file);
-       aa_destroy_policydb(&rules->policy);
+       aa_put_pdb(rules->file);
+       aa_put_pdb(rules->policy);
        aa_free_cap_rules(&rules->caps);
        aa_free_rlimit_rules(&rules->rlimits);
 
@@ -590,16 +625,8 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
        /* TODO: ideally we should inherit abi from parent */
        profile->label.flags |= FLAG_NULL;
        rules = list_first_entry(&profile->rules, typeof(*rules), list);
-       rules->file.dfa = aa_get_dfa(nulldfa);
-       rules->file.perms = kcalloc(2, sizeof(struct aa_perms), gfp);
-       if (!rules->file.perms)
-               goto fail;
-       rules->file.size = 2;
-       rules->policy.dfa = aa_get_dfa(nulldfa);
-       rules->policy.perms = kcalloc(2, sizeof(struct aa_perms), gfp);
-       if (!rules->policy.perms)
-               goto fail;
-       rules->policy.size = 2;
+       rules->file = aa_get_pdb(nullpdb);
+       rules->policy = aa_get_pdb(nullpdb);
 
        if (parent) {
                profile->path_flags = parent->path_flags;
@@ -610,11 +637,6 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
        }
 
        return profile;
-
-fail:
-       aa_free_profile(profile);
-
-       return NULL;
 }
 
 /**
index 1eb98d6..3fad34b 100644 (file)
@@ -703,24 +703,29 @@ fail_reset:
        return -EPROTO;
 }
 
-static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
+static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
                      bool required_dfa, bool required_trans,
                      const char **info)
 {
+       struct aa_policydb *pdb;
        void *pos = e->pos;
        int i, flags, error = -EPROTO;
        ssize_t size;
 
-       size = unpack_perms_table(e, &policy->perms);
+       pdb = aa_alloc_pdb(GFP_KERNEL);
+       if (!pdb)
+               return -ENOMEM;
+
+       size = unpack_perms_table(e, &pdb->perms);
        if (size < 0) {
                error = size;
-               policy->perms = NULL;
+               pdb->perms = NULL;
                *info = "failed to unpack - perms";
                goto fail;
        }
-       policy->size = size;
+       pdb->size = size;
 
-       if (policy->perms) {
+       if (pdb->perms) {
                /* perms table present accept is index */
                flags = TO_ACCEPT1_FLAG(YYTD_DATA32);
        } else {
@@ -729,13 +734,13 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
                        TO_ACCEPT2_FLAG(YYTD_DATA32);
        }
 
-       policy->dfa = unpack_dfa(e, flags);
-       if (IS_ERR(policy->dfa)) {
-               error = PTR_ERR(policy->dfa);
-               policy->dfa = NULL;
+       pdb->dfa = unpack_dfa(e, flags);
+       if (IS_ERR(pdb->dfa)) {
+               error = PTR_ERR(pdb->dfa);
+               pdb->dfa = NULL;
                *info = "failed to unpack - dfa";
                goto fail;
-       } else if (!policy->dfa) {
+       } else if (!pdb->dfa) {
                if (required_dfa) {
                        *info = "missing required dfa";
                        goto fail;
@@ -749,18 +754,18 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
         * sadly start was given different names for file and policydb
         * but since it is optional we can try both
         */
-       if (!aa_unpack_u32(e, &policy->start[0], "start"))
+       if (!aa_unpack_u32(e, &pdb->start[0], "start"))
                /* default start state */
-               policy->start[0] = DFA_START;
-       if (!aa_unpack_u32(e, &policy->start[AA_CLASS_FILE], "dfa_start")) {
+               pdb->start[0] = DFA_START;
+       if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) {
                /* default start state for xmatch and file dfa */
-               policy->start[AA_CLASS_FILE] = DFA_START;
+               pdb->start[AA_CLASS_FILE] = DFA_START;
        }       /* setup class index */
        for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
-               policy->start[i] = aa_dfa_next(policy->dfa, policy->start[0],
+               pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0],
                                               i);
        }
-       if (!unpack_trans_table(e, &policy->trans) && required_trans) {
+       if (!unpack_trans_table(e, &pdb->trans) && required_trans) {
                *info = "failed to unpack profile transition table";
                goto fail;
        }
@@ -768,9 +773,11 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
        /* TODO: move compat mapping here, requires dfa merging first */
        /* TODO: move verify here, it has to be done after compat mappings */
 out:
+       *policy = pdb;
        return 0;
 
 fail:
+       aa_put_pdb(pdb);
        e->pos = pos;
        return error;
 }
@@ -854,15 +861,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        }
 
        /* neither xmatch_len not xmatch_perms are optional if xmatch is set */
-       if (profile->attach.xmatch.dfa) {
+       if (profile->attach.xmatch->dfa) {
                if (!aa_unpack_u32(e, &tmp, NULL)) {
                        info = "missing xmatch len";
                        goto fail;
                }
                profile->attach.xmatch_len = tmp;
-               profile->attach.xmatch.start[AA_CLASS_XMATCH] = DFA_START;
-               if (!profile->attach.xmatch.perms) {
-                       error = aa_compat_map_xmatch(&profile->attach.xmatch);
+               profile->attach.xmatch->start[AA_CLASS_XMATCH] = DFA_START;
+               if (!profile->attach.xmatch->perms) {
+                       error = aa_compat_map_xmatch(profile->attach.xmatch);
                        if (error) {
                                info = "failed to convert xmatch permission table";
                                goto fail;
@@ -979,16 +986,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                if (error)
                        goto fail;
                /* Fixup: drop when we get rid of start array */
-               if (aa_dfa_next(rules->policy.dfa, rules->policy.start[0],
+               if (aa_dfa_next(rules->policy->dfa, rules->policy->start[0],
                                AA_CLASS_FILE))
-                       rules->policy.start[AA_CLASS_FILE] =
-                         aa_dfa_next(rules->policy.dfa,
-                                     rules->policy.start[0],
+                       rules->policy->start[AA_CLASS_FILE] =
+                         aa_dfa_next(rules->policy->dfa,
+                                     rules->policy->start[0],
                                      AA_CLASS_FILE);
                if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
                        goto fail;
-               if (!rules->policy.perms) {
-                       error = aa_compat_map_policy(&rules->policy,
+               if (!rules->policy->perms) {
+                       error = aa_compat_map_policy(rules->policy,
                                                     e->version);
                        if (error) {
                                info = "failed to remap policydb permission table";
@@ -996,44 +1003,25 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                        }
                }
        } else {
-               rules->policy.dfa = aa_get_dfa(nulldfa);
-               rules->policy.perms = kcalloc(2, sizeof(struct aa_perms),
-                                             GFP_KERNEL);
-               if (!rules->policy.perms)
-                       goto fail;
-               rules->policy.size = 2;
+               rules->policy = aa_get_pdb(nullpdb);
        }
        /* get file rules */
        error = unpack_pdb(e, &rules->file, false, true, &info);
        if (error) {
                goto fail;
-       } else if (rules->file.dfa) {
-               if (!rules->file.perms) {
-                       error = aa_compat_map_file(&rules->file);
+       } else if (rules->file->dfa) {
+               if (!rules->file->perms) {
+                       error = aa_compat_map_file(rules->file);
                        if (error) {
                                info = "failed to remap file permission table";
                                goto fail;
                        }
                }
-       } else if (rules->policy.dfa &&
-                  rules->policy.start[AA_CLASS_FILE]) {
-               rules->file.dfa = aa_get_dfa(rules->policy.dfa);
-               rules->file.start[AA_CLASS_FILE] = rules->policy.start[AA_CLASS_FILE];
-               rules->file.perms = kcalloc(rules->policy.size,
-                                           sizeof(struct aa_perms),
-                                           GFP_KERNEL);
-               if (!rules->file.perms)
-                       goto fail;
-               memcpy(rules->file.perms, rules->policy.perms,
-                      rules->policy.size * sizeof(struct aa_perms));
-               rules->file.size = rules->policy.size;
+       } else if (rules->policy->dfa &&
+                  rules->policy->start[AA_CLASS_FILE]) {
+               rules->file = aa_get_pdb(rules->policy);
        } else {
-               rules->file.dfa = aa_get_dfa(nulldfa);
-               rules->file.perms = kcalloc(2, sizeof(struct aa_perms),
-                                           GFP_KERNEL);
-               if (!rules->file.perms)
-                       goto fail;
-               rules->file.size = 2;
+               rules->file = aa_get_pdb(nullpdb);
        }
        error = -EPROTO;
        if (aa_unpack_nameX(e, AA_STRUCT, "data")) {
@@ -1240,32 +1228,32 @@ static int verify_profile(struct aa_profile *profile)
        if (!rules)
                return 0;
 
-       if (rules->file.dfa && !verify_dfa_accept_index(rules->file.dfa,
-                                                       rules->file.size)) {
+       if (rules->file->dfa && !verify_dfa_accept_index(rules->file->dfa,
+                                                       rules->file->size)) {
                audit_iface(profile, NULL, NULL,
                            "Unpack: file Invalid named transition", NULL,
                            -EPROTO);
                return -EPROTO;
        }
-       if (rules->policy.dfa &&
-           !verify_dfa_accept_index(rules->policy.dfa, rules->policy.size)) {
+       if (rules->policy->dfa &&
+           !verify_dfa_accept_index(rules->policy->dfa, rules->policy->size)) {
                audit_iface(profile, NULL, NULL,
                            "Unpack: policy Invalid named transition", NULL,
                            -EPROTO);
                return -EPROTO;
        }
 
-       if (!verify_perms(&rules->file)) {
+       if (!verify_perms(rules->file)) {
                audit_iface(profile, NULL, NULL,
                            "Unpack: Invalid perm index", NULL, -EPROTO);
                return -EPROTO;
        }
-       if (!verify_perms(&rules->policy)) {
+       if (!verify_perms(rules->policy)) {
                audit_iface(profile, NULL, NULL,
                            "Unpack: Invalid perm index", NULL, -EPROTO);
                return -EPROTO;
        }
-       if (!verify_perms(&profile->attach.xmatch)) {
+       if (!verify_perms(profile->attach.xmatch)) {
                audit_iface(profile, NULL, NULL,
                            "Unpack: Invalid perm index", NULL, -EPROTO);
                return -EPROTO;