Currently there's no way to introspect the current text policy for a
given profile. This adds the functionality of having the raw
text profile in the rawdata fs when enabled by a config.

It reuses the raw_data data structure by storing the offset to the
text policy and its size. That means that the raw text is compressed
and must be decompressed for reading. The infrastructure to do so
was reused from raw_data.

Signed-off-by: Georgia Garcia <georgia.gar...@canonical.com>
---
Changes since v1:

Use unpack_blob instead of unpack_str because the text string
can be at most 64KB (a 16 bit value) which will be problematic on a
large policy. Blob is more accurate since it has a 32 bit limit.

v1: https://lists.ubuntu.com/archives/apparmor/2021-July/012337.html
---
 security/apparmor/Kconfig                 |  8 ++++
 security/apparmor/apparmorfs.c            | 46 +++++++++++++++++++++++
 security/apparmor/include/apparmor.h      |  1 +
 security/apparmor/include/apparmorfs.h    |  1 +
 security/apparmor/include/policy_unpack.h |  3 ++
 security/apparmor/lsm.c                   |  6 +++
 security/apparmor/policy_unpack.c         | 27 +++++++++++--
 7 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index 348ed6cfa08a..b8d64f511742 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -40,6 +40,14 @@ config SECURITY_APPARMOR_HASH_DEFAULT
         these cases policy hashing can be disabled by default and
         enabled only if needed.
 
+config SECURITY_APPARMOR_TEXT_PROFILE
+       bool "Enable storage and introspection of raw text profile"
+       depends on SECURITY_APPARMOR
+       default n
+       help
+         This option selects whether raw text profiles can be loaded
+        and introspected via the apparmor filesystem.
+
 config SECURITY_APPARMOR_DEBUG
        bool "Build AppArmor with debug code"
        depends on SECURITY_APPARMOR
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 2ee3b3d29f10..41e952d41ff1 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -1335,6 +1335,18 @@ static int deflate_decompress(char *src, size_t slen, 
char *dst, size_t dlen)
        return error;
 }
 
