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.
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
Field | Description |
---|---|
magic | Module id for the SELinux module. |
inode | Back pointer to the associated inode. |
list | Pointer used to maintain the list of allocated inode security structures. |
task_sid | SID of the creating task. |
sid | SID of the inode. |
sclass | Security class of this inode. |
avcr | AVC entry reference. |
initialized | Flag indicating whether the security structure has been initialized. |
initializing | Flag indicating whether the security structure is in the process of being initialized. |
ctl | Pointer into the shadow sysctl table for /proc/sys entries (See Section 14.1.3.1). |
sem | Semaphore 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 Section 17.4.
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.
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 Section 17.2.1. 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
Mode | Security Class |
---|---|
S_IFREG | file |
S_IFDIR | dir |
S_IFLNK | lnk_file |
S_IFFIFO | fifo_file |
S_IFSOCK | sock_file |
S_IFBLK | blk_file |
S_IFCHR | chr_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.
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 Section 23.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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
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 12 and Table 13.
Table 12. Link Permission Checks
Source | Target | Permission(s) |
---|---|---|
Current | ParentDirectory | search, add_name |
Current | File | link |
Table 13. 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 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
Source | Target | Permission(s) |
---|---|---|
Current | OldParentDirectory | search, remove_name |
Current | File | rename |
Current | NewParentDirectory | search, add_name |
Table 15. Additional Rename Permission Checks if NewFile Exists
Source | Target | Permission(s) |
---|---|---|
Current | NewParentDirectory | remove_name |
Current | NewFile | unlink or rmdir |
Table 16. 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 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.
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.
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
Hook | Permission |
---|---|
selinux_inode_readlink | read |
selinux_inode_follow_link | read |
selinux_inode_setattr | setattr |
selinux_inode_stat | getattr |
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.