Skip to content

Commit

Permalink
apparmor: Initial implementation of raw policy blob compression
Browse files Browse the repository at this point in the history
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 <chris.coulson@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
  • Loading branch information
Chris Coulson authored and John Johansen committed Feb 7, 2019
1 parent 43aa09f commit 876dd86
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 8 deletions.
130 changes: 124 additions & 6 deletions security/apparmor/apparmorfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/rcupdate.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/zlib.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>

Expand Down Expand Up @@ -68,6 +69,35 @@
* support fns
*/

struct rawdata_f_data {
struct aa_loaddata *loaddata;
};

#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)

static void rawdata_f_data_free(struct rawdata_f_data *private)
{
if (!private)
return;

aa_put_loaddata(private->loaddata);
kvfree(private);
}

static struct rawdata_f_data *rawdata_f_data_alloc(size_t size)
{
struct rawdata_f_data *ret;

if (size > SIZE_MAX - sizeof(*ret))
return ERR_PTR(-EINVAL);

ret = kvzalloc(sizeof(*ret) + size, GFP_KERNEL);
if (!ret)
return ERR_PTR(-ENOMEM);

return ret;
}

/**
* aa_mangle_name - mangle a profile name to std profile layout form
* @name: profile name to mangle (NOT NULL)
Expand Down Expand Up @@ -1274,36 +1304,117 @@ static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
return 0;
}

static int seq_rawdata_compressed_size_show(struct seq_file *seq, void *v)
{
struct aa_loaddata *data = seq->private;

seq_printf(seq, "%zu\n", data->compressed_size);

return 0;
}

SEQ_RAWDATA_FOPS(abi);
SEQ_RAWDATA_FOPS(revision);
SEQ_RAWDATA_FOPS(hash);
SEQ_RAWDATA_FOPS(compressed_size);

static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen)
{
int error;
struct z_stream_s strm;

if (aa_g_rawdata_compression_level == 0) {
if (dlen < slen)
return -EINVAL;
memcpy(dst, src, slen);
return 0;
}

memset(&strm, 0, sizeof(strm));

strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
if (!strm.workspace)
return -ENOMEM;

strm.next_in = src;
strm.avail_in = slen;

error = zlib_inflateInit(&strm);
if (error != Z_OK) {
error = -ENOMEM;
goto fail_inflate_init;
}

strm.next_out = dst;
strm.avail_out = dlen;

error = zlib_inflate(&strm, Z_FINISH);
if (error != Z_STREAM_END)
error = -EINVAL;
else
error = 0;

zlib_inflateEnd(&strm);
fail_inflate_init:
kvfree(strm.workspace);
return error;
}

static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
{
struct aa_loaddata *rawdata = file->private_data;
struct rawdata_f_data *private = file->private_data;

return simple_read_from_buffer(buf, size, ppos, rawdata->data,
rawdata->size);
return simple_read_from_buffer(buf, size, ppos,
RAWDATA_F_DATA_BUF(private),
private->loaddata->size);
}

static int rawdata_release(struct inode *inode, struct file *file)
{
aa_put_loaddata(file->private_data);
rawdata_f_data_free(file->private_data);

return 0;
}

static int rawdata_open(struct inode *inode, struct file *file)
{
int error;
struct aa_loaddata *loaddata;
struct rawdata_f_data *private;

if (!policy_view_capable(NULL))
return -EACCES;
file->private_data = __aa_get_loaddata(inode->i_private);
if (!file->private_data)

loaddata = __aa_get_loaddata(inode->i_private);
if (!loaddata)
/* lost race: this entry is being reaped */
return -ENOENT;

private = rawdata_f_data_alloc(loaddata->size);
if (IS_ERR(private)) {
error = PTR_ERR(private);
goto fail_private_alloc;
}

private->loaddata = loaddata;

error = deflate_decompress(loaddata->data, loaddata->compressed_size,
RAWDATA_F_DATA_BUF(private),
loaddata->size);
if (error)
goto fail_decompress;

file->private_data = private;
return 0;

fail_decompress:
rawdata_f_data_free(private);
return error;

