Blame SOURCES/libvirt-security-make-it-possible-to-set-SELinux-label-of-child-process-from-its-binary.patch

acda74
From 75c9ad56f08bfa0d86737f8872ea7cf7a5426bad Mon Sep 17 00:00:00 2001
acda74
Message-Id: <75c9ad56f08bfa0d86737f8872ea7cf7a5426bad@dist-git>
acda74
From: Laine Stump <laine@redhat.com>
acda74
Date: Wed, 1 Mar 2023 15:34:32 -0500
acda74
Subject: [PATCH] security: make it possible to set SELinux label of child
acda74
 process from its binary
acda74
acda74
Normally when a child process is started by libvirt, the SELinux label
acda74
of that process is set to virtd_t (plus an MCS range). In at least one
acda74
case (passt) we need for the SELinux label of a child process label to
acda74
match the label that the binary would have transitioned to
acda74
automatically if it had been run standalone (in the case of passt,
acda74
that label is passt_t).
acda74
acda74
This patch modifies virSecuritySELinuxSetChildProcessLabel() (and all
acda74
the functions above it in the call chain) so that the toplevel
acda74
function can set a new argument "useBinarySpecificLabel" to true. If
acda74
it is true, then virSecuritySELinuxSetChildProcessLabel() will call
acda74
the new function virSecuritySELinuxContextSetFromFile(), which uses
acda74
the selinux library function security_compute_create() to determine
acda74
what would be the label of the new process if it had been run
acda74
standalone (rather than being run by libvirt) - the MCS range from the
acda74
normally-used label is added to this newly derived label, and that is
acda74
what is used for the new process rather than whatever is in the
acda74
domain's security label (which will usually be virtd_t).
acda74
acda74
In order to easily verify that nothing was broken by these changes to
acda74
the call chain, all callers currently set useBinarySpecificPath =
acda74
false, so all behavior should be completely unchanged. (The next
acda74
patch will set it to true only for the case of running passt.)
acda74
acda74
https://bugzilla.redhat.com/2172267
acda74
Signed-off-by: Laine Stump <laine@redhat.com>
acda74
Reviewed-by: Andrea Bolognani <abologna@redhat.com>
acda74
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
acda74
(cherry picked from commit 75056f61f12d6efec51f699f2b901f8d02cd075c)
acda74
acda74
Conflicts:
acda74
	src/qemu/qemu_dbus.c
acda74
	src/qemu/qemu_passt.c
acda74
	src/qemu/qemu_security.c
acda74
	src/qemu/qemu_security.h
acda74
	src/qemu/qemu_slirp.c
acda74
	src/qemu/qemu_tpm.c
acda74
	src/qemu/qemu_vhost_user_gpu.c
acda74
acda74
  The argument list for qemuSecurityCommandRun changed upstream to
acda74
  remove one of the arguments, but that changeset has not been
acda74
  backported to the rhel-9.2.0 branch. (see the 4 commits starting at
acda74
  upstream commit 0634d640)
acda74
acda74
https://bugzilla.redhat.com/2172267
acda74
Signed-off-by: Laine Stump <laine@redhat.com>
acda74
---
acda74
 src/qemu/qemu_dbus.c             |  5 ++-
acda74
 src/qemu/qemu_passt.c            |  4 +-
acda74
 src/qemu/qemu_process.c          |  2 +-
acda74
 src/qemu/qemu_security.c         |  5 ++-
acda74
 src/qemu/qemu_security.h         |  1 +
acda74
 src/qemu/qemu_slirp.c            |  4 +-
acda74
 src/qemu/qemu_tpm.c              |  3 +-
acda74
 src/qemu/qemu_vhost_user_gpu.c   |  4 +-
acda74
 src/security/security_apparmor.c |  1 +
acda74
 src/security/security_dac.c      |  1 +
acda74
 src/security/security_driver.h   |  1 +
acda74
 src/security/security_manager.c  |  8 +++-
acda74
 src/security/security_manager.h  |  1 +
acda74
 src/security/security_nop.c      |  1 +
acda74
 src/security/security_selinux.c  | 73 +++++++++++++++++++++++++++++++-
acda74
 src/security/security_stack.c    |  5 ++-
acda74
 16 files changed, 107 insertions(+), 12 deletions(-)
