April 17th, 2013

Audit2allow should be your third option not the first.

Whenever I talk about SELinux lately, I make everyone stand up and say.

SELinux is a labeling system.
Every process has a label every object on the system has a label.  Files, Directories, network ports ...
The SELinux policy controls how process labels interact with other labels on the system.
The kernel enforces the policy rules.

The follow up to this is, if you get a denial the First thing you should think is, perhaps one of the labels is WRONG.

I wrote a paper  a while ago called the SELinux Four Things. In which I point out the four causes of SELinux denials.

  1. You have a labeling problem.

  2. You have changed a process configuration, and you forgot to tell SELinux about it.

  3. You have a bug in either an application or SELinux policy.  Either SELinux policy did not know an application could do something, or the application is broken.

  4. You have been hacked.

The solution to #3 is to report a bug and use audit2allow to generate a local policy module and install it until either the application is fixed or SELinux policy adds rules to allow it.

The problem i see is that administrators seem to go to this option when ever they see a denial.   Lets look at a recent example from email.

In a recent email on selinux@lists.fedoraproject.org, Richard reported:

I have a CGI application named "mapserv" that needs to write to a specific location: "/rwg/mapserver/tmp"

He figured out that Apache content was usually labeled httpd_sys_content_t, which was a step in the right direction, so I guess he labeled this rectory tree as

Then he asked about writing policy that looked like.

module test 1.0;

require {
        type httpd_sys_content_t;
        type httpd_sys_script_t;
        class dir add_name;
        class file { write create };

#============= httpd_sys_script_t ==============
allow httpd_sys_script_t httpd_sys_content_t:dir add_name;
allow httpd_sys_script_t httpd_sys_content_t:file { write create };

This policy obviously came from audit2allow, and would work, except for a couple of problems, the biggest being that all CGI Scripts would be able to read write all Apache content.  The policy is also fragile since you might get an AVC like httpd_sys_script_t is not allowed to append to httpd_sys_content_t.

manage_files_pattern(httpd_sys_script_t, httpd_sys_content_t, httpd_sys_content_t) Would have been better.

Is there a better label I could have used?

But the main point of this blog would be that Richard should not have used a audit2allow module.  He could have used httpd_sys_rw_content_t for the tmp directory.

# semanage fcontext -a -t httpd_sys_rw_content_t "/rwg/mapserver/tmp(/.*?)"
# restorecon -R -v /rwg/mapserver/tmp

This would have been choice "1" above, one of the labels is wrong.

Could I change the process label?

Dominic Grift pointed out, in a mail reply, another solution.

cat > mywebapp.te << EOF
policy_module(mywebappp, 1.0.0)

make -f /usr/share/selinux/devel/Makefile mywebapp.pp
sudo semodule -i mywebapp

Now you can use the following new types:

httpd_mywebapp_script_t (mywebapp process type)
httpd_mywebapp_script_exec_t (mywebapp cgi executable file type)
httpd_mywebapp_content_t (mywebapp readonly file type)
httpd_mywebapp_content_rw_t (mywebapp read/write file type)
httpd_mywebapp_content_ra_t (mywebapp read/append file type)
httpd_mywebapp_htaccess_t (mywebapp htaccess file types)

Basically you can just label the cgi script with the mywebapp script executable file type and then the mywebapp process will run with the mywebapp process type creating files with the mywebapp content file types.

This solution satisfies "1", changing both the process label and the target label.  This is probably the best solution, since if Richard used other CGI scripts on his machine, only the mapserver cgi script would be able to write the mapserver cgi content, and he would have better separation.

Note:  I would have used sepolicy generate --cgi PATHTO/mapserver.cgi to generate the policy.

Could I modify the SELinux configuration to allow this access?

A third option would be to turn on the httpd_unified boolean.  (This would an option of type "2").  Although not the best solution for the problem.

httpd_unified is described as "Unify HTTPD handling of all content files."

# setsebool -P httpd_unified 1

This means it will allow the apache process and default apache scripts to read/write/execute all default labeled apache content.

Bottom line, when you see an SELinux AVC, the way your decision tree should go something like the following.

  1. Is the process that is being denied running with the correct label?  Could I make it run with a better label?

  2. Does the target object have the correct label or could I assign it a label that would allow the access from the process label?

  3. Is their an SELinux Boolean that would allow the access?

  4. If this is a network port being denied, did I modify the default settings and could I modify the labels on network packets using semanage port.

  5. Do I believe this is a bug in an application?  If yes, I need to report a bug?

  6. Is this a bug in SELinux policy and the access should be allowed by default?  I should install a local modification and report this as a bug.

  7. Am I being hacked?

setroubleshoot attempts to help you walk through this process, and newer versions of audit2allow show you booleans and potential labels you can use.

Understanding MCS Separation.

We designed a new way of using MCS, Multi Category Security, when we developed sVirt (Secure Virtualization) a few years ago.

To understand MCS it is helpful to understand something about MLS.

When Red Hat Enterprise Linux 5 came out, we had a done a lot of work adding MLS, Multi Level Security.  MLS requires the Belle & La Padula model of security.  This model takes into account the "Level and Category" of the data, and adds rules like a higher level process (TopSecret) is not allowed to write down (Secret) and a lower level (Secret) process is not allowed to read a higher level data (TopSecret).  The simple description of categories would be in order to read or write your process must have all the categories of the target.    Levels can go from s0 (SystemLow) to s15 (SystemHigh).  Then you can have any combination of 1024 categories.

In MCS we stole the idea of categories and dropped the concept of levels.

As I mentioned above the process category most include ALL of the categories of the target, otherwise MCS separation would block the access.   It is best to see an example of this.

Say I have a process labeled system_u:system_r:svirt_t:s0:c1,c2 from MCS Separation point of view.  This process would be allowed to access any file with any of these 4 MCS labels.

  • s0

  • s0:c1

  • s0:c2

  • s0:c1,c2

Since most files on a targeted system have the MCS label s0.  MCS Labeling does not block much mcs constrained applications.

By convention MCS confinement always uses two categories, and the categories can not be the same.  s0:c1,c1 == s0:c1.  Secondarily tools that launch MCS separate apps like libvirt and sandbox, make sure they never launch two processes with the same MCS label.  Which means we guarantee that two svirt_t always have two MCS labels that no other svirt_t or svirt_image_t would have.  Therefore a process running svirt_t:s0:c1,c2 would not be prevented by  MCS from access to any file with a MCS label of s0 or s0:c1,c2.  It would be denied from reading any file labeled s0:c1,c3 or s0:c2,c4 or s0:c4,c6.

But looking at MCS separation as the only control, misses out on type enforcement controls.

svirt_t access

We define our virtual machines as svirt_t type, and then we define rules about which types an svirt_t type is allowed access to.  If I wanted to see which types svirt_t is allowed to write, I could execute the following command.

# sesearch -A -s svirt_t -c file -p write -C | grep open | grep -v ^D
   allow virt_domain svirt_tmp_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow svirt_t xen_image_t : file { ioctl read write getattr lock append open } ;
   allow virt_domain svirt_image_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow virt_domain svirt_tmpfs_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow virt_domain virt_cache_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow virt_domain qemu_var_run_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow virt_domain anon_inodefs_t : file { ioctl read write getattr lock append open } ;
   allow virt_domain svirt_home_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow svirt_t svirt_t : file { ioctl read write getattr lock append open } ;
ET allow virt_domain dosfs_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ virt_use_usb ]
ET allow virt_domain usbfs_t : file { ioctl read write getattr lock append open } ; [ virt_use_usb ]

This means a confined virtual machine running as svirt_t:s0:c1,c2 would ONLY allowed to write to the following file types IFF they are MCS labeled s0 or s0:c1,c2

svirt_tmp_t, svirt_image_t, svirt_tmpfs_t, virt_cache_t, qemu_var_run_t, svirt_home_t,

dosfs_t and usbfs_t iff the virt_use_usb boolean is turned on, perhaps we should turn this off by default.

anon_inodefs_t and svirt_t are not file types, svirt_t is a process types which would be in /proc.

libvirt and the kernel need to ensure that non of these files get labeled with s0.

Openshift domains are allowed to write/open

Kernel file systems anon_inodefs_t, openshift_t,hugetlbfs_t,  security_t

openshift_tmp_t, openshift_rw_file_t, openshift_tmpfs_t

MCS Constrained Types

Thee following types are MCS Constrained.

seinfo  -amcs_constrained_type -x