mirror of https://gitee.com/openkylin/linux.git
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 <chris.coulson@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
parent
582549e3fb
commit
63c16c3a76
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/poll.h>
|
#include <linux/poll.h>
|
||||||
|
#include <linux/zlib.h>
|
||||||
#include <uapi/linux/major.h>
|
#include <uapi/linux/major.h>
|
||||||
#include <uapi/linux/magic.h>
|
#include <uapi/linux/magic.h>
|
||||||
|
|
||||||
|
@ -68,6 +69,35 @@
|
||||||
* support fns
|
* 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
|
* aa_mangle_name - mangle a profile name to std profile layout form
|
||||||
* @name: profile name to mangle (NOT NULL)
|
* @name: profile name to mangle (NOT NULL)
|
||||||
|
@ -1275,36 +1305,117 @@ static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
|
||||||
return 0;
|
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(abi);
|
||||||
SEQ_RAWDATA_FOPS(revision);
|
SEQ_RAWDATA_FOPS(revision);
|
||||||
SEQ_RAWDATA_FOPS(hash);
|
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,
|
static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
|
||||||
loff_t *ppos)
|
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,
|
return simple_read_from_buffer(buf, size, ppos,
|
||||||
rawdata->size);
|
RAWDATA_F_DATA_BUF(private),
|
||||||
|
private->loaddata->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rawdata_release(struct inode *inode, struct file *file)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rawdata_open(struct inode *inode, struct file *file)
|
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))
|
if (!policy_view_capable(NULL))
|
||||||
return -EACCES;
|
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 */
|
/* lost race: this entry is being reaped */
|
||||||
return -ENOENT;
|
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;
|
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 = {
|
static const struct file_operations rawdata_fops = {
|
||||||
|
@ -1383,6 +1494,13 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
|
||||||
rawdata->dents[AAFS_LOADDATA_HASH] = dent;
|
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,
|
dent = aafs_create_file("raw_data", S_IFREG | 0444,
|
||||||
dir, rawdata, &rawdata_fops);
|
dir, rawdata, &rawdata_fops);
|
||||||
if (IS_ERR(dent))
|
if (IS_ERR(dent))
|
||||||
|
|
|
@ -40,6 +40,7 @@ extern enum audit_mode aa_g_audit;
|
||||||
extern bool aa_g_audit_header;
|
extern bool aa_g_audit_header;
|
||||||
extern bool aa_g_debug;
|
extern bool aa_g_debug;
|
||||||
extern bool aa_g_hash_policy;
|
extern bool aa_g_hash_policy;
|
||||||
|
extern int aa_g_rawdata_compression_level;
|
||||||
extern bool aa_g_lock_policy;
|
extern bool aa_g_lock_policy;
|
||||||
extern bool aa_g_logsyscall;
|
extern bool aa_g_logsyscall;
|
||||||
extern bool aa_g_paranoid_load;
|
extern bool aa_g_paranoid_load;
|
||||||
|
|
|
@ -45,6 +45,7 @@ enum {
|
||||||
AAFS_LOADDATA_REVISION,
|
AAFS_LOADDATA_REVISION,
|
||||||
AAFS_LOADDATA_HASH,
|
AAFS_LOADDATA_HASH,
|
||||||
AAFS_LOADDATA_DATA,
|
AAFS_LOADDATA_DATA,
|
||||||
|
AAFS_LOADDATA_COMPRESSED_SIZE,
|
||||||
AAFS_LOADDATA_DIR, /* must be last actual entry */
|
AAFS_LOADDATA_DIR, /* must be last actual entry */
|
||||||
AAFS_LOADDATA_NDENTS /* count of entries */
|
AAFS_LOADDATA_NDENTS /* count of entries */
|
||||||
};
|
};
|
||||||
|
@ -65,11 +66,16 @@ struct aa_loaddata {
|
||||||
struct dentry *dents[AAFS_LOADDATA_NDENTS];
|
struct dentry *dents[AAFS_LOADDATA_NDENTS];
|
||||||
struct aa_ns *ns;
|
struct aa_ns *ns;
|
||||||
char *name;
|
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 */
|
long revision; /* the ns policy revision this caused */
|
||||||
int abi;
|
int abi;
|
||||||
unsigned char *hash;
|
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;
|
char *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <linux/user_namespace.h>
|
#include <linux/user_namespace.h>
|
||||||
#include <linux/netfilter_ipv4.h>
|
#include <linux/netfilter_ipv4.h>
|
||||||
#include <linux/netfilter_ipv6.h>
|
#include <linux/netfilter_ipv6.h>
|
||||||
|
#include <linux/zlib.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
#include <uapi/linux/mount.h>
|
#include <uapi/linux/mount.h>
|
||||||
|
|
||||||
|
@ -1266,6 +1267,16 @@ static const struct kernel_param_ops param_ops_aauint = {
|
||||||
.get = param_get_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_set_aalockpolicy(const char *val, const struct kernel_param *kp);
|
||||||
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp);
|
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp);
|
||||||
#define param_check_aalockpolicy param_check_bool
|
#define param_check_aalockpolicy param_check_bool
|
||||||
|
@ -1296,6 +1307,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);
|
module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
|
||||||
#endif
|
#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, 0400);
|
||||||
|
|
||||||
/* Debug mode */
|
/* Debug mode */
|
||||||
bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES);
|
bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES);
|
||||||
module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
|
module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
|
||||||
|
@ -1460,6 +1476,37 @@ static int param_get_aaintbool(char *buffer, const struct kernel_param *kp)
|
||||||
return param_get_bool(buffer, &kp_local);
|
return param_get_bool(buffer, &kp_local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
static int param_get_audit(char *buffer, const struct kernel_param *kp)
|
||||||
{
|
{
|
||||||
if (!apparmor_enabled)
|
if (!apparmor_enabled)
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/zlib.h>
|
||||||
|
|
||||||
#include "include/apparmor.h"
|
#include "include/apparmor.h"
|
||||||
#include "include/audit.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)
|
if (l->size != r->size)
|
||||||
return false;
|
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)
|
if (aa_g_hash_policy && memcmp(l->hash, r->hash, aa_hash_size()) != 0)
|
||||||
return false;
|
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;
|
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
|
* aa_unpack - unpack packed binary profile(s) data loaded from user space
|
||||||
* @udata: user data copied to kmem (NOT NULL)
|
* @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;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
error = compress_loaddata(udata);
|
||||||
|
if (error)
|
||||||
|
goto fail;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail_profile:
|
fail_profile:
|
||||||
|
|
Loading…
Reference in New Issue