]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
UBUNTU: SAUCE: Revert "audit: fix auditd/kernel connection state tracking"
authorSeth Forshee <seth.forshee@canonical.com>
Thu, 6 Apr 2017 14:24:46 +0000 (15:24 +0100)
committerSeth Forshee <seth.forshee@canonical.com>
Thu, 6 Apr 2017 16:36:02 +0000 (17:36 +0100)
BugLink: http://bugs.launchpad.net/bugs/1680532
This commit was part of upstream 4.10.7 and has caused two ADT
regressions:

- snapd tests have regressed because expected audit messages are
  not being printed.
- On i386, the apparmor regression tests are causing OOMs.

Both are fixed by reverting this patch.

Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
kernel/audit.c
kernel/audit.h
kernel/auditsc.c

index ba4481d20fa1cbfe9d030f95d463cc73648880c3..6e399bb69d7c6ab54ddf22d2d908c630c0d17fc0 100644 (file)
 #include <linux/kthread.h>
 #include <linux/kernel.h>
 #include <linux/syscalls.h>
-#include <linux/spinlock.h>
-#include <linux/rcupdate.h>
-#include <linux/mutex.h>
-#include <linux/gfp.h>
 
 #include <linux/audit.h>
 
@@ -94,34 +90,13 @@ static u32  audit_default;
 /* If auditing cannot proceed, audit_failure selects what happens. */
 static u32     audit_failure = AUDIT_FAIL_PRINTK;
 
-/* private audit network namespace index */
-static unsigned int audit_net_id;
-
-/**
- * struct audit_net - audit private network namespace data
- * @sk: communication socket
- */
-struct audit_net {
-       struct sock *sk;
-};
-
-/**
- * struct auditd_connection - kernel/auditd connection state
- * @pid: auditd PID
- * @portid: netlink portid
- * @net: the associated network namespace
- * @lock: spinlock to protect write access
- *
- * Description:
- * This struct is RCU protected; you must either hold the RCU lock for reading
- * or the included spinlock for writing.
+/*
+ * If audit records are to be written to the netlink socket, audit_pid
+ * contains the pid of the auditd process and audit_nlk_portid contains
+ * the portid to use to send netlink messages to that process.
  */
-static struct auditd_connection {
-       int pid;
-       u32 portid;
-       struct net *net;
-       spinlock_t lock;
-} auditd_conn;
+int            audit_pid;
+static __u32   audit_nlk_portid;
 
 /* If audit_rate_limit is non-zero, limit the rate of sending audit records
  * to that number per second.  This prevents DoS attacks, but results in
@@ -148,6 +123,10 @@ u32                audit_sig_sid = 0;
 */
 static atomic_t    audit_lost = ATOMIC_INIT(0);
 
+/* The netlink socket. */
+static struct sock *audit_sock;
+static unsigned int audit_net_id;
+
 /* Hash for inode-based rules */
 struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
 
@@ -160,7 +139,6 @@ static LIST_HEAD(audit_freelist);
 
 /* queue msgs to send via kauditd_task */
 static struct sk_buff_head audit_queue;
-static void kauditd_hold_skb(struct sk_buff *skb);
 /* queue msgs due to temporary unicast send problems */
 static struct sk_buff_head audit_retry_queue;
 /* queue msgs waiting for new auditd connection */
@@ -214,43 +192,6 @@ struct audit_reply {
        struct sk_buff *skb;
 };
 
-/**
- * auditd_test_task - Check to see if a given task is an audit daemon
- * @task: the task to check
- *
- * Description:
- * Return 1 if the task is a registered audit daemon, 0 otherwise.
- */
-int auditd_test_task(const struct task_struct *task)
-{
-       int rc;
-
-       rcu_read_lock();
-       rc = (auditd_conn.pid && task->tgid == auditd_conn.pid ? 1 : 0);
-       rcu_read_unlock();
-
-       return rc;
-}
-
-/**
- * audit_get_sk - Return the audit socket for the given network namespace
- * @net: the destination network namespace
- *
- * Description:
- * Returns the sock pointer if valid, NULL otherwise.  The caller must ensure
- * that a reference is held for the network namespace while the sock is in use.
- */
-static struct sock *audit_get_sk(const struct net *net)
-{
-       struct audit_net *aunet;
-
-       if (!net)
-               return NULL;
-
-       aunet = net_generic(net, audit_net_id);
-       return aunet->sk;
-}
-
 static void audit_set_portid(struct audit_buffer *ab, __u32 portid)
 {
        if (ab) {
@@ -269,7 +210,9 @@ void audit_panic(const char *message)
                        pr_err("%s\n", message);
                break;
        case AUDIT_FAIL_PANIC:
-               panic("audit: %s\n", message);
+               /* test audit_pid since printk is always losey, why bother? */
+               if (audit_pid)
+                       panic("audit: %s\n", message);
                break;
        }
 }
@@ -427,87 +370,21 @@ static int audit_set_failure(u32 state)
        return audit_do_config_change("audit_failure", &audit_failure, state);
 }
 
-/**
- * auditd_set - Set/Reset the auditd connection state
- * @pid: auditd PID
- * @portid: auditd netlink portid
- * @net: auditd network namespace pointer
- *
- * Description:
- * This function will obtain and drop network namespace references as
- * necessary.
- */
-static void auditd_set(int pid, u32 portid, struct net *net)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&auditd_conn.lock, flags);
-       auditd_conn.pid = pid;
-       auditd_conn.portid = portid;
-       if (auditd_conn.net)
-               put_net(auditd_conn.net);
-       if (net)
-               auditd_conn.net = get_net(net);
-       else
-               auditd_conn.net = NULL;
-       spin_unlock_irqrestore(&auditd_conn.lock, flags);
-}
-
-/**
- * auditd_reset - Disconnect the auditd connection
- *
- * Description:
- * Break the auditd/kauditd connection and move all the queued records into the
- * hold queue in case auditd reconnects.
- */
-static void auditd_reset(void)
-{
-       struct sk_buff *skb;
-
-       /* if it isn't already broken, break the connection */
-       rcu_read_lock();
-       if (auditd_conn.pid)
-               auditd_set(0, 0, NULL);
-       rcu_read_unlock();
-
-       /* flush all of the main and retry queues to the hold queue */
-       while ((skb = skb_dequeue(&audit_retry_queue)))
-               kauditd_hold_skb(skb);
-       while ((skb = skb_dequeue(&audit_queue)))
-               kauditd_hold_skb(skb);
-}
-
-/**
- * kauditd_print_skb - Print the audit record to the ring buffer
- * @skb: audit record
- *
- * Whatever the reason, this packet may not make it to the auditd connection
- * so write it via printk so the information isn't completely lost.
+/*
+ * For one reason or another this nlh isn't getting delivered to the userspace
+ * audit daemon, just send it to printk.
  */
 static void kauditd_printk_skb(struct sk_buff *skb)
 {
        struct nlmsghdr *nlh = nlmsg_hdr(skb);
        char *data = nlmsg_data(nlh);
 
-       if (nlh->nlmsg_type != AUDIT_EOE && printk_ratelimit())
-               pr_notice("type=%d %s\n", nlh->nlmsg_type, data);
-}
-
-/**
- * kauditd_rehold_skb - Handle a audit record send failure in the hold queue
- * @skb: audit record
- *
- * Description:
- * This should only be used by the kauditd_thread when it fails to flush the
- * hold queue.
- */
-static void kauditd_rehold_skb(struct sk_buff *skb)
-{
-       /* put the record back in the queue at the same place */
-       skb_queue_head(&audit_hold_queue, skb);
-
-       /* fail the auditd connection */
-       auditd_reset();
+       if (nlh->nlmsg_type != AUDIT_EOE) {
+               if (printk_ratelimit())
+                       pr_notice("type=%d %s\n", nlh->nlmsg_type, data);
+               else
+                       audit_log_lost("printk limit exceeded");
+       }
 }
 
 /**
@@ -544,9 +421,6 @@ static void kauditd_hold_skb(struct sk_buff *skb)
        /* we have no other options - drop the message */
        audit_log_lost("kauditd hold queue overflow");
        kfree_skb(skb);
-
-       /* fail the auditd connection */
-       auditd_reset();
 }
 
 /**
@@ -567,122 +441,51 @@ static void kauditd_retry_skb(struct sk_buff *skb)
 }
 
 /**
- * auditd_send_unicast_skb - Send a record via unicast to auditd
- * @skb: audit record
+ * auditd_reset - Disconnect the auditd connection
  *
  * Description:
- * Send a skb to the audit daemon, returns positive/zero values on success and
- * negative values on failure; in all cases the skb will be consumed by this
- * function.  If the send results in -ECONNREFUSED the connection with auditd
- * will be reset.  This function may sleep so callers should not hold any locks
- * where this would cause a problem.
+ * Break the auditd/kauditd connection and move all the records in the retry
+ * queue into the hold queue in case auditd reconnects.  The audit_cmd_mutex
+ * must be held when calling this function.
  */
