3a9410
From e3487aab5319df05c5a06a83e4d3e4a87c1e51a9 Mon Sep 17 00:00:00 2001
3a9410
Message-Id: <e3487aab5319df05c5a06a83e4d3e4a87c1e51a9@dist-git>
3a9410
From: Vasiliy Ulyanov <vulyanov@suse.de>
3a9410
Date: Wed, 2 Feb 2022 17:28:16 +0100
3a9410
Subject: [PATCH] qemu: tpm: Get swtpm pid without binary validation
3a9410
3a9410
Access to /proc/[pid]/exe may be restricted in certain environments (e.g.
3a9410
in containers) and any attempt to stat(2) or readlink(2) the file will
3a9410
result in 'permission denied' error if the calling process does not have
3a9410
CAP_SYS_PTRACE capability. According to proc(5) manpage:
3a9410
3a9410
Permission to dereference or read (readlink(2)) this symbolic link is
3a9410
governed by a ptrace access mode PTRACE_MODE_READ_FSCREDS check; see
3a9410
ptrace(2).
3a9410
3a9410
The binary validation in virPidFileReadPathIfAlive may fail with EACCES.
3a9410
Therefore instead do only the check that the pidfile is locked by the
3a9410
correct process. To ensure this is always the case the daemonization and
3a9410
pidfile handling of the swtpm command is now controlled by libvirt.
3a9410
3a9410
Signed-off-by: Vasiliy Ulyanov <vulyanov@suse.de>
3a9410
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
3a9410
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
3a9410
(cherry picked from commit a9c500d2b50c5c041a1bb6ae9724402cf1cec8fe)
3a9410
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2152188
3a9410
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
3a9410
---
3a9410
 src/qemu/qemu_tpm.c | 94 ++++++++++++++++++++++++++-------------------
3a9410
 1 file changed, 54 insertions(+), 40 deletions(-)
3a9410
3a9410
diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c
3a9410
index 7e7b01768e..9c5d1ffed4 100644
3a9410
--- a/src/qemu/qemu_tpm.c
3a9410
+++ b/src/qemu/qemu_tpm.c
3a9410
@@ -44,6 +44,7 @@
3a9410
 #include "qemu_tpm.h"
3a9410
 #include "virtpm.h"
3a9410
 #include "virsecret.h"
3a9410
+#include "virtime.h"
3a9410
 
3a9410
 #define VIR_FROM_THIS VIR_FROM_NONE
3a9410
 
3a9410
@@ -258,13 +259,13 @@ qemuTPMEmulatorGetPid(const char *swtpmStateDir,
3a9410
                       const char *shortName,
3a9410
                       pid_t *pid)
3a9410
 {
3a9410
-    g_autofree char *swtpm = virTPMGetSwtpm();
3a9410
     g_autofree char *pidfile = qemuTPMEmulatorCreatePidFilename(swtpmStateDir,
3a9410
                                                                 shortName);
3a9410
+
3a9410
     if (!pidfile)
3a9410
         return -1;
3a9410
 
3a9410
-    if (virPidFileReadPathIfAlive(pidfile, pid, swtpm) < 0)
3a9410
+    if (virPidFileReadPathIfLocked(pidfile, pid) < 0)
3a9410
         return -1;
3a9410
 
3a9410
     return 0;
3a9410
@@ -660,9 +661,6 @@ qemuTPMEmulatorReconfigure(const char *storagepath,
3a9410
  * @privileged: whether we are running in privileged mode
3a9410
  * @swtpm_user: The uid for the swtpm to run as (drop privileges to from root)
3a9410
  * @swtpm_group: The gid for the swtpm to run as
3a9410
- * @swtpmStateDir: the directory where swtpm writes the pid file and creates the
3a9410
- *                 Unix socket
3a9410
- * @shortName: the short name of the VM
3a9410
  * @incomingMigration: whether we have an incoming migration
3a9410
  *
3a9410
  * Create the virCommand use for starting the emulator
3a9410
@@ -676,13 +674,10 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
3a9410
                             bool privileged,
3a9410
                             uid_t swtpm_user,
3a9410
                             gid_t swtpm_group,
3a9410
-                            const char *swtpmStateDir,
3a9410
-                            const char *shortName,
3a9410
                             bool incomingMigration)
3a9410
 {
3a9410
     g_autoptr(virCommand) cmd = NULL;
3a9410
     bool created = false;
3a9410
-    g_autofree char *pidfile = NULL;
3a9410
     g_autofree char *swtpm = virTPMGetSwtpm();
3a9410
     int pwdfile_fd = -1;
3a9410
     int migpwdfile_fd = -1;
3a9410
@@ -721,7 +716,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
3a9410
 
3a9410
     virCommandClearCaps(cmd);
3a9410
 
3a9410
-    virCommandAddArgList(cmd, "socket", "--daemon", "--ctrl", NULL);
3a9410
+    virCommandAddArgList(cmd, "socket", "--ctrl", NULL);
3a9410
     virCommandAddArgFormat(cmd, "type=unixio,path=%s,mode=0600",
3a9410
                            tpm->data.emulator.source->data.nix.path);
3a9410
 
3a9410
@@ -748,12 +743,6 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
3a9410
         break;
3a9410
     }
3a9410
 
3a9410
-    if (!(pidfile = qemuTPMEmulatorCreatePidFilename(swtpmStateDir, shortName)))
3a9410
-        goto error;
3a9410
-
3a9410
-    virCommandAddArg(cmd, "--pid");
3a9410
-    virCommandAddArgFormat(cmd, "file=%s", pidfile);
3a9410
-
3a9410
     if (tpm->data.emulator.hassecretuuid) {
3a9410
         if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD)) {
3a9410
             virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
3a9410
@@ -904,12 +893,14 @@ qemuExtTPMStartEmulator(virQEMUDriver *driver,
3a9410
                         bool incomingMigration)
3a9410
 {
3a9410
     g_autoptr(virCommand) cmd = NULL;
3a9410
-    int exitstatus = 0;
3a9410
-    g_autofree char *errbuf = NULL;
3a9410
+    VIR_AUTOCLOSE errfd = -1;
3a9410
     g_autoptr(virQEMUDriverConfig) cfg = NULL;
3a9410
     g_autofree char *shortName = virDomainDefGetShortName(vm->def);
3a9410
-    int cmdret = 0, timeout, rc;
3a9410
-    pid_t pid;
3a9410
+    g_autofree char *pidfile = NULL;
3a9410
+    virTimeBackOffVar timebackoff;
3a9410
+    const unsigned long long timeout = 1000; /* ms */
3a9410
+    int cmdret = 0;
3a9410
+    pid_t pid = -1;
3a9410
 
3a9410
     if (!shortName)
3a9410
         return -1;
3a9410
@@ -923,48 +914,71 @@ qemuExtTPMStartEmulator(virQEMUDriver *driver,
3a9410
                                             driver->privileged,
3a9410
                                             cfg->swtpm_user,
3a9410
                                             cfg->swtpm_group,
3a9410
-                                            cfg->swtpmStateDir, shortName,
3a9410
                                             incomingMigration)))
