[PATCH] Support for auditing on the actions of a not-yet-executed process.
by Peter Moody
eg:
-a exit,always -F arch=b64 -S socket -F 'a0!=1' -F exe=/bin/bash -F success=1
to see instances of /bin/bash opening a non-local socket. Or
-a exit,always -F arch=b64 -S socket -F 'a0!=1' -F exe_children=/bin/bash -F success=1
to instances of /bin/bash, and any descendant processes, opening a non local socket.
proposed https://www.redhat.com/archives/linux-audit/2012-June/msg00002.html
and it seemed like there was interest.
Signed-off-by: Peter Moody <pmoody(a)google.com>
---
trunk/lib/errormsg.h | 2 +-
trunk/lib/fieldtab.h | 2 ++
trunk/lib/libaudit.c | 11 +++++++++++
trunk/lib/libaudit.h | 7 ++++++-
4 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/trunk/lib/errormsg.h b/trunk/lib/errormsg.h
index 4d996d5..cd595ec 100644
--- a/trunk/lib/errormsg.h
+++ b/trunk/lib/errormsg.h
@@ -51,7 +51,7 @@ static const struct msg_tab err_msgtab[] = {
{ -15, 2, "-F unknown errno -"},
{ -16, 2, "-F unknown file type - " },
{ -17, 1, "can only be used with exit and entry filter list" },
- { -18, 1, "" }, // Unused
+ { -18, 1, "only takes = operator" },
{ -19, 0, "Key field needs a watch or syscall given prior to it" },
{ -20, 2, "-F missing value after operation for" },
{ -21, 2, "-F value should be number for" },
diff --git a/trunk/lib/fieldtab.h b/trunk/lib/fieldtab.h
index c0432cc..245b541 100644
--- a/trunk/lib/fieldtab.h
+++ b/trunk/lib/fieldtab.h
@@ -66,3 +66,5 @@ _S(AUDIT_ARG3, "a3" )
_S(AUDIT_FILTERKEY, "key" )
_S(AUDIT_FIELD_COMPARE, "field_compare" )
+_S(AUDIT_EXE, "exe" )
+_S(AUDIT_EXE_CHILDREN, "exe_children" )
diff --git a/trunk/lib/libaudit.c b/trunk/lib/libaudit.c
index 20eaf5f..06eed86 100644
--- a/trunk/lib/libaudit.c
+++ b/trunk/lib/libaudit.c
@@ -1400,6 +1400,17 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
else
return -21;
break;
+ case AUDIT_EXE_CHILDREN:
+ case AUDIT_EXE:
+ {
+ struct stat buf;
+ if ((stat(v, &buf)) < 0)
+ return -2;
+ if (op != AUDIT_EQUAL)
+ return -18;
+ rule->values[rule->field_count] = (unsigned long)buf.st_ino;
+ }
+ break;
case AUDIT_DEVMAJOR...AUDIT_INODE:
case AUDIT_SUCCESS:
if (flags != AUDIT_FILTER_EXIT)
diff --git a/trunk/lib/libaudit.h b/trunk/lib/libaudit.h
index 89dd588..2c8a802 100644
--- a/trunk/lib/libaudit.h
+++ b/trunk/lib/libaudit.h
@@ -243,6 +243,12 @@ extern "C" {
#ifndef AUDIT_FIELD_COMPARE
#define AUDIT_FIELD_COMPARE 111
#endif
+#ifndef AUDIT_EXE
+#define AUDIT_EXE 112
+#endif
+#ifndef AUDIT_EXE_CHILDREN
+#define AUDIT_EXE_CHILDREN 113
+#endif
#ifndef AUDIT_COMPARE_UID_TO_OBJ_UID
#define AUDIT_COMPARE_UID_TO_OBJ_UID 1
@@ -524,4 +530,3 @@ extern void audit_rule_free_data(struct audit_rule_data *rule);
#endif
#endif
-
--
1.7.7.3
10 years, 8 months
[PATCH] audit: audit on the future execution of a binary.
by Peter Moody
This adds the ability audit the actions of a not-yet-running process,
as well as the children of a not-yet-running process.
Signed-off-by: Peter Moody <pmoody(a)google.com>
---
include/linux/audit.h | 2 ++
kernel/auditfilter.c | 6 ++++++
kernel/auditsc.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 55 insertions(+), 0 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 22f292a..5506cb1 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -260,6 +260,8 @@
#define AUDIT_OBJ_UID 109
#define AUDIT_OBJ_GID 110
#define AUDIT_FIELD_COMPARE 111
+#define AUDIT_EXE 112
+#define AUDIT_EXE_CHILDREN 113
#define AUDIT_ARG0 200
#define AUDIT_ARG1 (AUDIT_ARG0+1)
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index a6c3f1a..1e6c571 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -546,6 +546,12 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
if (f->val > AUDIT_MAX_FIELD_COMPARE)
goto exit_free;
break;
+ case AUDIT_EXE:
+ case AUDIT_EXE_CHILDREN:
+ if (f->op != Audit_equal) {
+ goto exit_free;
+ }
+ break;
default:
goto exit_free;
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 4b96415..9cebe95 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -46,6 +46,7 @@
#include <asm/types.h>
#include <linux/atomic.h>
#include <linux/fs.h>
+#include <linux/dcache.h>
#include <linux/namei.h>
#include <linux/mm.h>
#include <linux/export.h>
@@ -68,6 +69,7 @@
#include <linux/capability.h>
#include <linux/fs_struct.h>
#include <linux/compat.h>
+#include <linux/sched.h>
#include "audit.h"
@@ -592,6 +594,35 @@ static int audit_field_compare(struct task_struct *tsk,
return 0;
}
+int audit_match_exe(struct task_struct *tsk, struct audit_field *f)
+{
+ int result = 0;
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+
+ if (!tsk)
+ goto out;
+
+ mm = tsk->mm;
+ if (!mm)
+ goto out;
+
+ down_read(&mm->mmap_sem);
+ vma = mm->mmap;
+ while (vma) {
+ if ((vma->vm_flags & VM_EXECUTABLE) &&
+ vma->vm_file) {
+ struct inode *ino = vma->vm_file->f_path.dentry->d_inode;
+ result = audit_comparator(ino->i_ino, f->op, f->val);
+ break;
+ }
+ vma = vma->vm_next;
+ }
+ up_read(&mm->mmap_sem);
+out:
+ return result;
+}
+
/* Determine if any context name data matches a rule's watch data */
/* Compare a task_struct with an audit_rule. Return 1 on match, 0
* otherwise.
@@ -629,6 +660,22 @@ static int audit_filter_rules(struct task_struct *tsk,
result = audit_comparator(ctx->ppid, f->op, f->val);
}
break;
+ case AUDIT_EXE:
+ result = audit_match_exe(tsk, f);
+ break;
+ case AUDIT_EXE_CHILDREN:
+ {
+ struct task_struct *ptsk;
+ for (ptsk = tsk;
+ ptsk->parent->pid > 0;
+ ptsk = find_task_by_vpid(ptsk->parent->pid)) {
+ if (audit_match_exe(ptsk, f)) {
+ ++result;
+ break;
+ }
+ }
+ }
+ break;
case AUDIT_UID:
result = audit_comparator(cred->uid, f->op, f->val);
break;
--
1.7.7.3
11 years, 3 months
user message limits
by LC Bruzenak
I know I can go look at the code, however I figured I'd ask here first
about the limits on the user message in both audit_log_user_message and
ausearch.
With audit_log_user_message the maximum length allowed appears to be
around MAX_AUDIT_MESSAGE_LENGTH-100. I think it may depend on the
executable name length (and other stuff auto-pushed into the string)
which is why I say "around".
Even when I get a successful return value (from audit_log_user_message),
I don't get my string back out in "ausearch" unless it is WAY smaller -
~1K or less I think.
Any ideas/thoughts?
This is the latest (1.7.11-2) audit package.
Thx,
LCB.
--
LC (Lenny) Bruzenak
lenny(a)magitekltd.com
11 years, 3 months
[PATCH 0/5] Build time disabling of auditd network listener
by Tyler Hicks
Hello Steve - This is a patch set that allows --disable-listener to be passed
to the configure script to disable the auditd network listener code at build
time. The reasoning is that a large number of users do not need centralized
audit logging and removing the network listening code from a root-owned auditd
process is appealing from a security perspective.
The existing implementation clearly does not initialize the listener when
tcp_listen_port is undefined in auditd.conf, but I still think there is value
in not having the listening code present in all auditd installations.
The first three patches in the set are refactoring patches to move nearly all of
the listening code into auditd-listen.c in order to minimize the number of
ifdefs that would need to be scattered throughout C source files. The fourth
patch is an optional cleanup patch. The last patch introduces the
--disable-listener option.
The auditd listener code is still enabled by default so that existing distro
packaging recipes will not need to be updated.
I look forward to your feedback. Thanks!
Tyler
12 years, 1 month
linux-audit: reconstruct path names from syscall events?
by John Feuerstein
Hi,
I would like to audit all changes to a directory tree using the linux
auditing system[1].
# auditctl -a exit,always -F dir=/etc/ -F perm=wa
It seems like the GNU coreutils are enough to break the audit trail.
The resulting SYSCALL events provide CWD and multiple PATH records,
depending on the syscall. If one of the PATH records is relative, I can
reconstruct the absolute path using the CWD record.
However, that does not work for the whole *at syscall family
(unlinkat(2), renameat(2), linkat(2), ...); accepting paths relative to
a given directory file descriptor. GNU coreutils are prominent users,
for example "rm -r" making use of unlinkat(2) to prevent races.
Things like dup(2) and fd passing via unix domain sockets come to mind.
It's the same old story again: mapping fds to path names is ambiguous at
best, if not impossible.
I wonder why such incomplete file system auditing rules are considered
sufficient in the CAPP/LSPP/NISPOM/STIG rulesets?
Here's a simplified example:
$ cd /tmp
$ mkdir dir
$ touch dir/file
$ ls -ldi /tmp /tmp/dir /tmp/dir/file
2057 drwxrwxrwt 9 root root 380 Sep 17 00:02 /tmp
58781 drwxr-xr-x 2 john john 40 Sep 17 00:02 /tmp/dir
56228 -rw-r--r-- 1 john john 0 Sep 17 00:02 /tmp/dir/file
$ cat > unlinkat.c
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int dirfd = open("dir", O_RDONLY);
unlinkat(dirfd, "file", 0);
return 0;
}
^D
$ make unlinkat
cc unlinkat.c -o unlinkat
$ sudo autrace ./unlinkat
Waiting to execute: ./unlinkat
Cleaning up...
Trace complete. You can locate the records with 'ausearch -i -p 32121'
$ ls -li dir
total 0
Now, looking at the resulting raw SYSCALL event for unlinkat(2):
type=SYSCALL msg=audit(1316210542.899:779): arch=c000003e syscall=263 success=yes exit=0 a0=3 a1=400690 a2=0 a3=0 items=2 ppid=32106 pid=32121 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts12 ses=36 comm="unlinkat" exe="/tmp/unlinkat" key=(null)
type=CWD msg=audit(1316210542.899:779): cwd="/tmp"
type=PATH msg=audit(1316210542.899:779): item=0 name="/tmp" inode=58781 dev=00:0e mode=040755 ouid=1000 ogid=1000 rdev=00:00
type=PATH msg=audit(1316210542.899:779): item=1 name="file" inode=56228 dev=00:0e mode=0100644 ouid=1000 ogid=1000 rdev=00:00
type=EOE msg=audit(1316210542.899:779):
- From this event alone, there's no way to answer "Who unlinked
/tmp/dir/file?". For what it's worth, the provided path names would be
exactly the same if we had unlinked "/tmp/dir/dir/dir/dir/dir/file".
- PATH item 0 reports the inode of "/tmp/dir" (58781, see ls output
above), however, the reported path name is "/tmp" (bug?).
In this example I've used autrace, which traces everything, so I could
possibly search for a previous open(2) of inode 58781. And indeed, there
it is:
type=SYSCALL msg=audit(1316210542.899:778): arch=c000003e syscall=2 success=yes exit=3 a0=40068c a1=0 a2=7fff22724fc8 a3=0 items=1 ppid=32106 pid=32121 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts12 ses=36 comm="unlinkat" exe="/tmp/unlinkat" key=(null)
type=CWD msg=audit(1316210542.899:778): cwd="/tmp"
type=PATH msg=audit(1316210542.899:778): item=0 name="dir" inode=58781 dev=00:0e mode=040755 ouid=1000 ogid=1000 rdev=00:00
type=EOE msg=audit(1316210542.899:778):
Great, so inode 58781 was opened using "/tmp/dir", and therefore, the relative
path "file" given to unlinkat(2) above could possibly translate to
"/tmp/dir/path"... not really feeling confident here.
- All file system auditing rules in various rulesets and the examples in
the documentation add the "-F perm=wa" (or similar) filter, so the
open(2) wouldn't even make it into the audit trail.
- If you can handle the volume and log all open(2), what happens if the
open(2) was done hours, days, weeks, ... ago?
- What if the open(2) was done by another process which passed the fd
on a unix domain socket?
It looks like the kernel auditing code should provide
... item=0 name="/tmp/dir" inode=58781 ...
in the unlinkat(2) syscall event above. Looking up the unlinkat(2)
documentation:
int unlinkat(int dirfd, const char *pathname, int flags);
If the pathname given in pathname is relative, then it is
interpreted relative to the directory referred to by the file
descriptor dirfd (rather than relative to the current working
directory of the calling process, as is done by unlink(2) and
rmdir(2) for a relative pathname).
If the pathname given in pathname is relative and dirfd is the
special value AT_FDCWD, then pathname is interpreted relative
to the current working directory of the calling process (like
unlink(2) and rmdir(2)).
As you might see, there's not only the fd->pathname problem, but
also the special case for AT_FDCWD. In this case the kernel side should
probably just duplicate CWD's path name into item 0's path name. But
that's just unlinkat(2), there are a lot more.
What am I missing here? Is there no way to audit a directory tree?
I've looked at alternatives: Inotify watches won't scale to big trees
and events lack so much detail that they can't be used for auditing.
Fanotify, while providing the pid, still lacks a lot of events and
passes fds; the example code relies on readlink("/proc/self/fd/...").
Thanks,
John
[1] http://people.redhat.com/sgrubb/audit/
--
John Feuerstein <john(a)feurix.com>
12 years, 2 months
[PATCH] audit: grab a reference to context->pwd when it's cached
by Peter Moody
On certain systems, in certain pathalogical cases, current's cwd can
be deleted while we're still processing a syscall. This should prevent
the system from evicting the inode while we're still referencing it.
This seems to fix the bug I reported here:
https://www.redhat.com/archives/linux-audit/2012-August/msg00017.html
Signed-off-by: Peter Moody <pmoody(a)google.com>
---
kernel/auditsc.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 4b96415..e86b8b9 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2064,6 +2064,7 @@ void __audit_getname(const char *name)
if (!context->pwd.dentry)
get_fs_pwd(current->fs, &context->pwd);
+ path_get(&context->pwd);
}
/* audit_putname - intercept a putname request
@@ -2091,6 +2092,7 @@ void audit_putname(const char *name)
n->name, n->name ?: "(null)");
}
#endif
+ path_put(&context->pwd);
__putname(name);
}
#if AUDIT_DEBUG
--
1.7.7.3
12 years, 2 months
max number of rules?
by Peter Moody
Does anyone know the number of audit rules that can be installed on a
system before having to traverse the list of rules on every syscall
starts to take a noticeable amount of time? I'm assuming no rules that
generate excessive logs, so nothing like '-a exit,always -S execve' or
'-a exit,always -S open'.
Cheers,
peter
--
Peter Moody Google 1.650.253.7306
Security Engineer pgp:0xC3410038
12 years, 4 months
[PATCH] auditctl Running In QEMU
by Nathaniel Husted
The current stable version of ARM will not run in QEMU and errors with
an "unknown machine type" error. The following patch adds two machine
times to the ARMEB machine architecture. "armv5tejl" is the type
returned by uname -m when running the versatilepb machine type with a
default CPU (under Debian Squeeze 6.0). "armv7l" is return by uname -m
when run on a Samgsung Galaxy S SGH-I897 phone.
Signed-off by: Nathaniel Husted <nhusted(a)gmail.com>
diff -rpuN audit-2.2.1/lib/machinetab.h audit-2.2.1-patched/lib/machinetab.h
--- audit-2.2.1/lib/machinetab.h 2012-03-23 08:42:44.000000000 -0400
+++ audit-2.2.1-patched/lib/machinetab.h 2012-06-15 16:18:33.069787303 -0400
@@ -35,4 +35,6 @@ _S(MACH_ALPHA, "alpha" )
#endif
#ifdef WITH_ARMEB
_S(MACH_ARMEB, "armeb" )
+_S(MACH_ARMEB, "armv5tejl")
+_S(MACH_ARMEB, "armv7l")
#endif
12 years, 4 months
[PATCH] Audit Compilation on QEMU/Debian Squeeze
by Nathaniel Husted
The current version of audit has issues compiling (Debian Squeeze --
http://www.raspberrypi.org/downloads). The compiler complains about
being unable to find SYS_RECVMMSG. The following patch solves this
issue by removing potential problematic system calls from being
compiled in when using the WITH_ARMEB directive. The patch was made
against the latest stable release (audit 2.2.1) and tested under QEMU
ARM with Debian Squeeze 6.0.
Signed-off by: Nathaniel Husted <nhusted(a)gmail.com>
diff -rpuN audit-2.2.1/auparse/socktab.h audit-2.2.1-patched/auparse/socktab.h
--- audit-2.2.1/auparse/socktab.h 2012-03-23 08:42:42.000000000 -0400
+++ audit-2.2.1-patched/auparse/socktab.h 2012-06-15 16:13:12.470054242 -0400
@@ -39,6 +39,8 @@ _S(SYS_GETSOCKOPT, "getsockopt" )
_S(SYS_SENDMSG, "sendmsg" )
_S(SYS_RECVMSG, "recvmsg" )
_S(SYS_ACCEPT4, "accept4" )
+#ifndef WITH_ARMEB
_S(SYS_RECVMMSG, "recvmmsg" )
_S(20, "sendmmsg" )
+#endif
diff -rpuN audit-2.2.1/src/ausearch-lookup.c
audit-2.2.1-patched/src/ausearch-lookup.c
--- audit-2.2.1/src/ausearch-lookup.c 2012-03-23 08:42:41.000000000 -0400
+++ audit-2.2.1-patched/src/ausearch-lookup.c 2012-06-15
16:12:41.839801930 -0400
@@ -116,7 +116,9 @@ static struct nv_pair socktab[] = {
{SYS_SENDMSG, "sendmsg"},
{SYS_RECVMSG, "recvmsg"},
{SYS_ACCEPT4, "accept4"},
+ #ifndef WITH_ARMEB
{SYS_RECVMMSG, "recvmmsg"},
+ #endif
{20, "sendmmsg"}
};
#define SOCK_NAMES (sizeof(socktab)/sizeof(socktab[0]))
12 years, 4 months
auparse delayed event emittance
by Giang Nguyen
>> - Do messages from different events ever get intermixed in the
>> output via audispd? And hence I need to cater for multiple simultaneous
>> events streaming in?
>
> Yes. This is a big problem. About 2 years ago I fixed this in ausearch/report.
> I started to fix this in libauparse but then I remembered it has this state
> machine in it to deal with the feed interface. I didn't write that code so it
> will take some time for me to figure out what it doing before fixing this
> problem. But basically you need a list of lists where each list is a
> collection of records that form one event.
Another, perhaps related, "issue" I have noticed is that libauparse
(auparse_next_event()), except for some known single-record events,
relies on a subsequent event coming in to detect that the current
accumulating event is complete. It compares and sees a different event
time stamp/serial than the one currently being accumulated. So, if I
have this sequence:
event1, record1 (time stamp = X)
event1, record2
// 10 seconds elapsed
event2 (time stamp = Y)
then libauparse won't call my auparse_callback() to notify me of
event1 until 10 seconds after event1 happened.
For my purpose, this delay is not ideal.
I looked into using auparse_flush_feed() after every auparse_feed(),
but it wouldn't work because that would mean I would get called back
for every record, not a complete event.
One possible fix is to make auparse know more about each different
type of event, perhaps as told by the application via some additional
API.
Do you think this design has any chance?
In the mean time, I guess I would do my own buffering/parsing on top
of auparse_feed() and tell it to auparse_flush_feed() when I know I
have a complete event. But this seems clunky, too.
Thanks.
12 years, 4 months