]> Pileus Git - ~andy/linux/blobdiff - kernel/audit.c
audit: rework AUDIT_TTY_SET to only grab spin_lock once
[~andy/linux] / kernel / audit.c
index 37ba59936dc5c69eef1e9f67bce36c63adca739c..b1d24a035ec998c24f27ac192c5a448823fd65a3 100644 (file)
@@ -101,7 +101,8 @@ static __u32        audit_nlk_portid;
  * audit records being dropped. */
 static int     audit_rate_limit;
 
-/* Number of outstanding audit_buffers allowed. */
+/* Number of outstanding audit_buffers allowed.
+ * When set to zero, this means unlimited. */
 static int     audit_backlog_limit = 64;
 #define AUDIT_BACKLOG_WAIT_TIME (60 * HZ)
 static int     audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME;
@@ -375,7 +376,8 @@ static int audit_set_failure(int state)
 static void audit_hold_skb(struct sk_buff *skb)
 {
        if (audit_default &&
-           skb_queue_len(&audit_skb_hold_queue) < audit_backlog_limit)
+           (!audit_backlog_limit ||
+            skb_queue_len(&audit_skb_hold_queue) < audit_backlog_limit))
                skb_queue_tail(&audit_skb_hold_queue, skb);
        else
                kfree_skb(skb);
@@ -408,10 +410,12 @@ static void kauditd_send_skb(struct sk_buff *skb)
        err = netlink_unicast(audit_sock, skb, audit_nlk_portid, 0);
        if (err < 0) {
                BUG_ON(err != -ECONNREFUSED); /* Shouldn't happen */
-               printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid);
-               audit_log_lost("auditd disappeared\n");
-               audit_pid = 0;
-               audit_sock = NULL;
+               if (audit_pid) {
+                       printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid);
+                       audit_log_lost("auditd disappeared\n");
+                       audit_pid = 0;
+                       audit_sock = NULL;
+               }
                /* we might get lucky and get this in the next auditd */
                audit_hold_skb(skb);
        } else
