From 63c16c3a7608558a8e5ced96b8b6b06c490fd513 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Wed, 23 Jan 2019 19:17:09 +0000 Subject: apparmor: Initial implementation of raw policy blob compression This adds an initial implementation of raw policy blob compression, using deflate. Compression level can be controlled via a new sysctl, "apparmor.rawdata_compression_level", which can be set to a value between 0 (no compression) and 9 (highest compression). Signed-off-by: Chris Coulson Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 107 +++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) (limited to 'security/apparmor/policy_unpack.c') diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index f6c2bcb2ab14..4c077aadc383 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "include/apparmor.h" #include "include/audit.h" @@ -143,9 +144,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; } /* @@ -1012,6 +1015,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); + vfree(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) @@ -1080,6 +1182,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: -- cgit v1.2.1 From 6a59d9243d349ae598e8ea74c36aa8e31705071a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 8 Feb 2019 17:14:35 -0800 Subject: apparmor: fix blob compression build failure on ppc security/apparmor/policy_unpack.c: In function 'deflate_compress': security/apparmor/policy_unpack.c:1064:4: error: implicit declaration of function 'vfree'; did you mean 'kfree'? [-Werror=implicit-function-declaration] vfree(stgbuf); ^~~~~ kfree Fixes: 876dd866c084 ("apparmor: Initial implementation of raw policy blob compression") Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/apparmor/policy_unpack.c') diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 4c077aadc383..c421801409e3 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -1062,7 +1062,7 @@ static int deflate_compress(const char *src, size_t slen, char **dst, dstbuf = kvzalloc(strm.total_out, GFP_KERNEL); if (dstbuf) { memcpy(dstbuf, stgbuf, strm.total_out); - vfree(stgbuf); + kvfree(stgbuf); } } else /* -- cgit v1.2.1 From 145a0ef21c8e944957f58e2c8ffcd8a10f46266a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 9 Mar 2019 16:58:10 -0800 Subject: apparmor: fix blob compression when ns is forced on a policy load When blob compression is turned on, if the policy namespace is forced onto a policy load, the policy load will fail as the namespace name being referenced is inside the compressed policy blob, resulting in invalid or names that are too long. So duplicate the name before the blob is compressed. Fixes: 876dd866c084 ("apparmor: Initial implementation of raw policy blob compression") Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'security/apparmor/policy_unpack.c') diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c421801409e3..20f07f629598 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -944,11 +944,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; -- cgit v1.2.1