In fedora the resources and files necessary for building new modular policies are in the devel directory under /usr/share/selinux/. This directory and all the files required for module development come from the fedora selinux-policy-devel rpm.
As an example, I am going to develop a policy module for the lighttpd http server, which is a "light" apache-like server.
The starting point is setting up a directory in which we will develop the three files (lighttp.te lighttp.fc lighttp.if) we need to build the module. A lot of the module content will be similar to the apache module (provided with the standard reference policy), and we will use the apache module as a guide. However, if you look at the apache module (in particular, the source files that make up the module; apache.te apache.fc apache.if) things look daunting! In these notes, this is going to be a step by step process, where we will start simple and come back again and again to the three files, developing and expanding them as we go along.
The more known about the daemon or program the easier the task will be for building an SElinux module. However, if you only know the basics, you will soon learn much more about how it works through the process of building the SElinux module. Like apache, lighttpd is a complex and highly configurable web server, and we restrict our initial module/policy development for only the basic configurations and loadable shared modules (to avoid equivocation, be careful to distinguish loadable lighttpd modules from SElinux modules). The loadable lighttpd modules we will initially cover in our SElinux policy are mod_rewrite, mod_access and mod_accesslog. These comprise a basic web server that can serve static web content, log its actions, and rewrite pages. The policy can be easily extended to cover additional loadable modules and facilities.
Type declarations
The starting point is to create the types and transitions we need for the daemon. We create a new/blank type enforcement file and call it lighttpd.te. I initially started editing these types of files using my preferred text editor emacs but now use eclipse with the SLIDE plugin, but any standard text editor will do.
The first thing to add to the empty lighttpd.te file is a declaration of the module name and version information
The "policy_module" is actually a macro that defines the module and specific useful header information for us. The version, "1.0.0", is simply a way to identify what version of this module is being used on a system. It is the job of the policy writer/amender to increment this appropriately when changes are made and it is important for "hot" updating modules in a live system.
The lighttpd daemon will need a unique type when it runs so that we can define rules that only apply to it. We could call this type anything we want but it is helpful to follow convention so that others can intuitively understand what the type is just from its name. The process type or domain will be called lighttpd_t
.
We also need to workout how the daemon gets the lighttpd_t
type when it starts up. A new file type lighttpd_exec_t
allows the lighttpd executable to be uniquely identified so we can create a transition rule.
The two type declarations together are:
# new process type (domain): for lighttpd daemons
type lighttpd_t;
# new file type: for the lighttpd executable
type lighttpd_exec_t;
Note, the hash or thatch (#
) is used in all files to make comments.
The domain transition
Specifying how a new process acquires a specific type is done through a transition rule. A transition rule requires three types to be specified; the file type of the executable, the domain or process type of the process that executes or creates the new process, and the domain or process type given to the new process.
The generic form of a transition rule that covers all varieties of transition (e.g. domain transitions, file transitions etc.) is:
For our purposes, we want a domain transition: selecting the new domain/process type lighttpd_t
when the executable with file type lighttpd_exec_t
is executed by the init scripts (which start/stop the system daemons). Knowing that the initrc scripts run with the type initrc_t
gives us all the information we need for the transition rule:
However, we will not be using this rule directly in the type enforcement file, rather, we use an interface to accomplish the same thing....
Using interfaces
As mentioned in earlier notes, interfaces are part of what make the module system work. An interface provides a way of using types and rules that other modules define. In general, types that are not declared or created in this module should not be directly used by this module. For example, the type initrc_t
was not declared in this module, rather in the init module/policies. If the lighttpd module uses the initrc_t
type directly, and at some later date there is a change to how the init module and policies work, that type may no longer do what it used to, or indeed may no longer be the right type to use. To avoid this dependency and make the lighttpd module robust to changes in other modules or other parts of the policy, we use interfaces. The init module and policies provides quite a number of interfaces. The lighttpd module needs one that creates the rules for a domain transition on execution of the lighttpd daemon. The interfaceinit_daemon_domain
will do what we need. It is a macro that takes two arguments, the domain type we want the daemon to take on (lighttpd_t
) and the file type of the executable(s) that will turn into this type when run by the init scripts (lighttpd_exec_t
):
Note, no semicolon is needed at the end of macros. This interface not only provides the transition rule we require, but also marks the given types (lighttpd_t
and lighttpd_exec_t
) with the appropriate attributes (e.g. giving lighttpd_t
the daemon
attribute). Furthermore, this interface creates all the appropriate rules for this transition to work (e.g. allowing the lighttpd daemon with the lighttpd_t
type to signal with "sigchld" its parent initrc process). Consequently, interfaces make the job of creating a new module much easier.
So, how do we go about finding the right interfaces to use? The first step is to know what you want. We wanted a transition rule that involved the init system. In fedora, the SElinux development rpm provides all the interfaces to the current reference policy in the directory /usr/share/selinux/devel/include. The grep
-r
command is your friend (that's recursive grep, see the grep man page). For example, grep
for types or rules you are looking for. Its also useful to look at other modules and how they work. The source to the reference policy is available from tresys which has many example modules.
Developing the lighttpd policy
There are lots of generic things the lighttpd web server needs to be able to do - access shared system libraries, localizations etc. These can be done through kernel and system policy interfaces. We add the following interface macros to the lighttpd.te file to create the rules for these general activities:
# use interface to init system (system/init)
# "Create a domain which can be started by initrc scripts"
init_daemon_domain(lighttpd_t, lighttpd_exec_t)
# use interface to the libraries (system/libraries.if)
# "Use the dynamic link/loader for automatic loading of shared libraries"
libs_use_ld_so(lighttpd_t)
# use interface to the libraries (system/libraries.if)
# "Load and execute functions from shared libraries"
libs_use_shared_libs(lighttpd_t)
# use interface to miscellaneous files (system/miscfiles.if)
# "Allow process to read localization info"
miscfiles_read_localization(lighttpd_t)
I found these interfaces partly by looking at other modules (a particularly good module to look at as an example in this case is the apache module) and partly by knowing what you need the daemon/program to be able to do. For example, loading shared libraries is an obvious need for most executables (well, non-static ones). Looking through the available library interfaces (at system/libraries.if) shows a number of useful interfaces that can be used.
Configuration files
The lighttpd daemon requires to read its configuration files in the directory /etc/lighttpd. These files should not need to be read by anything else on the system as they belong to the lighttpd server. We should make these configuration files private to the lighttpd server and provide and interface to reading these files in case other modules need to do this. We will define the interface in later notes (when we deal with the interface file), here we make these files private by creating a specific file type for them:
# new file type: for config files
type lighttpd_config_t;
# use interface to kernel files system (kernel/files.if)
# "Make the specified type usable for files in a filesystem"
files_type(lighttpd_config_t)
The macro "files_type" simply adds the attribute to identify the new lighttpd_config_t
type as a file type.
Of course, we want the lighttpd daemon to be able to read these files, so we add the rules necessary to do this. As the lighttpd_config_t
type is declared in this module we can directly use rules:
# rule: allow lighttpd_t to list directories with the lighttpd_config_t type
allow lighttpd_t lighttpd_config_t:dir list_dir_perms;
This rule allows the processes of the lighttpd_t
type to "list_dir_perms" on directories labelled with the lighttpd_config_t
type. The "list_dir_perms" is actually a macro that is a collection of permissions (in fact, { getattr search read lock ioctl }
). Clearly we need more than this, and thankfully there is a conveient macro (that is not an interface) that generates all the necessary rules for us:
# use a defined macro that allows files to be read
read_files_pattern(lighttpd_t,lighttpd_config_t,lighttpd_config_t)
How do we know what this macro does? To find out you can grep
for it in the SElinux development source tree:
#
cd /usr/share/selinux/devel/include
#
grep -r read_files_pattern *
support/file_patterns.spt:define(`read_files_pattern',`
This tells us that the read_files_pattern macro is defined in the file support/file_patterns.spt. The macro definition in that file is:
So we can see this macro simply creates two rules, the first allowing types of its first argument to search directories that have the type of the second argument. The second rule concerns reading of files. So this one macro has defined the necessary rules for the lighttpd server to read its configuration files.
Loadable modules
The lighttpd daemon uses loadable shared object modules that, on fedora, are located in /usr/lib/lighttpd. In a similar ethos to the configuration files, no other process should need to read nor load these modules but lighttpd, we can give them a specific type:
# new file type: for lighttpd modules
type lighttpd_modules_t;
# use interface to kernel files system (kernel/files.if)
# "Make the specified type usable for files in a filesystem"
files_type(lighttpd_modules_t)
We must also provide the rules for lighttpd to load and execute these shared object loadable modules:
# rule: allow lighttpd_t to list directories and permissions of the modules directories
allow lighttpd_t lighttpd_modules_t:dir list_dir_perms;
# use defined macros that allow modules to be read
read_files_pattern(lighttpd_t,lighttpd_modules_t,lighttpd_modules_t)
# use defined macro that allows modules to be executed
can_exec(lighttpd_t,lighttpd_modules_t)
In addition, two kernel interfaces are used to create the rules that allow the lighttpd daemon to look at/modify the system state:
# use interface to kernel sysctrls (kernel/kernel.if)
# "Read generic kernel sysctls"
kernel_read_kernel_sysctls(lighttpd_t)
# use interface for access to /proc/meminfo (kernel/kernel.if)
# "Allows caller to read system state information in proc"
kernel_read_system_state(lighttpd_t)
Logging
The logs from web servers are very important and may contain confidential information. Log files created by the lighttpd daemon will have their own file type (lighttpd_log_t
) readable and writeable by the daemon. In later notes we create an interface to allow other modules to have read access. The type declarations and rules are:
# new file type: for log files
type lighttpd_log_t;
# use interface to kernel message and system logging (system/logging.if)
# "Make the specified type a file used for logs"
logging_log_file(lighttpd_log_t)
# rule: allow lighttpd_t processes to set directory attributes
allow lighttpd_t lighttpd_log_t:dir setattr;
# use predefined macros that allow reading, creating, appending, etc for log file types
create_files_pattern(lighttpd_t,lighttpd_log_t,lighttpd_log_t)append_files_pattern(lighttpd_t,lighttpd_log_t,lighttpd_log_t)read_files_pattern(lighttpd_t,lighttpd_log_t,lighttpd_log_t)
As we are allowing the lighttpd daemon to create files, we also need to specify a file transition rule so that newly created log files are of the new lighttpd_log_t
type. This can be done using the interface to the logging policy module:
# use interface to kernel message and system logging (system/logging.if)
# "Create an object in the log directory, with a private type using a type transition"
logging_log_filetrans(lighttpd_t,lighttpd_log_t,file)
Access to the network
One of the main things the lighttpd daemon will be doing is accessing (both sending and receiving on) the network. This is mostly done through a large number of kernel corenet interfaces:
# rule: allow lighttpd_t processes create tcp sockets
allow lighttpd_t self:tcp_socket create_stream_socket_perms;
# use interface (kernel/corenetwork)
# "Send and receive TCP network traffic on all interfaces"
corenet_tcp_sendrecv_all_if(lighttpd_t)
# use interface (kernel/corenetwork)
# "Send and receive TCP network traffic on all nodes"
corenet_tcp_sendrecv_all_nodes(lighttpd_t)
# use interface (kernel/corenetwork)
# "Send and receive TCP network traffic on all ports"
corenet_tcp_sendrecv_all_ports(lighttpd_t)
# use interface (kernel/corenetwork)
# "Bind TCP sockets to all nodes"
corenet_tcp_bind_all_nodes(lighttpd_t)
# use interface (kernel/corenetwork)
# "Bind TCP sockets to the http port"
corenet_tcp_bind_http_port(lighttpd_t)
# use interface (kernel/corenetwork)
# "Bind TCP sockets to the http_cache port"
corenet_tcp_bind_http_cache_port(lighttpd_t)
# use interface (kernel/corenetwork)
# "Send and receive UDP network traffic on all interfaces"
corenet_udp_sendrecv_all_if(lighttpd_t)
# use interface (kernel/corenetwork)
# "Send and receive UDP network traffic on all nodes"
corenet_udp_sendrecv_all_nodes(lighttpd_t)
# use interface (kernel/corenetwork)
# "Send and receive UDP network traffic on all ports"
corenet_udp_sendrecv_all_ports(lighttpd_t)
# use interface (kernel/corenetwork)
# "Send and receive http_server packets"
corenet_sendrecv_http_server_packets(lighttpd_t)
Accessing the source web files
On a default fedora (core 6) system, web source files are located in /var/www and the system is configured for the apache web server. The web source html files can be wherever you want them as long as apache is configured correctly, and they are labelled with the httpd_sys_content_t
type. This actually makes the job of accessing these files easy, as we use this setup and the interfaces provided by the apache policy module to give our lighttpd daemon access to all the same system web file spaces:
# use interface apache interface (services/apache.if)
# "Allow the specified domain to manage apache system content files"
apache_manage_sys_content(lighttpd_t)
# use interface apache interface (services/apache.if)
# "Search apache system content"
apache_search_sys_content(lighttpd_t)
# use interface apache interface (services/apache.if)
# "Read apache system content"
apache_read_sys_content(lighttpd_t)
The next step
This is the bare bones of a policy, specifying the type enforcement declarations and rules for a module for the lighttpd web server. I have skiped a couple of small things such as creating pid files (in /var/run) that can be found in the provided lighttpd.te source. Note this source is as described above, i.e. it is really the bare bones, designed as a starting point for developing the module further (see later notes).
The next step is creating the file context file (lighttpd.fc) that labels the filesystem with the newly created types.