Gentoo Hardened SELinux Development
1.
Introduction
About this document...
Dealing with Mandatory Access Control is never easy. SELinux might be available
by default with Linux, enabling it can provide serious headaches - let alone
developing policies for it. Within Gentoo Hardened, we strive to offer a default
policy that is flexible enough to match the requirements of most of you (our
users) yet remain manageable by the limited number of developers that we have.
To ensure that the policy we offer is up to date, we definitely need help from
end users and other developers, because developing policies requires intimate
knowledge of the products they are written for. With over several thousand
packages, this is just not feasible for a handful of us. Hence, this Gentoo
Hardened SELinux Development guide.
Within this document, we will try to explain how to set up an environment ready
to build policies yourself and provide patches to Gentoo Hardened. We also cover
how to deal with malfunctioning domains and even how to create your own, new
domains from scratch (if we need to). Further down, we give an overview of the
guidelines that we try to follow during the policy developments and finally
talk about how to properly create patches and submit them to our bugzilla service.
For those who want to run Gentoo Hardened with their own policies, we've also
added a chapter on just that. We know that our policy does not match everyone's
requirements, so we definitely want to help you run your own too.
Intended audience
This document is a must-read for everyone willing to provide patches or develop
the Gentoo Hardened SELinux policies.
Other SELinux advanced users might find this document interesting as well.
What you need to know
This document does assume prior knowledge on SELinux policies and the way the
reference policy works. For those that need a quick recap, here are the
highlights...
-
SELinux uses domains and types to differentiate its various
security objects. A domain is usually referred to as the security context
of a process (or group of processes) whereas a type is usually referred to
as the label given to a particular resource (file, directory, network
interface, socket, network port, ...).
-
SELinux policies describe what interaction is allowed between a
domain and the other domains and types it needs to work with. If no policy
allows for a particular activity, then the activity is denied.
-
The structure in which policies are written are called SELinux policy
modules which contain three parts: a type enforcement file (with
suffix .te) that contains the intra-module permissions, an
interface file (with suffix .if) that contains the
inter-module permissions and a file contexts file (with suffix
.fc) that contains the file context definitions for all file
resources that are labeled with the type or types defined in the module
-
Inter-domain privileges must be declared through functions in the
interface file which can then be called by other modules. This
includes the necessary permissions to allow domain transitions
-
SELinux uses attributes to make multiple domains manageable. Domains can
have certain permissions against all domains or types that are given a
particular attribute. Be aware of this when you start assigning attributes
to your own types or domains.
2.
Setting Up Your Environment
Patching the reference policy
Gentoo Hardened builds its policy upon the reference policy as
provided by Tresys and managed through
an active community.
I suggest to use two workspaces when dealing with SELinux policies for Gentoo
Hardened: the hardened one for the Gentoo patched policy, and a
local one in which you work and make your patches in.
Of course, using a source control system like git can be helpful too. For now,
Gentoo Hardened doesn't have a git repository where its policies are based from
(yet). That might sound a bit dull, but it forces the developers to remain as
close to upstream as possible (and contribute the changes upstream too so that
newer releases include them automatically). You can definitely use a source
control system yourself - the only reason we do not use it in this document is
that it is easier to document without ;-)
Let's create the first workspace:
Code Listing2.1: Creating the SELinux policy workspace |
~$ mkdir dev/hardened
~$ cd dev/hardened
~$ ebuild /usr/portage/sec-policy/selinux-base-policy/selinux-base-policy-2.20110726-r3.ebuild prepare
~$ cp -r /var/tmp/portage/sec-policy/selinux-base-policy-2.20110726-r3/work/refpolicy .
~$ rm -rf /var/tmp/portage/sec-policy/selinux-base-policy-2.20110726-r3
|
As result, you now have a subdirectory called refpolicy inside
dev/hardened. This directory contains all the SELinux policy rules
available. Now the dev/hardened workspace is patched with the
Gentoo Hardened SELinux patches applicable to the policy. Gentoo Hardened has
two "flavors" of patches:
-
patches in the patchbundle contain the majority of patches
-
module patches that contain the permissions affecting the
domains and types that are defined in a single module (for instance, all
interaction between portage_t and portage_exec_t
or even portage_t and portage_fetch_t)
When we develop changes on the SELinux policy, we currently try to put those
changes in the patchbundle as soon as possible. Currently, the
selinux-base-policy package is updated fast enough to hold off module
patches and wait for a new release of selinux-base-policy (after which
the SELinux modules themselves can just refer to the new base policy to get
their patches).
However, when the selinux-base-policy is more stable, then patches might
be made part of the modules themselves. In that case, a module patch is
made.
Note:
Every time a new revision comes out, you'll need to clean the
dev/hardened workspace and rebuild it.
|
Add specific module files
If you want to or need to work on the policy of a SELinux module (rather than
the base policy), check its ebuild to see if it holds any additional patches
(mentioned through the POLICY_PATCH variable). If not, then you can work
off the snapshot taken earlier in this guide.
However, if a patch (or set of patches) is applied as well, you either need to
apply those manually on the snapshot, or use the following tactics to create a
snapshot just for this module:
Code Listing2.2: Updating the dev/hardened workspace |
~$ ls dev/hardened/refpolicy/policy/modules/*/postfix.te
dev/hardened/refpolicy/policy/modules/services/postfix.te
~$ ebuild /usr/portage/sec-policy/selinux-postfix/selinux-postfix-2.20110726-r1.ebuild prepare
~$ cp /var/tmp/portage/sec-policy/selinux-postfix-2.20110726-r1/work/strict/postfix.te \
dev/hardened/refpolicy/policy/modules/services/
~$ cp /var/tmp/portage/sec-policy/selinux-postfix-2.20110726-r1/work/strict/postfix.fc \
dev/hardened/refpolicy/policy/modules/services/
~$ rm -rf /var/tmp/portage/sec-policy/selinux-postfix-2.20110726-r1
|
Setting up a local workspace
Setting up a local workspace (where we will create changes and generate patches
out of later) is easy: just copy the dev/hardened one:
Code Listing2.3: Setting up a local workspace |
~$ cd dev/hardened
~$ cp -r refpolicy refpolicy.local/
|
Navigating the policy workspace
The main location you will work with is
dev/hardened/refpolicy.local/policy/modules. This location is subdivided in
categories:
- admin
- Administrative SELinux policy modules (portage, logrotate, sudo, ...)
- apps
- Application SELinux policy modules (evolution, mozilla, screen, ...)
- kernel
- Kernel specific SELinux policy domains (corenetwork, kernel, ...)
- roles
- Domains specific to SELinux roles (sysadm, user, staff, ...)
- services
- Daemon SELinux policy modules (postfix, apache, squid, ...)
- system
- Core SELinux policy modules (selinuxutil, mount, iptables, ...)
The categorization is arbitrary and serves no purpose other than keeping the
modules a but separated. Each module must have a unique name, regardless of the
category!
Inside the categories, the modules are available using their three files
Code Listing2.4: Listing the available sudo files |
~$ cd dev/hardened/refpolicy.local/policy/modules/admin
~$ ls sudo.*
sudo.fc sudo.if sudo.te
|
Building a module
To build a module, go to the location where the module code is. Then, run
make with the development Makefile as provided by the reference policy.
Note:
You can ignore warnings about duplicate interface definitions and such. That is
because the Makefile will include both the existing interfaces as well as the
current working directory - which of course contains the same interfaces.
|
Code Listing2.5: Building the portage module |
~$ cd dev/hardened/refpolicy.local/policy/modules/admin
~$ make -f /usr/share/selinux/strict/include/Makefile portage.pp
|
You now have a portage.pp file available which you can load (using
semodule -i portage.pp).
Building the base policy
If you want to build the base policy, run make base.
Code Listing2.6: Building the base policy |
~$ cd dev/hardened/refpolicy.local
~$ make base
|
The result should be a base.pp file that you can load using
semodule -b base.pp. However, if you intend to do a bit more than just
test this base policy quickly, it is seriously recommended to create your own
Gentoo overlay for your own selinux-base-policy and install that
one as installing a base policy is not only about the policy module itself, but
also about the include files that will then be stored in
/usr/share/selinux/strict/include.
3.
A Domain Does Not Function Properly
Introduction
The most likely problem that you are hitting is that a domain does exist in
Gentoo Hardened SELinux, but that it isn't functioning as it should. To solve
this problem, it is adviseable to use the following sequence of investigations:
-
Is it really SELinux that is restraining your system?
-
Is the problem related to wrong resource labels / security contexts?
-
Is the problem related to intra-module permissions?
-
Is the problem related to inter-module permissions?
Check if SELinux is to blame
Make sure that the problem you are seeing is a SELinux-triggered problem. An
easy way to find out is to run SELinux in permissive mode and try again:
Code Listing3.1: Switching to permissive mode |
~# setenforce 0
|
This only works if the problem is not to do with a SELinux-aware
application (unlike init or sudo which are linked to the
libselinux library). SELinux-aware applications might alter their behavior if
SELinux is set on the system regardless of it running in permissive mode or not.
A prime example is vixie-cron (as can be seen in bug #257111). But
for applications that are not SELinux aware, this is the easiest method to find
out if SELinux is to blame or not.
If running your system in permissive mode works around the problem, read on. If
it doesn't, check the regular permissions (strace'ing the application
might be a good idea too).
Get the proper AVC denials
Assuming that we now know that SELinux is to blame, we need to make sure that we
get the proper AVC denials. Either locate the proper denials in
/var/log/avc.log (or audit.log) around the time that
you encountered the issue, or run tail -f /var/log/avc.log and reproduce
the problem.
Code Listing3.2: Example denials |
~# tail -f /var/log/avc.log
Apr 22 15:03:33 www1 kernel: [16053.303739] type=1400 audit(1303477413.188:283):
avc: denied { dac_read_search } for pid=21758 comm="rm" capability=2
scontext=root:sysadm_r:portage_t tcontext=root:sysadm_r:portage_t
tclass=capability
|
Analyzing the meaning of the AVC denial is covered by Looking
at the AVC Log in the Gentoo Hardened SELinux handbook. The denial should
give you a pointer where to look for. However, it is possible that no denial is
occurring, or at least no relevant ones.
A first step to get potentially more denials is to switch the
gentoo_try_dontaudit boolean off. This boolean is used by the Gentoo
Hardened SELinux developers to hide denials which they assume are cosmetic. As
these developers are known to have a human side (as well), they are known to
make mistakes ;-)
Code Listing3.3: Disabling gentoo's dontaudit statements |
~# setsebool gentoo_try_dontaudit off
|
Retry getting the proper AVC denials.
If it still doesn't work, you can disable all dontaudit statements:
Code Listing3.4: Disabling all dontaudit statements |
~# semodule -R -D -B
|
Retry getting the proper AVC denials.
The moment you get the denials you are looking for, isolate them and then undo
the changes you made earlier:
Code Listing3.5: Resetting the auditing defaults |
~# setsebool gentoo_try_dontaudit on
~# semodule -R -B
|
If you still do not see any denials, then check out the dmesg output for
other problems. It is possible that SELinux is not even getting to the point of
the policy, which you will not notice by looking at the AVC denials alone.
However, the chance of this to happen is very slim - most of the time, you'll
find the AVC denials you are looking for.
Deducing the correct security contexts
The next step is to see if we are dealing with the right security contexts. This
does require a bit of insight in how both the application (that is failing) and
the policy relate to each other. In essence, you want to make sure that the
process is running in the right domain and is trying to work on the right target
type.
Say you are having issues with SELinux (re)labeling and you notice the following
AVC denial:
Code Listing3.6: AVC denial for setfiles |
Apr 16 14:39:57 testsys kernel: [ 115.778484] type=1400
audit(1302957597.827:224): avc: denied { create } for pid=3584
comm="setfiles" scontext=root:sysadm_r: tcontext=root:sysadm_r:sysadm_t
tclass=netlink_audit_socket
|
In this case, setfiles is running in the sysadm_t domain
even though it should run in setfiles_t. So check the security
context of the setfiles binary as well as the transition rules:
Code Listing3.7: Checking setfiles context and rules |
~# ls -lZ /sbin/setfiles
-rwxr-xr-x. 1 root root 26464 Apr 9 22:22 /sbin/setfiles
~# sesearch -s sysadm_t -t setfiles_t -c process -p transition -A -d
Found 1 semantic av rules:
allow sysadm_t setfiles_t : process transition ;
~# sesearch -s sysadm_t -t setfiles_exec_t -c file -p execute -A -d
...
~# sesearch -s setfiles_t -t setfiles_exec_t -c file -p entrypoint -A -d
...
|
In the above (forced) situation, the problem is with the security context of the
binary - it should have been setfiles_exec_t instead of
bin_t. Usually, entry points are named similarly (like
portage_exec_t or sudo_exec_t). If you are not certain
about which domain it should be, use sesearch
Code Listing3.8: Using sesearch to find the entrypoint type for a domain |
~# sesearch -s setfiles_t -c file -p entrypoint -A -d
Found 1 semantic av rules:
allow setfiles_t setfiles_exec_t : file { ioctl ... execute entrypoint open } ;
|
The sesearch utility is extremely powerful to query the SELinux policy
(which is currently in memory). I also advise you to use the -C switch to
see which rules are trigged by certain SELinux booleans:
Code Listing3.9: Looking for boolean-triggered settings |
~# sesearch -s named_t -t named_zone_t -c file -A -d -C
Found 2 semantic av rules:
allow named_t named_zone_t : file { ioctl read getattr lock open } ;
DT allow named_t named_zone_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ named_write_master_zones ]
|
In the above example, the named_t domain only has write privileges
on files labeled named_zone_t if the
named_write_master_zones boolean is set (which it currently isn't,
otherwise the line would stat with ET instead of DT).
To gain a bit of insight in the various, available domains, use seinfo:
Code Listing3.10: Getting a list of available domains |
~# seinfo -t | grep named
named_var_run_t
named_checkconf_exec_t
named_conf_t
named_initrc_exec_t
named_log_t
named_exec_t
named_zone_t
named_t
named_cache_t
named_tmp_t
|
To gain a bit of insight in the (current) file context rules, use
semanage:
Code Listing3.11: Getting the list of current file context rules |
~# semanage fcontext -l | grep named
/etc/bind(/.*)? all files system_u:object_r:named_zone_t
/etc/bind/named\.conf regular file system_u:object_r:named_conf_t
/etc/rc\.d/init\.d/named regular file system_u:object_r:named_initrc_exec_t
/etc/rc\.d/init\.d/unbound regular file system_u:object_r:named_initrc_exec_t
/etc/rndc.* regular file system_u:object_r:named_conf_t
/etc/unbound(/.*)? all files system_u:object_r:named_conf_t
/usr/sbin/lwresd regular file system_u:object_r:named_exec_t
/usr/sbin/named regular file system_u:object_r:named_exec_t
/usr/sbin/named-checkconf regular file system_u:object_r:named_checkconf_exec_t
/usr/sbin/unbound regular file system_u:object_r:named_exec_t
/var/bind(/.*)? all files system_u:object_r:named_cache_t
/var/bind/pri(/.*)? all files system_u:object_r:named_zone_t
/var/log/named.* regular file system_u:object_r:named_log_t
/var/run/bind(/.*)? all files system_u:object_r:named_var_run_t
/var/run/named(/.*)? all files system_u:object_r:named_var_run_t
/var/run/ndc socket system_u:object_r:named_var_run_t
/var/run/unbound(/.*)? all files system_u:object_r:named_var_run_t
|
Most of the time, fixing domain issues is a matter of relabeling files (or
updating the configuration to match the contexts already defined - both work).
Intra-module permissions are missing
It is possible that you get a denial between correct security contexts, but
that the permission is just never granted. In this case, you can choose between
two things:
-
Enhance the module so that the particular permission is granted, or
-
Enhance the module with an additional type where the permission is granted,
and assign this type/label to the related resources
In both cases you will need to edit the module files (most likely the
.te file), build the module, load it, perhaps even relabel the
files or the package and retry. It is also a good idea to take a look at
upstream (latest refpolicy repository or the repositories of Fedora and co) and
see if they have already solved this problem or not.
Granting additional permissions between existing domains is the easiest, but
might introduce additional problems: if this permission is only needed in a
particular case yet you grant it for all files and resources related to those
domains, then you are opening up the policy beyond what is necessary. Often,
creating an additional domain or type can be beneficial.
A noticeable example is Portage' support for CVS/SVN/GIT ebuilds (the so-called
live ebuilds). These ebuilds get their repository and store it in the
distfiles/svn+src location, which was by default labeled
portage_ebuild_t with only read-access for the
portage_sandbox_t domain. However, with those live ebuilds, the
portage_sandbox_t domain also needs write privileges to this
location. Rather than allowing portage_sandbox_t write privileges
to portage_ebuild_t, a new type was created called
portage_svnsrc_t for just this location and the rights are
transferred towards type.
Inter-module permissions are needed
If the solution for the problem requires permissions between modules, then you
need to create the proper interface functions in the target domain and call
these functions from the source domain.
Interface functions are the APIs that a module provides towards other SELinux
modules when they need to interact with the domains. For instance, the
mysql module provides, amongst other functions, the
mysql_stream_connect interface:
Code Listing3.12: mysql_stream_connect interface |
########################################
## <summary>
## Connect to MySQL using a unix domain stream socket.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed access.
## </summary>
## </param>
## <rolecap/>
#
interface(`mysql_stream_connect',`
gen_require(`
type mysqld_t, mysqld_var_run_t, mysqld_db_t;
')
stream_connect_pattern($1, mysqld_var_run_t, mysqld_var_run_t, mysqld_t)
stream_connect_pattern($1, mysqld_db_t, mysqld_var_run_t, mysqld_t)
')
|
The interface declares that the domain passed on as its first (and only)
argument gets the rights offered by stream_connect_pattern, which is a
macro (defined in policy/support/ipc_patterns.spt that looks like
so:
Code Listing3.13: stream_connect_pattern |
define(`stream_connect_pattern',`
allow $1 $2:dir search_dir_perms;
allow $1 $3:sock_file write_sock_file_perms;
allow $1 $4:unix_stream_socket connectto;
')
|
Modules that need to interact with MySQL through a Unix domain stream socket
(/var/run/mysqld/mysqld.sock) will need the proper permissions to
work with the target type (mysqld_var_run_t). Modules cannot just
set allow statements towards mysqld_var_run_t as they do not
know this type. Instead, they call the mysql_stream_connect interface,
like the postfix.te file does:
Code Listing3.14: Postfix module calling mysql_stream_connect |
optional_policy(`
mysql_stream_connect(postfix_master_t)
mysql_stream_connect(postfix_cleanup_t)
mysql_stream_connect(postfix_local_t)
')
|
If the change you need is adding existing interface calls to the module (in
the .te file) then you should be able to test it easily by building
the changed module and loading it. However, if you need to change the interface
of your module itself (in the .if file) you will eventually need
to rebuild the base policy and even provide and install a new
sec-policy/selinux-base-policy package as the interfaces are placed
in /usr/share/selinux/strict/include. This is one of the reasons
why the sec-policy/selinux-base-policy package in Gentoo Hardened
has a high revision number (and many updates).
4.
No Domain Exists (Yet)
Reuse existing domains
If you are facing problems because you run an application which has no domain
itself (and hence is probably running in the user_t,
staff_t or sysadm_t domains - or even tries to run in
the initrc_t domain), you will need to create one. But before we do
that, it might be possible that the application can work within the domain
definition of a different application.
One example here is lighttpd. This lightweight HTTPd service "uses" the
definitions offered by the apache module. By marking its executable
file httpd_exec_t it runs in the httpd_t domain and
uses the same policy like Apache. By labeling the files according to the
apache.fc definitions (but now for lighttpd) it might Just Work
™
Reusing existing domains requires that you at least consider the following
aspects:
-
Will the application run on the same system as the application for which the
domain is originally intended? If so, then both might run in the same domain
(and as such have more privileges towards each other than intended) which
might not be what you want.
-
Do you need to enhance (read: add additional privileges) the master domain?
If so, make sure that you don't add more privileges than the original domain
would ever need to the extend that these privileges become a security risk.
(Do Not) Copy from existing domains
If reusing existing domains introduces too many risks, you'll need to create a
new domain for the application. Many people would be inclined to copy the domain
definition of a similar application and work from there. Although this is a
viable approach, it is considered a bad practice because you start by providing
privileges to the domain that are never needed, and removing privileges from a
domain later is very difficult. Even more, if you are not the author of the
modules, most developers will not even try to remove them as they assume that
the author of the domain had a good reason to add it in the first place. This is
one of the reasons why upstream takes great care in accepting patches - they
must be properly documented before they are accepted.
Instead, create a domain from scratch but take a close eye on the domain you
belief is very similar. Issues that arise during the module development might be
quickly resolved by looking at how the original domain is defined.
Starting from scratch
To start the development of a new module from scratch, first identify the
domain(s) you want to have. An application that, in its entire lifespan only
constitutes of a single process, will most likely only have one domain. For
instance, the Skype client will have just skype_t. However,
applications that have multiple processes running might need multiple domains
too. For instance, the Postfix application runs a master
(postfix_master_t), queue manager (postfix_qmgr_t) and
pickup service (postfix_pickup_t), but depending on the commands
you execute, it will also have (short-lived) processes running as
postfix_cleanup_t, postfix_bounce_t, etc.) It is
considered a best practice to start with a fine-grained model for domains
and only later decide if merging multiple domains into one is beneficial.
Splitting domains later is more difficult. Don't forget to look at the
client-side aspect too!
Next, define the types that each domain interacts with. This of course includes
the binary (like skype_exec_t) but do not forget resources like
-
The configuration file(s) in /etc (f.i.
postfix_etc_t)
-
PID files (f.i. sshd_var_run_t)
-
Spool files (f.i. postfix_spool_t)
-
Variable data files (f.i. snmpd_var_lib_t)
-
Log files (f.i. zebra_log_t)
-
Cache files (f.i. squid_cache_t)
-
(User) content files (f.i. httpd_sys_content_t and
httpd_user_content_t)
Also, try to separate types that are used by other domains as well. This way,
the other domains can only interact with those files or resources that are
labeled accordingly, rather than interact with a broad spectrum of files. The
distinction that the apache module makes between system-provided
content (like phpmyadmin files) and user-provided content (in the
public_html directory in the users' home directories) seems (and
is) very logical, but one could wrongly say that for Apache itself, the access
controls are the same. Although that might be true, both types are clearly used
in different ways so this mandates the use of different domains.
Once you have defined those types too, start writing down the intra-domain
permissions. Right now is a good time to look at other modules to see how they
do things. Start with defining the accesses towards the domains.
Code Listing4.1: Snippet from the spamassassin module |
type spamassassin_t;
type spamassassin_exec_t;
application_domain(spamassassin_t, spamassassin_exec_t)
ubac_constrained(spamassassin_t)
|
This small snippet defines many things. The first two lines just mention the new
types (the spamassassin_t domain and
spamassassin_exec_t type). The application_domain interface
marks spamassassin_t as an application domain type (it gets the
application_domain_type and domain attributes and a
few default permissions (like allowing that it sends SIGCHLD and SIGNULL to
init). It also marks spamassassin_exec_t as an applications'
executable type (application_exec_type and exec_type
attributes) so that it can be executed by regular users (these domains have
execute rights against all resources that have the
application_exec_type attribute set. Finally, it marks the
spamassassin_t domain as a constrained domain for user-based access
controls. In other words, if SELinux users user_u and
staff_u launch the application in spamassassin_t
domains, then the domains are segregated from each other (the intra-domain rules
inside spamassassin_t are only valid for communication within the
same SELinux user, not between SELinux users).
Attributes are an important aspect in SELinux policy development. They make
managing the domains easier, but you should always consider the implications
when you add an attribute to one of your types. It usually means that a whole
lot of permissions are suddenly granted between other domains and yours.
Next, set the proper intra-domain permissions. For instance, allow your domain
to read its configuration files as well as more access inside its own
/var/lib location:
Code Listing4.2: Snippet from openca module |
allow openca_ca_t openca_etc_t:file read_file_perms;
allow openca_ca_t openca_etc_t:dir list_dir_perms;
manage_dirs_pattern(openca_ca_t, openca_var_lib_t, openca_var_lib_t)
manage_files_pattern(openca_ca_t, openca_var_lib_t, openca_var_lib_t)
|
The majority of work in developing SELinux policy modules is using and choosing
the right interfaces. Having a few functions available to browse through all the
available information is always interesting, so you might want to use the
following function definitions (definitely not mandatory - this is only to help
people skim through the policy definitions):
Code Listing4.3: SELinux policy development function definitions |
POLICY_LOCATION="http://www.gentoo.org/path/to/your/refpolicy";
# sefindif - Find interface definitions that have a string that matches the
# given regular expression
sefindif() {
REGEXP="$1";
cd ${POLICY_LOCATION}/policy/modules;
for FILE in */*.if;
do
awk "http://www.gentoo.org/(interface\(|template\()/ { NAME=\$NF; P=0 }; /${REGEXP}/ { if (P==0) {P=1; print NAME}; print };" ${FILE} | sed -e "s:^:${FILE}\: :g";
done
}
# seshowif - Show the interface definition
seshowif() {
INTERFACE="$1";
cd ${POLICY_LOCATION}/policy/modules;
for FILE in */*.if;
do
grep -A 9999 "\(interface(\`${INTERFACE}'\|template(\`${INTERFACE}'\)" ${FILE} | grep -B 9999 -m 1 "^')";
done
}
# sefinddef - Find macro definitions that have a string that matches the given
# regular expression
sefinddef() {
REGEXP="$1";
grep -H "define(\`.*${REGEXP}.*" ${POLICY_LOCATION}/policy/support/* | sed -e 's:.*\/\([^(]*\):\1:g'
}
# seshowdef - Show the macro definition
seshowdef() {
MACRONAME="$1";
cd ${POLICY_LOCATION}/policy/support;
for FILE in *.spt;
do
grep -A 9999 "define(\`${MACRONAME}'" ${FILE} | grep -B 999 -m 1 "')";
done
}
|
These functions can then be used to find the information / interfaces you are
looking for. For instance, you need the application to read the postfix
configuration files:
Code Listing4.4: Looking for the interface(s) needed |
~$ sefindif postfix_etc_t
services/postfix.if: template(`postfix_domain_template',`
services/postfix.if: allow postfix_$1_t postfix_etc_t:dir list_dir_perms;
services/postfix.if: read_files_pattern(postfix_$1_t, postfix_etc_t, postfix_etc_t)
services/postfix.if: read_lnk_files_pattern(postfix_$1_t, postfix_etc_t, postfix_etc_t)
services/postfix.if: interface(`postfix_config_filetrans',`
services/postfix.if: type postfix_etc_t;
services/postfix.if: filetrans_pattern($1, postfix_etc_t, $2, $3)
~$ seshowif postfix_read_config
interface(`postfix_read_config',`
gen_require(`
type postfix_etc_t;
')
read_files_pattern($1, postfix_etc_t, postfix_etc_t)
read_lnk_files_pattern($1, postfix_etc_t, postfix_etc_t)
files_search_etc($1)
')
|
Same thing if you want to look for the correct macro definition (usually, if you
notice something but you cannot find it as an interface, then it is most likely
a macro):
Code Listing4.5: Looking for the right macros |
~$ sefinddef connect
ipc_patterns.spt:define(`stream_connect_pattern',`
obj_perm_sets.spt:define(`connected_socket_perms', `{ create ioctl read getattr write setattr append bind getopt setopt shutdown }')
obj_perm_sets.spt:define(`connected_stream_socket_perms', `{ connected_socket_perms listen accept }')
~$ seshowdef ps_process_pattern
define(`ps_process_pattern',`
allow $1 $2:dir list_dir_perms;
allow $1 $2:file read_file_perms;
allow $1 $2:lnk_file read_lnk_file_perms;
allow $1 $2:process getattr;
')
|
As we strive to bring most of our patches upstream, please do consider the contribution
guidelines of the reference policy project. The project has a documented
style guide, naming convention and an online API reference (for the various
interfaces).
Note that, the moment you create a new module, you'll need to create the proper
role interfaces (if it is an application that is directly called from a user
domain). Take a look at tvtime_role and how it is used in the
staff.te and sysadm.te role definitions.
Testing new modules
When you test your application, test it in as many ways as possible. If your
application is a command-line application, run it both from a regular terminal
(tty) as well as a virtual one (in an xterm). See if it still works if you run
it in a screen session. Try out all functions and features that the application
supports.
This rigorous testing is necessary because SELinux denies everything that isn't
explicitly allowed. If you do not test certain features, chances are that the
module does not provide the necessary permissions and as such, users will be
impacted.
To test out a new module, load it (semodule -i modulename.pp) and relabel
the files affiliated with the application (either through rlpkg or using
restorecon). Consider the following testing activities if applicable (not
all domains are interactive domains, so please read the activities with your
domain definition in mind):
-
Sending signals to the application (if you need to be able to kill it, try
killing it)
-
Run it both as a regular user (user_u) as well as
administrative users (if applicable). If your domain needs to support
unconfined domains/users, run it from an unconfined user domain too.
-
Run it from a terminal, console, screen, sudo, ...
-
Change the applications' configuration file (including rendering it useless
with syntax errors) and look at the applications' behavior. Especially
syntax failures as that might trigger the application to log things at
places that you haven't discovered earlier.
5.
Policy Guidelines
Cosmetic denials
When working on policy modules, you'll notice that the application is trying to
do things which are denied, but have no obvious effect on the applications
functionality. This is to be expected: many applications do not handle file
descriptors properly (file descriptor leaks are common) or applications read
attributes of files but don't do anything with it. You'll notice that you learn
a lot from the application while writing its policy ;-)
Gentoo Hardened's idea here is to only allow what is actually needed by the
application. Cosmetic denials are to be dontaudit'ed. Gentoo Hardened
uses the gentoo_try_dontaudit boolean for this:
Code Listing5.1: Example usage of gentoo_try_dontaudit |
tunable_policy(`gentoo_try_dontaudit',`
kernel_dontaudit_request_load_module(sshd_t)
')
|
Gentoo Hardened SELinux policy
To streamline the policy development efforts, Gentoo Hardened as a SELinux Policy document explaining the
principles used during policy development and the implementation guidelines we
strive to follow during development.
Such a policy is important because we want to have a consistent security policy
that users and developers can relate to. By following the policy, we hope that
other developers can quickly jump in and work on it further.
6.
Submitting Patches
File context patches
If you are able to fix a problem by adding the proper file contexts (using
semanage fcontext -a), please consider the following:
-
If the location for which you set the context deviates from the standard
location as either intended by the project or Gentoo itself, it might be
best to document it in the forums or elsewhere. We will not change file
contexts to match every ones configuration, unless the file context change
is apparent for each installation.
-
Developers might not immediately push file context changes in new policy
module packages to keep the amount of policy module changes low. Instead,
these changes can be stacked and pushed when other changes occur as well.
If you believe that the change is needed for everyone using Gentoo Hardened with
SELinux, create a bugreport and assign
it to selinux@gentoo.org. In the bugreport, mention the file context you
think is necessary and why.
Module patches
Module patches with changes that are intra-module (and have no effect outside)
are best generated from the policy/modules location:
Code Listing6.1: Example generating patch for modular changes |
~$ cd dev/hardened/refpolicy.local/policy/modules
~$ diff -ut ../../../refpolicy/policy/modules/services/openct.te services/openct.te
--- ../../../../refpolicy/policy/modules/services/openct.te 2011-04-22 23:28:17.932918002 +0200
+++ services/openct.te 2011-04-23 09:55:08.156918002 +0200
@@ -47,6 +47,10 @@
miscfiles_read_localization(openct_t)
+tunable_policy(`gentoo_try_dontaudit',`
+ kernel_dontaudit_read_system_state(openct_t)
+')
+
userdom_dontaudit_use_unpriv_user_fds(openct_t)
userdom_dontaudit_search_user_home_dirs(openct_t)
|
Attach this patch to the bugreport
explaining why it is needed. If you think the patch itself is not obvious, make
sure that the necessary comments are in place inside the patch for future
reference.
Please have a separate patch file per module (do not combine multiple modules in
a single patch).
Base policy patches
If a patch extends a single module, or it includes interface changes on a
module, you'll need to create a patch for the base policy. In this case, the
patch is best made from the upper location.
Code Listing6.2: Generating a base policy patch |
~$ cd dev/hardened/refpolicy.local
~$ diff -ut ../refpolicy/policy/modules/services/openct.if policy/modules/services/openct.if
--- ../strict/policy/modules/services/openct.if 2011-04-22 23:28:17.918918002 +0200
+++ policy/modules/services/openct.if 2011-04-23 10:01:38.753918001 +0200
@@ -15,7 +15,7 @@
type openct_t;
')
- allow $1 openct_t:process signull;
+ allow $1 openct_t:process { signull sigchld };
')
########################################
|
Attach this patch to the bugreport
explaining why it is needed. If you think the patch itself is not obvious, make
sure that the necessary comments are in place inside the patch for future
reference.
Please have a separate patch file per major change (do not combine multiple
unrelated changes in a single patch).
7.
Running Your Own Policy
Creating a local overlay
If you want to use your own policy rather than Gentoo's, we seriously recommend
to use a local overlay which uses the same package names and constructs. This
allows your policy to integrate properly with the other Gentoo packages (which
might depend on the SELinux packages). For instance, when you install openldap,
it will still properly depend on the sec-policy/selinux-ldap
package even if you provide it completely.
To do so, first create a local overlay and copy the content of the
sec-policy category inside it.
Code Listing7.1: Creating a local overlay |
~$ mkdir dev/overlay
~$ cp -r /usr/portage/sec-policy dev/overlay
|
Next, tell Portage to not synchronise the sec-policy category of
the main tree anymore. To do so, create the file
/etc/portage/rsync_excludes with the following content:
Code Listing7.2: Rsync exclusion information |
sec-policy/
|
Finally, add your current overlay by editing /etc/make.conf:
Code Listing7.3: Editing make.conf |
PORTDIR_OVERLAY="${PORTDIR_OVERLAY} /home/user/dev/overlay"
|
From now onwards, Gentoo Portage will only use your local overlay (you can
remove /usr/portage/sec-policy if you don't want Portage to even
reuse the current set of packages.
Updating module packages
To create or update a module package, you can use the following skeleton for the
ebuilds:
Code Listing7.4: Skeleton for ebuilds, example for postfix |
# Copyright 1999-2011 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
EAPI="4"
IUSE=""
MODS="postfix"
BASEPOL="2.20110726-r3"
inherit selinux-policy-2
DESCRIPTION="SELinux policy for postfix"
KEYWORDS="~amd64 ~x86"
POLICY_PATCH="${FILESDIR}/fix-services-postfix-r3.patch"
|
The patch(es) that you can put in the files/ location (and referred to
in the POLICY_PATCH) should be made as defined earlier in this document.
You can put multiple patches in this variable if you want.
Don't forget to run repoman manifest with every change, and run
repoman scan to check for potential mistakes.
Updating base package
To provide updates on the base policy, it is recommended to keep all patches you
made centrally in a directory (say dev/hardened/base-patches). When
you want to create a new sec-policy/selinux-base-policy release,
create a patchbundle from your patch directory, put the bundle in the
files location, create the updated ebuild and try it out.
Code Listing7.5: Building a base policy package |
~$ cd dev/hardened/base-patches
~$ tar cjvf ../overlay/sec-policy/selinux-base-policy/files/patchbundle-selinux-base-policy-2.20110726-r3.tar.bz2 *
~$ cd ../overlay/sec-policy/selinux-base-policy
~$ cp selinux-base-policy-2.20110726-r3.ebuild selinux-base-policy-2.20110726-r4.ebuild
|
Don't forget to run repoman manifest and repoman scan. You can
then install sec-policy/selinux-base-policy-2.20110726-r4 and test
it out.
The contents of this document, unless otherwise expressly stated, are licensed under the CC-BY-SA-2.5 license. The Gentoo Name and Logo Usage Guidelines apply.
|