Skip to content

Commit 63c16c3

Browse files
chrisccoulsonjrjohansen
authored andcommitted
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 <[email protected]> Signed-off-by: John Johansen <[email protected]>
1 parent 582549e commit 63c16c3

File tree

5 files changed

+285
-8
lines changed

5 files changed

+285
-8
lines changed

security/apparmor/apparmorfs.c

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/rcupdate.h>
2525
#include <linux/fs.h>
2626
#include <linux/poll.h>
27+
#include <linux/zlib.h>
2728
#include <uapi/linux/major.h>
2829
#include <uapi/linux/magic.h>
2930

@@ -68,6 +69,35 @@
6869
* support fns
6970
*/
7071

72+
struct rawdata_f_data {
73+
struct aa_loaddata *loaddata;
74+
};
75+
76+
#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)
77+
78+
static void rawdata_f_data_free(struct rawdata_f_data *private)
79+
{
80+
if (!private)
81+
return;
82+
83+
aa_put_loaddata(private->loaddata);
84+
kvfree(private);
85+
}
86+
87+
static struct rawdata_f_data *rawdata_f_data_alloc(size_t size)
88+
{
89+
struct rawdata_f_data *ret;
90+
91+
if (size > SIZE_MAX - sizeof(*ret))
92+
return ERR_PTR(-EINVAL);
93+
94+
ret = kvzalloc(sizeof(*ret) + size, GFP_KERNEL);
95+
if (!ret)
96+
return ERR_PTR(-ENOMEM);
97+
98+
return ret;
99+
}
100+
71101
/**
72102
* aa_mangle_name - mangle a profile name to std profile layout form
73103
* @name: profile name to mangle (NOT NULL)
@@ -1275,36 +1305,117 @@ static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
12751305
return 0;
12761306
}
12771307

1308+
static int seq_rawdata_compressed_size_show(struct seq_file *seq, void *v)
1309+
{
1310+
struct aa_loaddata *data = seq->private;
1311+
1312+
seq_printf(seq, "%zu\n", data->compressed_size);
1313+
1314+
return 0;
1315+
}
1316+
12781317
SEQ_RAWDATA_FOPS(abi);
12791318
SEQ_RAWDATA_FOPS(revision);
12801319
SEQ_RAWDATA_FOPS(hash);
1320+
SEQ_RAWDATA_FOPS(compressed_size);
1321+
1322+
static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen)
1323+
{
1324+
int error;
1325+
struct z_stream_s strm;
1326+
1327+
if (aa_g_rawdata_compression_level == 0) {
1328+
if (dlen < slen)
1329+
return -EINVAL;
1330+
memcpy(dst, src, slen);
1331+
return 0;
1332+
}
1333+
1334+
memset(&strm, 0, sizeof(strm));
1335+
1336+
strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
1337+
if (!strm.workspace)
1338+
return -ENOMEM;
1339+
1340+
strm.next_in = src;
1341+
strm.avail_in = slen;
1342+
1343+
error = zlib_inflateInit(&strm);
1344+
if (error != Z_OK) {
1345+
error = -ENOMEM;
1346+
goto fail_inflate_init;
1347+
}
1348+
1349+
strm.next_out = dst;
1350+
strm.avail_out = dlen;
1351+
1352+
error = zlib_inflate(&strm, Z_FINISH);
1353+
if (error != Z_STREAM_END)
1354+
error = -EINVAL;
1355+
else
1356+
error = 0;
1357+
1358+
zlib_inflateEnd(&strm);
1359+
fail_inflate_init:
1360+
kvfree(strm.workspace);
1361+
return error;
1362+
}
12811363

12821364
static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
12831365
loff_t *ppos)
12841366
{
1285-
struct aa_loaddata *rawdata = file->private_data;
1367+
struct rawdata_f_data *private = file->private_data;
12861368

1287-
return simple_read_from_buffer(buf, size, ppos, rawdata->data,
1288-
rawdata->size);
1369+
return simple_read_from_buffer(buf, size, ppos,
1370+
RAWDATA_F_DATA_BUF(private),
1371+
private->loaddata->size);
12891372
}
12901373

12911374
static int rawdata_release(struct inode *inode, struct file *file)
12921375
{
1293-
aa_put_loaddata(file->private_data);
1376+
rawdata_f_data_free(file->private_data);
12941377

12951378
return 0;
12961379
}
12971380

12981381
static int rawdata_open(struct inode *inode, struct file *file)
12991382
{
1383+
int error;
1384+
struct aa_loaddata *loaddata;
1385+
struct rawdata_f_data *private;
1386+
13001387
if (!policy_view_capable(NULL))
13011388
return -EACCES;
1302-
file->private_data = __aa_get_loaddata(inode->i_private);
1303-
if (!file->private_data)
1389+
1390+
loaddata = __aa_get_loaddata(inode->i_private);
1391+
if (!loaddata)
13041392
/* lost race: this entry is being reaped */
13051393
return -ENOENT;
13061394

