diff options
Diffstat (limited to 'security/apparmor/policy_unpack.c')
-rw-r--r-- | security/apparmor/policy_unpack.c | 116 |
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: |