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 ordinary filesystems like ext2 and reiserfs and pseudo filesystems like devfs, procfs, 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 {
        unsigned long magic;           /* magic number for this module */
        struct inode *inode;           /* back pointer to inode object */
        struct list_head list;         /* list of inode_security_struct */
        security_id_t task_sid;        /* SID of creating task */
        security_id_t sid;             /* SID of this object */
        security_class_t sclass;       /* security class of this object */
        avc_entry_ref_t avcr;          /* reference to object permissions */
        unsigned char initialized;     /* initialization flag */
        unsigned long initializing;    /* initializing flag */
        ctl_sid *ctl;
        struct semaphore sem;
};

Table 9. inode_security_struct

FieldDescription
magicModule id for the SELinux module.
inodeBack pointer to the associated inode.
listPointer used to maintain the list of allocated inode security structures.
task_sidSID of the creating task.
sidSID of the inode.
sclassSecurity class of this inode.
avcrAVC entry reference.
initializedFlag indicating whether the security structure has been initialized.
initializingFlag indicating whether the security structure is in the process of being initialized.
ctlPointer into the shadow sysctl table for /proc/sys entries (See the Section called Procfs File Labeling).
semSemaphore for synchronizing file relabels.

When the extended socket call option is enabled, the inode_security_struct structure is extended to include additional fields related to the extended socket calls. This is discussed further in the Section called Extended Socket Call Processing.

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 tries to save the SID of the current task in the task_sid field. If the security structure of the current task is not already set, this function merely sets this field to the unlabeled SID. The selinux_inode_free_security hook function merely calls the inode_free_security helper function.

The inode_alloc_security function can not safely call task_precondition, because inode_alloc_security may be called indirectly from task_precondition. Hence, callers of inode_alloc_security should first call task_precondition on the current task when possible. This is done by the selinux_inode_alloc_security hook function.

inode_precondition

This helper function is the precondition function for inode security structures. This function ensures that the inode security structure is allocated and initialized prior to use. Prior to initializing the inode security structure, this function calls superblock_precondition to ensure that the security structure for the superblock that is associated with the inode has been allocated and initialized.

Since inodes can represent many different kinds of objects, the inode security class must be determined and set in the 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 the Section called selinux_socket_post_create. 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 10. If the inode does not have any of the modes listed in Table 10, then it defaults to the file security class.

Table 10. Inode Security Classes

ModeSecurity Class
S_IFREGfile
S_IFDIRdir
S_IFLNKlnk_file
S_IFFIFOfifo_file
S_IFSOCKsock_file
S_IFBLKblk_file
S_IFCHRchr_file

The inode security identifier (SID) is then determined based on information in the superblock security structure. If the filesystem can use the persistent label mapping, then the psid_to_sid function is called to obtain the SID of the inode. This is used for regular persistent filesystem types like ext2 and reiserfs.

If the inode represents a private object such as a socket or pipe, then the inode inherits the SID of the task that allocated its security structure. For inodes allocated after the initialization of the SELinux module, this is the same task that allocated the inode, so the private object inherits the SID of its creator. However, if the inode was allocated before the initialization of the SELinux module and subsequently caught by inode_precondition, then this may be a different task which simply happens to be the first to access the object since the module was loaded. Hence, for sockets and pipes, the principle of first use is applied to retroactively determine the SID of a pre-existing object. If SELinux is to be used as a separate module, then a better approach is needed for labeling pre-existing sockets and pipes.

The handling for pseudo filesystem types is specialized to provide reasonable security semantics for each type. At present, the SELinux security module defines labeling behaviors for the procfs, devpts, tmpfs, and devfs pseudo filesystem types. The handling for each of these filesystem types is described below.

Procfs File Labeling

For procfs inodes, the procfs_set_sid function is called to set the inode SID. The root directory inode is assigned the proc initial SID. Highly sensitive files such as kmsg and kcore are also assigned individual initial SIDs. The sys subdirectory and the per-process PID subdirectories are handled specially, as described below. Most inodes simply inherit the SID of their parent directory.

The sys subdirectory is assigned the sysctl initial SID. The SIDs of entries in the sys subdirectory are determined by traversing the ctl_sid_root_table hierarchical table. This table shadows the kernel sysctl table and allows SIDs to be selectively assigned at any level, with unspecified entries simply inheriting the SID of the parent entry. Since a pointer into the table is saved in the parent inode's security structure, this function simply searches the parent inode's table and does not need to reconstruct an absolute pathname. This table is also used by the sysctl hook as described in the Section called System Hook Functions.

