summaryrefslogtreecommitdiffstats
path: root/security/apparmor/policy_unpack.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-12-03 12:51:35 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2019-12-03 12:51:35 -0800
commit79e178a57dae819ae724065b47c25720494cc9f2 (patch)
tree821bf3adee5ad86ba88d2ed25f2131854aa10147 /security/apparmor/policy_unpack.c
parent01d1dff64662646023482806c6db8ef0b280c403 (diff)
parent341c1fda5e17156619fb71acfc7082b2669b4b72 (diff)
downloadlinux-79e178a57dae819ae724065b47c25720494cc9f2.tar.bz2
Merge tag 'apparmor-pr-2019-12-03' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
Pull apparmor updates from John Johansen: "Features: - increase left match history buffer size to provide improved conflict resolution in overlapping execution rules. - switch buffer allocation to use a memory pool and GFP_KERNEL where possible. - add compression of policy blobs to reduce memory usage. Cleanups: - fix spelling mistake "immutible" -> "immutable" Bug fixes: - fix unsigned len comparison in update_for_len macro - fix sparse warning for type-casting of current->real_cred" * tag 'apparmor-pr-2019-12-03' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: apparmor: make it so work buffers can be allocated from atomic context apparmor: reduce rcu_read_lock scope for aa_file_perm mediation apparmor: fix wrong buffer allocation in aa_new_mount apparmor: fix unsigned len comparison with less than zero apparmor: increase left match history buffer size apparmor: Switch to GFP_KERNEL where possible apparmor: Use a memory pool instead per-CPU caches apparmor: Force type-casting of current->real_cred apparmor: fix spelling mistake "immutible" -> "immutable" apparmor: fix blob compression when ns is forced on a policy load apparmor: fix missing ZLIB defines apparmor: fix blob compression build failure on ppc apparmor: Initial implementation of raw policy blob compression
Diffstat (limited to 'security/apparmor/policy_unpack.c')
-rw-r--r--security/apparmor/policy_unpack.c116
1 files changed, 112 insertions, 4 deletions
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 8cfc9493eefc..80364310fb1e 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -16,6 +16,7 @@
#include <asm/unaligned.h>
#include <linux/ctype.h>
#include <linux/errno.h>
+#include <linux/zlib.h>
#include "include/apparmor.h"
#include "include/audit.h"
@@ -139,9 +140,11 @@ bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r)
{
if (l->size != r->size)
return false;
+ if (l->compressed_size != r->compressed_size)
+ return false;
if (aa_g_hash_policy && memcmp(l->hash, r->hash, aa_hash_size()) != 0)
return false;
- return memcmp(l->data, r->data, r->size) == 0;
+ return memcmp(l->data, r->data, r->compressed_size ?: r->size) == 0;
}
/*
@@ -968,11 +971,14 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
e, error);
return error;
}
- if (*ns && strcmp(*ns, name))
+ if (*ns && strcmp(*ns, name)) {
audit_iface(NULL, NULL, NULL, "invalid ns change", e,
error);
- else if (!*ns)
- *ns = name;
+ } else if (!*ns) {
+ *ns = kstrdup(name, GFP_KERNEL);
+ if (!*ns)
+ return -ENOMEM;
+ }
}
return 0;
@@ -1039,6 +1045,105 @@ 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)
+{
+ 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;
+ }
+
+ stgbuf = kvzalloc(stglen, GFP_KERNEL);
+ if (!stgbuf) {
+ error = -ENOMEM;
+ goto fail_stg_alloc;
+ }
+
+ strm.next_in = src;
+ strm.avail_in = slen;
+ strm.next_out = stgbuf;
+ strm.avail_out = stglen;
+
+ error = zlib_deflate(&strm, Z_FINISH);
+ if (error != Z_STREAM_END) {
+ error = -EINVAL;
+ goto fail_deflate;
+ }
+ error = 0;
+
+ if (is_vmalloc_addr(stgbuf)) {
+ dstbuf = kvzalloc(strm.total_out, GFP_KERNEL);
+ if (dstbuf) {
+ memcpy(dstbuf, stgbuf, strm.total_out);
+ kvfree(stgbuf);
+ }
+ } 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);
+
+ if (!dstbuf) {
+ error = -ENOMEM;
+ goto fail_deflate;
+ }
+
+ *dst = dstbuf;
+ *dlen = strm.total_out;
+
+fail_stg_alloc:
+ zlib_deflateEnd(&strm);
+fail_deflate_init:
+ kvfree(strm.workspace);
+ return error;
+
+fail_deflate:
+ kvfree(stgbuf);
+ goto fail_stg_alloc;
+}
+
+static int compress_loaddata(struct aa_loaddata *data)
+{
+
+ AA_BUG(data->compressed_size > 0);
+
+ /*
+ * Shortcut the no compression case, else we increase the amount of
+ * storage required by a small amount
+ */
+ 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)
+ return error;
+
+ kvfree(udata);
+ } else
+ data->compressed_size = data->size;
+
+ return 0;
+}
+
/**
* aa_unpack - unpack packed binary profile(s) data loaded from user space
* @udata: user data copied to kmem (NOT NULL)
@@ -1107,6 +1212,9 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
goto fail;
}
}
+ error = compress_loaddata(udata);
+ if (error)
+ goto fail;
return 0;
fail_profile: