/* Obtain an audit buffer. This routine does locking to obtain the * audit buffer, but then no locking is required for calls to * audit_log_*format. If the tsk is a task that is currently in a * syscall, then the syscall is marked as auditable and an audit record * will be written at syscall exit. If there is no associated task, tsk * should be NULL. */ struct audit_buffer *audit_log_start(struct audit_context *ctx) { struct audit_buffer *ab = NULL; unsigned long flags; struct timespec t; int serial = 0; if (!audit_initialized) return NULL; if (audit_backlog_limit && atomic_read(&audit_backlog) > audit_backlog_limit) { if (audit_rate_check()) printk(KERN_WARNING "audit: audit_backlog=%d > " "audit_backlog_limit=%d\n", atomic_read(&audit_backlog), audit_backlog_limit); audit_log_lost("backlog limit exceeded"); return NULL; } 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; }
/* Move data from tmp buffer into an skb. This is an extra copy, and * that is unfortunate. However, the copy will only occur when a record * is being written to user space, which is already a high-overhead * operation. (Elimination of the copy is possible, for example, by * writing directly into a pre-allocated skb, at the cost of wasting * memory. */ static void audit_log_move(struct audit_buffer *ab) { struct sk_buff *skb; char *start; int extra = ab->nlh ? 0 : NLMSG_SPACE(0); /* possible resubmission */ if (ab->len == 0) return; skb = skb_peek(&ab->sklist); if (!skb || skb_tailroom(skb) <= ab->len + extra) { skb = alloc_skb(2 * ab->len + extra, GFP_ATOMIC); if (!skb) { ab->len = 0; /* Lose information in ab->tmp */ audit_log_lost("out of memory in audit_log_move"); return; } __skb_queue_tail(&ab->sklist, skb); if (!ab->nlh) ab->nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(0)); } start = skb_put(skb, ab->len); memcpy(start, ab->tmp, ab->len); ab->len = 0; }
/* Iterate over the skbuff in the audit_buffer, sending their contents * to user space. */ static inline int audit_log_drain(struct audit_buffer *ab) { struct sk_buff *skb; while ((skb = skb_dequeue(&ab->sklist))) { int retval = 0; if (audit_pid) { if (ab->nlh) { ab->nlh->nlmsg_len = ab->total; ab->nlh->nlmsg_type = ab->type; ab->nlh->nlmsg_flags = 0; ab->nlh->nlmsg_seq = 0; ab->nlh->nlmsg_pid = ab->pid; } skb_get(skb); /* because netlink_* frees */ retval = netlink_unicast(audit_sock, skb, audit_pid, MSG_DONTWAIT); } if (retval == -EAGAIN && ab->count < 5) { ++ab->count; skb_queue_tail(&ab->sklist, skb); audit_log_end_irq(ab); return 1; } if (retval < 0) { if (retval == -ECONNREFUSED) { printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid); audit_pid = 0; } else audit_log_lost("netlink socket too busy"); } if (!audit_pid) { /* No daemon */ int offset = ab->nlh ? NLMSG_SPACE(0) : 0; int len = skb->len - offset; printk(KERN_ERR "%*.*s\n", len, len, skb->data + offset); } kfree_skb(skb); ab->nlh = NULL; } return 0; }
/** * tty_audit_buf_get - Get an audit buffer. * * Get an audit buffer for @tty, allocate it if necessary. Return %NULL * if TTY auditing is disabled or out of memory. Otherwise, return a new * reference to the buffer. */ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, unsigned icanon) { struct tty_audit_buf *buf, *buf2; unsigned long flags; buf = NULL; buf2 = NULL; spin_lock_irqsave(¤t->sighand->siglock, flags); if (likely(!current->signal->audit_tty)) goto out; buf = current->signal->tty_audit_buf; if (buf) { atomic_inc(&buf->count); goto out; } spin_unlock_irqrestore(¤t->sighand->siglock, flags); buf2 = tty_audit_buf_alloc(tty->driver->major, tty->driver->minor_start + tty->index, icanon); if (buf2 == NULL) { audit_log_lost("out of memory in TTY auditing"); return NULL; } spin_lock_irqsave(¤t->sighand->siglock, flags); if (!current->signal->audit_tty) goto out; buf = current->signal->tty_audit_buf; if (!buf) { current->signal->tty_audit_buf = buf2; buf = buf2; buf2 = NULL; } atomic_inc(&buf->count); /* Fall through */ out: spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (buf2) tty_audit_buf_free(buf2); return buf; }