Merge tag 'apparmor-pr-2022-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 14 Dec 2022 21:42:09 +0000 (13:42 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 14 Dec 2022 21:42:09 +0000 (13:42 -0800)
Pull apparmor updates from John Johansen:
 "Features:
   - switch to zstd compression for profile raw data

  Cleanups:
   - simplify obtaining the newest label on a cred
   - remove useless static inline functions
   - compute permission conversion on policy unpack
   - refactor code to share common permissins
   - refactor unpack to group policy backwards compatiblity code
   - add __init annotation to aa_{setup/teardown}_dfa_engine()

  Bug Fixes:
   - fix a memleak in
       - multi_transaction_new()
       - free_ruleset()
       - unpack_profile()
       - alloc_ns()
   - fix lockdep warning when removing a namespace
   - fix regression in stacking due to label flags
   - fix loading of child before parent
   - fix kernel-doc comments that differ from fns
   - fix spelling errors in comments
   - store return value of unpack_perms_table() to signed variable"

* tag 'apparmor-pr-2022-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (64 commits)
  apparmor: Fix uninitialized symbol 'array_size' in policy_unpack_test.c
  apparmor: Add __init annotation to aa_{setup/teardown}_dfa_engine()
  apparmor: Fix memleak in alloc_ns()
  apparmor: Fix memleak issue in unpack_profile()
  apparmor: fix a memleak in free_ruleset()
  apparmor: Fix spelling of function name in comment block
  apparmor: Use pointer to struct aa_label for lbs_cred
  AppArmor: Fix kernel-doc
  LSM: Fix kernel-doc
  AppArmor: Fix kernel-doc
  apparmor: Fix loading of child before parent
  apparmor: refactor code that alloc null profiles
  apparmor: fix obsoleted comments for aa_getprocattr() and audit_resource()
  apparmor: remove useless static inline functions
  apparmor: Fix unpack_profile() warn: passing zero to 'ERR_PTR'
  apparmor: fix uninitialize table variable in error in unpack_trans_table
  apparmor: store return value of unpack_perms_table() to signed variable
  apparmor: Fix kunit test for out of bounds array
  apparmor: Fix decompression of rawdata for read back to userspace
  apparmor: Fix undefined references to zstd_ symbols
  ...

35 files changed:
security/apparmor/Kconfig
security/apparmor/Makefile
security/apparmor/apparmorfs.c
security/apparmor/audit.c
security/apparmor/capability.c
security/apparmor/domain.c
security/apparmor/file.c
security/apparmor/include/apparmor.h
security/apparmor/include/audit.h
security/apparmor/include/cred.h
security/apparmor/include/domain.h
security/apparmor/include/file.h
security/apparmor/include/label.h
security/apparmor/include/lib.h
security/apparmor/include/match.h
security/apparmor/include/net.h
security/apparmor/include/perms.h
security/apparmor/include/policy.h
security/apparmor/include/policy_compat.h [new file with mode: 0644]
security/apparmor/include/policy_unpack.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_compat.c [new file with mode: 0644]
security/apparmor/policy_ns.c
security/apparmor/policy_unpack.c
security/apparmor/policy_unpack_test.c
security/apparmor/procattr.c
security/apparmor/resource.c
security/apparmor/task.c

index f334e7c..e0d1dd0 100644 (file)
@@ -85,8 +85,8 @@ config SECURITY_APPARMOR_HASH_DEFAULT
 config SECURITY_APPARMOR_EXPORT_BINARY
        bool "Allow exporting the raw binary policy"
        depends on SECURITY_APPARMOR_INTROSPECT_POLICY
-       select ZLIB_INFLATE
-       select ZLIB_DEFLATE
+       select ZSTD_COMPRESS
+       select ZSTD_DECOMPRESS
        default y
        help
          This option allows reading back binary policy as it was loaded.
index 065f4e3..b9c5879 100644 (file)
@@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
 
 apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \
               path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
-              resource.o secid.o file.o policy_ns.o label.o mount.o net.o
+              resource.o secid.o file.o policy_ns.o label.o mount.o net.o \
+              policy_compat.o
 apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
 
 obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o
index d066ccc..424b2c1 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/fs.h>
 #include <linux/fs_context.h>
 #include <linux/poll.h>
-#include <linux/zlib.h>
+#include <linux/zstd.h>
 #include <uapi/linux/major.h>
 #include <uapi/linux/magic.h>
 
@@ -611,29 +611,30 @@ static const struct file_operations aa_fs_ns_revision_fops = {
 static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
                             const char *match_str, size_t match_len)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_perms tmp = { };
-       struct aa_dfa *dfa;
-       unsigned int state = 0;
+       aa_state_t state = DFA_NOMATCH;
 
        if (profile_unconfined(profile))
                return;
-       if (profile->file.dfa && *match_str == AA_CLASS_FILE) {
-               dfa = profile->file.dfa;
-               state = aa_dfa_match_len(dfa, profile->file.start,
+       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_compute_fperms(dfa, state, &cond);
+                       tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
                }
-       } else if (profile->policy.dfa) {
-               if (!PROFILE_MEDIATES(profile, *match_str))
+       } else if (rules->policy.dfa) {
+               if (!RULE_MEDIATES(rules, *match_str))
                        return; /* no change to current perms */
-               dfa = profile->policy.dfa;
-               state = aa_dfa_match_len(dfa, profile->policy.start[0],
+               state = aa_dfa_match_len(rules->policy.dfa,
+                                        rules->policy.start[0],
                                         match_str, match_len);
                if (state)
-                       aa_compute_perms(dfa, state, &tmp);
+                       tmp = *aa_lookup_perms(&rules->policy, state);
        }
        aa_apply_modes_to_perms(profile, &tmp);
        aa_perms_accum_raw(perms, &tmp);
@@ -868,8 +869,10 @@ static struct multi_transaction *multi_transaction_new(struct file *file,
        if (!t)
                return ERR_PTR(-ENOMEM);
        kref_init(&t->count);
-       if (copy_from_user(t->data, buf, size))
+       if (copy_from_user(t->data, buf, size)) {
+               put_multi_transaction(t);
                return ERR_PTR(-EFAULT);
+       }
 
        return t;
 }
@@ -1090,9 +1093,9 @@ static int seq_profile_attach_show(struct seq_file *seq, void *v)
        struct aa_proxy *proxy = seq->private;
        struct aa_label *label = aa_get_label_rcu(&proxy->label);
        struct aa_profile *profile = labels_profile(label);
-       if (profile->attach)
-               seq_printf(seq, "%s\n", profile->attach);
-       else if (profile->xmatch)
+       if (profile->attach.xmatch_str)
+               seq_printf(seq, "%s\n", profile->attach.xmatch_str);
+       else if (profile->attach.xmatch.dfa)
                seq_puts(seq, "<unknown>\n");
        else
                seq_printf(seq, "%s\n", profile->base.name);
@@ -1197,10 +1200,24 @@ static int seq_ns_name_show(struct seq_file *seq, void *v)
        return 0;
 }
 
+static int seq_ns_compress_min_show(struct seq_file *seq, void *v)
+{
+       seq_printf(seq, "%d\n", AA_MIN_CLEVEL);
+       return 0;
+}
+
+static int seq_ns_compress_max_show(struct seq_file *seq, void *v)
+{
+       seq_printf(seq, "%d\n", AA_MAX_CLEVEL);
+       return 0;
+}
+
 SEQ_NS_FOPS(stacked);
 SEQ_NS_FOPS(nsstacked);
 SEQ_NS_FOPS(level);
 SEQ_NS_FOPS(name);
+SEQ_NS_FOPS(compress_min);
+SEQ_NS_FOPS(compress_max);
 
 
 /* policy/raw_data/ * file ops */
@@ -1295,42 +1312,34 @@ SEQ_RAWDATA_FOPS(revision);
 SEQ_RAWDATA_FOPS(hash);
 SEQ_RAWDATA_FOPS(compressed_size);
 
-static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen)
+static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
 {
 #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
-       if (aa_g_rawdata_compression_level != 0) {
-               int error = 0;
-               struct z_stream_s strm;
-
-               memset(&strm, 0, sizeof(strm));
-
-               strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
-               if (!strm.workspace)
-                       return -ENOMEM;
-
-               strm.next_in = src;
-               strm.avail_in = slen;
-
-               error = zlib_inflateInit(&strm);
-               if (error != Z_OK) {
-                       error = -ENOMEM;
-                       goto fail_inflate_init;
+       if (slen < dlen) {
+               const size_t wksp_len = zstd_dctx_workspace_bound();
+               zstd_dctx *ctx;
+               void *wksp;
+               size_t out_len;
+               int ret = 0;
+
+               wksp = kvzalloc(wksp_len, GFP_KERNEL);
+               if (!wksp) {
+                       ret = -ENOMEM;
+                       goto cleanup;
                }
-
-               strm.next_out = dst;
-               strm.avail_out = dlen;
-
-               error = zlib_inflate(&strm, Z_FINISH);
-               if (error != Z_STREAM_END)
-                       error = -EINVAL;
-               else
-                       error = 0;
-
-               zlib_inflateEnd(&strm);
-fail_inflate_init:
-               kvfree(strm.workspace);
-
-               return error;
+               ctx = zstd_init_dctx(wksp, wksp_len);
+               if (ctx == NULL) {
+                       ret = -ENOMEM;
+                       goto cleanup;
+               }
+               out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
+               if (zstd_is_error(out_len)) {
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+cleanup:
+               kvfree(wksp);
+               return ret;
        }
 #endif
 
@@ -1379,9 +1388,9 @@ static int rawdata_open(struct inode *inode, struct file *file)
 
        private->loaddata = loaddata;
 
-       error = deflate_decompress(loaddata->data, loaddata->compressed_size,
-                                  RAWDATA_F_DATA_BUF(private),
-                                  loaddata->size);
+       error = decompress_zstd(loaddata->data, loaddata->compressed_size,
+                               RAWDATA_F_DATA_BUF(private),
+                               loaddata->size);
        if (error)
                goto fail_decompress;
 
@@ -2392,6 +2401,8 @@ static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
        AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops),
        AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
        AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
+       AA_SFS_FILE_FOPS("raw_data_compression_level_min", 0444, &seq_ns_compress_min_fops),
+       AA_SFS_FILE_FOPS("raw_data_compression_level_max", 0444, &seq_ns_compress_max_fops),
        AA_SFS_DIR("features", aa_sfs_entry_features),
        { }
 };
index 704b0c8..5a7978a 100644 (file)
@@ -36,6 +36,43 @@ static const char *const aa_audit_type[] = {
        "AUTO"
 };
 
+static const char *const aa_class_names[] = {
+       "none",
+       "unknown",
+       "file",
+       "cap",
+       "net",
+       "rlimits",
+       "domain",
+       "mount",
+       "unknown",
+       "ptrace",
+       "signal",
+       "xmatch",
+       "unknown",
+       "unknown",
+       "net",
+       "unknown",
+       "label",
+       "posix_mqueue",
+       "io_uring",
+       "module",
+       "lsm",
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+       "unknown",
+       "X",
+       "dbus",
+};
+
+
 /*
  * Currently AppArmor auditing is fed straight into the audit framework.
  *
@@ -46,7 +83,7 @@ static const char *const aa_audit_type[] = {
  */
 
 /**
- * audit_base - core AppArmor function.
+ * audit_pre() - core AppArmor function.
  * @ab: audit buffer to fill (NOT NULL)
  * @ca: audit structure containing data to audit (NOT NULL)
  *
@@ -65,6 +102,12 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
                audit_log_format(ab, " operation=\"%s\"", aad(sa)->op);
        }
 
+       if (aad(sa)->class)
+               audit_log_format(ab, " class=\"%s\"",
+                                aad(sa)->class <= AA_CLASS_LAST ?
+                                aa_class_names[aad(sa)->class] :
+                                "unknown");
+
        if (aad(sa)->info) {
                audit_log_format(ab, " info=\"%s\"", aad(sa)->info);
                if (aad(sa)->error)
index deccea8..326a518 100644 (file)
@@ -64,6 +64,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
 static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
                      int cap, int error)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct audit_cache *ent;
        int type = AUDIT_APPARMOR_AUTO;
 
@@ -72,13 +74,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
        if (likely(!error)) {
                /* test if auditing is being forced */
                if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
-                          !cap_raised(profile->caps.audit, cap)))
+                          !cap_raised(rules->caps.audit, cap)))
                        return 0;
                type = AUDIT_APPARMOR_AUDIT;
        } else if (KILL_MODE(profile) ||
-                  cap_raised(profile->caps.kill, cap)) {
+                  cap_raised(rules->caps.kill, cap)) {
                type = AUDIT_APPARMOR_KILL;
-       } else if (cap_raised(profile->caps.quiet, cap) &&
+       } else if (cap_raised(rules->caps.quiet, cap) &&
                   AUDIT_MODE(profile) != AUDIT_NOQUIET &&
                   AUDIT_MODE(profile) != AUDIT_ALL) {
                /* quiet auditing */
@@ -114,10 +116,12 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
 static int profile_capable(struct aa_profile *profile, int cap,
                           unsigned int opts, struct common_audit_data *sa)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        int error;
 
-       if (cap_raised(profile->caps.allow, cap) &&
-           !cap_raised(profile->caps.denied, cap))
+       if (cap_raised(rules->caps.allow, cap) &&
+           !cap_raised(rules->caps.denied, cap))
                error = 0;
        else
                error = -EPERM;
@@ -148,7 +152,7 @@ int aa_capable(struct aa_label *label, int cap, unsigned int opts)
 {
        struct aa_profile *profile;
        int error = 0;
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, AA_CLASS_CAP, OP_CAPABLE);
 
        sa.u.cap = cap;
        error = fn_for_each_confined(label, profile,
index 00dc0ec..6dd3cc5 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;
index d436798..cb3d306 100644 (file)
@@ -95,7 +95,7 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
                  kuid_t ouid, const char *info, int error)
 {
        int type = AUDIT_APPARMOR_AUTO;
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, AA_CLASS_FILE, op);
 
        sa.u.tsk = NULL;
        aad(&sa)->request = request;
@@ -141,19 +141,6 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
        return aa_audit(type, profile, &sa, file_audit_cb);
 }
 
-/**
- * is_deleted - test if a file has been completely unlinked
- * @dentry: dentry of file to test for deletion  (NOT NULL)
- *
- * Returns: true if deleted else false
- */
-static inline bool is_deleted(struct dentry *dentry)
-{
-       if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
-               return true;
-       return false;
-}
-
 static int path_name(const char *op, struct aa_label *label,
                     const struct path *path, int flags, char *buffer,
                     const char **name, struct path_cond *cond, u32 request)
@@ -175,73 +162,28 @@ static int path_name(const char *op, struct aa_label *label,
 }
 
 /**
- * map_old_perms - map old file perms layout to the new layout
- * @old: permission set in old mapping
- *
- * Returns: new permission mapping
- */
-static u32 map_old_perms(u32 old)
-{
-       u32 new = old & 0xf;
-       if (old & MAY_READ)
-               new |= AA_MAY_GETATTR | AA_MAY_OPEN;
-       if (old & MAY_WRITE)
-               new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
-                      AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN;
-       if (old & 0x10)
-               new |= AA_MAY_LINK;
-       /* the old mapping lock and link_subset flags where overlaid
-        * and use was determined by part of a pair that they were in
-        */
-       if (old & 0x20)
-               new |= AA_MAY_LOCK | AA_LINK_SUBSET;
-       if (old & 0x40) /* AA_EXEC_MMAP */
-               new |= AA_EXEC_MMAP;
-
-       return new;
-}
-
-/**
- * aa_compute_fperms - convert dfa compressed perms to internal perms
- * @dfa: dfa to compute perms for   (NOT NULL)
+ * aa_lookup_fperms - convert dfa compressed perms to internal perms
+ * @dfa: dfa to lookup perms for   (NOT NULL)
  * @state: state in dfa
  * @cond:  conditions to consider  (NOT NULL)
  *
- * TODO: convert from dfa + state to permission entry, do computation conversion
- *       at load time.
+ * TODO: convert from dfa + state to permission entry
  *
- * Returns: computed permission set
+ * Returns: a pointer to a file permission set
  */
-struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
-                                 struct path_cond *cond)
+struct aa_perms default_perms = {};
+struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules,
+                                aa_state_t state, struct path_cond *cond)
 {
-       /* FIXME: change over to new dfa format
-        * currently file perms are encoded in the dfa, new format
-        * splits the permissions from the dfa.  This mapping can be
-        * done at profile load
-        */
-       struct aa_perms perms = { };
+       unsigned int index = ACCEPT_TABLE(file_rules->dfa)[state];
 
-       if (uid_eq(current_fsuid(), cond->uid)) {
-               perms.allow = map_old_perms(dfa_user_allow(dfa, state));
-               perms.audit = map_old_perms(dfa_user_audit(dfa, state));
-               perms.quiet = map_old_perms(dfa_user_quiet(dfa, state));
-               perms.xindex = dfa_user_xindex(dfa, state);
-       } else {
-               perms.allow = map_old_perms(dfa_other_allow(dfa, state));
-               perms.audit = map_old_perms(dfa_other_audit(dfa, state));
-               perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
-               perms.xindex = dfa_other_xindex(dfa, state);
-       }
-       perms.allow |= AA_MAY_GETATTR;
+       if (!(file_rules->perms))
+               return &default_perms;
 
-       /* change_profile wasn't determined by ownership in old mapping */
-       if (ACCEPT_TABLE(dfa)[state] & 0x80000000)
-               perms.allow |= AA_MAY_CHANGE_PROFILE;
-       if (ACCEPT_TABLE(dfa)[state] & 0x40000000)
-               perms.allow |= AA_MAY_ONEXEC;
+       if (uid_eq(current_fsuid(), cond->uid))
+               return &(file_rules->perms[index]);
 
-       return perms;
+       return &(file_rules->perms[index + 1]);
 }
 
 /**
@@ -254,26 +196,30 @@ struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
  *
  * Returns: the final state in @dfa when beginning @start and walking @name
  */
-unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
-                         const char *name, struct path_cond *cond,
-                         struct aa_perms *perms)
+aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
+                       const char *name, struct path_cond *cond,
+                       struct aa_perms *perms)
 {
-       unsigned int state;
-       state = aa_dfa_match(dfa, start, name);
-       *perms = aa_compute_fperms(dfa, state, cond);
+       aa_state_t state;
+       state = aa_dfa_match(file_rules->dfa, start, name);
+       *perms = *(aa_lookup_fperms(file_rules, state, cond));
 
        return state;
 }
 
-int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
-                  u32 request, struct path_cond *cond, int flags,
-                  struct aa_perms *perms)
+static int __aa_path_perm(const char *op, struct aa_profile *profile,
+                         const char *name, u32 request,
+                         struct path_cond *cond, int flags,
+                         struct aa_perms *perms)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        int e = 0;
 
        if (profile_unconfined(profile))
                return 0;
-       aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms);
+       aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
+                    name, cond, perms);
        if (request & ~perms->allow)
                e = -EACCES;
        return aa_audit_file(profile, perms, op, request, name, NULL, NULL,
@@ -360,11 +306,13 @@ static int profile_path_link(struct aa_profile *profile,
                             const struct path *target, char *buffer2,
                             struct path_cond *cond)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        const char *lname, *tname = NULL;
        struct aa_perms lperms = {}, perms;
        const char *info = NULL;
        u32 request = AA_MAY_LINK;
-       unsigned int state;
+       aa_state_t state;
        int error;
 
        error = path_name(OP_LINK, &profile->label, link, profile->path_flags,
@@ -380,15 +328,16 @@ static int profile_path_link(struct aa_profile *profile,
 
        error = -EACCES;
        /* aa_str_perms - handles the case of the dfa being NULL */
-       state = aa_str_perms(profile->file.dfa, profile->file.start, 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(profile->file.dfa, state);
-       aa_str_perms(profile->file.dfa, 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.
@@ -410,8 +359,8 @@ static int profile_path_link(struct aa_profile *profile,
        /* Do link perm subset test requiring allowed permission on link are
         * a subset of the allowed permissions on target.
         */
-       aa_str_perms(profile->file.dfa, profile->file.start, tname, cond,
-                    &perms);
+       aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
+                    tname, cond, &perms);
 
        /* AA_MAY_LINK is not considered in the subset test */
        request = lperms.allow & ~AA_MAY_LINK;
index 9c3fc36..8a81557 100644 (file)
@@ -16,7 +16,7 @@
 /*
  * Class of mediation types in the AppArmor policy db
  */
-#define AA_CLASS_ENTRY         0
+#define AA_CLASS_NONE          0
 #define AA_CLASS_UNKNOWN       1
 #define AA_CLASS_FILE          2
 #define AA_CLASS_CAP           3
 #define AA_CLASS_MOUNT         7
 #define AA_CLASS_PTRACE                9
 #define AA_CLASS_SIGNAL                10
+#define AA_CLASS_XMATCH                11
 #define AA_CLASS_NET           14
 #define AA_CLASS_LABEL         16
+#define AA_CLASS_POSIX_MQUEUE  17
+#define AA_CLASS_IO_URING      18
+#define AA_CLASS_MODULE                19
+#define AA_CLASS_DISPLAY_LSM   20
 
-#define AA_CLASS_LAST          AA_CLASS_LABEL
+#define AA_CLASS_X             31
+#define AA_CLASS_DBUS          32
+
+#define AA_CLASS_LAST          AA_CLASS_DBUS
 
 /* Control parameters settable through module/boot flags */
 extern enum audit_mode aa_g_audit;
@@ -43,4 +51,15 @@ extern bool aa_g_logsyscall;
 extern bool aa_g_paranoid_load;
 extern unsigned int aa_g_path_max;
 
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
+#define AA_MIN_CLEVEL zstd_min_clevel()
+#define AA_MAX_CLEVEL zstd_max_clevel()
+#define AA_DEFAULT_CLEVEL ZSTD_CLEVEL_DEFAULT
+#else
+#define AA_MIN_CLEVEL 0
+#define AA_MAX_CLEVEL 0
+#define AA_DEFAULT_CLEVEL 0
+#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
+
+
 #endif /* __APPARMOR_H */
index 18519a4..c328f07 100644 (file)
@@ -107,6 +107,7 @@ enum audit_type {
 struct apparmor_audit_data {
        int error;
        int type;
+       u16 class;
        const char *op;
        struct aa_label *label;
        const char *name;
@@ -155,9 +156,12 @@ struct apparmor_audit_data {
 
 /* macros for dealing with  apparmor_audit_data structure */
 #define aad(SA) ((SA)->apparmor_audit_data)
-#define DEFINE_AUDIT_DATA(NAME, T, X)                                  \
+#define DEFINE_AUDIT_DATA(NAME, T, C, X)                               \
        /* TODO: cleanup audit init so we don't need _aad = {0,} */     \
-       struct apparmor_audit_data NAME ## _aad = { .op = (X), };       \
+       struct apparmor_audit_data NAME ## _aad = {                     \
+               .class = (C),                                           \
+               .op = (X),                                              \
+       };                                                              \
        struct common_audit_data NAME =                                 \
        {                                                               \
        .type = (T),                                                    \
index 0b9ae48..58fdc72 100644 (file)
@@ -63,19 +63,6 @@ static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred)
        return aa_get_newest_label(aa_cred_raw_label(cred));
 }
 
-/**
- * __aa_task_raw_label - retrieve another task's label
- * @task: task to query  (NOT NULL)
- *
- * Returns: @task's label without incrementing its ref count
- *
- * If @task != current needs to be called in RCU safe critical section
- */
-static inline struct aa_label *__aa_task_raw_label(struct task_struct *task)
-{
-       return aa_cred_raw_label(__task_cred(task));
-}
-
 /**
  * aa_current_raw_label - find the current tasks confining label
  *
index d14928f..77f9a0e 100644 (file)
 #ifndef __AA_DOMAIN_H
 #define __AA_DOMAIN_H
 
-struct aa_domain {
-       int size;
-       char **table;
-};
-
 #define AA_CHANGE_NOFLAGS 0
 #define AA_CHANGE_TEST 1
 #define AA_CHANGE_CHILD 2
@@ -32,7 +27,6 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
 
 int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm);
 
-void aa_free_domain_entries(struct aa_domain *domain);
 int aa_change_hat(const char *hats[], int count, u64 token, int flags);
 int aa_change_profile(const char *fqname, int flags);
 
index 029cb20..5be620a 100644 (file)
@@ -17,6 +17,7 @@
 #include "match.h"
 #include "perms.h"
 
+struct aa_policydb;
 struct aa_profile;
 struct path;
 
@@ -87,18 +88,17 @@ static inline struct aa_label *aa_get_file_label(struct aa_file_ctx *ctx)
  * - exec type - which determines how the executable name and index are used
  * - flags - which modify how the destination name is applied
  */
-#define AA_X_INDEX_MASK                0x03ff
+#define AA_X_INDEX_MASK                AA_INDEX_MASK
 
-#define AA_X_TYPE_MASK         0x0c00
-#define AA_X_TYPE_SHIFT                10
-#define AA_X_NONE              0x0000
-#define AA_X_NAME              0x0400  /* use executable name px */
-#define AA_X_TABLE             0x0800  /* use a specified name ->n# */
+#define AA_X_TYPE_MASK         0x0c000000
+#define AA_X_NONE              AA_INDEX_NONE
+#define AA_X_NAME              0x04000000 /* use executable name px */
+#define AA_X_TABLE             0x08000000 /* use a specified name ->n# */
 
-#define AA_X_UNSAFE            0x1000
-#define AA_X_CHILD             0x2000  /* make >AA_X_NONE apply to children */
-#define AA_X_INHERIT           0x4000
-#define AA_X_UNCONFINED                0x8000
+#define AA_X_UNSAFE            0x10000000
+#define AA_X_CHILD             0x20000000
+#define AA_X_INHERIT           0x40000000
+#define AA_X_UNCONFINED                0x80000000
 
 /* need to make conditional which ones are being set */
 struct path_cond {
@@ -108,90 +108,17 @@ struct path_cond {
 
 #define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill)
 
-/* FIXME: split perms from dfa and match this to description
- *        also add delegation info.
- */
-static inline u16 dfa_map_xindex(u16 mask)
-{
-       u16 old_index = (mask >> 10) & 0xf;
-       u16 index = 0;
-
-       if (mask & 0x100)
-               index |= AA_X_UNSAFE;
-       if (mask & 0x200)
-               index |= AA_X_INHERIT;
-       if (mask & 0x80)
-               index |= AA_X_UNCONFINED;
-
-       if (old_index == 1) {
-               index |= AA_X_UNCONFINED;
-       } else if (old_index == 2) {
-               index |= AA_X_NAME;
-       } else if (old_index == 3) {
-               index |= AA_X_NAME | AA_X_CHILD;
-       } else if (old_index) {
-               index |= AA_X_TABLE;
-               index |= old_index - 4;
-       }
-
-       return index;
-}
-
-/*
- * map old dfa inline permissions to new format
- */
-#define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \
-                                   ((ACCEPT_TABLE(dfa)[state]) & 0x80000000))
-#define dfa_user_xbits(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 7) & 0x7f)
-#define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f)
-#define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f)
-#define dfa_user_xindex(dfa, state) \
-       (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff))
-
-#define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \
-                                     0x7f) |                           \
-                                    ((ACCEPT_TABLE(dfa)[state]) & 0x80000000))
-#define dfa_other_xbits(dfa, state) \
-       ((((ACCEPT_TABLE(dfa)[state]) >> 7) >> 14) & 0x7f)
-#define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f)
-#define dfa_other_quiet(dfa, state) \
-       ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f)
-#define dfa_other_xindex(dfa, state) \
-       dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
-
 int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
                  const char *op, u32 request, const char *name,
                  const char *target, struct aa_label *tlabel, kuid_t ouid,
                  const char *info, int error);
 
-/**
- * struct aa_file_rules - components used for file rule permissions
- * @dfa: dfa to match path names and conditionals against
- * @perms: permission table indexed by the matched state accept entry of @dfa
- * @trans: transition table for indexed by named x transitions
- *
- * File permission are determined by matching a path against @dfa and
- * then using the value of the accept entry for the matching state as
- * an index into @perms.  If a named exec transition is required it is
- * looked up in the transition table.
- */
-struct aa_file_rules {
-       unsigned int start;
-       struct aa_dfa *dfa;
-       /* struct perms perms; */
-       struct aa_domain trans;
-       /* TODO: add delegate table */
-};
-
-struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
-                                   struct path_cond *cond);
-unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
-                         const char *name, struct path_cond *cond,
-                         struct aa_perms *perms);
+struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules,
+                                 aa_state_t state, struct path_cond *cond);
+aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
+                       const char *name, struct path_cond *cond,
+                       struct aa_perms *perms);
 
-int __aa_path_perm(const char *op, struct aa_profile *profile,
-                  const char *name, u32 request, struct path_cond *cond,
-                  int flags, struct aa_perms *perms);
 int aa_path_perm(const char *op, struct aa_label *label,
                 const struct path *path, int flags, u32 request,
                 struct path_cond *cond);
@@ -204,11 +131,6 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
 
 void aa_inherit_files(const struct cred *cred, struct files_struct *files);
 
-static inline void aa_free_file_rules(struct aa_file_rules *rules)
-{
-       aa_put_dfa(rules->dfa);
-       aa_free_domain_entries(&rules->trans);
-}
 
 /**
  * aa_map_file_perms - map file flags to AppArmor permissions
index 860484c..2a72e6b 100644 (file)
@@ -261,7 +261,7 @@ for ((I).i = (I).j = 0;                                                     \
        struct label_it i;                                              \
        int ret = 0;                                                    \
        label_for_each(i, (L), profile) {                               \
-               if (PROFILE_MEDIATES(profile, (C))) {                   \
+               if (RULE_MEDIATES(&profile->rules, (C))) {              \
                        ret = 1;                                        \
                        break;                                          \
                }                                                       \
@@ -333,7 +333,7 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
 static inline const char *aa_label_strn_split(const char *str, int n)
 {
        const char *pos;
-       unsigned int state;
+       aa_state_t state;
 
        state = aa_dfa_matchn_until(stacksplitdfa, DFA_START, str, n, &pos);
        if (!ACCEPT_TABLE(stacksplitdfa)[state])
@@ -345,7 +345,7 @@ static inline const char *aa_label_strn_split(const char *str, int n)
 static inline const char *aa_label_str_split(const char *str)
 {
        const char *pos;
-       unsigned int state;
+       aa_state_t state;
 
        state = aa_dfa_match_until(stacksplitdfa, DFA_START, str, &pos);
        if (!ACCEPT_TABLE(stacksplitdfa)[state])
@@ -357,9 +357,10 @@ static inline const char *aa_label_str_split(const char *str)
 
 
 struct aa_perms;
-int aa_label_match(struct aa_profile *profile, struct aa_label *label,
-                  unsigned int state, bool subns, u32 request,
-                  struct aa_perms *perms);
+struct aa_ruleset;
+int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
+                  struct aa_label *label, aa_state_t state, bool subns,
+                  u32 request, struct aa_perms *perms);
 
 
 /**
index f42359f..f1a29ab 100644 (file)
@@ -87,8 +87,8 @@ static inline bool aa_strneq(const char *str, const char *sub, int len)
  * character which is not used in standard matching and is only
  * used to separate pairs.
  */
-static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
-                                                 unsigned int start)
+static inline aa_state_t aa_dfa_null_transition(struct aa_dfa *dfa,
+                                               aa_state_t start)
 {
        /* the null transition only needs the string's null terminator byte */
        return aa_dfa_next(dfa, start, 0);
@@ -99,6 +99,12 @@ static inline bool path_mediated_fs(struct dentry *dentry)
        return !(dentry->d_sb->s_flags & SB_NOUSER);
 }
 
+struct aa_str_table {
+       int size;
+       char **table;
+};
+
+void aa_free_str_table(struct aa_str_table *table);
 
 struct counted_str {
        struct kref count;
index 8844895..58fbf67 100644 (file)
@@ -125,19 +125,19 @@ static inline size_t table_size(size_t len, size_t el_size)
 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);
-unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
-                             const char *str, int len);
-unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
-                         const char *str);
-unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
-                        const char c);
-unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa,
-                                        unsigned int state);
-unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
-                               const char *str, const char **retpos);
-unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start,
-                                const char *str, int n, const char **retpos);
+aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
+                           const char *str, int len);
+aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start,
+                       const char *str);
+aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c);
+aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state);
+aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
+                             const char *str, const char **retpos);
+aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
+                              const char *str, int n, const char **retpos);
 
 void aa_dfa_free_kref(struct kref *kref);
 
@@ -156,8 +156,8 @@ struct match_workbuf N = {          \
        .len = 0,                       \
 }
 
-unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start,
-                             const char *str, unsigned int *count);
+aa_state_t aa_dfa_leftmatch(struct aa_dfa *dfa, aa_state_t start,
+                           const char *str, unsigned int *count);
 
 /**
  * aa_get_dfa - increment refcount on dfa @p
index aadb4b2..6fa440b 100644 (file)
@@ -59,6 +59,7 @@ struct aa_sk_ctx {
        DEFINE_AUDIT_DATA(NAME,                                           \
                          ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
                                                     LSM_AUDIT_DATA_NONE, \
+                                                    AA_CLASS_NET,        \
                          OP);                                            \
        NAME.u.net = &(NAME ## _net);                                     \
        aad(&NAME)->net.type = (T);                                       \
index 13f20c5..797a7a0 100644 (file)
@@ -65,29 +65,90 @@ extern const char *aa_file_perm_names[];
 
 struct aa_perms {
        u32 allow;
-       u32 audit;      /* set only when allow is set */
-
        u32 deny;       /* explicit deny, or conflict if allow also set */
-       u32 quiet;      /* set only when ~allow | deny */
-       u32 kill;       /* set only when ~allow | deny */
-       u32 stop;       /* set only when ~allow | deny */
 
-       u32 complain;   /* accumulates only used when ~allow & ~deny */
+       u32 subtree;    /* allow perm on full subtree only when allow is set */
        u32 cond;       /* set only when ~allow and ~deny */
 
-       u32 hide;       /* set only when  ~allow | deny */
+       u32 kill;       /* set only when ~allow | deny */
+       u32 complain;   /* accumulates only used when ~allow & ~deny */
        u32 prompt;     /* accumulates only used when ~allow & ~deny */
 
-       /* Reserved:
-        * u32 subtree; / * set only when allow is set * /
-        */
-       u16 xindex;
+       u32 audit;      /* set only when allow is set */
+       u32 quiet;      /* set only when ~allow | deny */
+       u32 hide;       /* set only when  ~allow | deny */
+
+
+       u32 xindex;
+       u32 tag;        /* tag string index, if present */
+       u32 label;      /* label string index, if present */
 };
 
+/*
+ * Indexes are broken into a 24 bit index and 8 bit flag.
+ * For the index to be valid there must be a value in the flag
+ */
+#define AA_INDEX_MASK                  0x00ffffff
+#define AA_INDEX_FLAG_MASK             0xff000000
+#define AA_INDEX_NONE                  0
+
 #define ALL_PERMS_MASK 0xffffffff
 extern struct aa_perms nullperms;
 extern struct aa_perms allperms;
 
+/**
+ * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
+ * @accum - perms struct to accumulate into
+ * @addend - perms struct to add to @accum
+ */
+static inline void aa_perms_accum_raw(struct aa_perms *accum,
+                                     struct aa_perms *addend)
+{
+       accum->deny |= addend->deny;
+       accum->allow &= addend->allow & ~addend->deny;
+       accum->audit |= addend->audit & addend->allow;
+       accum->quiet &= addend->quiet & ~addend->allow;
+       accum->kill |= addend->kill & ~addend->allow;
+       accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
+       accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
+       accum->hide &= addend->hide & ~addend->allow;
+       accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
+       accum->subtree |= addend->subtree & ~addend->deny;
+
+       if (!accum->xindex)
+               accum->xindex = addend->xindex;
+       if (!accum->tag)
+               accum->tag = addend->tag;
+       if (!accum->label)
+               accum->label = addend->label;
+}
+
+/**
+ * aa_perms_accum - accumulate perms, masking off overlapping perms
+ * @accum - perms struct to accumulate into
+ * @addend - perms struct to add to @accum
+ */
+static inline void aa_perms_accum(struct aa_perms *accum,
+                                 struct aa_perms *addend)
+{
+       accum->deny |= addend->deny;
+       accum->allow &= addend->allow & ~accum->deny;
+       accum->audit |= addend->audit & accum->allow;
+       accum->quiet &= addend->quiet & ~accum->allow;
+       accum->kill |= addend->kill & ~accum->allow;
+       accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
+       accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
+       accum->hide &= addend->hide & ~accum->allow;
+       accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
+       accum->subtree &= addend->subtree & ~accum->deny;
+
+       if (!accum->xindex)
+               accum->xindex = addend->xindex;
+       if (!accum->tag)
+               accum->tag = addend->tag;
+       if (!accum->label)
+               accum->label = addend->label;
+}
 
 #define xcheck(FN1, FN2)       \
 ({                             \
@@ -133,6 +194,9 @@ extern struct aa_perms allperms;
        xcheck(fn_for_each((L1), (P), (FN1)), fn_for_each((L2), (P), (FN2)))
 
 
+extern struct aa_perms default_perms;
+
+
 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,11 +205,10 @@ 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,
+void aa_profile_match_label(struct aa_profile *profile,
+                           struct aa_ruleset *rules, struct aa_label *label,
                            int type, u32 request, struct aa_perms *perms);
 int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
                          u32 request, int type, u32 *deny,
index 639b5b2..545f791 100644 (file)
@@ -44,6 +44,8 @@ extern const char *const aa_profile_mode_names[];
 
 #define COMPLAIN_MODE(_profile)        PROFILE_MODE((_profile), APPARMOR_COMPLAIN)
 
+#define USER_MODE(_profile)    PROFILE_MODE((_profile), APPARMOR_USER)
+
 #define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL)
 
 #define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT)
@@ -67,20 +69,47 @@ enum profile_mode {
        APPARMOR_COMPLAIN,      /* allow and log access violations */
        APPARMOR_KILL,          /* kill task on access violation */
        APPARMOR_UNCONFINED,    /* profile set to unconfined */
+       APPARMOR_USER,          /* modified complain mode to userspace */
 };
 
 
 /* struct aa_policydb - match engine for a policy
  * 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 {
-       /* Generic policy DFA specific rule types will be subsections of it */
        struct aa_dfa *dfa;
-       unsigned int start[AA_CLASS_LAST + 1];
-
+       struct {
+               struct aa_perms *perms;
+               u32 size;
+       };
+       struct aa_str_table trans;
+       aa_state_t start[AA_CLASS_LAST + 1];
 };
 
+static inline void aa_destroy_policydb(struct aa_policydb *policy)
+{
+       aa_put_dfa(policy->dfa);
+       if (policy->perms)
+               kvfree(policy->perms);
+       aa_free_str_table(&policy->trans);
+
+}
+
+static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy,
+                                              aa_state_t state)
+{
+       unsigned int index = ACCEPT_TABLE(policy->dfa)[state];
+
+       if (!(policy->perms))
+               return &default_perms;
+
+       return &(policy->perms[index]);
+}
+
+
 /* struct aa_data - generic data structure
  * key: name for retrieving this data
  * size: size of data in bytes
@@ -94,6 +123,47 @@ struct aa_data {
        struct rhash_head head;
 };
 
+/* struct aa_ruleset - data covering mediation rules
+ * @list: list the rule is on
+ * @size: the memory consumed by this ruleset
+ * @policy: general match rules governing policy
+ * @file: The set of rules governing basic file access and domain transitions
+ * @caps: capabilities for the profile
+ * @rlimits: rlimits for the profile
+ * @secmark_count: number of secmark entries
+ * @secmark: secmark label match info
+ */
+struct aa_ruleset {
+       struct list_head list;
+
+       int size;
+
+       /* TODO: merge policy and file */
+       struct aa_policydb policy;
+       struct aa_policydb file;
+       struct aa_caps caps;
+
+       struct aa_rlimit rlimits;
+
+       int secmark_count;
+       struct aa_secmark *secmark;
+};
+
+/* struct aa_attachment - data and rules for a profiles attachment
+ * @list:
+ * @xmatch_str: human readable attachment string
+ * @xmatch: optional extended matching for unconfined executables names
+ * @xmatch_len: xmatch prefix len, used to determine xmatch priority
+ * @xattr_count: number of xattrs in table
+ * @xattrs: table of xattrs
+ */
+struct aa_attachment {
+       const char *xmatch_str;
+       struct aa_policydb xmatch;
+       unsigned int xmatch_len;
+       int xattr_count;
+       char **xattrs;
+};
 
 /* struct aa_profile - basic confinement data
  * @base - base components of the profile (name, refcount, lists, lock ...)
@@ -101,18 +171,13 @@ struct aa_data {
  * @parent: parent of profile
  * @ns: namespace the profile is in
  * @rename: optional profile name that this profile renamed
- * @attach: human readable attachment string
- * @xmatch: optional extended matching for unconfined executables names
- * @xmatch_len: xmatch prefix len, used to determine xmatch priority
+ *
  * @audit: the auditing mode of the profile
  * @mode: the enforcement mode of the profile
  * @path_flags: flags controlling path generation behavior
  * @disconnected: what to prepend if attach_disconnected is specified
- * @size: the memory consumed by this profiles rules
- * @policy: general match rules governing policy
- * @file: The set of rules governing basic file access and domain transitions
- * @caps: capabilities for the profile
- * @rlimits: rlimits for the profile
+ * @attach: attachment rules for the profile
+ * @rules: rules to be enforced
  *
  * @dents: dentries for the profiles file entries in apparmorfs
  * @dirname: name of the profile dir in apparmorfs
@@ -137,26 +202,13 @@ struct aa_profile {
        struct aa_ns *ns;
        const char *rename;
 
-       const char *attach;
-       struct aa_dfa *xmatch;
-       unsigned int xmatch_len;
        enum audit_mode audit;
        long mode;
        u32 path_flags;
        const char *disconnected;
-       int size;
 
-       struct aa_policydb policy;
-       struct aa_file_rules file;
-       struct aa_caps caps;
-
-       int xattr_count;
-       char **xattrs;
-
-       struct aa_rlimit rlimits;
-
-       int secmark_count;
-       struct aa_secmark *secmark;
+       struct aa_attachment attach;
+       struct list_head rules;
 
        struct aa_loaddata *rawdata;
        unsigned char *hash;
@@ -179,10 +231,13 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
 
 
 void aa_free_proxy_kref(struct kref *kref);
+struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp);
 struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy,
                                    gfp_t gfp);
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
-                                      const char *base, gfp_t gfp);
+struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
+                                gfp_t gfp);
+struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat,
+                                          const char *base, gfp_t gfp);
 void aa_free_profile(struct aa_profile *profile);
 void aa_free_profile_kref(struct kref *kref);
 struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
@@ -217,24 +272,34 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
        return labels_profile(aa_get_newest_label(&p->label));
 }
 
-static inline unsigned int PROFILE_MEDIATES(struct aa_profile *profile,
-                                           unsigned char class)
+static inline aa_state_t RULE_MEDIATES(struct aa_ruleset *rules,
+                                      unsigned char class)
 {
        if (class <= AA_CLASS_LAST)
-               return profile->policy.start[class];
+               return rules->policy.start[class];
        else
-               return aa_dfa_match_len(profile->policy.dfa,
-                                       profile->policy.start[0], &class, 1);
+               return aa_dfa_match_len(rules->policy.dfa,
+                                       rules->policy.start[0], &class, 1);
 }
 
-static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile,
-                                              u16 AF) {
-       unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
+static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF)
+{
+       aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NET);
        __be16 be_af = cpu_to_be16(AF);
 
        if (!state)
-               return 0;
-       return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2);
+               return DFA_NOMATCH;
+       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,
+                                          unsigned char class)
+{
+       struct aa_ruleset *rule;
+
+       /* TODO: change to list walk */
+       rule = list_first_entry(head, typeof(*rule), list);
+       return RULE_MEDIATES(rule, class);
 }
 
 /**
diff --git a/security/apparmor/include/policy_compat.h b/security/apparmor/include/policy_compat.h
new file mode 100644 (file)
index 0000000..af0e174
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * AppArmor security module
+ *
+ * Code to provide backwards compatibility with older policy versions,
+ * by converting/mapping older policy formats into the newer internal
+ * formats.
+ *
+ * Copyright 2022 Canonical Ltd.
+ */
+
+#ifndef __POLICY_COMPAT_H
+#define __POLICY_COMPAT_H
+
+#include "policy.h"
+
+#define K_ABI_MASK 0x3ff
+#define FORCE_COMPLAIN_FLAG 0x800
+#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
+#define VERSION_LE(X, Y) (((X) & K_ABI_MASK) <= ((Y) & K_ABI_MASK))
+#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
+
+#define v5     5       /* base version */
+#define v6     6       /* per entry policydb mediation check */
+#define v7     7
+#define v8     8       /* full network masking */
+#define v9     9       /* xbits are used as permission bits in policydb */
+
+int aa_compat_map_xmatch(struct aa_policydb *policy);
+int aa_compat_map_policy(struct aa_policydb *policy, u32 version);
+int aa_compat_map_file(struct aa_policydb *policy);
+
+#endif /* __POLICY_COMPAT_H */
index e89b701..a6f4611 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/dcache.h>
 #include <linux/workqueue.h>
 
+
 struct aa_load_ent {
        struct list_head list;
        struct aa_profile *new;
@@ -35,6 +36,7 @@ struct aa_load_ent *aa_load_ent_alloc(void);
 #define PACKED_MODE_COMPLAIN   1
 #define PACKED_MODE_KILL       2
 #define PACKED_MODE_UNCONFINED 3
+#define PACKED_MODE_USER       4
 
 struct aa_ns;
 
@@ -170,7 +172,7 @@ bool aa_unpack_X(struct aa_ext *e, enum aa_code code);
 bool aa_unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name);
 bool aa_unpack_u32(struct aa_ext *e, u32 *data, const char *name);
 bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *name);
-size_t aa_unpack_array(struct aa_ext *e, const char *name);
+bool aa_unpack_array(struct aa_ext *e, const char *name, u16 *size);
 size_t aa_unpack_blob(struct aa_ext *e, char **blob, const char *name);
 int aa_unpack_str(struct aa_ext *e, const char **string, const char *name);
 int aa_unpack_strdup(struct aa_ext *e, char **string, const char *name);
index 3dbbc59..5acde74 100644 (file)
@@ -45,7 +45,7 @@ static const char *audit_signal_mask(u32 mask)
 }
 
 /**
- * audit_cb - call back for signal specific audit fields
+ * audit_signal_cb() - call back for signal specific audit fields
  * @ab: audit_buffer  (NOT NULL)
  * @va: audit struct to audit values of  (NOT NULL)
  */
@@ -78,19 +78,21 @@ static int profile_signal_perm(struct aa_profile *profile,
                               struct aa_label *peer, u32 request,
                               struct common_audit_data *sa)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_perms perms;
-       unsigned int state;
+       aa_state_t state;
 
        if (profile_unconfined(profile) ||
-           !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
+           !ANY_RULE_MEDIATES(&profile->rules, AA_CLASS_SIGNAL))
                return 0;
 
        aad(sa)->peer = peer;
        /* TODO: secondary cache check <profile, profile, perm> */
-       state = aa_dfa_next(profile->policy.dfa,
-                           profile->policy.start[AA_CLASS_SIGNAL],
+       state = aa_dfa_next(rules->policy.dfa,
+                           rules->policy.start[AA_CLASS_SIGNAL],
                            aad(sa)->signal);
-       aa_label_match(profile, peer, state, false, request, &perms);
+       aa_label_match(profile, rules, peer, state, false, request, &perms);
        aa_apply_modes_to_perms(profile, &perms);
        return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
 }
@@ -98,7 +100,7 @@ static int profile_signal_perm(struct aa_profile *profile,
 int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
 {
        struct aa_profile *profile;
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_SIGNAL, OP_SIGNAL);
 
        aad(&sa)->signal = map_signal_num(sig);
        aad(&sa)->unmappedsig = sig;
index 0f36ee9..8a2af96 100644 (file)
@@ -197,15 +197,18 @@ static bool vec_is_stale(struct aa_profile **vec, int n)
        return false;
 }
 
-static long union_vec_flags(struct aa_profile **vec, int n, long mask)
+static long accum_vec_flags(struct aa_profile **vec, int n)
 {
-       long u = 0;
+       long u = FLAG_UNCONFINED;
        int i;
 
        AA_BUG(!vec);
 
        for (i = 0; i < n; i++) {
-               u |= vec[i]->label.flags & mask;
+               u |= vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 |
+                                           FLAG_STALE);
+               if (!(u & vec[i]->label.flags & FLAG_UNCONFINED))
+                       u &= ~FLAG_UNCONFINED;
        }
 
        return u;
@@ -1097,8 +1100,7 @@ static struct aa_label *label_merge_insert(struct aa_label *new,
                else if (k == b->size)
                        return aa_get_label(b);
        }
-       new->flags |= union_vec_flags(new->vec, new->size, FLAG_UNCONFINED |
-                                             FLAG_DEBUG1 | FLAG_DEBUG2);
+       new->flags |= accum_vec_flags(new->vec, new->size);
        ls = labels_set(new);
        write_lock_irqsave(&ls->lock, flags);
        label = __label_insert(labels_set(new), new, false);
@@ -1254,32 +1256,27 @@ out:
        return label;
 }
 
