Implementing SELinux as a Linux Security Module | ||
---|---|---|
<<< Previous | Next >>> |
This section summarizes the changes between the original SELinux kernel patch and the LSM-based SELinux security module. At a high level, the LSM-based SELinux security module provides equivalent security functionality to the original SELinux kernel patch. However, there have been some changes to the specific controls, partly driven by design constraints imposed by LSM and partly based on further review of the original SELinux controls. There have also been significant changes in the underlying implementation, likewise partly driven by differences in LSM and partly based on a review of the original SELinux implementation. The following subsections summarize the changes, grouped by category.
This subsection describes general changes between the original SELinux kernel patch and the LSM-based SELinux security module. These changes include adding a new level of indirection, dynamically allocating security fields, stacking with the capabilities module, redesigning the SELinux API, and leveraging the existing Linux functions for checking permissions.
The original SELinux kernel patch provided clean separation between the policy enforcement code and the policy decision-making code by using the Flask security architecture and interfaces. The policy enforcement code was directly inserted into the kernel code at appropriate points, and the policy decision-making code was encapsulated in the security server, with a well-defined interface between the two components. Similarly, policy-independent data types for security information were directly inserted into kernel data structures, and only the security server could interpret these data types. This level of separation permitted many different kinds of nondiscretionary access control policies to be implemented in the security server without any changes to the policy enforcement code.
The LSM kernel patch inserts calls to hook functions on kernel objects into the kernel code at appropriate points, and it inserts void* security fields into the kernel data structures for kernel objects. In the LSM-based SELinux security module, the policy enforcement code is implemented in the hook functions, and the policy-independent data types are stored using the security fields in the kernel data structures. Internally, the SELinux code continues to use the Flask architecture and interfaces, and the security server remains as a separate component of the module. Hence, LSM introduces an additional level of indirection for the SELinux code and data. The internal architecture of the SELinux security module is discussed further in the Section called Internal Architecture.
In the original SELinux kernel patch, fields for security data were inserted directly into the appropriate kernel objects and were allocated and freed with the kernel object. Since LSM inserts only a single void* security field into each kernel object, the LSM-based SELinux security module must manage a dynamically allocated security structure for each kernel object unless it only needs to store a single word of security data. The SELinux security module uses a dynamically-allocated security structure for the security fields of the kernel data structures.
The original SELinux kernel patch added the SELinux nondiscretionary access controls as additional restrictions to the existing Linux access control logic. This left the existing Linux logic intact and unchanged, including the discretionary access control logic and the capabilities logic. LSM moves most of the capabilities logic into an optional capabilities security module and provides a dummy security module that implements traditional superuser logic. Hence, the LSM-based SELinux security module provides support for stacking with either the capabilities module or the dummy module. Since some existing applications (e.g. named, sendmail) expect capabilities to be present in Linux, it is recommended that the SELinux module always be stacked with the capabilities module. The stacking support is discussed further in the Section called Stacking with Other Modules.
In the original SELinux kernel patch, extended system calls such as execve_secure and stat_secure were implemented by extending the internal kernel functions to optionally pass and process SID parameters. Initially, in the LSM-based SELinux security module, these extended system calls were implemented using the security system call and by passing SID parameters to and from the hook functions via fields in the current task's security structure. However, when the security system call was removed from LSM, the SELinux API was completely redesigned in order to gain acceptance into the mainline kernel. This is discussed further in the Section called SELinux API.
The original SELinux kernel patch directly inserted its own permission checks throughout the kernel code rather than trying to leverage existing Linux permission functions such as permission and ipcperms due to the coarse-grained permissions supported by these functions and the need to perform permission checks in many locations where no Linux check already existed. The one notable exception to this practice in the original SELinux kernel patch was the insertion of a SELinux permission check into the existing capable kernel function so that SELinux could perform a parallel check for the large number of existing calls to capable.
In contrast, LSM inserts hook calls into all of the existing Linux permission functions in order to leverage these functions. In some cases, LSM also inserts additional hook calls in specific operations to provide finer-grained control, but in other cases, it merely relies on a hook in one of the existing Linux permission functions to control an operation. The LSM-based SELinux security module uses the hooks in the existing Linux permission functions to perform a parallel check for each Linux permission check. These parallel checks for the Linux permission checks ensure that every Linux access control is also controlled by SELinux. They also reduce the risk that future changes to Linux will introduce operations that are completely uncontrolled by SELinux.
Using these hooks required defining some additional coarse-grained permissions for SELinux. These permissions are discussed further in the Section called Leveraging permission and in the Section called Leveraging ipcperms. Whenever possible, the LSM-based SELinux security module leverages these hooks to provide control. When SELinux requires finer-grained control, the module implements these finer-grained SELinux controls using the additional LSM hooks.
This subsection describes general changes between the original SELinux
kernel patch and the LSM-based SELinux security module related to
program execution. These changes include replacing the process
execute
permission with a new file
execute_no_trans
permission, and changing the
controls over the inheritance of state across a context-changing
execve. Each of these changes is described below.
execute_no_trans
PermissionIn the original SELinux kernel patch, the file
execute
permission controlled the ability to
initiate the execution of a program, while the process
execute
permission controlled the ability to
execute code from an executable image. The distinction was necessary
because the SID of a task can be changed by program execution, so the
SID of the initiator may differ from the SID of the transformed
process. However, the process execute
permission
was redundant with the process entrypoint
permission when the SID of the task was changing, so it only served a
useful purpose when the task SID was left unchanged. Furthermore,
since this permission was between a task SID and a program file SID,
it properly belonged in the file class, not the process class.
Hence, the process execute
permission was
replaced by a new file execute_no_trans
permission in the LSM-based SELinux security module. Unlike the
original process execute
permission, the file
execute_no_trans
permission is only checked when
the SID of the task would remain unchanged. The process
entrypoint
permission was also moved into the
file class for consistency. The file execute
and
process transition
permissions were left
unchanged. These checks are described further in the Section called selinux_bprm_set_security.
Several changes were made to the controls over the inheritance of state across a context-changing execve. These changes included changes to the file descriptor inheritance controls, changes to the controls over process tracing and state sharing, and the addition of new controls.
The file descriptor inheritance permission checks during program execution were revised for the LSM-based SELinux security module. This is discussed in the Section called File Descriptor Permissions.
In the original SELinux kernel patch, checks for process tracing and
sharing process state when the SID was changed were inserted into the
compute_creds kernel function with the existing
Linux tests for these conditions for setuid programs. However, this
function can not return an error, so SELinux merely left the task SID
unchanged if these checks failed, just as Linux leaves the uid
unchanged if its tests fail. Additionally, the original SELinux
kernel patch used a hardcoded test for process 1 to permit the kernel
to transition to a new SID for init even though
it was sharing state. In the LSM-based SELinux security module, the
ptrace and share checks were changed to also send a SIGKILL to the
task to terminate it upon a permission failure, and a new process
share
permission was added to provide
configurable control over process state sharing across SID
transitions. This is described further in the Section called selinux_bprm_apply_creds.
New permission checks were implemented in the LSM-based SELinux to
control inheritance of signal-related state and resource limits.
These checks are also described in the Section called selinux_bprm_apply_creds. Furthermore, a
AT_SECURE
flag was added to the ELF auxiliary
table so that the SELinux module could inform glibc when to enable its
own secure mode in order to sanitize the environment and other state
on a context-changing exec. This behavior is also controlled based on
a permission check between the relevant contexts, and is described in
the Section called selinux_bprm_secureexec.
This subsection describes changes between the original SELinux kernel patch and the LSM-based SELinux security module related to the filesystem. These changes include using extended attributes rather than the persistent label mapping for file security contexts on persistent filesystems, reimplementing file labeling support for pseudo filesystem types, leveraging the hook in the existing permission function, revising the file descriptor permission checks, and eliminating the pipe security class. Each change is described below.
In the original SELinux kernel patch, a persistent label mapping was maintained in each filesystem that stored a mapping from integer persistent security identifiers (PSIDs) to security contexts, and a PSID was stored in a spare field of the on-disk ext2 inode. Since LSM provides all of its file-related hooks in the VFS layer and does not provide any filesystem-specific hooks, the SELinux persistent label mapping was initially changed to maintain the inode-to-PSID mapping in a regular file rather than using a spare field in the ext2 on-disk inode. This change allowed SELinux to support other file system types more easily, but had disadvantages in terms of performance and consistency. Since support for extended attributes was integrated into the Linux 2.6 kernel, extended attribute handlers were created for a new security namespace, and SELinux was modified to store file security contexts as extended attributes. This eliminated the need for the persistent label mapping.
In the original SELinux kernel patch, code was directly inserted into the procfs and devpts pseudo filesystem implementations to provide appropriate file labeling behaviors. Since LSM did not provide filesystem-specific hooks, the LSM-based SELinux security module had to reimplement this functionality using the hooks in the VFS layer. Subsequently, as part of the integration of SELinux into Linux 2.6, a LSM hook was introduced into the proc filesystem to better support labeling of /proc/pid inodes, and a fake xattr handler was added to the devpts pseudo filesystem implementation to export pty labels to userspace. However, labeling of other proc inodes and the initial labeling of devpts inodes is still handled by the hooks called by the VFS layer. The LSM-based SELinux also expanded and generalized support for pseudo filesystem labeling. The handling for these pseudo filesystem types is described in the Section called inode_doinit, selinux_d_instantiate.
As discussed in the Section called Leveraging Linux Permission Functions, LSM inserts a hook into the existing Linux functions for permission checking, including the permission function for checking access to objects represented by inodes. The LSM-based SELinux security module leverages this hook to perform a parallel check for each existing Linux inode permission check. The use of this hook posed a problem for preserving the SELinux distinction between opening a file with append access vs. opening a file with write access, requiring an additional change to the Linux kernel.
The use of this hook also posed a problem for the SELinux directory
permissions, which partition traditional write access into separate
permissions for adding entries (add_name
),
removing entries (remove_name
), and reparenting
the directory (reparent
). Since these
distinctions are not possible in the
selinux_inode_permission hook called by the
permission kernel function, a directory
write
permission was added to SELinux. This
permission is checked by this hook when write access is requested, and
the finer-grained directory permissions are checked by the additional
hooks that are called when a directory operation is performed.
Hence, directory modifications require both a
write
permission and the appropriate
finer-grained permission to the directory. Whenever one of the
finer-grained permissions is granted in the policy, the
write
permission should also be granted in the
policy. The write
permission check on
directories could be omitted, but it is present to ensure that all
directory write accesses are controlled by SELinux.
In the original SELinux kernel patch, distinct file descriptor
permissions were defined for getting the file offset or flags
(getattr
), setting the file offset or flags
(setattr
), inheriting the descriptor across an
execve (inherit
), and
receiving the descriptor via socket IPC
(receive
). These permissions were reduced to a
single use
permission in the LSM-based SELinux
security module that is checked whenever the descriptor is inherited,
received, or used.
Additionally, in the original SELinux kernel patch, only the
inherit
or receive
permissions were checked when a descriptor was inherited or received.
The other descriptor permissions and the appropriate file permissions
were only checked when an attempt was made to use the descriptor. In
the LSM-based SELinux security module, the use
permission and the appropriate file permissions are checked whenever
the descriptor is inherited, received, or used.
These changes to the SELinux file descriptor permission checks bring SELinux into conformity with the base Linux control model, where possession of a descriptor implies the right to use it in accordance with its mode and flags. This reduces the risk of misuse of a descriptor by a process, and also reduces the risk that future changes to Linux will open vulnerabilities in the SELinux control model. With these changes, the SELinux permission checks on calls such as read and write are only necessary to support revocation of access for relabeled files or policy changes.
In the original SELinux kernel patch, a separate security class was
defined for pipes, although this security class merely inherited the
common file permissions. In the LSM-based SELinux security module,
this class was eliminated, and the fifo_file
security class is used for both pipes and for named FIFOs. This
has no impact on the ability to control pipe operations distinctly,
since pipes are still labeled with the SID of the creating task while
named FIFOs are labeled in the same manner as other files.
This subsection describes changes between the original SELinux kernel patch and the LSM-based SELinux security module related to socket IPC and networking. These changes include redesigning the SELinux network access controls, storing socket security information in the associated inode security field, reimplementing the SELinux access controls using minimally invasive hooks, changing the file descriptor transfer controls, omitting some of the low-level ioctl controls, and implementing the extended socket calls.
As part of integrating SELinux into Linux 2.6, the network access controls were redesigned based on past experience and on what could be readily supported by the Linux 2.6 kernel, since most of the LSM networking hooks were rejected. This is discussed further in the Section called Controlling Receipt of Packets and the Section called IP Networking Hook Functions.
The original SELinux kernel patch added security fields to the network
layer sock
structure for socket security
data, and also mirrored the SID and security class of the socket in
the inode
structure associated with the
socket. LSM also provides a security field within the sock structure,
but SELinux can only use this field to store peer security data for
Unix stream connections during connection setup. Otherwise, the
LSM-based SELinux security module stores all socket security data in
the security field of the associated inode once the user socket is
established. This is discussed further in the Section called Managing Socket Security Fields and
the Section called IP Networking Hook Functions.
Since the original SELinux kernel patch added security fields to the
lower-level struct sock
structure, most of
the SELinux changes were inserted directly into the specific protocol
family implementations (e.g. the AF_INET and AF_UNIX code). The
original SELinux kernel patch was fairly invasive in inserting SELinux
processing throughout the protocol family implementations, and did not
try to leverage the existing Linux packet filtering support.
LSM provides a set of hooks in the abstract socket layer for controlling socket operations at a high level, and leverages the Linux NetFilter support for hooking network operations. The LSM-based SELinux security module implements as many of the SELinux socket and network controls as possible using these socket layer hooks and NetFilter-based hooks. Hence, NetFilter support should be enabled in the kernel configuration when using SELinux.
For the SELinux Unix domain IPC controls, the LSM-based SELinux security module leverages the hooks in the existing Linux permission functions but also required two additional hooks in the Unix domain protocol implementation due to the abstract namespace. The SELinux socket access controls are described in the Section called Controlling Socket Operations and the SELinux network layer access controls are described in the Section called IP Networking Hook Functions.
The file descriptor transfer permission checks during socket IPC were revised for the LSM-based SELinux security module. This is discussed in the Section called File Descriptor Permissions.
In the original SELinux kernel patch, a small set of controls were implemented in low-level ioctl routines to support fine-grained control over configuring network devices, accessing the kernel routing table, and accessing the kernel ARP and RARP tables. During the development of LSM, the feasibility of providing hooks to support these controls was explored, but it was determined that providing hooks in every location necessary to control configuring network devices would be too invasive, and the other controls offered little benefit over the existing capable calls. Hence, the LSM-based SELinux security module does not implement these controls, and control over these operations is handled based on the capable calls.
In the original SELinux kernel patch, a set of extended socket calls were implemented. These calls were reimplemented initially for the LSM-based SELinux, and an experimental labeled networking implementation was also contributed. However, as part of the SELinux API redesign and the rejection of the LSM networking hooks, the extended socket calls and labeled networking do not exist in Linux 2.6. There is one exception: a getpeercon API has been implemented to support obtaining peer security contexts for Unix stream connections, and is available in Linux 2.6.
This subsection describes changes between the original SELinux kernel patch and the LSM-based SELinux security module related to System V IPC. Since the System V IPC security enhancements were never ported from the 2.2 series to the 2.4 series prior to the transition to using LSM, the LSM-based SELinux security module had to adapt the implementation of the SELinux security enhancements to the 2.4 series. In addition to this adaptation, the changes include an easier solution for storing the IPC security data and leveraging the hook in the existing ipcperms function.
In the original SELinux kernel patch for the 2.2 series, it was
difficult to add security data to the semaphore and message queue
structures because the kernel exported the same data structure that it
used internally to applications. Hence, the original SELinux kernel
patch wrapped these data structures with private kernel data
structures that contained both the original structure and the
additional security data. This required extensive changes to the IPC
code to dereference fields in the original structure. In the 2.4
series, the IPC code was rewritten to use private kernel data
structures for all of the IPC objects, and each of these structures
included a struct kern_ipc_perm
structure
with common information. Hence, LSM was able to add a single security
field to this common structure and a single security field to the
structure for individual messages. This is discussed further in the Section called Managing System V IPC Security Fields.
As discussed in the Section called Leveraging Linux Permission Functions, LSM inserts a
hook into the existing Linux functions for permission checking,
including the ipcperms function for checking
access to IPC objects. The LSM-based SELinux security module
leverages this hook to perform a parallel check for each existing
Linux IPC permission check. However, since the SELinux IPC
permissions are much finer-grained than the Linux concepts of read or
write access to IPC objects, new unix_read
and
unix_write
permissions were defined to correspond
with the Linux permissions. These new permissions are checked by the
hook called by ipcperms, and the finer-grained
SELinux permissions are checked by the other IPC hooks. Hence, IPC
operations require the unix_read
or
unix_write
permission and the appropriate
finer-grained permission. The coarse-grained permission checks could
be omitted, but they are present to ensure that all IPC accesses are
controlled by SELinux. These checks are discussed in the Section called selinux_ipc_permission.
In addition to the changes described above, the LSM-based SELinux security module had to reimplement the approach for controlling the sysctl call. It also added new controls for some system operations that were not specifically addressed in the original SELinux kernel patch, such as syslog, which were formerly controlled only via the coarse-grained capable controls. Fine-grained controls over netlink operations were also introduced as part of the 2.6 SELinux. These controls are discussed in the Section called Miscellaneous Hook Functions.
<<< Previous | Home | Next >>> |
SELinux Basic Concepts | Internal Architecture |