+ audit-rework-execve-audit.patch added to -mm tree
by akpm@linux-foundation.org
The patch titled
audit: rework execve audit
has been added to the -mm tree. Its filename is
audit-rework-execve-audit.patch
*** Remember to use Documentation/SubmitChecklist when testing your code ***
See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find
out what to do about this
------------------------------------------------------
Subject: audit: rework execve audit
From: Peter Zijlstra <a.p.zijlstra(a)chello.nl>
The purpose of audit_bprm() is to log the argv array to a userspace daemon at
the end of the execve system call. Since user-space hasn't had time to run,
this array is still in pristine state on the process' stack; so no need to
copy it, we can just grab it from there.
In order to minimize the damage to audit_log_*() copy each string into a
temporary kernel buffer first.
Currently the audit code requires that the full argument vector fits in a
single packet. So currently it does clip the argv size to a (sysctl) limit,
but only when execve auditing is enabled.
If the audit protocol gets extended to allow for multiple packets this check
can be removed.
Signed-off-by: Peter Zijlstra <a.p.zijlstra(a)chello.nl>
Signed-off-by: Ollie Wild <aaw(a)google.com>
Cc: Al Viro <viro(a)zeniv.linux.org.uk>
Cc: <linux-audit(a)redhat.com>
Signed-off-by: Andrew Morton <akpm(a)linux-foundation.org>
---
fs/exec.c | 3 +
include/linux/binfmts.h | 1
kernel/auditsc.c | 76 +++++++++++++++++++++++++++-----------
kernel/sysctl.c | 11 +++++
4 files changed, 70 insertions(+), 21 deletions(-)
diff -puN fs/exec.c~audit-rework-execve-audit fs/exec.c
--- a/fs/exec.c~audit-rework-execve-audit
+++ a/fs/exec.c
@@ -1154,6 +1154,7 @@ int do_execve(char * filename,
{
struct linux_binprm *bprm;
struct file *file;
+ unsigned long tmp;
int retval;
int i;
@@ -1208,9 +1209,11 @@ int do_execve(char * filename,
if (retval < 0)
goto out;
+ tmp = bprm->p;
retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
goto out;
+ bprm->argv_len = tmp - bprm->p;
retval = search_binary_handler(bprm,regs);
if (retval >= 0) {
diff -puN include/linux/binfmts.h~audit-rework-execve-audit include/linux/binfmts.h
--- a/include/linux/binfmts.h~audit-rework-execve-audit
+++ a/include/linux/binfmts.h
@@ -40,6 +40,7 @@ struct linux_binprm{
unsigned interp_flags;
unsigned interp_data;
unsigned long loader, exec;
+ unsigned long argv_len;
};
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
diff -puN kernel/auditsc.c~audit-rework-execve-audit kernel/auditsc.c
--- a/kernel/auditsc.c~audit-rework-execve-audit
+++ a/kernel/auditsc.c
@@ -156,7 +156,7 @@ struct audit_aux_data_execve {
struct audit_aux_data d;
int argc;
int envc;
- char mem[0];
+ struct mm_struct *mm;
};
struct audit_aux_data_socketcall {
@@ -834,6 +834,47 @@ static int audit_log_pid_context(struct
return rc;
}
+static void audit_log_execve_info(struct audit_buffer *ab,
+ struct audit_aux_data_execve *axi)
+{
+ int i;
+ long len;
+ const char __user *p = (const char __user *)axi->mm->arg_start;
+
+ if (axi->mm != current->mm)
+ return; /* execve failed, no additional info */
+
+ for (i = 0; i < axi->argc; i++, p += len) {
+ long ret;
+ char *tmp;
+
+ len = strnlen_user(p, MAX_ARG_PAGES*PAGE_SIZE);
+ /*
+ * We just created this mm, if we can't find the strings
+ * we just copied in something is _very_ wrong.
+ */
+ BUG_ON(!len);
+
+ tmp = kmalloc(len, GFP_KERNEL);
+ if (!tmp) {
+ audit_panic("out of memory for argv string\n");
+ break;
+ }
+
+ ret = copy_from_user(tmp, p, len);
+ /*
+ * There is no reason for this copy to be short.
+ */
+ BUG_ON(ret);
+
+ audit_log_format(ab, "a%d=", i);
+ audit_log_untrustedstring(ab, tmp);
+ audit_log_format(ab, "\n");
+
+ kfree(tmp);
+ }
+}
+
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
{
int i, call_panic = 0;
@@ -974,13 +1015,7 @@ static void audit_log_exit(struct audit_
case AUDIT_EXECVE: {
struct audit_aux_data_execve *axi = (void *)aux;
- int i;
- const char *p;
- for (i = 0, p = axi->mem; i < axi->argc; i++) {
- audit_log_format(ab, "a%d=", i);
- p = audit_log_untrustedstring(ab, p);
- audit_log_format(ab, "\n");
- }
+ audit_log_execve_info(ab, axi);
break; }
case AUDIT_SOCKETCALL: {
@@ -1824,32 +1859,31 @@ int __audit_ipc_set_perm(unsigned long q
return 0;
}
+int audit_argv_kb = 32;
+
int audit_bprm(struct linux_binprm *bprm)
{
struct audit_aux_data_execve *ax;
struct audit_context *context = current->audit_context;
- unsigned long p, next;
- void *to;
if (likely(!audit_enabled || !context || context->dummy))
return 0;
- ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p,
- GFP_KERNEL);
+ /*
+ * Even though the stack code doesn't limit the arg+env size any more,
+ * the audit code requires that _all_ arguments be logged in a single
+ * netlink skb. Hence cap it :-(
+ */
+ if (bprm->argv_len > (audit_argv_kb << 10))
+ return -E2BIG;
+
+ ax = kmalloc(sizeof(*ax), GFP_KERNEL);
if (!ax)
return -ENOMEM;
ax->argc = bprm->argc;
ax->envc = bprm->envc;
- for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) {
- struct page *page = bprm->page[p / PAGE_SIZE];
- void *kaddr = kmap(page);
- next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1);
- memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p);
- to += next - p;
- kunmap(page);
- }
-
+ ax->mm = bprm->mm;
ax->d.type = AUDIT_EXECVE;
ax->d.next = context->aux;
context->aux = (void *)ax;
diff -puN kernel/sysctl.c~audit-rework-execve-audit kernel/sysctl.c
--- a/kernel/sysctl.c~audit-rework-execve-audit
+++ a/kernel/sysctl.c
@@ -81,6 +81,7 @@ extern int percpu_pagelist_fraction;
extern int compat_log;
extern int maps_protect;
extern int sysctl_stat_interval;
+extern int audit_argv_kb;
/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
static int maxolduid = 65535;
@@ -266,6 +267,16 @@ static ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+#ifdef CONFIG_AUDITSYSCALL
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "audit_argv_kb",
+ .data = &audit_argv_kb,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+#endif
{
.ctl_name = KERN_CORE_USES_PID,
.procname = "core_uses_pid",
_
Patches currently in -mm which might be from a.p.zijlstra(a)chello.nl are
lumpy-reclaim-v4.patch
split-mmap.patch
only-allow-nonlinear-vmas-for-ram-backed-filesystems.patch
percpu_counters-use-cpu-notifiers.patch
percpu_counters-use-for_each_online_cpu.patch
arch-personality-independent-stack-top.patch
audit-rework-execve-audit.patch
audit-rework-execve-audit-fix.patch
mm-move_page_tables_up.patch
mm-variable-length-argument-support.patch
fix-raw_spinlock_t-vs-lockdep.patch
lockdep-sanitise-config_prove_locking.patch
lockdep-reduce-the-ifdeffery.patch
lockstat-core-infrastructure.patch
lockstat-core-infrastructure-fix.patch
lockstat-core-infrastructure-fix-fix.patch
lockstat-human-readability-tweaks.patch
lockstat-hook-into-spinlock_t-rwlock_t-rwsem-and-mutex.patch
17 years, 6 months
[RFC] TTY auditing
by Miloslav Trmac
Hello,
the attached patches propose a way to audit administrative commands.
Summary
-------
A per-process "audit TTY input" attribute is added. The attribute is
inherited across fork (). A new PAM module is used to turn the
attribute on or off on login. Data read from TTYs by processes with the
attribute is sent to the audit subsystem by the kernel. Optionally,
user-space applications can send advisory audit events describing the
"meaning" of the TTY input.
Fundamental limitations
-----------------------
Only TTY input is logged, so an administrator may execute unknown code
by downloading shell scripts over the network. The act of downloading
the shell script would be audited, however.
For GUI or a complex TUI applications (e.g. emacs or mc), auditing the
TTY input probably does not save enough information to reproduce the
sequence of executed commands. If necessary, these applications may be
extended to send advisory audit events. (Any approach to administrative
action auditing would have to extend these applications).
Why auditing needs to be done by the kernel
-------------------------------------------
If system call auditing is not an option, there are simply too many
applications that can be used to perform non-trivial administrative
tasks that would have to be extended. All shells, most programming
language interpreters, awk, m4, ... . In the worst case, the user might
be using a proprietary shell. The system should also be able to handle
at least the trivial workarounds like (cat | sh).
So, if we can't audit the program actions (system calls), and we can't
in general modify the programs themselves, the only remaining option is
to audit the inputs to the programs - TTY input.
This could be done in user-space by running all administrative sessions
in a pseudo-TTY and auditing the data sent to the pseudo-TTY.
Unfortunately that's not transparent enough, and changes behavior (after
logging on to a text console, /dev/stdin is not a VT and can't be used
to send VT ioctls - for a simple example, see /etc/profile.d/lang.sh on
Fedora/RHEL).
Auditing processes, not TTYs
----------------------------
If actions of ordinary users are not audited, after (su -) there are
both administrative and non-administrative processes with the TTY open.
The answer to the question "should this particular byte of input to
the TTY be audited" depends on whether the byte is processed by an
"administrative process", not on whether the TTY is /dev/tty1 or a PTY
representing a ssh connection, or on whether an administrative process
has ever been executed on the TTY since last hangup.
Audit event generation based on a process-inherited flag has one
additional advantage: If root within a (su -) session runs (su -
unprivileged user), root's actions as the unprivileged user are audited.
A potential problem with is approach is unwanted auditing of TTY input
to system daemons run (or restarted) by an administrator; if the
administrator restarts an *getty daemon, all inputs to the daemon would
be audited. As a special hack, opening a TTY in a process that has no
TTY currently open automatically disables the "audit TTY input" flag.
Closing the current TTY and opening another one does not really make any
sense in a regular application, but daemons which close all file
descriptors on startup would be handled by the hack. If the hack
doesn't handle a specific daemon automatically, the daemon could either
be modified to disable auditing, or its startup scripts could explicitly
close TTYs to activate the hack.
Semantics of the logged data
----------------------------
The data is not logged byte by byte; a per-process buffer of data to be
audited is kept, collecting the characters as they are read by the
application. The contents of the buffer are audited if:
- the buffer is full
- ICANON is enabled and an EOL or EOF character is "delivered" to the
application ("delivering EOF" doesn't actually provide any bytes)
- ICANON is enabled or disabled
- auditing TTY input is disabled for the process
- the process exits
- the process sends an advisory TTY input audit event.
Thus, for applications using ICANON, input is audited line by line. For
applications not using ICANON (e.g. uses readline), it is audited in
blocks of N_TTY_BUF bytes. If the application is not using ICANON, it
may send advisory messages; in that case, each "command" is audited
using both the kernel's audit events containing the exact tty input
(e.g. C-r up RET) and the advisory message (e.g. "yum upgrade"), and the
raw input is always audited before the advisory messages.
As a special case, input read when the TTY is using ICANON without ECHO
is _not_ audited, to avoid storing passwords in the audit log. On the
other hand, non-ICANON input is always audited (e.g. vim/emacs/mc input)
in full. Note that passwords may still be audited if they are echoed,
e.g. when sending CREATE USER commands to a SQL server.
Attached code
-------------
- a kernel patch, against a current-ish Linux tree.
- a patch against audit-1.5.3 to recognize the new netlink message types
- a PAM module which allows enabling/disabling TTY auditing on login
- a patch against readline-5.2 to generate advisory audit events on
returned strings. The exact same patch can be used for the readline
copy included in bash-3.2.
Unresolved questions:
---------------------
The advisory audit events may be emitted by any process for which TTY
input is audited, no additional privileges are necessary. Is it
necessary to separately limit the rate of the generated events, or is
the current kernel rate limit sufficient?
Reading and modifying the "audit TTY input" attribute using a the audit
netlink socket works, but it feels unnatural. Should it be done
differently (e.g. /proc, prctl ())? Is it enough to allow an
administrative process to read/modify its own "audit TTY input"
attribute, or is it necessary to access the attribute of other processes?
I'll be grateful for any comments.
Mirek
17 years, 7 months