After spending a week in Washington learning the darkest secrets of SELinux, I can see why a lot of people “just turn it off”. This is terrible. SELinux can save your @$$ in the event that you get hit with a 0-day. Most admins don’t understand SELinux, other than that “it breaks stuff”, and just disable it. It’s understandable, since most people don’t work for the DoD or other agencies where Mandatory Access Controls are used. SELinux is not Discretionary Access Control Lists (DACLs) like in Windows, or UGO- style controls in Unix… or even POSIX ACLs. SELinux labels are much more powerful. Properly configured and running, SELinux can prevent root from doing everyday root things like cat’ing /etc/shadow. So imagine what SELinux can do if someone pops your webserver. Without SELinux, they would have the access privileges of the Apache user. But with SELinux, they can only access files and ports that the underlying httpd process is allowed to access.
So let’s try to pull the veil of mystery back on SELinux….SElinux uses 5 “labels”- user:role:type:sensitivity:classification. Generally, we only deal with the first three, and then usually only the type. Everything on a Linux system will be labelled- files, processes, ports, etc… everything.
We can view an object’s SELinux context with the -Z switch for most commands:
[root@localhost sbin]# ls -Z httpd
-rwxr-xr-x root root system_u:object_r:httpd_exec_t httpd
Here, the Apache binary is of the system_u user, object_r role and httpd_exec_t type. We’ll make sense of all this in a second.
Users have contexts:
[root@localhost sbin]# id -Z
The SystemLow-SystemHigh is a classification and not normally used. Don’t sweat it. The root user is of the root user type (duh), system_r role and unconfined_t type. Rule #1 for Selinux- a process launched by a user or other process assumes the context of whatever launched it. So normally, our beloved “vi” looks like this:
[root@localhost sbin]# ls -Z /bin/vi
-rwxr-xr-x root root system_u:object_r:bin_t /bin/vi
vi is of the bin_t type, but if root launches it:
[root@localhost sbin]# ps axZ|grep vi
root:system_r:unconfined_t:SystemLow-SystemHigh 28030 pts/0 T 0:00 vi
The process assumes root’s context. The unconfined_t type is pretty much that- it pretty much isn’t confined by other policies. In more strict SELinux policies, this could change.
Rule #2- anything that is not explicitly allowed in SELinux is denied, much like a firewall.
So let’s take a really simple example. Forget rule #1 for just a moment. Say that we have a daemon “myservice” that is of the myservice_t type, and its config file is kept in /etc/myservice/myservice.conf. This file has inherited the etc_t label. With a stock SELinux policy, if the daemon tried to launch, SELinux would block the daemon from reading /etc/myservice/myservice.conf and fail to start. What we need is to write a policy that allows myservice_t type to read etc_t type files.
A part of a new policy module to allow this would look like:
allow myservice_t etc_t:dir read; allow myservice_t etc_t:file read;
This allows the myservice_t type to read directories and files that are labelled with the etc_t. See now, with a little effort, we can get VERY granular on what processes are allowed to touch files. See above, the process is only able to READ files, SELinux will prevent writes to that file and directory even if the Unix file perms are 777.
So now, some of you may be asking “What about rule #2? If you launched that as root, it should inherit the unconfined_t type and be able to read that file!”. YES! You are correct. Same situation with initrc. It has its own type, and is pretty much unconfined. It follows that every daemon started by init should inherit the initrc_t type. This would be bad. Fortunately, we have what is called “transition”, that allows a process to assume a new type. Let’s take Apache for example…
Starting Apache either through init, or root issuing “service httpd start”, Apache should inherit initrc_t or unconfined_t, despite being labelled as httpd_exec_t. But in SELinux, we can dictate that something labeled of a particular type (httpd_exec_t) transition to a new type (httpd_t) when started up by init or root. So Apache, when started up will assume the httpd_t label.
[root@localhost sbin]# ps -ZC httpd LABEL PID TTY TIME CMD user_u:system_r:httpd_t 3878 ? 00:00:00 httpd
Here, we can see that httpd, with the httpd_exec_t type, has transitioned to httpd_t and not inherited initrc_t or unconfined_t. It looks really confusing, but here is the policy that handles this:
type httpd_exec_type, file_type, exec_type; type httpd_t, domain; init_daemon_domain(httpd_t, httpd_exec_t); domain_auto_trans(unconfined_t, httpd_exec_t, httpd_t);
It probably makes no sense now, but this basically says that whenever something of httpd_exec_t is launched by something of initrc_t or unconfined_t, to change its type to httpd_t. Now we can write a policy that allows httpd_t to do only what it needs to do to do its job. We can label all the files in /var/www/html with a particular label and then allow httpd_t to only READ the files. Let’s label the files as httpd_sys_content_t and look at some policy
allow httpd_t httpd_sys_content_t:file read;
Think about this for a moment. Even if all the files in /var/www/html are owned by Apache and chmodded 777, SELinux will prevent httpd_t from WRITING to those files. If a 0-day comes out that allows an attacker to access the filesystem as Apache, he’s also accessing the filesystem as httpd_t, and SELinux won’t allow him to change the contents of your website, since httpd_t is only allowed READ access. He also can’t do much outside of /var/www/html, since we haven’t allowed httpd_t to access anything else other than httpd_sys_content_t types.