Merge branch 'stable-4.8' of git://git.infradead.org/users/pcmoore/audit
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 30 Jul 2016 00:54:17 +0000 (17:54 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 30 Jul 2016 00:54:17 +0000 (17:54 -0700)
Pull audit updates from Paul Moore:
 "Six audit patches for 4.8.

  There are a couple of style and minor whitespace tweaks for the logs,
  as well as a minor fixup to catch errors on user filter rules, however
  the major improvements are a fix to the s390 syscall argument masking
  code (reviewed by the nice s390 folks), some consolidation around the
  exclude filtering (less code, always a win), and a double-fetch fix
  for recording the execve arguments"

* 'stable-4.8' of git://git.infradead.org/users/pcmoore/audit:
  audit: fix a double fetch in audit_log_single_execve_arg()
  audit: fix whitespace in CWD record
  audit: add fields to exclude filter by reusing user filter
  s390: ensure that syscall arguments are properly masked on s390
  audit: fix some horrible switch statement style crimes
  audit: fixup: log on errors from filter user rules

arch/s390/kernel/ptrace.c
include/linux/audit.h
kernel/audit.c
kernel/audit.h
kernel/auditfilter.c
kernel/auditsc.c

index cea1701..9336e82 100644 (file)
@@ -821,6 +821,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 
 asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 {
+       unsigned long mask = -1UL;
+
        /*
         * The sysc_tracesys code in entry.S stored the system
         * call number to gprs[2].
@@ -846,9 +848,12 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
                trace_sys_enter(regs, regs->gprs[2]);
 
-       audit_syscall_entry(regs->gprs[2], regs->orig_gpr2,
-                           regs->gprs[3], regs->gprs[4],
-                           regs->gprs[5]);
+       if (is_compat_task())
+               mask = 0xffffffff;
+
+       audit_syscall_entry(regs->gprs[2], regs->orig_gpr2 & mask,
+                           regs->gprs[3] &mask, regs->gprs[4] &mask,
+                           regs->gprs[5] &mask);
 
        return regs->gprs[2];
 }
index e38e3fc..9d4443f 100644 (file)
@@ -163,8 +163,6 @@ extern void audit_log_task_info(struct audit_buffer *ab,
 extern int                 audit_update_lsm_rules(void);
 
                                /* Private API (for audit.c only) */
-extern int audit_filter_user(int type);
-extern int audit_filter_type(int type);
 extern int audit_rule_change(int type, __u32 portid, int seq,
                                void *data, size_t datasz);
 extern int audit_list_rules_send(struct sk_buff *request_skb, int seq);
index 8d528f9..a8a91bd 100644 (file)
@@ -932,7 +932,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                if (!audit_enabled && msg_type != AUDIT_USER_AVC)
                        return 0;
 
-               err = audit_filter_user(msg_type);
+               err = audit_filter(msg_type, AUDIT_FILTER_USER);
                if (err == 1) { /* match or error */
                        err = 0;
                        if (msg_type == AUDIT_USER_TTY) {
@@ -1379,7 +1379,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
        if (audit_initialized != AUDIT_INITIALIZED)
                return NULL;
 
-       if (unlikely(audit_filter_type(type)))
+       if (unlikely(!audit_filter(type, AUDIT_FILTER_TYPE)))
                return NULL;
 
        if (gfp_mask & __GFP_DIRECT_RECLAIM) {
index a492f4c..431444c 100644 (file)
@@ -331,6 +331,8 @@ extern pid_t audit_sig_pid;
 extern kuid_t audit_sig_uid;
 extern u32 audit_sig_sid;
 
+extern int audit_filter(int msgtype, unsigned int listtype);
+
 #ifdef CONFIG_AUDITSYSCALL
 extern int __audit_signal_info(int sig, struct task_struct *t);
 static inline int audit_signal_info(int sig, struct task_struct *t)
index 94ca7b1..85d9cac 100644 (file)
@@ -1290,113 +1290,72 @@ int audit_compare_dname_path(const char *dname, const char *path, int parentlen)
        return strncmp(p, dname, dlen);
 }
 
-static int audit_filter_user_rules(struct audit_krule *rule, int type,
-                                  enum audit_state *state)
+int audit_filter(int msgtype, unsigned int listtype)
 {
-       int i;
-
-       for (i = 0; i < rule->field_count; i++) {
-               struct audit_field *f = &rule->fields[i];
-               pid_t pid;
-               int result = 0;
-               u32 sid;
-
-               switch (f->type) {
-               case AUDIT_PID:
-                       pid = task_pid_nr(current);
-                       result = audit_comparator(pid, f->op, f->val);
-                       break;
-               case AUDIT_UID:
-                       result = audit_uid_comparator(current_uid(), f->op, f->uid);
-                       break;
-               case AUDIT_GID:
-                       result = audit_gid_comparator(current_gid(), f->op, f->gid);
-                       break;
-               case AUDIT_LOGINUID:
-                       result = audit_uid_comparator(audit_get_loginuid(current),
-                                                 f->op, f->uid);
-                       break;
-               case AUDIT_LOGINUID_SET:
-                       result = audit_comparator(audit_loginuid_set(current),
-                                                 f->op, f->val);
-                       break;
-               case AUDIT_MSGTYPE:
-                       result = audit_comparator(type, f->op, f->val);
-                       break;
-               case AUDIT_SUBJ_USER:
-               case AUDIT_SUBJ_ROLE:
-               case AUDIT_SUBJ_TYPE:
-               case AUDIT_SUBJ_SEN:
-               case AUDIT_SUBJ_CLR:
-                       if (f->lsm_rule) {
-                               security_task_getsecid(current, &sid);
-                               result = security_audit_rule_match(sid,
-                                                                  f->type,
-                                                                  f->op,
-                                                                  f->lsm_rule,
-                                                                  NULL);
-                       }
-                       break;
-               }
-
-               if (!result)
-                       return 0;
-       }
-       switch (rule->action) {
-       case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
-       case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
-       }
-       return 1;
-}
-
-int audit_filter_user(int type)
-{
-       enum audit_state state = AUDIT_DISABLED;
        struct audit_entry *e;
-       int rc, ret;
-
-       ret = 1; /* Audit by default */
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
-               rc = audit_filter_user_rules(&e->rule, type, &state);
-               if (rc) {
-                       if (rc > 0 && state == AUDIT_DISABLED)
-                               ret = 0;
-                       break;
-               }
-       }
-       rcu_read_unlock();
-
-       return ret;
-}
-
-int audit_filter_type(int type)
-{
-       struct audit_entry *e;
-       int result = 0;
+       int ret = 1; /* Audit by default */
 
        rcu_read_lock();
-       if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE]))
+       if (list_empty(&audit_filter_list[listtype]))
                goto unlock_and_return;
+       list_for_each_entry_rcu(e, &audit_filter_list[listtype], list) {
+               int i, result = 0;
 
-       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE],
-                               list) {
-               int i;
                for (i = 0; i < e->rule.field_count; i++) {
                        struct audit_field *f = &e->rule.fields[i];
-                       if (f->type == AUDIT_MSGTYPE) {
-                               result = audit_comparator(type, f->op, f->val);
-                               if (!result)
-                                       break;
+                       pid_t pid;
+                       u32 sid;
+
+                       switch (f->type) {
+                       case AUDIT_PID:
+                               pid = task_pid_nr(current);
+                               result = audit_comparator(pid, f->op, f->val);
+                               break;
+                       case AUDIT_UID:
+                               result = audit_uid_comparator(current_uid(), f->op, f->uid);
+                               break;
+                       case AUDIT_GID:
+                               result = audit_gid_comparator(current_gid(), f->op, f->gid);
+                               break;
+                       case AUDIT_LOGINUID:
+                               result = audit_uid_comparator(audit_get_loginuid(current),
+                                                             f->op, f->uid);
+                               break;
+                       case AUDIT_LOGINUID_SET:
+                               result = audit_comparator(audit_loginuid_set(current),
+                                                         f->op, f->val);
+                               break;
+                       case AUDIT_MSGTYPE:
+                               result = audit_comparator(msgtype, f->op, f->val);
+                               break;
+                       case AUDIT_SUBJ_USER:
+                       case AUDIT_SUBJ_ROLE:
+                       case AUDIT_SUBJ_TYPE:
+                       case AUDIT_SUBJ_SEN:
+                       case AUDIT_SUBJ_CLR:
+                               if (f->lsm_rule) {
+                                       security_task_getsecid(current, &sid);
+                                       result = security_audit_rule_match(sid,
+                                                       f->type, f->op, f->lsm_rule, NULL);
+                               }
+                               break;
+                       default:
+                               goto unlock_and_return;
                        }
+                       if (result < 0) /* error */
+                               goto unlock_and_return;
+                       if (!result)
+                               break;
+               }
+               if (result > 0) {
+                       if (e->rule.action == AUDIT_NEVER || listtype == AUDIT_FILTER_TYPE)
+                               ret = 0;
+                       break;
                }
-               if (result)
-                       goto unlock_and_return;
        }
 unlock_and_return:
        rcu_read_unlock();
-       return result;
+       return ret;
 }
 
 static int update_lsm_rule(struct audit_krule *r)
