audit: add netlink multicast group for log read

Add a netlink multicast socket with one group to kaudit for "best-effort"
delivery to read-only userspace clients such as systemd, in addition to the
existing bidirectional unicast auditd userspace client.

Currently, auditd is intended to use the CAP_AUDIT_CONTROL and CAP_AUDIT_WRITE
capabilities, but actually uses CAP_NET_ADMIN.  The CAP_AUDIT_READ capability
is added for use by read-only AUDIT_NLGRP_READLOG netlink multicast group
clients to the kaudit subsystem.

This will safely give access to services such as systemd to consume audit logs
while ensuring write access remains restricted for integrity.

Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Richard Guy Briggs 2014-04-22 21:31:57 -04:00 committed by David S. Miller
parent 3a101b8de0
commit 451f921639
2 changed files with 55 additions and 4 deletions

View file

@ -373,6 +373,14 @@ enum {
*/ */
#define AUDIT_MESSAGE_TEXT_MAX 8560 #define AUDIT_MESSAGE_TEXT_MAX 8560
/* Multicast Netlink socket groups (default up to 32) */
enum audit_nlgrps {
AUDIT_NLGRP_NONE, /* Group 0 not used */
AUDIT_NLGRP_READLOG, /* "best effort" read only socket */
__AUDIT_NLGRP_MAX
};
#define AUDIT_NLGRP_MAX (__AUDIT_NLGRP_MAX - 1)
struct audit_status { struct audit_status {
__u32 mask; /* Bit mask for valid entries */ __u32 mask; /* Bit mask for valid entries */
__u32 enabled; /* 1 = enabled, 0 = disabled */ __u32 enabled; /* 1 = enabled, 0 = disabled */

View file

@ -423,6 +423,35 @@ static void kauditd_send_skb(struct sk_buff *skb)
consume_skb(skb); consume_skb(skb);
} }
/*
* kauditd_send_multicast_skb - send the skb to multicast userspace listeners
*
* 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 audit_net *aunet = net_generic(&init_net, audit_net_id);
struct sock *sock = aunet->nlsk;
/*
* The seemingly wasteful skb_copy() rather than bumping the refcount
* using skb_get() is necessary because non-standard mods are made to
* the skb by the original kaudit unicast socket send routine. The
* existing auditd daemon assumes this breakage. Fixing this would
* require co-ordinating a change in the established protocol between
* the kaudit kernel subsystem and the auditd userspace code. There is
* no reason for new multicast clients to continue with this
* non-compliance.
*/
copy = skb_copy(skb, GFP_KERNEL);
if (!copy)
return;
nlmsg_multicast(sock, copy, 0, AUDIT_NLGRP_READLOG, GFP_KERNEL);
}
/* /*
* flush_hold_queue - empty the hold queue if auditd appears * flush_hold_queue - empty the hold queue if auditd appears
* *
@ -1090,6 +1119,8 @@ static int __net_init audit_net_init(struct net *net)
struct netlink_kernel_cfg cfg = { struct netlink_kernel_cfg cfg = {
.input = audit_receive, .input = audit_receive,
.bind = audit_bind, .bind = audit_bind,
.flags = NL_CFG_F_NONROOT_RECV,
.groups = AUDIT_NLGRP_MAX,
}; };
struct audit_net *aunet = net_generic(net, audit_net_id); struct audit_net *aunet = net_generic(net, audit_net_id);
@ -1911,10 +1942,10 @@ void audit_log_link_denied(const char *operation, struct path *link)
* audit_log_end - end one audit record * audit_log_end - end one audit record
* @ab: the audit_buffer * @ab: the audit_buffer
* *
* The netlink_* functions cannot be called inside an irq context, so * netlink_unicast() cannot be called inside an irq context because it blocks
* the audit buffer is placed on a queue and a tasklet is scheduled to * (last arg, flags, is not set to MSG_DONTWAIT), so the audit buffer is placed
* remove them from the queue outside the irq context. May be called in * on a queue and a tasklet is scheduled to remove them from the queue outside
* any context. * the irq context. May be called in any context.
*/ */
void audit_log_end(struct audit_buffer *ab) void audit_log_end(struct audit_buffer *ab)
{ {
@ -1924,6 +1955,18 @@ void audit_log_end(struct audit_buffer *ab)
audit_log_lost("rate limit exceeded"); audit_log_lost("rate limit exceeded");
} else { } else {
struct nlmsghdr *nlh = nlmsg_hdr(ab->skb); struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
kauditd_send_multicast_skb(ab->skb);
/*
* The original kaudit unicast socket sends up messages with
* nlmsg_len set to the payload length rather than the entire
* message length. This breaks the standard set by netlink.
* The existing auditd daemon assumes this breakage. Fixing
* this would require co-ordinating a change in the established
* protocol between the kaudit kernel subsystem and the auditd
* userspace code.
*/
nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN; nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN;
if (audit_pid) { if (audit_pid) {