-static int auditd_send_unicast_skb(struct sk_buff *skb)
+static void auditd_reset(void)
 {
-       int rc;
-       u32 portid;
-       struct net *net;
-       struct sock *sk;
-
-       /* NOTE: we can't call netlink_unicast while in the RCU section so
-        *       take a reference to the network namespace and grab local
-        *       copies of the namespace, the sock, and the portid; the
-        *       namespace and sock aren't going to go away while we hold a
-        *       reference and if the portid does become invalid after the RCU
-        *       section netlink_unicast() should safely return an error */
-
-       rcu_read_lock();
-       if (!auditd_conn.pid) {
-               rcu_read_unlock();
-               rc = -ECONNREFUSED;
-               goto err;
-       }
-       net = auditd_conn.net;
-       get_net(net);
-       sk = audit_get_sk(net);
-       portid = auditd_conn.portid;
-       rcu_read_unlock();
-
-       rc = netlink_unicast(sk, skb, portid, 0);
-       put_net(net);
-       if (rc < 0)
-               goto err;
+       struct sk_buff *skb;
 
-       return rc;
+       /* break the connection */
+       if (audit_sock) {
+               sock_put(audit_sock);
+               audit_sock = NULL;
+       }
+       audit_pid = 0;
+       audit_nlk_portid = 0;
 
-err:
-       if (rc == -ECONNREFUSED)
-               auditd_reset();
-       return rc;
+       /* flush all of the retry queue to the hold queue */
+       while ((skb = skb_dequeue(&audit_retry_queue)))
+               kauditd_hold_skb(skb);
 }
 
 /**
- * kauditd_send_queue - Helper for kauditd_thread to flush skb queues
- * @sk: the sending sock
- * @portid: the netlink destination
- * @queue: the skb queue to process
- * @retry_limit: limit on number of netlink unicast failures
- * @skb_hook: per-skb hook for additional processing
- * @err_hook: hook called if the skb fails the netlink unicast send
- *
- * Description:
- * Run through the given queue and attempt to send the audit records to auditd,
- * returns zero on success, negative values on failure.  It is up to the caller
- * to ensure that the @sk is valid for the duration of this function.
- *
+ * kauditd_send_unicast_skb - Send a record via unicast to auditd
+ * @skb: audit record
  */