-static inline bool label_is_visible(struct aa_profile *profile,
-                                   struct aa_label *label)
-{
-       return aa_ns_visible(profile->ns, labels_ns(label), true);
-}
-
 /* match a profile and its associated ns component if needed
  * Assumes visibility test has already been done.
  * 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,
-                                          unsigned int state)
+static inline aa_state_t match_component(struct aa_profile *profile,
+                                        struct aa_ruleset *rules,
+                                        struct aa_profile *tp,
+                                        aa_state_t state)
 {
        const char *ns_name;
 
        if (profile->ns == tp->ns)
-               return aa_dfa_match(profile->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(profile->policy.dfa, state, ":", 1);
-       state = aa_dfa_match(profile->policy.dfa, state, ns_name);
-       state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
-       return aa_dfa_match(profile->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);
 }
 
 /**
@@ -1298,8 +1295,9 @@ static inline unsigned int match_component(struct aa_profile *profile,
  *        check to be stacked.
  */
 static int label_compound_match(struct aa_profile *profile,
+                               struct aa_ruleset *rules,
                                struct aa_label *label,
-                               unsigned int state, bool subns, u32 request,
+                               aa_state_t state, bool subns, u32 request,
                                struct aa_perms *perms)
 {
        struct aa_profile *tp;
@@ -1309,7 +1307,7 @@ static int label_compound_match(struct aa_profile *profile,
        label_for_each(i, label, tp) {
                if (!aa_ns_visible(profile->ns, tp->ns, subns))
                        continue;
-               state = match_component(profile, tp, state);
+               state = match_component(profile, rules, tp, state);
                if (!state)
                        goto fail;
                goto next;
@@ -1323,12 +1321,12 @@ next:
        label_for_each_cont(i, label, tp) {
                if (!aa_ns_visible(profile->ns, tp->ns, subns))
                        continue;
-               state = aa_dfa_match(profile->policy.dfa, state, "//&");
-               state = match_component(profile, tp, state);
+               state = aa_dfa_match(rules->policy.dfa, state, "//&");
+               state = match_component(profile, rules, tp, state);
                if (!state)
                        goto fail;
        }
-       aa_compute_perms(profile->policy.dfa, state, perms);
+       *perms = *aa_lookup_perms(&rules->policy, state);
        aa_apply_modes_to_perms(profile, perms);
        if ((perms->allow & request) != request)
                return -EACCES;
@@ -1343,6 +1341,7 @@ fail:
 /**
  * label_components_match - find perms for all subcomponents of a label
  * @profile: profile to find perms for
+ * @rules: ruleset to search
  * @label: label to check access permissions for
  * @start: state to start match in
  * @subns: whether to do permission checks on components in a subns
@@ -1356,20 +1355,21 @@ fail:
  *        check to be stacked.
  */
 static int label_components_match(struct aa_profile *profile,
-                                 struct aa_label *label, unsigned int start,
+                                 struct aa_ruleset *rules,
+                                 struct aa_label *label, aa_state_t start,
                                  bool subns, u32 request,
                                  struct aa_perms *perms)
 {
        struct aa_profile *tp;
        struct label_it i;
        struct aa_perms tmp;
-       unsigned int state = 0;
+       aa_state_t state = 0;
 
        /* find first subcomponent to test */
        label_for_each(i, label, tp) {
                if (!aa_ns_visible(profile->ns, tp->ns, subns))
                        continue;
-               state = match_component(profile, tp, start);
+               state = match_component(profile, rules, tp, start);
                if (!state)
                        goto fail;
                goto next;
@@ -1379,16 +1379,16 @@ static int label_components_match(struct aa_profile *profile,
        return 0;
 
 next:
-       aa_compute_perms(profile->policy.dfa, state, &tmp);
+       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) {
                if (!aa_ns_visible(profile->ns, tp->ns, subns))
                        continue;
-               state = match_component(profile, tp, start);
+               state = match_component(profile, rules, tp, start);
                if (!state)
                        goto fail;
-               aa_compute_perms(profile->policy.dfa, state, &tmp);
+               tmp = *aa_lookup_perms(&rules->policy, state);
                aa_apply_modes_to_perms(profile, &tmp);
                aa_perms_accum(perms, &tmp);
        }
@@ -1406,6 +1406,7 @@ fail:
 /**
  * aa_label_match - do a multi-component label match
  * @profile: profile to match against (NOT NULL)
+ * @rules: ruleset to search
  * @label: label to match (NOT NULL)
  * @state: state to start in
  * @subns: whether to match subns components
@@ -1414,18 +1415,18 @@ fail:
  *
  * Returns: the state the match finished in, may be the none matching state
  */
-int aa_label_match(struct aa_profile *profile, struct aa_label *label,
-                  unsigned int state, bool subns, u32 request,
-                  struct aa_perms *perms)
+int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
+                  struct aa_label *label, aa_state_t state, bool subns,
+                  u32 request, struct aa_perms *perms)
 {
-       int error = label_compound_match(profile, label, state, subns, request,
-                                        perms);
+       int error = label_compound_match(profile, rules, label, state, subns,
+                                        request, perms);
        if (!error)
                return error;
 
        *perms = allperms;
-       return label_components_match(profile, label, state, subns, request,
-                                     perms);
+       return label_components_match(profile, rules, label, state, subns,
+                                     request, perms);
 }
 
 
index 1c72a61..a630c95 100644 (file)
@@ -25,6 +25,25 @@ struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
                             .quiet = ALL_PERMS_MASK,
                             .hide = ALL_PERMS_MASK };
 
+/**
+ * aa_free_str_table - free entries str table
+ * @str: the string table to free  (MAYBE NULL)
+ */
+void aa_free_str_table(struct aa_str_table *t)
+{
+       int i;
+
+       if (t) {
+               if (!t->table)
+                       return;
+
+               for (i = 0; i < t->size; i++)
+                       kfree_sensitive(t->table[i]);
+               kfree_sensitive(t->table);
+               t->table = NULL;
+       }
+}
+
 /**
  * aa_split_fqname - split a fqname into a profile and namespace name
  * @fqname: a full qualified name in namespace profile format (NOT NULL)
@@ -124,7 +143,7 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
 void aa_info_message(const char *str)
 {
        if (audit_enabled) {
-               DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
+               DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
 
                aad(&sa)->info = str;
                aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
@@ -308,103 +327,22 @@ void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
                perms->kill = ALL_PERMS_MASK;
        else if (COMPLAIN_MODE(profile))
                perms->complain = ALL_PERMS_MASK;
-/*
- *  TODO:
- *     else if (PROMPT_MODE(profile))
- *             perms->prompt = ALL_PERMS_MASK;
- */
-}
-
-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
- * @addend - perms struct to add to @accum
- */
-void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
-{
-       accum->deny |= addend->deny;
-       accum->allow &= addend->allow & ~addend->deny;
-       accum->audit |= addend->audit & addend->allow;
-       accum->quiet &= addend->quiet & ~addend->allow;
-       accum->kill |= addend->kill & ~addend->allow;
-       accum->stop |= addend->stop & ~addend->allow;
-       accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
-       accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
-       accum->hide &= addend->hide & ~addend->allow;
-       accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
-}
-
-/**
- * aa_perms_accum - accumulate perms, masking off overlapping perms
- * @accum - perms struct to accumulate into
- * @addend - perms struct to add to @accum
- */
-void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
-{
-       accum->deny |= addend->deny;
-       accum->allow &= addend->allow & ~accum->deny;
-       accum->audit |= addend->audit & accum->allow;
-       accum->quiet &= addend->quiet & ~accum->allow;
-       accum->kill |= addend->kill & ~accum->allow;
-       accum->stop |= addend->stop & ~accum->allow;
-       accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
-       accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
-       accum->hide &= addend->hide & ~accum->allow;
-       accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
+       else if (USER_MODE(profile))
+               perms->prompt = ALL_PERMS_MASK;
 }
 
-void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
+void aa_profile_match_label(struct aa_profile *profile,
+                           struct aa_ruleset *rules,
+                           struct aa_label *label,
                            int type, u32 request, struct aa_perms *perms)
 {
        /* TODO: doesn't yet handle extended types */
-       unsigned int state;
+       aa_state_t state;
 
-       state = aa_dfa_next(profile->policy.dfa,
-                           profile->policy.start[AA_CLASS_LABEL],
+       state = aa_dfa_next(rules->policy.dfa,
+                           rules->policy.start[AA_CLASS_LABEL],
                            type);
-       aa_label_match(profile, label, state, false, request, perms);
+       aa_label_match(profile, rules, label, state, false, request, perms);
 }
 
 
@@ -413,13 +351,16 @@ int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
                          u32 request, int type, u32 *deny,
                          struct common_audit_data *sa)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_perms perms;
 
        aad(sa)->label = &profile->label;
        aad(sa)->peer = &target->label;
        aad(sa)->request = request;
 
-       aa_profile_match_label(profile, &target->label, type, request, &perms);
+       aa_profile_match_label(profile, rules, &target->label, type, request,
+                              &perms);
        aa_apply_modes_to_perms(profile, &perms);
        *deny |= request & perms.deny;
        return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
index ff14fe0..c6728a6 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/user_namespace.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
-#include <linux/zlib.h>
+#include <linux/zstd.h>
 #include <net/sock.h>
 #include <uapi/linux/mount.h>
 
@@ -163,12 +163,15 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
                struct label_it i;
 
                label_for_each_confined(i, label, profile) {
+                       struct aa_ruleset *rules;
                        if (COMPLAIN_MODE(profile))
                                continue;
+                       rules = list_first_entry(&profile->rules,
+                                                typeof(*rules), list);
                        *effective = cap_intersect(*effective,
-                                                  profile->caps.allow);
+                                                  rules->caps.allow);
                        *permitted = cap_intersect(*permitted,
-                                                  profile->caps.allow);
+                                                  rules->caps.allow);
                }
        }
        rcu_read_unlock();
@@ -661,7 +664,8 @@ static int apparmor_setprocattr(const char *name, void *value,
        char *command, *largs = NULL, *args = value;
        size_t arg_size;
        int error;
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE,
+                         OP_SETPROCATTR);
 
        if (size == 0)
                return -EINVAL;
@@ -751,7 +755,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
 }
 
 /**
- * apparmor_bprm_committed_cred - do cleanup after new creds committed
+ * apparmor_bprm_committed_creds() - do cleanup after new creds committed
  * @bprm: binprm for the exec  (NOT NULL)
  */
 static void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
@@ -1205,10 +1209,10 @@ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb
 #endif
 
 /*
- * The cred blob is a pointer to, not an instance of, an aa_task_ctx.
+ * The cred blob is a pointer to, not an instance of, an aa_label.
  */
 struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = {
-       .lbs_cred = sizeof(struct aa_task_ctx *),
+       .lbs_cred = sizeof(struct aa_label *),
        .lbs_file = sizeof(struct aa_file_ctx),
        .lbs_task = sizeof(struct aa_task_ctx),
 };
@@ -1373,7 +1377,7 @@ module_param_named(export_binary, aa_g_export_binary, aabool, 0600);
 #endif
 
 /* policy loaddata compression level */
-int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION;
+int aa_g_rawdata_compression_level = AA_DEFAULT_CLEVEL;
 module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level,
                   aacompressionlevel, 0400);
 
