Merge tag 'apparmor-pr-2022-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-block.git] / security / apparmor / domain.c
index 00dc0ec066de5960f888e594355172e5668c6be8..6dd3cc5309bfe5a2daa97c11bcadcd7fbd22ee0c 100644 (file)
 #include "include/policy.h"
 #include "include/policy_ns.h"
 
-/**
- * aa_free_domain_entries - free entries in a domain table
- * @domain: the domain table to free  (MAYBE NULL)
- */
-void aa_free_domain_entries(struct aa_domain *domain)
-{
-       int i;
-       if (domain) {
-               if (!domain->table)
-                       return;
-
-               for (i = 0; i < domain->size; i++)
-                       kfree_sensitive(domain->table[i]);
-               kfree_sensitive(domain->table);
-               domain->table = NULL;
-       }
-}
-
 /**
  * may_change_ptraced_domain - check if can change profile on ptraced task
  * @to_label: profile to change to  (NOT NULL)
@@ -95,23 +77,25 @@ out:
  * If a subns profile is not to be matched should be prescreened with
  * visibility test.
  */
-static inline unsigned int match_component(struct aa_profile *profile,
-                                          struct aa_profile *tp,
-                                          bool stack, unsigned int state)
+static inline aa_state_t match_component(struct aa_profile *profile,
+                                        struct aa_profile *tp,
+                                        bool stack, aa_state_t state)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        const char *ns_name;
 
        if (stack)
-               state = aa_dfa_match(profile->file.dfa, state, "&");
+               state = aa_dfa_match(rules->file.dfa, state, "&");
        if (profile->ns == tp->ns)
-               return aa_dfa_match(profile->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(profile->file.dfa, state, ":", 1);
-       state = aa_dfa_match(profile->file.dfa, state, ns_name);
-       state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
-       return aa_dfa_match(profile->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);
 }
 
 /**
@@ -132,9 +116,11 @@ static inline unsigned int match_component(struct aa_profile *profile,
  */
 static int label_compound_match(struct aa_profile *profile,
                                struct aa_label *label, bool stack,
-                               unsigned int state, bool subns, u32 request,
+                               aa_state_t state, bool subns, u32 request,
                                struct aa_perms *perms)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_profile *tp;
        struct label_it i;
        struct path_cond cond = { };
@@ -157,12 +143,12 @@ next:
        label_for_each_cont(i, label, tp) {
                if (!aa_ns_visible(profile->ns, tp->ns, subns))
                        continue;
-               state = aa_dfa_match(profile->file.dfa, state, "//&");
+               state = aa_dfa_match(rules->file.dfa, state, "//&");
                state = match_component(profile, tp, false, state);
                if (!state)
                        goto fail;
        }
-       *perms = aa_compute_fperms(profile->file.dfa, state, &cond);
+       *perms = *(aa_lookup_fperms(&(rules->file), state, &cond));
        aa_apply_modes_to_perms(profile, perms);
        if ((perms->allow & request) != request)
                return -EACCES;
@@ -192,14 +178,16 @@ fail:
  */
 static int label_components_match(struct aa_profile *profile,
                                  struct aa_label *label, bool stack,
-                                 unsigned int start, bool subns, u32 request,
+                                 aa_state_t start, bool subns, u32 request,
                                  struct aa_perms *perms)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_profile *tp;
        struct label_it i;
        struct aa_perms tmp;
        struct path_cond cond = { };
