apparmor: make export of raw binary profile to userspace optional
Embedded systems have limited space and don't need the introspection or checkpoint restore capability provided by exporting the raw profile binary data so make it so make it a config option. This will reduce run time memory use and also speed up policy loads. Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
parent
65cc9c391c
commit
d61c57fde8
|
@ -6,8 +6,6 @@ config SECURITY_APPARMOR
|
|||
select SECURITY_PATH
|
||||
select SECURITYFS
|
||||
select SECURITY_NETWORK
|
||||
select ZLIB_INFLATE
|
||||
select ZLIB_DEFLATE
|
||||
default n
|
||||
help
|
||||
This enables the AppArmor security module.
|
||||
|
@ -17,32 +15,6 @@ config SECURITY_APPARMOR
|
|||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_APPARMOR_HASH
|
||||
bool "Enable introspection of sha1 hashes for loaded profiles"
|
||||
depends on SECURITY_APPARMOR
|
||||
select CRYPTO
|
||||
select CRYPTO_SHA1
|
||||
default y
|
||||
help
|
||||
This option selects whether introspection of loaded policy
|
||||
hashes is available to userspace via the apparmor
|
||||
filesystem. This option provides a light weight means of
|
||||
checking loaded policy. This option adds to policy load
|
||||
time and can be disabled for small embedded systems.
|
||||
|
||||
config SECURITY_APPARMOR_HASH_DEFAULT
|
||||
bool "Enable policy hash introspection by default"
|
||||
depends on SECURITY_APPARMOR_HASH
|
||||
default y
|
||||
help
|
||||
This option selects whether sha1 hashing of loaded policy
|
||||
is enabled by default. The generation of sha1 hashes for
|
||||
loaded policy provide system administrators a quick way
|
||||
to verify that policy in the kernel matches what is expected,
|
||||
however it can slow down policy load on some devices. In
|
||||
these cases policy hashing can be disabled by default and
|
||||
enabled only if needed.
|
||||
|
||||
config SECURITY_APPARMOR_DEBUG
|
||||
bool "Build AppArmor with debug code"
|
||||
depends on SECURITY_APPARMOR
|
||||
|
@ -72,6 +44,56 @@ config SECURITY_APPARMOR_DEBUG_MESSAGES
|
|||
When enabled, various debug messages will be logged to
|
||||
the kernel message buffer.
|
||||
|
||||
config SECURITY_APPARMOR_INTROSPECT_POLICY
|
||||
bool "Allow loaded policy to be introspected"
|
||||
depends on SECURITY_APPARMOR
|
||||
default y
|
||||
help
|
||||
This option selects whether introspection of loaded policy
|
||||
is available to userspace via the apparmor filesystem. This
|
||||
adds to kernel memory usage. It is required for introspection
|
||||
of loaded policy, and check point and restore support. It
|
||||
can be disabled for embedded systems where reducing memory and
|
||||
cpu is paramount.
|
||||
|
||||
config SECURITY_APPARMOR_HASH
|
||||
bool "Enable introspection of sha1 hashes for loaded profiles"
|
||||
depends on SECURITY_APPARMOR_INTROSPECT_POLICY
|
||||
select CRYPTO
|
||||
select CRYPTO_SHA1
|
||||
default y
|
||||
help
|
||||
This option selects whether introspection of loaded policy
|
||||
hashes is available to userspace via the apparmor
|
||||
filesystem. This option provides a light weight means of
|
||||
checking loaded policy. This option adds to policy load
|
||||
time and can be disabled for small embedded systems.
|
||||
|
||||
config SECURITY_APPARMOR_HASH_DEFAULT
|
||||
bool "Enable policy hash introspection by default"
|
||||
depends on SECURITY_APPARMOR_HASH
|
||||
default y
|
||||
help
|
||||
This option selects whether sha1 hashing of loaded policy
|
||||
is enabled by default. The generation of sha1 hashes for
|
||||
loaded policy provide system administrators a quick way
|
||||
to verify that policy in the kernel matches what is expected,
|
||||
however it can slow down policy load on some devices. In
|
||||
these cases policy hashing can be disabled by default and
|
||||
enabled only if needed.
|
||||
|
||||
config SECURITY_APPARMOR_EXPORT_BINARY
|
||||
bool "Allow exporting the raw binary policy"
|
||||
depends on SECURITY_APPARMOR_INTROSPECT_POLICY
|
||||
select ZLIB_INFLATE
|
||||
select ZLIB_DEFLATE
|
||||
default y
|
||||
help
|
||||
This option allows reading back binary policy as it was loaded.
|
||||
It increases the amount of kernel memory needed by policy and
|
||||
also increases policy load time. This option is required for
|
||||
checkpoint and restore support, and debugging of loaded policy.
|
||||
|
||||
config SECURITY_APPARMOR_KUNIT_TEST
|
||||
bool "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT=y && SECURITY_APPARMOR
|
||||
|
|
|
@ -70,6 +70,7 @@ struct rawdata_f_data {
|
|||
struct aa_loaddata *loaddata;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)
|
||||
|
||||
static void rawdata_f_data_free(struct rawdata_f_data *private)
|
||||
|
@ -94,6 +95,7 @@ static struct rawdata_f_data *rawdata_f_data_alloc(size_t size)
|
|||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* aa_mangle_name - mangle a profile name to std profile layout form
|
||||
|
@ -1201,7 +1203,7 @@ SEQ_NS_FOPS(name);
|
|||
|
||||
|
||||
/* policy/raw_data/ * file ops */
|
||||
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
#define SEQ_RAWDATA_FOPS(NAME) \
|
||||
static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\
|
||||
{ \
|
||||
|
@ -1492,6 +1494,8 @@ fail:
|
|||
|
||||
return PTR_ERR(dent);
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
|
||||
|
||||
|
||||
/** fns to setup dynamic per profile/namespace files **/
|
||||
|
||||
|
@ -1557,6 +1561,7 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name,
|
|||
return dent;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
static int profile_depth(struct aa_profile *profile)
|
||||
{
|
||||
int depth = 0;
|
||||
|
@ -1658,7 +1663,7 @@ static const struct inode_operations rawdata_link_abi_iops = {
|
|||
static const struct inode_operations rawdata_link_data_iops = {
|
||||
.get_link = rawdata_get_link_data,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
|
||||
|
||||
/*
|
||||
* Requires: @profile->ns->lock held
|
||||
|
@ -1729,6 +1734,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
|
|||
profile->dents[AAFS_PROF_HASH] = dent;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
if (profile->rawdata) {
|
||||
dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir,
|
||||
profile->label.proxy, NULL, NULL,
|
||||
|
@ -1754,6 +1760,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
|
|||
aa_get_proxy(profile->label.proxy);
|
||||
profile->dents[AAFS_PROF_RAW_DATA] = dent;
|
||||
}
|
||||
#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
|
||||
|
||||
list_for_each_entry(child, &profile->base.profiles, base.list) {
|
||||
error = __aafs_profile_mkdir(child, prof_child_dir(profile));
|
||||
|
|
|
@ -36,6 +36,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 bool aa_g_export_binary;
|
||||
extern int aa_g_rawdata_compression_level;
|
||||
extern bool aa_g_lock_policy;
|
||||
extern bool aa_g_logsyscall;
|
||||
|
|
|
@ -114,7 +114,21 @@ int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
|
|||
struct dentry *dent);
|
||||
|
||||
struct aa_loaddata;
|
||||
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata);
|
||||
int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata);
|
||||
#else
|
||||
static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
|
||||
{
|
||||
/* empty stub */
|
||||
}
|
||||
|
||||
static inline int __aa_fs_create_rawdata(struct aa_ns *ns,
|
||||
struct aa_loaddata *rawdata)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
|
||||
|
||||
#endif /* __AA_APPARMORFS_H */
|
||||
|
|
|
@ -1357,6 +1357,12 @@ 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
|
||||
|
||||
/* whether policy exactly as loaded is retained for debug and checkpointing */
|
||||
bool aa_g_export_binary = IS_ENABLED(CONFIG_SECURITY_APPARMOR_EXPORT_BINARY);
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
module_param_named(export_binary, aa_g_export_binary, aabool, 0600);
|
||||
#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,
|
||||
|
|
|
@ -952,16 +952,18 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
|||
|
||||
mutex_lock_nested(&ns->lock, ns->level);
|
||||
/* check for duplicate rawdata blobs: space and file dedup */
|
||||
list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) {
|
||||
if (aa_rawdata_eq(rawdata_ent, udata)) {
|
||||
struct aa_loaddata *tmp;
|
||||
if (!list_empty(&ns->rawdata_list)) {
|
||||
list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) {
|
||||
if (aa_rawdata_eq(rawdata_ent, udata)) {
|
||||
struct aa_loaddata *tmp;
|
||||
|
||||
tmp = __aa_get_loaddata(rawdata_ent);
|
||||
/* check we didn't fail the race */
|
||||
if (tmp) {
|
||||
aa_put_loaddata(udata);
|
||||
udata = tmp;
|
||||
break;
|
||||
tmp = __aa_get_loaddata(rawdata_ent);
|
||||
/* check we didn't fail the race */
|
||||
if (tmp) {
|
||||
aa_put_loaddata(udata);
|
||||
udata = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -969,7 +971,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
|||
list_for_each_entry(ent, &lh, list) {
|
||||
struct aa_policy *policy;
|
||||
|
||||
ent->new->rawdata = aa_get_loaddata(udata);
|
||||
if (aa_g_export_binary)
|
||||
ent->new->rawdata = aa_get_loaddata(udata);
|
||||
error = __lookup_replace(ns, ent->new->base.hname,
|
||||
!(mask & AA_MAY_REPLACE_POLICY),
|
||||
&ent->old, &info);
|
||||
|
@ -1009,7 +1012,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
|||
}
|
||||
|
||||
/* create new fs entries for introspection if needed */
|
||||
if (!udata->dents[AAFS_LOADDATA_DIR]) {
|
||||
if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) {
|
||||
error = __aa_fs_create_rawdata(ns, udata);
|
||||
if (error) {
|
||||
info = "failed to create raw_data dir and files";
|
||||
|
@ -1037,12 +1040,14 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
|||
|
||||
/* Done with checks that may fail - do actual replacement */
|
||||
__aa_bump_ns_revision(ns);
|
||||
__aa_loaddata_update(udata, ns->revision);
|
||||
if (aa_g_export_binary)
|
||||
__aa_loaddata_update(udata, ns->revision);
|
||||
list_for_each_entry_safe(ent, tmp, &lh, list) {
|
||||
list_del_init(&ent->list);
|
||||
op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL;
|
||||
|
||||
if (ent->old && ent->old->rawdata == ent->new->rawdata) {
|
||||
if (ent->old && ent->old->rawdata == ent->new->rawdata &&
|
||||
ent->new->rawdata) {
|
||||
/* dedup actual profile replacement */
|
||||
audit_policy(label, op, ns_name, ent->new->base.hname,
|
||||
"same as current profile, skipping",
|
||||
|
|
|
@ -125,15 +125,16 @@ void __aa_loaddata_update(struct aa_loaddata *data, long revision)
|
|||
{
|
||||
AA_BUG(!data);
|
||||
AA_BUG(!data->ns);
|
||||
AA_BUG(!data->dents[AAFS_LOADDATA_REVISION]);
|
||||
AA_BUG(!mutex_is_locked(&data->ns->lock));
|
||||
AA_BUG(data->revision > revision);
|
||||
|
||||
data->revision = revision;
|
||||
d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
|
||||
current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
|
||||
d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
|
||||
current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
|
||||
if ((data->dents[AAFS_LOADDATA_REVISION])) {
|
||||
d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
|
||||
current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
|
||||
d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
|
||||
current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
|
||||
}
|
||||
}
|
||||
|
||||
bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r)
|
||||
|
@ -1216,9 +1217,12 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
|
|||
goto fail;
|
||||
}
|
||||
}
|
||||
error = compress_loaddata(udata);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
if (aa_g_export_binary) {
|
||||
error = compress_loaddata(udata);
|
||||
if (error)
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail_profile:
|
||||
|
|
Loading…
Reference in New Issue