When running libvirt from the build directory with the 'run' script, it
will run as unconfined_t. This can result in unexpected behavior when
selinux is enforcing due to the fact that the selinux policies are
written assuming that libvirt is running with the
system_u:system_r:virtd_t context. This patch adds a new --selinux
option to the run script. When this option is specified, it will launch
the specified binary using the 'runcon' utility to set its selinux
context to the one mentioned above. Since this requires root privileges,
setting the selinux context is not the default behavior and must be
enabled with the command line switch.

Signed-off-by: Jonathon Jongsma <jjong...@redhat.com>
---
 run.in | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 50 insertions(+), 6 deletions(-)

diff --git a/run.in b/run.in
index c6d3411082..4aa458b791 100644
--- a/run.in
+++ b/run.in
@@ -40,6 +40,7 @@
 #
 # ----------------------------------------------------------------------
 
+import argparse
 import os
 import os.path
 import random
@@ -59,15 +60,20 @@ def prepend(env, varname, extradir):
 
 here = "@abs_builddir@"
 
-if len(sys.argv) < 2:
-    print("syntax: %s BINARY [ARGS...]" % sys.argv[0], file=sys.stderr)
+parser = argparse.ArgumentParser(add_help=False)
+parser.add_argument('--selinux',
+                    action='store_true',
+                    help='Run in the appropriate selinux context')
+
+opts, args = parser.parse_known_args()
+
+if len(args) < 1:
+    print("syntax: %s [--selinux] BINARY [ARGS...]" % sys.argv[0], 
file=sys.stderr)
     sys.exit(1)
 
-prog = sys.argv[1]
-args = sys.argv[1:]
+prog = args[0]
 env = os.environ
 
-
 prepend(env, "LD_LIBRARY_PATH", os.path.join(here, "src"))
 prepend(env, "PKG_CONFIG_PATH", os.path.join(here, "src"))
 prepend(env, "PATH", os.path.join(here, "tools"))
@@ -130,10 +136,20 @@ def change_unit(name, action):
     return ret == 0
 
 
+def chcon(path, type):
+    ret = subprocess.call(["chcon", "-t", type, path])
+    return ret == 0
+
+
+def restorecon(path):
+    ret = subprocess.call(["restorecon", path])
+    return ret == 0
+
+
 try_stop_units = []
 if is_systemd_host():
     maybe_stopped_units = []
-    for arg in sys.argv:
+    for arg in args:
         name = os.path.basename(arg)
         if is_modular_daemon(name):
             # Only need to stop libvirtd or this specific modular unit
@@ -149,6 +165,31 @@ if is_systemd_host():
         if is_unit_active(unit):
             try_stop_units.append(unit)
 
+if opts.selinux:
+    # if using a wrapper command like 'gdb', setting the selinux context
+    # won't work because the wrapper command will not be a valid entrypoint
+    # for the virtd_t context
+    if os.path.basename(prog) not in ["libvirtd", *modular_daemons]:
+        sys.exit("'{}' is not recognized as a valid libvirt daemon. Selinux "
+                 "process context can only be set when using executing a "
+                 "libvirt daemon directly without wrapper 
commands".format(prog))
+
+    progpath = os.path.abspath(prog)
+    if not progpath.startswith(os.path.abspath(here)):
+        sys.exit("Refusing to change selinux context of file outside build "
+                 "directory: {}".format(prog))
+
+    # selinux won't allow us to transition to the virtd_t context from e.g. the
+    # user_home_t context (the likely label of the local executable file)
+    print("Setting file context of {} to virtd_exec_t...".format(progpath))
+    if not chcon(progpath, "virtd_exec_t"):
+        sys.exit("Failed to change selinux context of binary")
+
+    args = ['runcon',
+            '-u', 'system_u',
+            '-r', 'system_r',
+            '-t', 'virtd_t', *args]
+
 if len(try_stop_units) == 0:
     # Run the program directly, replacing ourselves
     os.execvpe(prog, args, env)
@@ -178,6 +219,9 @@ else:
     except Exception as e:
         print("%s" % e, file=sys.stderr)
     finally:
+        if opts.selinux:
+            print("Restoring selinux context...")
+            restorecon(prog)
         print("Re-starting original systemd units...")
         stopped_units.reverse()
         for unit in stopped_units:
-- 
2.39.2

Reply via email to