@@ -1555,9 +1559,8 @@ static int param_set_aacompressionlevel(const char *val,
        error = param_set_int(val, kp);
 
        aa_g_rawdata_compression_level = clamp(aa_g_rawdata_compression_level,
-                                              Z_NO_COMPRESSION,
-                                              Z_BEST_COMPRESSION);
-       pr_info("AppArmor: policy rawdata compression level set to %u\n",
+                                              AA_MIN_CLEVEL, AA_MAX_CLEVEL);
+       pr_info("AppArmor: policy rawdata compression level set to %d\n",
                aa_g_rawdata_compression_level);
 
        return error;
index 3e9e1ea..b97ef5e 100644 (file)
@@ -31,7 +31,7 @@ static char stacksplitdfa_src[] = {
 };
 struct aa_dfa *stacksplitdfa;
 
-int aa_setup_dfa_engine(void)
+int __init aa_setup_dfa_engine(void)
 {
        int error;
 
@@ -59,7 +59,7 @@ int aa_setup_dfa_engine(void)
        return 0;
 }
 
-void aa_teardown_dfa_engine(void)
+void __init aa_teardown_dfa_engine(void)
 {
        aa_put_dfa(stacksplitdfa);
        aa_put_dfa(nulldfa);
@@ -436,17 +436,17 @@ do {                                                      \
  *
  * Returns: final state reached after input is consumed
  */
-unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
-                             const char *str, int len)
+aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
+                           const char *str, int len)
 {
        u16 *def = DEFAULT_TABLE(dfa);
        u32 *base = BASE_TABLE(dfa);
        u16 *next = NEXT_TABLE(dfa);
        u16 *check = CHECK_TABLE(dfa);
-       unsigned int state = start;
+       aa_state_t state = start;
 
-       if (state == 0)
-               return 0;
+       if (state == DFA_NOMATCH)
+               return DFA_NOMATCH;
 
        /* current state is <state>, matching character *str */
        if (dfa->tables[YYTD_ID_EC]) {
@@ -476,17 +476,16 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
  *
  * Returns: final state reached after input is consumed
  */
-unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
-                         const char *str)
+aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
 {
        u16 *def = DEFAULT_TABLE(dfa);
        u32 *base = BASE_TABLE(dfa);
        u16 *next = NEXT_TABLE(dfa);
        u16 *check = CHECK_TABLE(dfa);
-       unsigned int state = start;
+       aa_state_t state = start;
 
-       if (state == 0)
-               return 0;
+       if (state == DFA_NOMATCH)
+               return DFA_NOMATCH;
 
        /* current state is <state>, matching character *str */
        if (dfa->tables[YYTD_ID_EC]) {
@@ -515,8 +514,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
  *
  * Returns: state reach after input @c
  */
-unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
-                         const char c)
+aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
 {
        u16 *def = DEFAULT_TABLE(dfa);
        u32 *base = BASE_TABLE(dfa);
@@ -534,7 +532,7 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
        return state;
 }
 
-unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, unsigned int state)
+aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
 {
        u16 *def = DEFAULT_TABLE(dfa);
        u32 *base = BASE_TABLE(dfa);
@@ -564,7 +562,7 @@ unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, unsigned int state)
  *
  * Returns: final state reached after input is consumed
  */
-unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
+aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
                                const char *str, const char **retpos)
 {
        u16 *def = DEFAULT_TABLE(dfa);
@@ -572,10 +570,10 @@ unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
        u16 *next = NEXT_TABLE(dfa);
        u16 *check = CHECK_TABLE(dfa);
        u32 *accept = ACCEPT_TABLE(dfa);
-       unsigned int state = start, pos;
+       aa_state_t state = start, pos;
 
-       if (state == 0)
-               return 0;
+       if (state == DFA_NOMATCH)
+               return DFA_NOMATCH;
 
        /* current state is <state>, matching character *str */
        if (dfa->tables[YYTD_ID_EC]) {
@@ -625,7 +623,7 @@ unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
  *
  * Returns: final state reached after input is consumed
  */
-unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start,
+aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
                                 const char *str, int n, const char **retpos)
 {
        u16 *def = DEFAULT_TABLE(dfa);
@@ -633,11 +631,11 @@ unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start,
        u16 *next = NEXT_TABLE(dfa);
        u16 *check = CHECK_TABLE(dfa);
        u32 *accept = ACCEPT_TABLE(dfa);
-       unsigned int state = start, pos;
+       aa_state_t state = start, pos;
 
        *retpos = NULL;
-       if (state == 0)
-               return 0;
+       if (state == DFA_NOMATCH)
+               return DFA_NOMATCH;
 
        /* current state is <state>, matching character *str */
        if (dfa->tables[YYTD_ID_EC]) {
@@ -677,11 +675,11 @@ do {                                                              \
 } while (0)
 
 /* For DFAs that don't support extended tagging of states */
-static bool is_loop(struct match_workbuf *wb, unsigned int state,
+static bool is_loop(struct match_workbuf *wb, aa_state_t state,
                    unsigned int *adjust)
 {
-       unsigned int pos = wb->pos;
-       unsigned int i;
+       aa_state_t pos = wb->pos;
+       aa_state_t i;
 
        if (wb->history[pos] < state)
                return false;
@@ -700,7 +698,7 @@ static bool is_loop(struct match_workbuf *wb, unsigned int state,
        return true;
 }
 
-static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
+static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start,
                                 const char *str, struct match_workbuf *wb,
                                 unsigned int *count)
 {
@@ -708,7 +706,7 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
        u32 *base = BASE_TABLE(dfa);
        u16 *next = NEXT_TABLE(dfa);
        u16 *check = CHECK_TABLE(dfa);
-       unsigned int state = start, pos;
+       aa_state_t state = start, pos;
 
        AA_BUG(!dfa);
        AA_BUG(!str);
@@ -716,8 +714,8 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
        AA_BUG(!count);
 
        *count = 0;
-       if (state == 0)
-               return 0;
+       if (state == DFA_NOMATCH)
+               return DFA_NOMATCH;
 
        /* current state is <state>, matching character *str */
        if (dfa->tables[YYTD_ID_EC]) {
@@ -781,8 +779,8 @@ out:
  *
  * Returns: final state reached after input is consumed
  */
-unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start,
-                             const char *str, unsigned int *count)
+aa_state_t aa_dfa_leftmatch(struct aa_dfa *dfa, aa_state_t start,
+                           const char *str, unsigned int *count)
 {
        DEFINE_MATCH_WB(wb);
 
index f612472..cdfa430 100644 (file)
@@ -134,7 +134,7 @@ static int audit_mount(struct aa_profile *profile, const char *op,
                       struct aa_perms *perms, const char *info, int error)
 {
        int audit_type = AUDIT_APPARMOR_AUTO;
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_MOUNT, op);
 
        if (likely(!error)) {
                u32 mask = perms->audit;
@@ -190,7 +190,7 @@ static int audit_mount(struct aa_profile *profile, const char *op,
  *
  * Returns: next state after flags match
  */
-static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
+static aa_state_t match_mnt_flags(struct aa_dfa *dfa, aa_state_t state,
                                    unsigned long flags)
 {
        unsigned int i;
@@ -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, aa_state_t 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_state_t 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, 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, state);
                if (perms->allow & AA_MAY_MOUNT)
                        return 0;
        }
@@ -320,13 +303,15 @@ static int match_mnt_path_str(struct aa_profile *profile,
 {
        struct aa_perms perms = { };
        const char *mntpnt = NULL, *info = NULL;
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        int pos, error;
 
        AA_BUG(!profile);
        AA_BUG(!mntpath);
        AA_BUG(!buffer);
 
-       if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
+       if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
                return 0;
 
        error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
@@ -341,8 +326,8 @@ static int match_mnt_path_str(struct aa_profile *profile,
        }
 
        error = -EACCES;
-       pos = do_match_mnt(profile->policy.dfa,
-                          profile->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];
@@ -375,12 +360,14 @@ static int match_mnt(struct aa_profile *profile, const struct path *path,
                     bool binary)
 {
        const char *devname = NULL, *info = NULL;
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        int error = -EACCES;
 
        AA_BUG(!profile);
        AA_BUG(devpath && !devbuffer);
 
-       if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
+       if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
                return 0;
 
        if (devpath) {
@@ -582,15 +569,17 @@ out:
 static int profile_umount(struct aa_profile *profile, const struct path *path,
                          char *buffer)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_perms perms = { };
        const char *name = NULL, *info = NULL;
-       unsigned int state;
+       aa_state_t state;
        int error;
 
        AA_BUG(!profile);
        AA_BUG(!path);
 
-       if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
+       if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
                return 0;
 
        error = aa_path_name(path, path_flags(profile, path), buffer, &name,
@@ -598,10 +587,10 @@ static int profile_umount(struct aa_profile *profile, const struct path *path,
        if (error)
                goto audit;
 
-       state = aa_dfa_match(profile->policy.dfa,
-                            profile->policy.start[AA_CLASS_MOUNT],
+       state = aa_dfa_match(rules->policy.dfa,
+                            rules->policy.start[AA_CLASS_MOUNT],
                             name);
-       perms = compute_mnt_perms(profile->policy.dfa, state);
+       perms = *aa_lookup_perms(&rules->policy, state);
        if (AA_MAY_UMOUNT & ~perms.allow)
                error = -EACCES;
 
@@ -641,10 +630,12 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
                                        const struct path *old_path,
                                        char *old_buffer)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        const char *old_name, *new_name = NULL, *info = NULL;
        const char *trans_name = NULL;
        struct aa_perms perms = { };
-       unsigned int state;
+       aa_state_t state;
        int error;
 
        AA_BUG(!profile);
@@ -652,7 +643,7 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
        AA_BUG(!old_path);
 
        if (profile_unconfined(profile) ||
-           !PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
+           !RULE_MEDIATES(rules, AA_CLASS_MOUNT))
                return aa_get_newest_label(&profile->label);
 
        error = aa_path_name(old_path, path_flags(profile, old_path),
@@ -667,12 +658,12 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
                goto audit;
 
        error = -EACCES;
-       state = aa_dfa_match(profile->policy.dfa,
-                            profile->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(profile->policy.dfa, state);
-       state = aa_dfa_match(profile->policy.dfa, state, old_name);
-       perms = compute_mnt_perms(profile->policy.dfa, 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 7efe4d1..788be16 100644 (file)
@@ -108,8 +108,10 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
 int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
                       u32 request, u16 family, int type)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_perms perms = { };
-       unsigned int state;
+       aa_state_t state;
        __be16 buffer[2];
 
        AA_BUG(family >= AF_MAX);
@@ -117,15 +119,15 @@ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
 
        if (profile_unconfined(profile))
                return 0;
-       state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
+       state = RULE_MEDIATES(rules, AA_CLASS_NET);
        if (!state)
                return 0;
 
        buffer[0] = cpu_to_be16(family);
        buffer[1] = cpu_to_be16((u16) type);
-       state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer,
+       state = aa_dfa_match_len(rules->policy.dfa, state, (char *) &buffer,
                                 4);
-       aa_compute_perms(profile->policy.dfa, state, &perms);
+       perms = *aa_lookup_perms(&rules->policy, state);
        aa_apply_modes_to_perms(profile, &perms);
 
        return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
@@ -216,25 +218,27 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
 {
        int i, ret;
        struct aa_perms perms = { };
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
 
-       if (profile->secmark_count == 0)
+       if (rules->secmark_count == 0)
                return 0;
 
-       for (i = 0; i < profile->secmark_count; i++) {
-               if (!profile->secmark[i].secid) {
-                       ret = apparmor_secmark_init(&profile->secmark[i]);
+       for (i = 0; i < rules->secmark_count; i++) {
+               if (!rules->secmark[i].secid) {
+                       ret = apparmor_secmark_init(&rules->secmark[i]);
                        if (ret)
                                return ret;
                }
 
-               if (profile->secmark[i].secid == secid ||
-                   profile->secmark[i].secid == AA_SECID_WILDCARD) {
-                       if (profile->secmark[i].deny)
+               if (rules->secmark[i].secid == secid ||
+                   rules->secmark[i].secid == AA_SECID_WILDCARD) {
+                       if (rules->secmark[i].deny)
                                perms.deny = ALL_PERMS_MASK;
                        else
                                perms.allow = ALL_PERMS_MASK;
 
-                       if (profile->secmark[i].audit)
+                       if (rules->secmark[i].audit)
                                perms.audit = ALL_PERMS_MASK;
                }
        }
index 499c020..51e8184 100644 (file)
@@ -94,6 +94,7 @@ const char *const aa_profile_mode_names[] = {
        "complain",
        "kill",
        "unconfined",
+       "user",
 };
 
 
@@ -192,6 +193,42 @@ static void aa_free_data(void *ptr, void *arg)
        kfree_sensitive(data);
 }
 
+static void free_attachment(struct aa_attachment *attach)
+{
+       int i;
+
+       for (i = 0; i < attach->xattr_count; i++)
+               kfree_sensitive(attach->xattrs[i]);
+       kfree_sensitive(attach->xattrs);
+       aa_destroy_policydb(&attach->xmatch);
+}
+
+static void free_ruleset(struct aa_ruleset *rules)
+{
+       int i;
+
+       aa_destroy_policydb(&rules->file);
+       aa_destroy_policydb(&rules->policy);
+       aa_free_cap_rules(&rules->caps);
+       aa_free_rlimit_rules(&rules->rlimits);
+
+       for (i = 0; i < rules->secmark_count; i++)
+               kfree_sensitive(rules->secmark[i].label);
+       kfree_sensitive(rules->secmark);
+       kfree_sensitive(rules);
+}
+
+struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp)
+{
+       struct aa_ruleset *rules;
+
+       rules = kzalloc(sizeof(*rules), gfp);
+       if (rules)
+               INIT_LIST_HEAD(&rules->list);
+
+       return rules;
+}
+
 /**
  * aa_free_profile - free a profile
  * @profile: the profile to free  (MAYBE NULL)
@@ -204,8 +241,8 @@ static void aa_free_data(void *ptr, void *arg)
  */
 void aa_free_profile(struct aa_profile *profile)
 {
+       struct aa_ruleset *rule, *tmp;
        struct rhashtable *rht;
-       int i;
 
        AA_DEBUG("%s(%p)\n", __func__, profile);
 
@@ -219,19 +256,17 @@ void aa_free_profile(struct aa_profile *profile)
        aa_put_ns(profile->ns);
        kfree_sensitive(profile->rename);
 
-       aa_free_file_rules(&profile->file);
-       aa_free_cap_rules(&profile->caps);
-       aa_free_rlimit_rules(&profile->rlimits);
+       free_attachment(&profile->attach);
 
-       for (i = 0; i < profile->xattr_count; i++)
-               kfree_sensitive(profile->xattrs[i]);
-       kfree_sensitive(profile->xattrs);
-       for (i = 0; i < profile->secmark_count; i++)
-               kfree_sensitive(profile->secmark[i].label);
-       kfree_sensitive(profile->secmark);
+       /*
+        * at this point there are no tasks that can have a reference
+        * to rules
+        */
+       list_for_each_entry_safe(rule, tmp, &profile->rules, list) {
+               list_del_init(&rule->list);
+               free_ruleset(rule);
+       }
        kfree_sensitive(profile->dirname);
-       aa_put_dfa(profile->xmatch);
-       aa_put_dfa(profile->policy.dfa);
 
        if (profile->data) {
                rht = profile->data;
@@ -258,6 +293,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
                                    gfp_t gfp)
 {
        struct aa_profile *profile;
+       struct aa_ruleset *rules;
 
        /* freed by free_profile - usually through aa_put_profile */
        profile = kzalloc(struct_size(profile, label.vec, 2), gfp);
@@ -269,6 +305,14 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
        if (!aa_label_init(&profile->label, 1, gfp))
                goto fail;
 
+       INIT_LIST_HEAD(&profile->rules);
+
+       /* allocate the first ruleset, but leave it empty */
+       rules = aa_alloc_ruleset(gfp);
+       if (!rules)
+               goto fail;
+       list_add(&rules->list, &profile->rules);
+
        /* update being set needed by fs interface */
        if (!proxy) {
                proxy = aa_alloc_proxy(&profile->label, gfp);
@@ -380,6 +424,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns,
        return &profile->base;
 }
 
+/**
+ * __create_missing_ancestors - create place holders for missing ancestores
+ * @ns: namespace to lookup profile in (NOT NULL)
+ * @hname: hierarchical profile name to find parent of (NOT NULL)
+ * @gfp: type of allocation.
+ *
+ * Returns: NULL on error, parent profile on success
+ *
+ * Requires: ns mutex lock held
+ *
+ * Returns: unrefcounted parent policy or NULL if error creating
+ *          place holder profiles.
+ */
+static struct aa_policy *__create_missing_ancestors(struct aa_ns *ns,
+                                                   const char *hname,
+                                                   gfp_t gfp)
+{
+       struct aa_policy *policy;
+       struct aa_profile *parent, *profile = NULL;
+       char *split;
+
+       AA_BUG(!ns);
+       AA_BUG(!hname);
+
+       policy = &ns->base;
+
+       for (split = strstr(hname, "//"); split;) {
+               parent = profile;
+               profile = __strn_find_child(&policy->profiles, hname,
+                                           split - hname);
+               if (!profile) {
+                       const char *name = kstrndup(hname, split - hname,
+                                                   gfp);
+                       if (!name)
+                               return NULL;
+                       profile = aa_alloc_null(parent, name, gfp);
+                       kfree(name);
+                       if (!profile)
+                               return NULL;
+                       if (!parent)
+                               profile->ns = aa_get_ns(ns);
+               }
+               policy = &profile->base;
+               hname = split + 2;
+               split = strstr(hname, "//");
+       }
+       if (!profile)
+               return &ns->base;
+       return &profile->base;
+}
+
 /**
  * __lookupn_profile - lookup the profile matching @hname
  * @base: base list to start looking up profile name from  (NOT NULL)
@@ -481,8 +576,36 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
        return profile;
 }
 
+
+struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
+                                gfp_t gfp)
+{
+       struct aa_profile *profile;
+       struct aa_ruleset *rules;
+
+       profile = aa_alloc_profile(name, NULL, gfp);
+       if (!profile)
+               return NULL;
+
+       /* 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->policy.dfa = aa_get_dfa(nulldfa);
+
+       if (parent) {
+               profile->path_flags = parent->path_flags;
+
+               /* released on free_profile */
+               rcu_assign_pointer(profile->parent, aa_get_profile(parent));
+               profile->ns = aa_get_ns(parent->ns);
+       }
+
+       return profile;
+}
+
 /**
- * aa_new_null_profile - create or find a null-X learning profile
+ * aa_new_learning_profile - create or find a null-X learning profile
  * @parent: profile that caused this profile to be created (NOT NULL)
  * @hat: true if the null- learning profile is a hat
  * @base: name to base the null profile off of
@@ -499,8 +622,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
  *
  * Returns: new refcounted profile else NULL on failure
  */
-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
-                                      const char *base, gfp_t gfp)
+struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat,
+                                          const char *base, gfp_t gfp)
 {
        struct aa_profile *p, *profile;
        const char *bname;
@@ -531,21 +654,12 @@ name:
        if (profile)
                goto out;
 
-       profile = aa_alloc_profile(name, NULL, gfp);
+       profile = aa_alloc_null(parent, name, gfp);
        if (!profile)
                goto fail;
-
        profile->mode = APPARMOR_COMPLAIN;
-       profile->label.flags |= FLAG_NULL;
        if (hat)
                profile->label.flags |= FLAG_HAT;
-       profile->path_flags = parent->path_flags;
-
-       /* released on free_profile */
-       rcu_assign_pointer(profile->parent, aa_get_profile(parent));
-       profile->ns = aa_get_ns(parent->ns);
-       profile->file.dfa = aa_get_dfa(nulldfa);
-       profile->policy.dfa = aa_get_dfa(nulldfa);
 
        mutex_lock_nested(&profile->ns->lock, profile->ns->level);
        p = __find_child(&parent->base.profiles, bname);
@@ -618,7 +732,7 @@ static int audit_policy(struct aa_label *label, const char *op,
                        const char *ns_name, const char *name,
                        const char *info, int error)
 {
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, op);
 
        aad(&sa)->iface.ns = ns_name;
        aad(&sa)->name = name;
@@ -970,6 +1084,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
        /* setup parent and ns info */
        list_for_each_entry(ent, &lh, list) {
                struct aa_policy *policy;
+               struct aa_profile *p;
 
                if (aa_g_export_binary)
                        ent->new->rawdata = aa_get_loaddata(udata);
@@ -994,21 +1109,38 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
                        continue;
 
                /* no ref on policy only use inside lock */
+               p = NULL;
                policy = __lookup_parent(ns, ent->new->base.hname);
                if (!policy) {
-                       struct aa_profile *p;
+                       /* first check for parent in the load set */
                        p = __list_lookup_parent(&lh, ent->new);
                        if (!p) {
-                               error = -ENOENT;
-                               info = "parent does not exist";
-                               goto fail_lock;
+                               /*
+                                * fill in missing parent with null
+                                * profile that doesn't have
+                                * permissions. This allows for
+                                * individual profile loading where
+                                * the child is loaded before the
+                                * parent, and outside of the current
+                                * atomic set. This unfortunately can
+                                * happen with some userspaces.  The
+                                * null profile will be replaced once
+                                * the parent is loaded.
+                                */
+                               policy = __create_missing_ancestors(ns,
+                                                       ent->new->base.hname,
+                                                       GFP_KERNEL);
+                               if (!policy) {
+                                       error = -ENOENT;
+                                       info = "parent does not exist";
+                                       goto fail_lock;
+                               }
                        }
-                       rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
-               } else if (policy != &ns->base) {
-                       /* released on profile replacement or free_profile */
-                       struct aa_profile *p = (struct aa_profile *) policy;
-                       rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
                }
+               if (!p && policy != &ns->base)
+                       /* released on profile replacement or free_profile */
+                       p = (struct aa_profile *) policy;
+               rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
        }
 
        /* create new fs entries for introspection if needed */
@@ -1170,7 +1302,7 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
 
        if (!name) {
                /* remove namespace - can only happen if fqname[0] == ':' */
-               mutex_lock_nested(&ns->parent->lock, ns->level);
+               mutex_lock_nested(&ns->parent->lock, ns->parent->level);
                __aa_bump_ns_revision(ns);
                __aa_remove_ns(ns);
                mutex_unlock(&ns->parent->lock);
diff --git a/security/apparmor/policy_compat.c b/security/apparmor/policy_compat.c
new file mode 100644 (file)
index 0000000..9e52e21
--- /dev/null
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor functions for unpacking policy loaded
+ * from userspace.
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2022 Canonical Ltd.
+ *
+ * Code to provide backwards compatibility with older policy versions,
+ * by converting/mapping older policy formats into the newer internal
+ * formats.
+ */
+
+#include <linux/ctype.h>
+#include <linux/errno.h>
+
+#include "include/lib.h"
+#include "include/policy_unpack.h"
+#include "include/policy_compat.h"
+
+/* remap old accept table embedded permissions to separate permission table */
+static u32 dfa_map_xindex(u16 mask)
+{
+       u16 old_index = (mask >> 10) & 0xf;
+       u32 index = 0;
+
+       if (mask & 0x100)
+               index |= AA_X_UNSAFE;
+       if (mask & 0x200)
+               index |= AA_X_INHERIT;
+       if (mask & 0x80)
+               index |= AA_X_UNCONFINED;
+
+       if (old_index == 1) {
+               index |= AA_X_UNCONFINED;
+       } else if (old_index == 2) {
+               index |= AA_X_NAME;
+       } else if (old_index == 3) {
+               index |= AA_X_NAME | AA_X_CHILD;
+       } else if (old_index) {
+               index |= AA_X_TABLE;
+               index |= old_index - 4;
+       }
+
+       return index;
+}
+
+/*
+ * map old dfa inline permissions to new format
+ */
+#define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \
+                                   ((ACCEPT_TABLE(dfa)[state]) & 0x80000000))
+#define dfa_user_xbits(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 7) & 0x7f)
+#define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f)
+#define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f)
+#define dfa_user_xindex(dfa, state) \
+       (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff))
+
+#define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \
+                                     0x7f) |                           \
+                                    ((ACCEPT_TABLE(dfa)[state]) & 0x80000000))
+#define dfa_other_xbits(dfa, state) \
+       ((((ACCEPT_TABLE(dfa)[state]) >> 7) >> 14) & 0x7f)
+#define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f)
+#define dfa_other_quiet(dfa, state) \
+       ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f)
+#define dfa_other_xindex(dfa, state) \
+       dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
+
+/**
+ * map_old_perms - map old file perms layout to the new layout
+ * @old: permission set in old mapping
+ *
+ * Returns: new permission mapping
+ */
+static u32 map_old_perms(u32 old)
+{
+       u32 new = old & 0xf;
+
+       if (old & MAY_READ)
+               new |= AA_MAY_GETATTR | AA_MAY_OPEN;
+       if (old & MAY_WRITE)
+               new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
+                      AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN;
+       if (old & 0x10)
+               new |= AA_MAY_LINK;
+       /* the old mapping lock and link_subset flags where overlaid
+        * and use was determined by part of a pair that they were in
+        */
+       if (old & 0x20)
+               new |= AA_MAY_LOCK | AA_LINK_SUBSET;
+       if (old & 0x40) /* AA_EXEC_MMAP */
+               new |= AA_EXEC_MMAP;
+
+       return new;
+}
+
+static void compute_fperms_allow(struct aa_perms *perms, struct aa_dfa *dfa,
+                                aa_state_t state)
+{
+       perms->allow |= AA_MAY_GETATTR;
+
+       /* change_profile wasn't determined by ownership in old mapping */
+       if (ACCEPT_TABLE(dfa)[state] & 0x80000000)
+               perms->allow |= AA_MAY_CHANGE_PROFILE;
+       if (ACCEPT_TABLE(dfa)[state] & 0x40000000)
+               perms->allow |= AA_MAY_ONEXEC;
+}
+
+static struct aa_perms compute_fperms_user(struct aa_dfa *dfa,
+                                          aa_state_t state)
+{
+       struct aa_perms perms = { };
+
+       perms.allow = map_old_perms(dfa_user_allow(dfa, state));
+       perms.audit = map_old_perms(dfa_user_audit(dfa, state));
+       perms.quiet = map_old_perms(dfa_user_quiet(dfa, state));
+       perms.xindex = dfa_user_xindex(dfa, state);
+
+       compute_fperms_allow(&perms, dfa, state);
+
+       return perms;
+}
+
+static struct aa_perms compute_fperms_other(struct aa_dfa *dfa,
+                                           aa_state_t state)
+{
+       struct aa_perms perms = { };
+
+       perms.allow = map_old_perms(dfa_other_allow(dfa, state));
+       perms.audit = map_old_perms(dfa_other_audit(dfa, state));
+       perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
+       perms.xindex = dfa_other_xindex(dfa, state);
+
+       compute_fperms_allow(&perms, dfa, state);
+
+       return perms;
+}
+
+/**
+ * compute_fperms - convert dfa compressed perms to internal perms and store
+ *                 them so they can be retrieved later.
+ * @dfa: a dfa using fperms to remap to internal permissions
+ *
+ * Returns: remapped perm table
+ */
+static struct aa_perms *compute_fperms(struct aa_dfa *dfa)
+{
+       aa_state_t state;
+       unsigned 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 * 2, 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 * 2] = compute_fperms_user(dfa, state);
+               table[state * 2 + 1] = compute_fperms_other(dfa, state);
+       }
+
+       return table;
+}
+
+static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch)
+{
+       struct aa_perms *perms;
+       int state;
+       int state_count;
+
+       AA_BUG(!xmatch);
+
+       state_count = xmatch->tables[YYTD_ID_BASE]->td_lolen;
+       /* DFAs are restricted from having a state_count of less than 2 */
+       perms = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL);
+
+       /* zero init so skip the trap state (state == 0) */
+       for (state = 1; state < state_count; state++)
+               perms[state].allow = dfa_user_allow(xmatch, state);
+
+       return 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);
+}
+
+static struct aa_perms compute_perms_entry(struct aa_dfa *dfa,
+                                          aa_state_t state,
+                                          u32 version)
+{
+       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);
+
+       /*
+        * This mapping is convulated due to history.
+        * v1-v4: only file perms, which are handled by compute_fperms
+        * v5: added policydb which dropped 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 no way to force auditing on the
+        *     perms represented by the xbits
+        */
+       perms.allow |= map_other(dfa_other_allow(dfa, state));
+       if (VERSION_LE(version, v8))
+               perms.allow |= AA_MAY_LOCK;
+       else
+               perms.allow |= map_xbits(dfa_user_xbits(dfa, state));
+
+       /*
+        * for v5-v9 perm mapping in the policydb, the other set is used
+        * to extend the general perm set
+        */
+       perms.audit |= map_other(dfa_other_audit(dfa, state));
+       perms.quiet |= map_other(dfa_other_quiet(dfa, state));
+       if (VERSION_GT(version, v8))
+               perms.quiet |= map_xbits(dfa_other_xbits(dfa, state));
+
+       return perms;
+}
+
+static struct aa_perms *compute_perms(struct aa_dfa *dfa, u32 version)
+{
+       unsigned int state;
+       unsigned 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, version);
+
+       return table;
+}
+
+/**
+ * remap_dfa_accept - remap old dfa accept table to be an index
+ * @dfa: dfa to do the remapping on
+ * @factor: scaling factor for the index conversion.
+ *
+ * Used in conjunction with compute_Xperms, it converts old style perms
+ * that are encoded in the dfa accept tables to the new style where
+ * there is a permission table and the accept table is an index into
+ * the permission table.
+ */
+static void remap_dfa_accept(struct aa_dfa *dfa, unsigned int factor)
+{
+       unsigned int state;
+       unsigned int state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
+
+       AA_BUG(!dfa);
+
+       for (state = 0; state < state_count; state++)
+               ACCEPT_TABLE(dfa)[state] = state * factor;
+       kvfree(dfa->tables[YYTD_ID_ACCEPT2]);
+       dfa->tables[YYTD_ID_ACCEPT2] = NULL;
+}
+
+/* TODO: merge different dfa mappings into single map_policy fn */
+int aa_compat_map_xmatch(struct aa_policydb *policy)
+{
+       policy->perms = compute_xmatch_perms(policy->dfa);
+       if (!policy->perms)
+               return -ENOMEM;
+
+       remap_dfa_accept(policy->dfa, 1);
+
+       return 0;
+}
+
+int aa_compat_map_policy(struct aa_policydb *policy, u32 version)
+{
+       policy->perms = compute_perms(policy->dfa, version);
+       if (!policy->perms)
+               return -ENOMEM;
+
+       remap_dfa_accept(policy->dfa, 1);
+
+       return 0;
+}
+
+int aa_compat_map_file(struct aa_policydb *policy)
+{
+       policy->perms = compute_fperms(policy->dfa);
+       if (!policy->perms)
+               return -ENOMEM;
+
+       remap_dfa_accept(policy->dfa, 2);
+
+       return 0;
+}
index 43beaad..fd5b7af 100644 (file)
@@ -84,15 +84,13 @@ static struct aa_profile *alloc_unconfined(const char *name)
 {
        struct aa_profile *profile;
 
-       profile = aa_alloc_profile(name, NULL, GFP_KERNEL);
+       profile = aa_alloc_null(NULL, name, GFP_KERNEL);
        if (!profile)
                return NULL;
 
        profile->label.flags |= FLAG_IX_ON_NAME_ERROR |
                FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
        profile->mode = APPARMOR_UNCONFINED;
-       profile->file.dfa = aa_get_dfa(nulldfa);
-       profile->policy.dfa = aa_get_dfa(nulldfa);
 
        return profile;
 }
@@ -134,7 +132,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
        return ns;
 
 fail_unconfined:
-       kfree_sensitive(ns->base.hname);
+       aa_policy_destroy(&ns->base);
 fail_ns:
        kfree_sensitive(ns);
        return NULL;
index 12e535f..6691565 100644 (file)
 #include <kunit/visibility.h>
 #include <linux/ctype.h>
 #include <linux/errno.h>
-#include <linux/zlib.h>
+#include <linux/zstd.h>
 
 #include "include/apparmor.h"
 #include "include/audit.h"
 #include "include/cred.h"
 #include "include/crypto.h"
+#include "include/file.h"
 #include "include/match.h"
 #include "include/path.h"
 #include "include/policy.h"
 #include "include/policy_unpack.h"
-
-#define K_ABI_MASK 0x3ff
-#define FORCE_COMPLAIN_FLAG 0x800
-#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
-#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
-
-#define v5     5       /* base version */
-#define v6     6       /* per entry policydb mediation check */
-#define v7     7
-#define v8     8       /* full network masking */
+#include "include/policy_compat.h"
 
 /* audit callback for unpack fields */
 static void audit_cb(struct audit_buffer *ab, void *va)
@@ -71,7 +63,7 @@ static int audit_iface(struct aa_profile *new, const char *ns_name,
                       int error)
 {
        struct aa_profile *profile = labels_profile(aa_current_raw_label());
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
        if (e)
                aad(&sa)->iface.pos = e->pos - e->start;
        aad(&sa)->iface.ns = ns_name;
@@ -321,22 +313,21 @@ fail:
 }
 EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u64);
 
-VISIBLE_IF_KUNIT size_t aa_unpack_array(struct aa_ext *e, const char *name)
+VISIBLE_IF_KUNIT bool aa_unpack_array(struct aa_ext *e, const char *name, u16 *size)
 {
        void *pos = e->pos;
 
        if (aa_unpack_nameX(e, AA_ARRAY, name)) {
-               int size;
                if (!aa_inbounds(e, sizeof(u16)))
                        goto fail;
-               size = (int)le16_to_cpu(get_unaligned((__le16 *) e->pos));
+               *size = le16_to_cpu(get_unaligned((__le16 *) e->pos));
                e->pos += sizeof(u16);
-               return size;
+               return true;
        }
 
 fail:
        e->pos = pos;
-       return 0;
+       return false;
 }
 EXPORT_SYMBOL_IF_KUNIT(aa_unpack_array);
 
@@ -411,10 +402,11 @@ EXPORT_SYMBOL_IF_KUNIT(aa_unpack_strdup);
 /**
  * unpack_dfa - unpack a file rule dfa
  * @e: serialized data extent information (NOT NULL)
+ * @flags: dfa flags to check
  *
  * returns dfa or ERR_PTR or NULL if no dfa
  */
-static struct aa_dfa *unpack_dfa(struct aa_ext *e)
+static struct aa_dfa *unpack_dfa(struct aa_ext *e, int flags)
 {
        char *blob = NULL;
        size_t size;
@@ -430,8 +422,6 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
                size_t sz = blob - (char *) e->start -
                        ((e->pos - e->start) & 7);
                size_t pad = ALIGN(sz, 8) - sz;
-               int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
-                       TO_ACCEPT2_FLAG(YYTD_DATA32);
                if (aa_g_paranoid_load)
                        flags |= DFA_FLAG_VERIFY_STATES;
                dfa = aa_dfa_unpack(blob + pad, size - pad, flags);
@@ -447,28 +437,32 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
 /**
  * unpack_trans_table - unpack a profile transition table
  * @e: serialized data extent information  (NOT NULL)
- * @profile: profile to add the accept table to (NOT NULL)
+ * @table: str table to unpack to (NOT NULL)
  *
- * Returns: true if table successfully unpacked
+ * Returns: true if table successfully unpacked or not present
  */
-static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
+static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs)
 {
        void *saved_pos = e->pos;
+       char **table = NULL;
 
        /* exec table is optional */
        if (aa_unpack_nameX(e, AA_STRUCT, "xtable")) {
-               int i, size;
-
-               size = aa_unpack_array(e, NULL);
-               /* currently 4 exec bits and entries 0-3 are reserved iupcx */
-               if (size > 16 - 4)
+               u16 size;
+               int i;
+
+               if (!aa_unpack_array(e, NULL, &size))
+                       /*
+                        * Note: index into trans table array is a max
+                        * of 2^24, but unpack array can only unpack
+                        * an array of 2^16 in size atm so no need
+                        * for size check here
+                        */
                        goto fail;
-               profile->file.trans.table = kcalloc(size, sizeof(char *),
-                                                   GFP_KERNEL);
-               if (!profile->file.trans.table)
+               table = kcalloc(size, sizeof(char *), GFP_KERNEL);
+               if (!table)
                        goto fail;
 
-               profile->file.trans.size = size;
                for (i = 0; i < size; i++) {
                        char *str;
                        int c, j, pos, size2 = aa_unpack_strdup(e, &str, NULL);
@@ -477,7 +471,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
                         */
                        if (!size2)
                                goto fail;
-                       profile->file.trans.table[i] = str;
+                       table[i] = str;
                        /* verify that name doesn't start with space */
                        if (isspace(*str))
                                goto fail;
@@ -511,11 +505,14 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
                        goto fail;
                if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
                        goto fail;
+
+               strs->table = table;
+               strs->size = size;
        }
        return true;
 
 fail:
-       aa_free_domain_entries(&profile->file.trans);
+       kfree_sensitive(table);
        e->pos = saved_pos;
        return false;
 }
@@ -525,15 +522,17 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
        void *pos = e->pos;
 
        if (aa_unpack_nameX(e, AA_STRUCT, "xattrs")) {
-               int i, size;
+               u16 size;
+               int i;
 
-               size = aa_unpack_array(e, NULL);
-               profile->xattr_count = size;
-               profile->xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL);
-               if (!profile->xattrs)
+               if (!aa_unpack_array(e, NULL, &size))
+                       goto fail;
+               profile->attach.xattr_count = size;
+               profile->attach.xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL);
+               if (!profile->attach.xattrs)
                        goto fail;
                for (i = 0; i < size; i++) {
-                       if (!aa_unpack_strdup(e, &profile->xattrs[i], NULL))
+                       if (!aa_unpack_strdup(e, &profile->attach.xattrs[i], NULL))
                                goto fail;
                }
                if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
@@ -549,27 +548,29 @@ fail:
        return false;
 }
 
-static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
+static bool unpack_secmark(struct aa_ext *e, struct aa_ruleset *rules)
 {
        void *pos = e->pos;
-       int i, size;
+       u16 size;
+       int i;
 
        if (aa_unpack_nameX(e, AA_STRUCT, "secmark")) {
-               size = aa_unpack_array(e, NULL);
+               if (!aa_unpack_array(e, NULL, &size))
+                       goto fail;
 
-               profile->secmark = kcalloc(size, sizeof(struct aa_secmark),
+               rules->secmark = kcalloc(size, sizeof(struct aa_secmark),
                                           GFP_KERNEL);
-               if (!profile->secmark)
+               if (!rules->secmark)
                        goto fail;
 
-               profile->secmark_count = size;
+               rules->secmark_count = size;
 
                for (i = 0; i < size; i++) {
-                       if (!unpack_u8(e, &profile->secmark[i].audit, NULL))
+                       if (!unpack_u8(e, &rules->secmark[i].audit, NULL))
                                goto fail;
-                       if (!unpack_u8(e, &profile->secmark[i].deny, NULL))
+                       if (!unpack_u8(e, &rules->secmark[i].deny, NULL))
                                goto fail;
-                       if (!aa_unpack_strdup(e, &profile->secmark[i].label, NULL))
+                       if (!aa_unpack_strdup(e, &rules->secmark[i].label, NULL))
                                goto fail;
                }
                if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
@@ -581,39 +582,40 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
        return true;
 
 fail:
-       if (profile->secmark) {
+       if (rules->secmark) {
                for (i = 0; i < size; i++)
-                       kfree(profile->secmark[i].label);
-               kfree(profile->secmark);
-               profile->secmark_count = 0;
-               profile->secmark = NULL;
+                       kfree(rules->secmark[i].label);
+               kfree(rules->secmark);
+               rules->secmark_count = 0;
+               rules->secmark = NULL;
        }
 
        e->pos = pos;
        return false;
 }
 
-static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
+static bool unpack_rlimits(struct aa_ext *e, struct aa_ruleset *rules)
 {
        void *pos = e->pos;
 
        /* rlimits are optional */
        if (aa_unpack_nameX(e, AA_STRUCT, "rlimits")) {
-               int i, size;
+               u16 size;
+               int i;
                u32 tmp = 0;
                if (!aa_unpack_u32(e, &tmp, NULL))
                        goto fail;
-               profile->rlimits.mask = tmp;
+               rules->rlimits.mask = tmp;
 
-               size = aa_unpack_array(e, NULL);
-               if (size > RLIM_NLIMITS)
+               if (!aa_unpack_array(e, NULL, &size) ||
+                   size > RLIM_NLIMITS)
                        goto fail;
                for (i = 0; i < size; i++) {
                        u64 tmp2 = 0;
                        int a = aa_map_resource(i);
                        if (!aa_unpack_u64(e, &tmp2, NULL))
                                goto fail;
-                       profile->rlimits.limits[a].rlim_max = tmp2;
+                       rules->rlimits.limits[a].rlim_max = tmp2;
                }
                if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
                        goto fail;
@@ -627,6 +629,140 @@ fail:
        return false;
 }
 
+static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm)
+{
+       if (version != 1)
+               return false;
+
+       return  aa_unpack_u32(e, &perm->allow, NULL) &&
+               aa_unpack_u32(e, &perm->allow, NULL) &&
+               aa_unpack_u32(e, &perm->deny, NULL) &&
+               aa_unpack_u32(e, &perm->subtree, NULL) &&
+               aa_unpack_u32(e, &perm->cond, NULL) &&
+               aa_unpack_u32(e, &perm->kill, NULL) &&
+               aa_unpack_u32(e, &perm->complain, NULL) &&
+               aa_unpack_u32(e, &perm->prompt, NULL) &&
+               aa_unpack_u32(e, &perm->audit, NULL) &&
+               aa_unpack_u32(e, &perm->quiet, NULL) &&
+               aa_unpack_u32(e, &perm->hide, NULL) &&
+               aa_unpack_u32(e, &perm->xindex, NULL) &&
+               aa_unpack_u32(e, &perm->tag, NULL) &&
+               aa_unpack_u32(e, &perm->label, NULL);
+}
+
+static ssize_t unpack_perms_table(struct aa_ext *e, struct aa_perms **perms)
+{
+       void *pos = e->pos;
+       u16 size = 0;
+
+       AA_BUG(!perms);
+       /*
+        * policy perms are optional, in which case perms are embedded
+        * in the dfa accept table
+        */
+       if (aa_unpack_nameX(e, AA_STRUCT, "perms")) {
+               int i;
+               u32 version;
+
+               if (!aa_unpack_u32(e, &version, "version"))
+                       goto fail_reset;
+               if (!aa_unpack_array(e, NULL, &size))
+                       goto fail_reset;
+               *perms = kcalloc(size, sizeof(struct aa_perms), GFP_KERNEL);
+               if (!*perms)
+                       goto fail_reset;
+               for (i = 0; i < size; i++) {
+                       if (!unpack_perm(e, version, &(*perms)[i]))
+                               goto fail;
+               }
+               if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
+                       goto fail;
+               if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
+                       goto fail;
+       } else
+               *perms = NULL;
+
+       return size;
+
+fail:
+       kfree(*perms);
+fail_reset:
+       e->pos = pos;
+       return -EPROTO;
+}
+
+static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
+                     bool required_dfa, bool required_trans,
+                     const char **info)
+{
+       void *pos = e->pos;
+       int i, flags, error = -EPROTO;
+       ssize_t size;
+
+       size = unpack_perms_table(e, &policy->perms);
+       if (size < 0) {
+               error = size;
+               policy->perms = NULL;
+               *info = "failed to unpack - perms";
+               goto fail;
+       }
+       policy->size = size;
+
+       if (policy->perms) {
+               /* perms table present accept is index */
+               flags = TO_ACCEPT1_FLAG(YYTD_DATA32);
+       } else {
+               /* packed perms in accept1 and accept2 */
+               flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
+                       TO_ACCEPT2_FLAG(YYTD_DATA32);
+       }
+
+       policy->dfa = unpack_dfa(e, flags);
+       if (IS_ERR(policy->dfa)) {
+               error = PTR_ERR(policy->dfa);
+               policy->dfa = NULL;
+               *info = "failed to unpack - dfa";
+               goto fail;
+       } else if (!policy->dfa) {
+               if (required_dfa) {
+                       *info = "missing required dfa";
+                       goto fail;
+               }
+               goto out;
+       }
+
+       /*
+        * only unpack the following if a dfa is present
+        *
+        * 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"))
+               /* default start state */
+               policy->start[0] = DFA_START;
+       if (!aa_unpack_u32(e, &policy->start[AA_CLASS_FILE], "dfa_start")) {
+               /* default start state for xmatch and file dfa */
+               policy->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],
+                                              i);
+       }
+       if (!unpack_trans_table(e, &policy->trans) && required_trans) {
+               *info = "failed to unpack profile transition table";
+               goto fail;
+       }
+
+       /* TODO: move compat mapping here, requires dfa merging first */
+       /* TODO: move verify here, it has to be done after compat mappings */
+out:
+       return 0;
+
+fail:
+       e->pos = pos;
+       return error;
+}
+
 static u32 strhash(const void *data, u32 len, u32 seed)
 {
        const char * const *key = data;
@@ -651,6 +787,7 @@ static int datacmp(struct rhashtable_compare_arg *arg, const void *obj)
  */
 static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
 {
+       struct aa_ruleset *rules;
        struct aa_profile *profile = NULL;
        const char *tmpname, *tmpns = NULL, *name = NULL;
        const char *info = "failed to unpack profile";
@@ -658,7 +795,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        struct rhashtable_params params = { 0 };
        char *key = NULL;
        struct aa_data *data;
-       int i, error = -EPROTO;
+       int error = -EPROTO;
        kernel_cap_t tmpcap;
        u32 tmp;
 
@@ -677,36 +814,46 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
                if (!*ns_name) {
                        info = "out of memory";
+                       error = -ENOMEM;
                        goto fail;
                }
                name = tmpname;
        }
 
        profile = aa_alloc_profile(name, NULL, GFP_KERNEL);
-       if (!profile)
-               return ERR_PTR(-ENOMEM);
+       if (!profile) {
+               info = "out of memory";
+               error = -ENOMEM;
+               goto fail;
+       }
+       rules = list_first_entry(&profile->rules, typeof(*rules), list);
 
        /* profile renaming is optional */
        (void) aa_unpack_str(e, &profile->rename, "rename");
 
        /* attachment string is optional */
-       (void) aa_unpack_str(e, &profile->attach, "attach");
+       (void) aa_unpack_str(e, &profile->attach.xmatch_str, "attach");
 
        /* xmatch is optional and may be NULL */
-       profile->xmatch = unpack_dfa(e);
-       if (IS_ERR(profile->xmatch)) {
-               error = PTR_ERR(profile->xmatch);
-               profile->xmatch = NULL;
+       error = unpack_pdb(e, &profile->attach.xmatch, false, false, &info);
+       if (error) {
                info = "bad xmatch";
                goto fail;
        }
-       /* xmatch_len is not optional if xmatch is set */
-       if (profile->xmatch) {
+
+       /* neither xmatch_len not xmatch_perms are optional if xmatch is set */
+       if (profile->attach.xmatch.dfa) {
                if (!aa_unpack_u32(e, &tmp, NULL)) {
                        info = "missing xmatch len";
                        goto fail;
                }
-               profile->xmatch_len = tmp;
+               profile->attach.xmatch_len = tmp;
+               profile->attach.xmatch.start[AA_CLASS_XMATCH] = DFA_START;
+               error = aa_compat_map_xmatch(&profile->attach.xmatch);
+               if (error) {
+                       info = "failed to convert xmatch permission table";
+                       goto fail;
+               }
        }
 
        /* disconnected attachment string is optional */
@@ -737,6 +884,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        } else if (tmp == PACKED_MODE_UNCONFINED) {
                profile->mode = APPARMOR_UNCONFINED;
                profile->label.flags |= FLAG_UNCONFINED;
+       } else if (tmp == PACKED_MODE_USER) {
+               profile->mode = APPARMOR_USER;
        } else {
                goto fail;
        }
@@ -757,11 +906,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                profile->path_flags = PATH_MEDIATE_DELETED;
 
        info = "failed to unpack profile capabilities";
-       if (!aa_unpack_u32(e, &(profile->caps.allow.cap[0]), NULL))
+       if (!aa_unpack_u32(e, &(rules->caps.allow.cap[0]), NULL))
                goto fail;
-       if (!aa_unpack_u32(e, &(profile->caps.audit.cap[0]), NULL))
+       if (!aa_unpack_u32(e, &(rules->caps.audit.cap[0]), NULL))
                goto fail;
-       if (!aa_unpack_u32(e, &(profile->caps.quiet.cap[0]), NULL))
+       if (!aa_unpack_u32(e, &(rules->caps.quiet.cap[0]), NULL))
                goto fail;
        if (!aa_unpack_u32(e, &tmpcap.cap[0], NULL))
                goto fail;
@@ -769,11 +918,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        info = "failed to unpack upper profile capabilities";
        if (aa_unpack_nameX(e, AA_STRUCT, "caps64")) {
                /* optional upper half of 64 bit caps */
-               if (!aa_unpack_u32(e, &(profile->caps.allow.cap[1]), NULL))
+               if (!aa_unpack_u32(e, &(rules->caps.allow.cap[1]), NULL))
                        goto fail;
-               if (!aa_unpack_u32(e, &(profile->caps.audit.cap[1]), NULL))
+               if (!aa_unpack_u32(e, &(rules->caps.audit.cap[1]), NULL))
                        goto fail;
-               if (!aa_unpack_u32(e, &(profile->caps.quiet.cap[1]), NULL))
+               if (!aa_unpack_u32(e, &(rules->caps.quiet.cap[1]), NULL))
                        goto fail;
                if (!aa_unpack_u32(e, &(tmpcap.cap[1]), NULL))
                        goto fail;
@@ -784,9 +933,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        info = "failed to unpack extended profile capabilities";
        if (aa_unpack_nameX(e, AA_STRUCT, "capsx")) {
                /* optional extended caps mediation mask */
-               if (!aa_unpack_u32(e, &(profile->caps.extended.cap[0]), NULL))
+               if (!aa_unpack_u32(e, &(rules->caps.extended.cap[0]), NULL))
                        goto fail;
-               if (!aa_unpack_u32(e, &(profile->caps.extended.cap[1]), NULL))
+               if (!aa_unpack_u32(e, &(rules->caps.extended.cap[1]), NULL))
                        goto fail;
                if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
                        goto fail;
@@ -797,12 +946,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                goto fail;
        }
 
-       if (!unpack_rlimits(e, profile)) {
+       if (!unpack_rlimits(e, rules)) {
                info = "failed to unpack profile rlimits";
                goto fail;
        }
 
-       if (!unpack_secmark(e, profile)) {
+       if (!unpack_secmark(e, rules)) {
                info = "failed to unpack profile secmark rules";
                goto fail;
        }
@@ -810,59 +959,52 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        if (aa_unpack_nameX(e, AA_STRUCT, "policydb")) {
                /* generic policy dfa - optional and may be NULL */
                info = "failed to unpack policydb";
-               profile->policy.dfa = unpack_dfa(e);
-               if (IS_ERR(profile->policy.dfa)) {
-                       error = PTR_ERR(profile->policy.dfa);
-                       profile->policy.dfa = NULL;
-                       goto fail;
-               } else if (!profile->policy.dfa) {
-                       error = -EPROTO;
+               error = unpack_pdb(e, &rules->policy, true, false,
+                                  &info);
+               if (error)
                        goto fail;
-               }
-               if (!aa_unpack_u32(e, &profile->policy.start[0], "start"))
-                       /* default start state */
-                       profile->policy.start[0] = DFA_START;
-               /* setup class index */
-               for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) {
-                       profile->policy.start[i] =
-                               aa_dfa_next(profile->policy.dfa,
-                                           profile->policy.start[0],
-                                           i);
-               }
+               /* Fixup: drop when we get rid of start array */
+               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],
+                                     AA_CLASS_FILE);
                if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
                        goto fail;
+               error = aa_compat_map_policy(&rules->policy, e->version);
+               if (error) {
+                       info = "failed to remap policydb permission table";
+                       goto fail;
+               }
        } else
-               profile->policy.dfa = aa_get_dfa(nulldfa);
+               rules->policy.dfa = aa_get_dfa(nulldfa);
 
        /* get file rules */
-       profile->file.dfa = unpack_dfa(e);
-       if (IS_ERR(profile->file.dfa)) {
-               error = PTR_ERR(profile->file.dfa);
-               profile->file.dfa = NULL;
-               info = "failed to unpack profile file rules";
+       error = unpack_pdb(e, &rules->file, false, true, &info);
+       if (error) {
                goto fail;
-       } else if (profile->file.dfa) {
-               if (!aa_unpack_u32(e, &profile->file.start, "dfa_start"))
-                       /* default start state */
-                       profile->file.start = DFA_START;
-       } else if (profile->policy.dfa &&
-                  profile->policy.start[AA_CLASS_FILE]) {
-               profile->file.dfa = aa_get_dfa(profile->policy.dfa);
-               profile->file.start = profile->policy.start[AA_CLASS_FILE];
+       } else if (rules->file.dfa) {
+               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];
        } else
-               profile->file.dfa = aa_get_dfa(nulldfa);
-
-       if (!unpack_trans_table(e, profile)) {
-               info = "failed to unpack profile transition table";
-               goto fail;
-       }
+               rules->file.dfa = aa_get_dfa(nulldfa);
 
+       error = -EPROTO;
        if (aa_unpack_nameX(e, AA_STRUCT, "data")) {
                info = "out of memory";
                profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
-               if (!profile->data)
+               if (!profile->data) {
+                       error = -ENOMEM;
                        goto fail;
-
+               }
                params.nelem_hint = 3;
                params.key_len = sizeof(void *);
                params.key_offset = offsetof(struct aa_data, key);
@@ -879,6 +1021,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                        data = kzalloc(sizeof(*data), GFP_KERNEL);
                        if (!data) {
                                kfree_sensitive(key);
+                               error = -ENOMEM;
                                goto fail;
                        }
 
@@ -888,6 +1031,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
                        if (data->size && !data->data) {
                                kfree_sensitive(data->key);
                                kfree_sensitive(data);
+                               error = -ENOMEM;
                                goto fail;
                        }
 
@@ -909,6 +1053,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
        return profile;
 
 fail:
+       if (error == 0)
+               /* default error covers most cases */
+               error = -EPROTO;
+       if (*ns_name) {
+               kfree(*ns_name);
+               *ns_name = NULL;
+       }
        if (profile)
                name = NULL;
        else if (!name)
@@ -946,7 +1097,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
         * if not specified use previous version
         * Mask off everything that is not kernel abi version
         */
-       if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) {
+       if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v9)) {
                audit_iface(NULL, NULL, NULL, "unsupported interface version",
                            e, error);
                return error;
@@ -987,11 +1138,51 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
 {
        int i;
        for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) {
-               if (!verify_xindex(dfa_user_xindex(dfa, i), table_size))
+               if (!verify_xindex(ACCEPT_TABLE(dfa)[i], table_size))
+                       return false;
+       }
+       return true;
+}
+
+static bool verify_perm(struct aa_perms *perm)
+{
+       /* TODO: allow option to just force the perms into a valid state */
+       if (perm->allow & perm->deny)
+               return false;
+       if (perm->subtree & ~perm->allow)
+               return false;
+       if (perm->cond & (perm->allow | perm->deny))
+               return false;
+       if (perm->kill & perm->allow)
+               return false;
+       if (perm->complain & (perm->allow | perm->deny))
+               return false;
+       if (perm->prompt & (perm->allow | perm->deny))
+               return false;
+       if (perm->complain & perm->prompt)
+               return false;
+       if (perm->hide & perm->allow)
+               return false;
+
+       return true;
+}
+
+static bool verify_perms(struct aa_policydb *pdb)
+{
+       int i;
+
+       for (i = 0; i < pdb->size; i++) {
+               if (!verify_perm(&pdb->perms[i]))
+                       return false;
+               /* verify indexes into str table */
+               if (pdb->perms[i].xindex >= pdb->trans.size)
                        return false;
-               if (!verify_xindex(dfa_other_xindex(dfa, i), table_size))
+               if (pdb->perms[i].tag >= pdb->trans.size)
+                       return false;
+               if (pdb->perms[i].label >= pdb->trans.size)
                        return false;
        }
+
        return true;
 }
 
@@ -1000,14 +1191,38 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
  * @profile: profile to verify (NOT NULL)
  *
  * Returns: 0 if passes verification else error
+ *
+ * This verification is post any unpack mapping or changes
  */
 static int verify_profile(struct aa_profile *profile)
 {
-       if (profile->file.dfa &&
-           !verify_dfa_xindex(profile->file.dfa,
-                              profile->file.trans.size)) {
-               audit_iface(profile, NULL, NULL, "Invalid named transition",
-                           NULL, -EPROTO);
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
+       if (!rules)
+               return 0;
+
+       if ((rules->file.dfa && !verify_dfa_xindex(rules->file.dfa,
+                                                 rules->file.trans.size)) ||
+           (rules->policy.dfa &&
+            !verify_dfa_xindex(rules->policy.dfa, rules->policy.trans.size))) {
+               audit_iface(profile, NULL, NULL,
+                           "Unpack: Invalid named transition", NULL, -EPROTO);
+               return -EPROTO;
+       }
+
+       if (!verify_perms(&rules->file)) {
+               audit_iface(profile, NULL, NULL,
+                           "Unpack: Invalid perm index", NULL, -EPROTO);
+               return -EPROTO;
+       }
+       if (!verify_perms(&rules->policy)) {
+               audit_iface(profile, NULL, NULL,
+                           "Unpack: Invalid perm index", NULL, -EPROTO);
+               return -EPROTO;
+       }
+       if (!verify_perms(&profile->attach.xmatch)) {
+               audit_iface(profile, NULL, NULL,
+                           "Unpack: Invalid perm index", NULL, -EPROTO);
                return -EPROTO;
        }
 
@@ -1033,81 +1248,73 @@ struct aa_load_ent *aa_load_ent_alloc(void)
        return ent;
 }
 
-static int deflate_compress(const char *src, size_t slen, char **dst,
-                           size_t *dlen)
+static int compress_zstd(const char *src, size_t slen, char **dst, size_t *dlen)
 {
 #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
-       int error;
-       struct z_stream_s strm;
-       void *stgbuf, *dstbuf;
-       size_t stglen = deflateBound(slen);
-
-       memset(&strm, 0, sizeof(strm));
-
-       if (stglen < slen)
-               return -EFBIG;
-
-       strm.workspace = kvzalloc(zlib_deflate_workspacesize(MAX_WBITS,
-                                                            MAX_MEM_LEVEL),
-                                 GFP_KERNEL);
-       if (!strm.workspace)
-               return -ENOMEM;
-
-       error = zlib_deflateInit(&strm, aa_g_rawdata_compression_level);
-       if (error != Z_OK) {
-               error = -ENOMEM;
-               goto fail_deflate_init;
+       const zstd_parameters params =
+               zstd_get_params(aa_g_rawdata_compression_level, slen);
+       const size_t wksp_len = zstd_cctx_workspace_bound(&params.cParams);
+       void *wksp = NULL;
+       zstd_cctx *ctx = NULL;
+       size_t out_len = zstd_compress_bound(slen);
+       void *out = NULL;
+       int ret = 0;
+
+       out = kvzalloc(out_len, GFP_KERNEL);
+       if (!out) {
+               ret = -ENOMEM;
+               goto cleanup;
        }
 
-       stgbuf = kvzalloc(stglen, GFP_KERNEL);
-       if (!stgbuf) {
-               error = -ENOMEM;
-               goto fail_stg_alloc;
+       wksp = kvzalloc(wksp_len, GFP_KERNEL);
+       if (!wksp) {
+               ret = -ENOMEM;
+               goto cleanup;
        }
 
-       strm.next_in = src;
-       strm.avail_in = slen;
-       strm.next_out = stgbuf;
-       strm.avail_out = stglen;
+       ctx = zstd_init_cctx(wksp, wksp_len);
+       if (!ctx) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
 
-       error = zlib_deflate(&strm, Z_FINISH);
-       if (error != Z_STREAM_END) {
-               error = -EINVAL;
-               goto fail_deflate;
+       out_len = zstd_compress_cctx(ctx, out, out_len, src, slen, &params);
+       if (zstd_is_error(out_len) || out_len >= slen) {
+               ret = -EINVAL;
+               goto cleanup;
        }
-       error = 0;
 
-       if (is_vmalloc_addr(stgbuf)) {
-               dstbuf = kvzalloc(strm.total_out, GFP_KERNEL);
-               if (dstbuf) {
-                       memcpy(dstbuf, stgbuf, strm.total_out);
-                       kvfree(stgbuf);
+       if (is_vmalloc_addr(out)) {
+               *dst = kvzalloc(out_len, GFP_KERNEL);
+               if (*dst) {
+                       memcpy(*dst, out, out_len);
+                       kvfree(out);
+                       out = NULL;
                }
-       } else
+       } else {
                /*
                 * If the staging buffer was kmalloc'd, then using krealloc is
                 * probably going to be faster. The destination buffer will
                 * always be smaller, so it's just shrunk, avoiding a memcpy
                 */
-               dstbuf = krealloc(stgbuf, strm.total_out, GFP_KERNEL);
+               *dst = krealloc(out, out_len, GFP_KERNEL);
+       }
 
-       if (!dstbuf) {
-               error = -ENOMEM;
-               goto fail_deflate;
+       if (!*dst) {
+               ret = -ENOMEM;
+               goto cleanup;
        }
 
-       *dst = dstbuf;
-       *dlen = strm.total_out;
+       *dlen = out_len;
 
-fail_stg_alloc:
-       zlib_deflateEnd(&strm);
-fail_deflate_init:
-       kvfree(strm.workspace);
-       return error;
+cleanup:
+       if (ret) {
+               kvfree(out);
+               *dst = NULL;
+       }
 
-fail_deflate:
-       kvfree(stgbuf);
-       goto fail_stg_alloc;
+       kvfree(wksp);
+       return ret;
 #else
        *dlen = slen;
        return 0;
@@ -1116,7 +1323,6 @@ fail_deflate:
 
 static int compress_loaddata(struct aa_loaddata *data)
 {
-
        AA_BUG(data->compressed_size > 0);
 
        /*
@@ -1125,11 +1331,12 @@ static int compress_loaddata(struct aa_loaddata *data)
         */
        if (aa_g_rawdata_compression_level != 0) {
                void *udata = data->data;
-               int error = deflate_compress(udata, data->size, &data->data,
-                                            &data->compressed_size);
-               if (error)
+               int error = compress_zstd(udata, data->size, &data->data,
+                                         &data->compressed_size);
+               if (error) {
+                       data->compressed_size = data->size;
                        return error;
-
+               }
                if (udata != data->data)
                        kvfree(udata);
        } else
@@ -1155,6 +1362,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
 {
        struct aa_load_ent *tmp, *ent;
        struct aa_profile *profile = NULL;
+       char *ns_name = NULL;
        int error;
        struct aa_ext e = {
                .start = udata->data,
@@ -1164,7 +1372,6 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
 
        *ns = NULL;
        while (e.pos < e.end) {
-               char *ns_name = NULL;
                void *start;
                error = verify_header(&e, e.pos == e.start, ns);
                if (error)
@@ -1195,6 +1402,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
 
                ent->new = profile;
                ent->ns_name = ns_name;
+               ns_name = NULL;
                list_add_tail(&ent->list, lh);
        }
        udata->abi = e.version & K_ABI_MASK;
@@ -1215,6 +1423,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
        return 0;
 
 fail_profile:
+       kfree(ns_name);
        aa_put_profile(profile);
 
 fail:
index f25cf2a..e1bfdab 100644 (file)
@@ -143,12 +143,11 @@ static void policy_unpack_test_inbounds_when_out_of_bounds(struct kunit *test)
 static void policy_unpack_test_unpack_array_with_null_name(struct kunit *test)
 {
        struct policy_unpack_fixture *puf = test->priv;
-       u16 array_size;
+       u16 array_size = 0;
 
        puf->e->pos += TEST_ARRAY_BUF_OFFSET;
 
-       array_size = aa_unpack_array(puf->e, NULL);
-
+       KUNIT_EXPECT_TRUE(test, aa_unpack_array(puf->e, NULL, &array_size));
        KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE);
        KUNIT_EXPECT_PTR_EQ(test, puf->e->pos,
                puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1);
@@ -158,12 +157,11 @@ static void policy_unpack_test_unpack_array_with_name(struct kunit *test)
 {
        struct policy_unpack_fixture *puf = test->priv;
        const char name[] = TEST_ARRAY_NAME;
-       u16 array_size;
+       u16 array_size = 0;
 
        puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET;
 
-       array_size = aa_unpack_array(puf->e, name);
-
+       KUNIT_EXPECT_TRUE(test, aa_unpack_array(puf->e, name, &array_size));
        KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE);
        KUNIT_EXPECT_PTR_EQ(test, puf->e->pos,
                puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1);
@@ -178,9 +176,7 @@ static void policy_unpack_test_unpack_array_out_of_bounds(struct kunit *test)
        puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET;
        puf->e->end = puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16);
 
-       array_size = aa_unpack_array(puf->e, name);
-
-       KUNIT_EXPECT_EQ(test, array_size, 0);
+       KUNIT_EXPECT_FALSE(test, aa_unpack_array(puf->e, name, &array_size));
        KUNIT_EXPECT_PTR_EQ(test, puf->e->pos,
                puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET);
 }
index 86ad26e..197d41f 100644 (file)
 
 
 /**
- * aa_getprocattr - Return the profile information for @profile
- * @profile: the profile to print profile info about  (NOT NULL)
- * @string: Returns - string containing the profile info (NOT NULL)
+ * aa_getprocattr - Return the label information for @label
+ * @label: the label to print label info about  (NOT NULL)
+ * @string: Returns - string containing the label info (NOT NULL)
  *
- * Requires: profile != NULL
+ * Requires: label != NULL && string != NULL
  *
- * Creates a string containing the namespace_name://profile_name for
- * @profile.
+ * Creates a string containing the label information for @label.
  *
  * Returns: size of string placed in @string else error code on failure
  */
index 1ae4874..e859481 100644 (file)
@@ -45,6 +45,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
  * @profile: profile being enforced  (NOT NULL)
  * @resource: rlimit being auditing
  * @value: value being set
+ * @peer: aa_albel of the task being set
+ * @info: info being auditing
  * @error: error value
  *
  * Returns: 0 or sa->error else other error code on failure
@@ -53,7 +55,8 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource,
                          unsigned long value, struct aa_label *peer,
                          const char *info, int error)
 {
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_RLIMITS,
+                         OP_SETRLIMIT);
 
        aad(&sa)->rlim.rlim = resource;
        aad(&sa)->rlim.max = value;
@@ -65,7 +68,7 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource,
 }
 
 /**
- * aa_map_resouce - map compiled policy resource to internal #
+ * aa_map_resource - map compiled policy resource to internal #
  * @resource: flattened policy resource number
  *
  * Returns: resource # for the current architecture.
@@ -81,10 +84,12 @@ int aa_map_resource(int resource)
 static int profile_setrlimit(struct aa_profile *profile, unsigned int resource,
                             struct rlimit *new_rlim)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        int e = 0;
 
-       if (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
-           profile->rlimits.limits[resource].rlim_max)
+       if (rules->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
+           rules->rlimits.limits[resource].rlim_max)
                e = -EACCES;
        return audit_resource(profile, resource, new_rlim->rlim_max, NULL, NULL,
                              e);
@@ -152,12 +157,15 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
         * to the lesser of the tasks hard limit and the init tasks soft limit
         */
        label_for_each_confined(i, old_l, old) {
-               if (old->rlimits.mask) {
+               struct aa_ruleset *rules = list_first_entry(&old->rules,
+                                                           typeof(*rules),
+                                                           list);
+               if (rules->rlimits.mask) {
                        int j;
 
                        for (j = 0, mask = 1; j < RLIM_NLIMITS; j++,
                                     mask <<= 1) {
-                               if (old->rlimits.mask & mask) {
+                               if (rules->rlimits.mask & mask) {
                                        rlim = current->signal->rlim + j;
                                        initrlim = init_task.signal->rlim + j;
                                        rlim->rlim_cur = min(rlim->rlim_max,
@@ -169,17 +177,20 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
 
        /* set any new hard limits as dictated by the new profile */
        label_for_each_confined(i, new_l, new) {
+               struct aa_ruleset *rules = list_first_entry(&new->rules,
+                                                           typeof(*rules),
+                                                           list);
                int j;
 
-               if (!new->rlimits.mask)
+               if (!rules->rlimits.mask)
                        continue;
                for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, mask <<= 1) {
-                       if (!(new->rlimits.mask & mask))
+                       if (!(rules->rlimits.mask & mask))
                                continue;
 
                        rlim = current->signal->rlim + j;
                        rlim->rlim_max = min(rlim->rlim_max,
-                                            new->rlimits.limits[j].rlim_max);
+                                            rules->rlimits.limits[j].rlim_max);
                        /* soft limit should not exceed hard limit */
                        rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
                }
index 503dc08..84d16a2 100644 (file)
@@ -31,7 +31,7 @@ struct aa_label *aa_get_task_label(struct task_struct *task)
        struct aa_label *p;
 
        rcu_read_lock();
-       p = aa_get_newest_label(__aa_task_raw_label(task));
+       p = aa_get_newest_cred_label(__task_cred(task));
        rcu_read_unlock();
 
        return p;
@@ -223,16 +223,18 @@ static void audit_ptrace_cb(struct audit_buffer *ab, void *va)
                        FLAGS_NONE, GFP_ATOMIC);
 }
 
-/* assumes check for PROFILE_MEDIATES is already done */
+/* assumes check for RULE_MEDIATES is already done */
 /* TODO: conditionals */
 static int profile_ptrace_perm(struct aa_profile *profile,
                             struct aa_label *peer, u32 request,
                             struct common_audit_data *sa)
 {
+       struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                   typeof(*rules), list);
        struct aa_perms perms = { };
 
        aad(sa)->peer = peer;
-       aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request,
+       aa_profile_match_label(profile, rules, peer, AA_CLASS_PTRACE, request,
                               &perms);
        aa_apply_modes_to_perms(profile, &perms);
        return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb);
@@ -243,7 +245,7 @@ static int profile_tracee_perm(struct aa_profile *tracee,
                               struct common_audit_data *sa)
 {
        if (profile_unconfined(tracee) || unconfined(tracer) ||
-           !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE))
+           !ANY_RULE_MEDIATES(&tracee->rules, AA_CLASS_PTRACE))
                return 0;
 
        return profile_ptrace_perm(tracee, tracer, request, sa);
@@ -256,7 +258,7 @@ static int profile_tracer_perm(struct aa_profile *tracer,
        if (profile_unconfined(tracer))
                return 0;
 
-       if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE))
+       if (ANY_RULE_MEDIATES(&tracer->rules, AA_CLASS_PTRACE))
                return profile_ptrace_perm(tracer, tracee, request, sa);
 
        /* profile uses the old style capability check for ptrace */
@@ -285,7 +287,7 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
 {
        struct aa_profile *profile;
        u32 xrequest = request << PTRACE_PERM_SHIFT;
-       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE);
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_PTRACE, OP_PTRACE);
 
        return xcheck_labels(tracer, tracee, profile,
                        profile_tracer_perm(profile, tracee, request, &sa),