Post by Steve Grubb Post by John Feuerstein
I would like to audit all changes to a directory tree using the linux
# auditctl -a exit,always -F dir=/etc/ -F perm=wa
It seems like the GNU coreutils are enough to break the audit trail.
I was hoping one of the kernel developers would have got involved with this question.
I pointed out the same problem as you maybe 5 years ago. The people working on it at
the time said that if you really want to know, just add events for opens and then you
can piece it together. In my opinion, that is avoiding the problem and not solving it.
There are way too many opens to put into an audit trail on the odd chance that you
might have needed one. In 5 years, the kernel has changed and so have the people
working on the code. Maybe this problem should be revisited.
Howdy. Kernel developer here.
The problem goes way back. Way, way back. I will do my
best to describe what is going on and why the kernel has
such a problem with pathnames and audit. I am afraid that
you may not be happy with the explanation, but I also
think that you should understand what is going on and why
it has been so difficult to get a satisfactory resolution.
The Linux (and UNIX before it) kernel does not have an
internal concept of a path. Pathname resolution is provided
for the convenience of user space code.
The kernel has a simple view of filesystem objects. They
are inodes and datablocks. So long as there is a name for
the inode somewhere on the system the object is retained,
and once all the names are gone it is expunged. There are
two kinds of names; open file descriptors and directory
entries. A directory entry contains exactly one component
of a pathname. You are not allowed to remove directories
unless they are empty because that would leave objects with
names in an inaccessible state.
The Linux filesystem semantics, inherited in all their
glory from UNIX, permit multiple directory entries to
refer to the same inode. That means that there can be
multiple names for the same object in the filesystem
name space. These names are all peers. None is the "real"
name of the object. The only possible real name for the
object is the inode number (combined with an identification
of the containing filesystem). This identifies the object
even when all entries in the filesystem namespace are
gone but the file is open. Auditible event can occur on
files that are open but have not filesystem entries.
It's a big mess because the auditor obviously wants to
know the name of the file, but it is entirely possible
that there are hundreds of names in the filesystem space
for the object and that there are hundreds of open file
descriptors for the object, none of which were created
by opening pathnames that refer to that object any longer.
The kernel can keep track of the path used to reach an
inode, but with hard links, symlinks, mount points and
namespaces the reality is that you can't identify the
object involved using that information. The best that
can be done is to record the pathname requested, the
pathname resolved, and the inode number. It is impossible
to track objects by pathname because the pathname is
not a kernel concept.
It's been this way forever. UNIX audit systems had/have
the exact same problem. This is why we have AppArmor and
TOMOYO. Unless someone smarter than I am has an outstanding
insight we aren't going to make you happy any time soon.
Post by Steve Grubb
Post by John Feuerstein
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?
$ 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
int main(int argc, char **argv)
int dirfd = open("dir", O_RDONLY);
unlinkat(dirfd, "file", 0);
$ make unlinkat
cc unlinkat.c -o unlinkat
$ sudo autrace ./unlinkat
Waiting to execute: ./unlinkat
Trace complete. You can locate the records with 'ausearch -i -p 32121'
$ ls -li dir
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
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