From 0f45aa18e65cf3d768082d7d86054a0d2a20bb18 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 19 Jun 2005 19:35:50 +0100 Subject: 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 --- kernel/auditsc.c | 92 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 38 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e75f84e1a1a..6b4fbb1c012 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; irule, 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; -- cgit v1.2.3-18-g5258 From f7056d64ae101d910f965a2e39831f635ef7891b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 20 Jun 2005 16:07:33 +0100 Subject: AUDIT: Really exempt auditd from having its actions audited. We were only avoiding it on syscall exit before; now stop _everything_. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 6b4fbb1c012..48a39579c45 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -477,6 +477,9 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, int word = AUDIT_WORD(ctx->major); int bit = AUDIT_BIT(ctx->major); + if (audit_pid && ctx->pid == audit_pid) + return AUDIT_DISABLED; + rcu_read_lock(); list_for_each_entry_rcu(e, list, list) { if ((e->rule.mask[word] & bit) == bit @@ -494,6 +497,9 @@ int audit_filter_user(struct task_struct *tsk, int type) struct audit_entry *e; enum audit_state state; + if (audit_pid && tsk->pid == audit_pid) + return AUDIT_DISABLED; + 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)) { @@ -816,7 +822,7 @@ void audit_free(struct task_struct *tsk) /* Check for system calls that do not go through the exit * function (e.g., exit_group), then free context block. */ - if (context->in_syscall && context->auditable && context->pid != audit_pid) + if (context->in_syscall && context->auditable) audit_log_exit(context); audit_free_context(context); @@ -921,7 +927,7 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code) if (likely(!context)) return; - if (context->in_syscall && context->auditable && context->pid != audit_pid) + if (context->in_syscall && context->auditable) audit_log_exit(context); context->in_syscall = 0; -- cgit v1.2.3-18-g5258 From ae7b961b1c943367dfe179411f120d7bf8eaba89 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 20 Jun 2005 16:11:05 +0100 Subject: AUDIT: Report lookup flags with path/inode records. When LOOKUP_PARENT is used, the inode which results is not the inode found at the pathname. Report the flags so that this doesn't generate misleading audit records. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 48a39579c45..031f979019d 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -95,6 +95,7 @@ struct audit_names { uid_t uid; gid_t gid; dev_t rdev; + unsigned flags; }; struct audit_aux_data { @@ -792,6 +793,8 @@ static void audit_log_exit(struct audit_context *context) audit_log_format(ab, " name="); audit_log_untrustedstring(ab, context->names[i].name); } + audit_log_format(ab, " flags=%x\n", context->names[i].flags); + if (context->names[i].ino != (unsigned long)-1) audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o" " ouid=%u ogid=%u rdev=%02x:%02x", @@ -1018,7 +1021,7 @@ void audit_putname(const char *name) /* Store the inode and device from a lookup. Called from * fs/namei.c:path_lookup(). */ -void audit_inode(const char *name, const struct inode *inode) +void audit_inode(const char *name, const struct inode *inode, unsigned flags) { int idx; struct audit_context *context = current->audit_context; @@ -1044,12 +1047,13 @@ void audit_inode(const char *name, const struct inode *inode) ++context->ino_count; #endif } - context->names[idx].ino = inode->i_ino; - context->names[idx].dev = inode->i_sb->s_dev; - context->names[idx].mode = inode->i_mode; - context->names[idx].uid = inode->i_uid; - context->names[idx].gid = inode->i_gid; - context->names[idx].rdev = inode->i_rdev; + context->names[idx].flags = flags; + context->names[idx].ino = inode->i_ino; + context->names[idx].dev = inode->i_sb->s_dev; + context->names[idx].mode = inode->i_mode; + context->names[idx].uid = inode->i_uid; + context->names[idx].gid = inode->i_gid; + context->names[idx].rdev = inode->i_rdev; } void auditsc_get_stamp(struct audit_context *ctx, -- cgit v1.2.3-18-g5258 From f6a789d19858a951e7ff9e297a44b377c21b6c33 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 21 Jun 2005 16:22:01 +0100 Subject: AUDIT: Spawn kernel thread to list filter rules. If we have enough rules to fill the netlink buffer space, it'll deadlock because auditctl isn't ever actually going to read from the socket until we return, and we aren't going to return until it reads... so we spawn a kernel thread to spew out the list and then exit. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 031f979019d..cb8a4494515 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -39,6 +39,7 @@ #include #include #include +#include #include /* 0 = no checking @@ -281,24 +282,60 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) return 0; } +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; irule, sizeof(entry->rule)); + } + audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); + + up(&audit_netlink_sem); + return 0; +} + int audit_receive_filter(int type, int pid, int uid, int seq, void *data, uid_t loginuid) { struct audit_entry *entry; + struct task_struct *tsk; + int *dest; 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. */ - for (i=0; irule, sizeof(entry->rule)); + /* 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); } - audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); break; case AUDIT_ADD: if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) -- cgit v1.2.3-18-g5258 From 4a4cd633b575609b741a1de7837223a2d9e1c34c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 22 Jun 2005 14:56:47 +0100 Subject: AUDIT: Optimise the audit-disabled case for discarding user messages Also exempt USER_AVC message from being discarded to preserve existing behaviour for SE Linux. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index cb8a4494515..fc858b0c044 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -530,22 +530,33 @@ 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) +int audit_filter_user(int pid, int type) { + struct task_struct *tsk; struct audit_entry *e; enum audit_state state; + int ret = 1; - if (audit_pid && tsk->pid == audit_pid) - return AUDIT_DISABLED; + read_lock(&tasklist_lock); + tsk = find_task_by_pid(pid); + if (tsk) + get_task_struct(tsk); + read_unlock(&tasklist_lock); + + if (!tsk) + return -ESRCH; 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; + if (state == AUDIT_DISABLED) + ret = 0; + break; } } rcu_read_unlock(); + put_task_struct(tsk); + return 1; /* Audit by default */ } -- cgit v1.2.3-18-g5258 From 9ad9ad385be27fcc7c16d290d972c6173e780a61 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 22 Jun 2005 15:04:33 +0100 Subject: AUDIT: Wait for backlog to clear when generating messages. Add a gfp_mask to audit_log_start() and audit_log(), to reduce the amount of GFP_ATOMIC allocation -- most of it doesn't need to be GFP_ATOMIC. Also if the mask includes __GFP_WAIT, then wait up to 60 seconds for the auditd backlog to clear instead of immediately abandoning the message. The timeout should probably be made configurable, but for now it'll suffice that it only happens if auditd is actually running. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index fc858b0c044..f463fd23084 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -346,7 +346,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, } listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND; audit_add_rule(entry, &audit_filter_list[listnr]); - audit_log(NULL, AUDIT_CONFIG_CHANGE, + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "auid=%u added an audit rule\n", loginuid); break; case AUDIT_DEL: @@ -356,7 +356,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, err = audit_del_rule(data, &audit_filter_list[listnr]); if (!err) - audit_log(NULL, AUDIT_CONFIG_CHANGE, + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "auid=%u removed an audit rule\n", loginuid); break; default: @@ -756,7 +756,7 @@ static void audit_log_exit(struct audit_context *context) struct audit_buffer *ab; struct audit_aux_data *aux; - ab = audit_log_start(context, AUDIT_SYSCALL); + ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL); if (!ab) return; /* audit_panic has been called */ audit_log_format(ab, "arch=%x syscall=%d", @@ -788,7 +788,7 @@ static void audit_log_exit(struct audit_context *context) for (aux = context->aux; aux; aux = aux->next) { - ab = audit_log_start(context, aux->type); + ab = audit_log_start(context, GFP_KERNEL, aux->type); if (!ab) continue; /* audit_panic has been called */ @@ -825,14 +825,14 @@ static void audit_log_exit(struct audit_context *context) } if (context->pwd && context->pwdmnt) { - ab = audit_log_start(context, AUDIT_CWD); + ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD); if (ab) { audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt); audit_log_end(ab); } } for (i = 0; i < context->name_count; i++) { - ab = audit_log_start(context, AUDIT_PATH); + ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH); if (!ab) continue; /* audit_panic has been called */ @@ -1118,7 +1118,7 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) if (task->audit_context) { struct audit_buffer *ab; - ab = audit_log_start(NULL, AUDIT_LOGIN); + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN); if (ab) { audit_log_format(ab, "login pid=%d uid=%u " "old auid=%u new auid=%u", -- cgit v1.2.3-18-g5258 From 9e94e66a5bc739ab525ec0a26ba75300aaf154f3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 23 Jun 2005 18:33:54 +0100 Subject: AUDIT: No really, we don't want to audit auditd. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index f463fd23084..20c7d8560af 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -573,7 +573,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, context->return_valid = return_valid; context->return_code = return_code; - if (context->in_syscall && !context->auditable) { + if (context->in_syscall && !context->auditable && tsk->pid != audit_pid) { enum audit_state state; state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); if (state == AUDIT_RECORD_CONTEXT) -- cgit v1.2.3-18-g5258 From 993e2d4106e94dae6e8cfbeb32073bd12cdee203 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 24 Jun 2005 08:21:49 +0100 Subject: AUDIT: Return correct result from audit_filter_rules() When the task refcounting was added to audit_filter_rules() it became more of a problem that this function was violating the 'only one return from each function' rule. In fixing it to use a variable to store 'ret' I stupidly neglected to actually change the 'return 1;' at the end. This makes it not work very well. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 20c7d8560af..7b123f0a948 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -557,7 +557,7 @@ int audit_filter_user(int pid, int type) rcu_read_unlock(); put_task_struct(tsk); - return 1; /* Audit by default */ + return ret; /* Audit by default */ } -- cgit v1.2.3-18-g5258 From 5bb289b5a0becb53ac3e1d60815ff8b779296b73 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 24 Jun 2005 14:14:05 +0100 Subject: AUDIT: Clean up user message filtering Don't look up the task by its pid and then use the syscall filtering helper. Just implement our own filter helper which operates solely on the information in the netlink_skb_parms. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 56 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 14 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 7b123f0a948..34a990223c9 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -40,6 +40,7 @@ #include #include #include +#include #include /* 0 = no checking @@ -530,35 +531,62 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, return AUDIT_BUILD_CONTEXT; } -int audit_filter_user(int pid, int type) +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_NEGATE; + u32 value = rule->values[i]; + int result = 0; + + switch (field) { + case AUDIT_PID: + result = (cb->creds.pid == value); + break; + case AUDIT_UID: + result = (cb->creds.uid == value); + break; + case AUDIT_GID: + result = (cb->creds.gid == value); + break; + case AUDIT_LOGINUID: + result = (cb->loginuid == value); + break; + } + + if (rule->fields[i] & AUDIT_NEGATE) + result = !result; + 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 task_struct *tsk; struct audit_entry *e; enum audit_state state; int ret = 1; - read_lock(&tasklist_lock); - tsk = find_task_by_pid(pid); - if (tsk) - get_task_struct(tsk); - read_unlock(&tasklist_lock); - - if (!tsk) - return -ESRCH; - 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)) { + if (audit_filter_user_rules(cb, &e->rule, &state)) { if (state == AUDIT_DISABLED) ret = 0; break; } } rcu_read_unlock(); - put_task_struct(tsk); return ret; /* Audit by default */ - } /* This should be called with task_lock() held. */ -- cgit v1.2.3-18-g5258 From 21af6c4f2aa5f63138871b4ddd77d7ebf2588c9d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 2 Jul 2005 14:10:46 +0100 Subject: AUDIT: Really don't audit auditd. The pid in the audit context isn't always set up. Use tsk->pid when checking whether it's auditd in audit_filter_syscall(), instead of ctx->pid. Remove a band-aid which did the same elsewhere. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 34a990223c9..0fdd90194ec 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -516,7 +516,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, int word = AUDIT_WORD(ctx->major); int bit = AUDIT_BIT(ctx->major); - if (audit_pid && ctx->pid == audit_pid) + if (audit_pid && tsk->pid == audit_pid) return AUDIT_DISABLED; rcu_read_lock(); @@ -601,7 +601,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, context->return_valid = return_valid; context->return_code = return_code; - if (context->in_syscall && !context->auditable && tsk->pid != audit_pid) { + if (context->in_syscall && !context->auditable) { enum audit_state state; state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); if (state == AUDIT_RECORD_CONTEXT) -- cgit v1.2.3-18-g5258 From 582edda586120004d0fb67113115fa442a0a1571 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 13 Jul 2005 22:39:34 +0100 Subject: AUDIT: Exempt the whole auditd thread-group from auditing and not just the one thread. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 0fdd90194ec..86d91fe2d93 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -516,7 +516,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, int word = AUDIT_WORD(ctx->major); int bit = AUDIT_BIT(ctx->major); - if (audit_pid && tsk->pid == audit_pid) + if (audit_pid && tsk->tgid == audit_tgid) return AUDIT_DISABLED; rcu_read_lock(); @@ -1255,7 +1255,7 @@ void audit_signal_info(int sig, struct task_struct *t) extern pid_t audit_sig_pid; extern uid_t audit_sig_uid; - if (unlikely(audit_pid && t->pid == audit_pid)) { + if (unlikely(audit_pid && t->tgid == audit_pid)) { if (sig == SIGTERM || sig == SIGHUP) { struct audit_context *ctx = current->audit_context; audit_sig_pid = current->pid; -- cgit v1.2.3-18-g5258 From f55619642e863990d5a46cf2c2c840170d22a9f9 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 13 Jul 2005 22:47:07 +0100 Subject: AUDIT: Avoid scheduling in idle thread When we flush a pending syscall audit record due to audit_free(), we might be doing that in the context of the idle thread. So use GFP_ATOMIC Signed-off-by: David Woodhouse --- kernel/auditsc.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 86d91fe2d93..517b253f1ef 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -41,6 +41,7 @@ #include #include #include +#include #include /* 0 = no checking @@ -778,13 +779,13 @@ static void audit_log_task_info(struct audit_buffer *ab) up_read(&mm->mmap_sem); } -static void audit_log_exit(struct audit_context *context) +static void audit_log_exit(struct audit_context *context, unsigned int gfp_mask) { int i; struct audit_buffer *ab; struct audit_aux_data *aux; - ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL); + ab = audit_log_start(context, gfp_mask, AUDIT_SYSCALL); if (!ab) return; /* audit_panic has been called */ audit_log_format(ab, "arch=%x syscall=%d", @@ -900,9 +901,11 @@ void audit_free(struct task_struct *tsk) return; /* Check for system calls that do not go through the exit - * function (e.g., exit_group), then free context block. */ + * function (e.g., exit_group), then free context block. + * We use GFP_ATOMIC here because we might be doing this + * in the context of the idle thread */ if (context->in_syscall && context->auditable) - audit_log_exit(context); + audit_log_exit(context, GFP_ATOMIC); audit_free_context(context); } @@ -1007,7 +1010,7 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code) return; if (context->in_syscall && context->auditable) - audit_log_exit(context); + audit_log_exit(context, GFP_KERNEL); context->in_syscall = 0; context->auditable = 0; -- cgit v1.2.3-18-g5258 From 351bb722590b2329ac5e72c4b824b8b6ce6e3082 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 14 Jul 2005 14:40:06 +0100 Subject: AUDIT: Fix compile error in audit_filter_syscall We didn't rename it to audit_tgid after all. Except once... Doh. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 517b253f1ef..242d45e5373 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -517,7 +517,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, int word = AUDIT_WORD(ctx->major); int bit = AUDIT_BIT(ctx->major); - if (audit_pid && tsk->tgid == audit_tgid) + if (audit_pid && tsk->tgid == audit_pid) return AUDIT_DISABLED; rcu_read_lock(); -- cgit v1.2.3-18-g5258 From ce625a801664d8ed7344117bbb57510e4e0e872c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 18 Jul 2005 14:24:46 -0400 Subject: AUDIT: Reduce contention in audit_serial() ... by generating serial numbers only if an audit context is actually _used_, rather than doing so at syscall entry even when the context isn't necessarily marked auditable. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 242d45e5373..46b45abceb9 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -984,7 +984,7 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major, if (likely(state == AUDIT_DISABLED)) return; - context->serial = audit_serial(); + context->serial = 0; context->ctime = CURRENT_TIME; context->in_syscall = 1; context->auditable = !!(state == AUDIT_RECORD_CONTEXT); @@ -1138,6 +1138,8 @@ void audit_inode(const char *name, const struct inode *inode, unsigned flags) void auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial) { + if (!ctx->serial) + ctx->serial = audit_serial(); t->tv_sec = ctx->ctime.tv_sec; t->tv_nsec = ctx->ctime.tv_nsec; *serial = ctx->serial; -- cgit v1.2.3-18-g5258 From 413a1c7520ad6207c9122a749983c500f29e3e32 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 17 Aug 2005 14:45:55 +0100 Subject: AUDIT: Fix task refcount leak in audit_filter_syscall() Signed-off-by: David Woodhouse --- kernel/auditsc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 46b45abceb9..a73176eaa57 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1007,7 +1007,7 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code) /* Not having a context here is ok, since the parent may have * called __put_task_struct. */ if (likely(!context)) - return; + goto out; if (context->in_syscall && context->auditable) audit_log_exit(context, GFP_KERNEL); @@ -1026,6 +1026,7 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code) audit_zero_context(context, context->state); tsk->audit_context = context; } + out: put_task_struct(tsk); } -- cgit v1.2.3-18-g5258 From c3896495942392f1a792da1cafba7a573cbf6fc2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 17 Aug 2005 14:49:57 +0100 Subject: AUDIT: Speed up audit_filter_syscall() for the non-auditable case. It was showing up fairly high on profiles even when no rules were set. Make sure the common path stays as fast as possible. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index a73176eaa57..818ef9fdab3 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -513,20 +513,23 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, struct list_head *list) { struct audit_entry *e; - enum audit_state state; - int word = AUDIT_WORD(ctx->major); - int bit = AUDIT_BIT(ctx->major); + enum audit_state state; if (audit_pid && tsk->tgid == audit_pid) return AUDIT_DISABLED; rcu_read_lock(); - list_for_each_entry_rcu(e, list, list) { - if ((e->rule.mask[word] & bit) == bit - && audit_filter_rules(tsk, &e->rule, ctx, &state)) { - rcu_read_unlock(); - return state; - } + if (!list_empty(list)) { + int word = AUDIT_WORD(ctx->major); + int bit = AUDIT_BIT(ctx->major); + + list_for_each_entry_rcu(e, list, list) { + if ((e->rule.mask[word] & bit) == bit + && audit_filter_rules(tsk, &e->rule, ctx, &state)) { + rcu_read_unlock(); + return state; + } + } } rcu_read_unlock(); return AUDIT_BUILD_CONTEXT; @@ -1023,7 +1026,6 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code) } else { audit_free_names(context); audit_free_aux(context); - audit_zero_context(context, context->state); tsk->audit_context = context; } out: -- cgit v1.2.3-18-g5258 From 3c789a19054034847afe80af2f23ebb0eebfbad6 Mon Sep 17 00:00:00 2001 From: Amy Griffis Date: Wed, 17 Aug 2005 16:05:35 +0100 Subject: AUDIT: Prevent duplicate syscall rules The following patch against audit.81 prevents duplicate syscall rules in a given filter list by walking the list on each rule add. I also removed the unused struct audit_entry in audit.c and made the static inlines in auditsc.c consistent. Signed-off-by: Amy Griffis Signed-off-by: David Woodhouse --- kernel/auditsc.c | 95 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 39 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 818ef9fdab3..488ba3dea8b 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -190,9 +190,36 @@ struct audit_entry { 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 int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) +static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) { int i; @@ -221,18 +248,37 @@ 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 void audit_add_rule(struct audit_entry *entry, +static inline int audit_add_rule(struct audit_rule *rule, struct list_head *list) { + struct audit_entry *entry; + + /* 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; + } + } + + 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 void audit_free_rule(struct rcu_head *head) +static inline void audit_free_rule(struct rcu_head *head) { struct audit_entry *e = container_of(head, struct audit_entry, rcu); kfree(e); @@ -258,32 +304,6 @@ static inline int audit_del_rule(struct audit_rule *rule, return -ENOENT; /* No matching rule */ } -/* Copy rule from user-space to kernel-space. Called during - * AUDIT_ADD. */ -static 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; -} - static int audit_list_rules(void *_dest) { int pid, seq; @@ -313,7 +333,6 @@ static int audit_list_rules(void *_dest) int audit_receive_filter(int type, int pid, int uid, int seq, void *data, uid_t loginuid) { - struct audit_entry *entry; struct task_struct *tsk; int *dest; int err = 0; @@ -340,16 +359,14 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, } break; case AUDIT_ADD: - if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) - return -ENOMEM; - if (audit_copy_rule(&entry->rule, data)) { - kfree(entry); + listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; + if (listnr >= AUDIT_NR_FILTERS) return -EINVAL; - } - listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND; - audit_add_rule(entry, &audit_filter_list[listnr]); - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u added an audit rule\n", loginuid); + + 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; -- cgit v1.2.3-18-g5258 From b01f2cc1c37ac3d5ca313c90370a586dffe5aca9 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 27 Aug 2005 10:25:43 +0100 Subject: [AUDIT] Allow filtering on system call success _or_ failure Signed-off-by: David Woodhouse --- kernel/auditsc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 488ba3dea8b..88696f639aa 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -440,8 +440,12 @@ static int audit_filter_rules(struct task_struct *tsk, result = (ctx->return_code == value); break; case AUDIT_SUCCESS: - if (ctx && ctx->return_valid) - result = (ctx->return_valid == AUDITSC_SUCCESS); + if (ctx && ctx->return_valid) { + if (value) + result = (ctx->return_valid == AUDITSC_SUCCESS); + else + result = (ctx->return_valid == AUDITSC_FAILURE); + } break; case AUDIT_DEVMAJOR: if (ctx) { -- cgit v1.2.3-18-g5258