3a9410
         return -1;
3a9410
 
3a9410
     if (qemuExtDeviceLogCommand(driver, vm, cmd, "TPM Emulator") < 0)
3a9410
         return -1;
3a9410
 
3a9410
-    virCommandSetErrorBuffer(cmd, &errbuf);
3a9410
+    if (!(pidfile = qemuTPMEmulatorCreatePidFilename(cfg->swtpmStateDir, shortName)))
3a9410
+        return -1;
3a9410
+
3a9410
+    virCommandDaemonize(cmd);
3a9410
+    virCommandSetPidFile(cmd, pidfile);
3a9410
+    virCommandSetErrorFD(cmd, &errfd);
3a9410
 
3a9410
     if (qemuSecurityStartTPMEmulator(driver, vm, cmd,
3a9410
                                      cfg->swtpm_user, cfg->swtpm_group,
3a9410
-                                     &exitstatus, &cmdret) < 0)
3a9410
+                                     NULL, &cmdret) < 0)
3a9410
         return -1;
3a9410
 
3a9410
-    if (cmdret < 0 || exitstatus != 0) {
3a9410
-        virReportError(VIR_ERR_INTERNAL_ERROR,
3a9410
-                       _("Could not start 'swtpm'. exitstatus: %d, "
3a9410
-                         "error: %s"), exitstatus, errbuf);
3a9410
-        return -1;
3a9410
+    if (cmdret < 0) {
3a9410
+        /* virCommandRun() hidden in qemuSecurityStartTPMEmulator()
3a9410
+         * already reported error. */
3a9410
+        goto error;
3a9410
     }
3a9410
 
3a9410
-    /* check that the swtpm has written its pid into the file */
3a9410
-    timeout = 1000; /* ms */
3a9410
-    while (timeout > 0) {
3a9410
-        rc = qemuTPMEmulatorGetPid(cfg->swtpmStateDir, shortName, &pid;;
3a9410
-        if (rc < 0) {
3a9410
-            timeout -= 50;
3a9410
-            g_usleep(50 * 1000);
3a9410
+    if (virPidFileReadPath(pidfile, &pid) < 0) {
3a9410
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
3a9410
+                       _("swtpm didn't show up"));
3a9410
+        goto error;
3a9410
+    }
3a9410
+
3a9410
+    if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0)
3a9410
+        goto error;
3a9410
+    while (virTimeBackOffWait(&timebackoff)) {
3a9410
+        char errbuf[1024] = { 0 };
3a9410
+
3a9410
+        if (virFileExists(tpm->data.emulator.source->data.nix.path))
3a9410
+            break;
3a9410
+
3a9410
+        if (virProcessKill(pid, 0) == 0)
3a9410
             continue;
3a9410
+
3a9410
+        if (saferead(errfd, errbuf, sizeof(errbuf) - 1) < 0) {
3a9410
+            virReportSystemError(errno, "%s",
3a9410
+                                 _("swtpm died unexpectedly"));
3a9410
+        } else {
3a9410
+            virReportError(VIR_ERR_OPERATION_FAILED,
3a9410
+                           _("swtpm died and reported: %s"), errbuf);
3a9410
         }
3a9410
-        if (rc == 0 && pid == (pid_t)-1)
3a9410
-            goto error;
3a9410
-        break;
3a9410
+        goto error;
3a9410
     }
3a9410
-    if (timeout <= 0)
3a9410
+
3a9410
+    if (!virFileExists(tpm->data.emulator.source->data.nix.path)) {
3a9410
+        virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
3a9410
+                       _("swtpm socket did not show up"));
3a9410
         goto error;
3a9410
+    }
3a9410
 
3a9410
     return 0;
3a9410
 
3a9410
  error:
3a9410
-    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
3a9410
-                   _("swtpm failed to start"));
3a9410
+    virCommandAbort(cmd);
3a9410
+    if (pid >= 0)
3a9410
+        virProcessKillPainfully(pid, true);
3a9410
+    if (pidfile)
3a9410
+        unlink(pidfile);
3a9410
     return -1;
3a9410
 }
3a9410
 
3a9410
-- 
3a9410
2.39.0
3a9410