The SELinux binprm hook function implementations manage the security
fields of linux_binprm
structures and perform
access control for program loading operations. This section
describes these hooks and their helper functions.
The bprm_security_struct
structure contains
security information for program loading. This structure is defined as follows:
struct bprm_security_struct { struct linux_binprm *bprm; u32 sid; unsigned char set; char unsafe; }; |
The selinux_bprm_alloc_security
and
selinux_bprm_free_security
hook functions
directly perform allocation and freeing of the linux_binprm security
structures rather than using primitive helper functions. However,
they perform the same basic processing as described in the Section called Primitive Allocation Helper Functions. In earlier versions of the SELinux module,
there was no bprm security structure and these functions did nothing
for SELinux, as only the new SID for the transformed process was needed and it
was stored directly in the linux_binprm security field.
The selinux_bprm_set_security
hook function is
called while loading a new program to fill in the
linux_binprm
security field and optionally to
check permissions. This hook function may be called multiple times
during a single execve, e.g. for interpreted scripts. This hook
function first calls the secondary security module to support Linux
capabilities. If the set flag in the bprm security structure has
already been set by a prior call to this hook, this hook merely
returns without further processing. This allows security transitions
to occur on scripts if permitted by the policy. Naturally, such
transitions should only occur when the caller is more trusted than the
new domain, as script invocation is subject to an inherent race and
scripts are highly susceptible to influence by their caller.
However, SELinux does allow transitions on scripts subject to policy,
e.g. to support shedding of permissions upon script invocation where
the caller is trusted.
By default, this hook function sets the SID in the bprm security
structure to the SID of the current task. It also clears any file
creation SID set earlier by the task to ensure that the new program
starts with a clean initial state. This function checks the current
task's security structure to see if the task specified an exec SID for
the next execve call. If so, then this exec SID is used and cleared.
Otherwise, the security server is consulted using the
security_transition_sid
interface to see whether
the SID should change based on the current SID of the task and the SID
of the program. If the filesystem is mounted
nosuid
, then any exec SID set previously or
transition SID obtained from the security server is ignored, and the
task SID is left unchanged.
This hook function then performs different permission checks depending
on whether the SID of the task is changing. The permission checks for
each case are described below. The file execute
permission check is performed by the
selinux_inode_permission
hook during
open_exec
processing, so it is not listed here.
The file execute_no_trans
permission is checked
when a task would remain in the same SID upon executing a program, as
shown in Table 5. This permission check
ensures that a task is allowed to execute a given program without
changing its security attributes. For example, although the login
process can execute a user shell, it should always change its SID at
the same time, so it does not need this permission to the shell
program.
Table 5. Permission Checks if Task SID is not changing on exec
Source | Target | Permission(s) |
---|---|---|
Current | ProgramFile | execute_no_trans |
The process transition
permission and the file
entrypoint
permission are checked when the SID of
a task changes. The transition
permission check
ensures that the old SID is allowed to transition to the new SID. The
entrypoint
permission check ensures that the new
SID can only be entered by executing particular programs. Such
programs are referred to as entrypoint programs for the SID. These
permission checks are shown in Table 6. If
all permission checks for a transition pass, then any unsafe personality
bits are cleared and the new SID is saved in the bprm security structure
for use by selinux_bprm_apply_creds
.
The selinux_bprm_apply_creds
hook function is
called to set the new security attributes for the transformed process
upon an execve after checking for certain unsafe conditions with the
task lock held. This hook function first calls the secondary security
module to support Linux capabilities. This hook then extracts the new
task SID from the bprm security structure, copies the current SID of
the task into the old SID field of the task security structure, and
clears the unsafe flag in the bprm security structure. If the new SID
is the same as the old SID, then nothing further is done by this hook.
Two additional permission checks may occur when the SID of the task is
changing. If the task was created via clone
and
has shared state, then the share
permission is
checked between the old and new SIDs. If the task is being traced,
then the ptrace
permission is checked between the
tracer task (saved in the ptrace_sid field of the current task's
security structure earlier upon ptrace_attach) and the new SID. The
permission checks are shown in Table 7. If
either of these permission checks fail, then the task SID is left
unchanged, the unsafe flag is set in the bprm security structure for
later use by the selinux_bprm_post_apply_creds
hook, and the hook immediately returns. If all permissions are
granted, this hook function changes the SID of the task to the new
SID and returns.
This hook function is called after the
selinux_bprm_apply_creds
without the task lock
held to support further processing that cannot be done safely under
the task lock or that does not require it to be held. The hook
function first checks whether the unsafe flag was set in the bprm
security structure, and if so, it forces a SIGKILL to the task to
terminate it and returns immediately. This function then checks whether
the task SID has changed, and if not, it returns immediately, as no further
processing is required.
Otherwise, this hook function proceeds to call the
flush_unauthorized_files
helper function to
revoke access to the controlling tty if the task is no longer allowed
to access it and to close any file descriptors to which the task
should no longer have access. After revalidating access to the
controlling tty and revoking it if necessary, the helper function
calls file_has_perm
on each open file with
requested permissions that correspond to the file mode and flags, and
closes the open file if these permissions are not granted under the
new SID. The file_has_perm
function is described
in the Section called file_has_perm. To avoid inducing errors in
applications that expect certain descriptors to be open, this helper
function also re-opens any descriptors it closes to refer to a null
device node that was set up in selinuxfs during initialization. The
helper function then returns.
The selinux_bprm_post_apply_creds
hook function
then applies two inheritance checks between the old and new SIDs, one
to control the ability to inherit signal-related state and one to
control the ability to inherit resource limits from the old SID.
These checks are intended to protect the program in the new SID
against certain forms of influence by the caller unless authorized by
policy. If the siginh
permission is denied, then
any itimers are cleared to avoid subsequent signal generation, pending
signals are flushed and unblocked, and all signal handlers are reset
to the default. If the rlimitinh
permission is
denied, then all soft resource limits are reset to the lower of the
current task's hard limit and the initial task's soft limit. This
control relies on the proper control of the
setrlimit
permission to prevent untrusted
processes from lowering hard limits as well. The inclusion of the
initial task's soft limits into the computation is to avoid resetting
soft limits higher than the default soft limit for cases where the
default is lower than the hard limit,
e.g. RLIMIT_CORE
or
RLIMIT_STACK
. These two inheritance checks
are shown in Table 8.
Table 8. Inheritance Permission Checks if Task SID is changing on exec
Source | Target | Permission(s) |
---|---|---|
OldTaskSID | NewTaskSID | siginh |
OldTaskSID | NewTaskSID | rlimitinh |
Finally, this hook function wakes up the parent task if it is waiting
on this task. This allows the selinux_task_wait
hook to recheck whether the parent task is allowed to wait on the task
under its new SID and to handle a denial appropriately.
This hook function is called after
selinux_bprm_post_apply_creds
to determine
whether the AT_SECURE flag in the ELF auxiliary table should be set to
cause glibc to enable its secure mode in order to sanitize the
environment and other state to protect the new program against certain
forms of influence by the caller. If the task SID has changed, then
this hook function checks noatsecure
permission
between the old and new task SIDs. If this permission is denied, the
hook function will set the AT_SECURE flag so that libc secure mode
will be enabled. If the permission is allowed, the hook function
calls the secondary security module to allow it to set the flag,
e.g. if there is a change in uid, gid or capabilities. Otherwise,
the flag will not be set and libc secure mode will not be enabled.