mirror of
https://github.com/adulau/aha.git
synced 2024-12-27 11:16:11 +00:00
Fix rule eviction order for AUDIT_DIR
If syscall removes the root of subtree being watched, we definitely do not want the rules refering that subtree to be destroyed without the syscall in question having a chance to match them. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
9d96098510
commit
916d75761c
4 changed files with 72 additions and 23 deletions
|
@ -133,7 +133,7 @@ static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
|
||||||
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
|
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
|
||||||
|
|
||||||
/* Serialize requests from userspace. */
|
/* Serialize requests from userspace. */
|
||||||
static DEFINE_MUTEX(audit_cmd_mutex);
|
DEFINE_MUTEX(audit_cmd_mutex);
|
||||||
|
|
||||||
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
|
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
|
||||||
* audit records. Since printk uses a 1024 byte buffer, this buffer
|
* audit records. Since printk uses a 1024 byte buffer, this buffer
|
||||||
|
@ -505,21 +505,6 @@ int audit_send_list(void *_dest)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_AUDIT_TREE
|
|
||||||
static int prune_tree_thread(void *unused)
|
|
||||||
{
|
|
||||||
mutex_lock(&audit_cmd_mutex);
|
|
||||||
audit_prune_trees();
|
|
||||||
mutex_unlock(&audit_cmd_mutex);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void audit_schedule_prune(void)
|
|
||||||
{
|
|
||||||
kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
|
struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
|
||||||
int multi, void *payload, int size)
|
int multi, void *payload, int size)
|
||||||
{
|
{
|
||||||
|
|
|
@ -128,10 +128,9 @@ extern int audit_add_tree_rule(struct audit_krule *);
|
||||||
extern int audit_remove_tree_rule(struct audit_krule *);
|
extern int audit_remove_tree_rule(struct audit_krule *);
|
||||||
extern void audit_trim_trees(void);
|
extern void audit_trim_trees(void);
|
||||||
extern int audit_tag_tree(char *old, char *new);
|
extern int audit_tag_tree(char *old, char *new);
|
||||||
extern void audit_schedule_prune(void);
|
|
||||||
extern void audit_prune_trees(void);
|
|
||||||
extern const char *audit_tree_path(struct audit_tree *);
|
extern const char *audit_tree_path(struct audit_tree *);
|
||||||
extern void audit_put_tree(struct audit_tree *);
|
extern void audit_put_tree(struct audit_tree *);
|
||||||
|
extern void audit_kill_trees(struct list_head *);
|
||||||
#else
|
#else
|
||||||
#define audit_remove_tree_rule(rule) BUG()
|
#define audit_remove_tree_rule(rule) BUG()
|
||||||
#define audit_add_tree_rule(rule) -EINVAL
|
#define audit_add_tree_rule(rule) -EINVAL
|
||||||
|
@ -140,6 +139,7 @@ extern void audit_put_tree(struct audit_tree *);
|
||||||
#define audit_put_tree(tree) (void)0
|
#define audit_put_tree(tree) (void)0
|
||||||
#define audit_tag_tree(old, new) -EINVAL
|
#define audit_tag_tree(old, new) -EINVAL
|
||||||
#define audit_tree_path(rule) "" /* never called */
|
#define audit_tree_path(rule) "" /* never called */
|
||||||
|
#define audit_kill_trees(list) BUG()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern char *audit_unpack_string(void **, size_t *, size_t);
|
extern char *audit_unpack_string(void **, size_t *, size_t);
|
||||||
|
@ -158,7 +158,10 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
|
extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
|
||||||
|
extern struct list_head *audit_killed_trees(void);
|
||||||
#else
|
#else
|
||||||
#define audit_signal_info(s,t) AUDIT_DISABLED
|
#define audit_signal_info(s,t) AUDIT_DISABLED
|
||||||
#define audit_filter_inodes(t,c) AUDIT_DISABLED
|
#define audit_filter_inodes(t,c) AUDIT_DISABLED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern struct mutex audit_cmd_mutex;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <linux/inotify.h>
|
#include <linux/inotify.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
|
#include <linux/kthread.h>
|
||||||
|
|
||||||
struct audit_tree;
|
struct audit_tree;
|
||||||
struct audit_chunk;
|
struct audit_chunk;
|
||||||
|
@ -517,6 +518,8 @@ static void trim_marked(struct audit_tree *tree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void audit_schedule_prune(void);
|
||||||
|
|
||||||
/* called with audit_filter_mutex */
|
/* called with audit_filter_mutex */
|
||||||
int audit_remove_tree_rule(struct audit_krule *rule)
|
int audit_remove_tree_rule(struct audit_krule *rule)
|
||||||
{
|
{
|
||||||
|
@ -822,10 +825,11 @@ int audit_tag_tree(char *old, char *new)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* That gets run when evict_chunk() ends up needing to kill audit_tree.
|
* That gets run when evict_chunk() ends up needing to kill audit_tree.
|
||||||
* Runs from a separate thread, with audit_cmd_mutex held.
|
* Runs from a separate thread.
|
||||||
*/
|
*/
|
||||||
void audit_prune_trees(void)
|
static int prune_tree_thread(void *unused)
|
||||||
{
|
{
|
||||||
|
mutex_lock(&audit_cmd_mutex);
|
||||||
mutex_lock(&audit_filter_mutex);
|
mutex_lock(&audit_filter_mutex);
|
||||||
|
|
||||||
while (!list_empty(&prune_list)) {
|
while (!list_empty(&prune_list)) {
|
||||||
|
@ -842,6 +846,40 @@ void audit_prune_trees(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&audit_filter_mutex);
|
mutex_unlock(&audit_filter_mutex);
|
||||||
|
mutex_unlock(&audit_cmd_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audit_schedule_prune(void)
|
||||||
|
{
|
||||||
|
kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ... and that one is done if evict_chunk() decides to delay until the end
|
||||||
|
* of syscall. Runs synchronously.
|
||||||
|
*/
|
||||||
|
void audit_kill_trees(struct list_head *list)
|
||||||
|
{
|
||||||
|
mutex_lock(&audit_cmd_mutex);
|
||||||
|
mutex_lock(&audit_filter_mutex);
|
||||||
|
|
||||||
|
while (!list_empty(list)) {
|
||||||
|
struct audit_tree *victim;
|
||||||
|
|
||||||
|
victim = list_entry(list->next, struct audit_tree, list);
|
||||||
|
kill_rules(victim);
|
||||||
|
list_del_init(&victim->list);
|
||||||
|
|
||||||
|
mutex_unlock(&audit_filter_mutex);
|
||||||
|
|
||||||
|
prune_one(victim);
|
||||||
|
|
||||||
|
mutex_lock(&audit_filter_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&audit_filter_mutex);
|
||||||
|
mutex_unlock(&audit_cmd_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -852,6 +890,8 @@ void audit_prune_trees(void)
|
||||||
static void evict_chunk(struct audit_chunk *chunk)
|
static void evict_chunk(struct audit_chunk *chunk)
|
||||||
{
|
{
|
||||||
struct audit_tree *owner;
|
struct audit_tree *owner;
|
||||||
|
struct list_head *postponed = audit_killed_trees();
|
||||||
|
int need_prune = 0;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (chunk->dead)
|
if (chunk->dead)
|
||||||
|
@ -867,15 +907,21 @@ static void evict_chunk(struct audit_chunk *chunk)
|
||||||
owner->root = NULL;
|
owner->root = NULL;
|
||||||
list_del_init(&owner->same_root);
|
list_del_init(&owner->same_root);
|
||||||
spin_unlock(&hash_lock);
|
spin_unlock(&hash_lock);
|
||||||
|
if (!postponed) {
|
||||||
kill_rules(owner);
|
kill_rules(owner);
|
||||||
list_move(&owner->list, &prune_list);
|
list_move(&owner->list, &prune_list);
|
||||||
audit_schedule_prune();
|
need_prune = 1;
|
||||||
|
} else {
|
||||||
|
list_move(&owner->list, postponed);
|
||||||
|
}
|
||||||
spin_lock(&hash_lock);
|
spin_lock(&hash_lock);
|
||||||
}
|
}
|
||||||
list_del_rcu(&chunk->hash);
|
list_del_rcu(&chunk->hash);
|
||||||
for (n = 0; n < chunk->count; n++)
|
for (n = 0; n < chunk->count; n++)
|
||||||
list_del_init(&chunk->owners[n].list);
|
list_del_init(&chunk->owners[n].list);
|
||||||
spin_unlock(&hash_lock);
|
spin_unlock(&hash_lock);
|
||||||
|
if (need_prune)
|
||||||
|
audit_schedule_prune();
|
||||||
mutex_unlock(&audit_filter_mutex);
|
mutex_unlock(&audit_filter_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -199,6 +199,7 @@ struct audit_context {
|
||||||
|
|
||||||
struct audit_tree_refs *trees, *first_trees;
|
struct audit_tree_refs *trees, *first_trees;
|
||||||
int tree_count;
|
int tree_count;
|
||||||
|
struct list_head killed_trees;
|
||||||
|
|
||||||
int type;
|
int type;
|
||||||
union {
|
union {
|
||||||
|
@ -853,6 +854,7 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
|
||||||
if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
|
if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
|
||||||
return NULL;
|
return NULL;
|
||||||
audit_zero_context(context, state);
|
audit_zero_context(context, state);
|
||||||
|
INIT_LIST_HEAD(&context->killed_trees);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1545,6 +1547,8 @@ void audit_free(struct task_struct *tsk)
|
||||||
/* that can happen only if we are called from do_exit() */
|
/* that can happen only if we are called from do_exit() */
|
||||||
if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
|
if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
|
||||||
audit_log_exit(context, tsk);
|
audit_log_exit(context, tsk);
|
||||||
|
if (!list_empty(&context->killed_trees))
|
||||||
|
audit_kill_trees(&context->killed_trees);
|
||||||
|
|
||||||
audit_free_context(context);
|
audit_free_context(context);
|
||||||
}
|
}
|
||||||
|
@ -1688,6 +1692,9 @@ void audit_syscall_exit(int valid, long return_code)
|
||||||
context->in_syscall = 0;
|
context->in_syscall = 0;
|
||||||
context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
|
context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
|
||||||
|
|
||||||
|
if (!list_empty(&context->killed_trees))
|
||||||
|
audit_kill_trees(&context->killed_trees);
|
||||||
|
|
||||||
if (context->previous) {
|
if (context->previous) {
|
||||||
struct audit_context *new_context = context->previous;
|
struct audit_context *new_context = context->previous;
|
||||||
context->previous = NULL;
|
context->previous = NULL;
|
||||||
|
@ -2521,3 +2528,11 @@ void audit_core_dumps(long signr)
|
||||||
audit_log_format(ab, " sig=%ld", signr);
|
audit_log_format(ab, " sig=%ld", signr);
|
||||||
audit_log_end(ab);
|
audit_log_end(ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct list_head *audit_killed_trees(void)
|
||||||
|
{
|
||||||
|
struct audit_context *ctx = current->audit_context;
|
||||||
|
if (likely(!ctx || !ctx->in_syscall))
|
||||||
|
return NULL;
|
||||||
|
return &ctx->killed_trees;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue