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 { 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 |
---|---|
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_with_dentry
helper function
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 if it has not been previously set to a more specific value than the initial default file class even even if the initialized flag 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_default
to convert it to
a SID, possibly inheriting some information from the default file SID
associated with the superblock. 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 the Section called try_context_mount, 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 the Section called selinux_task_to_inode.
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
security class has not already been set to a more specific value than
the initial default file class. The check for a more specific value than the
default file class is to avoid overwriting the class value set by the socket hooks for socket inodes, as this function cannot properly classify socket inodes.
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 selinux_inode_init_security
hook function is
called by the filesystem-specific code when creating a new file in
order to obtain the security attribute to assign to the new inode
and to set up the incore inode security structure for the new inode.
This support allows new inodes to be atomically labeled as part of the inode
creation transaction, ensuring that an inode is never visible without
a security label. This hook and the corresponding filesystem suppport
was introduced in Linux 2.6.14; prior kernel versions used a different
set of post creation hooks invoked by the VFS layer that did not provide
atomicity, allowing the new inode to be temporarily visible in an unlabeled
state. Support for atomic inode labeling was only implemented for the
ext2, ext3, tmpfs, and jfs filesystems in 2.6.14; similar support for
other filesystems like xfs and reiserfs has not yet been implemented at
the time of this writing.
This function first 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 incore inode security
structure.
If the filesystem is using mountpoint labeling, then no attribute
should be set on disk, so the function returns an
EOPNOTSUPP
error to the filesystem code to
skip setting of the on-disk attribute. Otherwise, if the
filesystem code supplied pointer arguments to receive the attribute name
and value, the function generates the SELinux attribute name and the
security context value for the inode and sets the arguments accordingly
before returning successfully. Certain filesystems such as tmpfs
do not provide pointer arguments for receiving the attribute name and
value because there is no attribute representation other than the incore
representation, unlike the disk-based filesystems that have on-disk
attribute storage.
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 was originally called on getxattr(2) calls on
attributes in the security namespace for filesystems that did not
provide native support for xattrs. It is now called (as of Linux
2.6.15) on all getxattr(2) calls on attributes in the security
namespace, even when the filesystem supports xattrs, in order to allow
SELinux to provide the canonical form of the security context to
userspace. After checking that the requested attribute is the SELinux
attribute, the function 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 upon setxattr(2) calls on attributes in
the security namespace for filesystems that do not provide native
support for xattrs. After checking that the attribute name is the
SELinux attribute, the function calls the
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 upon listxattr(2) calls to return the names of any security attributes supported by the security module for filesystems that do not provide native support for xattrs. It copies the name of the SELinux attribute into the provided buffer.
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).