Commit d61c57fd authored by John Johansen's avatar John Johansen

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: default avatarJohn Johansen <john.johansen@canonical.com>
parent 65cc9c39
...@@ -6,8 +6,6 @@ config SECURITY_APPARMOR ...@@ -6,8 +6,6 @@ config SECURITY_APPARMOR
select SECURITY_PATH select SECURITY_PATH
select SECURITYFS select SECURITYFS
select SECURITY_NETWORK select SECURITY_NETWORK
select ZLIB_INFLATE
select ZLIB_DEFLATE
default n default n
help help
This enables the AppArmor security module. This enables the AppArmor security module.
...@@ -17,32 +15,6 @@ config SECURITY_APPARMOR ...@@ -17,32 +15,6 @@ config SECURITY_APPARMOR
If you are unsure how to answer this question, answer N. 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 config SECURITY_APPARMOR_DEBUG
bool "Build AppArmor with debug code" bool "Build AppArmor with debug code"
depends on SECURITY_APPARMOR depends on SECURITY_APPARMOR
...@@ -72,6 +44,56 @@ config SECURITY_APPARMOR_DEBUG_MESSAGES ...@@ -72,6 +44,56 @@ config SECURITY_APPARMOR_DEBUG_MESSAGES
When enabled, various debug messages will be logged to When enabled, various debug messages will be logged to
the kernel message buffer. 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 config SECURITY_APPARMOR_KUNIT_TEST
bool "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS bool "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS
depends on KUNIT=y && SECURITY_APPARMOR depends on KUNIT=y && SECURITY_APPARMOR
......
...@@ -70,6 +70,7 @@ struct rawdata_f_data { ...@@ -70,6 +70,7 @@ struct rawdata_f_data {
struct aa_loaddata *loaddata; struct aa_loaddata *loaddata;
}; };
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1) #define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)
static void rawdata_f_data_free(struct rawdata_f_data *private) 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) ...@@ -94,6 +95,7 @@ static struct rawdata_f_data *rawdata_f_data_alloc(size_t size)
return ret; return ret;
} }
#endif
/** /**
* aa_mangle_name - mangle a profile name to std profile layout form * aa_mangle_name - mangle a profile name to std profile layout form
...@@ -1201,7 +1203,7 @@ SEQ_NS_FOPS(name); ...@@ -1201,7 +1203,7 @@ SEQ_NS_FOPS(name);
/* policy/raw_data/ * file ops */ /* policy/raw_data/ * file ops */
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
#define SEQ_RAWDATA_FOPS(NAME) \ #define SEQ_RAWDATA_FOPS(NAME) \
static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\ static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\
{ \ { \
...@@ -1492,6 +1494,8 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata) ...@@ -1492,6 +1494,8 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
return PTR_ERR(dent); return PTR_ERR(dent);
} }
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
/** fns to setup dynamic per profile/namespace files **/ /** fns to setup dynamic per profile/namespace files **/
...@@ -1557,6 +1561,7 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name, ...@@ -1557,6 +1561,7 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name,
return dent; return dent;
} }
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
static int profile_depth(struct aa_profile *profile) static int profile_depth(struct aa_profile *profile)
{ {
int depth = 0; int depth = 0;
...@@ -1658,7 +1663,7 @@ static const struct inode_operations rawdata_link_abi_iops = { ...@@ -1658,7 +1663,7 @@ static const struct inode_operations rawdata_link_abi_iops = {
static const struct inode_operations rawdata_link_data_iops = { static const struct inode_operations rawdata_link_data_iops = {
.get_link = rawdata_get_link_data, .get_link = rawdata_get_link_data,
}; };
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
/* /*
* Requires: @profile->ns->lock held * Requires: @profile->ns->lock held
...@@ -1729,6 +1734,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) ...@@ -1729,6 +1734,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
profile->dents[AAFS_PROF_HASH] = dent; profile->dents[AAFS_PROF_HASH] = dent;
} }
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
if (profile->rawdata) { if (profile->rawdata) {
dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir, dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir,
profile->label.proxy, NULL, NULL, profile->label.proxy, NULL, NULL,
...@@ -1754,6 +1760,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) ...@@ -1754,6 +1760,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
aa_get_proxy(profile->label.proxy); aa_get_proxy(profile->label.proxy);
profile->dents[AAFS_PROF_RAW_DATA] = dent; profile->dents[AAFS_PROF_RAW_DATA] = dent;
} }
#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
list_for_each_entry(child, &profile->base.profiles, base.list) { list_for_each_entry(child, &profile->base.profiles, base.list) {
error = __aafs_profile_mkdir(child, prof_child_dir(profile)); error = __aafs_profile_mkdir(child, prof_child_dir(profile));
......
...@@ -36,6 +36,7 @@ extern enum audit_mode aa_g_audit; ...@@ -36,6 +36,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 bool aa_g_export_binary;
extern int aa_g_rawdata_compression_level; 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;
......
...@@ -114,7 +114,21 @@ int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name, ...@@ -114,7 +114,21 @@ int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
struct dentry *dent); struct dentry *dent);
struct aa_loaddata; struct aa_loaddata;
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata); void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata);
int __aa_fs_create_rawdata(struct aa_ns *ns, 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 */ #endif /* __AA_APPARMORFS_H */
...@@ -1357,6 +1357,12 @@ bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT); ...@@ -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); module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
#endif #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 */ /* policy loaddata compression level */
int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION; int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION;
module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level, 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, ...@@ -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); mutex_lock_nested(&ns->lock, ns->level);
/* check for duplicate rawdata blobs: space and file dedup */ /* check for duplicate rawdata blobs: space and file dedup */
list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) { if (!list_empty(&ns->rawdata_list)) {
if (aa_rawdata_eq(rawdata_ent, udata)) { list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) {
struct aa_loaddata *tmp; if (aa_rawdata_eq(rawdata_ent, udata)) {
struct aa_loaddata *tmp;
tmp = __aa_get_loaddata(rawdata_ent);
/* check we didn't fail the race */ tmp = __aa_get_loaddata(rawdata_ent);
if (tmp) { /* check we didn't fail the race */
aa_put_loaddata(udata); if (tmp) {
udata = tmp; aa_put_loaddata(udata);
break; udata = tmp;
break;
}
} }
} }
} }
...@@ -969,7 +971,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, ...@@ -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) { list_for_each_entry(ent, &lh, list) {
struct aa_policy *policy; 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, error = __lookup_replace(ns, ent->new->base.hname,
!(mask & AA_MAY_REPLACE_POLICY), !(mask & AA_MAY_REPLACE_POLICY),
&ent->old, &info); &ent->old, &info);
...@@ -1009,7 +1012,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, ...@@ -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 */ /* 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); error = __aa_fs_create_rawdata(ns, udata);
if (error) { if (error) {
info = "failed to create raw_data dir and files"; 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, ...@@ -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 */ /* Done with checks that may fail - do actual replacement */
__aa_bump_ns_revision(ns); __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_for_each_entry_safe(ent, tmp, &lh, list) {
list_del_init(&ent->list); list_del_init(&ent->list);
op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; 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 */ /* dedup actual profile replacement */
audit_policy(label, op, ns_name, ent->new->base.hname, audit_policy(label, op, ns_name, ent->new->base.hname,
"same as current profile, skipping", "same as current profile, skipping",
......
...@@ -125,15 +125,16 @@ void __aa_loaddata_update(struct aa_loaddata *data, long revision) ...@@ -125,15 +125,16 @@ void __aa_loaddata_update(struct aa_loaddata *data, long revision)
{ {
AA_BUG(!data); AA_BUG(!data);
AA_BUG(!data->ns); AA_BUG(!data->ns);
AA_BUG(!data->dents[AAFS_LOADDATA_REVISION]);
AA_BUG(!mutex_is_locked(&data->ns->lock)); AA_BUG(!mutex_is_locked(&data->ns->lock));
AA_BUG(data->revision > revision); AA_BUG(data->revision > revision);
data->revision = revision; data->revision = revision;
d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime = if ((data->dents[AAFS_LOADDATA_REVISION])) {
current_time(d_inode(data->dents[AAFS_LOADDATA_DIR])); d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime = current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION])); 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) 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, ...@@ -1216,9 +1217,12 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
goto fail; goto fail;
} }
} }
error = compress_loaddata(udata);
if (error) if (aa_g_export_binary) {
goto fail; error = compress_loaddata(udata);
if (error)
goto fail;
}
return 0; return 0;
fail_profile: fail_profile:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment