/* The identity of the user shutting down the audit system. */
static kuid_t audit_sig_uid = INVALID_UID;
static pid_t audit_sig_pid = -1;
-static u32 audit_sig_sid;
+struct lsmblob audit_sig_lsm;
/* Records can be lost in several ways:
0) [suppressed in audit_alloc]
return rc;
audit_log_format(ab, "op=set %s=%u old=%u ", function_name, new, old);
audit_log_session_info(ab);
- rc = audit_log_task_context(ab);
+ rc = audit_log_task_context(ab, NULL);
if (rc)
allow_changes = 0; /* Something weird, deny request */
audit_log_format(ab, " res=%d", allow_changes);
+ audit_log_lsm(NULL, false);
audit_log_end(ab);
return rc;
}
/**
* kauditd_rehold_skb - Handle a audit record send failure in the hold queue
* @skb: audit record
+ * @error: error code (unused)
*
* 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)
+static void kauditd_rehold_skb(struct sk_buff *skb, __always_unused int error)
{
- /* put the record back in the queue at the same place */
- skb_queue_head(&audit_hold_queue, skb);
+ /* put the record back in the queue */
+ skb_queue_tail(&audit_hold_queue, skb);
}
/**
* kauditd_hold_skb - Queue an audit record, waiting for auditd
* @skb: audit record
+ * @error: error code
*
* Description:
* Queue the audit record, waiting for an instance of auditd. When this
* and queue it, if we have room. If we want to hold on to the record, but we
* don't have room, record a record lost message.
*/
-static void kauditd_hold_skb(struct sk_buff *skb)
+static void kauditd_hold_skb(struct sk_buff *skb, int error)
{
/* at this point it is uncertain if we will ever send this to auditd so
* try to send the message via printk before we go any further */
kauditd_printk_skb(skb);
/* can we just silently drop the message? */
- if (!audit_default) {
- kfree_skb(skb);
- return;
+ if (!audit_default)
+ goto drop;
+
+ /* the hold queue is only for when the daemon goes away completely,
+ * not -EAGAIN failures; if we are in a -EAGAIN state requeue the
+ * record on the retry queue unless it's full, in which case drop it
+ */
+ if (error == -EAGAIN) {
+ if (!audit_backlog_limit ||
+ skb_queue_len(&audit_retry_queue) < audit_backlog_limit) {
+ skb_queue_tail(&audit_retry_queue, skb);
+ return;
+ }
+ audit_log_lost("kauditd retry queue overflow");
+ goto drop;
}
- /* if we have room, queue the message */
+ /* if we have room in the hold queue, queue the message */
if (!audit_backlog_limit ||
skb_queue_len(&audit_hold_queue) < audit_backlog_limit) {
skb_queue_tail(&audit_hold_queue, skb);
/* we have no other options - drop the message */
audit_log_lost("kauditd hold queue overflow");
+drop:
kfree_skb(skb);
}
/**
* kauditd_retry_skb - Queue an audit record, attempt to send again to auditd
* @skb: audit record
+ * @error: error code (unused)
*
* Description:
* Not as serious as kauditd_hold_skb() as we still have a connected auditd,
* but for some reason we are having problems sending it audit records so
* queue the given record and attempt to resend.
*/
-static void kauditd_retry_skb(struct sk_buff *skb)
+static void kauditd_retry_skb(struct sk_buff *skb, __always_unused int error)
{
- /* NOTE: because records should only live in the retry queue for a
- * short period of time, before either being sent or moved to the hold
- * queue, we don't currently enforce a limit on this queue */
- skb_queue_tail(&audit_retry_queue, skb);
+ if (!audit_backlog_limit ||
+ skb_queue_len(&audit_retry_queue) < audit_backlog_limit) {
+ skb_queue_tail(&audit_retry_queue, skb);
+ return;
+ }
+
+ /* we have to drop the record, send it via printk as a last effort */
+ kauditd_printk_skb(skb);
+ audit_log_lost("kauditd retry queue overflow");
+ kfree_skb(skb);
}
/**
/* flush the retry queue to the hold queue, but don't touch the main
* queue since we need to process that normally for multicast */
while ((skb = skb_dequeue(&audit_retry_queue)))
- kauditd_hold_skb(skb);
+ kauditd_hold_skb(skb, -ECONNREFUSED);
}
/**
struct sk_buff_head *queue,
unsigned int retry_limit,
void (*skb_hook)(struct sk_buff *skb),
- void (*err_hook)(struct sk_buff *skb))
+ void (*err_hook)(struct sk_buff *skb, int error))
{
int rc = 0;
- struct sk_buff *skb;
- static unsigned int failed = 0;
+ struct sk_buff *skb = NULL;
+ struct sk_buff *skb_tail;
+ 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))) {
+ skb_tail = skb_peek_tail(queue);
+ while ((skb != skb_tail) && (skb = skb_dequeue(queue))) {
/* call the skb_hook for each skb we touch */
if (skb_hook)
(*skb_hook)(skb);
/* can we send to anyone via unicast? */
if (!sk) {
if (err_hook)
- (*err_hook)(skb);
+ (*err_hook)(skb, -ECONNREFUSED);
continue;
}
+retry:
/* 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? */
+ /* send failed - try a few times unless fatal error */
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 */
+ (*err_hook)(skb, rc);
+ if (rc == -EAGAIN)
+ rc = 0;
+ /* continue to drain the queue */
continue;
} else
- /* no - requeue to preserve ordering */
- skb_queue_head(queue, skb);
+ goto retry;
} else {
- /* it worked - drop the extra reference and continue */
+ /* skb sent - drop the extra reference and continue */
consume_skb(skb);
failed = 0;
}
}
-out:
return (rc >= 0 ? 0 : rc);
}
return;
audit_log_format(*ab, "pid=%d uid=%u ", pid, uid);
audit_log_session_info(*ab);
- audit_log_task_context(*ab);
+ audit_log_task_context(*ab, NULL);
}
static inline void audit_log_user_recv_msg(struct audit_buffer **ab,
u16 msg_type)
{
- audit_log_common_recv_msg(NULL, ab, msg_type);
+ struct audit_context *context;
+
+ if (!lsm_multiple_contexts()) {
+ audit_log_common_recv_msg(NULL, ab, msg_type);
+ return;
+ }
+
+ context = audit_context();
+ if (context) {
+ if (!context->in_syscall)
+ audit_stamp_context(context);
+ audit_log_common_recv_msg(context, ab, msg_type);
+ return;
+ }
+
+ audit_alloc(current);
+ context = audit_context();
+
+ audit_log_common_recv_msg(context, ab, msg_type);
}
int is_audit_feature_set(int i)
struct audit_buffer *ab;
u16 msg_type = nlh->nlmsg_type;
struct audit_sig_info *sig_data;
- char *ctx = NULL;
- u32 len;
err = audit_netlink_ok(skb, msg_type);
if (err)
audit_log_n_untrustedstring(ab, str, data_len);
}
audit_log_end(ab);
+ audit_log_lsm(NULL, false);
}
break;
case AUDIT_ADD_RULE:
kfree(new);
break;
}
- case AUDIT_SIGNAL_INFO:
- len = 0;
- if (audit_sig_sid) {
- err = security_secid_to_secctx(audit_sig_sid, &ctx, &len);
+ case AUDIT_SIGNAL_INFO: {
+ struct lsmcontext context = { };
+ int len = 0;
+
+ if (lsmblob_is_set(&audit_sig_lsm)) {
+ err = security_secid_to_secctx(&audit_sig_lsm,
+ &context, LSMBLOB_FIRST);
if (err)
return err;
}
- sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL);
+ sig_data = kmalloc(sizeof(*sig_data) + context.len, GFP_KERNEL);
if (!sig_data) {
- if (audit_sig_sid)
- security_release_secctx(ctx, len);
+ if (lsmblob_is_set(&audit_sig_lsm))
+ security_release_secctx(&context);
return -ENOMEM;
}
sig_data->uid = from_kuid(&init_user_ns, audit_sig_uid);
sig_data->pid = audit_sig_pid;
- if (audit_sig_sid) {
- memcpy(sig_data->ctx, ctx, len);
- security_release_secctx(ctx, len);
+ if (lsmblob_is_set(&audit_sig_lsm)) {
+ len = context.len;
+ memcpy(sig_data->ctx, context.context, len);
+ security_release_secctx(&context);
}
audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO, 0, 0,
sig_data, sizeof(*sig_data) + len);
kfree(sig_data);
break;
+ }
case AUDIT_TTY_GET: {
struct audit_tty_status s;
unsigned int t;
nlh = nlmsg_next(nlh, &len);
}
audit_ctl_unlock();
+
+ /* can't block with the ctrl lock, so penalize the sender now */
+ if (audit_backlog_limit &&
+ (skb_queue_len(&audit_queue) > audit_backlog_limit)) {
+ DECLARE_WAITQUEUE(wait, current);
+
+ /* wake kauditd to try and flush the queue */
+ wake_up_interruptible(&kauditd_wait);
+
+ add_wait_queue_exclusive(&audit_backlog_wait, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(audit_backlog_wait_time);
+ remove_wait_queue(&audit_backlog_wait, &wait);
+ }
}
/* Log information about who is connecting to the audit multicast socket */
tty ? tty_name(tty) : "(none)",
audit_get_sessionid(current));
audit_put_tty(tty);
- audit_log_task_context(ab); /* subj= */
+ audit_log_task_context(ab, NULL); /* subj= */
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, get_task_comm(comm, current));
audit_log_d_path_exe(ab, current->mm); /* exe= */
audit_panic("cannot initialize netlink socket in namespace");
return -ENOMEM;
}
- aunet->sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
+ /* limit the timeout in case auditd is blocked/stopped */
+ aunet->sk->sk_sndtimeo = HZ / 10;
return 0;
}
* 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 */
+ * while holding the mutex, although we do penalize the sender
+ * later in audit_receive() when it is safe to block
+ */
if (!(auditd_test_task(current) || audit_ctl_owner_current())) {
long stime = audit_backlog_wait_time;
/* cancel dummy context to enable supporting records */
if (ctx)
ctx->dummy = 0;
+ if (type == AUDIT_MAC_TASK_CONTEXTS && ab->ctx &&
+ ab->ctx->serial == 0) {
+ audit_stamp_context(ab->ctx);
+ audit_get_stamp(ab->ctx, &t, &serial);
+ }
audit_log_format(ab, "audit(%llu.%03lu:%u): ",
(unsigned long long)t.tv_sec, t.tv_nsec/1000000, serial);
audit_log_format(ab, "(null)");
}
-int audit_log_task_context(struct audit_buffer *ab)
+int audit_log_task_context(struct audit_buffer *ab, struct lsmblob *blob)
{
- char *ctx = NULL;
- unsigned len;
+ int i;
int error;
- u32 sid;
+ struct lsmblob localblob;
+ struct lsmcontext lsmdata;
- security_task_getsecid_subj(current, &sid);
- if (!sid)
+ /*
+ * If there is more than one security module that has a
+ * subject "context" it's necessary to put the subject data
+ * into a separate record to maintain compatibility.
+ */
+ if (lsm_multiple_contexts()) {
+ audit_log_format(ab, " subj=?");
return 0;
+ }
- error = security_secid_to_secctx(sid, &ctx, &len);
- if (error) {
- if (error != -EINVAL)
- goto error_path;
- return 0;
+ if (blob == NULL) {
+ security_task_getsecid_subj(current, &localblob);
+ if (!lsmblob_is_set(&localblob)) {
+ audit_log_format(ab, " subj=?");
+ return 0;
+ }
+ blob = &localblob;
}
- audit_log_format(ab, " subj=%s", ctx);
- security_release_secctx(ctx, len);
- return 0;
+ for (i = 0; i < LSMBLOB_ENTRIES; i++) {
+ if (blob->secid[i] == 0)
+ continue;
+ error = security_secid_to_secctx(blob, &lsmdata, i);
+ if (error && error != -EINVAL) {
+ audit_panic("error in audit_log_task_context");
+ return error;
+ }
+
+ audit_log_format(ab, " subj=%s", lsmdata.context);
+ security_release_secctx(&lsmdata);
+ break;
+ }
-error_path:
- audit_panic("error in audit_log_task_context");
- return error;
+ return 0;
}
EXPORT_SYMBOL(audit_log_task_context);
+int audit_log_object_context(struct audit_buffer *ab,
+ struct lsmblob *blob)
+{
+ int i;
+ int error;
+ bool sep = false;
+ struct lsmcontext lsmdata;
+ struct audit_buffer *lsmab = NULL;
+ struct audit_context *context = NULL;
+
+ /*
+ * If there is more than one security module that has a
+ * object "context" it's necessary to put the object data
+ * into a separate record to maintain compatibility.
+ */
+ if (lsm_multiple_contexts()) {
+ audit_log_format(ab, " obj=?");
+ context = ab->ctx;
+ if (context)
+ lsmab = audit_log_start(context, GFP_KERNEL,
+ AUDIT_MAC_OBJ_CONTEXTS);
+ }
+
+ for (i = 0; i < LSMBLOB_ENTRIES; i++) {
+ if (blob->secid[i] == 0)
+ continue;
+ error = security_secid_to_secctx(blob, &lsmdata, i);
+ if (error && error != -EINVAL) {
+ audit_panic("error in audit_log_object_context");
+ return error;
+ }
+
+ if (context) {
+ audit_log_format(lsmab, "%sobj_%s=%s",
+ sep ? " " : "",
+ security_lsm_slot_name(i),
+ lsmdata.context);
+ sep = true;
+ } else
+ audit_log_format(ab, " obj=%s", lsmdata.context);
+
+ security_release_secctx(&lsmdata);
+ if (!context)
+ break;
+ }
+
+ if (context)
+ audit_log_end(lsmab);
+
+ return 0;
+}
+EXPORT_SYMBOL(audit_log_object_context);
+
void audit_log_d_path_exe(struct audit_buffer *ab,
struct mm_struct *mm)
{
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, get_task_comm(comm, current));
audit_log_d_path_exe(ab, current->mm);
- audit_log_task_context(ab);
+ audit_log_task_context(ab, NULL);
}
EXPORT_SYMBOL(audit_log_task_info);
if (!audit_enabled)
return;
+ audit_stamp_context(audit_context());
ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_LOGIN);
if (!ab)
return;
tty = audit_get_tty();
audit_log_format(ab, "pid=%d uid=%u", task_tgid_nr(current), uid);
- audit_log_task_context(ab);
+ audit_log_task_context(ab, NULL);
audit_log_format(ab, " old-auid=%u auid=%u tty=%s old-ses=%u ses=%u res=%d",
oldloginuid, loginuid, tty ? tty_name(tty) : "(none)",
oldsessionid, sessionid, !rc);
audit_put_tty(tty);
+ audit_log_lsm(NULL, true);
audit_log_end(ab);
}
audit_sig_uid = auid;
else
audit_sig_uid = uid;
- security_task_getsecid_subj(current, &audit_sig_sid);
+ security_task_getsecid_subj(current, &audit_sig_lsm);
}
return audit_signal_info_syscall(t);