For security reasons, Linux systems can reduce the ability of processes to gain elevated privileges under certain situations or provide additional constraints to reduce the likelihood of vulnerabilities to be exploitable. SELinux developers, too, honor these situations.
Sanitizing environments on transition
When weexecute a higher-privileged command (be it asetuid
application or one where capabilities are added to the session), theGNU C library(glibc) willsanitize the environment. This means that a set of security-sensitive environment variables are discarded to make sure that attackers, malicious persons, or malicious applications cannot negatively influence the session.
Thissecure execution is controlled through anExecutable and Linkable Format(ELF) auxiliaryvector calledAT_SECURE. When set, environment variables such asLD_PRELOAD
,LD_AUDIT
,LD_DEBUG
,TMPDIR
, andNLSPATH
are removed from the session.
SELinux will force this sanitation on domain transitions as well, ensuring that the newly executed domain does not have access to these sensitive environment variables. Of course, sometimes the transitioned domain requires these variables. Not all domains can deal with sanitized environments, or use these environment variables to pass along important information, so always dropping the environment variables might result in unusable application domains.
To allow transitions without sanitizing the environment, thenoatsecure
permission can be granted to domain transitions. For instance, let’s consider the execution of a Firefox plugin:
# sesearch -t mozilla_plugin_t -p noatsecure -A
...
allow unconfined_t mozilla_plugin_t:process { ... noatsecure ...};
...
When an application running in the unconfined_t
domain executes the plugin (which results in a domain transition to mozilla_plugin_t
), the environment variables need to be kept as otherwise the plugin might not function properly. As such, the SELinux policy grants the noatsecure
permission to the domains that invoke Firefox plugins.
Disabling unconstrained transitions
A secondsecurity constraint that Linux supports is to mount a filesystem with thenosuid
option. When set, nosetuid
andsetgid
binaries on that filesystem will have any effect on the effective user or group ID of the executing session. Essentially, asetuid
application on a filesystem mounted withnosuid
will act as if nosetuid
bit is set.
To ensure that transitions triggered by applications hosted on anosuid
-mounted filesystem do not allow for elevated privileges, SELinux policy developers must explicitly mark a transition as allowed fornosuid
-mounted filesystems, using thenosuid_transition
permission. This permission is part of theprocess2
class:
$ sesearch -s unconfined_t -p nosuid_transition -A
allow unconfined_t initrc_t:process2 { nnp_transition nosuid_transition };
...
This allows policy developers to differentiate regular domain transitions from nosuid
-constrained domain transitions.
Note:
SELinux has a limit on the number of privileges that can be assigned to a class. When the number of privileges exceeds 32, the SELinux developers will create a different class and the permissions continue in this second class. Right now, the two classes that have more than 32 permissions are the capability
class and the process
class.
This permission-based approach might not be in place on all SELinux-enabled systems though. It is enabled when the nnp_nosuid_transition
policy capability is defined and set to 1
:
# cat /sys/fs/selinux/policy_capabilities/nnp_nosuid_transition
1
If this capability value is 0
, then SELinux will use a concept called type bounds to support domain transitions for applications hosted on nosuid
-mounted filesystems. Any executable with a file context that would result in a domain transition will only result in a domain transition if the target domain is bounded by the parent domain.
Note:
Policy capabilities cannot be tweaked by administrators. They are used by policy developers to inform the Linux kernel which behavior it expects. For the nnp_nosuid_transition
capability, the policy developer informs the kernel that the nosuid_transition
and nnp_transition
permission checks should be used rather than bounded domains, and that its policy will generally only include support for the transitions and not for the bounded domains.
If it is not bounded, then the domain transition will not occur, and the session will remain in the currentcontext (or the command will fail to execute if the application is not allowed to run in the current context).
Abounded domainis not just calculated live based on the permissions though. SELinux has anexplicit rule that enforces a target domain to be bounded by a parent domain. Even when permissions are later added to the bounded domain, they will be denied by the SELinux security subsystem if they aren’t part of the parent domain.
Withseinfo
, these type bounds can be listed as follows:
# seinfo --typebounds
Most distributions, however, do not have bounded domains defined in their SELinux policy anymore, as the new nosuid_transition
permission is much more flexible. The use of bounded domains required policy developers to extend the permissions of the parent domain every time the child domain needed to be extended, which was a major nuisance when the parent domain is a generic one (be it a container management platform or a system service daemon).
Using Linux’s NO_NEW_PRIVS
The useof filesystems mounted withnosuid
is a specific case of Linux’sNo New Privilege(NNP) support. NNP is a process-specificattribute that tells the Linux kernel that the process is no longer to be granted additional privileges. From that point onward, the constraints as mentioned before hold, and SELinux will only allow domain transitions if it has thennp_transition
permission, or toward a bounded domain if thennp_nosuid_transition
policy capability is not set.
The parameter can be set by applications themselves using the process control functionprctl()
, but the user can also influence this. Thesetpriv
command can be used to launch applications withPR_SET_NO_NEW_PRIVS
set (the parameter that applications can pass through theprctl()
function).
As an example, create the following simple Python-based CGI script in acgi-bin
directory inside a regular user’s home directory:
#!/usr/bin/env python3
import sys, time
import subprocess
import cgi, cgitb
cgitb.enable()
print('Content-Type: text/html;charset=utf-8\n')
PIPE = subprocess.PIPE
STDOUT = subprocess.STDOUT
pd = subprocess.Popen(['ping', '-c', '1', 'localhost'],
stdout=PIPE, stderr=STDOUT)
while True:
output = pd.stdout.read(1)
if output == '' and pd.poll() != None:
break
if output != '':
sys.stdout.write(output.decode('utf-8'))
sys.stdout.flush()
With this CGI script now available, first launch a simple CGI-capable web server (we will pick port 6020
as unprivileged users should be able to bind processes to this port) and connect to it:
$ python3 -m http.server --cgi 6020
In a different session, connect to the web server and call the newly created Python script (here named test.py
):
$ curl http://localhost:6020/cgi-bin/test.py
PING localhost(localhost(::1)) 56 data bytes ...
Now, launch the same CGI-capable web server, but with NNP enabled:
$ setpriv --no-new-privs python3 -m http.server --cgi 6020
Again, connect to the web server and call the test.py
CGI script:
$ curl http://localhost:6020/cgi-bin/test.py
ping: socket: Permission denied
Because Linux’s NNP is enabled, theping
command is not able to obtain the higher privileges needed to open the socket.
Sometimes, you’ll notice a denial for theexecute_no_trans
permission in the SELinux audit logs. This occurs when the SELinux policy does not allow an application to be executed without transitioning.