Paranoid Penguin - Introduction to SELinux

Invest some time into SELinux and worry less about zero-day attacks.
Security Contexts: Users, Roles and Domains

Every individual subject and object controlled by SELinux is governed by a security context, each consisting of a user, a role and a domain (also called a type).

A user is what you'd expect: an individual user, whether human or dæmon. However, SELinux maintains its own list of users separate from the Linux DAC system. In security contexts for subjects, the user label indicates which SELinux user account's privileges the subject (which, again, must be a process) is running. In security contexts for objects, the user label indicates which SELinux user account owns the object.

A role is sort of like a group in the Linux DAC system, in that a role may be assumed by any of a number of pre-authorized users, each of whom may be authorized to assume different roles at different times. The difference is that in SELinux, a user may assume only one role at a time, and may switch roles only if and when authorized to do so. The role specified in a security context indicates which role the specified user is operating within for that particular context.

Finally, a domain is sort of like a sandbox: a combination of subjects and objects that may interact with each other. Domains are also called types, and although domains and types are two different things in the Flask security model (on which the NSA based SELinux), in SELinux domain and type are synonymous.

This model, in which each process (subject) is assigned to a domain, wherein only certain operations are permitted, is called Type Enforcement (TE), and it's the heart of SELinux. Type Enforcement also constitutes the bulk of the SELinux implementation in Fedora and Red Hat Enterprise Linux.

There's a bit more to it than that, but before I go any further, I want to use an example scenario to illustrate security contexts.

Suppose we're securing my LED-blinking dæmon, blinkend, with SELinux. As you'll recall, it's run with the privileges of the account someguy, and it reads the messages it blinks from a text file, which we'll call /home/someguy/messages.txt.

Under SELinux, we'll need an SELinux user called someguy (remember, this is in addition to the underlying Linux DAC's someguy account—that is, the one in /etc/passwd). We'll also need a role for someguy to assume in this context; we could call it blink_r (by convention, SELinux role names end with _r).

The heart of blinkend's security context will be its domain, which we'll call blinkend_t (by convention, SELinux domain names end with _t—t is short for type). blinkend_t will specify rules that allow the blinkend process to read the file /home/someguy/messages.txt and then write data to, say, /dev/numlockled.

The file /home/someguy/messages.txt and the special file /dev/numlockled will need security contexts of their own. Both of these contexts can probably use the blinkend_t domain, but because they describe objects, not subjects, they'll specify the catch-all role object_r. Objects, which by definition are passive in nature (stuff gets done to them, not the other way around), generally don't assume meaningful roles, but every security context must include a role.

Decision Making in SELinux

There are two types of decisions SELinux must make concerning subjects, domains and objects: access decisions and transition decisions. Access decisions involve subjects doing things to objects that already exist or creating new things that remain in the expected domain. Access decisions are easy to understand. In our example, “can blinkend read /home/someguy/messages.txt?” is just such a decision.

Transition decisions, however, are a bit more subtle. They involve the invocation of processes in different domains than the one in which the subject process is running or the creation of objects in different types than their parent directories. (Note: even though domain and type are synonymous in SELinux, by convention we usually use domain when talking about processes and type when discussing files.)

That is to say, normally, if one process executes another, the second process will, by default, run within the same SELinux domain. If, for example, blinkend spawns a child process, the child process will run in the blinkend_t domain, the same as its parent. If, however, blinkend tries to spawn a process into some other domain, SELinux will need to make a domain transition decision to determine whether to allow this. Like everything else, transitions must be authorized explicitly in the SELinux policy. This is an important check against privilege-escalation attacks.

File transitions work in a similar way. If a subject creates a file in some directory (and if this file creation is allowed in the subject's domain), the new file normally will inherit the security context (user, role and domain) of the parent directory. For example, if blinkend's security context allows it to write a new file in /home/someguy/, say, /home/someguy/error.log, then error.log will inherit the security context (user, role and type) of /home/someguy/. If, for some reason, blinkend tries to label error.log with a different security context, SELinux will need to make a type transition decision.

Get the picture? Transition decisions are necessary because the same file or resource may be used in multiple domains/types; process and file transitions are a normal part of system operation. But, if domains can be changed arbitrarily, attackers will have a much easier time doing mischief.