-       unsigned int state = 0;
+       aa_state_t state = 0;
 
        /* find first subcomponent to test */
        label_for_each(i, label, tp) {
@@ -215,7 +203,7 @@ static int label_components_match(struct aa_profile *profile,
        return 0;
 
 next:
-       tmp = aa_compute_fperms(profile->file.dfa, 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) {
@@ -224,7 +212,7 @@ next:
                state = match_component(profile, tp, stack, start);
                if (!state)
                        goto fail;
-               tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
+               tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
                aa_apply_modes_to_perms(profile, &tmp);
                aa_perms_accum(perms, &tmp);
        }
@@ -252,7 +240,7 @@ fail:
  * Returns: the state the match finished in, may be the none matching state
  */
 static int label_match(struct aa_profile *profile, struct aa_label *label,
-                      bool stack, unsigned int state, bool subns, u32 request,
+                      bool stack, aa_state_t state, bool subns, u32 request,
                       struct aa_perms *perms)
 {
        int error;
@@ -286,7 +274,7 @@ static int label_match(struct aa_profile *profile, struct aa_label *label,
  */
 static int change_profile_perms(struct aa_profile *profile,
                                struct aa_label *target, bool stack,
-                               u32 request, unsigned int start,
+                               u32 request, aa_state_t start,
                                struct aa_perms *perms)
 {
        if (profile_unconfined(profile)) {
@@ -308,44 +296,47 @@ static int change_profile_perms(struct aa_profile *profile,
  * Returns: number of extended attributes that matched, or < 0 on error
  */
 static int aa_xattrs_match(const struct linux_binprm *bprm,
-                          struct aa_profile *profile, unsigned int state)
+                          struct aa_profile *profile, aa_state_t state)
 {
        int i;
        struct dentry *d;
        char *value = NULL;
-       int size, value_size = 0, ret = profile->xattr_count;
+       struct aa_attachment *attach = &profile->attach;
+       int size, value_size = 0, ret = attach->xattr_count;
 
-       if (!bprm || !profile->xattr_count)
+       if (!bprm || !attach->xattr_count)
                return 0;
        might_sleep();
 
        /* transition from exec match to xattr set */
-       state = aa_dfa_outofband_transition(profile->xmatch, state);
+       state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
        d = bprm->file->f_path.dentry;
 
-       for (i = 0; i < profile->xattr_count; i++) {
-               size = vfs_getxattr_alloc(&init_user_ns, d, profile->xattrs[i],
+       for (i = 0; i < attach->xattr_count; i++) {
+               size = vfs_getxattr_alloc(&init_user_ns, d, attach->xattrs[i],
                                          &value, value_size, GFP_KERNEL);
                if (size >= 0) {
-                       u32 perm;
+                       u32 index, perm;
 
                        /*
                         * Check the xattr presence before value. This ensure
                         * that not present xattr can be distinguished from a 0
                         * length value or rule that matches any value
                         */
-                       state = aa_dfa_null_transition(profile->xmatch, state);
+                       state = aa_dfa_null_transition(attach->xmatch.dfa,
+                                                      state);
                        /* Check xattr value */
-                       state = aa_dfa_match_len(profile->xmatch, state, value,
-                                                size);
-                       perm = dfa_user_allow(profile->xmatch, 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;
                        if (!(perm & MAY_EXEC)) {
                                ret = -EINVAL;
                                goto out;
                        }
                }
                /* transition to next element */
-               state = aa_dfa_outofband_transition(profile->xmatch, state);
+               state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
                if (size < 0) {
                        /*
                         * No xattr match, so verify if transition to
@@ -397,6 +388,8 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm,
        rcu_read_lock();
 restart:
        list_for_each_entry_rcu(profile, head, base.list) {
+               struct aa_attachment *attach = &profile->attach;
+
                if (profile->label.flags & FLAG_NULL &&
                    &profile->label == ns_unconfined(profile->ns))
                        continue;
@@ -412,13 +405,16 @@ restart:
                 * as another profile, signal a conflict and refuse to
                 * match.
                 */
-               if (profile->xmatch) {
-                       unsigned int state, count;
-                       u32 perm;
-
-                       state = aa_dfa_leftmatch(profile->xmatch, DFA_START,
-                                                name, &count);
-                       perm = dfa_user_allow(profile->xmatch, state);
+               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],
+                                       name, &count);
+                       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;
@@ -426,7 +422,7 @@ restart:
                                if (count < candidate_len)
                                        continue;
 
-                               if (bprm && profile->xattr_count) {
+                               if (bprm && attach->xattr_count) {
                                        long rev = READ_ONCE(ns->revision);
 
                                        if (!aa_get_profile_not0(profile))
@@ -465,7 +461,7 @@ restart:
                                 * xattrs, or a longer match
                                 */
                                candidate = profile;
-                               candidate_len = max(count, profile->xmatch_len);
+                               candidate_len = max(count, attach->xmatch_len);
                                candidate_xattrs = ret;
                                conflict = false;
                        }
@@ -509,6 +505,8 @@ static const char *next_name(int xtype, const char *name)
 struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
                                const char **name)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_label *label = NULL;
        u32 xtype = xindex & AA_X_TYPE_MASK;
        int index = xindex & AA_X_INDEX_MASK;
@@ -519,7 +517,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 = profile->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;
@@ -558,6 +556,8 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
                                   const char **lookupname,
                                   const char **info)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_label *new = NULL;
        struct aa_ns *ns = profile->ns;
        u32 xtype = xindex & AA_X_TYPE_MASK;
@@ -570,7 +570,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 = profile->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);
@@ -624,9 +624,11 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
                                           char *buffer, struct path_cond *cond,
                                           bool *secure_exec)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_label *new = NULL;
        const char *info = NULL, *name = NULL, *target = NULL;
-       unsigned int state = profile->file.start;
+       aa_state_t state = rules->file.start[AA_CLASS_FILE];
        struct aa_perms perms = {};
        bool nonewprivs = false;
        int error = 0;
@@ -660,7 +662,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
        }
 
        /* find exec permissions for name */
-       state = aa_str_perms(profile->file.dfa, 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,
@@ -678,8 +680,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
                /* no exec permission - learning mode */
                struct aa_profile *new_profile = NULL;
 
-               new_profile = aa_new_null_profile(profile, false, name,
-                                                 GFP_KERNEL);
+               new_profile = aa_new_learning_profile(profile, false, name,
+                                                     GFP_KERNEL);
                if (!new_profile) {
                        error = -ENOMEM;
                        info = "could not create null profile";
@@ -722,7 +724,9 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
                          char *buffer, struct path_cond *cond,
                          bool *secure_exec)
 {
-       unsigned int state = profile->file.start;
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
+       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;
@@ -755,7 +759,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
        }
 
        /* find exec permissions for name */
-       state = aa_str_perms(profile->file.dfa, 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;
@@ -764,7 +768,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
         * onexec permission is linked to exec with a standard pairing
         * exec\0change_profile
         */
-       state = aa_dfa_null_transition(profile->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) {
@@ -1004,8 +1008,8 @@ static struct aa_label *build_change_hat(struct aa_profile *profile,
        if (!hat) {
                error = -ENOENT;
                if (COMPLAIN_MODE(profile)) {
-                       hat = aa_new_null_profile(profile, true, name,
-                                                 GFP_KERNEL);
+                       hat = aa_new_learning_profile(profile, true, name,
+                                                     GFP_KERNEL);
                        if (!hat) {
                                info = "failed null profile create";
                                error = -ENOMEM;
@@ -1261,12 +1265,15 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
                                        struct aa_label *target, bool stack,
                                        u32 request, struct aa_perms *perms)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        const char *info = NULL;
        int error = 0;
 
        if (!error)
                error = change_profile_perms(profile, target, stack, request,
-                                            profile->file.start, perms);
+                                            rules->file.start[AA_CLASS_FILE],
+                                            perms);
        if (error)
                error = aa_audit_file(profile, perms, op, request, name,
                                      NULL, target, GLOBAL_ROOT_UID, info,
@@ -1353,8 +1360,8 @@ int aa_change_profile(const char *fqname, int flags)
                    !COMPLAIN_MODE(labels_profile(label)))
                        goto audit;
                /* released below */
-               tprofile = aa_new_null_profile(labels_profile(label), false,
-                                              fqname, GFP_KERNEL);
+               tprofile = aa_new_learning_profile(labels_profile(label), false,
+                                                  fqname, GFP_KERNEL);
                if (!tprofile) {
                        info = "failed null profile create";
                        error = -ENOMEM;