The per-process PID subdirectories are assigned the SID of the associated process. For each top-level PID subdirectory, the task is looked up by PID and its SID is used for the inode. Entries within the PID subdirectories simply inherit the SID of their parent directory.

Devpts and Tmpfs File Labeling

For devpts and tmpfs inodes, a SID is assigned when the inode is first accessed based on the SID of the process and an initial SID defined for the filesystem type. This SID is computed using the security_transition_sid interface of the security server. This permits the security policy configuration to define derived types for each domain's pseudo terminal devices and for each domain's shared memory pseudo files via the type_transition rules.

Devfs File Labeling

For devfs inodes, a SID is assigned when the inode is first accessed based on the pathname (relative to the root of the filesystem) and the security class of the inode. This SID is computed using the security_devfs_sid interface of the security server. This permits the security policy configuration to define security contexts for devfs nodes based on their pathname. SELinux support for using devfs is still experimental.

selinux_inode_post_lookup

This hook function is called after a successful lookup. At this point, useful information such as the inode number and mode are available for use in determining the security attributes of the inode. This function simply calls the inode_precondition function to set the SID and security class on the inode if it has not already been set.

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 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. NFS does not instantiate a dentry for symbolic links. If the dentry has a null inode, then this function merely returns.

This function checks the current task's security structure to see if the task specified a SID for the new file. If so, then this SID is used. Otherwise, a SID is obtained from the security server by calling the security_transition_sid interface; passing in the task and 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 filesystem uses a persistent label mapping. If so, then this functions call the sid_to_psid function to set the persistent SID for the inode in the persistent label mapping.

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

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.

selinux_inode_delete

This hook function is called when a deleted inode is released, i.e. an inode with no hard links has its use count drop to zero. The function calls the clear_psid to clear the persistent SID for the inode in the persistent label mapping.

selinux_inode_revalidate

This hook function is called to revalidate the inode attributes. When support for NFS file labeling is added to SELinux, this hook function will be used to revalidate the SID of the inode. At present, this hook function merely returns success.

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 two optional parameters. The first optional parameter, aeref, allows another AVC entry reference, such as the one in the file security structure, to be passed for use instead of the reference in the inode security structure. The second optional parameter, adp, allows other audit data, such as the particular dentry, to be passed for use if an audit message is generated. This function simply 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. This function saves the dentry in the audit data structure and 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 specified a SID for the new file. If so, 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 11.

Table 11. 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 12 and Table 13.

Table 12. Link Permission Checks

SourceTargetPermission(s)
CurrentParentDirectorysearch, add_name
CurrentFilelink

Table 13. 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 14, Table 15, and Table 16. The permissions in Table 14 are always checked. The permissions in Table 15 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 16 are only checked if the file is a directory and its parent directory is being changed by the rename.

Table 14. Basic Rename Permission Checks

SourceTargetPermission(s)
CurrentOldParentDirectorysearch, remove_name
CurrentFilerename
CurrentNewParentDirectorysearch, add_name

Table 15. Additional Rename Permission Checks if NewFile Exists

SourceTargetPermission(s)
CurrentNewParentDirectoryremove_name
CurrentNewFileunlink or rmdir

Table 16. 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 Linux permission function to check permission when accessing an inode. It 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 17 specifies the SELinux permission that is checked for each permission mask flag when checking access to a directory. Table 18 provides the corresponding permission information when checking access to a non-directory file.

In Table 17, 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 17. Directory Permission Checks

MaskPermission
MAY_EXECsearch
MAY_READread
MAY_WRITEwrite

In Table 18, 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 18. Non-Directory Permission Checks

MaskPermission
MAY_EXECexecute
MAY_READread
MAY_APPENDappend
MAY_WRITEwrite

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

Table 19. Remaining Inode Hook Permission Checks

HookPermission
selinux_inode_readlinkread
selinux_inode_follow_linkread
selinux_inode_setattrsetattr
selinux_inode_statgetattr
Of these hooks, only two require further description. First, the selinux_inode_setattr hook merely checks the general setattr permission to the file. 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 nondiscretionary access control policies. Second, in addition to performing a permission check, the selinux_inode_stat saves the SID of the inode in an element of the out_sid array in the task security structure for use by the stat_secure system calls.