[PATCH v3 00/10] swtpm: Add support for profiles

Upcoming libtpms v0.10 and swtpm v0.10 will have TPM profile support that allows to restrict a TPM's provided set of crypto algorithms and commands and through which backwards compatibility and migration from newer versions of libtpms to older ones (up to libtpms v0.9) is supported. For the latter to work it is necessary that the user chooses the right ('null') profile. This series adds support for passing a profile choice to swtpm_setup by setting it in the domain XML using the <profile/> XML node. An optional attribute 'remove_disabled' can be set in this node and accepts two values: "check": test a few crypto algorithms (tdes, camellia, unpadded encryption, and others) for whether they are currently disabled due to FIPS mode on the host and remove these algorithms in the 'custom' profile if they are disabled; "fips-host": do not test but remove all the possibly disabled crypto algorithms (from list above) Also extend the documentation but point the user to swtpm and libtpms documentation for further details. Follow Deniel's suggestions there's now a PR for swtpm_setup to support searching for profiles though a configurable local directory, distro directory and if no profile could be found there (with appended ".json" suffix) it will fall back to try to use a built-in profile by the provided name: https://github.com/stefanberger/swtpm/pull/918 Stefan v3: - 2/10: Adjustments to due rebase - Applied Marc-André's R-b tags - 10/10: Read back profile name from swtpm and adjust it in emulator defs Stefan Berger (10): conf: Move TPM emulator parameters into own struct qemu: Pass virQEMUDriverConfig rather than some of its fields util: Add parsing support for swtpm_setup's cmdarg-profile capability conf: Define enum virDomainTPMProfileRemoveDisabled schema: Extend schema for TPM emulator profile node conf: Add support for profile parameter on TPM emulator in domain XML docs: Add documentation for the TPM backend profile node qemu: Extend swtpm_setup command line to set a profile by its name qemu: Move adding of keys to swtpm command line into own function qemu: Read the profile name after creation of TPM instance docs/formatdomain.rst | 30 +++ src/conf/domain_conf.c | 43 +++++ src/conf/domain_conf.h | 35 ++-- src/conf/domain_validate.c | 7 + src/conf/schemas/domaincommon.rng | 25 +++ src/conf/virconftypes.h | 2 + src/qemu/qemu_extdevice.c | 5 +- src/qemu/qemu_tpm.c | 310 ++++++++++++++++++++---------- src/qemu/qemu_tpm.h | 3 +- src/util/virtpm.c | 2 + src/util/virtpm.h | 2 + tests/testutilsqemu.c | 1 + 12 files changed, 355 insertions(+), 110 deletions(-) -- 2.47.0

