I will use the policy written for INN (InterNetNews server) to help you get a better idea of writing policy for an application. After you edit and save the policy file, run "make load" in your policy source directory to apply the changes.
There are three files involved with the policy for INN. One is /etc/selinux/domains/program/innd.te . The second is the corresponding file_contexts file, /etc/selinux/file_contexts/program/innd.fc. The third is the net_contexts file.
Here is the entire policy. When you start writing policy, you can try and start the daemon. You'll get lots of messages in your logs, so you'll have to create rules to begin eliminating any "denied" messages. These give you clues as to what to do next.
#DESC INN - InterNetNews server
#
# Author: Faye Coker <[email protected]>
# X-Debian-Packages: inn
#
################################
# Types for the server port and news spool.
#
type innd_port_t, port_type;
type news_spool_t, file_type, sysadmfile;
# need privmail attribute so innd can access system_mail_t
daemon_domain(innd, `, privmail')
# allow innd to create files and directories of type news_spool_t
create_dir_file(innd_t, news_spool_t)
# allow user domains to read files and directories these types
r_dir_file(userdomain, { news_spool_t innd_var_lib_t innd_etc_t })
can_exec(initrc_t, innd_etc_t)
can_exec(innd_t, { innd_exec_t bin_t })
ifdef(`hostname.te', `
can_exec(innd_t, hostname_exec_t)
')
allow innd_t var_spool_t:dir { getattr search };
can_network(innd_t)
can_unix_send( { innd_t sysadm_t }, { innd_t sysadm_t } )
allow innd_t self:unix_dgram_socket create_socket_perms;
allow innd_t self:unix_stream_socket create_stream_socket_perms;
can_unix_connect(innd_t, self)
allow innd_t self:fifo_file rw_file_perms;
allow innd_t innd_port_t:tcp_socket name_bind;
allow innd_t self:capability { dac_override kill setgid setuid net_bind_service };
allow innd_t self:process setsched;
allow innd_t { bin_t sbin_t }:dir search;
allow innd_t usr_t:lnk_file read;
allow innd_t usr_t:file { getattr read ioctl };
allow innd_t lib_t:file ioctl;
allow innd_t { etc_t resolv_conf_t }:file { getattr read };
allow innd_t { proc_t etc_runtime_t }:file { getattr read };
allow innd_t urandom_device_t:chr_file read;
allow innd_t innd_var_run_t:sock_file create_file_perms;
# allow innd to read directories of type innd_etc_t (/etc/news/(/.*)? and symbolic links with that type
etcdir_domain(innd)
# allow innd to create files under /var/log of type innd_log_t and have a directory for its own files that
# it can write to
logdir_domain(innd)
# allow innd read-write directory permissions to /var/lib/news.
var_lib_domain(innd)
ifdef(`crond.te', `
system_crond_entry(innd_exec_t, innd_t)
')
The first step is to create the file. Because innd is a daemon listening on a port, we will need to create the type
innd_port_t and assign it the port_type attribute (remember the attrib.te file?). We also know that a news spool is required, and we want to have a label assigned to the news spool files specific to INN. We call that type
news_spool_t and give it the attributes of file_type and sysadmfile. sysadmfile is needed because we must grant access to the administrator domain to access those files with type news_spool_t. So from all this, we get
type innd_port_t, port_type;
type news_spool_t, file_type, sysadmfile;
Next, we need the macro
daemon_domain() so that we can establish innd as a daemon with its own domain (which is innd_t). We need the attribute
privmail because innd needs to be able to transition to the system_mail_t domain in order to send mail. Now we have
daemon_domain(innd, `, privmail')
Above we created the type
news_spool_t and now we want innd to be able to create the files and directories with that type. The create_dir_file() macro is needed for this so we have
create_dir_file(innd_t, news_spool_t)
which says that innd_t can create directories and files of type news_spool_t.
We need our unprivileged users to be able to read the news spool files. We also need them to be able to read /var/lib/news/ which has been assigned the type innd_var_lib_t (see the innd.fc file), and we need them to be able to read /etc/news/ which has the type innd_etc_t. When these rules aren't plugged in, you'll get "avc denied" messages in your logs, so from there we work out what labels and rules are needed. Now we have
r_dir_file(userdomain, { news_spool_t innd_var_lib_t innd_etc_t })
When starting up innd at this point, I received avc denied messages for initrc_d not being able to access innd_etc_t, and innd_t not being to access
innd_exec_t and
bin_t. We need the
can_exec() macro here so that innd_t can execute programs of those types, giving us
can_exec(initrc_t, innd_etc_t)
can_exec(innd_t, { innd_exec_t bin_t })
An avc denied message in the logs was showing that innd_t could not access
hostname_exec_t. We could put in a rule allowing innd_t to execute files of type hostname_exec_t but instead we take the following approach:
ifdef(`hostname.te', `
can_exec(innd_t, hostname_exec_t)
')
The ifdef line is used with the
can_exec macro because if you don't name hostname.te in the policy, then there will be no type
hostname_exec_t and the policy won't compile. We then use the
can_exec() macro as before.
We now want innd_t to be able to search and get the attributes of directories with the type var_spool_t and looking for var_spool_t which is the /var/spool directory.
allow innd_t var_spool_t:dir { getattr search };
We know that innd_t will need network functionality so we need the following:
can_network(innd_t)
After that, we'll place all the network related stuff together to make for easier reading.
can_unix_send( { innd_t sysadm_t }, { innd_t sysadm_t } )
The
can_unix_send() macro is used for sending Unix datagrams. In the line above we are saying "innd_t can send to and receive from itself and the sysadm_t domain, and sysadm_t can send to and receive from innd_t and itself".
We now need innd_t to be able to create socket permissions in itself (that is, in its own domain of innd_t) so we add
allow innd_t self:unix_dgram_socket create_socket_perms;
allow innd_t self:unix_stream_socket create_stream_socket_perms;
innd_t will also need to be able to establish a stream connection to itself and for this we need the following macro and parameters:
can_unix_connect(innd_t, self)
innd_t will need read-write file permissions on anonymous pipes (created by the pipe() system call) in the innd_t domain. For this we include
allow innd_t self:fifo_file rw_file_perms;
innd_t will need to bind to a tcp_socket so we need
allow innd_t innd_port_t:tcp_socket name_bind;
innd_t will need the following capabilities listed. See
/usr/include/linux/capability.h for more information on what each capability does. innd_t will also need to be able to change the nice levels of innd processes.
allow innd_t self:capability { dac_override kill setgid setuid net_bind_service };
allow innd_t self:process setsched;
innd_t will need access to the listed domains. For the first allow rule below, innd_t will need to be able to search directories of types
bin_t and
sbin_t. For the second allow rule, innd_t will need read access to symbolic links of type
usr_t. When you have a getattr operation, include a read operation as well (as in the third, fifth and sixth allow rules below) because when you require getattr access, you'll almost certainly be needing read access too.
allow innd_t { bin_t sbin_t }:dir search;
allow innd_t usr_t:lnk_file read;
allow innd_t usr_t:file { getattr read ioctl };
allow innd_t lib_t:file ioctl;
allow innd_t { etc_t resolv_conf_t }:file { getattr read };
allow innd_t { proc_t etc_runtime_t }:file { getattr read };
allow innd_t urandom_device_t:chr_file read;
allow innd_t innd_var_run_t:sock_file create_file_perms;
innd_t will need to be able to read the
/etc/news directory and files in it. innd_etc_t is the type assigned to
/etc/news (see innd.fc). The file global_macros.te contains the macro definition for
etcdir_domain, so read it to get a better idea of what it does. To our innd.te policy file we now add
etcdir_domain(innd)
innd_t will need to create log files under
/var/log with a type of
innd_var_log_t. It will need a directory of its own that it can write to. To achieve this, we need
logdir_domain(innd)
innd_t will need read-write directory permissions to
/var/lib/news so we include
var_lib_domain(innd)
Lastly, we need the crond_t domain to be able to transition to innd_t when executing a program with the type innd_exec_t.
ifdef(`crond.te', `
system_crond_entry(innd_exec_t, innd_t)
')
The innd.fc file contains the file contexts we need to create for INN. These are the contents:
/usr/sbin/innd.* -- system_u:object_r:innd_exec_t /var/run/innd(/.*)? system_u:object_r:innd_var_run_t /etc/news(/.*)? system_u:object_r:innd_etc_t /etc/news/boot -- system_u:object_r:innd_exec_t /var/spool/news(/.*)? system_u:object_r:news_spool_t /var/log/news(/.*)? system_u:object_r:innd_log_t /var/lib/news(/.*)? system_u:object_r:innd_var_lib_t /usr/sbin/in.nnrpd -- system_u:object_r:innd_exec_t /usr/lib/news/bin/.* -- system_u:object_r:innd_exec_t /usr/bin/inews -- system_u:object_r:innd_exec_t /usr/bin/rnews -- system_u:object_r:innd_exec_t
On the lefthand side of the file, we have the files and directories specific to INN. On the righthand side we assign the contexts. It is best to follow naming conventions at all times, such as appending _exec_t to the daemon name for executable files. In the middle of innd.fc we have -- which refers to regular files only. The entry for
/etc/news(/.*)? can not contain -- because that directory contains subdirectories and does not consist solely of regular files.
After saving your changes to this file, you need to apply the file contexts. From your policy source directory, run the following command to make the main file_contexts file so you can then relabel parts of the file system concerned with what you've specified in innd.fc :
make file_contexts/file_contexts
If you're not in the policy source directory, run
make -C file_contexts/file_contexts
The next step is to run the setfiles command to relabel each file or directory you have listed on the lefthand side. I'll take the first line of innd.fc as an example:
setfiles -v file_contexts/file_contexts /usr/sbin
After running this command, run a ls -Z command on
/usr/sbin/innd.* to see that the relabelling has taken place.
The net_contexts file in your policy source directory contains security contexts for network entities. The following line needs to be added to this file so that you are assigning tcp port 119 the type innd_port_t and no other type will be able to bind to that port.
ifdef(`innd.te', `portcon tcp 119 system_u:object_r:innd_port_t')