Notes on SElinux: Multi-Category Security

SElinux, and Mandatory Access Control (MAC) in general, is complex. In some sense it mirrors the complexity of the underlying software and system it is protecting. It is not very system user friendly, and it is certainly not end user friendly. Adding additional complexity such as Multi-Level Security (MLS) makes the situation considerably worse. Most system administrators would like their end users not even to know MAC is present.

Involving end users in the operation of an organisation's system security can be useful. It not only gives the end user a sense of being part of the security process, but it also draws on a greater depth of knowledge across an organisation. People dealing with secure data may have more idea on how it should be treated than system administrators or even general security/risk managers. Providing an infrastructure that is not as hierarchical and complex as MLS, but provides the ability for end users to apply their knowledge within the restricted MAC framework can lead to a greater level of security than MAC alone.

Multi-Category Security (MCS) is a model within the general mandatory access control system that can provide an additional level of (optionally) user lead security. {Technically, it is an abstraction of MLS, but doesn't provide security levels nor the Bell La-Padula model.}

MCS in a nutshell: MCS allows users to be able to label certain objects (e.g. files) with "categories". Only processes that are also labelled with those same categories can gain access to those objects. Process are labelled with categories by giving users sets of categories, so that any process/program that user starts inherits their set of categories.

The default fedora targeted policy uses an MCS model. When you look at a security context of a file in a default fedora set up, there is no indication you are running with MCS:

#ls -Z /usr/bin/bzip2 -rwxr-xr-x root root system_u:object_r:bin_t /usr/bin/bzip2

As MCS is an abstraction/simplification of MLS, it uses the 4th field in the security context in a similar manner to MLS. The default targeted policy hides this field. It can be seen if we look at the extended file system attributes directly ("extended attributes" are where the security contexts for files are actually stored):

#getfattr -d -m ".*" /usr/bin/bzip2 security.selinux="system_u:object_r:bin_t:s0\000"

This shows that the file bzip2 has security level s0. As MCS does not implement security levels (unlike the full MLS) yet is a simplification of MLS, every object and process in a MCS system is given the same security level s0.


In the targeted policy of SElinux on fedora, the definition of "human readable" categories is made in /etc/selinux/targeted/setrans.conf. By default there are 1024 categories named c0 to c1023. Human readable names can be assigned to these default categories in this file. Categories can also be defined as ranges over categories. A key default category called SystemLow-SystemHigh is in fact a range over all categories (see below for more on ranges). In this example we define five human readable categories by adding the following lines to the setrans.conf file:

# new categories s0:c0=GeneralConfidential s0:c1=UserConfidential s0:c2=AccountsConfidential s0:c3=SystemConfidential s0:c4=Restricted

For example, this creates a human readable category "AccountsConfidential" that actually corresponds to real category c2. Note that security level "s0" is used in each definition as this is the only level MCS operates with. Although a reboot should not be necessary after making these changes, on some systems it may be required before the chcat command works as below.

All users by default are assigned no categories. This is evident form the seusers configuration file, or can be seen using the semanage command:

#semanage login -l Login Name SELinux User MLS/MCS Range __default__ user_u s0 root root SystemLow-SystemHigh system_u system_u SystemLow-SystemHigh

From this default configuration, all users (except root) are assigned the user_u identity with no categories (the MLS/MCS Range is simply s0 without any associated categories). Note that the root user has access to all categories on the system. In order to assign categories to specific users we need to be able to distinguish them. We do not need to create new SElinux user identities (although we could do this). Rather, we can simply map system login names to the default user_u identity. A "map" says, for example, that when user "domenico" logs in he gets the SElinux identity user_u. Given this is the default identity, he would have got it anyway, but it is the map that allows us to refer to specific users. We manipulate the login map using the semanage command:

#semanage login -a domenico #semanage login -a johann

This explicitly maps the users "domenico" and "johann" to the default SElinux identity (user_u). This can be seen by listing the SElinux logins again:

#semanage login -l Login Name SELinux User MLS/MCS Range __default__ user_u s0 domenico user_u s0 johann user_u s0 root root SystemLow-SystemHigh system_u system_u SystemLow-SystemHigh

Users can then be assigned categories using the chcat command:

#chcat -l -- +UserConfidential,+SystemConfidential domenico #chcat -l -- +UserConfidential,+AccountsConfidential johann

The double dash is simply a method of saying "don't process any more arguments" so that when categories are removed (e.g. using -UserConfidential) the minus sign does not get interpreted as an argument (its a unix/gnu/linux thing).

Using MCS

As with the rest of SElinux, MCS works on top of the ordinary DAC (discretionary access control) security. Only if access is allowed by the normal linux DAC will SElinux and MCS even be consulted. The simplest example of using MCS is in a similar manner to Access Control Lists (ACLs). Suppose user johann creates two files in /tmp:

johann#echo "don't lend money to domenico" > /tmp/accounts-admin johann#echo "domenico balance: -30"> /tmp/accounts-users

Listing the files shows they have johann's default DAC permissions:

#ls -l /tmp/accounts* -rw-r--r-- 1 johann staff 29 Jul 23 19:13 /tmp/accounts-admin -rw-r--r-- 1 johann staff 22 Jul 23 19:14 /tmp/accounts-users

These permissions allow anyone to read these files. User johann can add categories to these files, restricting how they are used:

johann#chcat -- +AccountsConfidential /tmp/accounts-admin johann#chcat -- +UserConfidential /tmp/accounts-users

The category UserConfidential is associated with user domenico, but the AccountsConfidential category is not. This means user domenico can read the accounts-users file but not the accounts-admin:

domenico#cat /tmp/accounts-admin cat: /tmp/accounts-admin: Permission denied domenico#cat /tmp/accounts-users domenico balance: -30

Furthermore, the denied read access of the file accounts-admin is logged in the normal SElinux manner using the audit logs.

Category Ranges

Following from the simple example above, if you look at the processes owned by the users domenico and johann (using ps -Z) they actually run with a range of categories (e.g. for user johann):

#ps -Z user_u:system_r:unconfined_t:s0-AccountsConfidential,UserConfidential 3764 pts/5 00:00:00 bash

The categories are given as a range, s0-AccountsConfidential,UserConfidential, i.e. ranging from s0 in other words "no categories", to the group of categories AccountsConfidential,UserConfidential. For processes in MCS, the high level of the range determines the access granted to files, and the low level determines the default level of files that are created. For example, processes belonging to user johann will create files/directories with no assigned categories, but use the group AccountsConfidential,UserConfidential when accessing objects. Note that the range is between groups of categories. This can be seen by looking at the default SystemLow-SystemHigh range, defined (from the setrans.conf file):


The first (lower) group in the range is from s0 (i.e. an empty group of no categories) to s0:c0.c1023 (i.e. a group of all categories; the dot groups all categories from the start c0 to the final c1024).

Suppose we modify the user mapping for johann. We specify that user johann has access to all categories we defined above (the upper end of the range) and by default creates files with the AccountsConfidential category (the lower end of the range):

#semanage login -m -s user_u -r s0:c2-s0:c1.c4 johann

Here, we use the semanage command to modify the login mapping for user johann. The -r argument specifies the category range which is given in the raw form (e.g. using c2 instead of AccountsConfidential).

Any files created by user johann will be inaccessible by user domenico unless johann modifies their category to a group that domenico has access to. For user domenico to access the file, his process must have a group of categories that is a super-set of the categories associated with the file. For example, user johann creates a file:

johann#echo "domenico has paid his balance" > /tmp/accounts-notices

This is created with user johann's low-end group containing only AccountsConfidential. User domenico cannot access this file as AccountsConfidential is not in his high-end group (which only contains UserConfidential and SystemConfidential). Even if user johann then adds the UserConfidential category to the file:

johann#chcat -- +UserConfidential accounts-notices

user domenico can still not access the file as his categories are not a super set of the categories associated with the file:

#ls -Z accounts-notices -rw-r--r-- johann staff user_u:object_r:tmp_t:UserConfidential,AccountsConfidential accounts-notices

User domenico's group of categories contains UserConfidential and SystemConfidential which is not a super-set of UserConfidential and AccountsConfidential. If user johann removes the AccountsConfidential category from the file, domenico will be able to read it:

johann#chcat -- -AccountsConfidential accounts-notices