index 2672d10..5abf1dc 100644 (file)
@@ -72,6 +72,7 @@
 #include <linux/compat.h>
 #include <linux/ctype.h>
 #include <linux/string.h>
+#include <linux/uaccess.h>
 #include <uapi/linux/limits.h>
 
 #include "audit.h"
@@ -81,7 +82,8 @@
 #define AUDITSC_SUCCESS 1
 #define AUDITSC_FAILURE 2
 
-/* no execve audit message should be longer than this (userspace limits) */
+/* no execve audit message should be longer than this (userspace limits),
+ * see the note near the top of audit_log_execve_info() about this value */
 #define MAX_EXECVE_AUDIT_LEN 7500
 
 /* max length to print of cmdline/proctitle value during audit */
@@ -694,8 +696,12 @@ static int audit_filter_rules(struct task_struct *tsk,
                ctx->prio = rule->prio;
        }
        switch (rule->action) {
-       case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
-       case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
+       case AUDIT_NEVER:
+               *state = AUDIT_DISABLED;
+               break;
+       case AUDIT_ALWAYS:
+               *state = AUDIT_RECORD_CONTEXT;
+               break;
        }
        return 1;
 }
@@ -987,184 +993,178 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid,
        return rc;
 }
 
-/*
- * to_send and len_sent accounting are very loose estimates.  We aren't
- * really worried about a hard cap to MAX_EXECVE_AUDIT_LEN so much as being
- * within about 500 bytes (next page boundary)
- *
- * why snprintf?  an int is up to 12 digits long.  if we just assumed when
- * logging that a[%d]= was going to be 16 characters long we would be wasting
- * space in every audit message.  In one 7500 byte message we can log up to
- * about 1000 min size arguments.  That comes down to about 50% waste of space
- * if we didn't do the snprintf to find out how long arg_num_len was.
- */
-static int audit_log_single_execve_arg(struct audit_context *context,
-                                       struct audit_buffer **ab,
-                                       int arg_num,
-                                       size_t *len_sent,
-                                       const char __user *p,
-                                       char *buf)
+static void audit_log_execve_info(struct audit_context *context,
+                                 struct audit_buffer **ab)
 {
-       char arg_num_len_buf[12];
-       const char __user *tmp_p = p;
-       /* how many digits are in arg_num? 5 is the length of ' a=""' */
-       size_t arg_num_len = snprintf(arg_num_len_buf, 12, "%d", arg_num) + 5;
-       size_t len, len_left, to_send;
-       size_t max_execve_audit_len = MAX_EXECVE_AUDIT_LEN;
-       unsigned int i, has_cntl = 0, too_long = 0;
-       int ret;
-
-       /* strnlen_user includes the null we don't want to send */
-       len_left = len = strnlen_user(p, MAX_ARG_STRLEN) - 1;
-
-       /*
-        * We just created this mm, if we can't find the strings
-        * we just copied into it something is _very_ wrong. Similar
-        * for strings that are too long, we should not have created
-        * any.
-        */
-       if (WARN_ON_ONCE(len < 0 || len > MAX_ARG_STRLEN - 1)) {
-               send_sig(SIGKILL, current, 0);
-               return -1;
+       long len_max;
+       long len_rem;
+       long len_full;
+       long len_buf;
+       long len_abuf;
+       long len_tmp;
+       bool require_data;
+       bool encode;
+       unsigned int iter;
+       unsigned int arg;
+       char *buf_head;
+       char *buf;
+       const char __user *p = (const char __user *)current->mm->arg_start;
+
+       /* NOTE: this buffer needs to be large enough to hold all the non-arg
+        *       data we put in the audit record for this argument (see the
+        *       code below) ... at this point in time 96 is plenty */
+       char abuf[96];
+
+       /* NOTE: we set MAX_EXECVE_AUDIT_LEN to a rather arbitrary limit, the
+        *       current value of 7500 is not as important as the fact that it
+        *       is less than 8k, a setting of 7500 gives us plenty of wiggle
+        *       room if we go over a little bit in the logging below */
+       WARN_ON_ONCE(MAX_EXECVE_AUDIT_LEN > 7500);
+       len_max = MAX_EXECVE_AUDIT_LEN;
+
+       /* scratch buffer to hold the userspace args */
+       buf_head = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL);
+       if (!buf_head) {
+               audit_panic("out of memory for argv string");
+               return;
        }
+       buf = buf_head;
 
-       /* walk the whole argument looking for non-ascii chars */
+       audit_log_format(*ab, "argc=%d", context->execve.argc);
+
+       len_rem = len_max;
+       len_buf = 0;
+       len_full = 0;
+       require_data = true;
+       encode = false;
+       iter = 0;
+       arg = 0;
        do {
-               if (len_left > MAX_EXECVE_AUDIT_LEN)
-                       to_send = MAX_EXECVE_AUDIT_LEN;
-               else
-                       to_send = len_left;
-               ret = copy_from_user(buf, tmp_p, to_send);
-               /*
-                * There is no reason for this copy to be short. We just
-                * copied them here, and the mm hasn't been exposed to user-
-                * space yet.
-                */
-               if (ret) {
-                       WARN_ON(1);
-                       send_sig(SIGKILL, current, 0);
-                       return -1;
-               }
-               buf[to_send] = '\0';
-               has_cntl = audit_string_contains_control(buf, to_send);
-               if (has_cntl) {
-                       /*
-                        * hex messages get logged as 2 bytes, so we can only
-                        * send half as much in each message
-                        */
-                       max_execve_audit_len = MAX_EXECVE_AUDIT_LEN / 2;
-                       break;
-               }
-               len_left -= to_send;
-               tmp_p += to_send;
-       } while (len_left > 0);
-
-       len_left = len;
-
-       if (len > max_execve_audit_len)
-               too_long = 1;
-
-       /* rewalk the argument actually logging the message */
-       for (i = 0; len_left > 0; i++) {
-               int room_left;
-
-               if (len_left > max_execve_audit_len)
-                       to_send = max_execve_audit_len;
-               else
-                       to_send = len_left;
-
-               /* do we have space left to send this argument in this ab? */
-               room_left = MAX_EXECVE_AUDIT_LEN - arg_num_len - *len_sent;
-               if (has_cntl)
-                       room_left -= (to_send * 2);
-               else
-                       room_left -= to_send;
-               if (room_left < 0) {
-                       *len_sent = 0;
-                       audit_log_end(*ab);
-                       *ab = audit_log_start(context, GFP_KERNEL, AUDIT_EXECVE);
-                       if (!*ab)
-                               return 0;
-               }
+               /* NOTE: we don't ever want to trust this value for anything
+                *       serious, but the audit record format insists we
+                *       provide an argument length for really long arguments,
+                *       e.g. > MAX_EXECVE_AUDIT_LEN, so we have no choice but
+                *       to use strncpy_from_user() to obtain this value for
+                *       recording in the log, although we don't use it
+                *       anywhere here to avoid a double-fetch problem */
+               if (len_full == 0)
+                       len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1;
+
+               /* read more data from userspace */
+               if (require_data) {
+                       /* can we make more room in the buffer? */
+                       if (buf != buf_head) {
+                               memmove(buf_head, buf, len_buf);
+                               buf = buf_head;
+                       }
+
+                       /* fetch as much as we can of the argument */
+                       len_tmp = strncpy_from_user(&buf_head[len_buf], p,
+                                                   len_max - len_buf);
+                       if (len_tmp == -EFAULT) {
+                               /* unable to copy from userspace */
+                               send_sig(SIGKILL, current, 0);
+                               goto out;
+                       } else if (len_tmp == (len_max - len_buf)) {
+                               /* buffer is not large enough */
+                               require_data = true;
+                               /* NOTE: if we are going to span multiple
+                                *       buffers force the encoding so we stand
+                                *       a chance at a sane len_full value and
+                                *       consistent record encoding */
+                               encode = true;
+                               len_full = len_full * 2;
+                               p += len_tmp;
+                       } else {
+                               require_data = false;
+                               if (!encode)
+                                       encode = audit_string_contains_control(
+                                                               buf, len_tmp);
+                               /* try to use a trusted value for len_full */
+                               if (len_full < len_max)
+                                       len_full = (encode ?
+                                                   len_tmp * 2 : len_tmp);
+                               p += len_tmp + 1;
+                       }
+                       len_buf += len_tmp;
+                       buf_head[len_buf] = '\0';
 
-               /*
-                * first record needs to say how long the original string was
-                * so we can be sure nothing was lost.
-                */
-               if ((i == 0) && (too_long))
-                       audit_log_format(*ab, " a%d_len=%zu", arg_num,
-                                        has_cntl ? 2*len : len);
-
-               /*
-                * normally arguments are small enough to fit and we already
-                * filled buf above when we checked for control characters
-                * so don't bother with another copy_from_user
-                */
-               if (len >= max_execve_audit_len)
-                       ret = copy_from_user(buf, p, to_send);
-               else
-                       ret = 0;
-               if (ret) {
-                       WARN_ON(1);
-                       send_sig(SIGKILL, current, 0);
-                       return -1;
+                       /* length of the buffer in the audit record? */
+                       len_abuf = (encode ? len_buf * 2 : len_buf + 2);
                }
-               buf[to_send] = '\0';
-
-               /* actually log it */
-               audit_log_format(*ab, " a%d", arg_num);
-               if (too_long)
-                       audit_log_format(*ab, "[%d]", i);
-               audit_log_format(*ab, "=");
-               if (has_cntl)
-                       audit_log_n_hex(*ab, buf, to_send);
-               else
-                       audit_log_string(*ab, buf);
-
-               p += to_send;
-               len_left -= to_send;
-               *len_sent += arg_num_len;
-               if (has_cntl)
-                       *len_sent += to_send * 2;
-               else
-                       *len_sent += to_send;
-       }
-       /* include the null we didn't log */
-       return len + 1;
-}
 
-static void audit_log_execve_info(struct audit_context *context,
-                                 struct audit_buffer **ab)
-{
-       int i, len;
-       size_t len_sent = 0;
-       const char __user *p;
-       char *buf;
+               /* write as much as we can to the audit log */
+               if (len_buf > 0) {
+                       /* NOTE: some magic numbers here - basically if we
+                        *       can't fit a reasonable amount of data into the
+                        *       existing audit buffer, flush it and start with
+                        *       a new buffer */
+                       if ((sizeof(abuf) + 8) > len_rem) {
+                               len_rem = len_max;
+                               audit_log_end(*ab);
+                               *ab = audit_log_start(context,
+                                                     GFP_KERNEL, AUDIT_EXECVE);
+                               if (!*ab)
+                                       goto out;
+                       }
 
-       p = (const char __user *)current->mm->arg_start;
+                       /* create the non-arg portion of the arg record */
+                       len_tmp = 0;
+                       if (require_data || (iter > 0) ||
+                           ((len_abuf + sizeof(abuf)) > len_rem)) {
+                               if (iter == 0) {
+                                       len_tmp += snprintf(&abuf[len_tmp],
+                                                       sizeof(abuf) - len_tmp,
+                                                       " a%d_len=%lu",
+                                                       arg, len_full);
+                               }
+                               len_tmp += snprintf(&abuf[len_tmp],
+                                                   sizeof(abuf) - len_tmp,
+                                                   " a%d[%d]=", arg, iter++);
+                       } else
+                               len_tmp += snprintf(&abuf[len_tmp],
+                                                   sizeof(abuf) - len_tmp,
+                                                   " a%d=", arg);
+                       WARN_ON(len_tmp >= sizeof(abuf));
+                       abuf[sizeof(abuf) - 1] = '\0';
+
+                       /* log the arg in the audit record */
+                       audit_log_format(*ab, "%s", abuf);
+                       len_rem -= len_tmp;
+                       len_tmp = len_buf;
+                       if (encode) {
+                               if (len_abuf > len_rem)
+                                       len_tmp = len_rem / 2; /* encoding */
+                               audit_log_n_hex(*ab, buf, len_tmp);
+                               len_rem -= len_tmp * 2;
+                               len_abuf -= len_tmp * 2;
+                       } else {
+                               if (len_abuf > len_rem)
+                                       len_tmp = len_rem - 2; /* quotes */
+                               audit_log_n_string(*ab, buf, len_tmp);
+                               len_rem -= len_tmp + 2;
+                               /* don't subtract the "2" because we still need
+                                * to add quotes to the remaining string */
+                               len_abuf -= len_tmp;
+                       }
+                       len_buf -= len_tmp;
+                       buf += len_tmp;
+               }
 
-       audit_log_format(*ab, "argc=%d", context->execve.argc);
+               /* ready to move to the next argument? */
+               if ((len_buf == 0) && !require_data) {
+                       arg++;
+                       iter = 0;
+                       len_full = 0;
+                       require_data = true;
+                       encode = false;
+               }
+       } while (arg < context->execve.argc);
 
-       /*
-        * we need some kernel buffer to hold the userspace args.  Just
-        * allocate one big one rather than allocating one of the right size
-        * for every single argument inside audit_log_single_execve_arg()
-        * should be <8k allocation so should be pretty safe.
-        */
-       buf = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL);
-       if (!buf) {
-               audit_panic("out of memory for argv string");
-               return;
-       }
+       /* NOTE: the caller handles the final audit_log_end() call */
 
-       for (i = 0; i < context->execve.argc; i++) {
-               len = audit_log_single_execve_arg(context, ab, i,
-                                                 &len_sent, p, buf);
-               if (len <= 0)
-                       break;
-               p += len;
-       }
-       kfree(buf);
+out:
+       kfree(buf_head);
 }
 
 static void show_special(struct audit_context *context, int *call_panic)
@@ -1425,7 +1425,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
        if (context->pwd.dentry && context->pwd.mnt) {
                ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
                if (ab) {
-                       audit_log_d_path(ab, " cwd=", &context->pwd);
+                       audit_log_d_path(ab, "cwd=", &context->pwd);
                        audit_log_end(ab);
                }
        }