+static ssize_t rawtext_read(struct file *file, char __user *buf,
+                           size_t size, loff_t *ppos)
+{
+       struct rawdata_f_data *private = file->private_data;
+       unsigned long text_offset = private->loaddata->raw_text_offset;
+       size_t text_size = private->loaddata->raw_text_size;
+
+       return simple_read_from_buffer(buf, size, ppos,
+                                      RAWDATA_F_DATA_BUF(private) +
+                                      text_offset, text_size);
+}
+
 static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
                            loff_t *ppos)
 {
@@ -1399,6 +1411,13 @@ static const struct file_operations rawdata_fops = {
        .release = rawdata_release,
 };
 
+static const struct file_operations rawtext_fops = {
+       .open = rawdata_open,
+       .read = rawtext_read,
+       .llseek = generic_file_llseek,
+       .release = rawdata_release,
+};
+
 static void remove_rawdata_dents(struct aa_loaddata *rawdata)
 {
        int i;
@@ -1475,6 +1494,14 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct 
aa_loaddata *rawdata)
                goto fail;
        rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent;
 
+       if (aa_g_raw_text) {
+               dent = aafs_create_file("raw_text", S_IFREG | 0444, dir,
+                                       rawdata, &rawtext_fops);
+               if (IS_ERR(dent))
+                       goto fail;
+               rawdata->dents[AAFS_LOADDATA_TEXT] = dent;
+       }
+
        dent = aafs_create_file("raw_data", S_IFREG | 0444,
                                      dir, rawdata, &rawdata_fops);
        if (IS_ERR(dent))
@@ -1649,6 +1676,14 @@ static const char *rawdata_get_link_data(struct dentry 
*dentry,
        return rawdata_get_link_base(dentry, inode, done, "raw_data");
 }
 
+
+static const char *rawdata_get_link_text(struct dentry *dentry,
+                                        struct inode *inode,
+                                        struct delayed_call *done)
+{
+       return rawdata_get_link_base(dentry, inode, done, "raw_text");
+}
+
 static const struct inode_operations rawdata_link_sha1_iops = {
        .get_link       = rawdata_get_link_sha1,
 };
@@ -1659,6 +1694,9 @@ static const struct inode_operations 
rawdata_link_abi_iops = {
 static const struct inode_operations rawdata_link_data_iops = {
        .get_link       = rawdata_get_link_data,
 };
+static const struct inode_operations rawdata_link_text_iops = {
+       .get_link       = rawdata_get_link_text,
+};
 
 
 /*
@@ -1754,6 +1792,14 @@ int __aafs_profile_mkdir(struct aa_profile *profile, 
struct dentry *parent)
                        goto fail;
                aa_get_proxy(profile->label.proxy);
                profile->dents[AAFS_PROF_RAW_DATA] = dent;
+
+               dent = aafs_create("raw_text", S_IFLNK | 0444, dir,
+                                  profile->label.proxy, NULL, NULL,
+                                  &rawdata_link_text_iops);
+               if (IS_ERR(dent))
+                       goto fail;
+               aa_get_proxy(profile->label.proxy);
+               profile->dents[AAFS_PROF_RAW_TEXT] = dent;
        }
 
        list_for_each_entry(child, &profile->base.profiles, base.list) {
diff --git a/security/apparmor/include/apparmor.h 
b/security/apparmor/include/apparmor.h
index 1fbabdb565a8..414f1298bf3c 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -41,5 +41,6 @@ extern bool aa_g_lock_policy;
 extern bool aa_g_logsyscall;
 extern bool aa_g_paranoid_load;
 extern unsigned int aa_g_path_max;
+extern bool aa_g_raw_text;
 
 #endif /* __APPARMOR_H */
diff --git a/security/apparmor/include/apparmorfs.h 
b/security/apparmor/include/apparmorfs.h
index 6e14f6cecdb9..8688946c969f 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -89,6 +89,7 @@ enum aafs_prof_type {
        AAFS_PROF_RAW_DATA,
        AAFS_PROF_RAW_HASH,
        AAFS_PROF_RAW_ABI,
+       AAFS_PROF_RAW_TEXT,
        AAFS_PROF_SIZEOF,
 };
 
diff --git a/security/apparmor/include/policy_unpack.h 
b/security/apparmor/include/policy_unpack.h
index e0e1ca7ebc38..9a79ad8d3439 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -42,6 +42,7 @@ enum {
        AAFS_LOADDATA_HASH,
        AAFS_LOADDATA_DATA,
        AAFS_LOADDATA_COMPRESSED_SIZE,
+       AAFS_LOADDATA_TEXT,
        AAFS_LOADDATA_DIR,              /* must be last actual entry */
        AAFS_LOADDATA_NDENTS            /* count of entries */
 };
@@ -67,6 +68,8 @@ struct aa_loaddata {
        long revision;                  /* the ns policy revision this caused */
        int abi;
        unsigned char *hash;
+       unsigned long raw_text_offset;  /* the offset of data to the raw_text*/
+       size_t raw_text_size;           /* the size of the raw text policy */
 
        /* Pointer to payload. If @compressed_size > 0, then this is the
         * compressed version of the payload, else it is the uncompressed
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index f72406fe1bf2..e3b5b2dfac4a 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1375,6 +1375,12 @@ module_param_named(path_max, aa_g_path_max, aauint, 
S_IRUSR);
 bool aa_g_paranoid_load = true;
 module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO);
 
+/* Determines if raw text policy is available */
+bool aa_g_raw_text = IS_ENABLED(CONFIG_SECURITY_APPARMOR_TEXT_PROFILE);
+#ifdef CONFIG_SECURITY_APPARMOR_TEXT_PROFILE
+module_param_named(raw_text, aa_g_raw_text, aabool, 0600);
+#endif
+
 static int param_get_aaintbool(char *buffer, const struct kernel_param *kp);
 static int param_set_aaintbool(const char *val, const struct kernel_param *kp);
 #define param_check_aaintbool param_check_int
diff --git a/security/apparmor/policy_unpack.c 
b/security/apparmor/policy_unpack.c
index 4e1f96b216a8..86d46558e1b7 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -668,17 +668,23 @@ static int datacmp(struct rhashtable_compare_arg *arg, 
const void *obj)
 /**
  * unpack_profile - unpack a serialized profile
  * @e: serialized data extent information (NOT NULL)
+ * @ns_name:  pointer to the string containing the ns name
+ * @raw_text_size: pointer to the size of the raw text profile
+ * @raw_text_offset: pointer to the offset of the raw profile in data
  *
  * NOTE: unpack profile sets audit struct if there is a failure
  */
-static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
+static struct aa_profile *unpack_profile(struct aa_ext *e,
+                                        char **ns_name,
+                                        size_t *raw_text_size,
+                                        unsigned long *raw_text_offset)
 {
        struct aa_profile *profile = NULL;
        const char *tmpname, *tmpns = NULL, *name = NULL;
        const char *info = "failed to unpack profile";
        size_t ns_len;
        struct rhashtable_params params = { 0 };
-       char *key = NULL;
+       char *key = NULL, *tmptext;
        struct aa_data *data;
        int i, error = -EPROTO;
        kernel_cap_t tmpcap;
@@ -917,6 +923,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, 
char **ns_name)
                }
        }
 
+       /* text policy is optinal */
+       *raw_text_size = unpack_blob(e, &tmptext, "text_policy");
+       if (*raw_text_size) {
+               *raw_text_offset = (void *) tmptext - e->start;
+       }
+
        if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
                info = "failed to unpack end of profile";
                goto fail;
@@ -1175,18 +1187,27 @@ int aa_unpack(struct aa_loaddata *udata, struct 
list_head *lh,
        *ns = NULL;
        while (e.pos < e.end) {
                char *ns_name = NULL;
+               size_t raw_text_size = 0;
+               unsigned long raw_text_offset;
                void *start;
                error = verify_header(&e, e.pos == e.start, ns);
                if (error)
                        goto fail;
 
                start = e.pos;
-               profile = unpack_profile(&e, &ns_name);
+               profile = unpack_profile(&e, &ns_name,
+                                        &raw_text_size,
+                                        &raw_text_offset);
                if (IS_ERR(profile)) {
                        error = PTR_ERR(profile);
                        goto fail;
                }
 
+               if (aa_g_raw_text && raw_text_size) {
+                       udata->raw_text_size = raw_text_size;
+                       udata->raw_text_offset = raw_text_offset;
+               }
+
                error = verify_profile(profile);
                if (error)
                        goto fail_profile;
-- 
2.25.1


-- 
AppArmor mailing list
AppArmor@lists.ubuntu.com
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to