]> git.ipfire.org Git - people/ms/linux.git/commitdiff
apparmor: make export of raw binary profile to userspace optional
authorJohn Johansen <john.johansen@canonical.com>
Mon, 1 Feb 2021 11:43:18 +0000 (03:43 -0800)
committerJohn Johansen <john.johansen@canonical.com>
Sat, 9 Jul 2022 22:13:59 +0000 (15:13 -0700)
Embedded systems have limited space and don't need the introspection
or checkpoint restore capability provided by exporting the raw
profile binary data so make it so make it a config option.

This will reduce run time memory use and also speed up policy loads.

Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/Kconfig
security/apparmor/apparmorfs.c
security/apparmor/include/apparmor.h
security/apparmor/include/apparmorfs.h
security/apparmor/lsm.c
security/apparmor/policy.c
security/apparmor/policy_unpack.c

index 272dca497c6d2830d42b7718756aad547e81d68a..4c34a28a2ddf0aa6f369e9d68eabfb9772bf2e9d 100644 (file)
@@ -6,8 +6,6 @@ config SECURITY_APPARMOR
        select SECURITY_PATH
        select SECURITYFS
        select SECURITY_NETWORK
-       select ZLIB_INFLATE
-       select ZLIB_DEFLATE
        default n
        help
          This enables the AppArmor security module.
@@ -17,32 +15,6 @@ config SECURITY_APPARMOR
 
          If you are unsure how to answer this question, answer N.
 
-config SECURITY_APPARMOR_HASH
-       bool "Enable introspection of sha1 hashes for loaded profiles"
-       depends on SECURITY_APPARMOR
-       select CRYPTO
-       select CRYPTO_SHA1
-       default y
-       help
-         This option selects whether introspection of loaded policy
-         hashes is available to userspace via the apparmor
-         filesystem. This option provides a light weight means of
-         checking loaded policy.  This option adds to policy load
-         time and can be disabled for small embedded systems.
-
-config SECURITY_APPARMOR_HASH_DEFAULT
-       bool "Enable policy hash introspection by default"
-       depends on SECURITY_APPARMOR_HASH
-       default y
-       help
-         This option selects whether sha1 hashing of loaded policy
-        is enabled by default. The generation of sha1 hashes for
-        loaded policy provide system administrators a quick way
-        to verify that policy in the kernel matches what is expected,
-        however it can slow down policy load on some devices. In
-        these cases policy hashing can be disabled by default and
-        enabled only if needed.
-
 config SECURITY_APPARMOR_DEBUG
        bool "Build AppArmor with debug code"
        depends on SECURITY_APPARMOR
@@ -72,6 +44,56 @@ config SECURITY_APPARMOR_DEBUG_MESSAGES
          When enabled, various debug messages will be logged to
          the kernel message buffer.
 
+config SECURITY_APPARMOR_INTROSPECT_POLICY
+       bool "Allow loaded policy to be introspected"
+       depends on SECURITY_APPARMOR
+       default y
+       help
+         This option selects whether introspection of loaded policy
+         is available to userspace via the apparmor filesystem. This
+         adds to kernel memory usage. It is required for introspection
+         of loaded policy, and check point and restore support. It
+         can be disabled for embedded systems where reducing memory and
+         cpu is paramount.
+
+config SECURITY_APPARMOR_HASH
+       bool "Enable introspection of sha1 hashes for loaded profiles"
+       depends on SECURITY_APPARMOR_INTROSPECT_POLICY
+       select CRYPTO
+       select CRYPTO_SHA1
+       default y
+       help
+         This option selects whether introspection of loaded policy
+         hashes is available to userspace via the apparmor
+         filesystem. This option provides a light weight means of
+         checking loaded policy.  This option adds to policy load
+         time and can be disabled for small embedded systems.
+
+config SECURITY_APPARMOR_HASH_DEFAULT
+       bool "Enable policy hash introspection by default"
+       depends on SECURITY_APPARMOR_HASH
+       default y
+       help
+         This option selects whether sha1 hashing of loaded policy
+        is enabled by default. The generation of sha1 hashes for
+        loaded policy provide system administrators a quick way
+        to verify that policy in the kernel matches what is expected,
+        however it can slow down policy load on some devices. In
+        these cases policy hashing can be disabled by default and
+        enabled only if needed.
+
+config SECURITY_APPARMOR_EXPORT_BINARY
+       bool "Allow exporting the raw binary policy"
+       depends on SECURITY_APPARMOR_INTROSPECT_POLICY
+       select ZLIB_INFLATE
+       select ZLIB_DEFLATE
+       default y
+       help
+         This option allows reading back binary policy as it was loaded.
+         It increases the amount of kernel memory needed by policy and
+         also increases policy load time. This option is required for
+         checkpoint and restore support, and debugging of loaded policy.
+
 config SECURITY_APPARMOR_KUNIT_TEST
        bool "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS
        depends on KUNIT=y && SECURITY_APPARMOR
