SELinux Main Page

Copyright 2013 by Citrix Systems, Inc. This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/.

Introduction

It's been decided that SELinux has to be fully integrated in to OpenXT's predecessor.

For that, it needs a policy governing every action that every program can do.

This is hardly a job for a one-man-army ;-), so every developer needs to be able to write policy bits for their code.

This document intends to help you dealing with it.

SEWhat?

SELinux consists of a number of cooperating technologies. Firstly the SELinux policy is written in a language based on Type Enforcement (TE). There have been a number of additions to TE in the current policy language to support things like MLS/MCS and generic constraints.

Second the Linux Kernel was modified to add access control hooks to code paths accessing kernel objects. These hooks were generalized into the Linux Security Modules (LSM) so as to allow any security model to play in the kernel. There are however very few LSM modules and SELinux has become "the one true LSM".

Third is the reference policy (refpol) which is "the one true policy" maintained by the community. It's more than just a policy though. There is a build system (it's horrible Makefiles and M4 macros) that provides a rough set of interfaces and some encapsulation as well. Despite all of it's shortcomings though it works quite well.

Lastly there is a set of userland tools for managing SELinux. This includes stuffs like the policy compiler, the module linker and utilities to perform management tasks like changing the current enforcement mode (permissive vs enforcing). In permissive mode, SELinux will just print what it would have denied in the console. It will NEVER actually block ANYTHING in this mode. Really, NEVER. In enforced mode, it will also actually blocks actions.

A good summary of SELinux history can be found here: https://www.ibm.com/developerworks/library/l-secure-linux-ru/

In OpenXT's Predecessor

For now, SELinux is present in dom0 and ndvm, and as of the latest Spindrift release it is enforced from first boot in a Kent installation, permissive for all the other cases.

The default behaviour is defined in "/etc/selinux/config".

You can get the current SELinux state of a VM by running getenforce.

You can set the current SELinux state of a VM by running setenforce, followed by 0 for Permissive, 1 for Enforcing.

SELinux is also used to separate QEMU processes from each other using an architecture known as sVirt. Our implementation of sVirt is documented in the OpenXT's predecessor sVirt page.

How it works

SELinux is built on the notion of contexts.

They are two main types of contexts, the file context and the processes context.

Most of the rules define what process contexts can do to file contexts.

SELinux is built on a deny-by-default base. So you'll need to have rules for everything!

The rules

The SELinux rules are located in the OpenXT's predecessor git repository selinux-policy.

They are divided in modules, stored in policy/modules, and activated by policy/modules.conf.

Usually, a module represents a program.

A module is divided in 3 files :

- module.fc: This file defines the SELinux contexts of the files that belongs to the module.

- module.te: This file is a list of rules. It is generally the list of thing that the program can do.

- module.if: This file is used to define higher-level interfaces for another module to interact with it. Using the interfaces is strongly recommended, as it allows the policy to evolve more easily, makes rules way more clear and gives a very agreeable abstraction level.

Writing a rule

So you added a new feature to your program and now SELinux complains? Here is what you should do :

First, you need to be in Permissive mode before running your program. Otherwise, SELinux could block it to early to trigger all the denials.

Then, get the errors from the logs, those are called AVCs in the SELinux dialect. You can for example try grep avc /var/log/messages | grep <program>, to get <program>'s denials.

Let's for example say that you just added V4V communications to the program fish. grep avc /var/log/messages | grep fish, gives you :

[...] avc:  denied  { open } for  pid=131 comm="fish" path="/dev/v4v_stream" dev=tmpfs ino=3626 scontext=system_u:system_r:fish_t:s0 tcontext=system_u:object_r:v4v_t:s0 tclass=chr_file
[...] avc:  denied  { read } for  pid=131 comm="fish" path="/dev/v4v_stream" dev=tmpfs ino=3626 scontext=system_u:system_r:fish_t:s0 tcontext=system_u:object_r:v4v_t:s0 tclass=chr_file
[...] avc:  denied  { write } for  pid=131 comm="fish" path="/dev/v4v_stream" dev=tmpfs ino=3626 scontext=system_u:system_r:fish_t:s0 tcontext=system_u:object_r:v4v_t:s0 tclass=chr_file

So you need to open policy/apps/fish.te, then there are 3 ways to write the rules allowing fish to do V4V.

The "seriously dude? come on!" solution

For this awful (but so easy...) solution, you take the denials and translate them into SELinux raw rules :

allow fish_t v4v_t:chr_file { open };
allow fish_t v4v_t:chr_file { read };
allow fish_t v4v_t:chr_file { write };

You paste that into fish.te, job done.

If you are bad enough to do that, please at least add the value of "path" ("/dev/v4v_stream" here) in a comment (or even the entire denial), so that you we clean-up after you!

The least effort solution

You decided to be just a bit kind with the policy maintainers, thank you!

SELinux has a lot of macros available for usual actions.

They are not always easy to find, but the folder policy/support is a good place to start from.

For our example, there is a macro in "policy/support/obj_perm_sets.spt": define('rw_chr_file_perms',{ getattr open read write append ioctl lock })

Let's use it! You can now write :

allow fish_t v4v_t:chr_file { rw_chr_file_perms };

OK, this is a bit better, but we still don't really know the exact thing your program wants to do, so again, please add a comment with at least the value of "path"

The Best solution

So you really want to be a good man today? Awesome!

As said before, when writing SELinux policies, interfaces should be used.

Let's check what we've got for V4V...

...[a few greps later]...

"policy/modules/system/xc_files.if" defines the interface xc_files_rw_v4v_chr, which take one argument : the source context.

As WE are the source context, the rule can be written like this :

xc_files_rw_v4v_chr(fish_t)

And this is a sexy and meaningful SELinux rule!

This type of rule usually makes more sense when read, but more importantly, it will cover denials that you would have had in the future (if a file becomes a symlink for example).

So keep in mind that writing good rules will save you time in the future!!

Creating a module

If you wrote a new program, you'll need an SELinux module for it.

Start by copying a small existing OpenXT's predecessor module, such as angelomachy, replacing of course the occurrences of "angelomachy" by your module name.

Put a context on your files in the .fc file, write rules as explained before in the .te file, and if your program is designed to be able to receive any kind of communication, you should write interface(s) in the .if file, such as interface(`talk_to_me')!