What about the whole concept of open source, freedom and the ability to modify applications to suit our needs rather than we modifying ourselves to suit the application’s needs?
Thankfully, SELinux gives us the power and complete freedom to achieve all this. There are two ways in which we can approach the whole concept of customising SELinux:
- Modifying the source of the policy (easily available through source RPMs).
- Developing modules that can be compiled and loaded along with the base policy.
For beginners, intermediate-level users and also for production purposes, I would not recommend the first option unless it is absolutely necessary. It requires more in-depth knowledge and experience to modify the core policy—we will take a look at this option in a later part of this series.
For most of us, the second option of building policy modules will suffice. It is an easier approach and also lets us develop a better understanding of the SELinux policy language before we delve deeper into it by modifying the core policy.
Customising the default policy — Power to the people
As discussed above, we will customise the default targeted policy by adding our own modules. To do so we will use the
semodule
command.semodule
is the tool used to manage SELinux policy modules, including installing, upgrading, listing and removing modules. It is installed by the policycoreutils
RPM. The man page, as usual, gives further details and helpful instructions on how to use the command.Listing SELinux modules
To list modules, use the following:
[root@vbg ~]# semodule -l
If you execute this command on a freshly installed system with SELinux enabled, you will see a list of modules. A sample output on my system follows:
[root@vbg ~]# semodule -l amavis 1.1.0 ccs 1.0.0 clamav 1.1.0 dcc 1.1.0 evolution 1.1.0 iscsid 1.0.0 mozilla 1.1.0 mplayer 1.1.0 nagios 1.1.0 oddjob 1.0.1 openoff 1.0.0 pcscd 1.0.0 pyzor 1.1.0 razor 1.1.0 ricci 1.0.0 smartmon 1.1.0 tmp 1.0.1 vbg 1.0.3
What this means is that the default SELinux installation does come with some modules loaded that are not part of the base policy. Looking more closely into the output of the above command, we see that in my system there are 18 policy modules installed. Each row of the above output corresponds to a policy module.
The output of the
semodule -l
command gives us two columns of information: Module Name and Module Version. Thus we can see that the amavis
module has a version number of 1.1.0 whereas the vbg
module has a version 1.0.3.
Also, these are the modules currently loaded into the memory and are active along with the base policy. But, where are these modules located? What difference do they make to the overall SELinux policy? How are they loaded and removed?
Let’s try to answer the above questions, one by one.
These are binary policy modules that, by default, have a file extension of
.pp
(Policy Package). Generally, the module name and the file name is kept the same, though it’s not mandatory. Therefore, we need to look up a file named amavis.pp
(Policy Package for the Amavis daemon). By default, the location of this file is/etc/selinux/targeted/modules/active/modules/
. If you go into this folder and list the contents, you will see the policy package files of all the currently loaded modules.
The policy modules obviously make a lot of difference. That’s what they were created for. To observe the difference they make, let’s use the wonderful
seinfo
tool discussed earlier.
To get an overall idea of the SELinux policy currently loaded, we shall use the command
seinfo
. A sample output from my system is shown below:[root@vbg modules]# seinfo Statistics for policy file: /etc/selinux/targeted/policy/policy.21 Policy Version & Type: v.21 (binary, MLS) Classes: 61 Permissions: 220 Types: 1516 Attributes: 148 Users: 3 Roles: 6 Booleans: 211 Cond. Expr.: 187 Sensitivities: 1 Categories: 1024 Allow: 82576 Neverallow: 0 Auditallow: 28 Dontaudit: 5086 Role allow: 5 Role trans: 0 Type_trans: 1400 Type_change: 17 Type_member: 0 Range_trans: 23 Constraints: 47 Validatetrans: 0 Fs_use: 15 Genfscon: 64 Portcon: 264 Netifcon: 0 Nodecon: 8 Initial SIDs: 27
We can see that there are 1,516 types and 82,576 allow rules being recognised by SELinux. You can redirect this output to a temporary file just for comparison, later. You could use the following command:
[root@vbg modules]# seinfo > /tmp/org-selinux-policy
Let’s now remove one of the loaded modules. As an example, let us remove the
amavis
module.Removing SELinux modules
To remove a loaded SELinux module, use the
semodule
command with the -r
option and the module name as the argument. For example:[root@vbg modules]# semodule -r amavis
This removes the
amavis
module.
To ensure that the above command has worked, list all the currently loaded modules:
root@vbg modules]# semodule -l ccs 1.0.0 clamav 1.1.0 dcc 1.1.0 evolution 1.1.0 iscsid 1.0.0 mozilla 1.1.0 mplayer 1.1.0 nagios 1.1.0 oddjob 1.0.1 openoff 1.0.0 pcscd 1.0.0 pyzor 1.1.0 razor 1.1.0 ricci 1.0.0 smartmon 1.1.0 tmp 1.0.1 vbg 1.0.3
To understand the difference made by the removal of the
amavis
package, again redirect the output ofseinfo
to a file:[root@vbg modules]# seinfo > /tmp/new-selinux-policy
…and run a
diff
on the two files:[root@vbg modules]# diff /tmp/org-selinux-policy /tmp/new-selinux-policy 6c6 < Types: 1516 Attributes: 148 --- > Types: 1507 Attributes: 148 8c8 < Booleans: 211 Cond. Expr.: 187 --- > Booleans: 210 Cond. Expr.: 186 10,11c10,11 < Allow: 82576 Neverallow: 0 < Auditallow: 28 Dontaudit: 5086 --- > Allow: 81929 Neverallow: 0 > Auditallow: 28 Dontaudit: 5062 13c13 < Type_trans: 1400 Type_change: 17 --- > Type_trans: 1387 Type_change: 17
You will see that removing
amavis
has made the following changes:- Reducing the number of ‘types’ from 1,516 to 1,507
- Reducing the number of ‘Booleans’ from 211 to 210
- Reducing the number of ‘allow rules’ from 82,576 to 81,929
- Reducing the number of ‘type transition rules’ from 1400 to 1,387, and so on…
Thus, we see that by using modules, we can at least add types, Booleans and rules to the core policy. That is pretty much what we want to do when we need to modify the default policy to suit our needs.
From what we’ve just covered, it is clear that we need to create SELinux policy modules—thereby creating new types, Booleans and various rules.
Creating SELinux modules
SELinux policy modules need to be written in the SELinux policy language. It is not a complicated language at all, but like most programming languages, requires a certain structure and syntax to be followed while creating the modules.
Also, once the text files containing our desired modifications have been created, we need to compile them into a binary policy module (Policy Package). Once the Policy Package files have been created, they just need to be tested and then finally loaded to enable the desired functionality.
To enable the development and compilation of policy modules, install the
selinux-policy-devel
RPM. The installed RPM on my system is selinux-policy-devel-2.4.6-106.el5_1.3
.
This module creates the
/usr/share/selinux/devel/
directory, which contains ‘include’ files and a makefile for compilation. It also installs three files—example.te
, example.fc
and example.if
—to assist you in the creation of policy modules.
The three files are important to understand the structure of policy modules:
- The type enforcement file (a file with the
.te
extension—for example,/usr/share/selinux/devel/example.te
) is the most important file. This contains the name of the module, its version and all the additions desired in the policy, such as types, rules, Booleans, etc. - The file contexts file (with the
.fc
extension—for example,/usr/share/selinux/devel/example.fc
) contains the default security contexts to be provided for files created/used by the application for which we are creating the policy module. - The interface file (one with the
.if
extension—for example,/usr/share/selinux/devel/example.if
) generally would contain macro definitions that assist in creating type enforcement rules.
The type enforcement file (
.te
) is mandatory, while the other two files (.fc
and .if
), if not required, need not be explicitly created. I would advise their use, but it generally depends on the kind of policy module to be developed.Syntax of the type enforcement file
The most important thing for a policy module to be clearly distinguished is its name and version—the output of the
semodule -l
command. This is specified as the first line in a .te
file aspolicy_module(,)
.
New types being introduced by the module are declared as
type ;
. Let’s create a small policy module called test
to introduce a new type called lfy_t
. Use the following steps to achieve the above:- Create a work directory for building and compiling your SELinux modules.
- Copy the necessary files needed for compiling SELinux modules.
- Create at least a
.te
file for your SELinux module, specifying the module name and version number. - Compile the source file above to a binary policy package file.
- Load the binary policy package.
- Test the changes in the SELinux Policy.
Step 1: Create a work directory for building and compiling your SELinux modules.
Instead of working in default directories, experience has taught me to work as a non-root user in non-default folders. Let us log in as the non-root user and make a working directory for our SELinux modules:
[vbg@vbg ~]$ mkdir test-selinux [vbg@vbg ~]$ cd test-selinux/ [vbg@vbg test-selinux]$ cp /usr/share/selinux/
Step 2: Copy the necessary files needed for compiling SELinux modules.
The only important file that you need to copy to your working directory is the
Makefile
from the/usr/share/selinux/devel/
directory.[vbg@vbg test-selinux]$ cp /usr/share/selinux/devel/Makefile . [vbg@vbg test-selinux]$ ls
Step 3: Create at least a .te file for your SELinux module, specifying the module name and version number:
Create a new file called test.te and append the statements in it:
policy_module(test,1.0) type lfy_t;
The above step specifies that we are creating a policy module named
test
with the version 1.0 and are declaring a new type to be introduced in the policy called lfy_t
.
Step 4: Compile the source file above to a binary policy package file.
To compile the source, simply run
make
(ensure you have copied the Makefile
in Step 2 shown above):[vbg@vbg test-selinux]$ make Compiling targeted test module /usr/bin/checkmodule: loading policy configuration from tmp/test.tmp /usr/bin/checkmodule: policy configuration loaded /usr/bin/checkmodule: writing binary representation (version 6) to tmp/test.mod Creating targeted test.pp policy package rm tmp/test.mod.fc tmp/test.mod
You should now have the compiled policy package file
test.pp
:[vbg@vbg test-selinux]$ ls -l total 60 -rw-r--r-- 1 vbg vbg 437 Sep 24 10:36 Makefile -rw-rw-r-- 1 vbg vbg 0 Sep 24 10:36 test.fc -rw-rw-r-- 1 vbg vbg 0 Sep 24 10:36 test.if -rw-rw-r-- 1 vbg vbg 22994 Sep 24 10:36 test.pp -rw-rw-r-- 1 vbg vbg 37 Sep 24 10:36 test.te drwxrwxr-x 2 vbg vbg 4096 Sep 24 10:36 tmp
To check if a type
lfy_t
exists, just use the following command:[root@vbg devel]# seinfo -t |grep lfy
You will not receive any output. This shows that the type
lfy_t
does not exist in our policy currently.
Step 5: Load the binary policy package.
As the root user, use the
semodule
command to load the compiled test.pp
policy package. The option to use with the semodule
command is -i
(which stands for insert).[root@vbg devel]# semodule -i /home/vbg/test-selinux/test.pp
To confirm if the module has been successfully loaded, list all the modules:
[root@vbg devel]# semodule -l cs 1.0.0 clamav 1.1.0 dcc 1.1.0 evolution 1.1.0 iscsid 1.0.0 mozilla 1.1.0 mplayer 1.1.0 nagios 1.1.0 oddjob 1.0.1 openoff 1.0.0 pcscd 1.0.0 pyzor 1.1.0 razor 1.1.0 ricci 1.0.0 smartmon 1.1.0 test 1.0 tmp 1.0.1 vbg 1.0.3
You can see that a module named
test
with version 1.0 has been loaded.
To check whether our new type has been added, retype the earlier command:
[root@vbg devel]# seinfo -t | grep lfy lfy_t
This shows that we have successfully introduced a new type in our SELinux policy by adding a policy module.
Up next
In this article, we have covered the basics of policy modules. In the next part of the series, we will look at adding Allow Rules, Type Transition Rules, Booleans and other policy building blocks. We shall also look at the
audit2allow
tool that helps to create policy modules.
I hope the article has helped you understand the modular structure of SELinux. It is this modularity that allows administrators to easily create SELinux policy modules for whatever applications they deploy—without compromising the security of their system.
0 comments
Post a Comment