Commit 797cee98 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'stable-4.8' of git://git.infradead.org/users/pcmoore/audit

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
parents 7a1e8b80 43761473
...@@ -821,6 +821,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, ...@@ -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) asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
{ {
unsigned long mask = -1UL;
/* /*
* The sysc_tracesys code in entry.S stored the system * The sysc_tracesys code in entry.S stored the system
* call number to gprs[2]. * call number to gprs[2].
...@@ -846,9 +848,12 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) ...@@ -846,9 +848,12 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->gprs[2]); trace_sys_enter(regs, regs->gprs[2]);
audit_syscall_entry(regs->gprs[2], regs->orig_gpr2, if (is_compat_task())
regs->gprs[3], regs->gprs[4], mask = 0xffffffff;
regs->gprs[5]);
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]; return regs->gprs[2];
} }
......
...@@ -163,8 +163,6 @@ extern void audit_log_task_info(struct audit_buffer *ab, ...@@ -163,8 +163,6 @@ extern void audit_log_task_info(struct audit_buffer *ab,
extern int audit_update_lsm_rules(void); extern int audit_update_lsm_rules(void);
/* Private API (for audit.c only) */ /* 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, extern int audit_rule_change(int type, __u32 portid, int seq,
void *data, size_t datasz); void *data, size_t datasz);
extern int audit_list_rules_send(struct sk_buff *request_skb, int seq); extern int audit_list_rules_send(struct sk_buff *request_skb, int seq);
......
...@@ -932,7 +932,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -932,7 +932,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (!audit_enabled && msg_type != AUDIT_USER_AVC) if (!audit_enabled && msg_type != AUDIT_USER_AVC)
return 0; return 0;
err = audit_filter_user(msg_type); err = audit_filter(msg_type, AUDIT_FILTER_USER);
if (err == 1) { /* match or error */ if (err == 1) { /* match or error */
err = 0; err = 0;
if (msg_type == AUDIT_USER_TTY) { if (msg_type == AUDIT_USER_TTY) {
...@@ -1379,7 +1379,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, ...@@ -1379,7 +1379,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
if (audit_initialized != AUDIT_INITIALIZED) if (audit_initialized != AUDIT_INITIALIZED)
return NULL; return NULL;
if (unlikely(audit_filter_type(type))) if (unlikely(!audit_filter(type, AUDIT_FILTER_TYPE)))
return NULL; return NULL;
if (gfp_mask & __GFP_DIRECT_RECLAIM) { if (gfp_mask & __GFP_DIRECT_RECLAIM) {
......
...@@ -331,6 +331,8 @@ extern pid_t audit_sig_pid; ...@@ -331,6 +331,8 @@ extern pid_t audit_sig_pid;
extern kuid_t audit_sig_uid; extern kuid_t audit_sig_uid;
extern u32 audit_sig_sid; extern u32 audit_sig_sid;
extern int audit_filter(int msgtype, unsigned int listtype);
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
extern int __audit_signal_info(int sig, struct task_struct *t); extern int __audit_signal_info(int sig, struct task_struct *t);
static inline int audit_signal_info(int sig, struct task_struct *t) static inline int audit_signal_info(int sig, struct task_struct *t)
......
...@@ -1290,113 +1290,72 @@ int audit_compare_dname_path(const char *dname, const char *path, int parentlen) ...@@ -1290,113 +1290,72 @@ int audit_compare_dname_path(const char *dname, const char *path, int parentlen)
return strncmp(p, dname, dlen); return strncmp(p, dname, dlen);
} }
static int audit_filter_user_rules(struct audit_krule *rule, int type, int audit_filter(int msgtype, unsigned int listtype)
enum audit_state *state)
{ {
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; struct audit_entry *e;
int rc, ret; int ret = 1; /* Audit by default */
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;
rcu_read_lock(); rcu_read_lock();
if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE])) if (list_empty(&audit_filter_list[listtype]))
goto unlock_and_return; 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++) { for (i = 0; i < e->rule.field_count; i++) {
struct audit_field *f = &e->rule.fields[i]; struct audit_field *f = &e->rule.fields[i];
if (f->type == AUDIT_MSGTYPE) { pid_t pid;
result = audit_comparator(type, f->op, f->val); u32 sid;
if (!result)
break; 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: unlock_and_return:
rcu_read_unlock(); rcu_read_unlock();
return result; return ret;
} }
static int update_lsm_rule(struct audit_krule *r) static int update_lsm_rule(struct audit_krule *r)
......
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/uaccess.h>
#include <uapi/linux/limits.h> #include <uapi/linux/limits.h>
#include "audit.h" #include "audit.h"
...@@ -81,7 +82,8 @@ ...@@ -81,7 +82,8 @@
#define AUDITSC_SUCCESS 1 #define AUDITSC_SUCCESS 1
#define AUDITSC_FAILURE 2 #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 #define MAX_EXECVE_AUDIT_LEN 7500
/* max length to print of cmdline/proctitle value during audit */ /* max length to print of cmdline/proctitle value during audit */
...@@ -694,8 +696,12 @@ static int audit_filter_rules(struct task_struct *tsk, ...@@ -694,8 +696,12 @@ static int audit_filter_rules(struct task_struct *tsk,
ctx->prio = rule->prio; ctx->prio = rule->prio;
} }
switch (rule->action) { switch (rule->action) {
case AUDIT_NEVER: *state = AUDIT_DISABLED; break; case AUDIT_NEVER:
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; *state = AUDIT_DISABLED;
break;
case AUDIT_ALWAYS:
*state = AUDIT_RECORD_CONTEXT;
break;
} }
return 1; return 1;
} }
...@@ -987,184 +993,178 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, ...@@ -987,184 +993,178 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid,
return rc; return rc;
} }
/* static void audit_log_execve_info(struct audit_context *context,
* to_send and len_sent accounting are very loose estimates. We aren't struct audit_buffer **ab)
* 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)
{ {
char arg_num_len_buf[12]; long len_max;
const char __user *tmp_p = p; long len_rem;
/* how many digits are in arg_num? 5 is the length of ' a=""' */ long len_full;
size_t arg_num_len = snprintf(arg_num_len_buf, 12, "%d", arg_num) + 5; long len_buf;
size_t len, len_left, to_send; long len_abuf;
size_t max_execve_audit_len = MAX_EXECVE_AUDIT_LEN; long len_tmp;
unsigned int i, has_cntl = 0, too_long = 0; bool require_data;
int ret; bool encode;
unsigned int iter;
/* strnlen_user includes the null we don't want to send */ unsigned int arg;
len_left = len = strnlen_user(p, MAX_ARG_STRLEN) - 1; char *buf_head;
char *buf;
/* const char __user *p = (const char __user *)current->mm->arg_start;
* We just created this mm, if we can't find the strings
* we just copied into it something is _very_ wrong. Similar /* NOTE: this buffer needs to be large enough to hold all the non-arg
* for strings that are too long, we should not have created * data we put in the audit record for this argument (see the
* any. * code below) ... at this point in time 96 is plenty */
*/ char abuf[96];
if (WARN_ON_ONCE(len < 0 || len > MAX_ARG_STRLEN - 1)) {
send_sig(SIGKILL, current, 0); /* NOTE: we set MAX_EXECVE_AUDIT_LEN to a rather arbitrary limit, the
return -1; * 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 { do {
if (len_left > MAX_EXECVE_AUDIT_LEN) /* NOTE: we don't ever want to trust this value for anything
to_send = MAX_EXECVE_AUDIT_LEN; * serious, but the audit record format insists we
else * provide an argument length for really long arguments,
to_send = len_left; * e.g. > MAX_EXECVE_AUDIT_LEN, so we have no choice but
ret = copy_from_user(buf, tmp_p, to_send); * to use strncpy_from_user() to obtain this value for
/* * recording in the log, although we don't use it
* There is no reason for this copy to be short. We just * anywhere here to avoid a double-fetch problem */
* copied them here, and the mm hasn't been exposed to user- if (len_full == 0)
* space yet. len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1;
*/
if (ret) { /* read more data from userspace */
WARN_ON(1); if (require_data) {
send_sig(SIGKILL, current, 0); /* can we make more room in the buffer? */
return -1; if (buf != buf_head) {
} memmove(buf_head, buf, len_buf);
buf[to_send] = '\0'; buf = buf_head;
has_cntl = audit_string_contains_control(buf, to_send); }
if (has_cntl) {
/* /* fetch as much as we can of the argument */
* hex messages get logged as 2 bytes, so we can only len_tmp = strncpy_from_user(&buf_head[len_buf], p,
* send half as much in each message len_max - len_buf);
*/ if (len_tmp == -EFAULT) {
max_execve_audit_len = MAX_EXECVE_AUDIT_LEN / 2; /* unable to copy from userspace */
break; send_sig(SIGKILL, current, 0);
} goto out;
len_left -= to_send; } else if (len_tmp == (len_max - len_buf)) {
tmp_p += to_send; /* buffer is not large enough */
} while (len_left > 0); require_data = true;
/* NOTE: if we are going to span multiple
len_left = len; * buffers force the encoding so we stand
* a chance at a sane len_full value and
if (len > max_execve_audit_len) * consistent record encoding */
too_long = 1; encode = true;
len_full = len_full * 2;
/* rewalk the argument actually logging the message */ p += len_tmp;
for (i = 0; len_left > 0; i++) { } else {
int room_left; require_data = false;
if (!encode)
if (len_left > max_execve_audit_len) encode = audit_string_contains_control(
to_send = max_execve_audit_len; buf, len_tmp);
else /* try to use a trusted value for len_full */
to_send = len_left; if (len_full < len_max)
len_full = (encode ?
/* do we have space left to send this argument in this ab? */ len_tmp * 2 : len_tmp);
room_left = MAX_EXECVE_AUDIT_LEN - arg_num_len - *len_sent; p += len_tmp + 1;
if (has_cntl) }
room_left -= (to_send * 2); len_buf += len_tmp;
else buf_head[len_buf] = '\0';
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;
}
/* /* length of the buffer in the audit record? */
* first record needs to say how long the original string was len_abuf = (encode ? len_buf * 2 : len_buf + 2);
* 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;
} }
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, /* write as much as we can to the audit log */
struct audit_buffer **ab) if (len_buf > 0) {
{ /* NOTE: some magic numbers here - basically if we
int i, len; * can't fit a reasonable amount of data into the
size_t len_sent = 0; * existing audit buffer, flush it and start with
const char __user *p; * a new buffer */
char *buf; 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);
/* /* NOTE: the caller handles the final audit_log_end() call */
* 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;
}
for (i = 0; i < context->execve.argc; i++) { out:
len = audit_log_single_execve_arg(context, ab, i, kfree(buf_head);
&len_sent, p, buf);
if (len <= 0)
break;
p += len;
}
kfree(buf);
} }
static void show_special(struct audit_context *context, int *call_panic) 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 ...@@ -1425,7 +1425,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
if (context->pwd.dentry && context->pwd.mnt) { if (context->pwd.dentry && context->pwd.mnt) {
ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD); ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
if (ab) { if (ab) {
audit_log_d_path(ab, " cwd=", &context->pwd); audit_log_d_path(ab, "cwd=", &context->pwd);
audit_log_end(ab); audit_log_end(ab);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment