Skip to content

Commit fee7a23

Browse files
rlee287jrjohansen
authored andcommitted
apparmor: add a cache entry expiration time aging out capability audit cache
When auditing capabilities, AppArmor uses a per-CPU, per-profile cache such that the same capability for the same profile doesn't get repeatedly audited, with the original goal of reducing audit logspam. However, this cache does not have an expiration time, resulting in confusion when a profile is shared across binaries (for example) and an expected DENIED audit entry doesn't appear, despite the cache entry having been populated much longer ago. This confusion was exacerbated by the per-CPU nature of the cache resulting in the expected entries sporadically appearing when the later denial+audit occurred on a different CPU. To resolve this, record the last time a capability was audited for a profile and add a timestamp expiration check before doing the audit. v1 -> v2: - Hardcode a longer timeout and drop the patches making it a sysctl, after discussion with John Johansen. - Cache the expiration time instead of the last-audited time. This value can never be zero, which lets us drop the kernel_cap_t caps field from the cache struct. Signed-off-by: Ryan Lee <[email protected]> Signed-off-by: John Johansen <[email protected]>
1 parent 8532503 commit fee7a23

File tree

1 file changed

+8
-3
lines changed

1 file changed

+8
-3
lines changed

security/apparmor/capability.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/errno.h>
1313
#include <linux/gfp.h>
1414
#include <linux/security.h>
15+
#include <linux/timekeeping.h>
1516

1617
#include "include/apparmor.h"
1718
#include "include/capability.h"
@@ -31,7 +32,8 @@ struct aa_sfs_entry aa_sfs_entry_caps[] = {
3132

3233
struct audit_cache {
3334
struct aa_profile *profile;
34-
kernel_cap_t caps;
35+
/* Capabilities go from 0 to CAP_LAST_CAP */
36+
u64 ktime_ns_expiration[CAP_LAST_CAP+1];
3537
};
3638

3739
static DEFINE_PER_CPU(struct audit_cache, audit_cache);
@@ -64,6 +66,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
6466
static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile,
6567
int cap, int error)
6668
{
69+
const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */
70+
6771
struct aa_ruleset *rules = list_first_entry(&profile->rules,
6872
typeof(*rules), list);
6973
struct audit_cache *ent;
@@ -89,7 +93,8 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
8993

9094
/* Do simple duplicate message elimination */
9195
ent = &get_cpu_var(audit_cache);
92-
if (profile == ent->profile && cap_raised(ent->caps, cap)) {
96+
/* If the capability was never raised the timestamp check would also catch that */
97+
if (profile == ent->profile && ktime_get_ns() <= ent->ktime_ns_expiration[cap]) {
9398
put_cpu_var(audit_cache);
9499
if (COMPLAIN_MODE(profile))
95100
return complain_error(error);
@@ -99,7 +104,7 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
99104
if (profile != ent->profile)
100105
cap_clear(ent->caps);
101106
ent->profile = aa_get_profile(profile);
102-
cap_raise(ent->caps, cap);
107+
ent->ktime_ns_expiration[cap] = ktime_get_ns() + AUDIT_CACHE_TIMEOUT_NS;
103108
}
104109
put_cpu_var(audit_cache);
105110

0 commit comments

Comments
 (0)