From d79542eec669eb9c449bb8228179e7a87e768017 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 13 Nov 2024 12:39:51 -0500 Subject: [PATCH] qemu: Read back the profile name after creation of a TPM instance Get the JSON profile that the swtpm instance was created with from the output of 'swtpm socket --tpm2 --print-info 0x20 --tpmstate ...'. Get the name of the profile from the JSON and set it in the current and persistent emulator descriptions as 'name' attribute and have the persistent description stored with this update. The user should avoid setting this 'name' attribute since it is meant to be read-only. The following is an example of how the XML could look like: If the user provided no profile node, and therefore swtpm_setup picked its default profile, the XML may now shows the 'name' attribute with the name of the profile. This makes the 'source' attribute now optional. Signed-off-by: Stefan Berger Signed-off-by: Michal Privoznik Reviewed-by: Michal Privoznik --- docs/formatdomain.rst | 16 +-- src/conf/domain_conf.c | 19 ++-- src/conf/domain_conf.h | 1 + src/conf/schemas/domaincommon.rng | 13 ++- src/qemu/qemu_extdevice.c | 5 +- src/qemu/qemu_tpm.c | 100 +++++++++++++++++- src/qemu/qemu_tpm.h | 3 +- src/util/virtpm.c | 1 + src/util/virtpm.h | 1 + .../tpm-emulator-crb-profile.xml | 2 +- 10 files changed, 137 insertions(+), 24 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index b8a0f59ad3..f0b79d5f8d 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8135,7 +8135,7 @@ Example: usage of the TPM Emulator - + @@ -8233,12 +8233,14 @@ Example: usage of the TPM Emulator ``profile`` The ``profile`` node is used to set a profile for a TPM 2.0 given in the source attribute. This profile will be set when the TPM is initially - created and after that cannot be changed anymore. If no profile is provided, - then swtpm will use the latest built-in 'default' profile or the default - profile set in swtpm_setup.conf. Otherwise swtpm_setup will search for a - profile with the given name with appended .json suffix in a configurable - local and then in a distro directory. If none could be found in either, it - will fall back trying to use a built-in one. + created and after that cannot be changed anymore. Once a profile has been + set the name attribute will be updated with the name of the profile that + is running. If no profile is provided, then swtpm will use the latest + built-in 'default' profile or the default profile set in swtpm_setup.conf. + Otherwise swtpm_setup will search for a profile with the given name with + appended .json suffix in a configurable local and then in a distro + directory. If none could be found in either, it will fall back trying to + use a built-in one. The built-in 'null' profile provides backwards compatibility with libtpms v0.9 but also restricts the user to use only TPM features that were diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index bec44eece1..295707ec1f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3479,6 +3479,7 @@ void virDomainTPMDefFree(virDomainTPMDef *def) g_free(def->data.emulator.logfile); virBitmapFree(def->data.emulator.activePcrBanks); g_free(def->data.emulator.profile.source); + g_free(def->data.emulator.profile.name); break; case VIR_DOMAIN_TPM_TYPE_EXTERNAL: virObjectUnref(def->data.external.source); @@ -10925,10 +10926,7 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, if ((profile = virXPathNode("./backend/profile[1]", ctxt))) { def->data.emulator.profile.source = virXMLPropString(profile, "source"); - if (!def->data.emulator.profile.source) { - virReportError(VIR_ERR_XML_ERROR, "%s", _("missing profile source")); - goto error; - } + def->data.emulator.profile.name = virXMLPropString(profile, "name"); if (virXMLPropEnum(profile, "removeDisabled", virDomainTPMProfileRemoveDisabledTypeFromString, VIR_XML_PROP_NONZERO, @@ -25139,16 +25137,23 @@ virDomainTPMDefFormat(virBuffer *buf, virDomainTPMSourceTypeTypeToString(def->data.emulator.source_type)); virBufferEscapeString(&backendChildBuf, " path='%s'/>\n", def->data.emulator.source_path); } - if (def->data.emulator.profile.source) { + if (def->data.emulator.profile.source || + def->data.emulator.profile.name) { g_auto(virBuffer) profileAttrBuf = VIR_BUFFER_INITIALIZER; - virBufferAsprintf(&profileAttrBuf, " source='%s'", - def->data.emulator.profile.source); + if (def->data.emulator.profile.source) { + virBufferAsprintf(&profileAttrBuf, " source='%s'", + def->data.emulator.profile.source); + } if (def->data.emulator.profile.removeDisabled) { virBufferAsprintf(&profileAttrBuf, " removeDisabled='%s'", virDomainTPMProfileRemoveDisabledTypeToString(def->data.emulator.profile.removeDisabled)); } + if (def->data.emulator.profile.name) { + virBufferAsprintf(&profileAttrBuf, " name='%s'", + def->data.emulator.profile.name); + } virXMLFormatElement(&backendChildBuf, "profile", &profileAttrBuf, NULL); } break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 5959e82262..a187ab4083 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1494,6 +1494,7 @@ struct _virDomainTPMEmulatorDef { virBitmap *activePcrBanks; struct { char *source; /* 'source' profile was created from */ + char *name; /* name read from active profile */ virDomainTPMProfileRemoveDisabled removeDisabled; } profile; }; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 91711470f1..bfd0044805 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -6061,9 +6061,11 @@ - - - + + + + + @@ -6072,6 +6074,11 @@ + + + + + diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c index dc1bb56237..a6f31f9773 100644 --- a/src/qemu/qemu_extdevice.c +++ b/src/qemu/qemu_extdevice.c @@ -175,6 +175,7 @@ qemuExtDevicesStart(virQEMUDriver *driver, virDomainObj *vm, bool incomingMigration) { + virDomainDef *persistentDef = vm->newDef; virDomainDef *def = vm->def; size_t i; @@ -189,9 +190,11 @@ qemuExtDevicesStart(virQEMUDriver *driver, for (i = 0; i < def->ntpms; i++) { virDomainTPMDef *tpm = def->tpms[i]; + virDomainTPMDef *persistentTPMDef = persistentDef->tpms[i]; if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR && - qemuExtTPMStart(driver, vm, tpm, incomingMigration) < 0) + qemuExtTPMStart(driver, vm, tpm, persistentTPMDef, + incomingMigration) < 0) return -1; } diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 6d56a845a4..f223dcb9ae 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -628,15 +628,89 @@ qemuTPMVirCommandSwtpmAddTPMState(virCommand *cmd, } } +/* qemuTPMEmulatorUpdateProfileName: + * + * @emulator: TPM emulator definition + * @persistentTPMDef: TPM definition from the persistent domain definition + * @cfg: virQEMUDriverConfig + * @saveDef: whether caller should save the persistent domain def + */ +static int +qemuTPMEmulatorUpdateProfileName(virDomainTPMEmulatorDef *emulator, + virDomainTPMDef *persistentTPMDef, + const virQEMUDriverConfig *cfg, + bool *saveDef) +{ + g_autoptr(virJSONValue) object = NULL; + g_autofree char *stderr_buf = NULL; + g_autofree char *stdout_buf = NULL; + g_autoptr(virCommand) cmd = NULL; + g_autofree char *swtpm = NULL; + virJSONValue *active_profile; + const char *profile_name; + int exitstatus; + + if (emulator->version != VIR_DOMAIN_TPM_VERSION_2_0 || + !virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PRINT_INFO)) + return 0; + + swtpm = virTPMGetSwtpm(); + if (!swtpm) + return -1; + + cmd = virCommandNew(swtpm); + + virCommandSetUID(cmd, cfg->swtpm_user); /* should be uid of 'tss' or 'root' */ + virCommandSetGID(cmd, cfg->swtpm_group); + + virCommandAddArgList(cmd, "socket", "--print-info", "0x20", "--tpm2", NULL); + + qemuTPMVirCommandSwtpmAddTPMState(cmd, emulator); + + if (qemuTPMVirCommandSwtpmAddEncryption(cmd, emulator, swtpm) < 0) + return -1; + + virCommandClearCaps(cmd); + + virCommandSetOutputBuffer(cmd, &stdout_buf); + virCommandSetErrorBuffer(cmd, &stderr_buf); + + if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not run '%1$s --print-info'. exitstatus: %2$d; stderr: %3$s"), + swtpm, exitstatus, stderr_buf); + return -1; + } + + if (!(object = virJSONValueFromString(stdout_buf))) + return -1; + + if (!(active_profile = virJSONValueObjectGetObject(object, "ActiveProfile"))) + return -1; + + profile_name = virJSONValueObjectGetString(active_profile, "Name"); + + g_free(emulator->profile.name); + emulator->profile.name = g_strdup(profile_name); + + *saveDef = true; + g_free(persistentTPMDef->data.emulator.profile.name); + persistentTPMDef->data.emulator.profile.name = g_strdup(profile_name); + + return 0; +} + /* * qemuTPMEmulatorBuildCommand: * * @tpm: TPM definition + * @persistentTPMDef: TPM definition from the persistent domain definition * @vmname: The name of the VM * @vmuuid: The UUID of the VM * @privileged: whether we are running in privileged mode * @cfg: virQEMUDriverConfig * @incomingMigration: whether we have an incoming migration + * @saveDef: whether caller should save the persistent domain def * * Create the virCommand use for starting the emulator * Do some initializations on the way, such as creation of storage @@ -644,11 +718,13 @@ qemuTPMVirCommandSwtpmAddTPMState(virCommand *cmd, */ static virCommand * qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, + virDomainTPMDef *persistentTPMDef, const char *vmname, const unsigned char *vmuuid, bool privileged, const virQEMUDriverConfig *cfg, - bool incomingMigration) + bool incomingMigration, + bool *saveDef) { g_autoptr(virCommand) cmd = NULL; bool created = false; @@ -697,6 +773,11 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, incomingMigration) < 0) goto error; + if (run_setup && !incomingMigration && + qemuTPMEmulatorUpdateProfileName(&tpm->data.emulator, persistentTPMDef, + cfg, saveDef) < 0) + goto error; + if (!incomingMigration && qemuTPMEmulatorReconfigure(&tpm->data.emulator, cfg, secretuuid) < 0) goto error; @@ -996,6 +1077,7 @@ qemuExtTPMEmulatorSetupCgroup(const char *swtpmStateDir, * @driver: QEMU driver * @vm: the domain object * @tpm: TPM definition + * @persistentTPMDef: TPM definition from persistent domain definition * @shortName: short and unique name of the domain * @incomingMigration: whether we have an incoming migration * @@ -1008,6 +1090,7 @@ qemuTPMEmulatorStart(virQEMUDriver *driver, virDomainObj *vm, const char *shortName, virDomainTPMDef *tpm, + virDomainTPMDef *persistentTPMDef, bool incomingMigration) { g_autoptr(virCommand) cmd = NULL; @@ -1016,6 +1099,7 @@ qemuTPMEmulatorStart(virQEMUDriver *driver, g_autofree char *pidfile = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 1000; /* ms */ + bool saveDef = false; pid_t pid = -1; bool lockMetadataException = false; @@ -1024,12 +1108,18 @@ qemuTPMEmulatorStart(virQEMUDriver *driver, /* stop any left-over TPM emulator for this VM */ qemuTPMEmulatorStop(cfg->swtpmStateDir, shortName); - if (!(cmd = qemuTPMEmulatorBuildCommand(tpm, vm->def->name, vm->def->uuid, + if (!(cmd = qemuTPMEmulatorBuildCommand(tpm, persistentTPMDef, + vm->def->name, vm->def->uuid, driver->privileged, cfg, - incomingMigration))) + incomingMigration, + &saveDef))) return -1; + if (saveDef && + virDomainDefSave(vm->newDef, driver->xmlopt, cfg->configDir) < 0) + goto error; + if (qemuExtDeviceLogCommand(driver, vm, cmd, "TPM Emulator") < 0) return -1; @@ -1213,6 +1303,7 @@ int qemuExtTPMStart(virQEMUDriver *driver, virDomainObj *vm, virDomainTPMDef *tpm, + virDomainTPMDef *persistentTPMDef, bool incomingMigration) { g_autofree char *shortName = virDomainDefGetShortName(vm->def); @@ -1220,7 +1311,8 @@ qemuExtTPMStart(virQEMUDriver *driver, if (!shortName) return -1; - return qemuTPMEmulatorStart(driver, vm, shortName, tpm, incomingMigration); + return qemuTPMEmulatorStart(driver, vm, shortName, tpm, persistentTPMDef, + incomingMigration); } diff --git a/src/qemu/qemu_tpm.h b/src/qemu/qemu_tpm.h index 3071dc3f71..7096060a2a 100644 --- a/src/qemu/qemu_tpm.h +++ b/src/qemu/qemu_tpm.h @@ -44,9 +44,10 @@ void qemuExtTPMCleanupHost(virQEMUDriver *driver, int qemuExtTPMStart(virQEMUDriver *driver, virDomainObj *vm, virDomainTPMDef *def, + virDomainTPMDef *persistentDefTPM, bool incomingMigration) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) - ATTRIBUTE_NONNULL(3) + ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4) G_GNUC_WARN_UNUSED_RESULT; void qemuExtTPMStop(virQEMUDriver *driver, diff --git a/src/util/virtpm.c b/src/util/virtpm.c index 1c736b0229..4016ad8fc4 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -42,6 +42,7 @@ VIR_ENUM_IMPL(virTPMSwtpmFeature, "cmdarg-migration", "nvram-backend-dir", "nvram-backend-file", + "cmdarg-print-info", ); VIR_ENUM_IMPL(virTPMSwtpmSetupFeature, diff --git a/src/util/virtpm.h b/src/util/virtpm.h index 9ca09c2d80..03fb92629a 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -33,6 +33,7 @@ typedef enum { VIR_TPM_SWTPM_FEATURE_CMDARG_MIGRATION, VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_DIR, VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_FILE, + VIR_TPM_SWTPM_FEATURE_CMDARG_PRINT_INFO, VIR_TPM_SWTPM_FEATURE_LAST } virTPMSwtpmFeature; diff --git a/tests/qemuxmlconfdata/tpm-emulator-crb-profile.xml b/tests/qemuxmlconfdata/tpm-emulator-crb-profile.xml index b8473cd894..40224f43a7 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-crb-profile.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-crb-profile.xml @@ -29,7 +29,7 @@ - +