14. 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.

14.1. Managing Inode Security Fields

14.1.1. Inode Security Structure

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

FieldDescription
magicModule id for the SELinux module.
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.

14.1.2. 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.

14.1.3. inode_doinit, selinux_d_instantiate

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.

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

14.1.4. post_create

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

14.1.5. 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.

14.1.6. selinux_inode_getsecurity

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.

14.1.7. selinux_inode_setsecurity

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.

14.1.8. selinux_inode_listsecurity

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.

14.1.9. selinux_inode_post_link/rename

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.

14.2. Controlling Inode Operations

14.2.1. 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.

14.2.2. 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.

14.2.3. 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

14.2.4. 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

14.2.5. 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

14.2.6. 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

14.2.7. 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

14.2.8. 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).