From Fedora Project Wiki

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
Important.png
Note that the binary version of the module is the same for targeted and minimum, but some policy modules may need to be enabled to allow deployment of the module in minimum.


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