[PATCH] Split audit filtering into auditfilter.c
Amy Griffis
amy.griffis at hp.com
Thu Dec 15 20:19:10 UTC 2005
David,
See comment below.
On Thu, Dec 15, 2005 at 06:47:52PM +0000, David Woodhouse wrote:
> I've committed this to my tree locally and checked that it builds with
> all three possible combinations of CONFIG_AUDIT, CONFIG_AUDITSYSCALL.
>
> My DSL line is down at the moment so I'll push it to the git tree later
> or tomorrow. This mail will be delayed too, but it'll probably get out
> as soon as the line comes back, and long before I notice and respond by
> pushing to the git tree.
>
> [AUDIT] Fix audit record filtering with !CONFIG_AUDITSYSCALL
>
> This fixes the per-user and per-message-type filtering when syscall
> auditing isn't enabled.
>
> Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
>
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index 36e5090..8817141 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -278,8 +278,6 @@ static inline void audit_inode_child(con
> }
>
> /* Private API (for audit.c only) */
> -extern int audit_receive_filter(int type, int pid, int uid, int seq,
> - void *data, uid_t loginuid);
> extern unsigned int audit_serial(void);
> extern void auditsc_get_stamp(struct audit_context *ctx,
> struct timespec *t, unsigned int *serial);
> @@ -290,8 +288,6 @@ extern int audit_socketcall(int nargs, u
> 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 netlink_skb_parms *cb, int type);
> -extern int audit_filter_exclude(int type);
> extern char *audit_ipc_context(struct kern_ipc_perm *ipcp);
> extern int audit_set_macxattr(const char *name);
> #else
> @@ -305,7 +301,6 @@ extern int audit_set_macxattr(const char
> #define __audit_inode_child(d,i,p) do { ; } while (0)
> #define audit_inode(n,i,f) do { ; } while (0)
> #define audit_inode_child(d,i,p) do { ; } while (0)
> -#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
> #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
> #define audit_get_loginuid(c) ({ -1; })
> #define audit_ipc_perms(q,u,g,m,i) ({ 0; })
> @@ -313,7 +308,6 @@ extern int audit_set_macxattr(const char
> #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(cb,t) ({ 1; })
> #define audit_ipc_context(i) do { ; } while (0)
> #define audit_set_macxattr(n) do { ; } while (0)
> #endif
> @@ -339,13 +333,11 @@ extern void audit_log_d_path(struct
> const char *prefix,
> struct dentry *dentry,
> struct vfsmount *vfsmnt);
> - /* Private API (for auditsc.c only) */
> -extern void audit_send_reply(int pid, int seq, int type,
> - int done, int multi,
> - void *payload, int size);
> -extern void audit_log_lost(const char *message);
> -extern void audit_panic(const char *message);
> -extern struct semaphore audit_netlink_sem;
> + /* Private API (for audit.c only) */
> +extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
> +extern int audit_filter_exclude(int type);
> +extern int audit_receive_filter(int type, int pid, int uid, int seq,
> + void *data, uid_t loginuid);
> #else
> #define audit_log(c,g,t,f,...) do { ; } while (0)
> #define audit_log_start(c,g,t) ({ NULL; })
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 4f5a145..56f6189 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -23,7 +23,7 @@ obj-$(CONFIG_COMPAT) += compat.o
> obj-$(CONFIG_CPUSETS) += cpuset.o
> obj-$(CONFIG_IKCONFIG) += configs.o
> obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
> -obj-$(CONFIG_AUDIT) += audit.o
> +obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
> obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
> obj-$(CONFIG_KPROBES) += kprobes.o
> obj-$(CONFIG_SYSFS) += ksysfs.o
> diff --git a/kernel/audit.c b/kernel/audit.c
> index 9c13bf6..5941a07 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -305,6 +305,7 @@ int kauditd_thread(void *dummy)
> remove_wait_queue(&kauditd_wait, &wait);
> }
> }
> + return 0;
> }
>
> /**
It looks like most of the changes you made to audit.c aren't included
here.
> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> index 3e46e1c..8f0a61c 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -51,17 +51,15 @@
> #include <linux/audit.h>
> #include <linux/personality.h>
> #include <linux/time.h>
> -#include <linux/kthread.h>
> #include <linux/netlink.h>
> #include <linux/compiler.h>
> #include <asm/unistd.h>
> #include <linux/security.h>
> +#include <linux/list.h>
>
> -/* 0 = no checking
> - 1 = put_count checking
> - 2 = verbose put_count checking
> -*/
> -#define AUDIT_DEBUG 0
> +#include "audit.h"
> +
> +extern struct list_head audit_filter_list[];
>
> /* No syscall auditing will take place unless audit_enabled != 0. */
> extern int audit_enabled;
> @@ -75,29 +73,6 @@ extern int audit_enabled;
> * path_lookup. */
> #define AUDIT_NAMES_RESERVED 7
>
> -/* At task start time, the audit_state is set in the audit_context using
> - a per-task filter. At syscall entry, the audit_state is augmented by
> - the syscall filter. */
> -enum audit_state {
> - AUDIT_DISABLED, /* Do not create per-task audit_context.
> - * No syscall-specific audit records can
> - * be generated. */
> - AUDIT_SETUP_CONTEXT, /* Create the per-task audit_context,
> - * but don't necessarily fill it in at
> - * syscall entry time (i.e., filter
> - * instead). */
> - AUDIT_BUILD_CONTEXT, /* Create the per-task audit_context,
> - * and always fill it in at syscall
> - * entry time. This makes a full
> - * syscall record available if some
> - * other part of the kernel decides it
> - * should be recorded. */
> - AUDIT_RECORD_CONTEXT /* Create the per-task audit_context,
> - * always fill it in at syscall entry
> - * time, and always write out the audit
> - * record at syscall exit time. */
> -};
> -
> /* When fs/namei.c:getname() is called, we store the pointer in name and
> * we don't let putname() free it (instead we free all of the saved
> * pointers at syscall exit time).
> @@ -182,264 +157,6 @@ struct audit_context {
> #endif
> };
>
> - /* Public API */
> -/* 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 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]),
> - LIST_HEAD_INIT(audit_filter_list[5]),
> -#if AUDIT_NR_FILTERS != 6
> -#error Fix audit_filter_list initialiser
> -#endif
> -};
> -
> -struct audit_entry {
> - struct list_head list;
> - struct rcu_head rcu;
> - struct audit_rule rule;
> -};
> -
> -extern int audit_pid;
> -
> -/* Copy rule from user-space to kernel-space. Called from
> - * audit_add_rule during AUDIT_ADD. */
> -static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
> -{
> - int i;
> -
> - if (s->action != AUDIT_NEVER
> - && s->action != AUDIT_POSSIBLE
> - && s->action != AUDIT_ALWAYS)
> - 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;
> - d->field_count = s->field_count;
> - for (i = 0; i < d->field_count; i++) {
> - d->fields[i] = s->fields[i];
> - d->values[i] = s->values[i];
> - }
> - for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
> - return 0;
> -}
> -
> -/* Check to see if two rules are identical. It is called from
> - * audit_add_rule during AUDIT_ADD and
> - * audit_del_rule during AUDIT_DEL. */
> -static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
> -{
> - int i;
> -
> - if (a->flags != b->flags)
> - return 1;
> -
> - if (a->action != b->action)
> - return 1;
> -
> - if (a->field_count != b->field_count)
> - return 1;
> -
> - for (i = 0; i < a->field_count; i++) {
> - if (a->fields[i] != b->fields[i]
> - || a->values[i] != b->values[i])
> - return 1;
> - }
> -
> - for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
> - if (a->mask[i] != b->mask[i])
> - return 1;
> -
> - return 0;
> -}
> -
> -/* 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_rule *rule,
> - struct list_head *list)
> -{
> - struct audit_entry *entry;
> - int i;
> -
> - /* Do not use the _rcu iterator here, since this is the only
> - * addition routine. */
> - list_for_each_entry(entry, list, list) {
> - if (!audit_compare_rule(rule, &entry->rule)) {
> - return -EEXIST;
> - }
> - }
> -
> - for (i = 0; i < rule->field_count; i++) {
> - if (rule->fields[i] & AUDIT_UNUSED_BITS)
> - return -EINVAL;
> - if ( rule->fields[i] & AUDIT_NEGATE )
> - rule->fields[i] |= AUDIT_NOT_EQUAL;
> - else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 )
> - rule->fields[i] |= AUDIT_EQUAL;
> - rule->fields[i] &= (~AUDIT_NEGATE);
> - }
> -
> - if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
> - return -ENOMEM;
> - if (audit_copy_rule(&entry->rule, rule)) {
> - kfree(entry);
> - return -EINVAL;
> - }
> -
> - 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 inline void audit_free_rule(struct rcu_head *head)
> -{
> - struct audit_entry *e = container_of(head, struct audit_entry, rcu);
> - kfree(e);
> -}
> -
> -/* 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_del_rule(struct audit_rule *rule,
> - struct list_head *list)
> -{
> - struct audit_entry *e;
> -
> - /* Do not use the _rcu iterator here, since this is the only
> - * deletion routine. */
> - list_for_each_entry(e, list, list) {
> - if (!audit_compare_rule(rule, &e->rule)) {
> - list_del_rcu(&e->list);
> - call_rcu(&e->rcu, audit_free_rule);
> - return 0;
> - }
> - }
> - return -ENOENT; /* No matching rule */
> -}
> -
> -static int audit_list_rules(void *_dest)
> -{
> - int pid, seq;
> - int *dest = _dest;
> - struct audit_entry *entry;
> - int i;
> -
> - pid = dest[0];
> - seq = dest[1];
> - kfree(dest);
> -
> - down(&audit_netlink_sem);
> -
> - /* The *_rcu iterators not needed here because we are
> - always called with audit_netlink_sem held. */
> - 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);
> -
> - up(&audit_netlink_sem);
> - return 0;
> -}
> -
> -/**
> - * audit_receive_filter - apply all rules to the specified message type
> - * @type: audit message type
> - * @pid: target pid for netlink audit messages
> - * @uid: target uid for netlink audit messages
> - * @seq: netlink audit message sequence (serial) number
> - * @data: payload data
> - * @loginuid: loginuid of sender
> - */
> -int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
> - uid_t loginuid)
> -{
> - struct task_struct *tsk;
> - int *dest;
> - int err = 0;
> - unsigned listnr;
> -
> - switch (type) {
> - case AUDIT_LIST:
> - /* We can't just spew out the rules here because we might fill
> - * the available socket buffer space and deadlock waiting for
> - * auditctl to read from it... which isn't ever going to
> - * happen if we're actually running in the context of auditctl
> - * trying to _send_ the stuff */
> -
> - dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
> - if (!dest)
> - return -ENOMEM;
> - dest[0] = pid;
> - dest[1] = seq;
> -
> - tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
> - if (IS_ERR(tsk)) {
> - kfree(dest);
> - err = PTR_ERR(tsk);
> - }
> - break;
> - case AUDIT_ADD:
> - listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
> - if (listnr >= AUDIT_NR_FILTERS)
> - return -EINVAL;
> -
> - err = audit_add_rule(data, &audit_filter_list[listnr]);
> - if (!err)
> - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
> - "auid=%u added an audit rule\n", loginuid);
> - break;
> - case AUDIT_DEL:
> - 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, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
> - "auid=%u removed an audit rule\n", loginuid);
> - break;
> - default:
> - return -EINVAL;
> - }
> -
> - return err;
> -}
> -
> -static int audit_comparator(const u32 left, const u32 op, const u32 right)
> -{
> - switch (op) {
> - case AUDIT_EQUAL:
> - return (left == right);
> - case AUDIT_NOT_EQUAL:
> - return (left != right);
> - case AUDIT_LESS_THAN:
> - return (left < right);
> - case AUDIT_LESS_THAN_OR_EQUAL:
> - return (left <= right);
> - case AUDIT_GREATER_THAN:
> - return (left > right);
> - case AUDIT_GREATER_THAN_OR_EQUAL:
> - return (left >= right);
> - default:
> - return -EINVAL;
> - }
> -}
>
> /* Compare a task_struct with an audit_rule. Return 1 on match, 0
> * otherwise. */
> @@ -612,95 +329,6 @@ static enum audit_state audit_filter_sys
> return AUDIT_BUILD_CONTEXT;
> }
>
> -static int audit_filter_user_rules(struct netlink_skb_parms *cb,
> - struct audit_rule *rule,
> - enum audit_state *state)
> -{
> - int i;
> -
> - for (i = 0; i < rule->field_count; i++) {
> - u32 field = rule->fields[i] & ~AUDIT_OPERATORS;
> - u32 op = rule->fields[i] & AUDIT_OPERATORS;
> - u32 value = rule->values[i];
> - int result = 0;
> -
> - switch (field) {
> - case AUDIT_PID:
> - result = audit_comparator(cb->creds.pid, op, value);
> - break;
> - case AUDIT_UID:
> - result = audit_comparator(cb->creds.uid, op, value);
> - break;
> - case AUDIT_GID:
> - result = audit_comparator(cb->creds.gid, op, value);
> - break;
> - case AUDIT_LOGINUID:
> - result = audit_comparator(cb->loginuid, op, value);
> - break;
> - }
> -
> - if (!result)
> - return 0;
> - }
> - switch (rule->action) {
> - case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
> - case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT; break;
> - case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
> - }
> - return 1;
> -}
> -
> -int audit_filter_user(struct netlink_skb_parms *cb, int type)
> -{
> - struct audit_entry *e;
> - enum audit_state state;
> - int ret = 1;
> -
> - rcu_read_lock();
> - list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
> - if (audit_filter_user_rules(cb, &e->rule, &state)) {
> - if (state == AUDIT_DISABLED)
> - ret = 0;
> - break;
> - }
> - }
> - rcu_read_unlock();
> -
> - return ret; /* Audit by default */
> -}
> -
> -int audit_filter_exclude(int type)
> -{
> - struct audit_entry *e;
> - int result = 0;
> -
> - rcu_read_lock();
> - if (list_empty(&audit_filter_list[AUDIT_FILTER_EXCLUDE]))
> - goto unlock_and_return;
> -
> - list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_EXCLUDE],
> - list) {
> - struct audit_rule *rule = &e->rule;
> - int i;
> - for (i = 0; i < rule->field_count; i++) {
> - u32 field = rule->fields[i] & ~AUDIT_OPERATORS;
> - u32 op = rule->fields[i] & AUDIT_OPERATORS;
> - u32 value = rule->values[i];
> - if ( field == AUDIT_MSGTYPE ) {
> - result = audit_comparator(type, op, value);
> - if (!result)
> - break;
> - }
> - }
> - if (result)
> - goto unlock_and_return;
> - }
> -unlock_and_return:
> - rcu_read_unlock();
> - return result;
> -}
> -
> -
> /* This should be called with task_lock() held. */
> static inline struct audit_context *audit_get_context(struct task_struct *tsk,
> int return_valid,
>
>
> --
> dwmw2
>
> --
> Linux-audit mailing list
> Linux-audit at redhat.com
> https://www.redhat.com/mailman/listinfo/linux-audit
>
More information about the Linux-audit
mailing list