|
@@ -58,6 +58,8 @@
|
|
|
#include <linux/rcupdate.h>
|
|
|
#include <linux/mutex.h>
|
|
|
#include <linux/gfp.h>
|
|
|
+#include <linux/pid.h>
|
|
|
+#include <linux/slab.h>
|
|
|
|
|
|
#include <linux/audit.h>
|
|
|
|
|
@@ -110,18 +112,19 @@ struct audit_net {
|
|
|
* @pid: auditd PID
|
|
|
* @portid: netlink portid
|
|
|
* @net: the associated network namespace
|
|
|
- * @lock: spinlock to protect write access
|
|
|
+ * @rcu: RCU head
|
|
|
*
|
|
|
* Description:
|
|
|
* This struct is RCU protected; you must either hold the RCU lock for reading
|
|
|
- * or the included spinlock for writing.
|
|
|
+ * or the associated spinlock for writing.
|
|
|
*/
|
|
|
static struct auditd_connection {
|
|
|
- int pid;
|
|
|
+ struct pid *pid;
|
|
|
u32 portid;
|
|
|
struct net *net;
|
|
|
- spinlock_t lock;
|
|
|
-} auditd_conn;
|
|
|
+ struct rcu_head rcu;
|
|
|
+} *auditd_conn = NULL;
|
|
|
+static DEFINE_SPINLOCK(auditd_conn_lock);
|
|
|
|
|
|
/* 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
|
|
@@ -151,12 +154,7 @@ static atomic_t audit_lost = ATOMIC_INIT(0);
|
|
|
/* Hash for inode-based rules */
|
|
|
struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
|
|
|
|
|
|
-/* The audit_freelist is a list of pre-allocated audit buffers (if more
|
|
|
- * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
|
|
|
- * being placed on the freelist). */
|
|
|
-static DEFINE_SPINLOCK(audit_freelist_lock);
|
|
|
-static int audit_freelist_count;
|
|
|
-static LIST_HEAD(audit_freelist);
|
|
|
+static struct kmem_cache *audit_buffer_cache;
|
|
|
|
|
|
/* queue msgs to send via kauditd_task */
|
|
|
static struct sk_buff_head audit_queue;
|
|
@@ -191,17 +189,12 @@ DEFINE_MUTEX(audit_cmd_mutex);
|
|
|
* should be at least that large. */
|
|
|
#define AUDIT_BUFSIZ 1024
|
|
|
|
|
|
-/* AUDIT_MAXFREE is the number of empty audit_buffers we keep on the
|
|
|
- * audit_freelist. Doing so eliminates many kmalloc/kfree calls. */
|
|
|
-#define AUDIT_MAXFREE (2*NR_CPUS)
|
|
|
-
|
|
|
/* The audit_buffer is used when formatting an audit record. The caller
|
|
|
* locks briefly to get the record off the freelist or to allocate the
|
|
|
* buffer, and locks briefly to send the buffer to the netlink layer or
|
|
|
* to place it on a transmit queue. Multiple audit_buffers can be in
|
|
|
* use simultaneously. */
|
|
|
struct audit_buffer {
|
|
|
- struct list_head list;
|
|
|
struct sk_buff *skb; /* formatted skb ready to send */
|
|
|
struct audit_context *ctx; /* NULL or associated context */
|
|
|
gfp_t gfp_mask;
|
|
@@ -220,17 +213,41 @@ struct audit_reply {
|
|
|
* Description:
|
|
|
* Return 1 if the task is a registered audit daemon, 0 otherwise.
|
|
|
*/
|
|
|
-int auditd_test_task(const struct task_struct *task)
|
|
|
+int auditd_test_task(struct task_struct *task)
|
|
|
{
|
|
|
int rc;
|
|
|
+ struct auditd_connection *ac;
|
|
|
|
|
|
rcu_read_lock();
|
|
|
- rc = (auditd_conn.pid && task->tgid == auditd_conn.pid ? 1 : 0);
|
|
|
+ ac = rcu_dereference(auditd_conn);
|
|
|
+ rc = (ac && ac->pid == task_tgid(task) ? 1 : 0);
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * auditd_pid_vnr - Return the auditd PID relative to the namespace
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the PID in relation to the namespace, 0 on failure.
|
|
|
+ */
|
|
|
+static pid_t auditd_pid_vnr(void)
|
|
|
+{
|
|
|
+ pid_t pid;
|
|
|
+ const struct auditd_connection *ac;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ ac = rcu_dereference(auditd_conn);
|
|
|
+ if (!ac || !ac->pid)
|
|
|
+ pid = 0;
|
|
|
+ else
|
|
|
+ pid = pid_vnr(ac->pid);
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return pid;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* audit_get_sk - Return the audit socket for the given network namespace
|
|
|
* @net: the destination network namespace
|
|
@@ -250,14 +267,6 @@ static struct sock *audit_get_sk(const struct net *net)
|
|
|
return aunet->sk;
|
|
|
}
|
|
|
|
|
|
-static void audit_set_portid(struct audit_buffer *ab, __u32 portid)
|
|
|
-{
|
|
|
- if (ab) {
|
|
|
- struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
|
|
|
- nlh->nlmsg_pid = portid;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
void audit_panic(const char *message)
|
|
|
{
|
|
|
switch (audit_failure) {
|
|
@@ -426,6 +435,24 @@ static int audit_set_failure(u32 state)
|
|
|
return audit_do_config_change("audit_failure", &audit_failure, state);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * auditd_conn_free - RCU helper to release an auditd connection struct
|
|
|
+ * @rcu: RCU head
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Drop any references inside the auditd connection tracking struct and free
|
|
|
+ * the memory.
|
|
|
+ */
|
|
|
+ static void auditd_conn_free(struct rcu_head *rcu)
|
|
|
+ {
|
|
|
+ struct auditd_connection *ac;
|
|
|
+
|
|
|
+ ac = container_of(rcu, struct auditd_connection, rcu);
|
|
|
+ put_pid(ac->pid);
|
|
|
+ put_net(ac->net);
|
|
|
+ kfree(ac);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* auditd_set - Set/Reset the auditd connection state
|
|
|
* @pid: auditd PID
|
|
@@ -434,22 +461,33 @@ static int audit_set_failure(u32 state)
|
|
|
*
|
|
|
* Description:
|
|
|
* This function will obtain and drop network namespace references as
|
|
|
- * necessary.
|
|
|
+ * necessary. Returns zero on success, negative values on failure.
|
|
|
*/
|
|
|
-static void auditd_set(int pid, u32 portid, struct net *net)
|
|
|
+static int auditd_set(struct pid *pid, u32 portid, struct net *net)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
+ struct auditd_connection *ac_old, *ac_new;
|
|
|
|
|
|
- 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);
|
|
|
+ if (!pid || !net)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ ac_new = kzalloc(sizeof(*ac_new), GFP_KERNEL);
|
|
|
+ if (!ac_new)
|
|
|
+ return -ENOMEM;
|
|
|
+ ac_new->pid = get_pid(pid);
|
|
|
+ ac_new->portid = portid;
|
|
|
+ ac_new->net = get_net(net);
|
|
|
+
|
|
|
+ spin_lock_irqsave(&auditd_conn_lock, flags);
|
|
|
+ ac_old = rcu_dereference_protected(auditd_conn,
|
|
|
+ lockdep_is_held(&auditd_conn_lock));
|
|
|
+ rcu_assign_pointer(auditd_conn, ac_new);
|
|
|
+ spin_unlock_irqrestore(&auditd_conn_lock, flags);
|
|
|
+
|
|
|
+ if (ac_old)
|
|
|
+ call_rcu(&ac_old->rcu, auditd_conn_free);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -544,13 +582,19 @@ static void kauditd_retry_skb(struct sk_buff *skb)
|
|
|
*/
|
|
|
static void auditd_reset(void)
|
|
|
{
|
|
|
+ unsigned long flags;
|
|
|
struct sk_buff *skb;
|
|
|
+ struct auditd_connection *ac_old;
|
|
|
|
|
|
/* if it isn't already broken, break the connection */
|
|
|
- rcu_read_lock();
|
|
|
- if (auditd_conn.pid)
|
|
|
- auditd_set(0, 0, NULL);
|
|
|
- rcu_read_unlock();
|
|
|
+ spin_lock_irqsave(&auditd_conn_lock, flags);
|
|
|
+ ac_old = rcu_dereference_protected(auditd_conn,
|
|
|
+ lockdep_is_held(&auditd_conn_lock));
|
|
|
+ rcu_assign_pointer(auditd_conn, NULL);
|
|
|
+ spin_unlock_irqrestore(&auditd_conn_lock, flags);
|
|
|
+
|
|
|
+ if (ac_old)
|
|
|
+ call_rcu(&ac_old->rcu, auditd_conn_free);
|
|
|
|
|
|
/* flush all of the main and retry queues to the hold queue */
|
|
|
while ((skb = skb_dequeue(&audit_retry_queue)))
|
|
@@ -576,6 +620,7 @@ static int auditd_send_unicast_skb(struct sk_buff *skb)
|
|
|
u32 portid;
|
|
|
struct net *net;
|
|
|
struct sock *sk;
|
|
|
+ struct auditd_connection *ac;
|
|
|
|
|
|
/* NOTE: we can't call netlink_unicast while in the RCU section so
|
|
|
* take a reference to the network namespace and grab local
|
|
@@ -585,15 +630,15 @@ static int auditd_send_unicast_skb(struct sk_buff *skb)
|
|
|
* section netlink_unicast() should safely return an error */
|
|
|
|
|
|
rcu_read_lock();
|
|
|
- if (!auditd_conn.pid) {
|
|
|
+ ac = rcu_dereference(auditd_conn);
|
|
|
+ if (!ac) {
|
|
|
rcu_read_unlock();
|
|
|
rc = -ECONNREFUSED;
|
|
|
goto err;
|
|
|
}
|
|
|
- net = auditd_conn.net;
|
|
|
- get_net(net);
|
|
|
+ net = get_net(ac->net);
|
|
|
sk = audit_get_sk(net);
|
|
|
- portid = auditd_conn.portid;
|
|
|
+ portid = ac->portid;
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
rc = netlink_unicast(sk, skb, portid, 0);
|
|
@@ -728,6 +773,7 @@ static int kauditd_thread(void *dummy)
|
|
|
u32 portid = 0;
|
|
|
struct net *net = NULL;
|
|
|
struct sock *sk = NULL;
|
|
|
+ struct auditd_connection *ac;
|
|
|
|
|
|
#define UNICAST_RETRIES 5
|
|
|
|
|
@@ -735,14 +781,14 @@ static int kauditd_thread(void *dummy)
|
|
|
while (!kthread_should_stop()) {
|
|
|
/* NOTE: see the lock comments in auditd_send_unicast_skb() */
|
|
|
rcu_read_lock();
|
|
|
- if (!auditd_conn.pid) {
|
|
|
+ ac = rcu_dereference(auditd_conn);
|
|
|
+ if (!ac) {
|
|
|
rcu_read_unlock();
|
|
|
goto main_queue;
|
|
|
}
|
|
|
- net = auditd_conn.net;
|
|
|
- get_net(net);
|
|
|
+ net = get_net(ac->net);
|
|
|
sk = audit_get_sk(net);
|
|
|
- portid = auditd_conn.portid;
|
|
|
+ portid = ac->portid;
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
/* attempt to flush the hold queue */
|
|
@@ -816,7 +862,7 @@ int audit_send_list(void *_dest)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-struct sk_buff *audit_make_reply(__u32 portid, int seq, int type, int done,
|
|
|
+struct sk_buff *audit_make_reply(int seq, int type, int done,
|
|
|
int multi, const void *payload, int size)
|
|
|
{
|
|
|
struct sk_buff *skb;
|
|
@@ -829,7 +875,7 @@ struct sk_buff *audit_make_reply(__u32 portid, int seq, int type, int done,
|
|
|
if (!skb)
|
|
|
return NULL;
|
|
|
|
|
|
- nlh = nlmsg_put(skb, portid, seq, t, size, flags);
|
|
|
+ nlh = nlmsg_put(skb, 0, seq, t, size, flags);
|
|
|
if (!nlh)
|
|
|
goto out_kfree_skb;
|
|
|
data = nlmsg_data(nlh);
|
|
@@ -873,7 +919,6 @@ static int audit_send_reply_thread(void *arg)
|
|
|
static void audit_send_reply(struct sk_buff *request_skb, int seq, int type, int done,
|
|
|
int multi, const void *payload, int size)
|
|
|
{
|
|
|
- u32 portid = NETLINK_CB(request_skb).portid;
|
|
|
struct net *net = sock_net(NETLINK_CB(request_skb).sk);
|
|
|
struct sk_buff *skb;
|
|
|
struct task_struct *tsk;
|
|
@@ -883,12 +928,12 @@ static void audit_send_reply(struct sk_buff *request_skb, int seq, int type, int
|
|
|
if (!reply)
|
|
|
return;
|
|
|
|
|
|
- skb = audit_make_reply(portid, seq, type, done, multi, payload, size);
|
|
|
+ skb = audit_make_reply(seq, type, done, multi, payload, size);
|
|
|
if (!skb)
|
|
|
goto out;
|
|
|
|
|
|
reply->net = get_net(net);
|
|
|
- reply->portid = portid;
|
|
|
+ reply->portid = NETLINK_CB(request_skb).portid;
|
|
|
reply->skb = skb;
|
|
|
|
|
|
tsk = kthread_run(audit_send_reply_thread, reply, "audit_send_reply");
|
|
@@ -1068,11 +1113,13 @@ static int audit_set_feature(struct sk_buff *skb)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int audit_replace(pid_t pid)
|
|
|
+static int audit_replace(struct pid *pid)
|
|
|
{
|
|
|
+ pid_t pvnr;
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
- skb = audit_make_reply(0, 0, AUDIT_REPLACE, 0, 0, &pid, sizeof(pid));
|
|
|
+ pvnr = pid_vnr(pid);
|
|
|
+ skb = audit_make_reply(0, AUDIT_REPLACE, 0, 0, &pvnr, sizeof(pvnr));
|
|
|
if (!skb)
|
|
|
return -ENOMEM;
|
|
|
return auditd_send_unicast_skb(skb);
|
|
@@ -1102,9 +1149,9 @@ 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();
|
|
|
+ /* NOTE: use pid_vnr() so the PID is relative to the current
|
|
|
+ * namespace */
|
|
|
+ s.pid = auditd_pid_vnr();
|
|
|
s.rate_limit = audit_rate_limit;
|
|
|
s.backlog_limit = audit_backlog_limit;
|
|
|
s.lost = atomic_read(&audit_lost);
|
|
@@ -1130,51 +1177,61 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
|
return err;
|
|
|
}
|
|
|
if (s.mask & AUDIT_STATUS_PID) {
|
|
|
- /* NOTE: we are using task_tgid_vnr() below because
|
|
|
- * the s.pid value is relative to the namespace
|
|
|
- * of the caller; at present this doesn't matter
|
|
|
- * much since you can really only run auditd
|
|
|
- * from the initial pid namespace, but something
|
|
|
- * to keep in mind if this changes */
|
|
|
- int new_pid = s.pid;
|
|
|
+ /* NOTE: we are using the vnr PID functions below
|
|
|
+ * because the s.pid value is relative to the
|
|
|
+ * namespace of the caller; at present this
|
|
|
+ * doesn't matter much since you can really only
|
|
|
+ * run auditd from the initial pid namespace, but
|
|
|
+ * something to keep in mind if this changes */
|
|
|
+ pid_t new_pid = s.pid;
|
|
|
pid_t auditd_pid;
|
|
|
- pid_t requesting_pid = task_tgid_vnr(current);
|
|
|
+ struct pid *req_pid = task_tgid(current);
|
|
|
+
|
|
|
+ /* sanity check - PID values must match */
|
|
|
+ if (new_pid != pid_vnr(req_pid))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
/* test the auditd connection */
|
|
|
- audit_replace(requesting_pid);
|
|
|
+ audit_replace(req_pid);
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
- auditd_pid = auditd_conn.pid;
|
|
|
+ auditd_pid = auditd_pid_vnr();
|
|
|
/* only the current auditd can unregister itself */
|
|
|
- if ((!new_pid) && (requesting_pid != auditd_pid)) {
|
|
|
- rcu_read_unlock();
|
|
|
+ if ((!new_pid) && (new_pid != auditd_pid)) {
|
|
|
audit_log_config_change("audit_pid", new_pid,
|
|
|
auditd_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);
|
|
|
return -EEXIST;
|
|
|
}
|
|
|
- rcu_read_unlock();
|
|
|
-
|
|
|
- if (audit_enabled != AUDIT_OFF)
|
|
|
- audit_log_config_change("audit_pid", new_pid,
|
|
|
- auditd_pid, 1);
|
|
|
|
|
|
if (new_pid) {
|
|
|
/* register a new auditd connection */
|
|
|
- auditd_set(new_pid,
|
|
|
- NETLINK_CB(skb).portid,
|
|
|
- sock_net(NETLINK_CB(skb).sk));
|
|
|
+ err = auditd_set(req_pid,
|
|
|
+ NETLINK_CB(skb).portid,
|
|
|
+ sock_net(NETLINK_CB(skb).sk));
|
|
|
+ if (audit_enabled != AUDIT_OFF)
|
|
|
+ audit_log_config_change("audit_pid",
|
|
|
+ new_pid,
|
|
|
+ auditd_pid,
|
|
|
+ err ? 0 : 1);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
/* try to process any backlog */
|
|
|
wake_up_interruptible(&kauditd_wait);
|
|
|
- } else
|
|
|
+ } else {
|
|
|
+ if (audit_enabled != AUDIT_OFF)
|
|
|
+ audit_log_config_change("audit_pid",
|
|
|
+ new_pid,
|
|
|
+ auditd_pid, 1);
|
|
|
+
|
|
|
/* unregister the auditd connection */
|
|
|
auditd_reset();
|
|
|
+ }
|
|
|
}
|
|
|
if (s.mask & AUDIT_STATUS_RATE_LIMIT) {
|
|
|
err = audit_set_rate_limit(s.rate_limit);
|
|
@@ -1242,7 +1299,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
|
size--;
|
|
|
audit_log_n_untrustedstring(ab, data, size);
|
|
|
}
|
|
|
- audit_set_portid(ab, NETLINK_CB(skb).portid);
|
|
|
audit_log_end(ab);
|
|
|
}
|
|
|
break;
|
|
@@ -1256,8 +1312,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
|
audit_log_end(ab);
|
|
|
return -EPERM;
|
|
|
}
|
|
|
- err = audit_rule_change(msg_type, NETLINK_CB(skb).portid,
|
|
|
- seq, data, nlmsg_len(nlh));
|
|
|
+ err = audit_rule_change(msg_type, seq, data, nlmsg_len(nlh));
|
|
|
break;
|
|
|
case AUDIT_LIST_RULES:
|
|
|
err = audit_list_rules_send(skb, seq);
|
|
@@ -1378,11 +1433,14 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
|
return err < 0 ? err : 0;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Get message from skb. Each message is processed by audit_receive_msg.
|
|
|
- * Malformed skbs with wrong length are discarded silently.
|
|
|
+/**
|
|
|
+ * audit_receive - receive messages from a netlink control socket
|
|
|
+ * @skb: the message buffer
|
|
|
+ *
|
|
|
+ * Parse the provided skb and deal with any messages that may be present,
|
|
|
+ * malformed skbs are discarded.
|
|
|
*/
|
|
|
-static void audit_receive_skb(struct sk_buff *skb)
|
|
|
+static void audit_receive(struct sk_buff *skb)
|
|
|
{
|
|
|
struct nlmsghdr *nlh;
|
|
|
/*
|
|
@@ -1395,6 +1453,7 @@ static void audit_receive_skb(struct sk_buff *skb)
|
|
|
nlh = nlmsg_hdr(skb);
|
|
|
len = skb->len;
|
|
|
|
|
|
+ mutex_lock(&audit_cmd_mutex);
|
|
|
while (nlmsg_ok(nlh, len)) {
|
|
|
err = audit_receive_msg(skb, nlh);
|
|
|
/* if err or if this message says it wants a response */
|
|
@@ -1403,13 +1462,6 @@ static void audit_receive_skb(struct sk_buff *skb)
|
|
|
|
|
|
nlh = nlmsg_next(nlh, &len);
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/* Receive messages from netlink socket. */
|
|
|
-static void audit_receive(struct sk_buff *skb)
|
|
|
-{
|
|
|
- mutex_lock(&audit_cmd_mutex);
|
|
|
- audit_receive_skb(skb);
|
|
|
mutex_unlock(&audit_cmd_mutex);
|
|
|
}
|
|
|
|
|
@@ -1447,10 +1499,11 @@ 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)
|
|
|
- auditd_reset();
|
|
|
- rcu_read_unlock();
|
|
|
+ /* NOTE: you would think that we would want to check the auditd
|
|
|
+ * connection and potentially reset it here if it lives in this
|
|
|
+ * namespace, but since the auditd connection tracking struct holds a
|
|
|
+ * reference to this namespace (see auditd_set()) we are only ever
|
|
|
+ * going to get here after that connection has been released */
|
|
|
|
|
|
netlink_kernel_release(aunet->sk);
|
|
|
}
|
|
@@ -1470,8 +1523,9 @@ 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);
|
|
|
+ audit_buffer_cache = kmem_cache_create("audit_buffer",
|
|
|
+ sizeof(struct audit_buffer),
|
|
|
+ 0, SLAB_PANIC, NULL);
|
|
|
|
|
|
skb_queue_head_init(&audit_queue);
|
|
|
skb_queue_head_init(&audit_retry_queue);
|
|
@@ -1538,60 +1592,33 @@ __setup("audit_backlog_limit=", audit_backlog_limit_set);
|
|
|
|
|
|
static void audit_buffer_free(struct audit_buffer *ab)
|
|
|
{
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
if (!ab)
|
|
|
return;
|
|
|
|
|
|
kfree_skb(ab->skb);
|
|
|
- spin_lock_irqsave(&audit_freelist_lock, flags);
|
|
|
- if (audit_freelist_count > AUDIT_MAXFREE)
|
|
|
- kfree(ab);
|
|
|
- else {
|
|
|
- audit_freelist_count++;
|
|
|
- list_add(&ab->list, &audit_freelist);
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(&audit_freelist_lock, flags);
|
|
|
+ kmem_cache_free(audit_buffer_cache, ab);
|
|
|
}
|
|
|
|
|
|
-static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
|
|
|
- gfp_t gfp_mask, int type)
|
|
|
+static struct audit_buffer *audit_buffer_alloc(struct audit_context *ctx,
|
|
|
+ gfp_t gfp_mask, int type)
|
|
|
{
|
|
|
- unsigned long flags;
|
|
|
- struct audit_buffer *ab = NULL;
|
|
|
- struct nlmsghdr *nlh;
|
|
|
-
|
|
|
- spin_lock_irqsave(&audit_freelist_lock, flags);
|
|
|
- if (!list_empty(&audit_freelist)) {
|
|
|
- ab = list_entry(audit_freelist.next,
|
|
|
- struct audit_buffer, list);
|
|
|
- list_del(&ab->list);
|
|
|
- --audit_freelist_count;
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(&audit_freelist_lock, flags);
|
|
|
-
|
|
|
- if (!ab) {
|
|
|
- ab = kmalloc(sizeof(*ab), gfp_mask);
|
|
|
- if (!ab)
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ struct audit_buffer *ab;
|
|
|
|
|
|
- ab->ctx = ctx;
|
|
|
- ab->gfp_mask = gfp_mask;
|
|
|
+ ab = kmem_cache_alloc(audit_buffer_cache, gfp_mask);
|
|
|
+ if (!ab)
|
|
|
+ return NULL;
|
|
|
|
|
|
ab->skb = nlmsg_new(AUDIT_BUFSIZ, gfp_mask);
|
|
|
if (!ab->skb)
|
|
|
goto err;
|
|
|
+ if (!nlmsg_put(ab->skb, 0, 0, type, 0, 0))
|
|
|
+ goto err;
|
|
|
|
|
|
- nlh = nlmsg_put(ab->skb, 0, 0, type, 0, 0);
|
|
|
- if (!nlh)
|
|
|
- goto out_kfree_skb;
|
|
|
+ ab->ctx = ctx;
|
|
|
+ ab->gfp_mask = gfp_mask;
|
|
|
|
|
|
return ab;
|
|
|
|
|
|
-out_kfree_skb:
|
|
|
- kfree_skb(ab->skb);
|
|
|
- ab->skb = NULL;
|
|
|
err:
|
|
|
audit_buffer_free(ab);
|
|
|
return NULL;
|
|
@@ -1622,10 +1649,10 @@ unsigned int audit_serial(void)
|
|
|
}
|
|
|
|
|
|
static inline void audit_get_stamp(struct audit_context *ctx,
|
|
|
- struct timespec *t, unsigned int *serial)
|
|
|
+ struct timespec64 *t, unsigned int *serial)
|
|
|
{
|
|
|
if (!ctx || !auditsc_get_stamp(ctx, t, serial)) {
|
|
|
- *t = CURRENT_TIME;
|
|
|
+ ktime_get_real_ts64(t);
|
|
|
*serial = audit_serial();
|
|
|
}
|
|
|
}
|
|
@@ -1649,7 +1676,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
|
|
|
int type)
|
|
|
{
|
|
|
struct audit_buffer *ab;
|
|
|
- struct timespec t;
|
|
|
+ struct timespec64 t;
|
|
|
unsigned int uninitialized_var(serial);
|
|
|
|
|
|
if (audit_initialized != AUDIT_INITIALIZED)
|
|
@@ -1702,8 +1729,8 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
|
|
|
}
|
|
|
|
|
|
audit_get_stamp(ab->ctx, &t, &serial);
|
|
|
- audit_log_format(ab, "audit(%lu.%03lu:%u): ",
|
|
|
- t.tv_sec, t.tv_nsec/1000000, serial);
|
|
|
+ audit_log_format(ab, "audit(%llu.%03lu:%u): ",
|
|
|
+ (unsigned long long)t.tv_sec, t.tv_nsec/1000000, serial);
|
|
|
|
|
|
return ab;
|
|
|
}
|