In this example i am going to show you how you can write, integrate and rebuild SELinux policy modules for Fedora 9 using Eclipse-Slide and RPMdevtools.
I will create a SELinux policy module for the irssi user application and i will integrate this new policy module into the main Fedora selinux-policy with RPM devtools.
What i need:
yum install rpmdevtools selinux-policy-devel eclipse-slide
Chapter 1. Preparing the source:
First we should determine which selinux policy we have installed so that we can go find and download the corresponding selinux-policy source rpm.
It is determined that the source rpm that i need is not available on the main mirrors and therefore i will try to find it on koji.fedoraproject.org/koji.
I will download the source rpm to my default Download location and extract the package simple by alter-click ing it and chooce extract here from the menu.
Once the source rpm is extracted, a new folder appears with its contents. I am going to copy the included serefpolicy-3.3.1.tgz plus policy-20071130.patch to my desktop. After that i will extract the serefpolicy-3..3.1.tgz located on my desktop and apply the patch thats located on my desktop as well.
At this point the folder serefpolicy-3.3.1 on my desktop represents the prepared source for the binary policy that is installed on my system.
Chapter 2. Loading source policy into a Eclipse-Slide project:
Now i am ready to load the prepared source policy into the Eclipse-Slide IDE. I do this by starting Eclipse, creating a new Slide project and loading the prepared serefpolicy-3.3.1 folder into my new Slide project.
Once the new project is loaded i will attempt to build the project. Note that Eclipse has a settings enabled that will cause your project to be auto compiled by default. This can be an annoyance and so i opt to disable this and build the project manually. Also not that building this project will likely fail. However even so we will still have access to some of the templates and features.
Once the attempt to compile our newly project has finished you will notice that the right hand filter browser is now somewhat populated. This window will assist me with quick navigation in the vast area of interfaces and templates. The left hand window has a project browser which gives me a good oversight of my project. The window below has a few tabs, of which i find the declaration tab very helpfull. The status tab will display any errors or warnings that occoured during compilation. The main window in the middle of the screen i will use to write policy.
Chapter 3. Integrating a new policy module into our Eclipse-Slide project:
Now its time to actually integrate a new policy module into the current project.
I choose the irssi application. This is application is started by users and therefore belongs in the apps subsection of the modules location in our project.
Once you finish the new Slide module wizard, a new workspace is presented with access to your newly create module in the main window in the middle of the screen. Notice hat a module is built out of 3 seperate files which you can access by clicking the corresponding tab in the lower end of the main window in the moddle of the screen.
Chapter 4. Installing and perusing Irssi application:
Since we are going to integrate policy for Irssi, we should install this package by running: yum install irssi. Once this package is installed we can query information about this package. We want to find out to which files irssi application needs to write or which objects Irssi must execute. In this case irssi also has a configuration file /etc/irssi.config that is of particular interest to us.Chapter 5. About policy modules:
Before i start to write policy i want to explain something. Since Irssi is a user application, it is expected that users (or as wel call them userdomains) have to interact with it. The SELinux world can have infinite userdomains, and policy is based on which user domain interacts with the irssi application domain.
You can imagine how much maintenance work it would take for us to keep our irssi policy up-to-date for any (new) selinux userdomains that may come or go.
It would require very many rules just to differentiate between users. This is why templates and in particular the per_role_template was invented. This template can be invokes for each userdomain.
User information is replaced by variables which are instantiated when the user domain calls the template. This mean that i can use one piece of policy for any user domains that may call it. This saves much maintenance work but also saves us from a huge pile of policy. These templates are hosted by the domain in the interface file in the module. This file is used by domains to host policy. Other domains have easy access to theres interfaces and templates to interact with the domain that hosts the interfaces or templates.
The first step for me to take is to declare any non-user specific types. We know that irssi has an executable in /usr/bin/irssi and we have to declare a type for this excutable: irssi_exec_t.
We also know that irssi owns a file in /etc/irssi.conf, and we should declare a type for this config file in etc as well: irssi_etc_t.
We do this in the irssi.te file. This file has local policy regarding to irssi. The .te file in a module has a few parts: first we declare the policy_module, than we declare any types, booleans and others. and than we call interfaces in other domains and specificy local policy.
Policy that is user domain specific does not belong in a .te file as this policy is called by the user domain and thus should be hosted in the .if file by our domain. More about the .if file later.
So now we have to declare a tpye for irssi_exec_t and irssi_etc_t in irssi.te file in out module
So now we have those two types declared and now i have to make sure that these objects that we declared types for get labelled (/usr/bin/irssi and /etc/irssi.conf) This is done in the .fc file
At this point all out non-user domain specific types are declared and we have taken care of those objects file labelling via the file context file in our module.
Chapter 6. User domain specific policy in modules interface file:
Now we are ready for the part of the module that is called by the userdomains that want to execute irssi. This part is done in the interface file of the module. policy in this file is accessible by domains that call it.
Here is how this works: user domains can call irssi per_role_template so that they can run irssi in its application domain. user domains are expected to call this template in their local policy (.te file): irssi_per_role_template(myuser, myuser_t, myuser_r)
Above you see an example of an empty per_role_template in the irssi.if file in the module. Interfaces and templated are headed by XML comments. These comments can be read by other domains. Other domains that want to interact with out application domain needs this info to be able to calls these templates in a correct manner. This particular template takes 3 directions. userdomain prefix, user domain, and role.
And so if a user domain called myuser wants to run our application domain (in its domain) than that user domain will have to call our per_role_template in its own local policy.
My next step is to declare the remaining user domain specific types. such as the type for the application domain and the type of our application domains objects in the user space.
After i declared those types i will also have to manage object labeling to the remaining declared type for irssi owned objects in the user space.
Now were done with the .fc file
If you remember , we have declared two non user domain specific types in our irssi.te file: irrsi_exec_t and irssi_etc_t. These types are locally declared however, user domains that call the irssi_per_role_template also need access to these types, and since other domains cannot access our local policy (.te) we hav to include or require those types in our irssi_per_role_template in our .if file so that other domains can also interact with these types.
Please note that there is a small syntax error in the picture above. See if you can find what it is. I will at the end of this article show the correction.
Next we have to make some more decisions. We can decide to let any (user)domain automatic transition to the irssi application domain once the user executes the irssi executable or we can decide that the source domain executes irssi in its own domain instead.
I have chosen for an option where we make the above decision tunable by way of a boolean. The result is that by default domains will not transition to the irssi app domain unless a boolean for that particular domain is set.
That boolean is called irssi_confine_$1, where $1 stands for the user domain prefix. The administrator can set a boolean for the particular user domain if he want that domain to run in the irssi application domain.
First we declare our userdomain specific boolean in the declaration section of our irssi_per_role template and in the local policy bit of our template we write policy for this tunable.
Now that we have declared a local boolean we can decide to make some other possibilities optional. For example, maybe we want to enable optional support for NFS or SAMBA home directories. Since these are global booleans, we do not have to declare these booleans but we jut have to write policy for them.
Note that i also added the fs_search_auto_mountpoints() interface so that we can also always search those locations. The contents of this interfaces can easely be referenced to the filesystem module interface file or you can view its contexts by simply selecting the entry and view its declarations in the declaration tab on the lower window (which incidentally is collapsed in the example above.)
New Chapter. Networking:
Irssi connect to a irc server on usually port 6667. This port is already declared as port ircd_port_t in refpolicy and so we can use that policy to let irssi connect to irc servers. however we should declare a network port range for Irssi IRC DCC server facility. We declare a port ircdcc_port_t for tcp 4990-5000. Irssi will use that as its ports for a DCC server or in a single instance mode also for connecting to DCC ports. In case we are not hosting but downloading from another DCC server.
Now that we have declared irssi's DCC server port range we should also set up the rest of irssi networking policy. We simply add a interface to allow connect and send and receive irc client packets from the ircd_port_t port. Plus some other corenetwork module interfaces that are used as defaults.
Please note again that you can inspect what those blue lines really mean in terms of policy if you select it and view its contents in the declaration window (which is collapsable in the lower end of the window.)
Next it is decision making time again. also lets first recap: by default users do not transition to the irssi domain. We choose to make this decision tunable by boolean. If this bool is set than the user runs irssi in its domain. As it stands now irssi may only connect to ports with type irc_port_t which is tcp 6667. irssi will not be able to connect to any other ports. Also the irc_dcc_t port we declared for irssi DCC SERVER is not yet allowed.
We can decide to make this bit tunable or default behaviour. In this example i chose to make iRC DCC tunable and disabled by default unless set (least privilege model), and so we have to create a tunable_policy block again to allow irssi either connect or bind to its dcc port range (tcp 4990-5000)But since this is a local (but not user domain specific) boolean and not a global boolean, we will have to declare this boolean in the irssi.te file of our module.
The final tunable option we are going to implement is to facilitate mass hosting of irssi. if you want to host multiple instances of irssi and allow those instances to bind and connect to any unreserved ports than you can set the irssi_unreserved_network boolean. this boolean will require all other booleans to be set as well for optimal performance. This scenario is for instances where you may have a shell server that hosts irssi on a machine in the dmz.
So again theres four modes: 1. Disabled unless enabled. 2. Strict (can only connect to port 6667) 3. Single instance mode( can connect to 6667 and connect and bind to 4990-5000) and mass hosting mode ( can connect and bind to any unreserved port plus at of the above)
And ofcourse since this is a local (but not a user domain specific) boolean we will have to also declare this boolean in the irssi.te file in our module.
This next part goes into local* policy that defines how our domain may interact. This is usually the first piece of policy in the local policy block of your module and in this case it is user domain specific and thus should this be in the interface file irssi.if.
This piece of policy i split into 3 parts, Part 1 is policy that governs how our domain can interact with its own process (allow $1_irssi_t self). Part 2 is policy that governs how user domains may interact with the application domain e.g. allow $2 $1_irssi_t. Part 3 is policy that governs how our application domain may interact with the userdomain or allow $1_irssi_t $2)
Now i will deal with our domains objects and how our domain can interact with these objects. First you may remember that we declared a type irssi_etc_t in our irssi.te file. This is a global configuration file that irssi should be able to read,.and so we should give our domain access to search /etc, and we should allow our domain read and get attribute access to files with type irssi_etc_t. This will allow irssi to read the global config file.
Next we must ensure that irssi can manage its userdomain specific objects in the user space. We must ensure that the userdomain can also manage irssi objects in the user home location.
Third we should ensure that the userdomain is able to relabel irssi domain objects in his or her home dir. So that a user can move objects in and out of the irssi domain in his or her home location.
Most of our policy is done now. There are some interfaces that we have to call in other domains. One particular issue is that the userdomain should be able to read the application domains process. This is for using ps auxZ , top and etcetera.
Also note the optional_policy block for nis_use_ypbind. The optional policy block means that the policy is only enabled when NIS is available. This option was added to add NIS support to our module.
Done! lets clean up the project
Now i have to copy my project to my desktop and archive it with exactly the same name as the serepolicy-3.3.1.tgz we extracted and prepared earlier.
Next we should create our personal rpmbuild root
In this part we are going to copy the contents of the folder that we have extracted from our source rpm in our Download location to the newly created SOURCES location in our ~/rpmbuild root.
we will remove the copied *.patch and serefpolicy-3.3.1.tgz from our ~/rpmbuild/SOURCES location and copy the selinux-policy.spec that is is also located there to ~/rpmbuild/SPECS/.
Next we will copy our modified serefpolicy-3.3.1.tgz from our desktop to the ~/.rpmbuild/SOURCES/ location.
Now we should edit our spec file located in ~/rpmbuild/SPECS/selinux-policy-spec. We have to bump up the version number to avoid difficulties. We also have to comment out any patch entries (this is because we already applied the included patches manually in the beginning of our exercise.)
Finally we should add our new module to the modules-targeted.conf file in ~/rpmbuild/SOURCES/ if we want our module to be active in the selinux-policy-targeted package.
Now we can execute rpmbuild -ba ~/rpmbuild/SPECS/selinux-policy.spec and let her rip.
If all (would) go well than you'd get your freshly brewed set of rpms.
But all did not went go well in this example as i made a mistake. The image below shows my mistake corrected. If you have any questions or comments please ping me at #fedora-selinux on freenode
You can find a copy of my irssi policy here http://pastebin.ca/768256?srch=irssi_exec_t it also includes policy for eggdrop and manual pages but it may need some work.