index 0797edb2fb3dc6c4e16921c7e3c3dad91ae98792..3770dde50a47cb0813d45d9d34421f53e2cb7cbb 100644 (file)
@@ -70,6 +70,7 @@ struct rawdata_f_data {
        struct aa_loaddata *loaddata;
 };
 
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
 #define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)
 
 static void rawdata_f_data_free(struct rawdata_f_data *private)
@@ -94,6 +95,7 @@ static struct rawdata_f_data *rawdata_f_data_alloc(size_t size)
 
        return ret;
 }
+#endif
 
 /**
  * aa_mangle_name - mangle a profile name to std profile layout form
@@ -1201,7 +1203,7 @@ SEQ_NS_FOPS(name);
 
 
 /* policy/raw_data/ * file ops */
-
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
 #define SEQ_RAWDATA_FOPS(NAME)                                               \
 static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\
 {                                                                            \
@@ -1492,6 +1494,8 @@ fail:
 
        return PTR_ERR(dent);
 }
+#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
+
 
 /** fns to setup dynamic per profile/namespace files **/
 
@@ -1557,6 +1561,7 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name,
        return dent;
 }
 
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
 static int profile_depth(struct aa_profile *profile)
 {
        int depth = 0;
@@ -1658,7 +1663,7 @@ static const struct inode_operations rawdata_link_abi_iops = {
 static const struct inode_operations rawdata_link_data_iops = {
        .get_link       = rawdata_get_link_data,
 };
-
+#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
 
 /*
  * Requires: @profile->ns->lock held
@@ -1729,6 +1734,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
                profile->dents[AAFS_PROF_HASH] = dent;
        }
 
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
        if (profile->rawdata) {
                dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir,
                                   profile->label.proxy, NULL, NULL,
@@ -1754,6 +1760,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
                aa_get_proxy(profile->label.proxy);
                profile->dents[AAFS_PROF_RAW_DATA] = dent;
        }
+#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
 
        list_for_each_entry(child, &profile->base.profiles, base.list) {
                error = __aafs_profile_mkdir(child, prof_child_dir(profile));
index 1fbabdb565a8c3a6a69edea5684cffbab2ce205a..9c3fc36a07023bcd41c0f769fb4d3fbd232c4d4c 100644 (file)
@@ -36,6 +36,7 @@ extern enum audit_mode aa_g_audit;
 extern bool aa_g_audit_header;
 extern bool aa_g_debug;
 extern bool aa_g_hash_policy;
+extern bool aa_g_export_binary;
 extern int aa_g_rawdata_compression_level;
 extern bool aa_g_lock_policy;
 extern bool aa_g_logsyscall;
index 6e14f6cecdb9af18ac07c80f299ce450fbc86af1..1e94904f68d9057858e1fa8f93835ff6cbfce5d2 100644 (file)
@@ -114,7 +114,21 @@ int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
                     struct dentry *dent);
 
 struct aa_loaddata;
+
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
 void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata);
 int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata);
+#else
+static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
+{
+       /* empty stub */
+}
+
+static inline int __aa_fs_create_rawdata(struct aa_ns *ns,
+                                        struct aa_loaddata *rawdata)
+{
+       return 0;
+}
+#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
 
 #endif /* __AA_APPARMORFS_H */
index 2654bcb5f462d5e936a59b5bf0e0a36fd4ae2887..84a4e63d922da706de2c2fe62b3e31cec7705273 100644 (file)
@@ -1357,6 +1357,12 @@ bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT);
 module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
 #endif
 