fail_private_alloc:
aa_put_loaddata(loaddata);
return error;
}

static const struct file_operations rawdata_fops = {
Expand Down Expand Up @@ -1382,6 +1493,13 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
rawdata->dents[AAFS_LOADDATA_HASH] = dent;
}

dent = aafs_create_file("compressed_size", S_IFREG | 0444, dir,
rawdata,
&seq_rawdata_compressed_size_fops);
if (IS_ERR(dent))
goto fail;
rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent;

dent = aafs_create_file("raw_data", S_IFREG | 0444,
dir, rawdata, &rawdata_fops);
if (IS_ERR(dent))
Expand Down
1 change: 1 addition & 0 deletions security/apparmor/include/apparmor.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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 int aa_g_rawdata_compression_level;
extern bool aa_g_lock_policy;
extern bool aa_g_logsyscall;
extern bool aa_g_paranoid_load;
Expand Down
8 changes: 7 additions & 1 deletion security/apparmor/include/policy_unpack.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum {
AAFS_LOADDATA_REVISION,
AAFS_LOADDATA_HASH,
AAFS_LOADDATA_DATA,
AAFS_LOADDATA_COMPRESSED_SIZE,
AAFS_LOADDATA_DIR, /* must be last actual entry */
AAFS_LOADDATA_NDENTS /* count of entries */
};
Expand All @@ -65,11 +66,16 @@ struct aa_loaddata {
struct dentry *dents[AAFS_LOADDATA_NDENTS];
struct aa_ns *ns;
char *name;
size_t size;
size_t size; /* the original size of the payload */
size_t compressed_size; /* the compressed size of the payload */
long revision; /* the ns policy revision this caused */
int abi;
unsigned char *hash;

/* Pointer to payload. If @compressed_size > 0, then this is the
* compressed version of the payload, else it is the uncompressed
* version (with the size indicated by @size).
*/
char *data;
};

Expand Down
47 changes: 47 additions & 0 deletions security/apparmor/lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/user_namespace.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/zlib.h>
#include <net/sock.h>

#include "include/apparmor.h"
Expand Down Expand Up @@ -1261,6 +1262,16 @@ static const struct kernel_param_ops param_ops_aauint = {
.get = param_get_aauint
};

static int param_set_aacompressionlevel(const char *val,
const struct kernel_param *kp);
static int param_get_aacompressionlevel(char *buffer,
const struct kernel_param *kp);
#define param_check_aacompressionlevel param_check_int
static const struct kernel_param_ops param_ops_aacompressionlevel = {
.set = param_set_aacompressionlevel,
.get = param_get_aacompressionlevel
};

static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp);
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp);
#define param_check_aalockpolicy param_check_bool
Expand Down Expand Up @@ -1291,6 +1302,11 @@ 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

/* policy loaddata compression level */
int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION;
module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level,
aacompressionlevel, S_IRUSR);

/* Debug mode */
bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES);
module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
Expand Down Expand Up @@ -1408,6 +1424,37 @@ static int param_get_aauint(char *buffer, const struct kernel_param *kp)
return param_get_uint(buffer, kp);
}

static int param_set_aacompressionlevel(const char *val,
const struct kernel_param *kp)
{
int error;

if (!apparmor_enabled)
return -EINVAL;
if (apparmor_initialized)
return -EPERM;

error = param_set_int(val, kp);

aa_g_rawdata_compression_level = clamp(aa_g_rawdata_compression_level,
Z_NO_COMPRESSION,
Z_BEST_COMPRESSION);
pr_info("AppArmor: policy rawdata compression level set to %u\n",
aa_g_rawdata_compression_level);

return error;
}

static int param_get_aacompressionlevel(char *buffer,
const struct kernel_param *kp)
{
if (!apparmor_enabled)
return -EINVAL;
if (apparmor_initialized && !policy_view_capable(NULL))
return -EPERM;
return param_get_int(buffer, kp);
}

static int param_get_audit(char *buffer, const struct kernel_param *kp)
{
if (!apparmor_enabled)
Expand Down
Loading

0 comments on commit 876dd86

Please sign in to comment.