The example used in addressing an SELinux constraint violation touched on giving mount_t the attributes 'mcsreadall' and 'mcswriteall' by way of incorporating calls to the interfaces mcs_file_read_all() and mcs_file_write_all() with the argument 'mount_t'. In part four of that example, one of the most important parts of changing SELinux policy, determining if the modification is acceptable, was glossed over. That resulted in a number of questions, so I will try to address it here.

The interfaces mcs_file_read_all() and mcs_file_write_all() allow the type provided as an argument to bypass the constraints which make categories restrictive. This means, if a type, let's say user_t, was granted mcs_file_read_all(), a user confined by user_t would be able to read files labeled with any category (provided they otherwise had access), and not just those categories to which they had been granted access.

It would be the functional equivalent of granting every instance of user_t SystemLow-SystemHigh (c0.c1024). That would obviously be less than desirable. If you had taken the time to label sensitive files into categories like Finance and Mind_Control_Research, the intent was clearly to protect those files from being read by users not explicitly granted access to those categories.

So, clearly we don't want to be assigning the attributes 'mcsreadall' and 'mcswriteall' to any random type, the grant needs to be carefully considered. In this case, that means, is it appropriate to grant anything labeled 'mount_t' the attributes 'mcsreadall' and 'mcswriteall'?

Ok, well, what is allowed to run as 'mount_t'? Let's start with what can transition to the type. It turns out, a few things:

sesearch -C -T | grep "process mount_t" | sort | uniq
   type_transition automount_t fusermount_exec_t : process mount_t; 
   type_transition automount_t mount_exec_t : process mount_t; 
   type_transition bootloader_t fusermount_exec_t : process mount_t; 
   type_transition bootloader_t mount_exec_t : process mount_t; 
   type_transition crond_t mount_exec_t : process mount_t; 
   type_transition devicekit_disk_t fusermount_exec_t : process mount_t; 
   type_transition devicekit_disk_t mount_exec_t : process mount_t; 
   type_transition hald_t fusermount_exec_t : process mount_t; 
   type_transition hald_t mount_exec_t : process mount_t; 
   type_transition hotplug_t fusermount_exec_t : process mount_t; 
   type_transition hotplug_t mount_exec_t : process mount_t; 
   type_transition initrc_t mount_exec_t : process mount_t; 
   type_transition insmod_t fusermount_exec_t : process mount_t; 
   type_transition insmod_t mount_exec_t : process mount_t; 
   type_transition livecd_t fusermount_exec_t : process mount_t; 
   type_transition livecd_t mount_exec_t : process mount_t; 
   type_transition local_login_t fusermount_exec_t : process mount_t; 
   type_transition local_login_t mount_exec_t : process mount_t; 
   type_transition puppet_t fusermount_exec_t : process mount_t; 
   type_transition puppet_t mount_exec_t : process mount_t; 
   type_transition remote_login_t fusermount_exec_t : process mount_t; 
   type_transition remote_login_t mount_exec_t : process mount_t; 
   type_transition rgmanager_t fusermount_exec_t : process mount_t; 
   type_transition rgmanager_t mount_exec_t : process mount_t; 
   type_transition ricci_modcluster_t fusermount_exec_t : process mount_t; 
   type_transition ricci_modcluster_t mount_exec_t : process mount_t; 
   type_transition ricci_modstorage_t fusermount_exec_t : process mount_t; 
   type_transition ricci_modstorage_t mount_exec_t : process mount_t; 
   type_transition rlogind_t fusermount_exec_t : process mount_t; 
   type_transition rlogind_t mount_exec_t : process mount_t; 
   type_transition rshd_t fusermount_exec_t : process mount_t; 
   type_transition rshd_t mount_exec_t : process mount_t; 
   type_transition sosreport_t fusermount_exec_t : process mount_t; 
   type_transition sosreport_t mount_exec_t : process mount_t; 
   type_transition sshd_t fusermount_exec_t : process mount_t; 
   type_transition sshd_t mount_exec_t : process mount_t; 
   type_transition staff_t fusermount_exec_t : process mount_t; 
   type_transition sysadm_t fusermount_exec_t : process mount_t; 
   type_transition sysadm_t mount_exec_t : process mount_t; 
   type_transition system_cronjob_t mount_exec_t : process mount_t; 
   type_transition udev_t fusermount_exec_t : process mount_t; 
   type_transition udev_t mount_exec_t : process mount_t; 
   type_transition user_t fusermount_exec_t : process mount_t; 
   type_transition xdm_t fusermount_exec_t : process mount_t; 
   type_transition xdm_t mount_exec_t : process mount_t; 
   type_transition xend_t fusermount_exec_t : process mount_t; 
   type_transition xend_t mount_exec_t : process mount_t; 

Each one of those lines indicates the second column, for instance, 'user_t', can transition to 'mount_t', by executing a binary labeled by the third column. In the case of 'user_t', it can transition to 'mount_t' by calling any binary labeled 'fusermount_exec_t'.

Alright then, there are quite a few source types, so let's first focus on the transition points. There seem to be a lot fewer of those.

$ sesearch -C -T | grep "process mount_t" | awk '{ print $3 }' | sort -u
fusermount_exec_t
mount_exec_t

Two, that is manageable. Unfortunately it just tells us that the binaries in question are going to be labeled 'fusermount_exec_t' and 'mount_exec_t', not which binaries. Of course based on the name, you can probably guess which binaries are going to be labeled with those two types, but let's check anyway.

$ egrep -h ':(fuser)?mount_exec_t' /etc/selinux/targeted/contexts/files/*
/bin/mount.*    --  system_u:object_r:mount_exec_t:s0
/bin/umount.*   --  system_u:object_r:mount_exec_t:s0
/sbin/mount.*   --  system_u:object_r:mount_exec_t:s0
/sbin/umount.*  --  system_u:object_r:mount_exec_t:s0
/bin/fusermount --  system_u:object_r:fusermount_exec_t:s0
/usr/bin/fusermount --  system_u:object_r:fusermount_exec_t:s0

Raise your hand if you didn't see that coming. Right, moving on. We are still trying to get an idea of how much we might regret granting 'mount_t' the ability to bypass all category security, and therefor trying to identify what paths exist from a malicious attacker to the type 'mount_t'.

Right now we know that a bad guy running as any of the following could transition to 'mount_t' by way of calling binaries labeled 'mount_exec_t' and 'fusermount_exec_t':

automount_t
bootloader_t
crond_t
devicekit_disk_t
hald_t
hotplug_t
initrc_t
insmod_t
livecd_t
local_login_t
puppet_t
remote_login_t
rgmanager_t
ricci_modcluster_t
ricci_modstorage_t
rlogind_t
rshd_t
sosreport_t
sshd_t
staff_t
sysadm_t
system_cronjob_t
udev_t
user_t
xdm_t
xend_t

The notable exceptions here are that 'staff_t' and 'user_t' can only use 'fusermount_exec_t', not 'mount_exec_t', and 'crond_t' which can only use 'mount_exec_t'.

Of course beyond just calling something labeled with these exec types, there would have to be flaw in the labeled binary that the malicious individual could exploit. Just to show how this would work, let's make an example.

As root, fake a buggy file labeled 'fusermount_exec_t'.

$ cat << EOF > /scratch/buggymount
#!/bin/bash
id -Z
cat /scratch/tmp/file
EOF
$ chmod 755 /scratch/buggymount
$ chcon -u system_u -r object_r -t fusermount_exec_t /scratch/buggymount

Then create a classified file

$ echo "classified" > /scratch/tmp/file
$ chcat +c2 /scratch/tmp/file
$ ls -lZ /scratch/tmp/file | cut -d\  -f 4-
staff_u:object_r:user_tmp_t:s0:c2 /scratch/tmp/file

Trying to read it via an unprivileged shell generates an AVC that is a constraint violation, as does trying to use our fake buggy mount binary.

$ runcon -l s0 /bin/zsh
$ id -Z
staff_u:staff_r:staff_t:s0
$ cat /scratch/tmp/file
cat: /scratch/tmp/file: Permission denied
$ /scratch/buggymount
context=staff_u:staff_r:mount_t:s0:c0
cat: /scratch/tmp/file: Permission denied

However, if the patch from earlier has been applied:

$ runcon -l s0 /bin/zsh
$ id -Z
staff_u:staff_r:staff_t:s0
$ cat /scratch/tmp/file
cat: /scratch/tmp/file: Permission denied
$ /scratch/buggymount
context=staff_u:staff_r:mount_t:s0:c0
classified

Do you care? Well, it depends on how paranoid you are (or what your business requirements dictate). As covered earlier the automount problem which originally exposed this constraint violation, can be fixed with a much smaller permission grant. There is likely no need to give mount_t an MCS override for anything more than 'search' on directories. However, there does not currently exist a way to do that without creating a different attribute, splitting up the constraint definitions, and adding an interface. Only then could we give 'mount_t' just the permissions it actually requires.

Such a change would be pretty invasive, so, as many things in computer security do, it boils down to risk management. In a perfect world, permissions should only be as wide as they absolutely need to be. Of course in a perfect world, code wouldn't have bugs, so there would be no way to trick mount into doing something malicious on your behalf, and we would have unlimited resources to develop precisely defined minimal permissions.

Realistically, it is a trade off. Granting 'mount_t' the attribute 'mcsreadall' is a lot better solution than turning off selinux, and the hole it opens is not large. The number of binaries labeled 'mount_t' are similarly small, and most have been around for many years and are heavily audited. That does not mean a bug does not exist or won't be introduced, but it does not exactly have the same security footprint of a web browser. Of course, /bin/fusermount is generally installed suid, which is always a good reason to step up the paranoia.

That is great and all, but what did the experts decide was best in this case? They granted the attributes 'mcsreadall' and 'mcswriteall' to 'mount_t' by way of the interfaces 'mcs_file_read_all()' and 'mcs_file_write_all()'.