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
Former user commented on 2018-05-16T08:08:20.273-0400 (edited 2018-05-16T09:19:06.234-0400):
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#icft=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.
Former user commented on 2019-08-09T09:59:29.595-0400:
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: