Custom Product Policies targeting multiple SELinux modes
In some cases a policy package may need to be used in multiple SELinux modes (targeted, mls and in rare instances also minimum).
This can be accomplished either by creating multiple sub-packages, each for a single SELinux mode, or using a multi-mode package.
In the first case conditional "Requires" statements are used to determine which sub-package should be installed based on which selinux-policy-{targeted|minimum|mls} package is installed on the system
(e.g.
Requires: (%{name}-selinux-mls if selinux-policy-mls)
). This means that each sub-package can carry different version of the module binary and all spec file scripts are targeted to a specific SELinux mode.
On the other hand, multi-mode package carries multiple module binaries and relies on package triggers to install each one when needed. This approach helps to reduce duplicate code and simplifies dealing with switching SELinux modes, which is why we'll focus on it in this guide.
SELinux modes and binary policy
When compiling the policy module, it's important to take note which SELinux mode it will be used in. While both targeted and minimum modes make use of MCS (Multi Cathegory Security) and differ mostly only in modules enabled by default (minimum ships the same modules, but most of them are disabled), MLS utilizes much more strict Multi Level Security approach. This is reflected in binary policy files and needs to be specified during compilation.
Both NAME and TYPE variables have to be specified for proper compilation of an MLS based module.
make -f /usr/share/selinux/devel/Makefile NAME=mls TYPE=mls %{modulename}.pp
In case of MCS based module (targeted and minimum) you can omit both values (they will be set based on "/etc/selinux/config" to NAME=targeted
and TYPE=mcs
).
make -f /usr/share/selinux/devel/Makefile %{modulename}.pp
The following Makefile can handle compilation and deployment of both MCS and MLS policy versions:
TARGET?=<module_name> MODULES?=${TARGET:=.pp.bz2} SHAREDIR?=/usr/share all: ${TARGET:=.pp.bz2} mls/${TARGET:=.pp.bz2} %.pp.bz2: %.pp bzip2 -9 $^ %.pp: %.te make -f ${SHAREDIR}/selinux/devel/Makefile $@ rm -rf tmp mls/%.pp: %.te rm -rf $(^:.te=.pp) make -f ${SHAREDIR}/selinux/devel/Makefile NAME=mls TYPE=mls $(^:.te=.pp) mkdir -p mls mv $(^:.te=.pp) $@ rm -rf tmp clean: rm -f *~ *.tc *.pp *.pp.bz2 rm -rf tmp *.tar.gz install-policy: all semodule -s targeted -i ${TARGET}.pp.bz2 semodule -s mls -i mls/${TARGET}.pp.bz2 install: all install -D -m 0644 ${TARGET}.pp.bz2 ${DESTDIR}${SHAREDIR}/selinux/packages/${TARGET}.pp.bz2 install -D -m 0644 mls/${TARGET}.pp.bz2 ${DESTDIR}${SHAREDIR}/selinux/packages/mls/${TARGET}.pp.bz2 install -D -p -m 0644 ${TARGET}.if %{buildroot}%{_datadir}/selinux/devel/include/distributed/${TARGET}.if
Policy Depoloyment
To avoid installation in non-existent policy stores, multi-mode package uses package triggers to install and uninstall the module based on the presence of corresponding selinux-policy package.
%triggerin -n %{modulename}-selinux -- selinux-policy- <targeted|minimum|mls> /usr/sbin/semodule -n -s <targeted|minimum|mls> -X 200 -i %{_datadir}/selinux/packages/%{modulename}.pp.bz2 || : %triggerun -n %{name}-selinux -- selinux-policy-<targeted|minimum|mls> if ([ $1 -eq 0 ] || [ $2 -eq 0 ]) && [ -e %{_sharedstatedir}/selinux/<targeted|minimum|mls>/active/modules/200/%{modulename} ]; then /usr/sbin/semodule -n -s <targeted|minimum|mls> -X 200 -r %{modulename} || : fi
Now that the new module is installed into appropriate policy stores, it can be loaded into kernel.
%post selinux # only policy reload is needed - module installation is managed by triggers /usr/sbin/selinuxenabled && /usr/sbin/load_policy || :
This step is only needed once, since only one policy store can be active at a time.
Similarly, policy needs to be reloaded when the custom module gets removed.
%postun selinux if [ $1 -eq 0 ]; then /usr/sbin/selinuxenabled && /usr/sbin/load_policy || : fi
Finally, any changes in file context definitions need to be propagated onto the files system.
This is done using %selinux_relabel_pre
and %selinux_relabel_post
macros.
However, only active policy store is relevant in this case, which is why
SELINUXTYPE needs to be loaded from /etc/selinux/config during installation.
Example spec file changes to incorporate multi-mode -selinux subpackage
%if 0%{?with_selinux} %global with_selinux 1 %global modulename mypolicy %endif ---------------------------------------- %if 0%{?with_selinux} Requires: (%{name}-selinux if selinux-policy-base) %endif ---------------------------------------- %if 0%{?with_selinux} # SELinux subpackage %package selinux Summary: Myapp SELinux policy BuildArch: noarch Requires: selinux-policy-base Requires(post): selinux-policy-base BuildRequires: selinux-policy-devel %{?selinux_requires} %description selinux Custom SELinux policy module %endif -------- %build section ---------------- # it's probably better to use the included Makefile for %build and %install phases %if 0%{?with_selinux} # compile MLC and MCS versions of SELinux policy module mkdir selinux cp -p %{SOURCE2} %{SOURCE3} %{SOURCE4} selinux/ cp -p %{SOURCE2} %{SOURCE3} %{SOURCE4} selinux/mls pushd selinux make -f %{_datadir}/selinux/devel/Makefile %{modulename}.pp bzip2 -9 %{modulename}.pp pushd mls make -f %{_datadir}/selinux/devel/Makefile NAME=mls TYPE=mls %{modulename}.pp bzip2 -9 %{modulename}.pp popd popd %endif -------- %install section -------------- %if 0%{?with_selinux} install -D -m 0644 selinux/%{modulename}.pp.bz2 %{buildroot}%{_datadir}/selinux/packages/%{modulename}.pp.bz2 install -D -m 0644 selinux/mls/%{modulename}.pp.bz2 %{buildroot}%{_datadir}/selinux/packages/mls/%{modulename}.pp.bz2 install -D -p -m 0644 selinux/%{modulename}.if %{buildroot}%{_datadir}/selinux/devel/include/distributed/%{modulename}.if %endif ---------------------------------------- %if 0%{?with_selinux} # SELinux contexts are saved so that only affected files can be # relabeled after the policy module installation %pre selinux if [ -e /etc/selinux/config ]; then # get current SELINUXTYPE value from selinux config . /etc/selinux/config %selinux_relabel_pre -s ${SELINUXTYPE} fi %post selinux # only policy reload is needed - module installation is managed by triggers /usr/sbin/selinuxenabled && /usr/sbin/load_policy || : %postun selinux if [ $1 -eq 0 ]; then /usr/sbin/selinuxenabled && /usr/sbin/load_policy || : fi %posttrans selinux if [ -e /etc/selinux/config ]; then . /etc/selinux/config %selinux_relabel_post -s ${SELINUXTYPE} fi # install the policy module to corresponding policy store if # selinux-policy-{targeted|mls|minimum} package is installed on the system %triggerin -n %{name}-selinux -- selinux-policy-targeted /usr/sbin/semodule -n -s targeted -X 200 -i %{_datadir}/selinux/packages/%{modulename}.pp.bz2 || : %triggerin -n %{name}-selinux -- selinux-policy-minimum /usr/sbin/semodule -n -s minimum -X 200 -i %{_datadir}/selinux/packages/%{modulename}.pp.bz2 || : # each contrib module is installed by default, but disabled -- enable it /usr/sbin/semodule -n -s minimum -e %{modulename} || : %triggerin -n %{name}-selinux -- selinux-policy-mls /usr/sbin/semodule -n -s mls -X 200 -i %{_datadir}/selinux/packages/mls/%{modulename}.pp.bz2 || : # remove the policy module from corresponding module store if # %{name}-selinux or selinux-policy-* was removed from the system, # but not when either package gets updated %triggerun -n %{name}-selinux -- selinux-policy-targeted if ([ $1 -eq 0 ] || [ $2 -eq 0 ]) && [ -e %{_sharedstatedir}/selinux/targeted/active/modules/200/%{modulename} ]; then /usr/sbin/semodule -n -s targeted -X 200 -r %{modulename} || : fi %triggerun -n %{name}-selinux -- selinux-policy-minimum if ([ $1 -eq 0 ] || [ $2 -eq 0 ]) && [ -e %{_sharedstatedir}/selinux/minimum/active/modules/200/%{modulename} ]; then /usr/sbin/semodule -n -s minimum -X 200 -r %{modulename} || : /usr/sbin/semodule -n -d %{modulename} || : fi %triggerun -n %{name}-selinux -- selinux-policy-mls if ([ $1 -eq 0 ] || [ $2 -eq 0 ]) && [ -e %{_sharedstatedir}/selinux/mls/active/modules/200/%{modulename} ]; then /usr/sbin/semodule -n -s mls -X 200 -r %{modulename} || : fi %endif ---------------------------------------- %if 0%{?with_selinux} %files selinux %{_datadir}/selinux/packages/%{modulename}.pp.* %{_datadir}/selinux/packages/mls/%{modulename}.pp.* %ghost %verify(not md5 size mtime) %{_sharedstatedir}/selinux/targeted/active/modules/200/%{modulename} %ghost %verify(not md5 size mtime) %{_sharedstatedir}/selinux/minimum/active/modules/200/%{modulename} %ghost %verify(not md5 size mtime) %{_sharedstatedir}/selinux/mls/active/modules/200/%{modulename} %{_datadir}/selinux/devel/include/distributed/%{modulename}.if %endif