+/* whether policy exactly as loaded is retained for debug and checkpointing */
+bool aa_g_export_binary = IS_ENABLED(CONFIG_SECURITY_APPARMOR_EXPORT_BINARY);
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
+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;
 module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level,
index 8357f4a639e1bf83bea201aa35de459429a0f5e5..499c0209b6a46360bf16755f0c840d38d07b4341 100644 (file)
@@ -952,16 +952,18 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
 
        mutex_lock_nested(&ns->lock, ns->level);
        /* check for duplicate rawdata blobs: space and file dedup */
-       list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) {
-               if (aa_rawdata_eq(rawdata_ent, udata)) {
-                       struct aa_loaddata *tmp;
-
-                       tmp = __aa_get_loaddata(rawdata_ent);
-                       /* check we didn't fail the race */
-                       if (tmp) {
-                               aa_put_loaddata(udata);
-                               udata = tmp;
-                               break;
+       if (!list_empty(&ns->rawdata_list)) {
+               list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) {
+                       if (aa_rawdata_eq(rawdata_ent, udata)) {
+                               struct aa_loaddata *tmp;
+
+                               tmp = __aa_get_loaddata(rawdata_ent);
+                               /* check we didn't fail the race */
+                               if (tmp) {
+                                       aa_put_loaddata(udata);
+                                       udata = tmp;
+                                       break;
+                               }
                        }
                }
        }
@@ -969,7 +971,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
        list_for_each_entry(ent, &lh, list) {
                struct aa_policy *policy;
 
-               ent->new->rawdata = aa_get_loaddata(udata);
+               if (aa_g_export_binary)
+                       ent->new->rawdata = aa_get_loaddata(udata);
                error = __lookup_replace(ns, ent->new->base.hname,
                                         !(mask & AA_MAY_REPLACE_POLICY),
                                         &ent->old, &info);
@@ -1009,7 +1012,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
        }
 
        /* create new fs entries for introspection if needed */
-       if (!udata->dents[AAFS_LOADDATA_DIR]) {
+       if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) {
                error = __aa_fs_create_rawdata(ns, udata);
                if (error) {
                        info = "failed to create raw_data dir and files";
@@ -1037,12 +1040,14 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
 
        /* Done with checks that may fail - do actual replacement */
        __aa_bump_ns_revision(ns);
-       __aa_loaddata_update(udata, ns->revision);
+       if (aa_g_export_binary)
+               __aa_loaddata_update(udata, ns->revision);
        list_for_each_entry_safe(ent, tmp, &lh, list) {
                list_del_init(&ent->list);
                op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL;
 
-               if (ent->old && ent->old->rawdata == ent->new->rawdata) {
+               if (ent->old && ent->old->rawdata == ent->new->rawdata &&
+                   ent->new->rawdata) {
                        /* dedup actual profile replacement */
                        audit_policy(label, op, ns_name, ent->new->base.hname,
                                     "same as current profile, skipping",
index 0acca6f2a93fcbe95f5ec3350f949996e492378f..31d2c2626ea56b0326ef89eae92f01f75fa1ab8c 100644 (file)
@@ -125,15 +125,16 @@ void __aa_loaddata_update(struct aa_loaddata *data, long revision)
 {
        AA_BUG(!data);
        AA_BUG(!data->ns);
-       AA_BUG(!data->dents[AAFS_LOADDATA_REVISION]);
        AA_BUG(!mutex_is_locked(&data->ns->lock));
        AA_BUG(data->revision > revision);
 
        data->revision = revision;
-       d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
-               current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
-       d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
-               current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
+       if ((data->dents[AAFS_LOADDATA_REVISION])) {
+               d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
+                       current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
+               d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
+                       current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
+       }
 }
 
 bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r)
@@ -1216,9 +1217,12 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
                        goto fail;
                }
        }
-       error = compress_loaddata(udata);
-       if (error)
-               goto fail;
+
+       if (aa_g_export_binary) {
+               error = compress_loaddata(udata);
+               if (error)
+                       goto fail;
+       }
        return 0;
 
 fail_profile: