Redhat – SELinux: Getting a custom binary to switch to another context

redhatrhel7selinux

I am working on a new project that must be secured with SELinux. We have a custom binary written in C (for the sake of this question it will be called "testprog") that needs to switch to it's own context so that we can confine it's operations, rather than letting it run in the unconfined domain.

I have created a simple sample policy file based on the learning I have done so far, but bash failed to execute the binary.

Here is the policy file as it exists so far:

policy_module(testprog, 0.1.6)

require {
type unconfined_t;
class file { ioctl getattr setattr create read write unlink open relabelto };
class process transition;
type fs_t;
class filesystem getattr;
}

type testprog_t;
type testprog_exec_t;

allow testprog_t fs_t:filesystem getattr;
allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ;
type_transition unconfined_t testprog_exec_t : process testprog_t;
allow unconfined_t testprog_t : process transition ;

I have set the context of the binary itself as follows:

-rwxr-xr-x. root root system_u:object_r:testprog_exec_t:s0 /usr/bin/testprog

However if I try and execute the command from the bash shell, I get a permission denied error and denial's logged in audit.log:

[root@selinux-dev ~]# testprog
type=AVC msg=audit(1504546613.537:237): avc:  denied  { getattr } for  pid=1300 comm="bash" path="/usr/bin/testprog" dev="dm-0" ino=34637336 scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:testprog_exec_t:s0 tclass=file
type=SYSCALL msg=audit(1504546613.537:237): arch=c000003e syscall=4 success=no exit=-13 a0=2518ca0 a1=7ffd90223980 a2=7ffd90223980 a3=0 items=0 ppid=1296 pid=1300 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=2 comm="bash" exe="/usr/bin/bash" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
type=PROCTITLE msg=audit(1504546613.537:237): proctitle="-bash"
type=AVC msg=audit(1504546613.537:238): avc:  denied  { getattr } for  pid=1300 comm="bash" path="/usr/bin/testprog" dev="dm-0" ino=34637336 scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:testprog_exec_t:s0 tclass=file
type=SYSCALL msg=audit(1504546613.537:238): arch=c000003e syscall=4 success=no exit=-13 a0=2518ca0 a1=7ffd90223980 a2=7ffd90223980 a3=0 items=0 ppid=1296 pid=1300 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=2 comm="bash" exe="/usr/bin/bash" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
type=PROCTITLE msg=audit(1504546613.537:238): proctitle="-bash"
-bash: testprog: command not found

Clearly my policy is a little over the top. Ultimately I want to be able to run the binary from systemd (my unit file, very simple, is shown below), but I want to transition the process from a bash shell and from systemd into the testprog_t type I have created.

[Unit]
Description=SELinux Test Program

[Service]
#Type=forking
# The PID file is optional, but recommended in the manpage
# "so that systemd can identify the main process of the daemon"
PIDFile=/var/run/testprog.pid
ExecStart=/usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid

[Install]
WantedBy=multi-user.target

Please can anyone help me see where I have gone wrong? Underlying operating system is RHEL 7.4

Thanks!

Best Answer

I got the transition working in the end thanks to a number of references - it actually turned out to be incredibly simple and I guess you could say related to a basic rule of good programming?

In the policy I posted in the question, I had defined two new types:

type testprog_t;
type testprog_exec_t;

And then proceeded to allow various things to happen with these types, and also specified a type transition:

allow testprog_t fs_t:filesystem getattr;
allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ;
type_transition unconfined_t testprog_exec_t : process testprog_t;
allow unconfined_t testprog_t : process transition ;

However at no point had I actually told SELinux what these types actually were. For example I set the context of the /usr/bin/testprog file to system_u:object_r:testprog_exec_t:s0 but I never actually told SELinux through my policy that testprog_exec_t was a file. As soon as I changed the policy by adding type specifications, things began to look more promising. I also needed to use the role statement to allow testprog_t to run under the unconfined_r role that shell commands typically run under on a RHEL system in targeted SELinux mode. The current fragment of the policy relating to this look like this:

# Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file
type testprog_t;
domain_type(testprog_t);
type testprog_exec_t;
files_type(testprog_exec_t);
type testprog_etc_t;
files_type(testprog_etc_t);
type testprog_var_run_t;
files_type(testprog_var_run_t);
type testprog_data_t;
files_type(testprog_data_t);

# Allow the testprog_t type under the unconfined_r role
role unconfined_r types testprog_t;

# Tell SELinux that testprog_exec_t is an entrypoint to the tetprog_t domain
allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ;
# Make the type transition from unconfined_t (i.e. user shell) to testprog_t
type_transition unconfined_t testprog_exec_t : process testprog_t;
# Explicitly allow the type transition we have just created
allow unconfined_t testprog_t : process transition ;

With this done, the type transition happens perfectly and the job of completing the policy has become one of using sealert to explore the behaviour of the program and to put the right policies in place to allow it to work as desired.

I am hoping to put together a full worked example of a non-SELinux aware application that needs to run in it's own confined context rather than unconfined for my colleagues (and anyone else who needs it) - it's all rather skeletal right now but if anyone is interested in tracking it you can find the code here:

https://github.com/jamesfreeman959/selinux-testprog