diff options
author | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-06-19 19:35:50 +0100 |
---|---|---|
committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-06-19 19:35:50 +0100 |
commit | 0f45aa18e65cf3d768082d7d86054a0d2a20bb18 (patch) | |
tree | 6e94dfcd813061f5a710b5621e1b2f5a01a95533 | |
parent | 0107b3cf3225aed6ddde4fa8dbcd4ed643b34f4d (diff) | |
download | lwn-0f45aa18e65cf3d768082d7d86054a0d2a20bb18.tar.gz lwn-0f45aa18e65cf3d768082d7d86054a0d2a20bb18.zip |
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>
-rw-r--r-- | include/linux/audit.h | 15 | ||||
-rw-r--r-- | kernel/audit.c | 36 | ||||
-rw-r--r-- | kernel/auditsc.c | 92 |
3 files changed, 85 insertions, 58 deletions
diff --git a/include/linux/audit.h b/include/linux/audit.h index bf2ad3ba72eb..b5bda24f7365 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -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 diff --git a/kernel/audit.c b/kernel/audit.c index ef35166fdc29..f0bbfe073136 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -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: diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e75f84e1a1a0..6b4fbb1c0129 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -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; |