[PATCH v4 2/3] selinux: Remove 'make' dependency

Vit Mojzis vmojzis at redhat.com
Fri Apr 30 20:58:00 UTC 2021


Compile the policy using a script executed by meson.

Generate 2 versions of the binary policy to allow installation
to systems with any selinux type (targeted, mls and minimum).

Signed-off-by: Vit Mojzis <vmojzis at redhat.com>
---
Changes:
- Hide errors regarding duplicate definition of interfaces

 libvirt.spec.in                        |  92 ++++++++++++++++
 src/security/meson.build               |  13 +++
 src/security/selinux/compile_policy.py | 144 +++++++++++++++++++++++++
 src/security/selinux/mcs/meson.build   |  20 ++++
 src/security/selinux/meson.build       |   7 ++
 src/security/selinux/mls/meson.build   |  20 ++++
 6 files changed, 296 insertions(+)
 create mode 100755 src/security/selinux/compile_policy.py
 create mode 100644 src/security/selinux/mcs/meson.build
 create mode 100644 src/security/selinux/meson.build
 create mode 100644 src/security/selinux/mls/meson.build

diff --git a/libvirt.spec.in b/libvirt.spec.in
index da7af2824e..940582b2c7 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -3,6 +3,12 @@
 # This spec file assumes you are building on a Fedora or RHEL version
 # that's still supported by the vendor. It may work on other distros
 # or versions, but no effort will be made to ensure that going forward.
+
+%if 0%{?fedora} > 33 || 0%{?rhel} > 8
+    %global with_selinux 1
+    %global modulename virt
+%endif
+
 %define min_rhel 7
 %define min_fedora 31
 
@@ -467,6 +473,12 @@ Requires(pre): shadow-utils
 # Needed by /usr/libexec/libvirt-guests.sh script.
 Requires: gettext
 
+%if 0%{?with_selinux}
+# This ensures that the *-selinux package and all it’s dependencies are not pulled
+# into containers and other systems that do not use SELinux
+Requires: (%{name}-daemon-selinux if selinux-policy-base)
+%endif
+
 # Ensure smooth upgrades
 Obsoletes: libvirt-admin < 7.3.0
 Provides: libvirt-admin
@@ -979,6 +991,19 @@ Requires: libvirt-daemon-driver-network = %{version}-%{release}
 %description nss
 Libvirt plugin for NSS for translating domain names into IP addresses.
 
+%if 0%{?with_selinux}
+# SELinux subpackage
+%package daemon-selinux
+Summary: Libvirt daemon SELinux policy
+Requires: selinux-policy-base
+Requires(post): selinux-policy-base
+BuildRequires: selinux-policy-devel
+BuildArch: noarch
+%{?selinux_requires}
+
+%description daemon-selinux
+SELinux policy module for libvirt daemons.
+%endif
 
 %prep
 
@@ -1495,6 +1520,63 @@ getent group virtlogin >/dev/null || groupadd -r virtlogin
 exit 0
 %endif
 
+%if 0%{?with_selinux}
+# SELinux contexts are saved so that only affected files can be
+# relabeled after the policy module installation
+%pre daemon-selinux
+if [ -e /etc/selinux/config ]; then
+    . /etc/selinux/config
+    %selinux_relabel_pre -s ${SELINUXTYPE}
+fi
+
+%post daemon-selinux
+# only policy reload is needed - module installation is managed by triggers
+/usr/sbin/selinuxenabled && /usr/sbin/load_policy || :
+
+%postun daemon-selinux
+if [ $1 -eq 0 ]; then
+    /usr/sbin/selinuxenabled && /usr/sbin/load_policy || :
+fi
+
+%posttrans daemon-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}-daemon-selinux -- selinux-policy-targeted
+/usr/sbin/semodule -n -s targeted -X 200 -i %{_datadir}/selinux/packages/%{modulename}.pp.bz2 || :
+
+%triggerin -n %{name}-daemon-selinux -- selinux-policy-minimum
+/usr/sbin/semodule -n -s minimum -X 200 -i %{_datadir}/selinux/packages/%{modulename}.pp.bz2 || :
+# libvirt module is installed by default, but disabled -- enable it
+/usr/sbin/semodule -n -s minimum -e %{modulename} || :
+
+%triggerin -n %{name}-daemon-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
+# libvirt-selinux or selinux-policy-* was removed from the system,
+# but not when either package gets updated
+%triggerun -n %{name}-daemon-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}-daemon-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}-daemon-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
+
 %files
 
 %files docs
@@ -1955,5 +2037,15 @@ exit 0
 %{_datadir}/libvirt/api/libvirt-qemu-api.xml
 %{_datadir}/libvirt/api/libvirt-lxc-api.xml
 
