= Building SELinux Policy Modules = {i} The procedure shown here is for SELinux Policy based on the [[http://oss.tresys.com/projects/refpolicy|SELinux Reference Policy]], which for Fedora means Fedora Core 5 and later. == Introduction == It is quite often necessary to make local changes to SELinux policy, for instance: * if your usage of some package is particularly unusual, or * if there is something missing from, or a bug in, the policy provided in Fedora Core Making such local changes isn't particularly difficult, and is certainly better than disabling SELinux altogether, which is often the first option people consider. This article provides a brief introduction to building local policy modules. <> == Get Ready for Local Policy Development == It's convenient to set aside a directory to use for local policy modules, and make sure that you have the necessary packages installed. I tend to use the `/root/selinux.local` directory. Set up your system as follows: {{{ # yum install make selinux-policy-devel # cd /root # mkdir selinux.local # cd selinux.local # chcon -R -t usr_t . # ln -s /usr/share/selinux/devel/Makefile . }}} <> == Identifying the Problem == The first step should be to decide whether or not any problem you are having is caused by SELinux. You can do this by running SELinux in ''permissive'' mode, where SELinux denials are logged but the action is not actually denied. Use the `setenforce` command to put SELinux in ''permissive'' mode: {{{ # setenforce 0 }}} This command takes effect immediately. If the problem you were having has now gone away, you can safely assume that SELinux is the issue. You can put SELinux back in ''enforcing'' mode as follows: {{{ # setenforce 1 }}} In order to decide how to fix an SELinux problem, you first need to find out what exactly the problem is. SELinux denials are logged with `"type=AVC"` messages to either `/var/log/audit/audit.log` if you are running `auditd`, or `/var/log/messages` otherwise. Supposing you were having problems with `squid` and found the following messages in your logs: {{{ type=AVC msg=audit(1152671338.823:21775): avc: denied { name_connect } for pid=2371 comm="squid" dest=8080 scontext=system_u:system_r:squid_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket type=SYSCALL msg=audit(1152671338.823:21775): arch=40000003 syscall=102 success=no exit=-13 a0=3 a1=bf9eb1a0 a2=52e1c4 a3=b7f1ca2c items=0 pid=2371 auid=4294967295 uid=23 gid=23 euid=23 suid=0 fsuid=23 egid=23 sgid=23 fsgid=23 tty=(none) comm="squid" exe="/usr/sbin/squid" subj=system_u:system_r:squid_t:s0 type=SOCKADDR msg=audit(1152671338.823:21775): saddr=02001F907F0000010000000000000000 type=SOCKETCALL msg=audit(1152671338.823:21775): nargs=3 a0=12 a1=bbdd8f8 a2=10}}} Even without being able to understand the log messages above, it should be fairly evident that they are related to `squid`. What is actually being denied by SELinux is the `squid` dæmon's ability to connect to the Internet Cache Protocol V2 port of another squid dæmon. <> == Deciding How to Fix the Problem == There may in fact be several options for fixing the problem. The first thing to check is that you actually have the current versions of SELinux-related packages. SELinux policy is undergoing rapid development and the problem you are seeing might already be fixed in a policy update: {{{ # yum update selinux-policy\* libse\* policycoreutils }}} Failing that, it's possible that there is an existing SELinux ''boolean'' that will, if enabled, allow the action that is currently being denied. This is quite likely to be the case if what you're trying to do is a normal action for the program concerned, but not everybody would need to be able to do that action (e.g. it may not be needed for a default install). Some SELinux booleans are described in manual pages (e.g. see the `httpd_selinux` manual page). However, some of the manual pages can be out of date and some booleans aren't actually documented anywhere. You can get a list of the booleans available using the `getsebool` command: {{{ # getsebool -a }}} This will produce a long list of available booleans that can be set, and their current states. In some cases it's fairly obvious what a particular boolean is for. For instance, `use_samba_home_dirs` allows you to have home directories mounted from a samba server (this is explained in the `samba_selinux` manual page). In other cases you may need to look at the policy sources to see exactly what is being allowed by a particular boolean. Considering the `squid` problem above, one option would be to enable the `squid_connect_any` boolean. This would allow squid to connect to any port on remote servers. The boolean could be set as follows: {{{ # setsebool -P squid_connect_any=1 }}} {i} The `-P` option makes the change permanent so that the boolean will remain set after a reboot (see the `setsebool` manual page) In this case, although this would fix the problem, it's not the ideal solution since it would allow the `squid` dæmon to make outbound connections to ''any'' port; a compromised `squid` server could then be used to send spam, connecting to the SMTP port on victims' mail servers. In the absence of an appropriate boolean, local policy changes will be needed. If the issue is likely to affect other people (for instance if what you're doing would be a common configuration), it will also be useful to report the issue either in [[RedHatBugzilla:|Fedora Bugzilla]] or on [[http://www.redhat.com/mailman/listinfo/fedora-selinux-list|fedora-selinux-list]]. <> == Examining Policy Sources == When making modifications to SELinux Policy, it's useful to be able to look at the policy sources to see what's already in policy, make comparisons with the policy for other services etc. The Fedora `selinux-policy` package usually has a number of significant patches so it's well worth getting and unpacking the `selinux-policy` source RPM for the sources. The `yumdownloader` tool from the `yum-utils` package is useful here: {{{ $ yumdownloader --source selinux-policy }}} Before unpacking the SRPM, you will to create an RPM build environment for your account (see [[CreateRPMBuildEnvironment]]). {{{ $ rpm -Uvh selinux-policy-*.src.rpm $ cd ~/rpmbuild/SPECS $ rpmbuild -bp selinux-policy.spec}}} You should now have the patched SELinux policy in the `~/rpmbuild/BUILD` directory. <> == Creating a Local Policy Module == The `audit2allow` tool is invaluable when it comes to creating local policy modules. It can take a file containing AVC messages and generate policy rules to allow the currently-denied action. So for instance, with a file called `avcs` containing the `squid` issues above, the output would be as follows: {{{ # audit2allow -i avcs -R #allow squid_t http_cache_port_t:tcp_socket name_connect; optional_policy(`corenetwork', ` corenet_tcp_connect_http_cache_port(squid_t) ');}}} The output of `audit2allow` will often include multiple options for rules that can be used, with all but one commented out. In cases like that, it's useful to know * what the "problem" program is actually needing to do, and * what the various options presented by `audit2allow` will allow it to do The first of these needs some understanding of the "problem" program, and the second involves looking at the policy source for the interface concerned. These are outside the scope of this article, but the rule to go for is the one that allows everything that's needed, and as little as possible of what isn't needed. So, the rule needed in this case is: {{{ corenet_tcp_connect_http_cache_port(squid_t) }}} {i} The `optional_policy` wrapper around this rule can be dropped since `corenetwork` is always included in the base policy. Adding the necessary preamble for a local policy module called `localmisc`, we end up with a policy rules file (which should be saved as `localmisc.te` in the `/root/selinux.local` directory created earlier: {{{ policy_module(localmisc, 0.1.0) require { type squid_t; }; # Squid connecting to and communicating with other caches corenet_tcp_connect_http_cache_port(squid_t) corenet_tcp_sendrecv_http_cache_port(squid_t)}}} Notes: * The number `0.1.0` in the `policy_module` declaration is a module version number. Use anything you like, but increment it every time you edit the module. * The `require` clause should list any SELinux types referenced in the policy module, which in this case is just `squid_t`. * The additional `corenet_tcp_sendrecv_http_cache_port(squid_t)` rule allows the squid dæmon to communicate with other `squid` dæmons after connecting to them. The need for this rule would have become apparent after creating a local policy module containing just the `corenet_tcp_connect_http_cache_port(squid_t)` rule, or by running `squid` with SELinux in ''permissive'' mode. To compile the policy module, just run `make` in the `/root/selinux.local` directory: {{{ # cd ~/selinux.local # make Compiling targeted localmisc module /usr/bin/checkmodule: loading policy configuration from tmp/localmisc.tmp /usr/bin/checkmodule: policy configuration loaded /usr/bin/checkmodule: writing binary representation (version 5) to tmp/localmisc.mod Creating targeted localmisc.pp policy package}}} The module can then be loaded using `semodule`: {{{ # semodule -i localmisc.pp }}} Running `semodule -l` should show that module `localmisc` version 0.1.0 (amongst others) is loaded: {{{ # semodule -l amavis 1.0.5 clamav 1.0.3 dcc 1.0.1 localmisc 0.1.0 pyzor 1.0.4 razor 1.0.1}}} At this point, the SELinux issues affecting `squid` should be resolved. If not, it's possible that additional rules are needed, as with the `corenet_tcp_sendrecv_http_cache_port(squid_t)` rule mentioned earlier. <> == Module Longevity == Local policy modules loaded using `semodule` will survive a reboot; there is nothing further to do to ensure this. They will also survice most base policy updates, unless there is a major policy format change or something in the new policy conflicts with your policy module. This is only likely to happen with more advanced modules that define their own types or file contexts, and not with modules that just contain rules generated using `audit2allow`. For instance, I had a local policy module for `proftpd` that supported use of the `ftpdctl` program to do run-time configuration changes to the FTP dæmon. This module defined a new type, `ftpdctl_exec_t`. I submitted this policy change for inclusion in the upstream reference policy, and eventually it was inoorporated into the Fedora `selinux-policy` package when that package updated to the reference policy version with my change in it. When the new `selinux-policy-targeted` package was installed, the rpm post-install script for the package failed as follows: {{{ libsepol.scope_copy_callback: proftpd: Duplicate declaration in module: type/attribute ftpdctl_exec_t libsemanage.semanage_link_sandbox: Link packages failed semodule: Failed!}}} So the new base policy couldn't be linked with my local `proftpd` policy module, since both tried to define `ftpdctl_exec_t`. The solution was to unload my local policy module as it was no longer needed: {{{ # semodule -r proftpd }}} However, that still left me with the old base policy loaded (since linking the new one with my local modules had failed at rpm upgrade time), so I also needed: {{{ # semodule -b /usr/share/selinux/targeted/base.pp }}} This got me fully up to date. ---- This article was written by PaulHowarth, with contributions from Hans Ulrich Niedermann and Klaus Weidner. ---- CategoryTip