1395+
private = rawdata_f_data_alloc(loaddata->size);
1396+
if (IS_ERR(private)) {
1397+
error = PTR_ERR(private);
1398+
goto fail_private_alloc;
1399+
}
1400+
1401+
private->loaddata = loaddata;
1402+
1403+
error = deflate_decompress(loaddata->data, loaddata->compressed_size,
1404+
RAWDATA_F_DATA_BUF(private),
1405+
loaddata->size);
1406+
if (error)
1407+
goto fail_decompress;
1408+
1409+
file->private_data = private;
13071410
return 0;
1411+
1412+
fail_decompress:
1413+
rawdata_f_data_free(private);
1414+
return error;
1415+
1416+
fail_private_alloc:
1417+
aa_put_loaddata(loaddata);
1418+
return error;
13081419
}
13091420

13101421
static const struct file_operations rawdata_fops = {
@@ -1383,6 +1494,13 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
13831494
rawdata->dents[AAFS_LOADDATA_HASH] = dent;
13841495
}
13851496

1497+
dent = aafs_create_file("compressed_size", S_IFREG | 0444, dir,
1498+
rawdata,
1499+
&seq_rawdata_compressed_size_fops);
1500+
if (IS_ERR(dent))
1501+
goto fail;
1502+
rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent;
1503+
13861504
dent = aafs_create_file("raw_data", S_IFREG | 0444,
13871505
dir, rawdata, &rawdata_fops);
13881506
if (IS_ERR(dent))

security/apparmor/include/apparmor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ extern enum audit_mode aa_g_audit;
4040
extern bool aa_g_audit_header;
4141
extern bool aa_g_debug;
4242
extern bool aa_g_hash_policy;
43+
extern int aa_g_rawdata_compression_level;
4344
extern bool aa_g_lock_policy;
4445
extern bool aa_g_logsyscall;
4546
extern bool aa_g_paranoid_load;

security/apparmor/include/policy_unpack.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ enum {
4545
AAFS_LOADDATA_REVISION,
4646
AAFS_LOADDATA_HASH,
4747
AAFS_LOADDATA_DATA,
48+
AAFS_LOADDATA_COMPRESSED_SIZE,
4849
AAFS_LOADDATA_DIR, /* must be last actual entry */
4950
AAFS_LOADDATA_NDENTS /* count of entries */
5051
};
@@ -65,11 +66,16 @@ struct aa_loaddata {
6566
struct dentry *dents[AAFS_LOADDATA_NDENTS];
6667
struct aa_ns *ns;
6768
char *name;
68-
size_t size;
69+
size_t size; /* the original size of the payload */
70+
size_t compressed_size; /* the compressed size of the payload */
6971
long revision; /* the ns policy revision this caused */
7072
int abi;
7173
unsigned char *hash;
7274

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

security/apparmor/lsm.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/user_namespace.h>
2626
#include <linux/netfilter_ipv4.h>
2727
#include <linux/netfilter_ipv6.h>
28+
#include <linux/zlib.h>
2829
#include <net/sock.h>
2930
#include <uapi/linux/mount.h>
3031

@@ -1266,6 +1267,16 @@ static const struct kernel_param_ops param_ops_aauint = {
12661267
.get = param_get_aauint
12671268
};
12681269

1270+
static int param_set_aacompressionlevel(const char *val,
1271+
const struct kernel_param *kp);
1272+
static int param_get_aacompressionlevel(char *buffer,
1273+
const struct kernel_param *kp);
1274+
#define param_check_aacompressionlevel param_check_int
1275+
static const struct kernel_param_ops param_ops_aacompressionlevel = {
1276+
.set = param_set_aacompressionlevel,
1277+
.get = param_get_aacompressionlevel
1278+
};
1279+
12691280
static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp);
12701281
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp);
12711282
#define param_check_aalockpolicy param_check_bool
@@ -1296,6 +1307,11 @@ bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT);
12961307
module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
12971308
#endif
12981309

1310+
/* policy loaddata compression level */
1311+
int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION;
1312+
module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level,
1313+
aacompressionlevel, 0400);
1314+
12991315
/* Debug mode */
13001316
bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES);
13011317
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)
14601476
return param_get_bool(buffer, &kp_local);
14611477
}
14621478

1479+
static int param_set_aacompressionlevel(const char *val,
1480+
const struct kernel_param *kp)
1481+
{
1482+
int error;
1483+
1484+
if (!apparmor_enabled)
1485+
return -EINVAL;
1486+
if (apparmor_initialized)
1487+
return -EPERM;
1488+
1489+
error = param_set_int(val, kp);
1490+
1491+
aa_g_rawdata_compression_level = clamp(aa_g_rawdata_compression_level,
1492+
Z_NO_COMPRESSION,
1493+
Z_BEST_COMPRESSION);
1494+
pr_info("AppArmor: policy rawdata compression level set to %u\n",
1495+
aa_g_rawdata_compression_level);
1496+
1497+
return error;
1498+
}
1499+
1500+
static int param_get_aacompressionlevel(char *buffer,
1501+
const struct kernel_param *kp)
1502+
{
1503+
if (!apparmor_enabled)
1504+
return -EINVAL;
1505+
if (apparmor_initialized && !policy_view_capable(NULL))
1506+
return -EPERM;
1507+
return param_get_int(buffer, kp);
1508+
}
1509+
14631510
static int param_get_audit(char *buffer, const struct kernel_param *kp)
14641511
{
14651512
if (!apparmor_enabled)

0 commit comments

Comments
 (0)