[libvirt] [PATCH v4 06/11] qemu: Extend QEMU with external TPM support
John Ferlan
jferlan at redhat.com
Tue May 15 13:53:55 UTC 2018
On 05/10/2018 05:57 PM, Stefan Berger wrote:
> Implement functions for managing the storage of the external swtpm as well
> as starting and stopping it. Also implement functions to use swtpm_setup,
> which simulates the manufacturing of a TPM, which includes creation of
> certificates for the device.
>
> Further, the external TPM needs storage on the host that we need to set
> up before it can be run. We can clean up the host once the domain is
> undefined.
>
> This patch also implements a small layer for external device support that
> calls into the TPM device layer if a domain has an attached TPM. This is
> the layer we will wire up later on.
>
> Signed-off-by: Stefan Berger <stefanb at linux.vnet.ibm.com>
> ---
> src/qemu/Makefile.inc.am | 4 +
> src/qemu/qemu_domain.c | 2 +
> src/qemu/qemu_driver.c | 5 +
> src/qemu/qemu_extdevice.c | 154 ++++++++++
> src/qemu/qemu_extdevice.h | 53 ++++
> src/qemu/qemu_migration.c | 3 +
> src/qemu/qemu_process.c | 12 +
> src/qemu/qemu_tpm.c | 753 ++++++++++++++++++++++++++++++++++++++++++++++
> src/qemu/qemu_tpm.h | 50 +++
> 9 files changed, 1036 insertions(+)
> create mode 100644 src/qemu/qemu_extdevice.c
> create mode 100644 src/qemu/qemu_extdevice.h
> create mode 100644 src/qemu/qemu_tpm.c
> create mode 100644 src/qemu/qemu_tpm.h
>
[...]
> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
> index 37876b8..9370de3 100644
> --- a/src/qemu/qemu_process.c
> +++ b/src/qemu/qemu_process.c
> @@ -48,6 +48,7 @@
> #include "qemu_migration_params.h"
> #include "qemu_interface.h"
> #include "qemu_security.h"
> +#include "qemu_extdevice.h"
>
> #include "cpu/cpu.h"
> #include "datatypes.h"
> @@ -5872,6 +5873,10 @@ qemuProcessPrepareHost(virQEMUDriverPtr driver,
> if (qemuProcessPrepareHostStorage(driver, vm, flags) < 0)
> goto cleanup;
>
> + VIR_DEBUG("Preparing external devices");
> + if (qemuExtDevicesPrepareHost(driver, vm->def) < 0)
> + goto cleanup;
> +
> ret = 0;
> cleanup:
> virObjectUnref(cfg);
> @@ -5955,6 +5960,9 @@ qemuProcessLaunch(virConnectPtr conn,
> goto cleanup;
> logfile = qemuDomainLogContextGetWriteFD(logCtxt);
>
> + if (qemuExtDevicesStart(driver, vm->def, logCtxt) < 0)
> + goto cleanup;
> +
> VIR_DEBUG("Building emulator command line");
> if (!(cmd = qemuBuildCommandLine(driver,
> qemuDomainLogContextGetManager(logCtxt),
> @@ -6194,6 +6202,8 @@ qemuProcessLaunch(virConnectPtr conn,
> ret = 0;
>
> cleanup:
> + if (ret)
if (ret < 0), right?
> + qemuExtDevicesStop(driver, vm->def);
> qemuDomainSecretDestroy(vm);
> virCommandFree(cmd);
> virObjectUnref(logCtxt);
> @@ -6614,6 +6624,8 @@ void qemuProcessStop(virQEMUDriverPtr driver,
>
> qemuDomainCleanupRun(driver, vm);
>
> + qemuExtDevicesStop(driver, vm->def);
> +
> /* Stop autodestroy in case guest is restarted */
> qemuProcessAutoDestroyRemove(driver, vm);
>
> diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c
> new file mode 100644
> index 0000000..024d24d
> --- /dev/null
> +++ b/src/qemu/qemu_tpm.c
> @@ -0,0 +1,753 @@
> +/*
> + * 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/>.
> + *
> + * Author: Stefan Berger <stefanb at linux.vnet.ibm.com>
> + */
> +
[...]
> +/*
> + * qemuTPMCreateEmulatorStoragePath
> + *
> + * @swtpmStorageDir: directory for swtpm persistent state
> + * @vmname: The name of the VM for which to create the storage
> + *
> + * Create the swtpm's storage path
> + */
> +static char *
> +qemuTPMCreateEmulatorStoragePath(const char *swtpmStorageDir,
> + const char *vmname)
You changed at some point to use the uuidstr - probably should change
this too. Comment and argument
> +{
> + char *path = NULL;
> +
> + ignore_value(virAsprintf(&path, "%s/%s/tpm1.2", swtpmStorageDir, vmname));
> +
> + return path;
> +}
> +
> +
[...]
> +/*
> + * qemuTPMCreateEmulatorStorage
> + *
> + * @storagepath: directory for swtpm's pesistent state
persistent
> + * @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
> +qemuTPMCreateEmulatorStorage(const char *storagepath,
> + bool *created,
> + uid_t swtpm_user,
> + gid_t swtpm_group)
> +{
> + int ret = -1;
> + char *swtpmStorageDir = qemuTPMGetTPMStorageDir(storagepath);
> +
> + if (!swtpmStorageDir)
> + return -1;
> +
> + if (qemuTPMEmulatorInitStorage(swtpmStorageDir) < 0)
> + goto cleanup;
> +
> + *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);
> + goto cleanup;
> + }
> +
> + if (virFileChownFiles(storagepath, swtpm_user, swtpm_group) < 0)
> + goto cleanup;
> +
> + ret = 0;
> +
> + cleanup:
> + VIR_FREE(swtpmStorageDir);
> +
> + return ret;
> +}
> +
> +
[...]
> +/*
> + * qemuTPMEmulatorPrepareHost:
> + *
> + * @tpm: tpm definition
> + * @logDir: directory where swtpm writes its logs into
> + * @vmname: name of the VM
> + * @swtpm_user: uid to run the swtpm with
> + * @swtpm_group: gid to run the swtpm with
> + * @swtpmStateDir: directory for swtpm's persistent 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(virDomainTPMDefPtr tpm,
> + const char *logDir,
> + const char *vmname,
> + uid_t swtpm_user,
> + gid_t swtpm_group,
> + const char *swtpmStateDir,
> + uid_t qemu_user,
> + const char *shortName)
> +{
> + int ret = -1;
> +
> + if (qemuTPMEmulatorInit() < 0)
> + return -1;
> +
> + /* create log dir ... */
> + if (virFileMakePathWithMode(logDir, 0730) < 0)
> + return -1;
> +
> + /* ... and adjust ownership */
> + if (virDirCreate(logDir, 0730, swtpm_user, swtpm_group,
> + VIR_DIR_CREATE_ALLOW_EXIST) < 0)
> + goto cleanup;
Do we really need both? IOW: Why not just virDirCreate
> +
> + /* create logfile name ... */
> + if (!tpm->data.emulator.logfile &&
> + virAsprintf(&tpm->data.emulator.logfile, "%s/%s-swtpm.log",
> + logDir, vmname) < 0)
> + goto cleanup;
> +
> + /* ... and make sure it can be accessed by swtpm_user */
> + if (virFileExists(tpm->data.emulator.logfile) &&
> + chown(tpm->data.emulator.logfile, swtpm_user, swtpm_group) < 0) {
> + virReportSystemError(errno,
> + _("Could not chown on swtpm logfile %s"),
> + tpm->data.emulator.logfile);
> + goto cleanup;
> + }
> +
> + /*
> + 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)
> + goto cleanup;
> +
> + /* create the socket filename */
> + if (!tpm->data.emulator.source.data.nix.path &&
> + !(tpm->data.emulator.source.data.nix.path =
> + qemuTPMCreateEmulatorSocket(swtpmStateDir, shortName)))
> + goto cleanup;
> + tpm->data.emulator.source.type = VIR_DOMAIN_CHR_TYPE_UNIX;
> +
> + ret = 0;
> +
> + cleanup:
> +
> + return ret;
> +}
[...]
> +int
> +qemuExtTPMInitPaths(virQEMUDriverPtr driver,
> + virDomainDefPtr def)
When I see qemuExt - I generally expect the code to be in
qemu_extdevice.c... Similar for anything qemuExtTPM. I understand why
the Ext is there (e.g. external), but not sure it's necessary.
FWIW: Although I didn't win my argument when last discussed, my feeling
is static functions should be something like "tpmEmulatorInitPaths";
whereas, external functions should be qemuTPMEmulatorInitPaths.
John
> +{
> + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
> + int ret = 0;
> +
> + switch (def->tpm->type) {
> + case VIR_DOMAIN_TPM_TYPE_EMULATOR:
> + ret = qemuTPMEmulatorInitPaths(def->tpm, cfg->swtpmStorageDir,
> + def->uuid);
> + break;
> + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
> + case VIR_DOMAIN_TPM_TYPE_LAST:
> + break;
> + }
> +
> + virObjectUnref(cfg);
> +
> + return ret;
> +}
> +
> +
[...]
More information about the libvir-list
mailing list