AUDIT: Allow filtering of user messages
Turn the field from a bitmask to an enumeration and add a list to allow filtering of messages generated by userspace. We also define a list for file system watches in anticipation of that feature. Signed-off-by: David Woodhouse <dwmw2@infradead.org>
This commit is contained in:
parent
0107b3cf32
commit
0f45aa18e6
3 changed files with 85 additions and 58 deletions
|
@ -75,10 +75,15 @@
|
|||
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
|
||||
|
||||
/* Rule flags */
|
||||
#define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */
|
||||
#define AUDIT_AT_ENTRY 0x02 /* Apply rule at syscall entry */
|
||||
#define AUDIT_AT_EXIT 0x04 /* Apply rule at syscall exit */
|
||||
#define AUDIT_PREPEND 0x10 /* Prepend to front of list */
|
||||
#define AUDIT_FILTER_USER 0x00 /* Apply rule to user-generated messages */
|
||||
#define AUDIT_FILTER_TASK 0x01 /* Apply rule at task creation (not syscall) */
|
||||
#define AUDIT_FILTER_ENTRY 0x02 /* Apply rule at syscall entry */
|
||||
#define AUDIT_FILTER_WATCH 0x03 /* Apply rule to file system watches */
|
||||
#define AUDIT_FILTER_EXIT 0x04 /* Apply rule at syscall exit */
|
||||
|
||||
#define AUDIT_NR_FILTERS 5
|
||||
|
||||
#define AUDIT_FILTER_PREPEND 0x10 /* Prepend to front of list */
|
||||
|
||||
/* Rule actions */
|
||||
#define AUDIT_NEVER 0 /* Do not build context if rule matches */
|
||||
|
@ -230,6 +235,7 @@ extern int audit_socketcall(int nargs, unsigned long *args);
|
|||
extern int audit_sockaddr(int len, void *addr);
|
||||
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
|
||||
extern void audit_signal_info(int sig, struct task_struct *t);
|
||||
extern int audit_filter_user(struct task_struct *tsk, int type);
|
||||
#else
|
||||
#define audit_alloc(t) ({ 0; })
|
||||
#define audit_free(t) do { ; } while (0)
|
||||
|
@ -246,6 +252,7 @@ extern void audit_signal_info(int sig, struct task_struct *t);
|
|||
#define audit_sockaddr(len, addr) ({ 0; })
|
||||
#define audit_avc_path(dentry, mnt) ({ 0; })
|
||||
#define audit_signal_info(s,t) do { ; } while (0)
|
||||
#define audit_filter_user(struct ({ 1; })
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
|
|
|
@ -107,13 +107,6 @@ static struct sk_buff_head audit_skb_queue;
|
|||
static struct task_struct *kauditd_task;
|
||||
static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
|
||||
|
||||
/* There are three lists of rules -- one to search at task creation
|
||||
* time, one to search at syscall entry time, and another to search at
|
||||
* syscall exit time. */
|
||||
static LIST_HEAD(audit_tsklist);
|
||||
static LIST_HEAD(audit_entlist);
|
||||
static LIST_HEAD(audit_extlist);
|
||||
|
||||
/* The netlink socket is only to be read by 1 CPU, which lets us assume
|
||||
* that list additions and deletions never happen simultaneously in
|
||||
* auditsc.c */
|
||||
|
@ -376,6 +369,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|||
u16 msg_type = nlh->nlmsg_type;
|
||||
uid_t loginuid; /* loginuid of sender */
|
||||
struct audit_sig_info sig_data;
|
||||
struct task_struct *tsk;
|
||||
|
||||
err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
|
||||
if (err)
|
||||
|
@ -435,15 +429,25 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|||
break;
|
||||
case AUDIT_USER:
|
||||
case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
|
||||
ab = audit_log_start(NULL, msg_type);
|
||||
if (!ab)
|
||||
break; /* audit_panic has been called */
|
||||
audit_log_format(ab,
|
||||
"user pid=%d uid=%u auid=%u"
|
||||
" msg='%.1024s'",
|
||||
pid, uid, loginuid, (char *)data);
|
||||
audit_set_pid(ab, pid);
|
||||
audit_log_end(ab);
|
||||
read_lock(&tasklist_lock);
|
||||
tsk = find_task_by_pid(pid);
|
||||
if (tsk)
|
||||
get_task_struct(tsk);
|
||||
read_unlock(&tasklist_lock);
|
||||
if (!tsk)
|
||||
return -ESRCH;
|
||||
|
||||
if (audit_filter_user(tsk, msg_type)) {
|
||||
ab = audit_log_start(NULL, msg_type);
|
||||
if (ab) {
|
||||
audit_log_format(ab,
|
||||
"user pid=%d uid=%u auid=%u msg='%.1024s'",
|
||||
pid, uid, loginuid, (char *)data);
|
||||
audit_set_pid(ab, pid);
|
||||
audit_log_end(ab);
|
||||
}
|
||||
}
|
||||
put_task_struct(tsk);
|
||||
break;
|
||||
case AUDIT_ADD:
|
||||
case AUDIT_DEL:
|
||||
|
|
|
@ -167,9 +167,16 @@ struct audit_context {
|
|||
/* There are three lists of rules -- one to search at task creation
|
||||
* time, one to search at syscall entry time, and another to search at
|
||||
* syscall exit time. */
|
||||
static LIST_HEAD(audit_tsklist);
|
||||
static LIST_HEAD(audit_entlist);
|
||||
static LIST_HEAD(audit_extlist);
|
||||
static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
|
||||
LIST_HEAD_INIT(audit_filter_list[0]),
|
||||
LIST_HEAD_INIT(audit_filter_list[1]),
|
||||
LIST_HEAD_INIT(audit_filter_list[2]),
|
||||
LIST_HEAD_INIT(audit_filter_list[3]),
|
||||
LIST_HEAD_INIT(audit_filter_list[4]),
|
||||
#if AUDIT_NR_FILTERS != 5
|
||||
#error Fix audit_filter_list initialiser
|
||||
#endif
|
||||
};
|
||||
|
||||
struct audit_entry {
|
||||
struct list_head list;
|
||||
|
@ -210,16 +217,15 @@ static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
|
|||
/* Note that audit_add_rule and audit_del_rule are called via
|
||||
* audit_receive() in audit.c, and are protected by
|
||||
* audit_netlink_sem. */
|
||||
static inline int audit_add_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
static inline void audit_add_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
{
|
||||
if (entry->rule.flags & AUDIT_PREPEND) {
|
||||
entry->rule.flags &= ~AUDIT_PREPEND;
|
||||
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
|
||||
entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
|
||||
list_add_rcu(&entry->list, list);
|
||||
} else {
|
||||
list_add_tail_rcu(&entry->list, list);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audit_free_rule(struct rcu_head *head)
|
||||
|
@ -245,7 +251,7 @@ static inline int audit_del_rule(struct audit_rule *rule,
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
return -EFAULT; /* No matching rule */
|
||||
return -ENOENT; /* No matching rule */
|
||||
}
|
||||
|
||||
/* Copy rule from user-space to kernel-space. Called during
|
||||
|
@ -260,6 +266,8 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
|
|||
return -1;
|
||||
if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS)
|
||||
return -1;
|
||||
if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS)
|
||||
return -1;
|
||||
|
||||
d->flags = s->flags;
|
||||
d->action = s->action;
|
||||
|
@ -275,23 +283,20 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
|
|||
int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||
uid_t loginuid)
|
||||
{
|
||||
u32 flags;
|
||||
struct audit_entry *entry;
|
||||
int err = 0;
|
||||
int i;
|
||||
unsigned listnr;
|
||||
|
||||
switch (type) {
|
||||
case AUDIT_LIST:
|
||||
/* The *_rcu iterators not needed here because we are
|
||||
always called with audit_netlink_sem held. */
|
||||
list_for_each_entry(entry, &audit_tsklist, list)
|
||||
audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
&entry->rule, sizeof(entry->rule));
|
||||
list_for_each_entry(entry, &audit_entlist, list)
|
||||
audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
&entry->rule, sizeof(entry->rule));
|
||||
list_for_each_entry(entry, &audit_extlist, list)
|
||||
audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
&entry->rule, sizeof(entry->rule));
|
||||
for (i=0; i<AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry(entry, &audit_filter_list[i], list)
|
||||
audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
&entry->rule, sizeof(entry->rule));
|
||||
}
|
||||
audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
|
||||
break;
|
||||
case AUDIT_ADD:
|
||||
|
@ -301,26 +306,20 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
|||
kfree(entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
flags = entry->rule.flags;
|
||||
if (!err && (flags & AUDIT_PER_TASK))
|
||||
err = audit_add_rule(entry, &audit_tsklist);
|
||||
if (!err && (flags & AUDIT_AT_ENTRY))
|
||||
err = audit_add_rule(entry, &audit_entlist);
|
||||
if (!err && (flags & AUDIT_AT_EXIT))
|
||||
err = audit_add_rule(entry, &audit_extlist);
|
||||
listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND;
|
||||
audit_add_rule(entry, &audit_filter_list[listnr]);
|
||||
audit_log(NULL, AUDIT_CONFIG_CHANGE,
|
||||
"auid=%u added an audit rule\n", loginuid);
|
||||
break;
|
||||
case AUDIT_DEL:
|
||||
flags =((struct audit_rule *)data)->flags;
|
||||
if (!err && (flags & AUDIT_PER_TASK))
|
||||
err = audit_del_rule(data, &audit_tsklist);
|
||||
if (!err && (flags & AUDIT_AT_ENTRY))
|
||||
err = audit_del_rule(data, &audit_entlist);
|
||||
if (!err && (flags & AUDIT_AT_EXIT))
|
||||
err = audit_del_rule(data, &audit_extlist);
|
||||
audit_log(NULL, AUDIT_CONFIG_CHANGE,
|
||||
"auid=%u removed an audit rule\n", loginuid);
|
||||
listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
|
||||
if (listnr >= AUDIT_NR_FILTERS)
|
||||
return -EINVAL;
|
||||
|
||||
err = audit_del_rule(data, &audit_filter_list[listnr]);
|
||||
if (!err)
|
||||
audit_log(NULL, AUDIT_CONFIG_CHANGE,
|
||||
"auid=%u removed an audit rule\n", loginuid);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -454,7 +453,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk)
|
|||
enum audit_state state;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(e, &audit_tsklist, list) {
|
||||
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
|
||||
if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
|
||||
rcu_read_unlock();
|
||||
return state;
|
||||
|
@ -490,6 +489,23 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
|
|||
return AUDIT_BUILD_CONTEXT;
|
||||
}
|
||||
|
||||
int audit_filter_user(struct task_struct *tsk, int type)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
enum audit_state state;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
|
||||
if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
|
||||
rcu_read_unlock();
|
||||
return state != AUDIT_DISABLED;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return 1; /* Audit by default */
|
||||
|
||||
}
|
||||
|
||||
/* This should be called with task_lock() held. */
|
||||
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
|
||||
int return_valid,
|
||||
|
@ -504,7 +520,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,
|
|||
|
||||
if (context->in_syscall && !context->auditable) {
|
||||
enum audit_state state;
|
||||
state = audit_filter_syscall(tsk, context, &audit_extlist);
|
||||
state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
|
||||
if (state == AUDIT_RECORD_CONTEXT)
|
||||
context->auditable = 1;
|
||||
}
|
||||
|
@ -876,7 +892,7 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
|
|||
|
||||
state = context->state;
|
||||
if (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)
|
||||
state = audit_filter_syscall(tsk, context, &audit_entlist);
|
||||
state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]);
|
||||
if (likely(state == AUDIT_DISABLED))
|
||||
return;
|
||||
|
||||
|
|
Loading…
Reference in a new issue