Program Loading Hook Functions

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.

Managing Binprm Security Fields

Binprm Security Structure

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;
};

Table 4. task_security_struct

FieldDescription
bprmBack pointer to the associated linux_binprm structure.
sidSID for the transformed process.
setFlag indicating whether sid has been set.
unsafeFlag indicating whether an unsafe transition was attempted.

selinux_bprm_alloc_security and selinux_bprm_free_security

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.

selinux_bprm_set_security

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

SourceTargetPermission(s)
CurrentProgramFileexecute_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.

Table 6. Permission Checks if Task SID is changing on exec

SourceTargetPermission(s)
CurrentNewTaskSIDtransition
NewTaskSIDProgramFileentrypoint

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.

Table 7. Permission Checks if Task SID is changing on exec

SourceTargetPermission(s)
TracerTaskNewTaskSIDptrace
CurrentNewTaskSIDshare

selinux_bprm_post_apply_creds

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

SourceTargetPermission(s)
OldTaskSIDNewTaskSIDsiginh
OldTaskSIDNewTaskSIDrlimitinh

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.

selinux_bprm_secureexec

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.