@@ -680,8 +684,12 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
 {
        struct audit_buffer *ab;
 
+       if (audit_enabled == AUDIT_OFF)
+               return;
+
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
-       audit_log_format(ab, "feature=%s new=%d old=%d old_lock=%d new_lock=%d res=%d",
+       audit_log_task_info(ab, current);
+       audit_log_format(ab, "feature=%s old=%d new=%d old_lock=%d new_lock=%d res=%d",
                         audit_feature_names[which], !!old_feature, !!new_feature,
                         !!old_lock, !!new_lock, res);
        audit_log_end(ab);
@@ -711,7 +719,7 @@ static int audit_set_feature(struct sk_buff *skb)
                old_lock = af.lock & feature;
 
                /* are we changing a locked feature? */
-               if ((af.lock & feature) && (new_feature != old_feature)) {
+               if (old_lock && (new_feature != old_feature)) {
                        audit_log_feature_change(i, old_feature, new_feature,
                                                 old_lock, new_lock, 0);
                        return -EPERM;
@@ -784,7 +792,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               = 2;
+               s.version               = AUDIT_VERSION_LATEST;
                s.backlog_wait_time     = audit_backlog_wait_time;
                audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_GET, 0, 0,
                                 &s, sizeof(s));
@@ -808,11 +816,13 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                if (s.mask & AUDIT_STATUS_PID) {
                        int new_pid = s.pid;
 
+                       if ((!new_pid) && (task_tgid_vnr(current) != audit_pid))
+                               return -EACCES;
                        if (audit_enabled != AUDIT_OFF)
                                audit_log_config_change("audit_pid", new_pid, audit_pid, 1);
                        audit_pid = new_pid;
                        audit_nlk_portid = NETLINK_CB(skb).portid;
-                       audit_sock = NETLINK_CB(skb).sk;
+                       audit_sock = skb->sk;
                }
                if (s.mask & AUDIT_STATUS_RATE_LIMIT) {
                        err = audit_set_rate_limit(s.rate_limit);
@@ -824,22 +834,15 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        if (err < 0)
                                return err;
                }
-               switch (s.version) {
-               /* add future vers # cases immediately below and allow
-                * to fall through */
-               case 2:
-                       if (s.mask & AUDIT_STATUS_BACKLOG_WAIT_TIME) {
-                               if (sizeof(s) > (size_t)nlh->nlmsg_len)
-                                       return -EINVAL;
-                               if (s.backlog_wait_time < 0 ||
-                                   s.backlog_wait_time > 10*AUDIT_BACKLOG_WAIT_TIME)
-                                       return -EINVAL;
-                               err = audit_set_backlog_wait_time(s.backlog_wait_time);
-                               if (err < 0)
-                                       return err;
-                       }
-               default:
-                       break;
+               if (s.mask & AUDIT_STATUS_BACKLOG_WAIT_TIME) {
+                       if (sizeof(s) > (size_t)nlh->nlmsg_len)
+                               return -EINVAL;
+                       if (s.backlog_wait_time < 0 ||
+                           s.backlog_wait_time > 10*AUDIT_BACKLOG_WAIT_TIME)
+                               return -EINVAL;
+                       err = audit_set_backlog_wait_time(s.backlog_wait_time);
+                       if (err < 0)
+                               return err;
                }
                break;
        }
@@ -860,13 +863,14 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        return 0;
 
                err = audit_filter_user(msg_type);
-               if (err == 1) {
+               if (err == 1) { /* match or error */
                        err = 0;
                        if (msg_type == AUDIT_USER_TTY) {
                                err = tty_audit_push_current();
                                if (err)
                                        break;
                        }
+                       mutex_unlock(&audit_cmd_mutex);
                        audit_log_common_recv_msg(&ab, msg_type);
                        if (msg_type != AUDIT_USER_TTY)
                                audit_log_format(ab, " msg='%.*s'",
@@ -884,6 +888,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        }
                        audit_set_portid(ab, NETLINK_CB(skb).portid);
                        audit_log_end(ab);
+                       mutex_lock(&audit_cmd_mutex);
                }
                break;
        case AUDIT_ADD_RULE:
@@ -896,11 +901,12 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        audit_log_end(ab);
                        return -EPERM;
                }
-               /* fallthrough */
-       case AUDIT_LIST_RULES:
-               err = audit_receive_filter(msg_type, NETLINK_CB(skb).portid,
+               err = audit_rule_change(msg_type, NETLINK_CB(skb).portid,
                                           seq, data, nlmsg_len(nlh));
                break;
+       case AUDIT_LIST_RULES:
+               err = audit_list_rules_send(NETLINK_CB(skb).portid, seq);
+               break;
        case AUDIT_TRIM:
                audit_trim_trees();
                audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE);
@@ -982,20 +988,36 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                break;
        }
        case AUDIT_TTY_SET: {
-               struct audit_tty_status s;
+               struct audit_tty_status s, old;
                struct task_struct *tsk = current;
+               struct audit_buffer     *ab;
 
                memset(&s, 0, sizeof(s));
                /* guard against past and future API changes */
                memcpy(&s, data, min_t(size_t, sizeof(s), nlmsg_len(nlh)));
+               /* check if new data is valid */
                if ((s.enabled != 0 && s.enabled != 1) ||
                    (s.log_passwd != 0 && s.log_passwd != 1))
-                       return -EINVAL;
+                       err = -EINVAL;
 
                spin_lock(&tsk->sighand->siglock);
-               tsk->signal->audit_tty = s.enabled;
-               tsk->signal->audit_tty_log_passwd = s.log_passwd;
+               old.enabled = tsk->signal->audit_tty;
+               old.log_passwd = tsk->signal->audit_tty_log_passwd;
+               if (!err) {
+                       tsk->signal->audit_tty = s.enabled;
+                       tsk->signal->audit_tty_log_passwd = s.log_passwd;
+               }
                spin_unlock(&tsk->sighand->siglock);
+
+               audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE);
+               audit_log_format(ab, " op=tty_set"
+                                " old-enabled=%d old-log_passwd=%d"
+                                " new-enabled=%d new-log_passwd=%d"
+                                " res=%d",
+                                old.enabled, old.log_passwd,
+                                s.enabled, s.log_passwd,
+                                !err);
+               audit_log_end(ab);
                break;
        }
        default:
@@ -1052,12 +1074,11 @@ static int __net_init audit_net_init(struct net *net)
        pr_info("audit: initializing netlink socket in namespace\n");
 
        aunet->nlsk = netlink_kernel_create(net, NETLINK_AUDIT, &cfg);
-       if (aunet->nlsk == NULL)
-               return -ENOMEM;
-       if (!aunet->nlsk)
+       if (aunet->nlsk == NULL) {
                audit_panic("cannot initialize netlink socket in namespace");
-       else
-               aunet->nlsk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
+               return -ENOMEM;
+       }
+       aunet->nlsk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
        return 0;
 }
 
