2017-04-04 16:22:31 +00:00
|
|
|
/*
|
|
|
|
* qemu_tpm.c: QEMU TPM support
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018 IBM Corporation
|
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2019-07-25 18:22:04 +00:00
|
|
|
#include <sys/stat.h>
|
2017-04-04 16:22:31 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include "qemu_extdevice.h"
|
2018-04-04 16:40:32 +00:00
|
|
|
#include "qemu_security.h"
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
#include "conf/domain_conf.h"
|
|
|
|
#include "vircommand.h"
|
2021-05-07 15:53:40 +00:00
|
|
|
#include "viridentity.h"
|
2017-04-04 16:22:31 +00:00
|
|
|
#include "virlog.h"
|
|
|
|
#include "viruuid.h"
|
|
|
|
#include "virfile.h"
|
2018-04-05 19:06:55 +00:00
|
|
|
#include "virpidfile.h"
|
2017-04-04 16:22:31 +00:00
|
|
|
#include "configmake.h"
|
|
|
|
#include "qemu_tpm.h"
|
2019-07-25 18:22:02 +00:00
|
|
|
#include "virtpm.h"
|
2019-10-24 16:00:55 +00:00
|
|
|
#include "virsecret.h"
|
2022-02-02 16:28:16 +00:00
|
|
|
#include "virtime.h"
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
2019-01-20 16:32:42 +00:00
|
|
|
VIR_LOG_INIT("qemu.tpm");
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2022-01-27 16:17:36 +00:00
|
|
|
/**
|
|
|
|
* qemuTPMEmulatorStorageBuildPath:
|
2017-04-04 16:22:31 +00:00
|
|
|
* @swtpmStorageDir: directory for swtpm persistent state
|
2022-01-27 16:17:36 +00:00
|
|
|
* @uuidstr: UUID of the VM
|
2018-04-04 18:40:17 +00:00
|
|
|
* @tpmversion: version of the TPM
|
2017-04-04 16:22:31 +00:00
|
|
|
*
|
2022-01-27 16:17:36 +00:00
|
|
|
* Generate the swtpm's storage path.
|
2017-04-04 16:22:31 +00:00
|
|
|
*/
|
|
|
|
static char *
|
2022-01-27 16:17:36 +00:00
|
|
|
qemuTPMEmulatorStorageBuildPath(const char *swtpmStorageDir,
|
|
|
|
const char *uuidstr,
|
|
|
|
virDomainTPMVersion tpmversion)
|
2017-04-04 16:22:31 +00:00
|
|
|
{
|
|
|
|
char *path = NULL;
|
2018-04-04 18:40:17 +00:00
|
|
|
const char *dir = "";
|
|
|
|
|
|
|
|
switch (tpmversion) {
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_1_2:
|
|
|
|
dir = "tpm1.2";
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_2_0:
|
|
|
|
dir = "tpm2";
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_DEFAULT:
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_LAST:
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
path = g_strdup_printf("%s/%s/%s", swtpmStorageDir, uuidstr, dir);
|
2017-04-04 16:22:31 +00:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-25 10:41:08 +00:00
|
|
|
/**
|
2022-01-27 16:17:36 +00:00
|
|
|
* qemuTPMEmulatorLogBuildPath:
|
|
|
|
* @logDir: directory for swtpm log files
|
2021-02-25 10:41:08 +00:00
|
|
|
* @vmname: name of the VM
|
|
|
|
*
|
2022-01-27 16:17:36 +00:00
|
|
|
* Generate the swtpm's log path.
|
2021-02-25 10:41:08 +00:00
|
|
|
*/
|
|
|
|
static char*
|
2022-01-27 16:17:36 +00:00
|
|
|
qemuTPMEmulatorLogBuildPath(const char *logDir,
|
|
|
|
const char *vmname)
|
2021-02-25 10:41:08 +00:00
|
|
|
{
|
|
|
|
return g_strdup_printf("%s/%s-swtpm.log", logDir, vmname);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-04 18:37:51 +00:00
|
|
|
/**
|
|
|
|
* qemuTPMEmulatorSocketBuildPath:
|
|
|
|
* @swtpmStateDir: directory for swtpm runtime state
|
|
|
|
* @shortName: short and unique name of the domain
|
|
|
|
*
|
|
|
|
* Generate the swtpm's Unix socket path.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
qemuTPMEmulatorSocketBuildPath(const char *swtpmStateDir,
|
|
|
|
const char *shortName)
|
|
|
|
{
|
|
|
|
return g_strdup_printf("%s/%s-swtpm.sock", swtpmStateDir, shortName);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuTPMEmulatorPidFileBuildPath:
|
|
|
|
* @swtpmStateDir: directory for swtpm runtime state
|
|
|
|
* @shortName: short and unique name of the domain
|
|
|
|
*
|
|
|
|
* Generate the swtpm's pidfile path.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
qemuTPMEmulatorPidFileBuildPath(const char *swtpmStateDir,
|
|
|
|
const char *shortName)
|
|
|
|
{
|
|
|
|
g_autofree char *devicename = NULL;
|
|
|
|
|
|
|
|
devicename = g_strdup_printf("%s-swtpm", shortName);
|
|
|
|
|
|
|
|
return virPidFileBuildPath(swtpmStateDir, devicename);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qemuTPMEmulatorGetPid
|
|
|
|
*
|
|
|
|
* @swtpmStateDir: the directory where swtpm writes the pidfile into
|
|
|
|
* @shortName: short name of the domain
|
|
|
|
* @pid: pointer to pid
|
|
|
|
*
|
|
|
|
* Return -1 upon error, or zero on successful reading of the pidfile.
|
|
|
|
* If the PID was not still alive, zero will be returned, and @pid will be
|
|
|
|
* set to -1;
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuTPMEmulatorGetPid(const char *swtpmStateDir,
|
|
|
|
const char *shortName,
|
|
|
|
pid_t *pid)
|
|
|
|
{
|
|
|
|
g_autofree char *pidfile = qemuTPMEmulatorPidFileBuildPath(swtpmStateDir,
|
|
|
|
shortName);
|
|
|
|
if (!pidfile)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virPidFileReadPathIfLocked(pidfile, pid) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-04 16:20:38 +00:00
|
|
|
/**
|
|
|
|
* qemuTPMEmulatorCreateStorage:
|
2022-02-04 16:26:23 +00:00
|
|
|
* @tpm: TPM definition for an emulator type
|
2017-04-04 16:22:31 +00:00
|
|
|
* @created: a pointer to a bool that will be set to true if the
|
|
|
|
* storage was created because it did not exist yet
|
|
|
|
* @swtpm_user: The uid that needs to be able to access the directory
|
|
|
|
* @swtpm_group: The gid that needs to be able to access the directory
|
|
|
|
*
|
|
|
|
* Unless the storage path for the swtpm for the given VM
|
|
|
|
* already exists, create it and make it accessible for the given userid.
|
|
|
|
* Adapt ownership of the directory and all swtpm's state files there.
|
|
|
|
*/
|
|
|
|
static int
|
2022-02-04 16:26:23 +00:00
|
|
|
qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm,
|
2017-04-04 16:22:31 +00:00
|
|
|
bool *created,
|
|
|
|
uid_t swtpm_user,
|
|
|
|
gid_t swtpm_group)
|
|
|
|
{
|
2022-02-04 16:26:23 +00:00
|
|
|
const char *storagepath = tpm->data.emulator.storagepath;
|
2020-01-31 23:01:13 +00:00
|
|
|
g_autofree char *swtpmStorageDir = g_path_get_dirname(storagepath);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2022-02-04 15:31:01 +00:00
|
|
|
/* allow others to cd into this dir */
|
|
|
|
if (g_mkdir_with_parents(swtpmStorageDir, 0711) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Could not create TPM directory %s"),
|
|
|
|
swtpmStorageDir);
|
2020-01-31 23:16:06 +00:00
|
|
|
return -1;
|
2022-02-04 15:31:01 +00:00
|
|
|
}
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
*created = false;
|
|
|
|
|
|
|
|
if (!virFileExists(storagepath))
|
|
|
|
*created = true;
|
|
|
|
|
|
|
|
if (virDirCreate(storagepath, 0700, swtpm_user, swtpm_group,
|
|
|
|
VIR_DIR_CREATE_ALLOW_EXIST) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Could not create directory %s as %u:%d"),
|
|
|
|
storagepath, swtpm_user, swtpm_group);
|
2020-01-31 23:16:06 +00:00
|
|
|
return -1;
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virFileChownFiles(storagepath, swtpm_user, swtpm_group) < 0)
|
2020-01-31 23:16:06 +00:00
|
|
|
return -1;
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2020-01-31 23:16:06 +00:00
|
|
|
return 0;
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-04 16:24:06 +00:00
|
|
|
/**
|
|
|
|
* qemuTPMEmulatorDeleteStorage:
|
|
|
|
* @tpm: TPM definition
|
|
|
|
*
|
|
|
|
* Delete all persistent storage associated with the swtpm.
|
|
|
|
*/
|
2017-04-04 16:22:31 +00:00
|
|
|
static void
|
2022-02-04 16:20:38 +00:00
|
|
|
qemuTPMEmulatorDeleteStorage(virDomainTPMDef *tpm)
|
2017-04-04 16:22:31 +00:00
|
|
|
{
|
2019-12-20 16:48:02 +00:00
|
|
|
g_autofree char *path = g_path_get_dirname(tpm->data.emulator.storagepath);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2019-12-20 16:48:02 +00:00
|
|
|
ignore_value(virFileDeleteTree(path));
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-25 18:22:11 +00:00
|
|
|
/*
|
|
|
|
* qemuTPMSetupEncryption
|
|
|
|
*
|
|
|
|
* @secretuuid: The UUID with the secret holding passphrase
|
|
|
|
* @cmd: the virCommand to transfer the secret to
|
|
|
|
*
|
|
|
|
* Returns file descriptor representing the read-end of a pipe.
|
|
|
|
* The passphrase can be read from this pipe. Returns < 0 in case
|
|
|
|
* of error.
|
|
|
|
*
|
|
|
|
* This function reads the passphrase and writes it into the
|
|
|
|
* write-end of a pipe so that the read-end of the pipe can be
|
|
|
|
* passed to the emulator for reading the passphrase from.
|
2021-03-01 10:04:54 +00:00
|
|
|
*
|
|
|
|
* Note that the returned FD is owned by @cmd.
|
2019-07-25 18:22:11 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuTPMSetupEncryption(const unsigned char *secretuuid,
|
2021-03-11 07:16:13 +00:00
|
|
|
virCommand *cmd)
|
2019-07-25 18:22:11 +00:00
|
|
|
{
|
2021-03-01 10:04:54 +00:00
|
|
|
g_autoptr(virConnect) conn = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree uint8_t *secret = NULL;
|
2019-07-25 18:22:11 +00:00
|
|
|
size_t secret_len;
|
|
|
|
virSecretLookupTypeDef seclookupdef = {
|
|
|
|
.type = VIR_SECRET_LOOKUP_TYPE_UUID,
|
|
|
|
};
|
2021-05-07 15:53:40 +00:00
|
|
|
VIR_IDENTITY_AUTORESTORE virIdentity *oldident = virIdentityElevateCurrent();
|
|
|
|
|
|
|
|
if (!oldident)
|
|
|
|
return -1;
|
2019-07-25 18:22:11 +00:00
|
|
|
|
|
|
|
conn = virGetConnectSecret();
|
|
|
|
if (!conn)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memcpy(seclookupdef.u.uuid, secretuuid, sizeof(seclookupdef.u.uuid));
|
|
|
|
if (virSecretGetSecretString(conn, &seclookupdef,
|
|
|
|
VIR_SECRET_USAGE_TYPE_VTPM,
|
|
|
|
&secret, &secret_len) < 0)
|
2021-03-01 10:04:54 +00:00
|
|
|
return -1;
|
2019-07-25 18:22:11 +00:00
|
|
|
|
2021-03-01 10:04:54 +00:00
|
|
|
return virCommandSetSendBuffer(cmd, g_steal_pointer(&secret), secret_len);
|
2019-07-25 18:22:11 +00:00
|
|
|
}
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2021-10-19 13:43:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* qemuTPMCreateConfigFiles: run swtpm_setup --create-config-files skip-if-exist
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuTPMCreateConfigFiles(const char *swtpm_setup)
|
|
|
|
{
|
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
|
|
|
g_autofree char *errbuf = NULL;
|
|
|
|
int exitstatus;
|
|
|
|
|
|
|
|
if (!virTPMSwtpmSetupCapsGet(
|
|
|
|
VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_CREATE_CONFIG_FILES))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cmd = virCommandNew(swtpm_setup);
|
|
|
|
|
|
|
|
virCommandAddArgList(cmd, "--create-config-files", "skip-if-exist", NULL);
|
|
|
|
virCommandClearCaps(cmd);
|
|
|
|
virCommandSetErrorBuffer(cmd, &errbuf);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, &exitstatus) < 0)
|
|
|
|
return -1;
|
|
|
|
if (exitstatus != 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Could not run '%s' to create config files. "
|
|
|
|
"exitstatus: %d;\nError: %s"),
|
|
|
|
swtpm_setup, exitstatus, errbuf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-01 17:23:39 +00:00
|
|
|
/*
|
|
|
|
* Add encryption parameters to swtpm_setup command line.
|
|
|
|
*
|
|
|
|
* @cmd: virCommand to add options to
|
|
|
|
* @swtpm_setup: swtpm_setup tool path
|
|
|
|
* @secretuuid: The secret's uuid; may be NULL
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuTPMVirCommandAddEncryption(virCommand *cmd,
|
|
|
|
const char *swtpm_setup,
|
|
|
|
const unsigned char *secretuuid)
|
|
|
|
{
|
|
|
|
int pwdfile_fd;
|
|
|
|
|
|
|
|
if (!secretuuid)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_PWDFILE_FD)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
|
|
|
_("%s does not support passing a passphrase using a file "
|
|
|
|
"descriptor"), swtpm_setup);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((pwdfile_fd = qemuTPMSetupEncryption(secretuuid, cmd)) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
virCommandAddArg(cmd, "--pwdfile-fd");
|
|
|
|
virCommandAddArgFormat(cmd, "%d", pwdfile_fd);
|
|
|
|
virCommandAddArgList(cmd, "--cipher", "aes-256-cbc", NULL);
|
|
|
|
virCommandPassFD(cmd, pwdfile_fd, VIR_COMMAND_PASS_FD_CLOSE_PARENT);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-04 16:22:31 +00:00
|
|
|
/*
|
|
|
|
* qemuTPMEmulatorRunSetup
|
|
|
|
*
|
|
|
|
* @storagepath: path to the directory for TPM state
|
|
|
|
* @vmname: the name of the VM
|
|
|
|
* @vmuuid: the UUID of the VM
|
|
|
|
* @privileged: whether we are running in privileged mode
|
|
|
|
* @swtpm_user: The userid to switch to when setting up the TPM;
|
|
|
|
* typically this should be the uid of 'tss' or 'root'
|
|
|
|
* @swtpm_group: The group id to switch to
|
|
|
|
* @logfile: The file to write the log into; it must be writable
|
|
|
|
* for the user given by userid or 'tss'
|
2018-04-04 18:40:17 +00:00
|
|
|
* @tpmversion: The version of the TPM, either a TPM 1.2 or TPM 2
|
2019-07-25 18:22:11 +00:00
|
|
|
* @encryption: pointer to virStorageEncryption holding secret
|
2019-07-26 20:41:10 +00:00
|
|
|
* @incomingMigration: whether we have an incoming migration
|
2017-04-04 16:22:31 +00:00
|
|
|
*
|
|
|
|
* Setup the external swtpm by creating endorsement key and
|
|
|
|
* certificates for it.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuTPMEmulatorRunSetup(const char *storagepath,
|
|
|
|
const char *vmname,
|
|
|
|
const unsigned char *vmuuid,
|
|
|
|
bool privileged,
|
|
|
|
uid_t swtpm_user,
|
|
|
|
gid_t swtpm_group,
|
2018-04-04 18:40:17 +00:00
|
|
|
const char *logfile,
|
2019-07-25 18:22:11 +00:00
|
|
|
const virDomainTPMVersion tpmversion,
|
2019-07-26 20:41:10 +00:00
|
|
|
const unsigned char *secretuuid,
|
|
|
|
bool incomingMigration)
|
2017-04-04 16:22:31 +00:00
|
|
|
{
|
2020-01-31 23:11:22 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2017-04-04 16:22:31 +00:00
|
|
|
int exitstatus;
|
|
|
|
char uuid[VIR_UUID_STRING_BUFLEN];
|
2020-01-31 23:01:13 +00:00
|
|
|
g_autofree char *vmid = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *swtpm_setup = virTPMGetSwtpmSetup();
|
2019-07-25 18:22:02 +00:00
|
|
|
|
|
|
|
if (!swtpm_setup)
|
|
|
|
return -1;
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2021-10-30 04:01:37 +00:00
|
|
|
if (!privileged && tpmversion == VIR_DOMAIN_TPM_VERSION_1_2 &&
|
|
|
|
!virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_TPM12_NOT_NEED_ROOT)) {
|
2017-04-04 16:22:31 +00:00
|
|
|
return virFileWriteStr(logfile,
|
|
|
|
_("Did not create EK and certificates since "
|
2018-05-23 20:53:09 +00:00
|
|
|
"this requires privileged mode for a "
|
|
|
|
"TPM 1.2\n"), 0600);
|
2021-10-30 04:01:37 +00:00
|
|
|
}
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2021-10-19 13:43:20 +00:00
|
|
|
if (!privileged && qemuTPMCreateConfigFiles(swtpm_setup) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2017-04-04 16:22:31 +00:00
|
|
|
cmd = virCommandNew(swtpm_setup);
|
|
|
|
|
|
|
|
virUUIDFormat(vmuuid, uuid);
|
2019-10-22 13:26:14 +00:00
|
|
|
vmid = g_strdup_printf("%s:%s", vmname, uuid);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
virCommandSetUID(cmd, swtpm_user);
|
|
|
|
virCommandSetGID(cmd, swtpm_group);
|
|
|
|
|
2018-04-04 18:40:17 +00:00
|
|
|
switch (tpmversion) {
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_1_2:
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_2_0:
|
|
|
|
virCommandAddArgList(cmd, "--tpm2", NULL);
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_DEFAULT:
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-11-01 17:23:39 +00:00
|
|
|
if (qemuTPMVirCommandAddEncryption(cmd, swtpm_setup, secretuuid) < 0)
|
|
|
|
return -1;
|
2018-04-04 18:40:17 +00:00
|
|
|
|
2019-07-26 20:41:10 +00:00
|
|
|
if (!incomingMigration) {
|
|
|
|
virCommandAddArgList(cmd,
|
|
|
|
"--tpm-state", storagepath,
|
|
|
|
"--vmid", vmid,
|
|
|
|
"--logfile", logfile,
|
|
|
|
"--createek",
|
|
|
|
"--create-ek-cert",
|
|
|
|
"--create-platform-cert",
|
|
|
|
"--lock-nvram",
|
|
|
|
"--not-overwrite",
|
|
|
|
NULL);
|
|
|
|
} else {
|
|
|
|
virCommandAddArgList(cmd,
|
|
|
|
"--tpm-state", storagepath,
|
|
|
|
"--overwrite",
|
|
|
|
NULL);
|
|
|
|
}
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
virCommandClearCaps(cmd);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Could not run '%s'. exitstatus: %d; "
|
|
|
|
"Check error log '%s' for details."),
|
|
|
|
swtpm_setup, exitstatus, logfile);
|
2020-01-31 23:16:06 +00:00
|
|
|
return -1;
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
2020-01-31 23:16:06 +00:00
|
|
|
return 0;
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-03 17:04:23 +00:00
|
|
|
static char *
|
2022-08-01 11:52:47 +00:00
|
|
|
qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks)
|
2021-11-03 17:04:23 +00:00
|
|
|
{
|
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2022-08-01 11:52:47 +00:00
|
|
|
ssize_t bank = -1;
|
|
|
|
|
2022-08-11 18:57:02 +00:00
|
|
|
if (!activePcrBanks)
|
|
|
|
return NULL;
|
|
|
|
|
2022-08-01 11:52:47 +00:00
|
|
|
while ((bank = virBitmapNextSetBit(activePcrBanks, bank)) > -1)
|
|
|
|
virBufferAsprintf(&buf, "%s,", virDomainTPMPcrBankTypeToString(bank));
|
|
|
|
|
|
|
|
virBufferTrim(&buf, ",");
|
|
|
|
|
2021-11-03 17:04:23 +00:00
|
|
|
return virBufferContentAndReset(&buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qemuTPMEmulatorReconfigure
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @storagepath: path to the directory for TPM state
|
|
|
|
* @swtpm_user: The userid to switch to when setting up the TPM;
|
|
|
|
* typically this should be the uid of 'tss' or 'root'
|
|
|
|
* @swtpm_group: The group id to switch to
|
|
|
|
* @activePcrBanks: The string describing the active PCR banks
|
|
|
|
* @logfile: The file to write the log into; it must be writable
|
|
|
|
* for the user given by userid or 'tss'
|
|
|
|
* @tpmversion: The version of the TPM, either a TPM 1.2 or TPM 2
|
|
|
|
* @secretuuid: The secret's UUID needed for state encryption
|
|
|
|
*
|
|
|
|
* Reconfigure the active PCR banks of a TPM 2.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuTPMEmulatorReconfigure(const char *storagepath,
|
|
|
|
uid_t swtpm_user,
|
|
|
|
gid_t swtpm_group,
|
2022-08-01 11:52:47 +00:00
|
|
|
virBitmap *activePcrBanks,
|
2021-11-03 17:04:23 +00:00
|
|
|
const char *logfile,
|
|
|
|
const virDomainTPMVersion tpmversion,
|
|
|
|
const unsigned char *secretuuid)
|
|
|
|
{
|
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
|
|
|
int exitstatus;
|
2021-11-05 08:48:52 +00:00
|
|
|
g_autofree char *activePcrBanksStr = NULL;
|
2021-11-03 17:04:23 +00:00
|
|
|
g_autofree char *swtpm_setup = virTPMGetSwtpmSetup();
|
|
|
|
|
|
|
|
if (!swtpm_setup)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (tpmversion != VIR_DOMAIN_TPM_VERSION_2_0 ||
|
|
|
|
(activePcrBanksStr = qemuTPMPcrBankBitmapToStr(activePcrBanks)) == NULL ||
|
|
|
|
!virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_RECONFIGURE_PCR_BANKS))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cmd = virCommandNew(swtpm_setup);
|
|
|
|
|
|
|
|
virCommandSetUID(cmd, swtpm_user);
|
|
|
|
virCommandSetGID(cmd, swtpm_group);
|
|
|
|
|
|
|
|
virCommandAddArgList(cmd, "--tpm2", NULL);
|
|
|
|
|
|
|
|
if (qemuTPMVirCommandAddEncryption(cmd, swtpm_setup, secretuuid) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
virCommandAddArgList(cmd,
|
|
|
|
"--tpm-state", storagepath,
|
|
|
|
"--logfile", logfile,
|
|
|
|
"--pcr-banks", activePcrBanksStr,
|
|
|
|
"--reconfigure",
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
virCommandClearCaps(cmd);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Could not run '%s --reconfigure'. exitstatus: %d; "
|
|
|
|
"Check error log '%s' for details."),
|
|
|
|
swtpm_setup, exitstatus, logfile);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-04 16:22:31 +00:00
|
|
|
/*
|
|
|
|
* qemuTPMEmulatorBuildCommand:
|
|
|
|
*
|
|
|
|
* @tpm: TPM definition
|
|
|
|
* @vmname: The name of the VM
|
|
|
|
* @vmuuid: The UUID of the VM
|
|
|
|
* @privileged: whether we are running in privileged mode
|
|
|
|
* @swtpm_user: The uid for the swtpm to run as (drop privileges to from root)
|
|
|
|
* @swtpm_group: The gid for the swtpm to run as
|
2019-07-26 20:41:10 +00:00
|
|
|
* @incomingMigration: whether we have an incoming migration
|
2017-04-04 16:22:31 +00:00
|
|
|
*
|
|
|
|
* Create the virCommand use for starting the emulator
|
|
|
|
* Do some initializations on the way, such as creation of storage
|
|
|
|
* and emulator setup.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
static virCommand *
|
|
|
|
qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
|
2017-04-04 16:22:31 +00:00
|
|
|
const char *vmname,
|
|
|
|
const unsigned char *vmuuid,
|
|
|
|
bool privileged,
|
|
|
|
uid_t swtpm_user,
|
2018-04-05 19:06:55 +00:00
|
|
|
gid_t swtpm_group,
|
2019-07-26 20:41:10 +00:00
|
|
|
bool incomingMigration)
|
2017-04-04 16:22:31 +00:00
|
|
|
{
|
2020-01-31 23:11:22 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2017-04-04 16:22:31 +00:00
|
|
|
bool created = false;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *swtpm = virTPMGetSwtpm();
|
2021-03-01 10:04:54 +00:00
|
|
|
int pwdfile_fd = -1;
|
|
|
|
int migpwdfile_fd = -1;
|
2019-07-25 18:22:11 +00:00
|
|
|
const unsigned char *secretuuid = NULL;
|
2019-07-25 18:22:02 +00:00
|
|
|
|
|
|
|
if (!swtpm)
|
|
|
|
return NULL;
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2022-02-04 16:26:23 +00:00
|
|
|
if (qemuTPMEmulatorCreateStorage(tpm, &created, swtpm_user, swtpm_group) < 0)
|
2017-04-04 16:22:31 +00:00
|
|
|
return NULL;
|
|
|
|
|
2019-07-25 18:22:11 +00:00
|
|
|
if (tpm->data.emulator.hassecretuuid)
|
|
|
|
secretuuid = tpm->data.emulator.secretuuid;
|
|
|
|
|
2017-04-04 16:22:31 +00:00
|
|
|
if (created &&
|
|
|
|
qemuTPMEmulatorRunSetup(tpm->data.emulator.storagepath, vmname, vmuuid,
|
|
|
|
privileged, swtpm_user, swtpm_group,
|
2022-07-15 16:04:21 +00:00
|
|
|
tpm->data.emulator.logfile,
|
|
|
|
tpm->data.emulator.version,
|
2019-07-26 20:41:10 +00:00
|
|
|
secretuuid, incomingMigration) < 0)
|
2017-04-04 16:22:31 +00:00
|
|
|
goto error;
|
|
|
|
|
2021-11-03 17:04:23 +00:00
|
|
|
if (!incomingMigration &&
|
|
|
|
qemuTPMEmulatorReconfigure(tpm->data.emulator.storagepath,
|
|
|
|
swtpm_user, swtpm_group,
|
|
|
|
tpm->data.emulator.activePcrBanks,
|
2022-07-15 16:04:21 +00:00
|
|
|
tpm->data.emulator.logfile,
|
|
|
|
tpm->data.emulator.version,
|
2021-11-03 17:04:23 +00:00
|
|
|
secretuuid) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2021-11-05 15:51:22 +00:00
|
|
|
unlink(tpm->data.emulator.source->data.nix.path);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2019-07-25 18:22:02 +00:00
|
|
|
cmd = virCommandNew(swtpm);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
virCommandClearCaps(cmd);
|
|
|
|
|
2022-02-02 16:28:16 +00:00
|
|
|
virCommandAddArgList(cmd, "socket", "--ctrl", NULL);
|
2017-04-04 16:22:31 +00:00
|
|
|
virCommandAddArgFormat(cmd, "type=unixio,path=%s,mode=0600",
|
2021-11-05 15:51:22 +00:00
|
|
|
tpm->data.emulator.source->data.nix.path);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
virCommandAddArg(cmd, "--tpmstate");
|
|
|
|
virCommandAddArgFormat(cmd, "dir=%s,mode=0600",
|
|
|
|
tpm->data.emulator.storagepath);
|
|
|
|
|
|
|
|
virCommandAddArg(cmd, "--log");
|
|
|
|
virCommandAddArgFormat(cmd, "file=%s", tpm->data.emulator.logfile);
|
|
|
|
|
2021-09-13 06:16:18 +00:00
|
|
|
virCommandAddArg(cmd, "--terminate");
|
|
|
|
|
2017-04-04 16:22:31 +00:00
|
|
|
virCommandSetUID(cmd, swtpm_user);
|
|
|
|
virCommandSetGID(cmd, swtpm_group);
|
|
|
|
|
2022-07-15 16:04:21 +00:00
|
|
|
switch (tpm->data.emulator.version) {
|
2018-04-04 18:40:17 +00:00
|
|
|
case VIR_DOMAIN_TPM_VERSION_1_2:
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_2_0:
|
|
|
|
virCommandAddArg(cmd, "--tpm2");
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_DEFAULT:
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-07-25 18:22:11 +00:00
|
|
|
if (tpm->data.emulator.hassecretuuid) {
|
|
|
|
if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
|
|
|
_("%s does not support passing passphrase via file descriptor"),
|
2019-07-26 20:56:30 +00:00
|
|
|
swtpm);
|
2019-07-25 18:22:11 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
pwdfile_fd = qemuTPMSetupEncryption(tpm->data.emulator.secretuuid, cmd);
|
2021-03-01 10:04:54 +00:00
|
|
|
migpwdfile_fd = qemuTPMSetupEncryption(tpm->data.emulator.secretuuid, cmd);
|
2019-07-25 18:22:11 +00:00
|
|
|
|
|
|
|
virCommandAddArg(cmd, "--key");
|
2021-03-01 10:04:54 +00:00
|
|
|
virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", pwdfile_fd);
|
2019-07-25 18:22:12 +00:00
|
|
|
|
|
|
|
virCommandAddArg(cmd, "--migration-key");
|
2021-03-01 10:04:54 +00:00
|
|
|
virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", migpwdfile_fd);
|
2019-07-25 18:22:11 +00:00
|
|
|
}
|
|
|
|
|
2020-01-31 23:11:22 +00:00
|
|
|
return g_steal_pointer(&cmd);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
error:
|
|
|
|
if (created)
|
2022-02-04 16:20:38 +00:00
|
|
|
qemuTPMEmulatorDeleteStorage(tpm);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-04 18:47:21 +00:00
|
|
|
/* --------------------
|
|
|
|
* High-level actions
|
|
|
|
* --------------------
|
|
|
|
*
|
|
|
|
* Each of these corresponds to one of the public entry points
|
|
|
|
* defined below, but operates on a single TPM device instead of the
|
|
|
|
* entire VM.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qemuTPMEmulatorInitPaths:
|
|
|
|
*
|
|
|
|
* @tpm: TPM definition for an emulator type
|
|
|
|
* @swtpmStorageDir: the general swtpm storage dir which is used as a base
|
|
|
|
* directory for creating VM specific directories
|
|
|
|
* @logDir: directory where swtpm writes its logs into
|
|
|
|
* @vmname: name of the VM
|
|
|
|
* @uuid: the UUID of the VM
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuTPMEmulatorInitPaths(virDomainTPMDef *tpm,
|
|
|
|
const char *swtpmStorageDir,
|
|
|
|
const char *logDir,
|
|
|
|
const char *vmname,
|
|
|
|
const unsigned char *uuid)
|
|
|
|
{
|
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
|
|
|
|
virUUIDFormat(uuid, uuidstr);
|
|
|
|
|
|
|
|
if (!tpm->data.emulator.storagepath &&
|
|
|
|
!(tpm->data.emulator.storagepath =
|
|
|
|
qemuTPMEmulatorStorageBuildPath(swtpmStorageDir, uuidstr,
|
2022-07-15 16:04:21 +00:00
|
|
|
tpm->data.emulator.version)))
|
2022-02-04 18:47:21 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!tpm->data.emulator.logfile) {
|
|
|
|
tpm->data.emulator.logfile = qemuTPMEmulatorLogBuildPath(logDir,
|
|
|
|
vmname);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuTPMEmulatorCleanupHost:
|
|
|
|
* @tpm: TPM definition
|
|
|
|
*
|
|
|
|
* Clean up persistent storage for the swtpm.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
qemuTPMEmulatorCleanupHost(virDomainTPMDef *tpm)
|
|
|
|
{
|
|
|
|
if (!tpm->data.emulator.persistent_state)
|
|
|
|
qemuTPMEmulatorDeleteStorage(tpm);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qemuTPMEmulatorPrepareHost:
|
|
|
|
*
|
|
|
|
* @tpm: tpm definition
|
|
|
|
* @logDir: directory where swtpm writes its logs into
|
|
|
|
* @swtpm_user: uid to run the swtpm with
|
|
|
|
* @swtpm_group: gid to run the swtpm with
|
|
|
|
* @swtpmStateDir: directory for swtpm runtime state
|
|
|
|
* @qemu_user: uid that qemu will run with; we share the socket file with it
|
|
|
|
* @shortName: short and unique name of the domain
|
|
|
|
*
|
|
|
|
* Prepare the log directory for the swtpm and adjust ownership of it and the
|
|
|
|
* log file we will be using. Prepare the state directory where we will share
|
|
|
|
* the socket between tss and qemu users.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuTPMEmulatorPrepareHost(virDomainTPMDef *tpm,
|
|
|
|
const char *logDir,
|
|
|
|
uid_t swtpm_user,
|
|
|
|
gid_t swtpm_group,
|
|
|
|
const char *swtpmStateDir,
|
|
|
|
uid_t qemu_user,
|
|
|
|
const char *shortName)
|
|
|
|
{
|
|
|
|
/* create log dir ... allow 'tss' user to cd into it */
|
|
|
|
if (g_mkdir_with_parents(logDir, 0711) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* ... and adjust ownership */
|
|
|
|
if (virDirCreate(logDir, 0730, swtpm_user, swtpm_group,
|
|
|
|
VIR_DIR_CREATE_ALLOW_EXIST) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!virFileExists(tpm->data.emulator.logfile) &&
|
|
|
|
virFileTouch(tpm->data.emulator.logfile, 0644) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ... and make sure it can be accessed by swtpm_user */
|
|
|
|
if (chown(tpm->data.emulator.logfile, swtpm_user, swtpm_group) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Could not chown on swtpm logfile %s"),
|
|
|
|
tpm->data.emulator.logfile);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
create our swtpm state dir ...
|
|
|
|
- QEMU user needs to be able to access the socket there
|
|
|
|
- swtpm group needs to be able to create files there
|
|
|
|
- in privileged mode 0570 would be enough, for non-privileged mode
|
|
|
|
we need 0770
|
|
|
|
*/
|
|
|
|
if (virDirCreate(swtpmStateDir, 0770, qemu_user, swtpm_group,
|
|
|
|
VIR_DIR_CREATE_ALLOW_EXIST) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* create the socket filename */
|
|
|
|
if (!tpm->data.emulator.source->data.nix.path &&
|
|
|
|
!(tpm->data.emulator.source->data.nix.path =
|
|
|
|
qemuTPMEmulatorSocketBuildPath(swtpmStateDir, shortName)))
|
|
|
|
return -1;
|
|
|
|
tpm->data.emulator.source->type = VIR_DOMAIN_CHR_TYPE_UNIX;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-04 16:22:31 +00:00
|
|
|
/*
|
|
|
|
* qemuTPMEmulatorStop
|
|
|
|
* @swtpmStateDir: A directory where the socket is located
|
|
|
|
* @shortName: short and unique name of the domain
|
|
|
|
*
|
|
|
|
* Gracefully stop the swptm
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
qemuTPMEmulatorStop(const char *swtpmStateDir,
|
|
|
|
const char *shortName)
|
|
|
|
{
|
2021-12-13 13:06:20 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2020-01-31 23:01:13 +00:00
|
|
|
g_autofree char *pathname = NULL;
|
|
|
|
g_autofree char *errbuf = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *swtpm_ioctl = virTPMGetSwtpmIoctl();
|
2022-08-18 14:03:01 +00:00
|
|
|
g_autofree char *pidfile = qemuTPMEmulatorPidFileBuildPath(swtpmStateDir,
|
|
|
|
shortName);
|
|
|
|
if (swtpm_ioctl &&
|
|
|
|
(pathname = qemuTPMEmulatorSocketBuildPath(swtpmStateDir, shortName)) &&
|
|
|
|
virFileExists(pathname)) {
|
2019-07-25 18:22:02 +00:00
|
|
|
|
2022-08-18 14:03:01 +00:00
|
|
|
cmd = virCommandNewArgList(swtpm_ioctl, "--unix", pathname, "-s", NULL);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2022-08-18 14:03:01 +00:00
|
|
|
virCommandSetErrorBuffer(cmd, &errbuf);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2022-08-18 14:03:01 +00:00
|
|
|
ignore_value(virCommandRun(cmd, NULL));
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2022-08-18 14:03:01 +00:00
|
|
|
/* clean up the socket */
|
|
|
|
unlink(pathname);
|
|
|
|
}
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2022-08-18 14:03:01 +00:00
|
|
|
if (pidfile)
|
|
|
|
virPidFileForceCleanupPath(pidfile);
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-04 16:35:46 +00:00
|
|
|
/**
|
|
|
|
* qemuExtTPMEmulatorSetupCgroup:
|
2022-02-04 17:58:23 +00:00
|
|
|
* @swtpmStateDir: directory for swtpm runtime state
|
2022-02-04 16:35:46 +00:00
|
|
|
* @shortName: short and unique name of the domain
|
|
|
|
* @cgroup: cgroup to add the swtpm process to
|
|
|
|
*
|
|
|
|
* Add the swtpm process to the appropriate cgroup.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuExtTPMEmulatorSetupCgroup(const char *swtpmStateDir,
|
|
|
|
const char *shortName,
|
|
|
|
virCgroup *cgroup)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
rc = qemuTPMEmulatorGetPid(swtpmStateDir, shortName, &pid);
|
|
|
|
if (rc < 0 || (rc == 0 && pid == (pid_t)-1)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Could not get process id of swtpm"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCgroupAddProcess(cgroup, pid) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-03 18:56:46 +00:00
|
|
|
/**
|
|
|
|
* qemuTPMEmulatorStart:
|
2017-04-04 16:22:31 +00:00
|
|
|
* @driver: QEMU driver
|
2019-06-05 10:31:00 +00:00
|
|
|
* @vm: the domain object
|
2022-02-04 18:44:26 +00:00
|
|
|
* @tpm: TPM definition
|
2022-02-03 19:06:04 +00:00
|
|
|
* @shortName: short and unique name of the domain
|
2019-07-26 20:41:10 +00:00
|
|
|
* @incomingMigration: whether we have an incoming migration
|
2017-04-04 16:22:31 +00:00
|
|
|
*
|
|
|
|
* Start the external TPM Emulator:
|
|
|
|
* - have the command line built
|
|
|
|
* - start the external TPM Emulator and sync with it before QEMU start
|
|
|
|
*/
|
|
|
|
static int
|
2022-02-03 18:56:46 +00:00
|
|
|
qemuTPMEmulatorStart(virQEMUDriver *driver,
|
|
|
|
virDomainObj *vm,
|
2022-02-03 19:06:04 +00:00
|
|
|
const char *shortName,
|
2022-02-03 18:56:46 +00:00
|
|
|
virDomainTPMDef *tpm,
|
|
|
|
bool incomingMigration)
|
2017-04-04 16:22:31 +00:00
|
|
|
{
|
2020-01-24 18:32:21 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2022-02-02 16:28:16 +00:00
|
|
|
VIR_AUTOCLOSE errfd = -1;
|
2020-01-24 18:32:21 +00:00
|
|
|
g_autoptr(virQEMUDriverConfig) cfg = NULL;
|
2022-02-02 16:28:16 +00:00
|
|
|
g_autofree char *pidfile = NULL;
|
|
|
|
virTimeBackOffVar timebackoff;
|
|
|
|
const unsigned long long timeout = 1000; /* ms */
|
|
|
|
int cmdret = 0;
|
|
|
|
pid_t pid = -1;
|
2017-04-04 16:22:31 +00:00
|
|
|
|
|
|
|
cfg = virQEMUDriverGetConfig(driver);
|
|
|
|
|
|
|
|
/* stop any left-over TPM emulator for this VM */
|
|
|
|
qemuTPMEmulatorStop(cfg->swtpmStateDir, shortName);
|
|
|
|
|
2018-11-13 14:14:43 +00:00
|
|
|
if (!(cmd = qemuTPMEmulatorBuildCommand(tpm, vm->def->name, vm->def->uuid,
|
2017-04-04 16:22:31 +00:00
|
|
|
driver->privileged,
|
|
|
|
cfg->swtpm_user,
|
2018-04-05 19:06:55 +00:00
|
|
|
cfg->swtpm_group,
|
2019-07-26 20:41:10 +00:00
|
|
|
incomingMigration)))
|
2020-01-24 18:34:07 +00:00
|
|
|
return -1;
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2019-08-08 14:54:55 +00:00
|
|
|
if (qemuExtDeviceLogCommand(driver, vm, cmd, "TPM Emulator") < 0)
|
2020-01-24 18:34:07 +00:00
|
|
|
return -1;
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2022-01-27 16:17:36 +00:00
|
|
|
if (!(pidfile = qemuTPMEmulatorPidFileBuildPath(cfg->swtpmStateDir, shortName)))
|
2022-02-02 16:28:16 +00:00
|
|
|
return -1;
|
|
|
|
|
2022-03-21 12:33:06 +00:00
|
|
|
virCommandDoAsyncIO(cmd);
|
2022-02-02 16:28:16 +00:00
|
|
|
virCommandDaemonize(cmd);
|
|
|
|
virCommandSetPidFile(cmd, pidfile);
|
|
|
|
virCommandSetErrorFD(cmd, &errfd);
|
2017-04-04 16:22:31 +00:00
|
|
|
|
2018-11-13 14:14:43 +00:00
|
|
|
if (qemuSecurityStartTPMEmulator(driver, vm, cmd,
|
2018-04-04 16:40:32 +00:00
|
|
|
cfg->swtpm_user, cfg->swtpm_group,
|
2022-02-02 16:28:16 +00:00
|
|
|
NULL, &cmdret) < 0)
|
2020-01-24 18:34:07 +00:00
|
|
|
return -1;
|
2018-04-04 16:40:32 +00:00
|
|
|
|
2022-02-02 16:28:16 +00:00
|
|
|
if (cmdret < 0) {
|
|
|
|
/* virCommandRun() hidden in qemuSecurityStartTPMEmulator()
|
|
|
|
* already reported error. */
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virPidFileReadPath(pidfile, &pid) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("swtpm didn't show up"));
|
|
|
|
goto error;
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
2022-02-02 16:28:16 +00:00
|
|
|
if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0)
|
|
|
|
goto error;
|
|
|
|
while (virTimeBackOffWait(&timebackoff)) {
|
|
|
|
char errbuf[1024] = { 0 };
|
|
|
|
|
|
|
|
if (virFileExists(tpm->data.emulator.source->data.nix.path))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (virProcessKill(pid, 0) == 0)
|
2018-04-05 19:06:55 +00:00
|
|
|
continue;
|
2022-02-02 16:28:16 +00:00
|
|
|
|
|
|
|
if (saferead(errfd, errbuf, sizeof(errbuf) - 1) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("swtpm died unexpectedly"));
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("swtpm died and reported: %s"), errbuf);
|
2018-04-05 19:06:55 +00:00
|
|
|
}
|
2022-02-02 16:28:16 +00:00
|
|
|
goto error;
|
2018-04-05 19:06:55 +00:00
|
|
|
}
|
2022-02-02 16:28:16 +00:00
|
|
|
|
|
|
|
if (!virFileExists(tpm->data.emulator.source->data.nix.path)) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
|
|
|
|
_("swtpm socket did not show up"));
|
2018-04-05 19:06:55 +00:00
|
|
|
goto error;
|
2022-02-02 16:28:16 +00:00
|
|
|
}
|
2018-04-05 19:06:55 +00:00
|
|
|
|
2020-01-24 18:34:07 +00:00
|
|
|
return 0;
|
2018-04-05 19:06:55 +00:00
|
|
|
|
|
|
|
error:
|
2022-02-02 16:28:16 +00:00
|
|
|
virCommandAbort(cmd);
|
|
|
|
if (pid >= 0)
|
|
|
|
virProcessKillPainfully(pid, true);
|
|
|
|
if (pidfile)
|
|
|
|
unlink(pidfile);
|
2020-01-24 18:34:07 +00:00
|
|
|
return -1;
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-04 18:38:46 +00:00
|
|
|
/* ---------------------
|
|
|
|
* Module entry points
|
|
|
|
* ---------------------
|
|
|
|
*
|
|
|
|
* These are the public functions that will be called by other parts
|
|
|
|
* of the QEMU driver.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuExtTPMInitPaths(virQEMUDriver *driver,
|
2022-04-29 14:38:33 +00:00
|
|
|
virDomainDef *def,
|
|
|
|
virDomainTPMDef *tpm)
|
2022-02-04 18:38:46 +00:00
|
|
|
{
|
|
|
|
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
|
|
|
|
2022-04-29 14:38:33 +00:00
|
|
|
return qemuTPMEmulatorInitPaths(tpm,
|
|
|
|
cfg->swtpmStorageDir,
|
|
|
|
cfg->swtpmLogDir,
|
|
|
|
def->name,
|
|
|
|
def->uuid);
|
2022-02-04 18:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuExtTPMPrepareHost(virQEMUDriver *driver,
|
2022-04-29 14:38:33 +00:00
|
|
|
virDomainDef *def,
|
|
|
|
virDomainTPMDef *tpm)
|
2022-02-04 18:38:46 +00:00
|
|
|
{
|
|
|
|
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
|
|
|
g_autofree char *shortName = virDomainDefGetShortName(def);
|
|
|
|
|
|
|
|
if (!shortName)
|
|
|
|
return -1;
|
|
|
|
|
2022-04-29 14:38:33 +00:00
|
|
|
return qemuTPMEmulatorPrepareHost(tpm,
|
|
|
|
cfg->swtpmLogDir,
|
|
|
|
cfg->swtpm_user,
|
|
|
|
cfg->swtpm_group,
|
|
|
|
cfg->swtpmStateDir,
|
|
|
|
cfg->user,
|
|
|
|
shortName);
|
2022-02-04 18:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2022-04-29 14:38:33 +00:00
|
|
|
qemuExtTPMCleanupHost(virDomainTPMDef *tpm)
|
2022-02-04 18:38:46 +00:00
|
|
|
{
|
2022-04-29 14:38:33 +00:00
|
|
|
qemuTPMEmulatorCleanupHost(tpm);
|
2022-02-04 18:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-04 16:22:31 +00:00
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuExtTPMStart(virQEMUDriver *driver,
|
|
|
|
virDomainObj *vm,
|
2022-04-29 14:38:33 +00:00
|
|
|
virDomainTPMDef *tpm,
|
2019-07-26 20:41:10 +00:00
|
|
|
bool incomingMigration)
|
2017-04-04 16:22:31 +00:00
|
|
|
{
|
2022-02-03 19:06:04 +00:00
|
|
|
g_autofree char *shortName = virDomainDefGetShortName(vm->def);
|
2020-06-10 18:11:47 +00:00
|
|
|
|
2022-02-03 19:06:04 +00:00
|
|
|
if (!shortName)
|
|
|
|
return -1;
|
|
|
|
|
2022-04-29 14:38:33 +00:00
|
|
|
return qemuTPMEmulatorStart(driver, vm, shortName, tpm, incomingMigration);
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuExtTPMStop(virQEMUDriver *driver,
|
|
|
|
virDomainObj *vm)
|
2017-04-04 16:22:31 +00:00
|
|
|
{
|
2020-01-31 23:11:22 +00:00
|
|
|
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
2022-02-03 19:06:04 +00:00
|
|
|
g_autofree char *shortName = virDomainDefGetShortName(vm->def);
|
2020-06-10 18:11:47 +00:00
|
|
|
|
2022-02-03 19:06:04 +00:00
|
|
|
if (!shortName)
|
|
|
|
return;
|
2020-11-14 12:56:41 +00:00
|
|
|
|
2022-04-29 14:38:33 +00:00
|
|
|
qemuTPMEmulatorStop(cfg->swtpmStateDir, shortName);
|
|
|
|
qemuSecurityCleanupTPMEmulator(driver, vm);
|
2017-04-04 16:22:31 +00:00
|
|
|
}
|
2018-04-05 19:06:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
qemuExtTPMSetupCgroup(virQEMUDriver *driver,
|
|
|
|
virDomainDef *def,
|
|
|
|
virCgroup *cgroup)
|
2018-04-05 19:06:55 +00:00
|
|
|
{
|
2020-01-31 23:11:22 +00:00
|
|
|
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
2022-02-03 19:06:04 +00:00
|
|
|
g_autofree char *shortName = virDomainDefGetShortName(def);
|
2020-06-10 18:11:47 +00:00
|
|
|
|
2022-02-03 19:06:04 +00:00
|
|
|
if (!shortName)
|
|
|
|
return -1;
|
2020-11-14 12:56:41 +00:00
|
|
|
|
2022-04-29 14:38:33 +00:00
|
|
|
if (qemuExtTPMEmulatorSetupCgroup(cfg->swtpmStateDir, shortName, cgroup) < 0)
|
|
|
|
return -1;
|
2018-04-05 19:06:55 +00:00
|
|
|
|
2020-01-31 23:16:06 +00:00
|
|
|
return 0;
|
2018-04-05 19:06:55 +00:00
|
|
|
}
|