Hello,
:: TERMINOLOGY ::
watch : data that describes a file or directory that should be audited
watchlist : a linked list of watchlist entries residing on a directory
watchlist entry (wentry): an entry to a watchlist that contains a watch
:: INTRODUCTION ::
In an effort to make the mainline kernel's audit subsystem Controlled Access
Protection Profile (CAPP)/Evaluation Assurance Level (EAL) 4 compliant, this
patch adds file system auditing support to the audit subsystem. Such
support is essential in meeting certification requirements because it allows
the evaluator to confirm all claims made about the Target of Evaluation (TOE)
regarding the behavior of file system objects (which are outlined in the
Security Target for the given evaluation) by consulting the audit log.
To achieve such results, it's necessary for the audit subsystem to identify
and keep track of such objects. Due to the abstract nature of "identity"
with regards to file system objects and how that "identity" translates
between the user's perspective and the kernel's perspective and visa-versa, a
fairly strict definition is devised. This implementation uses a scheme by
which parent directories have a "watchlist" that qualifying children may
point into at the "watchlist entry" that holds their "watch".
Pointing at a
"watchlist entry" translates into "being watched".
It is also important to keep in mind that in a CAPP environment, we assume the
administrator to be benign and that we are preventing subversion of the audit
subsystem for the purpose of evaluation and not user/process malice.
This component is not designed for filesystem notifications, process/user
snooping, intrusion detection, etc.
:: DESCRIPTION ::
Below is a basic description of this patches capabilities. These capabilities
are enabled by the user space program, auditctl, which is available in the
audit package (found at:
http://people.redhat.com/sgrubb).
1. Insertions
When the administrator targets a file system object for audit, they do so by
<path> name. This is an absolute target -- meaning, the administrator targets
the file system object by name, on a given device, in a given namespace.
Provided the parent directory of the targeted object exists, we add to it's
"watchlist" the "watch" for our targeted object. Thus, all
information about
the watched object is stored on inodes, in memory, and not on disk. When
adding a "watch" at <path>, the terminating file or directory at
<path> need
not exist. (ie: if we wish to watch /tmp/foo, /tmp must exist, but 'foo'
does not have too). This is reasonable in a CAPP environment.
2. Removal
Likewise to inserting watches, we may remove a watch in the same fashion.
If the terminating file or directory name was found in its parent's watchlist,
the corresponding "watchlist entry" is unhashed. Once this "watchlist
entry"
is unhashed, it becomes invalid (ie: it may be overwritten and will no longer
generate audit records).
3. Listings
It'd be helpful for the administrator to be able to determine what watches
already exist directly under a directory, on a given device, in a given
namespace. To do so, the admistrator must target a specific directory via a
path (using the given device, in the given namespace) and a list of any
watches in that directory's watchlist will be returned.
4. Hooks
To make this all work, there are three sets of hooks the audit subsystem uses.
1. The first set of hooks manage the inode's audit_data memory. Two hooks:
one to allocate memory and one to free memory.
2. The second set of hooks is used in the dcache to attach watches to a
dentry's inode->i_audit->wentry field (ie: these hooks are responsible for
*watching* a file system object).
Creation:
We use hooks in d_instantiate() and d_splice_alias() to immediately attach
watches, if they exist, to newly created / spliced dentries.
Watch/removal:
We use the __d_lookup() hook for two reasons: to assign a new "watch", if one
exists at this location (ie: a hardlink that's just become "unwatched"
exists
in a location that has a "watch") and to detach unhashed (invalid) watchlist
entries (wentries) on inodes.
Deletion:
The d_delete() hook is used to drain watchlists and detach from a "watch".
We've effectively left the "watch".
Movement:
The d_move() hook is used to remove the "watch" and drain the
"watchlist" from
a dentry prior to "moving" it (leaving the "watch"), and then attach
to it, a
new "watch", if the location it's now at is being "watched".
3. The third set of hooks are all used to notify the audit subsystem about
access to a "watched" object. These hooks tell the audit subsystem to
generate a record.
Permissions:
This is a good junction to place a hook that generates audit records. These
functions are consulted before we commit to action, thus, whether we fail or
not, we get records. Not always can we map a permissions check one-to-one
with a watched file system object (ie: unlink), thus other hooks are
required.
An added benefit of hooking permission functions, is the ability to "watch"
the parent directory of a "watched" file, to see how it was consulted when
attempting access of the "watched" file. We have hooks at permission() and
exec_permission_lite().
Creation:
For creation, we have hooks in vfs_link()/symlink()/create()/mkdir()/mknod().
Once we have the inode (post creation), and we're attached (post
audit_attach_watch), we want to generate a record.
Deletion:
For deletion, we hook may_delete(). We do so because vfs_unlink()/rmdir()
both make use of this function; it is a good junction.
Rename:
For renaming, we hook vfs_rename_other()/rename_dir() to genereate audit
records describing the rename in to a "watched" location, and rely on the
may_delete() hook to give us an audit record describing the rename out of a
"watched" location.
Open:
I think these hooks can be dropped. Will do before we send out to
linux-fsdevel.
5. Notable Behavior
This system allows for only one type of implicit watch; hardlinks. One may
create a hardlink to a "watched" file and it too, will be watched. They can
"move" this hardlink around, and it will remain watched. This is because
both the watched object and the hardlink share the same inode. However,
should the "watched" object (ie: the dentry belonging to this inode that meets
the aforementioned criteria) no longer meet this criteria, the hardlink will
no longer be attached to this "watch" -- In fact, the next time the inode is
accessed, should a hardlink exist in another "watched" location, the inode
would attach to this "watch" (See, 4. Hooks). This makes sense, but in a
subtle way. If we create a dentry, such that we become watched again, even
though there are now at least two files on the system that could contain the
same content, our one time hardlink, has effectively become a separate object
to us. Thus it is important to realize that we are not auditing access to
specific content.
This being said, if we decide to "move" in any way, out of a "watched"
location, we lose the "watch" -- Thus, if we: mv, cp, rm (or use their
underlying syscalls), we'll lose the "watch" and thus, we will no longer be
audited. It's important, however, to keep in mind that we will get final
records based on these actions (ie: if we do mv /tmp/foo to /tmp/bar
and /tmp/foo is being watched, we will see a record for the rename out
of /tmp/foo. And, if we do mv /tmp/bar /tmp/foo, we will see a record for
the rename into /tmp/foo).
-tim