Implementing SELinux as a Linux Security Module | ||
---|---|---|
<<< Previous | Next >>> |
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 |
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).<<< Previous | Home | Next >>> |
Superblock Hook Functions | File Hook Functions |