Re: [RFC PATCH ghak9 0/3] audit: Record the path of FDs passed to *at(2) syscalls

Ondrej Mosnacek
2018-07-24T14:12:17Z

On Mon, Jul 23, 2018 at 10:49 PM Paul Moore <paul@paul-moore.com> wrote:
> On Fri, Jul 20, 2018 at 6:12 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
> > On Wed, Jul 18, 2018 at 10:41 PM Paul Moore <paul@paul-moore.com> wrote:
> > > On Thu, Jul 12, 2018 at 7:36 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
> > > > This patchset is a prototype implementation of the feature requested in GHAK issue #9 [1]. I decided for a simple auxiliary record with just 2 fields (fd and path) that is emitted whenever we want to record the full path for a file descriptor passed to a syscall (e.g. the dirfd argument of openat(2)). I choose this approach because for some syscalls there is more than one file descriptor we might be interested in (a good example is the renameat(2) syscall).
> > > >
> > > > The motivation for this feature (as I understand it) is to avoid the need to reconstruct the paths corresponding to the file descriptors passed to syscalls, as this might be difficult and time consuming or even impossible in case not all of the right sycalls are being logged. Note that it is always possible to disable these records by simply adding an exclude filter rule matching all records of type FD_PATH.
> > > >
> > > > At this moment I only implement logging for a single syscall (openat(2)) to keep it simple. In the final version I plan to add support for other similar syscalls ()mkdirat, mknodeat, fchownat, ...).
> > > >
> > > > Please let me know if the general approach and the proposed record format make sense to you so I can improve/complete the solution.
> > > >
> > > > Thanks,
> > > > Ondrej
> > > >
> > > > [1] https://github.com/linux-audit/audit-kernel/issues/9
> > >
> > > While I recognize that the GH issue did raise the idea of possibly
> > > creating a new record type, looking at these patches I'm not sure a
> > > new record type is justified, I think reusing the existing PATH record
> > > type would be more beneficial. I recognize that this proposed FD_PATH
> > > record also contains the file descriptor number, but that information
> > > should also be contained in the associated SYSCALL record and arguably
> > > the fd number is only useful if you are logging the SYSCALL
> > > information.
> >
> > To be honest, I'm not sure what is the exact semantic of the PATH
> > record... Is it simply "log information about files that the syscall
> > somehow touched"? Or "log information about any string syscall
> > argument that represents a file path"?
>
> The PATH record exists to record path name information about files
> related to the audit event, depending on the circumstances, both
> relative and absolute paths could be expected. Look at the functions,
> and callers, that add to the audit_context->names_list to get a better
> understanding of where the path name information is collected. I
> doubt you will be surprised at this, but fair warning, it's a bit ugly
> in places.
>
> > In the PATH record samples I
> > have seen, the name=... field sometimes contains just the last segment
> > of the path, other times it contains the full path (huh?).
>
> Depending on the syscall, the syscall arguments, and the "nametype" of
> the PATH record you could see either a full or partial path. In the
> cases of a partial path, there *should* be additional PATH records
> which can help recreate a full path to the file for that particular
> event instance.
>
> > When we log
> > the full path in PATH, then I guess the FD_PATH record would be
> > (almost) redundant, but it seems that whenever openat(2) is called
> > with dirfd != AT_FDCWD, PATH name=... contains just the relative path
> > supplied to the syscall (thus FD_PATH actually provides the missing
> > directory path).
> >
> > So... assuming we would want to provide the missing information in the
> > existing PATH record, how should it look like? Should the name=...
> > field simply always contain the full path? Should there be another
> > PATH record for the directory?
>
> The only real hard requirement is that there be enough to information
> to determine the file full path(s) for any given audit event using
> one, or a combination, of PATH records associated with the event.
>
> > If so, how do we indicate the association between the two PATH records?
>
> Look at how the "nametype" field is used.