-static int kauditd_send_queue(struct sock *sk, u32 portid,
-                             struct sk_buff_head *queue,
-                             unsigned int retry_limit,
-                             void (*skb_hook)(struct sk_buff *skb),
-                             void (*err_hook)(struct sk_buff *skb))
+static int kauditd_send_unicast_skb(struct sk_buff *skb)
 {
-       int rc = 0;
-       struct sk_buff *skb;
-       static unsigned int failed = 0;
-
-       /* NOTE: kauditd_thread takes care of all our locking, we just use
-        *       the netlink info passed to us (e.g. sk and portid) */
-
-       while ((skb = skb_dequeue(queue))) {
-               /* call the skb_hook for each skb we touch */
-               if (skb_hook)
-                       (*skb_hook)(skb);
+       int rc;
 
-               /* can we send to anyone via unicast? */
-               if (!sk) {
-                       if (err_hook)
-                               (*err_hook)(skb);
-                       continue;
-               }
+       /* if we know nothing is connected, don't even try the netlink call */
+       if (!audit_pid)
+               return -ECONNREFUSED;
 
-               /* grab an extra skb reference in case of error */
-               skb_get(skb);
-               rc = netlink_unicast(sk, skb, portid, 0);
-               if (rc < 0) {
-                       /* fatal failure for our queue flush attempt? */
-                       if (++failed >= retry_limit ||
-                           rc == -ECONNREFUSED || rc == -EPERM) {
-                               /* yes - error processing for the queue */
-                               sk = NULL;
-                               if (err_hook)
-                                       (*err_hook)(skb);
-                               if (!skb_hook)
-                                       goto out;
-                               /* keep processing with the skb_hook */
-                               continue;
-                       } else
-                               /* no - requeue to preserve ordering */
-                               skb_queue_head(queue, skb);
-               } else {
-                       /* it worked - drop the extra reference and continue */
-                       consume_skb(skb);
-                       failed = 0;
-               }
+       /* get an extra skb reference in case we fail to send */
+       skb_get(skb);
+       rc = netlink_unicast(audit_sock, skb, audit_nlk_portid, 0);
+       if (rc >= 0) {
+               consume_skb(skb);
+               rc = 0;
        }
 
-out:
-       return (rc >= 0 ? 0 : rc);
+       return rc;
 }
 
 /*
@@ -690,19 +493,16 @@ out:
  * @skb: audit record
  *
  * Description:
- * Write a multicast message to anyone listening in the initial network
- * namespace.  This function doesn't consume an skb as might be expected since
- * it has to copy it anyways.
+ * This function doesn't consume an skb as might be expected since it has to
+ * copy it anyways.
  */
 static void kauditd_send_multicast_skb(struct sk_buff *skb)
 {
        struct sk_buff *copy;
-       struct sock *sock = audit_get_sk(&init_net);
+       struct audit_net *aunet = net_generic(&init_net, audit_net_id);
+       struct sock *sock = aunet->nlsk;
        struct nlmsghdr *nlh;
 
-       /* NOTE: we are not taking an additional reference for init_net since
-        *       we don't have to worry about it going away */
-
        if (!netlink_has_listeners(sock, AUDIT_NLGRP_READLOG))
                return;
 
@@ -726,75 +526,149 @@ static void kauditd_send_multicast_skb(struct sk_buff *skb)
 }
 
 /**
- * kauditd_thread - Worker thread to send audit records to userspace
- * @dummy: unused
+ * kauditd_wake_condition - Return true when it is time to wake kauditd_thread
+ *
+ * Description:
+ * This function is for use by the wait_event_freezable() call in
+ * kauditd_thread().
  */
+static int kauditd_wake_condition(void)
+{
+       static int pid_last = 0;
+       int rc;
+       int pid = audit_pid;
+
+       /* wake on new messages or a change in the connected auditd */
+       rc = skb_queue_len(&audit_queue) || (pid && pid != pid_last);
+       if (rc)
+               pid_last = pid;
+
+       return rc;
+}
+
 static int kauditd_thread(void *dummy)
 {
        int rc;
-       u32 portid = 0;
-       struct net *net = NULL;
-       struct sock *sk = NULL;
+       int auditd = 0;
+       int reschedule = 0;
+       struct sk_buff *skb;
+       struct nlmsghdr *nlh;
 
 #define UNICAST_RETRIES 5
+#define AUDITD_BAD(x,y) \
+       ((x) == -ECONNREFUSED || (x) == -EPERM || ++(y) >= UNICAST_RETRIES)
+
+       /* NOTE: we do invalidate the auditd connection flag on any sending
+        * errors, but we only "restore" the connection flag at specific places
+        * in the loop in order to help ensure proper ordering of audit
+        * records */
 
        set_freezable();
        while (!kthread_should_stop()) {
-               /* NOTE: see the lock comments in auditd_send_unicast_skb() */
-               rcu_read_lock();
-               if (!auditd_conn.pid) {
-                       rcu_read_unlock();
-                       goto main_queue;
-               }
-               net = auditd_conn.net;
-               get_net(net);
-               sk = audit_get_sk(net);
-               portid = auditd_conn.portid;
-               rcu_read_unlock();
+               /* NOTE: possible area for future improvement is to look at
+                *       the hold and retry queues, since only this thread
+                *       has access to these queues we might be able to do
+                *       our own queuing and skip some/all of the locking */
+
+               /* NOTE: it might be a fun experiment to split the hold and
+                *       retry queue handling to another thread, but the
+                *       synchronization issues and other overhead might kill
+                *       any performance gains */
 
                /* attempt to flush the hold queue */
-               rc = kauditd_send_queue(sk, portid,
-                                       &audit_hold_queue, UNICAST_RETRIES,
-                                       NULL, kauditd_rehold_skb);
-               if (rc < 0) {
-                       sk = NULL;
-                       goto main_queue;
+               while (auditd && (skb = skb_dequeue(&audit_hold_queue))) {
+                       rc = kauditd_send_unicast_skb(skb);
+                       if (rc) {
+                               /* requeue to the same spot */
+                               skb_queue_head(&audit_hold_queue, skb);
+
+                               auditd = 0;
+                               if (AUDITD_BAD(rc, reschedule)) {
+                                       mutex_lock(&audit_cmd_mutex);
+                                       auditd_reset();
+                                       mutex_unlock(&audit_cmd_mutex);
+                                       reschedule = 0;
+                               }
+                       } else
+                               /* we were able to send successfully */
+                               reschedule = 0;
                }
 
                /* attempt to flush the retry queue */
-               rc = kauditd_send_queue(sk, portid,
-                                       &audit_retry_queue, UNICAST_RETRIES,
-                                       NULL, kauditd_hold_skb);
-               if (rc < 0) {
-                       sk = NULL;
-                       goto main_queue;
-               }
-
-main_queue:
-               /* process the main queue - do the multicast send and attempt
-                * unicast, dump failed record sends to the retry queue; if
-                * sk == NULL due to previous failures we will just do the
-                * multicast send and move the record to the retry queue */
-               kauditd_send_queue(sk, portid, &audit_queue, 1,
-                                  kauditd_send_multicast_skb,
-                                  kauditd_retry_skb);
-
-               /* drop our netns reference, no auditd sends past this line */
-               if (net) {
-                       put_net(net);
-                       net = NULL;
+               while (auditd && (skb = skb_dequeue(&audit_retry_queue))) {
+                       rc = kauditd_send_unicast_skb(skb);
+                       if (rc) {
+                               auditd = 0;
+                               if (AUDITD_BAD(rc, reschedule)) {
+                                       kauditd_hold_skb(skb);
+                                       mutex_lock(&audit_cmd_mutex);
+                                       auditd_reset();
+                                       mutex_unlock(&audit_cmd_mutex);
+                                       reschedule = 0;
+                               } else
+                                       /* temporary problem (we hope), queue
+                                        * to the same spot and retry */
+                                       skb_queue_head(&audit_retry_queue, skb);
+                       } else
+                               /* we were able to send successfully */
+                               reschedule = 0;
                }
-               sk = NULL;
 
-               /* we have processed all the queues so wake everyone */
-               wake_up(&audit_backlog_wait);
+               /* standard queue processing, try to be as quick as possible */
+quick_loop:
+               skb = skb_dequeue(&audit_queue);
+               if (skb) {
+                       /* setup the netlink header, see the comments in
+                        * kauditd_send_multicast_skb() for length quirks */
+                       nlh = nlmsg_hdr(skb);
+                       nlh->nlmsg_len = skb->len - NLMSG_HDRLEN;
+
+                       /* attempt to send to any multicast listeners */
+                       kauditd_send_multicast_skb(skb);
+
+                       /* attempt to send to auditd, queue on failure */
+                       if (auditd) {
+                               rc = kauditd_send_unicast_skb(skb);
+                               if (rc) {
+                                       auditd = 0;
+                                       if (AUDITD_BAD(rc, reschedule)) {
+                                               mutex_lock(&audit_cmd_mutex);
+                                               auditd_reset();
+                                               mutex_unlock(&audit_cmd_mutex);
+                                               reschedule = 0;
+                                       }
+
+                                       /* move to the retry queue */
+                                       kauditd_retry_skb(skb);
+                               } else
+                                       /* everything is working so go fast! */
+                                       goto quick_loop;
+                       } else if (reschedule)
+                               /* we are currently having problems, move to
+                                * the retry queue */
+                               kauditd_retry_skb(skb);
+                       else
+                               /* dump the message via printk and hold it */
+                               kauditd_hold_skb(skb);
+               } else {
+                       /* we have flushed the backlog so wake everyone */
+                       wake_up(&audit_backlog_wait);
+
+                       /* if everything is okay with auditd (if present), go
+                        * to sleep until there is something new in the queue
+                        * or we have a change in the connected auditd;
+                        * otherwise simply reschedule to give things a chance
+                        * to recover */
+                       if (reschedule) {
+                               set_current_state(TASK_INTERRUPTIBLE);
+                               schedule();
+                       } else
+                               wait_event_freezable(kauditd_wait,
+                                                    kauditd_wake_condition());
 
-               /* NOTE: we want to wake up if there is anything on the queue,
-                *       regardless of if an auditd is connected, as we need to
-                *       do the multicast send and rotate records from the
-                *       main queue to the retry/hold queues */
-               wait_event_freezable(kauditd_wait,
-                                    (skb_queue_len(&audit_queue) ? 1 : 0));
+                       /* update the auditd connection status */
+                       auditd = (audit_pid ? 1 : 0);
+               }
        }
 
        return 0;
@@ -804,16 +678,17 @@ int audit_send_list(void *_dest)
 {
        struct audit_netlink_list *dest = _dest;
        struct sk_buff *skb;
-       struct sock *sk = audit_get_sk(dest->net);
+       struct net *net = dest->net;
+       struct audit_net *aunet = net_generic(net, audit_net_id);
 
        /* wait for parent to finish and send an ACK */
        mutex_lock(&audit_cmd_mutex);
        mutex_unlock(&audit_cmd_mutex);
 
        while ((skb = __skb_dequeue(&dest->q)) != NULL)
-               netlink_unicast(sk, skb, dest->portid, 0);
+               netlink_unicast(aunet->nlsk, skb, dest->portid, 0);
 
-       put_net(dest->net);
+       put_net(net);
        kfree(dest);
 
        return 0;
@@ -847,15 +722,16 @@ out_kfree_skb:
 static int audit_send_reply_thread(void *arg)
 {
        struct audit_reply *reply = (struct audit_reply *)arg;
-       struct sock *sk = audit_get_sk(reply->net);
+       struct net *net = reply->net;
+       struct audit_net *aunet = net_generic(net, audit_net_id);
 
        mutex_lock(&audit_cmd_mutex);
        mutex_unlock(&audit_cmd_mutex);
 
        /* Ignore failure. It'll only happen if the sender goes away,
           because our timeout is set to infinite. */
-       netlink_unicast(sk, reply->skb, reply->portid, 0);
-       put_net(reply->net);
+       netlink_unicast(aunet->nlsk , reply->skb, reply->portid, 0);
+       put_net(net);
        kfree(reply);
        return 0;
 }
@@ -1073,12 +949,12 @@ static int audit_set_feature(struct sk_buff *skb)
 
 static int audit_replace(pid_t pid)
 {
-       struct sk_buff *skb;
+       struct sk_buff *skb = audit_make_reply(0, 0, AUDIT_REPLACE, 0, 0,
+                                              &pid, sizeof(pid));
 
-       skb = audit_make_reply(0, 0, AUDIT_REPLACE, 0, 0, &pid, sizeof(pid));
        if (!skb)
                return -ENOMEM;
-       return auditd_send_unicast_skb(skb);
+       return netlink_unicast(audit_sock, skb, audit_nlk_portid, 0);
 }
 
 static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -1105,9 +981,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                memset(&s, 0, sizeof(s));
                s.enabled               = audit_enabled;
                s.failure               = audit_failure;
-               rcu_read_lock();
-               s.pid                   = auditd_conn.pid;
-               rcu_read_unlock();
+               s.pid                   = audit_pid;
                s.rate_limit            = audit_rate_limit;
                s.backlog_limit         = audit_backlog_limit;
                s.lost                  = atomic_read(&audit_lost);
@@ -1140,44 +1014,30 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                         *       from the initial pid namespace, but something
                         *       to keep in mind if this changes */
                        int new_pid = s.pid;
-                       pid_t auditd_pid;
                        pid_t requesting_pid = task_tgid_vnr(current);
 
-                       /* test the auditd connection */
-                       audit_replace(requesting_pid);
-
-                       rcu_read_lock();
-                       auditd_pid = auditd_conn.pid;
-                       /* only the current auditd can unregister itself */
-                       if ((!new_pid) && (requesting_pid != auditd_pid)) {
-                               rcu_read_unlock();
-                               audit_log_config_change("audit_pid", new_pid,
-                                                       auditd_pid, 0);
+                       if ((!new_pid) && (requesting_pid != audit_pid)) {
+                               audit_log_config_change("audit_pid", new_pid, audit_pid, 0);
                                return -EACCES;
                        }
-                       /* replacing a healthy auditd is not allowed */
-                       if (auditd_pid && new_pid) {
-                               rcu_read_unlock();
-                               audit_log_config_change("audit_pid", new_pid,
-                                                       auditd_pid, 0);
+                       if (audit_pid && new_pid &&
+                           audit_replace(requesting_pid) != -ECONNREFUSED) {
+                               audit_log_config_change("audit_pid", new_pid, audit_pid, 0);
                                return -EEXIST;
                        }
-                       rcu_read_unlock();
-
                        if (audit_enabled != AUDIT_OFF)
-                               audit_log_config_change("audit_pid", new_pid,
-                                                       auditd_pid, 1);
-
+                               audit_log_config_change("audit_pid", new_pid, audit_pid, 1);
                        if (new_pid) {
-                               /* register a new auditd connection */
-                               auditd_set(new_pid,
-                                          NETLINK_CB(skb).portid,
-                                          sock_net(NETLINK_CB(skb).sk));
-                               /* try to process any backlog */
-                               wake_up_interruptible(&kauditd_wait);
-                       } else
-                               /* unregister the auditd connection */
+                               if (audit_sock)
+                                       sock_put(audit_sock);
+                               audit_pid = new_pid;
+                               audit_nlk_portid = NETLINK_CB(skb).portid;
+                               sock_hold(skb->sk);
+                               audit_sock = skb->sk;
+                       } else {
                                auditd_reset();
+                       }
+                       wake_up_interruptible(&kauditd_wait);
                }
                if (s.mask & AUDIT_STATUS_RATE_LIMIT) {
                        err = audit_set_rate_limit(s.rate_limit);
@@ -1224,6 +1084,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                                if (err)
                                        break;
                        }
+                       mutex_unlock(&audit_cmd_mutex);
                        audit_log_common_recv_msg(&ab, msg_type);
                        if (msg_type != AUDIT_USER_TTY)
                                audit_log_format(ab, " msg='%.*s'",
@@ -1241,6 +1102,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        }
                        audit_set_portid(ab, NETLINK_CB(skb).portid);
                        audit_log_end(ab);
+                       mutex_lock(&audit_cmd_mutex);
                }
                break;
        case AUDIT_ADD_RULE:
@@ -1430,26 +1292,26 @@ static int __net_init audit_net_init(struct net *net)
 
        struct audit_net *aunet = net_generic(net, audit_net_id);
 
-       aunet->sk = netlink_kernel_create(net, NETLINK_AUDIT, &cfg);
-       if (aunet->sk == NULL) {
+       aunet->nlsk = netlink_kernel_create(net, NETLINK_AUDIT, &cfg);
+       if (aunet->nlsk == NULL) {
                audit_panic("cannot initialize netlink socket in namespace");
                return -ENOMEM;
        }
-       aunet->sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
-
+       aunet->nlsk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
        return 0;
 }
 
 static void __net_exit audit_net_exit(struct net *net)
 {
        struct audit_net *aunet = net_generic(net, audit_net_id);
-
-       rcu_read_lock();
-       if (net == auditd_conn.net)
+       struct sock *sock = aunet->nlsk;
+       mutex_lock(&audit_cmd_mutex);
+       if (sock == audit_sock)
                auditd_reset();
-       rcu_read_unlock();
+       mutex_unlock(&audit_cmd_mutex);
 
-       netlink_kernel_release(aunet->sk);
+       netlink_kernel_release(sock);
+       aunet->nlsk = NULL;
 }
 
 static struct pernet_operations audit_net_ops __net_initdata = {
@@ -1467,24 +1329,20 @@ static int __init audit_init(void)
        if (audit_initialized == AUDIT_DISABLED)
                return 0;
 
-       memset(&auditd_conn, 0, sizeof(auditd_conn));
-       spin_lock_init(&auditd_conn.lock);
-
-       skb_queue_head_init(&audit_queue);
-       skb_queue_head_init(&audit_retry_queue);
-       skb_queue_head_init(&audit_hold_queue);
-
-       for (i = 0; i < AUDIT_INODE_BUCKETS; i++)
-               INIT_LIST_HEAD(&audit_inode_hash[i]);
-
        pr_info("initializing netlink subsys (%s)\n",
                audit_default ? "enabled" : "disabled");
        register_pernet_subsys(&audit_net_ops);
 
+       skb_queue_head_init(&audit_queue);
+       skb_queue_head_init(&audit_retry_queue);
+       skb_queue_head_init(&audit_hold_queue);
        audit_initialized = AUDIT_INITIALIZED;
        audit_enabled = audit_default;
        audit_ever_enabled |= !!audit_default;
 
+       for (i = 0; i < AUDIT_INODE_BUCKETS; i++)
+               INIT_LIST_HEAD(&audit_inode_hash[i]);
+
        kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd");
        if (IS_ERR(kauditd_task)) {
                int err = PTR_ERR(kauditd_task);
@@ -1653,16 +1511,20 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
        if (unlikely(!audit_filter(type, AUDIT_FILTER_TYPE)))
                return NULL;
 
-       /* NOTE: don't ever fail/sleep on these two conditions:
+       /* don't ever fail/sleep on these two conditions:
         * 1. auditd generated record - since we need auditd to drain the
         *    queue; also, when we are checking for auditd, compare PIDs using
         *    task_tgid_vnr() since auditd_pid is set in audit_receive_msg()
         *    using a PID anchored in the caller's namespace
-        * 2. generator holding the audit_cmd_mutex - we don't want to block
-        *    while holding the mutex */
-       if (!(auditd_test_task(current) ||
-             (current == __mutex_owner(&audit_cmd_mutex)))) {
-               long stime = audit_backlog_wait_time;
+        * 2. audit command message - record types 1000 through 1099 inclusive
+        *    are command messages/records used to manage the kernel subsystem
+        *    and the audit userspace, blocking on these messages could cause
+        *    problems under load so don't do it (note: not all of these
+        *    command types are valid as record types, but it is quicker to
+        *    just check two ints than a series of ints in a if/switch stmt) */
+       if (!((audit_pid && audit_pid == task_tgid_vnr(current)) ||
+             (type >= 1000 && type <= 1099))) {
+               long sleep_time = audit_backlog_wait_time;
 
                while (audit_backlog_limit &&
                       (skb_queue_len(&audit_queue) > audit_backlog_limit)) {
@@ -1671,13 +1533,14 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
 
                        /* sleep if we are allowed and we haven't exhausted our
                         * backlog wait limit */
-                       if (gfpflags_allow_blocking(gfp_mask) && (stime > 0)) {
+                       if ((gfp_mask & __GFP_DIRECT_RECLAIM) &&
+                           (sleep_time > 0)) {
                                DECLARE_WAITQUEUE(wait, current);
 
                                add_wait_queue_exclusive(&audit_backlog_wait,
                                                         &wait);
                                set_current_state(TASK_UNINTERRUPTIBLE);
-                               stime = schedule_timeout(stime);
+                               sleep_time = schedule_timeout(sleep_time);
                                remove_wait_queue(&audit_backlog_wait, &wait);
                        } else {
                                if (audit_rate_check() && printk_ratelimit())
@@ -2256,27 +2119,15 @@ out:
  */
 void audit_log_end(struct audit_buffer *ab)
 {
-       struct sk_buff *skb;
-       struct nlmsghdr *nlh;
-
        if (!ab)
                return;
-
-       if (audit_rate_check()) {
-               skb = ab->skb;
-               ab->skb = NULL;
-
-               /* setup the netlink header, see the comments in
-                * kauditd_send_multicast_skb() for length quirks */
-               nlh = nlmsg_hdr(skb);
-               nlh->nlmsg_len = skb->len - NLMSG_HDRLEN;
-
-               /* queue the netlink packet and poke the kauditd thread */
-               skb_queue_tail(&audit_queue, skb);
-               wake_up_interruptible(&kauditd_wait);
-       } else
+       if (!audit_rate_check()) {
                audit_log_lost("rate limit exceeded");
-
+       } else {
+               skb_queue_tail(&audit_queue, ab->skb);
+               wake_up_interruptible(&kauditd_wait);
+               ab->skb = NULL;
+       }
        audit_buffer_free(ab);
 }
 
index c6fba919b2e4fbfa3b0aad056c4c4a20d4aafeb2..960d49c9db5e3c79b70171c6c9ca1f4d8acf58f2 100644 (file)
@@ -215,7 +215,7 @@ extern void audit_log_name(struct audit_context *context,
                           struct audit_names *n, const struct path *path,
                           int record_num, int *call_panic);
 
-extern int auditd_test_task(const struct task_struct *task);
+extern int audit_pid;
 
 #define AUDIT_INODE_BUCKETS    32
 extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
@@ -247,6 +247,10 @@ struct audit_netlink_list {
 
 int audit_send_list(void *);
 
+struct audit_net {
+       struct sock *nlsk;
+};
+
 extern int selinux_audit_rule_update(void);
 
 extern struct mutex audit_filter_mutex;
@@ -333,7 +337,8 @@ extern int audit_filter(int msgtype, unsigned int listtype);
 extern int __audit_signal_info(int sig, struct task_struct *t);
 static inline int audit_signal_info(int sig, struct task_struct *t)
 {
-       if (auditd_test_task(t) || (audit_signals && !audit_dummy_context()))
+       if (unlikely((audit_pid && t->tgid == audit_pid) ||
+                    (audit_signals && !audit_dummy_context())))
                return __audit_signal_info(sig, t);
        return 0;
 }
index 9e69c3a6b7320e2764e1093bce4eee7c2b563120..cf1fa43512c111b6fff0a81403acb62f9a4620a4 100644 (file)
@@ -762,7 +762,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
        struct audit_entry *e;
        enum audit_state state;
 
-       if (auditd_test_task(tsk))
+       if (audit_pid && tsk->tgid == audit_pid)
                return AUDIT_DISABLED;
 
        rcu_read_lock();
@@ -816,7 +816,7 @@ void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)
 {
        struct audit_names *n;
 
-       if (auditd_test_task(tsk))
+       if (audit_pid && tsk->tgid == audit_pid)
                return;
 
        rcu_read_lock();
@@ -2251,7 +2251,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
        struct audit_context *ctx = tsk->audit_context;
        kuid_t uid = current_uid(), t_uid = task_uid(t);
 
-       if (auditd_test_task(t)) {
+       if (audit_pid && t->tgid == audit_pid) {
                if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) {
                        audit_sig_pid = task_tgid_nr(tsk);
                        if (uid_valid(tsk->loginuid))