OS-6951: lx: audit PATH records


Issue Type:Bug
Priority:4 - Normal
Created at:2018-05-14T12:22:12.913Z
Updated at:2020-01-21T21:35:09.269Z


Created by:Former user
Reported by:Former user

Related Issues




This ticket encompasses the work to emit AUDIT_PATH (1302) records out to the auditd. This includes adding these events to any syscalls which require them, even if there is no file system watch. For example, an audit event for the open syscall should also emit an AUDIT_PATH record for the file being opened. A PATH record looks like this:

type=PATH msg=audit(1525901041.051:3730): item=0 name="/etc/audit/audit.rules" inode=287783 dev=fd:01 mode=0100640 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL


Comment by Former user
Created at 2018-05-16T12:08:20.273Z
Updated at 2018-05-16T13:19:06.234Z

Here is the TL;DR summary for this work

1) If syscall fails, emit CWD record and nothing else (for now)
2) Special case for chdir syscall, record cwd in lwp on entry, then free on return, otherwise just getcwd when emitting records
3) Use flags on syscall to copyin the path arg(s) and format/emit PATH record
4) For the "nametype", most of this can be inferred from the syscall.
   a) open and openat need special handling
   b) for now, if create flag is set, just call it a CREATE

The initial work for OS-6212 only emits a single SYSCALL audit record for the syscall itself, however Linux emits a set of multiple records when a syscall audit event occurs.

This ticket is focused on emitting the CWD and PATH records for a syscall audit event. There are other records which Linux will emit, but those can be handled by other tickets.

Note that all of the audit records emitted in a set must have the same timestamp and serial number. This is how records can be grouped into a single event.

All syscall events need to emit a CWD record. This is fairly simple since we can get the CWD when we're emitted the audit records. A CWD record looks like this

type=CWD msg=audit(1526471369.163:42901):  cwd="/home/jerry"

Note that there is one complexity with CWD, which is that represents the directory when the syscall starts. Thus, for a chdir sycall we need to record original CWD at the start of the syscall. For efficiency, we can only do this when auditing is in use and only for the chdir syscall. We can save the CWD in the lx brand LWP data and free it at the end of the syscall. All other syscalls could simply get the CWD when the set of audit records are being emitted.

The following list of syscalls will emit one or more PATH audit records. My design for handling this is that we use some bits from the sy_flags member of the lx_sysent_t structure to indicate which audit records to emit, then update the lx_sysent64 and lx_sysent32 syscall tables to flag the relevant syscalls. We need 3 bits to indicate which arguments to the syscall need audit records:

0x010   LX_AUDIT_PATH0
0x020   LX_AUDIT_PATH1
0x030   LX_AUDIT_PATH01
0x040   LX_AUDIT_PATH02
0x050   LX_AUDIT_PATH13

For example, the open syscall needs a PATH record emitted for argument 0, so we will tag that syscall with LX_AUDIT_PATH0. The symlink syscall needs 2 PATH records emitted, one for argument 0 and one for argument 2, so we will tag that syscall with LX_AUDIT_PATH02. Similarly, the renameat syscall needs 2 PATH records, one for argument 1 and one for argument 3, so it is tagged with LX_AUDIT_PATH13.

Here is the full list of syscalls which need PARENT and PATH records:

