]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - kernel/audit.c
audit: create private file name copies when auditing inodes
[karo-tx-linux.git] / kernel / audit.c
index f30106459a3243945d75038d8c069b06906efa13..c7e097a0d7af935af52502cd0c8a63886256e1d1 100644 (file)
@@ -126,7 +126,7 @@ static atomic_t    audit_lost = ATOMIC_INIT(0);
 
 /* The netlink socket. */
 static struct sock *audit_sock;
-int audit_net_id;
+static int audit_net_id;
 
 /* Hash for inode-based rules */
 struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
@@ -423,6 +423,38 @@ static void kauditd_send_skb(struct sk_buff *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, gfp_t gfp_mask)
+{
+       struct sk_buff          *copy;
+       struct audit_net        *aunet = net_generic(&init_net, audit_net_id);
+       struct sock             *sock = aunet->nlsk;
+
+       if (!netlink_has_listeners(sock, AUDIT_NLGRP_READLOG))
+               return;
+
+       /*
+        * 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_mask);
+       if (!copy)
+               return;
+
+       nlmsg_multicast(sock, copy, 0, AUDIT_NLGRP_READLOG, gfp_mask);
+}
+
 /*
  * flush_hold_queue - empty the hold queue if auditd appears
  *
@@ -692,7 +724,7 @@ static int audit_get_feature(struct sk_buff *skb)
 
        seq = nlmsg_hdr(skb)->nlmsg_seq;
 
-       audit_send_reply(skb, seq, AUDIT_GET, 0, 0, &af, sizeof(af));
+       audit_send_reply(skb, seq, AUDIT_GET_FEATURE, 0, 0, &af, sizeof(af));
 
        return 0;
 }
@@ -707,7 +739,7 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
 
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
        audit_log_task_info(ab, current);
-       audit_log_format(ab, "feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
+       audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
                         audit_feature_names[which], !!old_feature, !!new_feature,
                         !!old_lock, !!new_lock, res);
        audit_log_end(ab);
@@ -718,7 +750,7 @@ static int audit_set_feature(struct sk_buff *skb)
        struct audit_features *uaf;
        int i;
 
-       BUILD_BUG_ON(AUDIT_LAST_FEATURE + 1 > sizeof(audit_feature_names)/sizeof(audit_feature_names[0]));
+       BUILD_BUG_ON(AUDIT_LAST_FEATURE + 1 > ARRAY_SIZE(audit_feature_names));
        uaf = nlmsg_data(nlmsg_hdr(skb));
 
        /* if there is ever a version 2 we should handle that here */
@@ -810,7 +842,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                s.backlog_limit         = audit_backlog_limit;
                s.lost                  = atomic_read(&audit_lost);
                s.backlog               = skb_queue_len(&audit_skb_queue);
-               s.version               = AUDIT_VERSION_LATEST;
+               s.feature_bitmap        = AUDIT_FEATURE_BITMAP_ALL;
                s.backlog_wait_time     = audit_backlog_wait_time;
                audit_send_reply(skb, seq, AUDIT_GET, 0, 0, &s, sizeof(s));
                break;
@@ -1076,10 +1108,22 @@ static void audit_receive(struct sk_buff  *skb)
        mutex_unlock(&audit_cmd_mutex);
 }
 
+/* Run custom bind function on netlink socket group connect or bind requests. */
+static int audit_bind(int group)
+{
+       if (!capable(CAP_AUDIT_READ))
+               return -EPERM;
+
+       return 0;
+}
+
 static int __net_init audit_net_init(struct net *net)
 {
        struct netlink_kernel_cfg cfg = {
                .input  = audit_receive,
+               .bind   = audit_bind,
+               .flags  = NL_CFG_F_NONROOT_RECV,
+               .groups = AUDIT_NLGRP_MAX,
        };
 
        struct audit_net *aunet = net_generic(net, audit_net_id);
@@ -1257,19 +1301,9 @@ err:
  */
 unsigned int audit_serial(void)
 {
-       static DEFINE_SPINLOCK(serial_lock);
-       static unsigned int serial = 0;
-
-       unsigned long flags;
-       unsigned int ret;
+       static atomic_t serial = ATOMIC_INIT(0);
 
-       spin_lock_irqsave(&serial_lock, flags);
-       do {
-               ret = ++serial;
-       } while (unlikely(!ret));
-       spin_unlock_irqrestore(&serial_lock, flags);
-
-       return ret;
+       return atomic_add_return(1, &serial);
 }
 
 static inline void audit_get_stamp(struct audit_context *ctx,
@@ -1637,7 +1671,7 @@ void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
        }
 }
 
-void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
+static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
 {
        kernel_cap_t *perm = &name->fcap.permitted;
        kernel_cap_t *inh = &name->fcap.inheritable;
@@ -1816,7 +1850,7 @@ EXPORT_SYMBOL(audit_log_task_context);
 void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
 {
        const struct cred *cred;
-       char name[sizeof(tsk->comm)];
+       char comm[sizeof(tsk->comm)];
        struct mm_struct *mm = tsk->mm;
        char *tty;
 
@@ -1850,9 +1884,8 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
                         from_kgid(&init_user_ns, cred->fsgid),
                         tty, audit_get_sessionid(tsk));
 
-       get_task_comm(name, tsk);
        audit_log_format(ab, " comm=");
-       audit_log_untrustedstring(ab, name);
+       audit_log_untrustedstring(ab, get_task_comm(comm, tsk));
 
        if (mm) {
                down_read(&mm->mmap_sem);
@@ -1901,10 +1934,10 @@ out:
  * audit_log_end - end one audit record
  * @ab: the audit_buffer
  *
- * The netlink_* functions cannot be called inside an irq context, so
- * the audit buffer is placed on a queue and a tasklet is scheduled to
- * remove them from the queue outside the irq context.  May be called in
- * any context.
+ * netlink_unicast() cannot be called inside an irq context because it blocks
+ * (last arg, flags, is not set to MSG_DONTWAIT), so the audit buffer is placed
+ * on a queue and a tasklet is scheduled to remove them from the queue outside
+ * the irq context.  May be called in any context.
  */
 void audit_log_end(struct audit_buffer *ab)
 {
@@ -1914,7 +1947,20 @@ void audit_log_end(struct audit_buffer *ab)
                audit_log_lost("rate limit exceeded");
        } else {
                struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
-               nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN;
+
+               nlh->nlmsg_len = ab->skb->len;
+               kauditd_send_multicast_skb(ab->skb, ab->gfp_mask);
+
+               /*
+                * 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 -= NLMSG_HDRLEN;
 
                if (audit_pid) {
                        skb_queue_tail(&audit_skb_queue, ab->skb);