Commit 5b52330b authored by Paul Moore's avatar Paul Moore

audit: fix auditd/kernel connection state tracking

What started as a rather straightforward race condition reported by
Dmitry using the syzkaller fuzzer ended up revealing some major
problems with how the audit subsystem managed its netlink sockets and
its connection with the userspace audit daemon.  Fixing this properly
had quite the cascading effect and what we are left with is this rather
large and complicated patch.  My initial goal was to try and decompose
this patch into multiple smaller patches, but the way these changes
are intertwined makes it difficult to split these changes into
meaningful pieces that don't break or somehow make things worse for
the intermediate states.

The patch makes a number of changes, but the most significant are
highlighted below:

* The auditd tracking variables, e.g. audit_sock, are now gone and
replaced by a RCU/spin_lock protected variable auditd_conn which is
a structure containing all of the auditd tracking information.

* We no longer track the auditd sock directly, instead we track it
via the network namespace in which it resides and we use the audit
socket associated with that namespace.  In spirit, this is what the
code was trying to do prior to this patch (at least I think that is
what the original authors intended), but it was done rather poorly
and added a layer of obfuscation that only masked the underlying
problems.

* Big backlog queue cleanup, again.  In v4.10 we made some pretty big
changes to how the audit backlog queues work, here we haven't changed
the queue design so much as cleaned up the implementation.  Brought
about by the locking changes, we've simplified kauditd_thread() quite
a bit by consolidating the queue handling into a new helper function,
kauditd_send_queue(), which allows us to eliminate a lot of very
similar code and makes the looping logic in kauditd_thread() clearer.

* All netlink messages sent to auditd are now sent via
auditd_send_unicast_skb().  Other than just making sense, this makes
the lock handling easier.

* Change the audit_log_start() sleep behavior so that we never sleep
on auditd events (unchanged) or if the caller is holding the
audit_cmd_mutex (changed).  Previously we didn't sleep if the caller
was auditd or if the message type fell between a certain range; the
type check was a poor effort of doing what the cmd_mutex check now
does.  Richard Guy Briggs originally proposed not sleeping the
cmd_mutex owner several years ago but his patch wasn't acceptable
at the time.  At least the idea lives on here.

* A problem with the lost record counter has been resolved.  Steve
Grubb and I both happened to notice this problem and according to
some quick testing by Steve, this problem goes back quite some time.
It's largely a harmless problem, although it may have left some
careful sysadmins quite puzzled.

Cc: <stable@vger.kernel.org> # 4.10.x-
Reported-by: default avatarDmitry Vyukov <dvyukov@google.com>
Signed-off-by: default avatarPaul Moore <paul@paul-moore.com>
parent 97da3854
This diff is collapsed.
...@@ -218,7 +218,7 @@ extern void audit_log_name(struct audit_context *context, ...@@ -218,7 +218,7 @@ extern void audit_log_name(struct audit_context *context,
struct audit_names *n, const struct path *path, struct audit_names *n, const struct path *path,
int record_num, int *call_panic); int record_num, int *call_panic);
extern int audit_pid; extern int auditd_test_task(const struct task_struct *task);
#define AUDIT_INODE_BUCKETS 32 #define AUDIT_INODE_BUCKETS 32
extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
...@@ -250,10 +250,6 @@ struct audit_netlink_list { ...@@ -250,10 +250,6 @@ struct audit_netlink_list {
int audit_send_list(void *); int audit_send_list(void *);
struct audit_net {
struct sock *nlsk;
};
extern int selinux_audit_rule_update(void); extern int selinux_audit_rule_update(void);
extern struct mutex audit_filter_mutex; extern struct mutex audit_filter_mutex;
...@@ -340,8 +336,7 @@ extern int audit_filter(int msgtype, unsigned int listtype); ...@@ -340,8 +336,7 @@ extern int audit_filter(int msgtype, unsigned int listtype);
extern int __audit_signal_info(int sig, struct task_struct *t); extern int __audit_signal_info(int sig, struct task_struct *t);
static inline int audit_signal_info(int sig, struct task_struct *t) static inline int audit_signal_info(int sig, struct task_struct *t)
{ {
if (unlikely((audit_pid && t->tgid == audit_pid) || if (auditd_test_task(t) || (audit_signals && !audit_dummy_context()))
(audit_signals && !audit_dummy_context())))
return __audit_signal_info(sig, t); return __audit_signal_info(sig, t);
return 0; return 0;
} }
......
...@@ -762,7 +762,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, ...@@ -762,7 +762,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
struct audit_entry *e; struct audit_entry *e;
enum audit_state state; enum audit_state state;
if (audit_pid && tsk->tgid == audit_pid) if (auditd_test_task(tsk))
return AUDIT_DISABLED; return AUDIT_DISABLED;
rcu_read_lock(); rcu_read_lock();
...@@ -816,7 +816,7 @@ void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx) ...@@ -816,7 +816,7 @@ void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)
{ {
struct audit_names *n; struct audit_names *n;
if (audit_pid && tsk->tgid == audit_pid) if (auditd_test_task(tsk))
return; return;
rcu_read_lock(); rcu_read_lock();
...@@ -2256,7 +2256,7 @@ int __audit_signal_info(int sig, struct task_struct *t) ...@@ -2256,7 +2256,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
struct audit_context *ctx = tsk->audit_context; struct audit_context *ctx = tsk->audit_context;
kuid_t uid = current_uid(), t_uid = task_uid(t); kuid_t uid = current_uid(), t_uid = task_uid(t);
if (audit_pid && t->tgid == audit_pid) { if (auditd_test_task(t)) {
if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) { if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) {
audit_sig_pid = task_tgid_nr(tsk); audit_sig_pid = task_tgid_nr(tsk);
if (uid_valid(tsk->loginuid)) if (uid_valid(tsk->loginuid))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment