Notes on SElinux: policies and modules

A policy for SElinux contains all the definitions for user identities, object types, process types, and roles. It also contains all the rules that specify how types interact (see the notes on type enforcement). The ability to dynamically change or introduce new policy components into the current system policy is an important recent feature of SElinux. There are now self-contained policy modules that can be loaded and which safely interact with the currently active policy. In fact, loaded modules become part of the current active policy.

SElinux policies are big and complex. There is no getting away from this because the task SElinux has of implementing Mandatory Access Control (MAC) for linux is a big and complex task! In the targeted policy of fedora, most of the policy is contained in a single large module called the base module. This module is in a file called "base.pp" in /etc/selinux/targeted/modules/active (see notes on configuration files).

The strict and targeted policies

SElinux is designed to be strict and deny all requests that are not explicitly allowed by some rule. All rules are rules that allow. This has the advantage that everything that happens is explicitly allowed by some rule and with appropriate rules the system can be very secure. The original implementation of this effected everything on the running system, all processes from boot up to shut-down. This is termed the strict policy. For servers that run only a few unchanging services this is great. A compromise of one service can be isolated from the others and cause minimal damage and the policy for a system with a restricted number of services can be relatively straight forward. However, on a desktop or more general purpose machine where there are hundreds of possible services and applications potentially being run, the complexity of a strict policy can become unwieldy. Enter the targeted policy.

As I mentioned in the introductory notes, the targeted policy is a way of using SElinux that allows the bulk of user programs to run as if they were only protected by the standard linux security (DAC). Targeted services like http, ftp, ntp, samba, etc. are run within more isolated SElinux types/roles so that the primary network/service routes into the machine are protected. In the targeted policy all processes by default get the type unconfined_t. In essence this type is unrestricted so that any process with this type runs with the standard linux security (DAC). When daemons and services start they make a transition to more restricted types. For example, apache processes (httpd) run with the httpd_t type. This creates the situation where every process that isn't specifically isolated by some context transition is "unconfined" by SElinux and only the default linux security applies.

The advantage of the targeted policy is that desktop and general purpose machines remain fairly usable from a user perspective and have their major services restricted with SElinux. The disadvantage is that it is not as secure as the strict policy. Strict policies should still be run on servers and machines performing a limited variety of tasks.


A module should contain all the required type and role declarations, specifications of how objects are labelled, and the transition and access rules required for protecting a particular target program or process. A module must also allow the rest of the policy (i.e. the base and other modules) to be aware of the types and rules it defines. There are three types of files used to specify a module:

  • Type Enforcement file (.te extension) this contains the types and rules for type interactions for the target program
  • Interface files (.if extension) these files provide an "interface" so other modules can interact/use this module and its types if necessary. For example, the modules governing aspects of the X11 server provide interfaces so that modules governing programs that use the X11 graphical system can have access to specific X server types/resources without having to explicitly know what these types are.
  • File Context file (.fc extension) specifies how files are labelled with appropriate contexts for this module (if any are)

Type enforcement declarations

Remember that everything in Type Enforcement comes down to types. Types must be declared or created before rules can be written about them. The keyword type is used to declare a type. A type declaration has the form:

type type-name [, attributes] [alias aliases] ;

where type-name is the name of the type being declared. For example,

type mozilla_t;

declares the mozilla_t type. Types may have aliases so that multiple names refer to the same type. Types can also be assigned attributes.

There is a useful way of grouping security types by assigning different types a common attribute when they share certain common characteristics. Grouping in this way means you can easily refer to all types that have a certain characteristic by simply referring to the appropriate shared attribute. For example, all types that are assigned to processes are given the attribute called "domain". This is the reason security types that belong to processes are often called domains.

Attributes, like types, must be declared before they can be used. This is done using the keyword attribute. An attribute declaration has the form:

attribute attribute-name ;

where attribute-name is the name of the attribute being declared. For example,

attribute httpdcontent;

defines the attribute called httpdcontent (this attribute is actually used to group types associated with the httpd server). Types are assigned attributes either when the type itself is declared (using the type declaration described above) or using the typeattribute keyword. For example,

type httpd_user_content_t; typeattribute httpd_user_content_t  httpdcontent;

declares the type httpd_user_content_t and assigns it the attribute httpdcontent. These two policy lines are equivalent to the single type declaration:

type httpd_user_content_t, httpdcontent;

Type enforcement rules

Type enforcement rules are used to build the access vectors that contain a list of relevant permissions for access to a particular object (see notes on initial concepts). In the modular policy these rules are specified in both the type enforcement and interface files. A type enforcement rule has the form:

rule-form subject-type(s) object-type(s) : object-class(es) permission(s) ;

The rule-form is one of:

  • allow permit a subject to act in a specific way with an object. For example,
    allow mozilla_t xdm_t:fd use;
    This rule allows processes of the mozilla_t type to have permission to "use" the fd object class (file descriptors) that have the file type xdm_t.
  • auditallow audit or log that the access permission given by this rule is granted (although this form of rule does not actually grant it, it just logs that it is granted). For example:
    allow secadm_t security_t:security setsecparam; auditallow secadm_t security_t:security setsecparam;
    These two rules allow the processes labelled with the secadm_t type to set kernel Access Vector Cache tuning parameters (object class security, permission "setsecparam") for security_t object types. Although the first rule grants access, it is a serious event worthy of logging, so the second rule logs this access. auditallow rules are not common, but are important for security logging.
  • dontaudit specifies that a certain subject object interaction is not to be audited or logged. This is generally used for interactions that are not allowed but are not severe enough or are too common to be logged.
  • neverallow is generally not used in specific modules. It's purpose is to minimise bad policy writing by specifying that particular subject object interactions are never to be allowed.

Grouping of types, permissions and classes is used to make rules applicable to more than one type/permission/class. It is done using curly brackets {}. For example:

allow mozilla_home_t user_home_dir_t:dir { read getattr lock search ioctl }

This example gives permissions to read/access the user home directory to the mozilla_home_t process type. As there are groups of permissions that are commonly used over and over again, many such commonly used groups are defined as macros. So the above rule could be written in a simpler form:

allow mozilla_home_t user_home_dir_t:dir r_dir_perms;

where the macro r_dir_perms represents the group of permissions (read, getattr, lock, search, ioctl).