Inode Hook Functions

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.

Managing Inode Security Fields

Inode Security Structure

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

FieldDescription
inodeBack pointer to the associated inode.
listLink into list of inode security structures setup prior to superblock security initialization.
task_sidSID of the task that allocated this inode.
sidSID of this inode.
sclassSecurity class of this inode.
initializedFlag indicating whether the inode SID has been initialized.
semSemaphore for synchronizing initialization.

inode_alloc_security and inode_free_security

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.

inode_doinit, selinux_d_instantiate

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.

Table 12. Inode Security Classes

ModeSecurity Class
S_IFREGfile
S_IFDIRdir
S_IFLNKlnk_file
S_IFFIFOfifo_file
S_IFSOCKsock_file
S_IFBLKblk_file
S_IFCHRchr_file

selinux_inode_init_security

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.

selinux_inode_post_setxattr

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.

selinux_inode_getsecurity

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.

selinux_inode_setsecurity

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.

selinux_inode_listsecurity

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.

Controlling Inode Operations

inode_has_perm

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.

dentry_has_perm

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.

may_create

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

SourceTargetPermission(s)
CurrentParentDirectorysearch, add_name
CurrentFilecreate
FileFilesystemassociate

This helper function is called by the following inode hook functions:

  • selinux_inode_create

  • selinux_inode_symlink

  • selinux_inode_mkdir

  • selinux_inode_mknod

may_link

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

SourceTargetPermission(s)
CurrentParentDirectorysearch, add_name
CurrentFilelink

Table 15. Unlink or Rmdir Permission Checks

SourceTargetPermission(s)
CurrentParentDirectorysearch, remove_name
CurrentFileunlink or rmdir

This helper function is called by the following inode hook functions:

  • selinux_inode_link

  • selinux_inode_unlink

  • selinux_inode_rmdir

may_rename

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

SourceTargetPermission(s)
CurrentOldParentDirectorysearch, remove_name
CurrentFilerename
CurrentNewParentDirectorysearch, add_name

Table 17. Additional Rename Permission Checks if NewFile Exists

SourceTargetPermission(s)
CurrentNewParentDirectoryremove_name
CurrentNewFileunlink or rmdir

Table 18. Additional Rename Permission Checks if Reparenting

SourceTargetPermission(s)
CurrentFilereparent

This helper function is called by the following inode hook functions:

  • selinux_inode_rename

selinux_inode_permission

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.

Table 19. Directory Permission Checks

MaskPermission
MAY_EXECsearch
MAY_READread
MAY_WRITEwrite

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.

Table 20. Non-Directory Permission Checks

MaskPermission
MAY_EXECexecute
MAY_READread
MAY_APPENDappend
MAY_WRITEwrite

selinux_inode_setxattr

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.

Table 21. Setxattr Permission Checks

SourceTargetPermission(s)
CurrentOldFileContextrelabelfrom
CurrentNewFileContextrelabelto
FileFilesystemassociate

Other inode access control hook functions

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

HookPermission
selinux_inode_readlinkread
selinux_inode_follow_linkread
selinux_inode_setattrsetattr or write
selinux_inode_getattrgetattr
selinux_inode_getxattrgetattr
selinux_inode_listxattrgetattr
Of these hooks, only two require further description. First, the 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).