+%if 0%{?with_selinux}
+%files daemon-selinux
+%{_datadir}/selinux/packages/%{modulename}.pp.*
+%{_datadir}/selinux/packages/mls/%{modulename}.pp.*
+%ghost %{_sharedstatedir}/selinux/targeted/active/modules/200/%{modulename}
+%ghost %{_sharedstatedir}/selinux/minimum/active/modules/200/%{modulename}
+%ghost %{_sharedstatedir}/selinux/mls/active/modules/200/%{modulename}
+%{_datadir}/selinux/devel/include/contrib/%{modulename}.if
+%endif
+
 
 %changelog
diff --git a/src/security/meson.build b/src/security/meson.build
index 416fec7900..1d377bbbf9 100644
--- a/src/security/meson.build
+++ b/src/security/meson.build
@@ -56,3 +56,16 @@ endif
 if conf.has('WITH_APPARMOR_PROFILES')
   subdir('apparmor')
 endif
+
+os_release = run_command('grep', '^ID=', '/etc/os-release').stdout()
+os_version = run_command('grep', '^VERSION_ID=', '/etc/os-release').stdout().split('=')
+if (os_version.length() == 2)
+  os_version = os_version[1]
+else
+  os_version = 0
+endif
+
+if ((os_release.contains('fedora') and os_version.version_compare('>33')) or
+    (os_release.contains('rhel') and os_version.version_compare('>8')))
+  subdir('selinux')
+endif
diff --git a/src/security/selinux/compile_policy.py b/src/security/selinux/compile_policy.py
new file mode 100755
index 0000000000..95f0741d1a
--- /dev/null
+++ b/src/security/selinux/compile_policy.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+# This script is based on selinux-policy Makefile
+# https://github.com/fedora-selinux/selinux-policy/blob/rawhide/support/Makefile.devel
+
+import subprocess
+import sys
+import os
+import glob
+
+if len(sys.argv) != 7:
+    print(("Usage: {} <policy>.te <policy>.if <policy>.fc <output>.pp <tmpdir>"
+           " <type (mls/mcs)>").format(sys.argv[0]), file=sys.stderr)
+    exit(os.EX_USAGE)
+
+module_name = os.path.splitext(os.path.basename(sys.argv[1]))[0]
+
+m4param = ["-D", "distro_redhat", "-D", "hide_broken_symptoms",
+           "-D", "mls_num_sens=16", "-D", "mls_num_cats=1024",
+           "-D", "mcs_num_cats=1024"]
+
+if sys.argv[6] == "mls":
+    m4param = ["-D", "enable_mls"] + m4param
+else:
+    m4param = ["-D", "enable_mcs"] + m4param
+
+SHAREDIR = "/usr/share/selinux"
+HEADERDIR = os.path.join(SHAREDIR, "devel/include")
+
+m4support = sorted(glob.glob("{}/support/*.spt".format(HEADERDIR)))
+header_layers = glob.glob("{}/*/".format(HEADERDIR))
+header_layers = sorted([x for x in header_layers
+                        if os.path.join(HEADERDIR, "support") not in x])
+
+header_interfaces = []
+for layer in header_layers:
+    header_interfaces.extend(glob.glob("{}/*.if".format(layer)))
+header_interfaces.sort()
+
+# prepare temp folder
+try:
+    os.makedirs(sys.argv[5])
+except Exception:
+    pass
+
+# remove old trash from the temp folder
+tmpfiles = ["{}.{}".format(module_name, ext)
+            for ext in ["mod", "mod.fc", "tmp"]]
+for name in ["iferror.m4", "all_interfaces.conf"] + tmpfiles:
+    try:
+        os.remove(os.path.join(sys.argv[5], name))
+    except Exception:
+        pass
+
+# tmp/all_interfaces.conf
+# echo "ifdef(\`__if_error',\`m4exit(1)')" > $5/iferror.m4
+with open(os.path.join(sys.argv[5], "iferror.m4"), "w") as file:
+    file.write("ifdef(`__if_error',`m4exit(1)')\n")
+
+# echo "divert(-1)" > $5/all_interfaces.conf
+with open(os.path.join(sys.argv[5], "all_interfaces.conf"), "w") as int_file:
+    int_file.write("divert(-1)\n")
+
+# m4 $M4SUPPORT $HEADER_INTERFACES $2 $5/iferror.m4
+#   | sed -e s/dollarsstar/\$\$\*/g >> $5/all_interfaces.conf
+m4_run = subprocess.run(r"m4 {} | sed -e s/dollarsstar/\$\$\*/g >> {}".format(
+                        " ".join([*m4support, *header_interfaces, sys.argv[2],
+                                  os.path.join(sys.argv[5], "iferror.m4")]),
+                        os.path.join(sys.argv[5], "all_interfaces.conf")),
+                        shell=True, check=True, stderr=subprocess.PIPE,
+                        universal_newlines=True)
+
+# Filter out messages about duplicate definition of interfaces. e.g.
+# virt.if:13: Error: duplicate definition of virt_stub_lxc(). Original
+# definition on 13.
+# They are expected and can be safely ignored.
+for line in m4_run.stderr.split('\n'):
+    if line and "Error: duplicate definition of" not in line:
+        print(line, file=sys.stderr)
+
+# doesn't work properly without "shell=True"
+#    m4_process = Popen(["m4", *m4support, *header_interfaces, sys.argv[2],
+#                        os.path.join(sys.argv[5], "iferror.m4")],
+#                       stdout=PIPE, stderr=PIPE)
+#    sed_process = Popen(["sed", "-e", "s/dollarsstar/\$\$\*/g"],
+#                        stdin=m4_process.stdout, stdout=int_file)
+#    outs, errs = m4_process.communicate()
+
+# echo "divert" >> $5/all_interfaces.conf
+with open(os.path.join(sys.argv[5], "all_interfaces.conf"), "a") as file:
+    file.write("divert\n")
+
+# tmp/%.mod
+# m4 $M4PARAM -s $M4SUPPORT $5/all_interfaces.conf $1 > $5/$MODULE_NAME.tmp
+with open(os.path.join(sys.argv[5], "{}.tmp".format(module_name)),
+          "w") as tmp_file:
+    subprocess.run(["m4", *m4param, "-s", *m4support,
+                    os.path.join(sys.argv[5], "all_interfaces.conf"),
+                    sys.argv[1]], stdout=tmp_file, check=True)
+
+# /usr/bin/checkmodule -M -m $5/$MODULE_NAME.tmp -o $5/$MODULE_NAME.mod
+subprocess.run(["/usr/bin/checkmodule",
+                "-M",
+                "-m",
+                os.path.join(sys.argv[5], "{}.tmp".format(module_name)),
+                "-o",
+                os.path.join(sys.argv[5], "{}.mod".format(module_name))],
+               check=True)
+
+
+# tmp/%.mod.fc
+# m4 $M4PARAM $M4SUPPORT $3 > $5/$MODULE_NAME.mod.fc
+with open(os.path.join(sys.argv[5],
+                       "{}.mod.fc".format(module_name)), "w") as mod_fc_file:
+    subprocess.run(["m4", *m4param, *m4support, sys.argv[3]],
+                   stdout=mod_fc_file, check=True)
+
+# %.pp
+# /usr/bin/semodule_package -o $4 -m $5/$MODULE_NAME.mod
+#   -f $5/$MODULE_NAME.mod.fc
+subprocess.run(["/usr/bin/semodule_package",
+                "-o",
+                sys.argv[4],
+                "-m",
+                os.path.join(sys.argv[5], "{}.mod".format(module_name)),
+                "-f",
+                os.path.join(sys.argv[5], "{}.mod.fc".format(module_name))],
+               check=True)
diff --git a/src/security/selinux/mcs/meson.build b/src/security/selinux/mcs/meson.build
new file mode 100644
index 0000000000..419253f151
--- /dev/null
+++ b/src/security/selinux/mcs/meson.build
@@ -0,0 +1,20 @@
+selinux_sources = [
+  '../virt.te',
+  '../virt.if',
+  '../virt.fc',
+]
+
+# targeted/minimum policy module
+virt_pp = custom_target('virt.pp',
+  output : 'virt.pp',
+  input : selinux_sources,
+  command : [compile_policy_prog, '@INPUT@', '@OUTPUT@', 'selinux/mcs/tmp', 'mcs'],
+  install : false)
+
+bzip = custom_target('virt.pp.bz2',
+  output : 'virt.pp.bz2',
+  input : virt_pp,
+  command : [bzip2_prog, '-c', '-9', '@INPUT@'],
+  capture : true,
+  install : true,
+  install_dir : 'share/selinux/packages')
diff --git a/src/security/selinux/meson.build b/src/security/selinux/meson.build
new file mode 100644
index 0000000000..c8eec463d2
--- /dev/null
+++ b/src/security/selinux/meson.build
@@ -0,0 +1,7 @@
+set_variable('compile_policy_prog', find_program('compile_policy.py'))
+set_variable('bzip2_prog', find_program('bzip2'))
+
+install_data('virt.if', install_dir : 'share/selinux/devel/include/contrib')
+
+subdir('mcs')
+subdir('mls')
diff --git a/src/security/selinux/mls/meson.build b/src/security/selinux/mls/meson.build
new file mode 100644
index 0000000000..20bab41fea
--- /dev/null
+++ b/src/security/selinux/mls/meson.build
@@ -0,0 +1,20 @@
+selinux_sources = [
+  '../virt.te',
+  '../virt.if',
+  '../virt.fc',
+]
+
+# MLS policy module
+virt_pp_mls = custom_target('virt.pp',
+  output : 'virt.pp',
+  input : selinux_sources,
+  command : [compile_policy_prog, '@INPUT@', '@OUTPUT@', 'selinux/mls/tmp', 'mls'],
+  install : false)
+
+bzip_mls = custom_target('virt.pp.bz2',
+  output : 'virt.pp.bz2',
+  input : virt_pp_mls,
+  command : [bzip2_prog, '-c', '-9', '@INPUT@'],
+  capture : true,
+  install : true,
+  install_dir : 'share/selinux/packages/mls')
-- 
2.30.2




More information about the libvir-list mailing list