AUDIT: Allow filtering of user messages
David Woodhouse [Sun, 19 Jun 2005 18:35:50 +0000 (19:35 +0100)]
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>

include/linux/audit.h
kernel/audit.c
kernel/auditsc.c

index bf2ad3b..b5bda24 100644 (file)
 #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
index ef35166..f0bbfe0 100644 (file)
@@ -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:
index e75f84e..6b4fbb1 100644 (file)
@@ -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;