OK, so I just wrote a small script to see what PATH records would be
generated for a renameat(2) syscall with non-AT_FDCWD fd arguments,
and it seems the current implementation is not lacking information,
but actually buggy.

strace output:
openat(AT_FDCWD, "/tmp/tmp.CXtBRafonK/a", O_RDONLY|O_PATH|O_DIRECTORY) = 3
openat(AT_FDCWD, "/tmp/tmp.CXtBRafonK/b", O_RDONLY|O_PATH|O_DIRECTORY) = 4
renameat(3, "f", 4, "g") = 0
close(3) = 0
close(4) = 0

Corresponding audit records for renameat():
type=SYSCALL msg=audit(1532439957.660:5): arch=c000003e syscall=264
success=yes exit=0 a0=3 a1=7ffcc364de2a a2=4 a3=7ffcc364de42 items=4
ppid=594 pid=635 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0
sgid=0 fsgid=0 tty=(none) ses=1 comm="trigger-renamea"
exe="/tmp/tmp.GEfuEtn1II/trigger-renameat"
subj=system_u:system_r:kernel_t:s0 key=(null)
type=CWD msg=audit(1532439957.660:5): cwd="/root/Dokumenty/Kernel"
type=PATH msg=audit(1532439957.660:5): item=0
name="/root/Dokumenty/Kernel" inode=2155 dev=00:1a mode=040755 ouid=0
ogid=0 rdev=00:00 obj=system_u:object_r:tmpfs_t:s0 nametype=PARENT
cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=PATH msg=audit(1532439957.660:5): item=1
name="/root/Dokumenty/Kernel" inode=2156 dev=00:1a mode=040755 ouid=0
ogid=0 rdev=00:00 obj=system_u:object_r:tmpfs_t:s0 nametype=PARENT
cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=PATH msg=audit(1532439957.660:5): item=2 name="f" inode=2157
dev=00:1a mode=0100644 ouid=0 ogid=0 rdev=00:00
obj=system_u:object_r:tmpfs_t:s0 nametype=DELETE
cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=PATH msg=audit(1532439957.660:5): item=3 name="g" inode=2157
dev=00:1a mode=0100644 ouid=0 ogid=0 rdev=00:00
obj=system_u:object_r:tmpfs_t:s0 nametype=CREATE
cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=PROCTITLE msg=audit(1532439957.660:5):
proctitle=2F746D702F746D702E4745667545746E3149492F747269676765722D72656E616D656174002F746D702F746D702E4745667545746E3149492F610066002F746D702F746D702E4745667545746E3149492F620067

The PARENT paths are incorrectly reporting the CWD path instead of the
path of the source/destination directories, even though the inode
numbers seem to be correct.

Beyond that, there is really no information in the records that would
allow reconstructing which PARENT path belongs to which CREATE/DELETE
path... (Intuitively you can guess that src will come before dst, but
that is not very reliable.) I think a "parent inode" field in the PATH
records could fix this, but maybe there is a better solution...

I'll see if/how I can fix these issues (especially the first one) and
then I'll get back to the original issue. renameat() and maybe a few
other syscalls should be OK after the fix, but at least openat() will
need some further work (right now it only emits just one PATH record
with only relative path).

> > > Can you explain what advantage the FS_PATH record type has over
> > > reusing the existing PATH record? I know you mention multiple
> > > fd/paths as in the case of renameat(2), but we already have to deal
> > > with this in the non-at rename(2) case.
> >
> > As I said above, I don't fully understand the PATH record so I can't
> > compare them right now. Multiple fd arguments was meant as more of a
> > justification for the presence of the fd=... field in the new FD_PATH
> > record.
>
> I suggest taking some time to better understand how the current file
> path auditing works and then revisiting this patchset.
>
> --
> paul moore
> www.paul-moore.com--
Ondrej Mosnacek <omosnace at redhat dot com>
Associate Software Engineer, Security Technologies
Red Hat, Inc.