open          LX_AUDIT_PATH0
stat          LX_AUDIT_PATH0
lstat         LX_AUDIT_PATH0
access       LX_AUDIT_PATH0
execve       LX_AUDIT_PATH0          (item 0 is exec pathname)
truncate     LX_AUDIT_PATH0
chdir        LX_AUDIT_PATH0          (cwd is original dir, item 0 new dir)
rename       LX_AUDIT_PATH01
mkdir        LX_AUDIT_PATH0
rmdir        LX_AUDIT_PATH0
creat        LX_AUDIT_PATH0
link         LX_AUDIT_PATH01
unlink       LX_AUDIT_PATH0
symlink      LX_AUDIT_PATH01     (item 0 is target, item 2 is symlink name)
readlink     LX_AUDIT_PATH0
chmod        LX_AUDIT_PATH0
chown        LX_AUDIT_PATH0
lchown       LX_AUDIT_PATH0
chroot          LX_AUDIT_PATH0
mknod       LX_AUDIT_PATH0
statfs      LX_AUDIT_PATH0
mount       LX_AUDIT_PATH1          (path record is target mount point)
umount          LX_AUDIT_PATH0          (32-bit only)
umount2         LX_AUDIT_PATH0          (path record is mount point)
setxattr    LX_AUDIT_PATH0
lsetxattr   LX_AUDIT_PATH0
getxattr        LX_AUDIT_PATH0
lgetxattr       LX_AUDIT_PATH0
listxattr       LX_AUDIT_PATH0
llistxattr      LX_AUDIT_PATH0
removexattr     LX_AUDIT_PATH0
lremovexattr    LX_AUDIT_PATH0
openat      LX_AUDIT_PATH1
mkdirat     LX_AUDIT_PATH1
mknodat     LX_AUDIT_PATH1
fchownat    LX_AUDIT_PATH1
fstatat     LX_AUDIT_PATH1
unlinkat    LX_AUDIT_PATH1
renameat    LX_AUDIT_PATH13
linkat      LX_AUDIT_PATH13
symlinkat       LX_AUDIT_PATH02
readlinkat      LX_AUDIT_PATH1
fchmodat    LX_AUDIT_PATH1
faccessat   LX_AUDIT_PATH1

When we're emitting a SYSCALL record, we can check the syscall table and if it is tagged with any of these flags, we can emit the appropriate additional audit records.

The way that Linux emits the PATH records should be easy to handle. The record is simply the string as passed in to the syscall, so there is no work needed to resolve the string to the real path. For example, if the path string is "foo", then "foo" is used in the record, if it is "../mydir/foo", then that string is used, as so on. For each filetype PATH record, there is also an associated PARENT record. This is equivalent to the dirname on the path. So, for the path string "./foo", the PARENT record would be "./", or for the path string "../mydir/foo", the PARENT record is "../mydir/". When only a bare path string is used, then the PARENT record is the CWD, so a path string of "foo" would get an associated PARENT record populated with the CWD.

Here is an example of the two PATH records emitted from an open syscall audit event:

type=PATH msg=audit(1526475236.547:43359): item=0 name="/var/log/audit/" inode=369669 dev=fd:01 mode=040750 ouid=0 ogid=0 rdev=00:00 nametype=PARENT
type=PATH msg=audit(1526475236.547:43359): item=1 name="/var/log/audit/.audit.log.swp" inode=287786 dev=fd:01 mode=0100600 ouid=0 ogid=0 rdev=00:00 nametype=CREATE

Note that the first record is tagged as the PARENT and the second record is the actual file. Also note that the item field increments, so if we had 4 audit records (e.g. from rename) then there would be 4 path records with item values 0-3.

When we have to emit PATH records, we can copyin the relevant syscall parameters and format the records using that string data. The first 4 syscall arguments are already being saved in br_syscall_args in the brand LWP struct as part of the work for OS-6212.

The nametype on the audit records is either PARENT or one of; NORMAL, CREATE, DELETE or UNKNOWN. A NORMAL record is just emitted when accessing an existing file. CREATE or DELETE is emitted when the file is being created or removed. In general, the nametype is obvious from the syscall. For example, if the audit record is for the rename syscall, the old path will have a DELETE record and the new path will have a CREATE record.

There is one complexity with the open and openat syscalls since the nametype could be either NORMAL or CREATE. It is not easy for us to handle this at audit record time since the actual determination of create vs. normal is embedded down in the various file systems. To avoid having to stat the path ahead of performing the open syscall (which may not even need an audit record), it seems reasonable to simply emit a CREATE record if the O_CREAT flag is set on the syscall, otherwise emit a NORMAL record. We could revisit this later if it becomes important.

When the syscall fails, Linux can emit some PATH records, but it varies on which records are emitted, and there is not always enough records to actually reconstruct what the syscall arguments were. For now, it seems reasonable for us to simply not emit PATH records if the syscall fails. Again, we could revisit this later if it becomes important.

Comment by Former user
Created at 2019-08-09T13:59:29.595Z

While cleaning up workspaces, I found that I had started work on this about a year ago.  I don't know if the work that I started will be useful should I or someone else pick it up or not.  Attaching just in case: OS-6951.patch