To avoid passing TPM emulator parameters around individually, move them into a structure and pass around the structure. Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- v3: - Made virDomainTPMEmulatorDef first parameter to functions - Applied Marc-André's R-b --- src/conf/domain_conf.h | 24 +++++++++-------- src/conf/virconftypes.h | 2 ++ src/qemu/qemu_tpm.c | 58 ++++++++++++++--------------------------- 3 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index a15af4fae3..e5aee3c2cf 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1465,6 +1465,18 @@ typedef enum { #define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0" +struct _virDomainTPMEmulatorDef { + virDomainTPMVersion version; + virDomainChrSourceDef *source; + char *storagepath; + char *logfile; + unsigned int debug; + unsigned char secretuuid[VIR_UUID_BUFLEN]; + bool hassecretuuid; + bool persistent_state; + virBitmap *activePcrBanks; +}; + struct _virDomainTPMDef { virObject *privateData; @@ -1475,17 +1487,7 @@ struct _virDomainTPMDef { struct { virDomainChrSourceDef *source; } passthrough; - struct { - virDomainTPMVersion version; - virDomainChrSourceDef *source; - char *storagepath; - char *logfile; - unsigned int debug; - unsigned char secretuuid[VIR_UUID_BUFLEN]; - bool hassecretuuid; - bool persistent_state; - virBitmap *activePcrBanks; - } emulator; + virDomainTPMEmulatorDef emulator; struct { virDomainChrSourceDef *source; } external; diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index f18ebcca10..59be61cea4 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -234,6 +234,8 @@ typedef struct _virDomainAudioDef virDomainAudioDef; typedef struct _virDomainTPMDef virDomainTPMDef; +typedef struct _virDomainTPMEmulatorDef virDomainTPMEmulatorDef; + typedef struct _virDomainThreadSchedParam virDomainThreadSchedParam; typedef struct _virDomainTimerCatchupDef virDomainTimerCatchupDef; diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 749e4232b9..0a5643b42b 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -343,31 +343,26 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, /* * qemuTPMEmulatorRunSetup * - * @storagepath: path to the directory for TPM state + * @emulator: emulator parameters * @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' - * @tpmversion: The version of the TPM, either a TPM 1.2 or TPM 2 - * @encryption: pointer to virStorageEncryption holding secret + * @secretuuid: UUID describing virStorageEncryption holding secret * @incomingMigration: whether we have an incoming migration * * Setup the external swtpm by creating endorsement key and * certificates for it. */ static int -qemuTPMEmulatorRunSetup(const char *storagepath, +qemuTPMEmulatorRunSetup(const virDomainTPMEmulatorDef *emulator, const char *vmname, const unsigned char *vmuuid, bool privileged, uid_t swtpm_user, gid_t swtpm_group, - const char *logfile, - const virDomainTPMVersion tpmversion, const unsigned char *secretuuid, bool incomingMigration) { @@ -380,9 +375,9 @@ qemuTPMEmulatorRunSetup(const char *storagepath, if (!swtpm_setup) return -1; - if (!privileged && tpmversion == VIR_DOMAIN_TPM_VERSION_1_2 && + if (!privileged && emulator->version == VIR_DOMAIN_TPM_VERSION_1_2 && !virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_TPM12_NOT_NEED_ROOT)) { - return virFileWriteStr(logfile, + return virFileWriteStr(emulator->logfile, _("Did not create EK and certificates since this requires privileged mode for a TPM 1.2\n"), 0600); } @@ -397,7 +392,7 @@ qemuTPMEmulatorRunSetup(const char *storagepath, virCommandSetUID(cmd, swtpm_user); virCommandSetGID(cmd, swtpm_group); - switch (tpmversion) { + switch (emulator->version) { case VIR_DOMAIN_TPM_VERSION_1_2: break; case VIR_DOMAIN_TPM_VERSION_2_0: @@ -413,9 +408,9 @@ qemuTPMEmulatorRunSetup(const char *storagepath, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", emulator->storagepath, "--vmid", vmid, - "--logfile", logfile, + "--logfile", emulator->logfile, "--createek", "--create-ek-cert", "--create-platform-cert", @@ -424,8 +419,8 @@ qemuTPMEmulatorRunSetup(const char *storagepath, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", storagepath, - "--logfile", logfile, + "--tpm-state", emulator->storagepath, + "--logfile", emulator->logfile, "--overwrite", NULL); } @@ -435,7 +430,7 @@ qemuTPMEmulatorRunSetup(const char *storagepath, if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not run '%1$s'. exitstatus: %2$d; Check error log '%3$s' for details."), - swtpm_setup, exitstatus, logfile); + swtpm_setup, exitstatus, emulator->logfile); return -1; } @@ -464,26 +459,18 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) /* * qemuTPMEmulatorReconfigure * - * - * @storagepath: path to the directory for TPM state + * @emulator: emulator parameters * @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, +qemuTPMEmulatorReconfigure(const virDomainTPMEmulatorDef *emulator, uid_t swtpm_user, gid_t swtpm_group, - virBitmap *activePcrBanks, - const char *logfile, - const virDomainTPMVersion tpmversion, const unsigned char *secretuuid) { g_autoptr(virCommand) cmd = NULL; @@ -494,8 +481,8 @@ qemuTPMEmulatorReconfigure(const char *storagepath, if (!swtpm_setup) return -1; - if (tpmversion != VIR_DOMAIN_TPM_VERSION_2_0 || - (activePcrBanksStr = qemuTPMPcrBankBitmapToStr(activePcrBanks)) == NULL || + if (emulator->version != VIR_DOMAIN_TPM_VERSION_2_0 || + (activePcrBanksStr = qemuTPMPcrBankBitmapToStr(emulator->activePcrBanks)) == NULL || !virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_RECONFIGURE_PCR_BANKS)) return 0; @@ -510,8 +497,8 @@ qemuTPMEmulatorReconfigure(const char *storagepath, return -1; virCommandAddArgList(cmd, - "--tpm-state", storagepath, - "--logfile", logfile, + "--tpm-state", emulator->storagepath, + "--logfile", emulator->logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", NULL); @@ -521,7 +508,7 @@ qemuTPMEmulatorReconfigure(const char *storagepath, if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not run '%1$s --reconfigure'. exitstatus: %2$d; Check error log '%3$s' for details."), - swtpm_setup, exitstatus, logfile); + swtpm_setup, exitstatus, emulator->logfile); return -1; } @@ -582,19 +569,14 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, secretuuid = tpm->data.emulator.secretuuid; if (created && - qemuTPMEmulatorRunSetup(tpm->data.emulator.storagepath, vmname, vmuuid, + qemuTPMEmulatorRunSetup(&tpm->data.emulator, vmname, vmuuid, privileged, swtpm_user, swtpm_group, - tpm->data.emulator.logfile, - tpm->data.emulator.version, secretuuid, incomingMigration) < 0) goto error; if (!incomingMigration && - qemuTPMEmulatorReconfigure(tpm->data.emulator.storagepath, + qemuTPMEmulatorReconfigure(&tpm->data.emulator, swtpm_user, swtpm_group, - tpm->data.emulator.activePcrBanks, - tpm->data.emulator.logfile, - tpm->data.emulator.version, secretuuid) < 0) goto error; -- 2.47.0

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- v3: - Adjustments due to rebase - Applied Marc-André's R-b --- src/qemu/qemu_tpm.c | 52 +++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 0a5643b42b..506743c268 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -347,9 +347,7 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, * @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 + * @cfg: virQEMUDriverConfig * @secretuuid: UUID describing virStorageEncryption holding secret * @incomingMigration: whether we have an incoming migration * @@ -361,8 +359,7 @@ qemuTPMEmulatorRunSetup(const virDomainTPMEmulatorDef *emulator, const char *vmname, const unsigned char *vmuuid, bool privileged, - uid_t swtpm_user, - gid_t swtpm_group, + const virQEMUDriverConfig *cfg, const unsigned char *secretuuid, bool incomingMigration) { @@ -389,8 +386,8 @@ qemuTPMEmulatorRunSetup(const virDomainTPMEmulatorDef *emulator, virUUIDFormat(vmuuid, uuid); vmid = g_strdup_printf("%s:%s", vmname, uuid); - virCommandSetUID(cmd, swtpm_user); - virCommandSetGID(cmd, swtpm_group); + virCommandSetUID(cmd, cfg->swtpm_user); /* should be uid of 'tss' or 'root' */ + virCommandSetGID(cmd, cfg->swtpm_group); switch (emulator->version) { case VIR_DOMAIN_TPM_VERSION_1_2: @@ -460,17 +457,14 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * qemuTPMEmulatorReconfigure * * @emulator: emulator parameters - * @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 + * @cfg: virQEMUDriverConfig * @secretuuid: The secret's UUID needed for state encryption * * Reconfigure the active PCR banks of a TPM 2. */ static int qemuTPMEmulatorReconfigure(const virDomainTPMEmulatorDef *emulator, - uid_t swtpm_user, - gid_t swtpm_group, + const virQEMUDriverConfig *cfg, const unsigned char *secretuuid) { g_autoptr(virCommand) cmd = NULL; @@ -488,8 +482,8 @@ qemuTPMEmulatorReconfigure(const virDomainTPMEmulatorDef *emulator, cmd = virCommandNew(swtpm_setup); - virCommandSetUID(cmd, swtpm_user); - virCommandSetGID(cmd, swtpm_group); + virCommandSetUID(cmd, cfg->swtpm_user); /* should be uid of 'tss' or 'root' */ + virCommandSetGID(cmd, cfg->swtpm_group); virCommandAddArgList(cmd, "--tpm2", NULL); @@ -523,9 +517,7 @@ qemuTPMEmulatorReconfigure(const virDomainTPMEmulatorDef *emulator, * @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 - * @sharedFilesystems: list of filesystem to consider shared + * @cfg: virQEMUDriverConfig * @incomingMigration: whether we have an incoming migration * * Create the virCommand use for starting the emulator @@ -537,9 +529,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, const char *vmname, const unsigned char *vmuuid, bool privileged, - uid_t swtpm_user, - gid_t swtpm_group, - char *const *sharedFilesystems, + const virQEMUDriverConfig *cfg, bool incomingMigration) { g_autoptr(virCommand) cmd = NULL; @@ -557,12 +547,14 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, /* Do not create storage and run swtpm_setup on incoming migration over * shared storage */ - on_shared_storage = virFileIsSharedFS(tpm->data.emulator.storagepath, sharedFilesystems) == 1; + on_shared_storage = virFileIsSharedFS(tpm->data.emulator.storagepath, + cfg->sharedFilesystems) == 1; if (incomingMigration && on_shared_storage) create_storage = false; if (create_storage && - qemuTPMEmulatorCreateStorage(tpm, &created, swtpm_user, swtpm_group) < 0) + qemuTPMEmulatorCreateStorage(tpm, &created, + cfg->swtpm_user, cfg->swtpm_group) < 0) return NULL; if (tpm->data.emulator.hassecretuuid) @@ -570,14 +562,12 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, if (created && qemuTPMEmulatorRunSetup(&tpm->data.emulator, vmname, vmuuid, - privileged, swtpm_user, swtpm_group, - secretuuid, incomingMigration) < 0) + privileged, cfg, secretuuid, + incomingMigration) < 0) goto error; if (!incomingMigration && - qemuTPMEmulatorReconfigure(&tpm->data.emulator, - swtpm_user, swtpm_group, - secretuuid) < 0) + qemuTPMEmulatorReconfigure(&tpm->data.emulator, cfg, secretuuid) < 0) goto error; unlink(tpm->data.emulator.source->data.nix.path); @@ -603,8 +593,8 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, virCommandAddArg(cmd, "--terminate"); - virCommandSetUID(cmd, swtpm_user); - virCommandSetGID(cmd, swtpm_group); + virCommandSetUID(cmd, cfg->swtpm_user); + virCommandSetGID(cmd, cfg->swtpm_group); switch (tpm->data.emulator.version) { case VIR_DOMAIN_TPM_VERSION_1_2: @@ -925,9 +915,7 @@ qemuTPMEmulatorStart(virQEMUDriver *driver, if (!(cmd = qemuTPMEmulatorBuildCommand(tpm, vm->def->name, vm->def->uuid, driver->privileged, - cfg->swtpm_user, - cfg->swtpm_group, - cfg->sharedFilesystems, + cfg, incomingMigration))) return -1; -- 2.47.0

Add support for parsing swtpm_setup 'cmdarg-profile' capability (since v0.10). Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- src/util/virtpm.c | 1 + src/util/virtpm.h | 1 + tests/testutilsqemu.c | 1 + 3 files changed, 3 insertions(+) diff --git a/src/util/virtpm.c b/src/util/virtpm.c index 81fd6166cf..d991657696 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -50,6 +50,7 @@ VIR_ENUM_IMPL(virTPMSwtpmSetupFeature, "cmdarg-reconfigure-pcr-banks", "tpm-1.2", "tpm-2.0", + "cmdarg-profile", ); /** diff --git a/src/util/virtpm.h b/src/util/virtpm.h index fb330effa8..18c2877c03 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -42,6 +42,7 @@ typedef enum { VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_RECONFIGURE_PCR_BANKS, VIR_TPM_SWTPM_SETUP_FEATURE_TPM_1_2, VIR_TPM_SWTPM_SETUP_FEATURE_TPM_2_0, + VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_PROFILE, VIR_TPM_SWTPM_SETUP_FEATURE_LAST } virTPMSwtpmSetupFeature; diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 4daee432e5..abc425b9b7 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -71,6 +71,7 @@ virTPMSwtpmSetupCapsGet(virTPMSwtpmSetupFeature cap) case VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_CREATE_CONFIG_FILES: case VIR_TPM_SWTPM_SETUP_FEATURE_TPM12_NOT_NEED_ROOT: case VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_RECONFIGURE_PCR_BANKS: + case VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_PROFILE: case VIR_TPM_SWTPM_SETUP_FEATURE_LAST: break; } -- 2.47.0

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- src/conf/domain_conf.c | 7 +++++++ src/conf/domain_conf.h | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6d7dee7956..9e9b9000a8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1330,6 +1330,13 @@ VIR_ENUM_IMPL(virDomainTPMPcrBank, "sha512", ); +VIR_ENUM_IMPL(virDomainTPMProfileRemoveDisabled, + VIR_DOMAIN_TPM_PROFILE_REMOVE_DISABLED_LAST, + "none", + "check", + "fips-host", +); + VIR_ENUM_IMPL(virDomainIOMMUModel, VIR_DOMAIN_IOMMU_MODEL_LAST, "intel", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e5aee3c2cf..ec821ea6fc 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1463,6 +1463,14 @@ typedef enum { VIR_DOMAIN_TPM_PCR_BANK_LAST } virDomainPcrBank; +typedef enum { + VIR_DOMAIN_TPM_PROFILE_REMOVE_DISABLED_NONE = 0, + VIR_DOMAIN_TPM_PROFILE_REMOVE_DISABLED_CHECK, + VIR_DOMAIN_TPM_PROFILE_REMOVE_DISABLED_FIPS_HOST, + + VIR_DOMAIN_TPM_PROFILE_REMOVE_DISABLED_LAST +} virDomainTPMProfileRemoveDisabled; + #define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0" struct _virDomainTPMEmulatorDef { @@ -4280,6 +4288,7 @@ VIR_ENUM_DECL(virDomainTPMModel); VIR_ENUM_DECL(virDomainTPMBackend); VIR_ENUM_DECL(virDomainTPMVersion); VIR_ENUM_DECL(virDomainTPMPcrBank); +VIR_ENUM_DECL(virDomainTPMProfileRemoveDisabled); VIR_ENUM_DECL(virDomainMemoryModel); VIR_ENUM_DECL(virDomainMemoryBackingModel); VIR_ENUM_DECL(virDomainMemorySource); -- 2.47.0

Extend the schema for the TPM emulator profile node. Require that the profile the user provides is describe in a name attribute. An optional remove_disabled attribute is also supported for swtpm to automatically remove algorithms from the 'custom' profile if they are disabled by FIPS mode on the host. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- src/conf/schemas/domaincommon.rng | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index efb5f00d77..862de141da 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -5923,6 +5923,7 @@ <interleave> <ref name="tpm-backend-emulator-encryption"/> <ref name="tpm-backend-emulator-active-pcr-banks"/> + <ref name="tpm-backend-emulator-profile"/> </interleave> <optional> <attribute name="persistent_state"> @@ -6020,6 +6021,30 @@ </optional> </define> + <define name="profileName"> + <data type="string"> + <param name="pattern">[A-Za-z0-9.\-:]+</param> + </data> + </define> + + <define name="tpm-backend-emulator-profile"> + <optional> + <element name="profile"> + <attribute name="name"> + <ref name="profileName"/> + </attribute> + <optional> + <attribute name="remove_disabled"> + <choice> + <value>check</value> + <value>fips-host</value> + </choice> + </attribute> + </optional> + </element> + </optional> + </define> + <define name="vsock"> <element name="vsock"> <optional> -- 2.47.0

Extend the parser and XML builder with support for the profile parameter and its remove_disabled attribute. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- src/conf/domain_conf.c | 36 ++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 2 ++ src/conf/domain_validate.c | 7 +++++++ 3 files changed, 45 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9e9b9000a8..c372727111 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3471,6 +3471,7 @@ void virDomainTPMDefFree(virDomainTPMDef *def) g_free(def->data.emulator.storagepath); g_free(def->data.emulator.logfile); virBitmapFree(def->data.emulator.activePcrBanks); + g_free(def->data.emulator.profile_name); break; case VIR_DOMAIN_TPM_TYPE_EXTERNAL: virObjectUnref(def->data.external.source); @@ -10779,6 +10780,15 @@ virDomainSmartcardDefParseXML(virDomainXMLOption *xmlopt, * <tpm model='tpm-tis'> * <backend type='emulator' version='2.0' persistent_state='yes'> * </tpm> + * + * A profile for a TPM 2.0 can be added like this: + * + * <tpm model='tpm-crb'> + * <backend type='emulator' version='2.0'> + * <profile name='local:restricted' remove_disabled='check'/> + * </backend> + * </tpm> + * */ static virDomainTPMDef * virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, @@ -10797,6 +10807,8 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, g_autofree xmlNodePtr *backends = NULL; g_autofree xmlNodePtr *nodes = NULL; g_autofree char *type = NULL; + virDomainTPMProfileRemoveDisabled profile_remove_disabled; + xmlNodePtr profile; int bank; if (!(def = virDomainTPMDefNew(xmlopt))) @@ -10887,6 +10899,22 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, } virBitmapSetBitExpand(def->data.emulator.activePcrBanks, bank); } + + if ((profile = virXPathNode("./backend/profile[1]", ctxt))) { + def->data.emulator.profile_name = virXMLPropString(profile, "name"); + if (!def->data.emulator.profile_name) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("missing profile name")); + goto error; + } + if (virXMLPropEnum(profile, "remove_disabled", + virDomainTPMProfileRemoveDisabledTypeFromString, + VIR_XML_PROP_NONZERO, + &profile_remove_disabled) < 0) + goto error; + if (profile_remove_disabled != VIR_DOMAIN_TPM_PROFILE_REMOVE_DISABLED_NONE) + def->data.emulator.profile_remove_disabled = + virDomainTPMProfileRemoveDisabledTypeToString(profile_remove_disabled); + } break; case VIR_DOMAIN_TPM_TYPE_EXTERNAL: if (!(type = virXPathString("string(./backend/source/@type)", ctxt))) { @@ -25077,6 +25105,14 @@ virDomainTPMDefFormat(virBuffer *buf, virXMLFormatElement(&backendChildBuf, "active_pcr_banks", NULL, &activePcrBanksBuf); } + if (def->data.emulator.profile_name) { + virBufferAsprintf(&backendChildBuf, "<profile name='%s'", + def->data.emulator.profile_name); + if (def->data.emulator.profile_remove_disabled) + virBufferAsprintf(&backendChildBuf, " remove_disabled='%s'", + def->data.emulator.profile_remove_disabled); + virBufferAddLit(&backendChildBuf, "/>\n"); + } break; case VIR_DOMAIN_TPM_TYPE_EXTERNAL: if (def->data.external.source->type == VIR_DOMAIN_CHR_TYPE_UNIX) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ec821ea6fc..6b08665bb7 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1483,6 +1483,8 @@ struct _virDomainTPMEmulatorDef { bool hassecretuuid; bool persistent_state; virBitmap *activePcrBanks; + char *profile_name; + const char *profile_remove_disabled; }; struct _virDomainTPMDef { diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index b8ae9ed79d..4610bd8e4d 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -3026,6 +3026,13 @@ virDomainTPMDevValidate(const virDomainTPMDef *tpm) virDomainTPMVersionTypeToString(VIR_DOMAIN_TPM_VERSION_2_0)); return -1; } + if (tpm->data.emulator.profile_name && + tpm->data.emulator.version != VIR_DOMAIN_TPM_VERSION_2_0) { + virReportError(VIR_ERR_XML_ERROR, + _("<profile/> requires TPM version '%1$s'"), + virDomainTPMVersionTypeToString(VIR_DOMAIN_TPM_VERSION_2_0)); + return -1; + } break; case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: -- 2.47.0

Add documentation for the TPM backend profile node and point the reader to further documentation about TPM profiles available in the swtpm and TPMLIB_SetProfile man pages. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- docs/formatdomain.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index e6f09a728f..b8071b299a 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8120,6 +8120,7 @@ Example: usage of the TPM Emulator <active_pcr_banks> <sha256/> </active_pcr_banks> + <profile name='local:restricted' remove_disabled='check'/> </backend> </tpm> </devices> @@ -8192,6 +8193,35 @@ Example: usage of the TPM Emulator and may not have any effect otherwise. The selection of PCR banks only works with the ``emulator`` backend. :since:`Since 7.10.0` +``profile`` + The ``profile`` node is used to set a profile for a TPM 2.0. 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. + + The built-in 'null' profile provides backwards compatibility with + libtpms v0.9 but also restricts the user to use only TPM features that were + available at the time of libtpms v0.9. The built-in 'custom' profile is the + only profile that a user can modify and where the ``remove_disabled`` + attribute has any effect. This attribute is particularly useful when a host + is running in FIPS mode and therefore some crypto algorithms (camellia, + tdes, unpadded RSA encryption, 1024-bit RSA keys, and others) are + disabled. When it is set to ``check`` (recommended) then only those + algorithms that are currently disabled will automatically be removed from + the 'custom' profile, while when it is set to ``fips-host`` then all + potentially disabled algorithms will be removed. :since:`Since 10.??.0` + + TPM profiles provided by a distro can be referenced with the 'distro' + attribute. Locally created TPM profiles can be referenced with the + 'local' attribute. + + For further information about TPM profiles see the man pages for ``swtpm`` + (swtpm v0.10) and libtpms's ``TPMLIB_SetProfile`` (libtpms v0.10). + ``encryption`` The ``encryption`` element allows the state of a TPM emulator to be encrypted. The ``secret`` must reference a secret object that holds the -- 2.47.0

Run swtpm_setup with the --profile-name option if the user provided the name of a profile. swtpm_setup will try to load the profile from directories with local profiles and distro profiles and if no profile by this name with appended '.json' suffix could be found there, it will fall back to try to use an internal profile with the given name. Also set the --profile-remove-disabled option if the user provided a value in the remove_disabled attribute in the profile XML node. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- src/qemu/qemu_tpm.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 506743c268..f49276d9be 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -340,6 +340,41 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, } +/* + * Add a (optional) profile to the swtpm_setup command line. + * + * @cmd: virCommand to add options to + * @emulator: emulator parameters + * + * Returns 0 on success, -1 on failure. + */ +static int +qemuTPMVirCommandAddProfile(virCommand *cmd, + const virDomainTPMEmulatorDef *emulator) +{ + if (!emulator->profile_name) + return 0; + + if (!virTPMSwtpmSetupCapsGet( + VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_PROFILE)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("swtpm_setup has no support for profiles")); + return -1; + } + + virCommandAddArgList(cmd, + "--profile-name", emulator->profile_name, + NULL); + + if (emulator->profile_remove_disabled) + virCommandAddArgList(cmd, + "--profile-remove-disable", + emulator->profile_remove_disabled, + NULL); + return 0; +} + + /* * qemuTPMEmulatorRunSetup * @@ -414,6 +449,8 @@ qemuTPMEmulatorRunSetup(const virDomainTPMEmulatorDef *emulator, "--lock-nvram", "--not-overwrite", NULL); + if (qemuTPMVirCommandAddProfile(cmd, emulator) < 0) + return -1; } else { virCommandAddArgList(cmd, "--tpm-state", emulator->storagepath, -- 2.47.0

On Sat, Oct 19, 2024 at 12:24 AM Stefan Berger <stefanb@linux.ibm.com> wrote:
Run swtpm_setup with the --profile-name option if the user provided the name of a profile. swtpm_setup will try to load the profile from directories with local profiles and distro profiles and if no profile by this name with appended '.json' suffix could be found there, it will fall back to try to use an internal profile with the given name.
Also set the --profile-remove-disabled option if the user provided a value in the remove_disabled attribute in the profile XML node.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
--- src/qemu/qemu_tpm.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 506743c268..f49276d9be 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -340,6 +340,41 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, }
+/* + * Add a (optional) profile to the swtpm_setup command line. + * + * @cmd: virCommand to add options to + * @emulator: emulator parameters + * + * Returns 0 on success, -1 on failure. + */ +static int +qemuTPMVirCommandAddProfile(virCommand *cmd, + const virDomainTPMEmulatorDef *emulator) +{ + if (!emulator->profile_name) + return 0; + + if (!virTPMSwtpmSetupCapsGet( + VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_PROFILE)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("swtpm_setup has no support for profiles")); + return -1; + } + + virCommandAddArgList(cmd, + "--profile-name", emulator->profile_name, + NULL); + + if (emulator->profile_remove_disabled) + virCommandAddArgList(cmd, + "--profile-remove-disable", + emulator->profile_remove_disabled, + NULL); + return 0; +} + + /* * qemuTPMEmulatorRunSetup * @@ -414,6 +449,8 @@ qemuTPMEmulatorRunSetup(const virDomainTPMEmulatorDef *emulator, "--lock-nvram", "--not-overwrite", NULL); + if (qemuTPMVirCommandAddProfile(cmd, emulator) < 0) + return -1; } else { virCommandAddArgList(cmd, "--tpm-state", emulator->storagepath, -- 2.47.0

Factor-out code related to adding key to the swtpm command line into its own function. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- src/qemu/qemu_tpm.c | 60 +++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index f49276d9be..99473bba87 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -546,6 +546,38 @@ qemuTPMEmulatorReconfigure(const virDomainTPMEmulatorDef *emulator, return 0; } +static int +qemuTPMVirCommandSwtpmAddEncryption(virCommand *cmd, + const virDomainTPMEmulatorDef *emulator, + const char *swtpm) +{ + int pwdfile_fd = -1; + int migpwdfile_fd = -1; + + if (emulator->hassecretuuid) { + if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("%1$s does not support passing passphrase via file descriptor"), + swtpm); + return -1; + } + + if (qemuTPMSetupEncryption(emulator->secretuuid, + cmd, &pwdfile_fd) < 0) + return -1; + + if (qemuTPMSetupEncryption(emulator->secretuuid, + cmd, &migpwdfile_fd) < 0) + return -1; + + virCommandAddArg(cmd, "--key"); + virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", pwdfile_fd); + + virCommandAddArg(cmd, "--migration-key"); + virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", migpwdfile_fd); + } + return 0; +} /* * qemuTPMEmulatorBuildCommand: @@ -572,8 +604,6 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, g_autoptr(virCommand) cmd = NULL; bool created = false; g_autofree char *swtpm = virTPMGetSwtpm(); - int pwdfile_fd = -1; - int migpwdfile_fd = -1; const unsigned char *secretuuid = NULL; bool create_storage = true; bool on_shared_storage; @@ -644,28 +674,10 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, break; } - if (tpm->data.emulator.hassecretuuid) { - if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD)) { - virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, - _("%1$s does not support passing passphrase via file descriptor"), - swtpm); - goto error; - } - - if (qemuTPMSetupEncryption(tpm->data.emulator.secretuuid, - cmd, &pwdfile_fd) < 0) - goto error; - - if (qemuTPMSetupEncryption(tpm->data.emulator.secretuuid, - cmd, &migpwdfile_fd) < 0) - goto error; - - virCommandAddArg(cmd, "--key"); - virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", pwdfile_fd); - - virCommandAddArg(cmd, "--migration-key"); - virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", migpwdfile_fd); - } + if (qemuTPMVirCommandSwtpmAddEncryption(cmd, + &tpm->data.emulator, + swtpm) < 0) + goto error; /* If swtpm supports it and the TPM state is stored on shared storage, * start swtpm with --migration release-lock-outgoing so it can migrate -- 2.47.0

On Sat, Oct 19, 2024 at 12:24 AM Stefan Berger <stefanb@linux.ibm.com> wrote:
Factor-out code related to adding key to the swtpm command line into its own function.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
--- src/qemu/qemu_tpm.c | 60 +++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 24 deletions(-)
diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index f49276d9be..99473bba87 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -546,6 +546,38 @@ qemuTPMEmulatorReconfigure(const virDomainTPMEmulatorDef *emulator, return 0; }
+static int +qemuTPMVirCommandSwtpmAddEncryption(virCommand *cmd, + const virDomainTPMEmulatorDef *emulator, + const char *swtpm) +{ + int pwdfile_fd = -1; + int migpwdfile_fd = -1; + + if (emulator->hassecretuuid) { + if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("%1$s does not support passing passphrase via file descriptor"), + swtpm); + return -1; + } + + if (qemuTPMSetupEncryption(emulator->secretuuid, + cmd, &pwdfile_fd) < 0) + return -1; + + if (qemuTPMSetupEncryption(emulator->secretuuid, + cmd, &migpwdfile_fd) < 0) + return -1; + + virCommandAddArg(cmd, "--key"); + virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", pwdfile_fd); + + virCommandAddArg(cmd, "--migration-key"); + virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", migpwdfile_fd); + } + return 0; +}
/* * qemuTPMEmulatorBuildCommand: @@ -572,8 +604,6 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, g_autoptr(virCommand) cmd = NULL; bool created = false; g_autofree char *swtpm = virTPMGetSwtpm(); - int pwdfile_fd = -1; - int migpwdfile_fd = -1; const unsigned char *secretuuid = NULL; bool create_storage = true; bool on_shared_storage; @@ -644,28 +674,10 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, break; }
- if (tpm->data.emulator.hassecretuuid) { - if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD)) { - virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, - _("%1$s does not support passing passphrase via file descriptor"), - swtpm); - goto error; - } - - if (qemuTPMSetupEncryption(tpm->data.emulator.secretuuid, - cmd, &pwdfile_fd) < 0) - goto error; - - if (qemuTPMSetupEncryption(tpm->data.emulator.secretuuid, - cmd, &migpwdfile_fd) < 0) - goto error; - - virCommandAddArg(cmd, "--key"); - virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", pwdfile_fd); - - virCommandAddArg(cmd, "--migration-key"); - virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc", migpwdfile_fd); - } + if (qemuTPMVirCommandSwtpmAddEncryption(cmd, + &tpm->data.emulator, + swtpm) < 0) + goto error;
/* If swtpm supports it and the TPM state is stored on shared storage, * start swtpm with --migration release-lock-outgoing so it can migrate -- 2.47.0

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 if it differs from the original profile name then update the profile name in the current and persistent emulator descriptions and have the persistent stored with the update. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- src/qemu/qemu_extdevice.c | 5 +- src/qemu/qemu_tpm.c | 105 ++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_tpm.h | 3 +- src/util/virtpm.c | 1 + src/util/virtpm.h | 1 + 5 files changed, 109 insertions(+), 6 deletions(-) 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 99473bba87..cbb6af8314 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -579,15 +579,94 @@ qemuTPMVirCommandSwtpmAddEncryption(virCommand *cmd, return 0; } + +/* 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); + + virCommandAddArg(cmd, "--tpmstate"); + virCommandAddArgFormat(cmd, "dir=%s", + emulator->storagepath); + + 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\n"), + swtpm, exitstatus, stderr_buf); + return -1; + } + + if (!(object = virJSONValueFromString(stdout_buf))) + return -1; + + if (!(active_profile = virJSONValueObjectGetObject(object, "ActiveProfile"))) + return -1; + + profile_name = g_strdup(virJSONValueObjectGetString(active_profile, "Name")); + if (STRNEQ_NULLABLE(emulator->profile_name, profile_name)) { + g_free(emulator->profile_name); + emulator->profile_name = g_strdup(profile_name); + } + if (STRNEQ_NULLABLE(persistentTPMDef->data.emulator.profile_name, 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 @@ -595,11 +674,13 @@ qemuTPMVirCommandSwtpmAddEncryption(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; @@ -633,6 +714,11 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, incomingMigration) < 0) goto error; + if (created && !incomingMigration && + qemuTPMEmulatorUpdateProfileName(&tpm->data.emulator, persistentTPMDef, + cfg, saveDef) < 0) + goto error; + if (!incomingMigration && qemuTPMEmulatorReconfigure(&tpm->data.emulator, cfg, secretuuid) < 0) goto error; @@ -934,6 +1020,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 * @@ -946,6 +1033,7 @@ qemuTPMEmulatorStart(virQEMUDriver *driver, virDomainObj *vm, const char *shortName, virDomainTPMDef *tpm, + virDomainTPMDef *persistentTPMDef, bool incomingMigration) { g_autoptr(virCommand) cmd = NULL; @@ -954,6 +1042,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; @@ -962,12 +1051,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; @@ -1151,6 +1246,7 @@ int qemuExtTPMStart(virQEMUDriver *driver, virDomainObj *vm, virDomainTPMDef *tpm, + virDomainTPMDef *persistentTPMDef, bool incomingMigration) { g_autofree char *shortName = virDomainDefGetShortName(vm->def); @@ -1158,7 +1254,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 d991657696..d639271b4f 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -40,6 +40,7 @@ VIR_ENUM_IMPL(virTPMSwtpmFeature, VIR_TPM_SWTPM_FEATURE_LAST, "cmdarg-pwd-fd", "cmdarg-migration", + "cmdarg-print-info", ); VIR_ENUM_IMPL(virTPMSwtpmSetupFeature, diff --git a/src/util/virtpm.h b/src/util/virtpm.h index 18c2877c03..a4ed9a3f95 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -31,6 +31,7 @@ bool virTPMHasSwtpm(void); typedef enum { VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD, VIR_TPM_SWTPM_FEATURE_CMDARG_MIGRATION, + VIR_TPM_SWTPM_FEATURE_CMDARG_PRINT_INFO, VIR_TPM_SWTPM_FEATURE_LAST } virTPMSwtpmFeature; -- 2.47.0

On Fri, Oct 18, 2024 at 04:24:04PM -0400, Stefan Berger wrote:
Upcoming libtpms v0.10 and swtpm v0.10 will have TPM profile support that allows to restrict a TPM's provided set of crypto algorithms and commands and through which backwards compatibility and migration from newer versions of libtpms to older ones (up to libtpms v0.9) is supported. For the latter to work it is necessary that the user chooses the right ('null') profile.
This series adds support for passing a profile choice to swtpm_setup by setting it in the domain XML using the <profile/> XML node. An optional attribute 'remove_disabled' can be set in this node and accepts two values:
"check": test a few crypto algorithms (tdes, camellia, unpadded encryption, and others) for whether they are currently disabled due to FIPS mode on the host and remove these algorithms in the 'custom' profile if they are disabled; "fips-host": do not test but remove all the possibly disabled crypto algorithms (from list above)
Also extend the documentation but point the user to swtpm and libtpms documentation for further details.
Follow Deniel's suggestions there's now a PR for swtpm_setup to support searching for profiles though a configurable local directory, distro directory and if no profile could be found there (with appended ".json" suffix) it will fall back to try to use a built-in profile by the provided name: https://github.com/stefanberger/swtpm/pull/918
Stefan
v3: - 2/10: Adjustments to due rebase - Applied Marc-André's R-b tags - 10/10: Read back profile name from swtpm and adjust it in emulator defs
Stefan Berger (10): conf: Move TPM emulator parameters into own struct qemu: Pass virQEMUDriverConfig rather than some of its fields util: Add parsing support for swtpm_setup's cmdarg-profile capability conf: Define enum virDomainTPMProfileRemoveDisabled schema: Extend schema for TPM emulator profile node conf: Add support for profile parameter on TPM emulator in domain XML docs: Add documentation for the TPM backend profile node qemu: Extend swtpm_setup command line to set a profile by its name qemu: Move adding of keys to swtpm command line into own function qemu: Read the profile name after creation of TPM instance
docs/formatdomain.rst | 30 +++ src/conf/domain_conf.c | 43 +++++ src/conf/domain_conf.h | 35 ++-- src/conf/domain_validate.c | 7 + src/conf/schemas/domaincommon.rng | 25 +++ src/conf/virconftypes.h | 2 + src/qemu/qemu_extdevice.c | 5 +- src/qemu/qemu_tpm.c | 310 ++++++++++++++++++++---------- src/qemu/qemu_tpm.h | 3 +- src/util/virtpm.c | 2 + src/util/virtpm.h | 2 + tests/testutilsqemu.c | 1 + 12 files changed, 355 insertions(+), 110 deletions(-)
The new XML schema doesn't affect QEMU CLI args, so we don't need a QEMU argv test file, but we should still have an XML test file to validate the parsing & formatting of the new fields. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
participants (3)
-
Daniel P. Berrangé
-
Marc-André Lureau
-
Stefan Berger