acda74
acda74
diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c
acda74
index cb2694795e..f13c792956 100644
acda74
--- a/src/qemu/qemu_dbus.c
acda74
+++ b/src/qemu/qemu_dbus.c
acda74
@@ -219,9 +219,10 @@ qemuDBusStart(virQEMUDriver *driver,
acda74
     virCommandDaemonize(cmd);
acda74
     virCommandAddArgFormat(cmd, "--config-file=%s", configfile);
acda74
 
acda74
-    if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1,
acda74
-                               &exitstatus, &cmdret) < 0)
acda74
+    if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, false,
acda74
+                               &exitstatus, &cmdret) < 0) {
acda74
         goto cleanup;
acda74
+    }
acda74
 
acda74
     if (cmdret < 0 || exitstatus != 0) {
acda74
         virReportError(VIR_ERR_INTERNAL_ERROR,
acda74
diff --git a/src/qemu/qemu_passt.c b/src/qemu/qemu_passt.c
acda74
index 8d28a55455..ed7b518212 100644
acda74
--- a/src/qemu/qemu_passt.c
acda74
+++ b/src/qemu/qemu_passt.c
acda74
@@ -285,8 +285,10 @@ qemuPasstStart(virDomainObj *vm,
acda74
     if (qemuExtDeviceLogCommand(driver, vm, cmd, "passt") < 0)
acda74
         return -1;
acda74
 
acda74
-    if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, &exitstatus, &cmdret) < 0)
acda74
+    if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, false,
acda74
+                               &exitstatus, &cmdret) < 0) {
acda74
         goto error;
acda74
+    }
acda74
 
acda74
     if (cmdret < 0 || exitstatus != 0) {
acda74
         virReportError(VIR_ERR_INTERNAL_ERROR,
acda74
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
acda74
index 298904fe2e..e5c438aa26 100644
acda74
--- a/src/qemu/qemu_process.c
acda74
+++ b/src/qemu/qemu_process.c
acda74
@@ -7764,7 +7764,7 @@ qemuProcessLaunch(virConnectPtr conn,
acda74
 
acda74
     VIR_DEBUG("Setting up security labelling");
acda74
     if (qemuSecuritySetChildProcessLabel(driver->securityManager,
acda74
-                                         vm->def, cmd) < 0)
acda74
+                                         vm->def, false, cmd) < 0)
acda74
         goto cleanup;
acda74
 
acda74
     virCommandSetOutputFD(cmd, &logfile);
acda74
diff --git a/src/qemu/qemu_security.c b/src/qemu/qemu_security.c
acda74
index beada669f7..a5c05b86a9 100644
acda74
--- a/src/qemu/qemu_security.c
acda74
+++ b/src/qemu/qemu_security.c
acda74
@@ -637,6 +637,7 @@ qemuSecurityCommandRun(virQEMUDriver *driver,
acda74
                        virCommand *cmd,
acda74
                        uid_t uid,
acda74
                        gid_t gid,
acda74
+                       bool useBinarySpecificLabel,
acda74
                        int *exitstatus,
acda74
                        int *cmdret)
acda74
 {
acda74
@@ -644,8 +645,10 @@ qemuSecurityCommandRun(virQEMUDriver *driver,
acda74
     qemuDomainObjPrivate *priv = vm->privateData;
acda74
 
acda74
     if (virSecurityManagerSetChildProcessLabel(driver->securityManager,
acda74
-                                               vm->def, cmd) < 0)
acda74
+                                               vm->def, useBinarySpecificLabel,
acda74
+                                               cmd) < 0) {
acda74
         return -1;
acda74
+    }
acda74
 
acda74
     if (uid != (uid_t) -1)
acda74
         virCommandSetUID(cmd, uid);
acda74
diff --git a/src/qemu/qemu_security.h b/src/qemu/qemu_security.h
acda74
index 8d1c6b38c3..a7ba16e076 100644
acda74
--- a/src/qemu/qemu_security.h
acda74
+++ b/src/qemu/qemu_security.h
acda74
@@ -115,6 +115,7 @@ int qemuSecurityCommandRun(virQEMUDriver *driver,
acda74
                            virCommand *cmd,
acda74
                            uid_t uid,
acda74
                            gid_t gid,
acda74
+                           bool useBinarySpecificLabel,
acda74
                            int *exitstatus,
acda74
                            int *cmdret);
acda74
 
acda74
diff --git a/src/qemu/qemu_slirp.c b/src/qemu/qemu_slirp.c
acda74
index 3f83db03bf..e22d86b521 100644
acda74
--- a/src/qemu/qemu_slirp.c
acda74
+++ b/src/qemu/qemu_slirp.c
acda74
@@ -329,8 +329,10 @@ qemuSlirpStart(virDomainObj *vm,
acda74
     if (qemuExtDeviceLogCommand(driver, vm, cmd, "slirp") < 0)
acda74
         goto error;
acda74
 
acda74
-    if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, &exitstatus, &cmdret) < 0)
acda74
+    if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, false,
acda74
+                               &exitstatus, &cmdret) < 0) {
acda74
         goto error;
acda74
+    }
acda74
 
