Implementing SELinux as a Linux Security Module | ||
---|---|---|
<<< Previous | Next >>> |
The SELinux security module provides a set of helper functions that are used extensively by the SELinux hook implementations. This section provides an overview of these helper functions. More detailed descriptions of individual helper functions are provided in the appropriate hooks section.
For each SELinux security data structure defined in selinux_plug.h, the security module provides a primitive alloc_security and free_security helper function, e.g. task_alloc_security and task_free_security. These helper functions are used both by the precondition functions described in the next subsection and by the alloc_security and free_security hook functions.
Each primitive alloc_security helper function allocates a security structure of the appropriate type, sets a magic number field for subsequent sanity checking, sets a back pointer to the kernel data structure, adds the security structure to a list of similar structures, initializes the security information, and sets the object security field to refer to this new security structure. Currently, the security structure list and back pointer fields are only needed to deallocate and clear all security fields when the module exits. However, these lists and back pointers could also be useful in implementing revocation callback functions. Each primitive free_security helper function clears the security field, removes the security structure from its list, and frees the security structure.
Since the alloc_security helper functions can be
called from the precondition functions, they must synchronize the
initial setting of the security field. To solve this problem, a
spinlock is defined for each of these functions and used to
synchronize access. Since precondition functions may also be invoked
from interrupt context, the alloc_security helper
functions use the SAFE_ALLOC
flag for memory
allocation and spin_lock_irqsave function for
locking. The SAFE_ALLOC
flag is defined in
include/linux/flask/flask_types.h. This flag
expands to GFP_ATOMIC
if in interrupt context or
to GFP_KERNEL
otherwise.
The SELinux security module defines a precondition function for each
security structure (e.g. task_precondition,
inode_precondition, etc). The SELinux hook
functions invoke the appropriate precondition function on each kernel
object prior to dereferencing its security field. If the security
field is already set and the security structure is initialized, then
the precondition function simply returns 1
,
indicating that the hook can proceed. Otherwise, the precondition
function attempts to allocate and/or initialize the security
structure, returning 1
on success. If the
precondition function returns a value less than or equal to zero, then
the hook function immediately returns this value to its caller rather
than proceeding to dereference the security field. A return value
less then zero indicates an error and is a negative errno value as
with other kernel functions. A return value of zero indicates that
the security structure could not be initialized but the operation
should proceed, e.g. during system initialization prior to the loading
of the security policy or during the loading of the persistent label
mapping for a filesystem.
The precondition functions serve several purposes. First, the precondition functions handle subjects and objects in the system that were created prior to module initialization. Some tasks and objects (e.g. the procfs root inode) are created prior to module initialization even when the module is compiled into the kernel, so there are always some pre-existing subjects and objects that must be handled. An alternative approach would be to traverse the kernel data structures (e.g. the task list and each task's open files) during module initialization and set the security field at that time for these pre-existing subjects and objects. However, locating all such subjects and objects may be difficult, especially if the module is dynamically loaded into a running kernel (e.g. an open file might be on a Unix domain socket awaiting receipt by a process). Hence, the precondition approach seems safer. Another alternative approach would be to view all such pre-existing subjects and objects as being outside the control of the module. However, this isn't an acceptable approach for a nondiscretionary access control scheme like SELinux.
It is important to note that the ability to determine the correct security attributes for these pre-existing subjects and objects may be limited. The SELinux module does what it can to determine the correct attributes after the fact, but it isn't always successful in the dynamically loaded module case. This is discussed in detail for inodes in the Section called inode_precondition and for tasks in the Section called task_precondition. We recommend always compiling the SELinux module into the kernel.
Second, the precondition functions handle objects whose security attributes cannot be fully determined at allocation time. For example, when an inode security structure is allocated, the alloc_security hook knows nothing useful about the inode, e.g. what kind of object will it represent (a file, a socket, a pipe, etc) and what specific object will it represent (for a file, what is the inode number or pathname?). All this hook can do is to mark the inode as unlabeled and save the label of the creating task for possible later use if the inode turns out to be a pipe or socket. If the inode is used to represent a file, then it will later be caught by the post_lookup hook, which can then set its security class and security identifier. If the inode is used to represent a socket, then it will later be caught by the post_create hook or the accept hook, which can likewise set its security class and identifier. If the inode is used to represent a pipe, it may not be caught until it is actually used for a read or write. This issue could be avoided by providing an explicit hook in LSM for initializing pipe security attributes.
Third, the precondition functions serve to deal with cyclical dependencies. Such cycles can be created by dependencies between the module and the file system, e.g. loading the persistent label mapping for a file system or loading the security policy configuration.
A set of helper functions on kernel objects and permissions are provided that invoke the appropriate precondition functions, dereference the security fields, and then invoke the access vector cache (AVC) to perform the permission check with the right set of parameters. These helper functions simplify the code for many of the hook functions that perform permission checks. They also reduce the risk that a security field will be dereferenced without a call to the precondition function. A few examples of these functions include task_has_perm, inode_has_perm, and may_create.
Although these helper functions can be convenient, hook functions are free to directly call the AVC to perform permission checks. This is done in several cases. First, some permission checks involve a security identifier (SID) that is not associated with a kernel object, e.g. a SID specified by an application using one of the new system calls or a SID obtained from the security server for an object that is about to be created. Second, some operations require multiple permission checks to be performed that are based on some of the same SIDs. Third, some hook functions perform both a permission check and set an output SID for return to the application. In these latter two cases, using the helper functions would cause redundant processing in order to extract the same SIDs multiple times.
<<< Previous | Home | Next >>> |
New System Calls | Task Hook Functions |