SELinux/Tutorials/How does a process get into a certain context

From Gentoo Wiki
Jump to: navigation, search

How does a process get into a certain context

In the previous tutorials, we already got across the context in which a process resides, and which can be checked using utilities like ps -Z.

root #ps -eZ | grep auditd
system_u:system_r:kernel_t:s0     448 ?        00:00:04 kauditd
system_u:system_r:auditd_t:s0    6731 ?        00:00:22 auditd

But how do processes get into this context?

Context inheritance

First of all, know that SELinux supports inheritance of contexts. Furthermore more, inheritance of contexts is the default behavior: if there is no policy in SELinux that specifies otherwise, then anything created will inherit the context of its parent.

  • A process running with context foo_t spawning another process (forking) will cause creation of a new process with foo_t context (as long as SELinux has no rules specifying otherwise)
  • A file or directory created in a directory with context bar_t will get the bar_t context as well (as long as SELinux has no rules specifying otherwise)

This inheritance is always the case, even if the SELinux management utilities (but not SELinux policy rules) say otherwise: even if there is a context definition saying that /var/log/audit should be auditd_log_t, if you create this audit directory within /var/log then this directory will inherit the var_log_t context from its parent directory. Only later, when you run restorecon, will the context be changed.

Since we are talking about inheritance, there probably is a root context for everything. Well, there is - of course this is defined by the policy and SELinux support, but it is safe to assume that the root context (domain) for processes is the kernel_t context (as this is the context that the kernel runs as, and it is the kernel that spawns init, which itself spawns various other processes) and the root context for the file system is root_t.

user $ls -ldZ /
dr-xr-xr-x. root root system_u:object_r:root_t:s0      /

Transitioning

Of course, SELinux would be quite dull if it didn't support switching to other contexts. And in this tutorial we want to talk about process context (domain) transitions: the idea that a process, once created, is within a different context than its parent process.

SELinux supports domain transitions when a process forks (spawns another process). And when you execute something, we mean that a process referred to some file (most likely a binary or a script) saying "execute this", so that the referred file is loaded and 'becomes' a process.

What an SELinux policy writer can do in these cases is to define a domain transition, like so:

type_transition init_t initrc_exec_t : process initrc_t;

Which reads:

When an init_t process executes a file with context initrc_exec_t, 
then the resulting process should run in the initrc_t context.

Or, in some ASCII-art flow:

[init_t] --(execute initrc_exec_t)--> [initrc_t]

Basically what the SELinux policy writer wants to achieve is that, when the init process (running on the Linux system) executes an init script (to be found in /etc/rc.d/init.d or /etc/init.d) then this script will run in the initrc_t domain.

Such a statement defines a default transition (that overrides the 'inherit parent context' rule - it can be used for file/directory contexts as well), but one can also use tools like runcon or SELinux API directly to achieve a non-default domain transition as long as it is allowed (read on).

The need for the file context

In the previous examples, you notice that the file context of the target file (to be executed) is used. Remember the previous tutorials on file contexts? SELinux does not use paths internally - it always uses contexts. So a policy writer cannot use /etc/init.d/sysstat, but has to refer to the context that this file would have - initrc_exec_t.

The syntax used is again a naming convention, but is not mandatory. SELinux also doesn't parse the name; the context could very well be called garble_mumble_joy. Naming conventions though are interesting since they allow us mere humans to understand what the purpose is of the context. So in case of initrc_exec_t, we could read this as "the context for an executable file that, when executed, might have the resulting process run in initrc_t".

Real-life example: startup of SSH

Let's look at a real-life example: the start-up of the SSH daemon.

Basically, what happens when the SSH daemon is started through its service, is

  1. the Linux kernel executes /sbin/init, resulting in the init process
  2. the init process executes /etc/init.d/sshd, resulting in this init service script running for a (short) while
  3. this sshd init service script executes /usr/sbin/sshd, resulting in the sshd daemon

For SELinux, with the common policy loaded, this could be noted as:

[kernel_t] --(execute init_exec_t)--> [init_t]
[init_t] --(execute initrc_exec_t)--> [initrc_t]
[initrc_t] --(execute sshd_exec_t)--> [sshd_t]

What we are defining here are domain transitions from one domain to another, through the execution of a file.

When is a transition allowed

Now, such a transition can only occur when the following three requirements are satisfied.

  1. The origin domain has execute permission on the file
  2. The file context itself is identified as an entry point for the target domain
  3. The origin domain is allowed to transition to the target domain