@@ -1116,17 +1137,8 @@ static int __init audit_enable(char *str)
        if (!audit_default)
                audit_initialized = AUDIT_DISABLED;
 
-       printk(KERN_INFO "audit: %s", audit_default ? "enabled" : "disabled");
-
-       if (audit_initialized == AUDIT_INITIALIZED) {
-               audit_enabled = audit_default;
-               audit_ever_enabled |= !!audit_default;
-       } else if (audit_initialized == AUDIT_UNINITIALIZED) {
-               printk(" (after initialization)");
-       } else {
-               printk(" (until reboot)");
-       }
-       printk("\n");
+       pr_info("audit: %s\n", audit_default ?
+               "enabled (after initialization)" : "disabled (until reboot)");
 
        return 1;
 }
@@ -1260,21 +1272,20 @@ static inline void audit_get_stamp(struct audit_context *ctx,
 /*
  * Wait for auditd to drain the queue a little
  */
-static unsigned long wait_for_auditd(unsigned long sleep_time)
+static long wait_for_auditd(long sleep_time)
 {
-       unsigned long timeout = sleep_time;
        DECLARE_WAITQUEUE(wait, current);
        set_current_state(TASK_UNINTERRUPTIBLE);
        add_wait_queue_exclusive(&audit_backlog_wait, &wait);
 
        if (audit_backlog_limit &&
            skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
-               timeout = schedule_timeout(sleep_time);
+               sleep_time = schedule_timeout(sleep_time);
 
        __set_current_state(TASK_RUNNING);
        remove_wait_queue(&audit_backlog_wait, &wait);
 
-       return timeout;
+       return sleep_time;
 }
 
 /**
@@ -1298,7 +1309,8 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
        struct audit_buffer     *ab     = NULL;
        struct timespec         t;
        unsigned int            uninitialized_var(serial);
-       int reserve;
+       int reserve = 5; /* Allow atomic callers to go up to five
+                           entries over the normal backlog limit */
        unsigned long timeout_start = jiffies;
 
        if (audit_initialized != AUDIT_INITIALIZED)
@@ -1307,22 +1319,22 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
        if (unlikely(audit_filter_type(type)))
                return NULL;
 
-       if (gfp_mask & __GFP_WAIT)
-               reserve = 0;
-       else
-               reserve = 5; /* Allow atomic callers to go up to five
-                               entries over the normal backlog limit */
+       if (gfp_mask & __GFP_WAIT) {
+               if (audit_pid && audit_pid == current->pid)
+                       gfp_mask &= ~__GFP_WAIT;
+               else
+                       reserve = 0;
+       }
 
        while (audit_backlog_limit
               && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) {
                if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time) {
-                       unsigned long sleep_time;
+                       long sleep_time;
 
-                       sleep_time = timeout_start + audit_backlog_wait_time -
-                                       jiffies;
-                       if ((long)sleep_time > 0) {
+                       sleep_time = timeout_start + audit_backlog_wait_time - jiffies;
+                       if (sleep_time > 0) {
                                sleep_time = wait_for_auditd(sleep_time);
-                               if ((long)sleep_time > 0)
+                               if (sleep_time > 0)
                                        continue;
                        }
                }
@@ -1592,7 +1604,7 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
 
 void audit_log_session_info(struct audit_buffer *ab)
 {
-       u32 sessionid = audit_get_sessionid(current);
+       unsigned int sessionid = audit_get_sessionid(current);
        uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current));
 
        audit_log_format(ab, " auid=%u ses=%u", auid, sessionid);
@@ -1840,7 +1852,8 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
                if (mm->exe_file)
                        audit_log_d_path(ab, " exe=", &mm->exe_file->f_path);
                up_read(&mm->mmap_sem);
-       }
+       } else
+               audit_log_format(ab, " exe=(null)");
        audit_log_task_context(ab);
 }
 EXPORT_SYMBOL(audit_log_task_info);