mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 16:35:02 +01:00
Add caching of capability and syscall reject logging so that only the
first reject or complain message is logged. This greatly reduces the pressure on the auditing subsystem. The audit log message caching is per task and applies to both complain and reject mode messages. The of the active task through profile replacement, setting, or changehat will cause the cache to clear so a new message can be emitted.
This commit is contained in:
parent
f562cc440f
commit
257e91530d
5 changed files with 127 additions and 18 deletions
|
@ -64,6 +64,18 @@ extern int apparmor_logsyscall;
|
|||
#define AA_WARN(fmt, args...) printk(KERN_WARNING "AppArmor: " fmt, ##args)
|
||||
#define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args)
|
||||
|
||||
|
||||
/* apparmor logged syscall reject caching */
|
||||
enum aasyscall {
|
||||
AA_SYSCALL_PTRACE,
|
||||
AA_SYSCALL_SYSCTL_WRITE,
|
||||
AA_SYSCALL_MOUNT,
|
||||
AA_SYSCALL_UMOUNT
|
||||
};
|
||||
|
||||
#define AA_SYSCALL_TO_MASK(X) (1 << (X))
|
||||
|
||||
|
||||
/* basic AppArmor data structures */
|
||||
|
||||
struct flagval {
|
||||
|
@ -172,6 +184,8 @@ struct aafile {
|
|||
* @hat_magic: the magic token controling the ability to leave a hat
|
||||
* @list: list this subdomain is on
|
||||
* @task: task that the subdomain confines
|
||||
* @cached_caps: caps that have previously generated log entries
|
||||
* @cached_syscalls: mediated syscalls that have previously been logged
|
||||
*
|
||||
* Contains the tasks current active profile (which could change due to
|
||||
* change_hat). Plus the hat_magic needed during change_hat.
|
||||
|
@ -184,6 +198,9 @@ struct subdomain {
|
|||
u32 hat_magic; /* used with change_hat */
|
||||
struct list_head list; /* list of subdomains */
|
||||
struct task_struct *task;
|
||||
|
||||
kernel_cap_t cached_caps;
|
||||
unsigned int cached_syscalls;
|
||||
};
|
||||
|
||||
typedef int (*aa_iter) (struct subdomain *, void *);
|
||||
|
@ -276,7 +293,7 @@ extern int attach_nullprofile(struct aaprofile *profile);
|
|||
extern int aa_audit_message(struct aaprofile *active, gfp_t gfp, int,
|
||||
const char *, ...);
|
||||
extern int aa_audit_syscallreject(struct aaprofile *active, gfp_t gfp,
|
||||
const char *);
|
||||
enum aasyscall call);
|
||||
extern int aa_audit(struct aaprofile *active, const struct aa_audit *);
|
||||
extern char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt);
|
||||
|
||||
|
@ -334,5 +351,6 @@ extern void destroy_apparmorfs(void);
|
|||
|
||||
/* capabilities.c */
|
||||
extern const char *capability_to_name(unsigned int cap);
|
||||
extern const char *syscall_to_name(enum aasyscall call);
|
||||
|
||||
#endif /* __APPARMOR_H */
|
||||
|
|
|
@ -54,3 +54,18 @@ const char *capability_to_name(unsigned int cap)
|
|||
|
||||
return name;
|
||||
}
|
||||
|
||||
static const char *syscall_names[] = {
|
||||
"ptrace",
|
||||
"sysctl (write)",
|
||||
"mount",
|
||||
"umount"
|
||||
};
|
||||
|
||||
const char *syscall_to_name(enum aasyscall call)
|
||||
{
|
||||
const char *name;
|
||||
name = (call < (sizeof(syscall_names) / sizeof(char *))
|
||||
? syscall_names[call] : "invalid-syscall");
|
||||
return name;
|
||||
}
|
||||
|
|
|
@ -114,6 +114,62 @@ static inline struct aaprofile *get_active_aaprofile(void)
|
|||
return get_task_active_aaprofile(current);
|
||||
}
|
||||
|
||||
/**
|
||||
* cap_is_cached - check if @cap access has already been logged for current
|
||||
* @cap: capability to test if cached
|
||||
*/
|
||||
static inline int cap_is_cached(int cap)
|
||||
{
|
||||
struct subdomain *sd = AA_SUBDOMAIN(current->security);
|
||||
return cap_raised(sd->cached_caps, cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* add_to_cached_caps - add a capability to the tasks logged capabilities cache
|
||||
* @cap: the capability to add
|
||||
*/
|
||||
static inline void add_to_cached_caps(int cap)
|
||||
{
|
||||
struct subdomain *sd = AA_SUBDOMAIN(current->security);
|
||||
sd->cached_caps = cap_combine(sd->cached_caps, CAP_TO_MASK(cap));
|
||||
}
|
||||
|
||||
/**
|
||||
* clear_cached_caps - clear the tasks logged capabilities cache
|
||||
*/
|
||||
static inline void clear_cached_caps(struct subdomain *sd)
|
||||
{
|
||||
sd->cached_caps = CAP_EMPTY_SET;
|
||||
}
|
||||
|
||||
/**
|
||||
* syscall_is_cached - check if @call access has already been logged
|
||||
* @call: syscall to test if cached
|
||||
*/
|
||||
static inline int syscall_is_cached(enum aasyscall call)
|
||||
{
|
||||
struct subdomain *sd = AA_SUBDOMAIN(current->security);
|
||||
return sd->cached_syscalls & AA_SYSCALL_TO_MASK(call);
|
||||
}
|
||||
|
||||
/**
|
||||
* add_to_cached_syscalls - add a syscall to the tasks logged syscalls cache
|
||||
* @call: the syscall to add
|
||||
*/
|
||||
static inline void add_to_cached_syscalls(enum aasyscall call)
|
||||
{
|
||||
struct subdomain *sd = AA_SUBDOMAIN(current->security);
|
||||
sd->cached_syscalls |= AA_SYSCALL_TO_MASK(call);
|
||||
}
|
||||
|
||||
/**
|
||||
* clear_cached_syscalls - clear the tasks logged syscalls cache
|
||||
*/
|
||||
static inline void clear_cached_syscalls(struct subdomain *sd)
|
||||
{
|
||||
sd->cached_syscalls = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_switch - change subdomain to use a new profile
|
||||
* @sd: subdomain to switch the active profile on
|
||||
|
@ -132,6 +188,8 @@ static inline void aa_switch(struct subdomain *sd, struct aaprofile *newactive)
|
|||
|
||||
/* noop if NULL */
|
||||
rcu_assign_pointer(sd->active, get_aaprofile(newactive));
|
||||
clear_cached_caps(sd);
|
||||
clear_cached_syscalls(sd);
|
||||
put_aaprofile(oldactive);
|
||||
}
|
||||
|
||||
|
|
|
@ -92,10 +92,9 @@ static int apparmor_ptrace(struct task_struct *parent,
|
|||
|
||||
active = get_task_active_aaprofile(parent);
|
||||
|
||||
if (!error && active) {
|
||||
error = aa_audit_syscallreject(active, GFP_KERNEL, "ptrace");
|
||||
WARN_ON(error != -EPERM);
|
||||
}
|
||||
if (!error && active)
|
||||
error = aa_audit_syscallreject(active, GFP_KERNEL,
|
||||
AA_SYSCALL_PTRACE);
|
||||
|
||||
put_aaprofile(active);
|
||||
|
||||
|
@ -157,7 +156,7 @@ static int apparmor_sysctl(struct ctl_table *table, int op)
|
|||
|
||||
if ((op & 002) && active && !capable(CAP_SYS_ADMIN)) {
|
||||
error = aa_audit_syscallreject(active, GFP_KERNEL,
|
||||
"sysctl (write)");
|
||||
AA_SYSCALL_SYSCTL_WRITE);
|
||||
WARN_ON(error != -EPERM);
|
||||
}
|
||||
|
||||
|
@ -220,7 +219,8 @@ static int apparmor_sb_mount(char *dev_name, struct nameidata *nd, char *type,
|
|||
active = get_active_aaprofile();
|
||||
|
||||
if (active) {
|
||||
error = aa_audit_syscallreject(active, GFP_KERNEL, "mount");
|
||||
error = aa_audit_syscallreject(active, GFP_KERNEL,
|
||||
AA_SYSCALL_MOUNT);
|
||||
WARN_ON(error != -EPERM);
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,8 @@ static int apparmor_umount(struct vfsmount *mnt, int flags)
|
|||
active = get_active_aaprofile();
|
||||
|
||||
if (active) {
|
||||
error = aa_audit_syscallreject(active, GFP_ATOMIC, "umount");
|
||||
error = aa_audit_syscallreject(active, GFP_ATOMIC,
|
||||
AA_SYSCALL_UMOUNT);
|
||||
WARN_ON(error != -EPERM);
|
||||
}
|
||||
|
||||
|
|
|
@ -546,22 +546,28 @@ int aa_audit_message(struct aaprofile *active, gfp_t gfp, int flags,
|
|||
/**
|
||||
* aa_audit_syscallreject - Log a syscall rejection to the audit subsystem
|
||||
* @active: profile to check against
|
||||
* @msg: string describing syscall being rejected
|
||||
* @gfp: memory allocation flags
|
||||
* @call: aa syscall cache bit number
|
||||
*/
|
||||
int aa_audit_syscallreject(struct aaprofile *active, gfp_t gfp,
|
||||
const char *msg)
|
||||
enum aasyscall call)
|
||||
{
|
||||
struct aa_audit sa;
|
||||
int error = -EPERM;
|
||||
|
||||
sa.type = AA_AUDITTYPE_SYSCALL;
|
||||
sa.name = msg;
|
||||
sa.flags = 0;
|
||||
sa.gfp_mask = gfp;
|
||||
sa.error_code = 0;
|
||||
sa.result = 0; /* failure */
|
||||
if (!syscall_is_cached(call)) {
|
||||
sa.type = AA_AUDITTYPE_SYSCALL;
|
||||
sa.name = syscall_to_name(call);
|
||||
sa.flags = 0;
|
||||
sa.gfp_mask = gfp;
|
||||
sa.error_code = 0;
|
||||
sa.result = 0; /* failure */
|
||||
|
||||
return aa_audit(active, &sa);
|
||||
error = aa_audit(active, &sa);
|
||||
if (error == -EPERM)
|
||||
add_to_cached_syscalls(call);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -619,6 +625,16 @@ int aa_audit(struct aaprofile *active, const struct aa_audit *sa)
|
|||
logcls = complain ? "PERMITTING" : "REJECTING";
|
||||
}
|
||||
|
||||
/* test if event has already been logged and cached used to log
|
||||
* only first time event occurs.
|
||||
*/
|
||||
if (sa->type == AA_AUDITTYPE_CAP) {
|
||||
if (cap_is_cached(sa->ival)) {
|
||||
opspec_error = -EPERM;
|
||||
goto skip_logging;
|
||||
}
|
||||
}
|
||||
|
||||
/* In future extend w/ per-profile flags
|
||||
* (flags |= sa->active->flags)
|
||||
*/
|
||||
|
@ -725,7 +741,7 @@ int aa_audit(struct aaprofile *active, const struct aa_audit *sa)
|
|||
audit_log_format(ab,
|
||||
"access to capability '%s' ",
|
||||
capability_to_name(sa->ival));
|
||||
|
||||
add_to_cached_caps(sa->ival);
|
||||
opspec_error = -EPERM;
|
||||
} else if (sa->type == AA_AUDITTYPE_SYSCALL) {
|
||||
audit_log_format(ab, "access to syscall '%s' ", sa->name);
|
||||
|
@ -742,6 +758,7 @@ int aa_audit(struct aaprofile *active, const struct aa_audit *sa)
|
|||
|
||||
audit_log_end(ab);
|
||||
|
||||
skip_logging:
|
||||
if (complain)
|
||||
error = 0;
|
||||
else
|
||||
|
|
Loading…
Add table
Reference in a new issue