If, and only if, all three requirements are satisfied, will such a domain transition be allowed to occur. Let's look at these three rules in more detail.

Execute permission on the file

As we have seen before, SELinux has a very fine-grained set of permissions that it can control. The execute permission on files is one of them. If a domain does not have execute permission on the file, then it will not be able to execute that file. Simple as that.

This too is a common source of denials or permission issues: a process tries to execute some file but is not allowed to. Again, as we learned in the past, this is very probably because of a wrong file context. On a Gentoo system, daemons that fail to execute files are most likely such problems because they are trying to execute a file with the lib_t context (often the case for files in /usr/lib somewhere) instead of the bin_t context.

So in case of the init script (initrc_t) calling the SSHd binary (sshd_exec_t):

root #sesearch -s initrc_t -t sshd_exec_t -c file -p execute -Ad
Found 1 semantic av rules:
   allow initrc_t sshd_exec_t : file { read getattr execute open } ;

Great, so the init service script is allowed to execute the SSHd binary.

Entrypoint permission for a domain on the file

By itself, executing a file with a specific label isn't sufficient to allow a domain transition. Remember that SELinux does not parse contexts itself - it has no knowledge that sshd_exec_t has anything to do with SSH daemons, let alone sshd_t. So we need to tell it that sshd_exec_t and sshd_t are related, by saying that the sshd_exec_t file is an entrypoint for the sshd_t domain.

root #sesearch -s sshd_t -t sshd_exec_t -c file -p entrypoint -Ad
Found 1 semantic av rules:
   allow sshd_t sshd_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ;

As a counter-example, checking if ssh_exec_t (which is used for the SSH client) is known as an entrypoint for the SSH daemon (sshd_t) shows no hits:

root #sesearch -s sshd_t -t ssh_exec_t -c file -p entrypoint -Ad

What we can do is ask sesearch for what domains the ssh_exec_t type is an entrypoint for.

root #sesearch -t ssh_exec_t -c file -p entrypoint -Ad
Found 5 semantic av rules:
   allow xm_ssh_t ssh_exec_t : file { ioctl read getattr lock execute entrypoint open } ;
   allow ssh_t ssh_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ;
   allow sge_job_ssh_t ssh_exec_t : file { ioctl read getattr lock execute entrypoint open } ;
   allow condor_startd_ssh_t ssh_exec_t : file { ioctl read getattr lock execute entrypoint open } ;
   allow nx_server_ssh_t ssh_exec_t : file { ioctl read getattr lock execute entrypoint open } ;

Process domain transition permission

The third requirement is that there must be an SELinux policy saying that that a transition from the source domain to the target domain is allowed. Let's look at the one from initrc_t to sshd_t:

root #sesearch -s initrc_t -t sshd_t -c process -p transition -Ad
Found 1 semantic av rules:
   allow initrc_t sshd_t : process { transition siginh } ;

Consider our previous example of the SSH client: the ssh_exec_t was entrypoint for multiple domains, including xm_ssh_t and nx_server_ssh_t. However, the domains that are allowed to execute the SSH client to transition to the xm_ssh_t and those that are allowed to execute the SSH client to transition to the nx_server_ssh_t are distinct.

root #sesearch -t xm_ssh_t -c process -p transition -Ad
Found 2 semantic av rules:
   allow xm_t xm_ssh_t : process { transition getattr } ;
   allow xm_ssh_t xm_ssh_t : process { fork transition sigchld sigkill sigstop signull signal getsched setsched getsession getpgid setpgid getcap setcap share getattr noatsecure siginh rlimitinh dyntransition execmem setkeycreate setsockcreate } ;
root #sesearch -t nx_server_ssh_t -c process -p transition -Ad
Found 2 semantic av rules:
   allow nx_server_t nx_server_ssh_t : process { transition getattr } ;
   allow nx_server_ssh_t nx_server_ssh_t : process { fork transition sigchld sigkill sigstop signull signal getsched setsched getsession getpgid setpgid getcap setcap share getattr noatsecure siginh rlimitinh dyntransition execmem setkeycreate setsockcreate } ;

What you need to remember

What you should remember from this tutorial is that

  • SELinux by default inherits contexts, be it from processes (on fork) or parent directories (on entry creation)
  • Context transition (change) can be triggered either by policy, by tools like runcon or via the SELinux API
  • Process context (domain) transition can happen only under the three conditions that
    • the target file context is executable for the source domain
    • the target file context is marked as an entrypoint for the target domain
    • the source domain is allowed to transition to the target domain