The SELinux inode hook function implementations manage the security fields of inode structures and perform access control for inode operations. Since inodes are used to represent pipes, files, and sockets, the hook functions must handle each of these abstractions. Furthermore, these hooks must handle multiple filesystem types, including both conventional disk-based filesystems like ext3 and pseudo filesystems like proc and tmpfs. This section begins by describing the inode hook functions for managing the security fields. It then discusses the inode hook functions for performing access control.
The inode_security_struct
structure contains
security information for inodes. This structure is defined as follows:
struct inode_security_struct { unsigned long magic; struct inode *inode; struct list_head list; u32 task_sid; u32 sid; u16 sclass; unsigned char initialized; struct semaphore sem; };
Table 11. inode_security_struct
Field | Description |
---|---|
magic | Module id for the SELinux module. |
inode | Back pointer to the associated inode. |
list | Link into list of inode security structures setup prior to superblock security initialization. |
task_sid | SID of the task that allocated this inode. |
sid | SID of this inode. |
sclass | Security class of this inode. |
initialized | Flag indicating whether the inode SID has been initialized. |
sem | Semaphore for synchronizing initialization. |
The inode_alloc_security
and
inode_free_security
helper functions are the
primitive allocation functions for inode security structures. In
addition to the general processing for these primitive allocation
functions, inode_alloc_security
saves the SID of
the allocating task in the task_sid
field.
The selinux_inode_alloc_security
and
selinux_inode_free_security
hook functions merely
calls these helper functions.
The inode_doinit
and
inode_doinit_with_dentry
helper functions
performs initialization for inode security structures. It is normally
called for file inodes by the
selinux_d_instantiate
hook function. However,
since this helper function cannot perform full initialization until
after the superblock security initialization is complete for the
associated superblock, it is also called by the
superblock_doinit
function to retroactively
complete initialization of inodes setup prior to the superblock
security initialization. This includes both inodes setup prior to the
initial policy load and any inodes directly populated by the
filesystem code during get_sb processing.
This helper function begins by checking the initialized flag to see whether the inode SID has already been initialized and, if so, jumps to the code for setting the inode security class. Setting of the inode security class is always performed by this function even if it has been previously set, as the inode mode is not always properly set at the time when an inode SID is first set. In particular, this is the case for /proc/pid inodes.
If the initialized flag has not been set, this function takes the
semaphore to synchronize with any other attempts to initialize the
inode SID and rechecks the initialized flag again. The function
then proceeds to check whether the superblock security structure has
been initialized. If not, the inode security structure is placed on
the list maintained in the superblock security structure for deferred
processingby superblock_doinit
and the function
returns after releasing the semaphore.
If the superblock security structure has been initialized, then this
function sets the inode SID based on the defined labeling behavior for
the superblock. If the labeling behavior is to use extended
attributes (xattr), then this function invokes the getxattr method to
fetch the context value and invokes
security_context_to_sid
to convert it to a SID.
This behavior is used for conventional disk-based filesystems that
support xattrs. If the inode has no xattr value, then the inode is
assigned the default SID from the superblock security structure, which
is either the initial file SID or a SID specified via the defcontext
mount option.
If the labeling behavior is to inherit the inode SID directly from the allocating task, then the function copies the task SID from the inode security structure into its own SID field. This behavior is used for private objects such as socket and pipes.
If the labeling behavior is to compute the inode SID based on both the
allocating task SID and the superblock SID, then the security
servers's security_transition_sid
function is
invoked to obtain the inode SID. This behavior is used for pseudo
filesystems like devpts and tmpfs where the inodes are labeled with
derived types reflecting both their creator and the kind of object
(e.g. a pty, a temporary file). As discussed in Section 13.1.5, the labeling behavior can be overridden via
the context mount option, so tmpfs mounts can be assigned a particular
security context instead, as is done for the tmpfs /dev used by udev.
For any other labeling behavior, the inode SID defaults to the
superblock SID. There is a further refinement for the proc
filesystem; if the inode is in the proc filesystem and is not a
/proc/pid inode, then the selinux_proc_get_sid
function is invoked to construct a pathname for the inode based on the
proc_dir_entry
information and then obtain a
SID for that pathname via the security server's
security_genfs_sid
function. The proc_dir_entry
information is used to ensure a stable and reliable name mapping,
unlike the filesystem namespace itself. Note that /proc/pid inodes
have their SIDs initialized separately by the
selinux_task_to_inode
hook function, as discussed
in Section 11.1.5.
After setting the inode SID, the function sets the initialized flag in
the inode security structure to indicate that the SID has been set.
Finally, the function determines the security class for the inode and
sets the corresponding field in the inode security structure.
If the inode represents a socket, then the
socket_type_to_security_class
function is used to
obtain the security class based on the socket family and type. The
socket security classes are described in Section 17.1.4. Otherwise, the
inode_mode_to_security_class
function is used to
obtain the security class based on the inode mode. The mapping
between inode modes and security classes is described in Table 12. If the inode does not have any of the
modes listed in Table 12, then it defaults
to the file security class.
The post_create
helper function is called by
several inode post-operation hooks which are called after a successful
file creation. This helper function sets the information in the inode
security structure for an inode that represents a newly created file.
This function first tests whether the dentry
for the newly created file has a null inode. This can happen if the
filesystem did not instantiate the dentry
for
the new file, e.g. some filesystems do not instantiate a
dentry
for symbolic links at creation time.
If the dentry
has a null inode, then this
function merely returns with a warning. Note that for such
filesystems, newly created symlinks will always inherit the default
for the filesystem's labeling behavior.
This function then checks the current task's security structure to see
if the task has set a fscreate SID for newly created files. If so
and mountpoint labeling is not being used for the filesystem, then
this SID is used. Otherwise, a SID is obtained from the security
server by calling the security_transition_sid
interface; passing in the creating task and parent directory SIDs.
The inode_security_set_sid
helper function is
called to set the SID and security class in the inode security
structure. This function then checks the superblock security
structure to see whether the labeling behavior is to use extended
attributes. If so and if the inode supports the setxattr method, then
this function calls the security server's
security_sid_to_context
function to convert the
SID to a context and then calls the setxattr method on the inode to
set the xattr.
This function is called by the following inode hook functions:
selinux_inode_post_create
selinux_inode_post_symlink
selinux_inode_post_mkdir
selinux_inode_post_mknod
This hook function is called to update the inode security structure after a successful setxattr operation while the inode semaphore is still held. It first checks whether the changed attribute is the SELinux attribute; if not, it returns immediately. Otherwise, it converts the attribute value to a SID and updates the inode SID.
This hook function is called to get the security attribute from the
inode's security structure for filesystems that do not provide native
support for xattrs or that have no persistent storage of them. It calls
security_sid_to_context
to convert the inode SID
to a context and copies the context into the provided buffer.
This hook function is called to set the security attribute in the
inode's security structure for filesystems that do not provide native
support for xattrs or that have no persistent storage of them. It calls
security_context_to_sid
to convert the provided
attribute value to a SID and sets the inode SID to it.
This hook function is called to return the names of any security attributes supported by the security module for filesystems that do not provide native support for xattrs or that have no persistent storage of them. It copies the name of the SELinux attribute into the provided buffer.
The selinux_inode_post_link
hook function is
called after a new hard link has been successfully created. The
selinux_inode_post_rename
hook function is called
after a successful rename. Both of these hook functions immediately
return. SELinux does not need to update any state when a new hard
link is created or a rename occurs, because security attributes are
associated with inodes, not pathnames.
This helper function checks whether a task has a particular permission
to an inode. In addition to taking the task, inode, and requested
permission as parameters, this function takes an optional auxiliary
audit data parameter. This optional parameter allows other audit
data, such as the particular dentry
, to be
passed for use if an audit message is generated. This function sets
up an auxiliary audit data structure if one is not provided and then
calls the AVC to check the requested permission to the inode.
This helper function is the same as the
inode_has_perm
except that it takes a
dentry
as a parameter rather than an inode,
and optionally takes a vfsmount
parameter.
This function saves the dentry and vfsmount in the audit data
structure and then calls inode_has_perm
with the
appropriate parameters.
This helper function checks whether the current task can create a
file. It takes the parent directory inode, the
dentry
for the new file, and the security
class for the new file. This function checks the current task's
security structure to see if the task has set a fscreate SID for newly
files. If so and mountpoint labeling is not being used, then this SID
is used. Otherwise, a SID is obtained from the security server using
the security_transition_sid
interface. The
function then checks permissions as described in Table 13.
Table 13. Create Permission Checks
Source | Target | Permission(s) |
---|---|---|
Current | ParentDirectory | search, add_name |
Current | File | create |
File | Filesystem | associate |
This helper function is called by the following inode hook functions:
selinux_inode_create
selinux_inode_symlink
selinux_inode_mkdir
selinux_inode_mknod
This helper function checks whether the current task can link, unlink, or rmdir
a file or directory. It takes the parent directory inode, the dentry
of the file, and a flag indicating the requested operation. The
permission checks for these operations are shown in
Table 14 and Table 15.
Table 14. Link Permission Checks
Source | Target | Permission(s) |
---|---|---|
Current | ParentDirectory | search, add_name |
Current | File | link |
Table 15. Unlink or Rmdir Permission Checks
Source | Target | Permission(s) |
---|---|---|
Current | ParentDirectory | search, remove_name |
Current | File | unlink or rmdir |
This helper function is called by the following inode hook functions:
selinux_inode_link
selinux_inode_unlink
selinux_inode_rmdir
This function checks whether the current task can rename a file or
directory. It takes the inodes of the old and new parent directories,
the dentry
of an existing link to the file, and the new dentry
for the
file. This function checks the permissions described in
Table 16, Table 17,
and Table 18.
The permissions in Table 16 are always
checked. The permissions in Table 17
are only checked if the new dentry
already has an existing inode (i.e. a file already exists with the
new name), in which case that file will be removed by the rename. The
permissions in Table 18 are only
checked if the file is a directory and its parent directory is being
changed by the rename.
Table 16. Basic Rename Permission Checks
Source | Target | Permission(s) |
---|---|---|
Current | OldParentDirectory | search, remove_name |
Current | File | rename |
Current | NewParentDirectory | search, add_name |
Table 17. Additional Rename Permission Checks if NewFile Exists
Source | Target | Permission(s) |
---|---|---|
Current | NewParentDirectory | remove_name |
Current | NewFile | unlink or rmdir |
Table 18. Additional Rename Permission Checks if Reparenting
Source | Target | Permission(s) |
---|---|---|
Current | File | reparent |
This helper function is called by the following inode hook functions:
selinux_inode_rename
This hook function is called by the kernel
permission
and
exec_permission_lite
functions to check
permission when accessing an inode. If the permission mask is null,
then there is no permission to check and the function simply returns
success. This can occur upon file existence tests via access(2) with
the F_OK
mode. Otherwise, this function converts
the permission mask to an access vector using the
file_mask_to_av
function, and calls
inode_has_perm
with the appropriate parameters.
Table 19 specifies the SELinux permission that
is checked for each permission mask flag when checking access to a
directory. Table 20 provides the
corresponding permission information when checking access to a
non-directory file.
In Table 19, notice that a
write permission mask causes the general write
permission to be checked. This hook function cannot distinguish among
the various kinds of modification operations on directories, so it
cannot use the finer-grained permissions
(add_name
, remove_name
, or
reparent
). Hence, directory modifications
require both the general write
permission and the
appropriate finer-grained permission to be granted between the task
and the inode. The general write
permission check
could be omitted from this hook, but it is performed to ensure that all
directory modifications are mediated by the policy.
In Table 20, notice that a
separate MAY_APPEND
permission mask and
append
permission are listed. This permission
mask was added by the LSM kernel patch and is used (along with
MAY_WRITE) when a file is opened with the
O_APPEND
flag. This allows the security module
to distinguish append access from general write access. The
selinux_file_fcntl
hook ensures that the
O_APPEND
flag is not subsequently cleared unless
the process has write
permission to the file.
This hook function is called to check permissions prior to setting an
extended attribute (xattr) for an inode. If the attribute is not the
SELinux attribute but is in the security namespace, then the function
checks CAP_SYS_ADMIN to protect the security namespace for
unprivileged processes. If the attribute is not in the security
namespace at all, then this function simply checks the
setattr
permission to the inode.
If the attribute is the SELinux attribute, then this function first
checks whether mountpoint labeling is being used, in which case it
immediately returns an error indicating that setxattr is not
supported. Otherwise, the function checks whether the process owns
the file and if not, checks CAP_FOWNER capability, in order to provide
a DAC restriction over file relabeling. The function then applies a
series of mandatory permission checks for file relabeling, as
summarized in Table 21. It also invokes the
security server's security_validate_transition
function to apply any checks based on all three security contexts (the
old file context, the new file context, and the process context)
together. This function was introduced as part of the enhanced MLS
support to support MLS upgrade and downgrade checks, but can be
generally applied for other kinds of policy logic as well.
The remaining inode hook functions are called to check permissions for various operations. Since each of these remaining hook functions only require a single permission between the current task and the file, the permission checks are all described in Table 22.
Table 22. Remaining Inode Hook Permission Checks
Hook | Permission |
---|---|
selinux_inode_readlink | read |
selinux_inode_follow_link | read |
selinux_inode_setattr | setattr or write |
selinux_inode_getattr | getattr |
selinux_inode_getxattr | getattr |
selinux_inode_listxattr | getattr |
selinux_inode_setattr
hook checks the
setattr
permission to the file if setting the
file mode, uid, gid or explicitly setting the timestamps to a
particular value via utimes; otherwise, it merely checks write
permission. Separate permissions could be defined for different kinds
of setattr operations, e.g. chown, chmod, utimes, truncate. However,
this level of distinction does not seem to be necessary to support
mandatory access control policies. Second, the
selinux_inode_follow_link
hook checks the same
permission as the selinux_inode_readlink
hook,
i.e. read permission. While this is correct from an information flow
perspective and while even reading a malicious symlink may constitute
a hazard (e.g. for realpath(3)), it may be desirable in the future to
introduce a separate follow permission to allow a trusted process to
see all symlinks (e.g. for ls -l) without necessarily being able to
follow them (in order to protect against malicious symlinks).