acda74
     if (cmdret < 0 || exitstatus != 0) {
acda74
         virReportError(VIR_ERR_INTERNAL_ERROR,
acda74
diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c
acda74
index 5831ffc32e..d4a87921d3 100644
acda74
--- a/src/qemu/qemu_tpm.c
acda74
+++ b/src/qemu/qemu_tpm.c
acda74
@@ -963,8 +963,9 @@ qemuTPMEmulatorStart(virQEMUDriver *driver,
acda74
         return -1;
acda74
 
acda74
     if (qemuSecurityCommandRun(driver, vm, cmd, cfg->swtpm_user,
acda74
-                               cfg->swtpm_group, NULL, &cmdret) < 0)
acda74
+                               cfg->swtpm_group, false, NULL, &cmdret) < 0) {
acda74
         goto error;
acda74
+    }
acda74
 
acda74
     if (cmdret < 0) {
acda74
         /* virCommandRun() hidden in qemuSecurityCommandRun()
acda74
diff --git a/src/qemu/qemu_vhost_user_gpu.c b/src/qemu/qemu_vhost_user_gpu.c
acda74
index bc5a1dc3ec..7909fffe64 100644
acda74
--- a/src/qemu/qemu_vhost_user_gpu.c
acda74
+++ b/src/qemu/qemu_vhost_user_gpu.c
acda74
@@ -153,8 +153,10 @@ int qemuExtVhostUserGPUStart(virQEMUDriver *driver,
acda74
             virCommandAddArgFormat(cmd, "--render-node=%s", video->accel->rendernode);
acda74
     }
acda74
 
acda74
-    if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, &exitstatus, &cmdret) < 0)
acda74
+    if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, false,
acda74
+                               &exitstatus, &cmdret) < 0) {
acda74
         goto error;
acda74
+    }
acda74
 
acda74
     if (cmdret < 0 || exitstatus != 0) {
acda74
         virReportError(VIR_ERR_INTERNAL_ERROR,
acda74
diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c
acda74
index b63b248975..b5642c9a28 100644
acda74
--- a/src/security/security_apparmor.c
acda74
+++ b/src/security/security_apparmor.c
acda74
@@ -570,6 +570,7 @@ AppArmorSetSecurityProcessLabel(virSecurityManager *mgr G_GNUC_UNUSED,
acda74
 static int
acda74
 AppArmorSetSecurityChildProcessLabel(virSecurityManager *mgr G_GNUC_UNUSED,
acda74
                                      virDomainDef *def,
acda74
+                                     bool useBinarySpecificLabel G_GNUC_UNUSED,
acda74
                                      virCommand *cmd)
acda74
 {
acda74
     g_autofree char *profile_name = NULL;
acda74
diff --git a/src/security/security_dac.c b/src/security/security_dac.c
acda74
index 9be8f458d1..ca3f4d2dc5 100644
acda74
--- a/src/security/security_dac.c
acda74
+++ b/src/security/security_dac.c
acda74
@@ -2273,6 +2273,7 @@ virSecurityDACSetProcessLabel(virSecurityManager *mgr,
acda74
 static int
acda74
 virSecurityDACSetChildProcessLabel(virSecurityManager *mgr,
acda74
                                    virDomainDef *def,
acda74
+                                   bool useBinarySpecificLabel G_GNUC_UNUSED,
acda74
                                    virCommand *cmd)
acda74
 {
acda74
     virSecurityDACData *priv = virSecurityManagerGetPrivateData(mgr);
acda74
diff --git a/src/security/security_driver.h b/src/security/security_driver.h
acda74
index fe6982ceca..aa1fb2125d 100644
acda74
--- a/src/security/security_driver.h
acda74
+++ b/src/security/security_driver.h
acda74
@@ -96,6 +96,7 @@ typedef int (*virSecurityDomainSetProcessLabel) (virSecurityManager *mgr,
acda74
                                                  virDomainDef *def);
acda74
 typedef int (*virSecurityDomainSetChildProcessLabel) (virSecurityManager *mgr,
acda74
                                                       virDomainDef *def,
acda74
+                                                      bool useBinarySpecificLabel,
acda74
                                                       virCommand *cmd);
acda74
 typedef int (*virSecurityDomainSecurityVerify) (virSecurityManager *mgr,
acda74
                                                 virDomainDef *def);
acda74
diff --git a/src/security/security_manager.c b/src/security/security_manager.c
acda74
index 2f8e89cb04..b0578d7209 100644
acda74
--- a/src/security/security_manager.c
acda74
+++ b/src/security/security_manager.c
acda74
@@ -885,10 +885,14 @@ virSecurityManagerSetProcessLabel(virSecurityManager *mgr,
acda74
 int
acda74
 virSecurityManagerSetChildProcessLabel(virSecurityManager *mgr,
acda74
                                        virDomainDef *vm,
acda74
+                                       bool useBinarySpecificLabel,
acda74
                                        virCommand *cmd)
acda74
 {
acda74
-    if (mgr->drv->domainSetSecurityChildProcessLabel)
acda74
-       return mgr->drv->domainSetSecurityChildProcessLabel(mgr, vm, cmd);
acda74
+    if (mgr->drv->domainSetSecurityChildProcessLabel) {
acda74
+       return mgr->drv->domainSetSecurityChildProcessLabel(mgr, vm,
acda74
+                                                           useBinarySpecificLabel,
acda74
+                                                           cmd);
acda74
+    }
acda74
 
acda74
     virReportUnsupportedError();
acda74
     return -1;
acda74
diff --git a/src/security/security_manager.h b/src/security/security_manager.h
acda74
index 4afdcc167b..97add3294d 100644
acda74
--- a/src/security/security_manager.h
acda74
+++ b/src/security/security_manager.h
acda74
@@ -145,6 +145,7 @@ int virSecurityManagerSetProcessLabel(virSecurityManager *mgr,
acda74
                                       virDomainDef *def);
acda74
 int virSecurityManagerSetChildProcessLabel(virSecurityManager *mgr,
acda74
                                            virDomainDef *def,
acda74
+                                           bool useBinarySpecificLabel,
acda74
                                            virCommand *cmd);
acda74
 int virSecurityManagerVerify(virSecurityManager *mgr,
acda74
                              virDomainDef *def);
acda74
diff --git a/src/security/security_nop.c b/src/security/security_nop.c
acda74
index 0dbc547feb..1413f43d57 100644
acda74
--- a/src/security/security_nop.c
acda74
+++ b/src/security/security_nop.c
acda74
@@ -152,6 +152,7 @@ virSecurityDomainSetProcessLabelNop(virSecurityManager *mgr G_GNUC_UNUSED,
acda74
 static int
acda74
 virSecurityDomainSetChildProcessLabelNop(virSecurityManager *mgr G_GNUC_UNUSED,
acda74
                                          virDomainDef *vm G_GNUC_UNUSED,
acda74
+                                         bool useBinarySpecificLabel G_GNUC_UNUSED,
acda74
                                          virCommand *cmd G_GNUC_UNUSED)
acda74
 {
acda74
     return 0;
acda74
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
acda74
index a0b3a5e147..7ea4ff5c1a 100644
acda74
--- a/src/security/security_selinux.c
acda74
+++ b/src/security/security_selinux.c
acda74
@@ -560,6 +560,52 @@ virSecuritySELinuxContextAddRange(const char *src,
acda74
     return ret;
acda74
 }
acda74
 
acda74
+
acda74
+static char *
acda74
+virSecuritySELinuxContextSetFromFile(const char *origLabel,
acda74
+                                     const char *binaryPath)
acda74
+{
acda74
+    g_autofree char *currentCon = NULL;
acda74
+    g_autofree char *binaryCon = NULL;
acda74
+    g_autofree char *naturalLabel = NULL;
acda74
+    g_autofree char *updatedLabel = NULL;
acda74
+
acda74
+    /* First learn what would be the context set
acda74
+     * if binaryPath was exec'ed from this process.
acda74
+     */
acda74
+    if (getcon(&currentCon) < 0) {
acda74
+        virReportSystemError(errno, "%s",
acda74
+                             _("unable to get SELinux context for current process"));
acda74
+        return NULL;
acda74
+    }
acda74
+
acda74
+    if (getfilecon(binaryPath, &binaryCon) < 0) {
acda74
+        virReportSystemError(errno, _("unable to get SELinux context for '%s'"),
acda74
+                             binaryPath);
acda74
+        return NULL;
acda74
+    }
acda74
+
acda74
+    if (security_compute_create(currentCon, binaryCon,
acda74
+                                string_to_security_class("process"),
acda74
+                                &naturalLabel) < 0) {
acda74
+        virReportSystemError(errno,
acda74
+                             _("unable create new SELinux label based on label '%s' and file '%s'"),
acda74
+                             origLabel, binaryPath);
acda74
+        return NULL;
acda74
+    }
acda74
+
acda74
+    /* now get the type from the original label
acda74
+     * (which already has proper MCS set) and add it to
acda74
+     * the new label
acda74
+     */
acda74
+    updatedLabel = virSecuritySELinuxContextAddRange(origLabel, naturalLabel);
acda74
+
acda74
+    VIR_DEBUG("original label: '%s' binary: '%s' binary-specific label: '%s'",
acda74
+              origLabel, binaryPath, NULLSTR(updatedLabel));
acda74
+    return g_steal_pointer(&updatedLabel);
acda74
+}
acda74
+
acda74
+
acda74
 static char *
acda74
 virSecuritySELinuxGenNewContext(const char *basecontext,
acda74
                                 const char *mcs,
acda74
@@ -2984,10 +3030,13 @@ virSecuritySELinuxSetProcessLabel(virSecurityManager *mgr G_GNUC_UNUSED,
acda74
 static int
acda74
 virSecuritySELinuxSetChildProcessLabel(virSecurityManager *mgr G_GNUC_UNUSED,
acda74
                                        virDomainDef *def,
acda74
+                                       bool useBinarySpecificLabel G_GNUC_UNUSED,
acda74
                                        virCommand *cmd)
acda74
 {
acda74
     /* TODO: verify DOI */
acda74
     virSecurityLabelDef *secdef;
acda74
+    g_autofree char *tmpLabel = NULL;
acda74
+    const char *label = NULL;
acda74
 
acda74
     secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME);
acda74
     if (!secdef || !secdef->label)
acda74
@@ -3004,8 +3053,30 @@ virSecuritySELinuxSetChildProcessLabel(virSecurityManager *mgr G_GNUC_UNUSED,
acda74
             return -1;
acda74
     }
acda74
 
acda74
+    /* pick either the common label used by most binaries exec'ed by
acda74
+     * libvirt, or the specific label of this binary.
acda74
+     */
acda74
+    if (useBinarySpecificLabel) {
acda74
+        const char *binaryPath = virCommandGetBinaryPath(cmd);
acda74
+
acda74
+        if (!binaryPath)
acda74
+            return -1; /* error was already logged */
acda74
+
acda74
+        tmpLabel = virSecuritySELinuxContextSetFromFile(secdef->label,
acda74
+                                                        binaryPath);
acda74
+        if (!tmpLabel)
acda74
+            return -1;
acda74
+
acda74
+        label = tmpLabel;
acda74
+
acda74
+    } else {
acda74
+
acda74
+        label = secdef->label;
acda74
+
acda74
+    }
acda74
+
acda74
     /* save in cmd to be set after fork/before child process is exec'ed */
acda74
-    virCommandSetSELinuxLabel(cmd, secdef->label);
acda74
+    virCommandSetSELinuxLabel(cmd, label);
acda74
     return 0;
acda74
 }
acda74
 
acda74
diff --git a/src/security/security_stack.c b/src/security/security_stack.c
acda74
index 560f797030..369b5dd3a6 100644
acda74
--- a/src/security/security_stack.c
acda74
+++ b/src/security/security_stack.c
acda74
@@ -458,6 +458,7 @@ virSecurityStackSetProcessLabel(virSecurityManager *mgr,
acda74
 static int
acda74
 virSecurityStackSetChildProcessLabel(virSecurityManager *mgr,
acda74
                                      virDomainDef *vm,
acda74
+                                     bool useBinarySpecificLabel,
acda74
                                      virCommand *cmd)
acda74
 {
acda74
     virSecurityStackData *priv = virSecurityManagerGetPrivateData(mgr);
acda74
@@ -465,8 +466,10 @@ virSecurityStackSetChildProcessLabel(virSecurityManager *mgr,
acda74
     int rc = 0;
acda74
 
acda74
     for (; item; item = item->next) {
acda74
-        if (virSecurityManagerSetChildProcessLabel(item->securityManager, vm, cmd) < 0)
acda74
+        if (virSecurityManagerSetChildProcessLabel(item->securityManager, vm,
acda74
+                                                   useBinarySpecificLabel, cmd) < 0) {
acda74
             rc = -1;
acda74
+        }
acda74
     }
acda74
 
acda74
     return rc;
acda74
-- 
acda74
2.40.0
acda74