Skip to content

Commit

Permalink
apparmor: make export of raw binary profile to userspace optional
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
John Johansen committed Jul 9, 2022
1 parent 65cc9c3 commit d61c57f
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 52 deletions.
78 changes: 50 additions & 28 deletions security/apparmor/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
11 changes: 9 additions & 2 deletions security/apparmor/apparmorfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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)\
{ \
Expand Down Expand Up @@ -1492,6 +1494,8 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)

return PTR_ERR(dent);
}
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */


/** fns to setup dynamic per profile/namespace files **/

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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));
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 @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions security/apparmor/include/apparmorfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
6 changes: 6 additions & 0 deletions security/apparmor/lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
33 changes: 19 additions & 14 deletions security/apparmor/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -952,24 +952,27 @@ 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;

tmp = __aa_get_loaddata(rawdata_ent);
/* check we didn't fail the race */
if (tmp) {
aa_put_loaddata(udata);
udata = tmp;
break;
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;
}
}
}
}
/* setup parent and ns info */
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);
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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",
Expand Down
20 changes: 12 additions & 8 deletions security/apparmor/policy_unpack.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit d61c57f

Please sign in to comment.