[PATCH v5 0/6] Add TPM emulator <source type='file/dir' path='..'/>

From: Marc-André Lureau <marcandre.lureau@redhat.com> Hi, When swtpm capabilities reports "nvram-backend-dir", it can accepts a single file or block device where TPM state will be stored. --tpmstate must be backend-uri=file://. v5: - fix indentation - update doc about state sharing/locking - add r-b from Stefan v4: - add "qemu: explicit swtpm state locking" - add r-b from Stefan, first patch only atm v3: - changed to <source type='file/dir' path='..'/> v2: - add <source dir='..'/> support as well (Daniel) Related: https://issues.redhat.com/browse/CNV-35250 Marc-André Lureau (6): util: check swtpm nvram-backend-{dir,file} capabilities tpm: rename 'storagepath' to 'source_path' schema: add TPM emulator <source type='file' path='..'> schema: add TPM emulator <source type='dir' path='..'> qemu_tpm: handle file/block storage source qemu: explicit swtpm state locking docs/formatdomain.rst | 22 ++++ src/conf/domain_conf.c | 31 ++++- src/conf/domain_conf.h | 12 +- src/conf/schemas/domaincommon.rng | 26 ++++ src/qemu/qemu_tpm.c | 114 +++++++++++++----- src/security/security_selinux.c | 6 +- src/util/virtpm.c | 3 + src/util/virtpm.h | 3 + .../qemuxmlconfdata/tpm-emulator-tpm2-enc.xml | 1 + tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + tests/testutilsqemu.c | 1 + 11 files changed, 187 insertions(+), 33 deletions(-) -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> --- src/util/virtpm.c | 2 ++ src/util/virtpm.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/util/virtpm.c b/src/util/virtpm.c index 81fd6166cf..298caaad80 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -40,6 +40,8 @@ VIR_ENUM_IMPL(virTPMSwtpmFeature, VIR_TPM_SWTPM_FEATURE_LAST, "cmdarg-pwd-fd", "cmdarg-migration", + "nvram-backend-dir", + "nvram-backend-file", ); VIR_ENUM_IMPL(virTPMSwtpmSetupFeature, diff --git a/src/util/virtpm.h b/src/util/virtpm.h index fb330effa8..99dbcc1dc8 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -31,6 +31,8 @@ bool virTPMHasSwtpm(void); typedef enum { VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD, VIR_TPM_SWTPM_FEATURE_CMDARG_MIGRATION, + VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_DIR, + VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_FILE, VIR_TPM_SWTPM_FEATURE_LAST } virTPMSwtpmFeature; -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> Mechanically replace existing 'storagepath' with 'source_path', as the following patches introduce <source path='..'> configuration. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> --- src/conf/domain_conf.c | 2 +- src/conf/domain_conf.h | 2 +- src/qemu/qemu_tpm.c | 46 ++++++++++++++++----------------- src/security/security_selinux.c | 6 ++--- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6d7dee7956..284a3815b3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3461,7 +3461,7 @@ void virDomainTPMDefFree(virDomainTPMDef *def) break; case VIR_DOMAIN_TPM_TYPE_EMULATOR: virObjectUnref(def->data.emulator.source); - g_free(def->data.emulator.storagepath); + g_free(def->data.emulator.source_path); g_free(def->data.emulator.logfile); virBitmapFree(def->data.emulator.activePcrBanks); break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index a15af4fae3..6b27322e3e 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1478,7 +1478,7 @@ struct _virDomainTPMDef { struct { virDomainTPMVersion version; virDomainChrSourceDef *source; - char *storagepath; + char *source_path; char *logfile; unsigned int debug; unsigned char secretuuid[VIR_UUID_BUFLEN]; diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 749e4232b9..2d9cd59643 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -173,8 +173,8 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm, uid_t swtpm_user, gid_t swtpm_group) { - const char *storagepath = tpm->data.emulator.storagepath; - g_autofree char *swtpmStorageDir = g_path_get_dirname(storagepath); + const char *source_path = tpm->data.emulator.source_path; + g_autofree char *swtpmStorageDir = g_path_get_dirname(source_path); /* allow others to cd into this dir */ if (g_mkdir_with_parents(swtpmStorageDir, 0711) < 0) { @@ -186,19 +186,19 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm, *created = false; - if (!virFileExists(storagepath) || - virDirIsEmpty(storagepath, true) > 0) + if (!virFileExists(source_path) || + virDirIsEmpty(source_path, true) > 0) *created = true; - if (virDirCreate(storagepath, 0700, swtpm_user, swtpm_group, + if (virDirCreate(source_path, 0700, swtpm_user, swtpm_group, VIR_DIR_CREATE_ALLOW_EXIST) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not create directory %1$s as %2$u:%3$d"), - storagepath, swtpm_user, swtpm_group); + source_path, swtpm_user, swtpm_group); return -1; } - if (virFileChownFiles(storagepath, swtpm_user, swtpm_group) < 0) + if (virFileChownFiles(source_path, swtpm_user, swtpm_group) < 0) return -1; return 0; @@ -214,7 +214,7 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm, static void qemuTPMEmulatorDeleteStorage(virDomainTPMDef *tpm) { - g_autofree char *path = g_path_get_dirname(tpm->data.emulator.storagepath); + g_autofree char *path = g_path_get_dirname(tpm->data.emulator.source_path); ignore_value(virFileDeleteTree(path)); } @@ -343,7 +343,7 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, /* * qemuTPMEmulatorRunSetup * - * @storagepath: path to the directory for TPM state + * @source_path: 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 @@ -360,7 +360,7 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, * certificates for it. */ static int -qemuTPMEmulatorRunSetup(const char *storagepath, +qemuTPMEmulatorRunSetup(const char *source_path, const char *vmname, const unsigned char *vmuuid, bool privileged, @@ -413,7 +413,7 @@ qemuTPMEmulatorRunSetup(const char *storagepath, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--vmid", vmid, "--logfile", logfile, "--createek", @@ -424,7 +424,7 @@ qemuTPMEmulatorRunSetup(const char *storagepath, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--logfile", logfile, "--overwrite", NULL); @@ -465,7 +465,7 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * qemuTPMEmulatorReconfigure * * - * @storagepath: path to the directory for TPM state + * @source_path: 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 @@ -478,7 +478,7 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * Reconfigure the active PCR banks of a TPM 2. */ static int -qemuTPMEmulatorReconfigure(const char *storagepath, +qemuTPMEmulatorReconfigure(const char *source_path, uid_t swtpm_user, gid_t swtpm_group, virBitmap *activePcrBanks, @@ -510,7 +510,7 @@ qemuTPMEmulatorReconfigure(const char *storagepath, return -1; virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--logfile", logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", @@ -570,7 +570,7 @@ 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.source_path, sharedFilesystems) == 1; if (incomingMigration && on_shared_storage) create_storage = false; @@ -582,7 +582,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, secretuuid = tpm->data.emulator.secretuuid; if (created && - qemuTPMEmulatorRunSetup(tpm->data.emulator.storagepath, vmname, vmuuid, + qemuTPMEmulatorRunSetup(tpm->data.emulator.source_path, vmname, vmuuid, privileged, swtpm_user, swtpm_group, tpm->data.emulator.logfile, tpm->data.emulator.version, @@ -590,7 +590,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, goto error; if (!incomingMigration && - qemuTPMEmulatorReconfigure(tpm->data.emulator.storagepath, + qemuTPMEmulatorReconfigure(tpm->data.emulator.source_path, swtpm_user, swtpm_group, tpm->data.emulator.activePcrBanks, tpm->data.emulator.logfile, @@ -610,7 +610,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, virCommandAddArg(cmd, "--tpmstate"); virCommandAddArgFormat(cmd, "dir=%s,mode=0600", - tpm->data.emulator.storagepath); + tpm->data.emulator.source_path); virCommandAddArg(cmd, "--log"); if (tpm->data.emulator.debug != 0) @@ -723,8 +723,8 @@ qemuTPMEmulatorInitPaths(virDomainTPMDef *tpm, virUUIDFormat(uuid, uuidstr); - if (!tpm->data.emulator.storagepath && - !(tpm->data.emulator.storagepath = + if (!tpm->data.emulator.source_path && + !(tpm->data.emulator.source_path = qemuTPMEmulatorStorageBuildPath(swtpmStorageDir, uuidstr, tpm->data.emulator.version))) return -1; @@ -759,7 +759,7 @@ qemuTPMEmulatorCleanupHost(virQEMUDriver *driver, * storage. */ if (outgoingMigration && - virFileIsSharedFS(tpm->data.emulator.storagepath, cfg->sharedFilesystems) == 1) + virFileIsSharedFS(tpm->data.emulator.source_path, cfg->sharedFilesystems) == 1) return; /* @@ -1040,7 +1040,7 @@ qemuTPMHasSharedStorage(virQEMUDriver *driver, switch (tpm->type) { case VIR_DOMAIN_TPM_TYPE_EMULATOR: - return virFileIsSharedFS(tpm->data.emulator.storagepath, + return virFileIsSharedFS(tpm->data.emulator.source_path, cfg->sharedFilesystems) == 1; case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: case VIR_DOMAIN_TPM_TYPE_EXTERNAL: diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 8f567d5488..18daa521d1 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -3726,7 +3726,7 @@ virSecuritySELinuxSetTPMLabels(virSecurityManager *mgr, if (setTPMStateLabel) { ret = virSecuritySELinuxSetFileLabels(mgr, - def->tpms[i]->data.emulator.storagepath, + def->tpms[i]->data.emulator.source_path, seclabel); } @@ -3756,14 +3756,14 @@ virSecuritySELinuxRestoreTPMLabels(virSecurityManager *mgr, if (restoreTPMStateLabel) { ret = virSecuritySELinuxRestoreFileLabels(mgr, - def->tpms[i]->data.emulator.storagepath); + def->tpms[i]->data.emulator.source_path); } else { /* Even if we're not restoring the original label for the * TPM state directory, we should still forget any * remembered label so that a subsequent attempt at TPM * startup will not fail due to the state directory being * considered as still in use */ - virSecuritySELinuxForgetLabels(def->tpms[i]->data.emulator.storagepath); + virSecuritySELinuxForgetLabels(def->tpms[i]->data.emulator.source_path); } if (ret == 0 && -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> Learn to parse a file path for the TPM state. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> --- docs/formatdomain.rst | 21 ++++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 73 insertions(+) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 3253a28e5a..9104c95a4a 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8181,6 +8181,27 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend. +``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + When specified, it is the user's responsability to prevent files from being + used by multiple VMs or emulators (swtpm will also use advisory locking). If + not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage. + + :since:`Since v10.9.0` + ``persistent_state`` The ``persistent_state`` attribute indicates whether 'swtpm' TPM state is kept or not when a transient domain is powered off or undefined. This diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 284a3815b3..9dd8b6b55d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1322,6 +1322,12 @@ VIR_ENUM_IMPL(virDomainTPMVersion, "2.0", ); +VIR_ENUM_IMPL(virDomainTPMSourceType, + VIR_DOMAIN_TPM_SOURCE_TYPE_LAST, + "default", + "file", +); + VIR_ENUM_IMPL(virDomainTPMPcrBank, VIR_DOMAIN_TPM_PCR_BANK_LAST, "sha1", @@ -10784,6 +10790,7 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, int nbackends; int nnodes; size_t i; + xmlNodePtr source_node = NULL; g_autofree char *path = NULL; g_autofree char *secretuuid = NULL; g_autofree char *persistent_state = NULL; @@ -10857,6 +10864,22 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, def->data.emulator.hassecretuuid = true; } + source_node = virXPathNode("./backend/source", ctxt); + if (source_node) { + if (virXMLPropEnum(source_node, "type", + virDomainTPMSourceTypeTypeFromString, + VIR_XML_PROP_NONZERO, + &def->data.emulator.source_type) < 0) + goto error; + path = virXMLPropString(source_node, "path"); + if (!path) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing TPM source path")); + goto error; + } + def->data.emulator.source_path = g_steal_pointer(&path); + } + persistent_state = virXMLPropString(backends[0], "persistent_state"); if (persistent_state) { if (virStringParseYesNo(persistent_state, @@ -25070,6 +25093,11 @@ virDomainTPMDefFormat(virBuffer *buf, virXMLFormatElement(&backendChildBuf, "active_pcr_banks", NULL, &activePcrBanksBuf); } + if (def->data.emulator.source_type != VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT) { + virBufferAsprintf(&backendChildBuf, "<source type='%s'", + virDomainTPMSourceTypeTypeToString(def->data.emulator.source_type)); + virBufferEscapeString(&backendChildBuf, " path='%s'/>\n", def->data.emulator.source_path); + } 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 6b27322e3e..7a70f68177 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1463,6 +1463,13 @@ typedef enum { VIR_DOMAIN_TPM_PCR_BANK_LAST } virDomainPcrBank; +typedef enum { + VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT = 0, + VIR_DOMAIN_TPM_SOURCE_TYPE_FILE, + + VIR_DOMAIN_TPM_SOURCE_TYPE_LAST +} virDomainTPMSourceType; + #define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0" struct _virDomainTPMDef { @@ -1478,6 +1485,7 @@ struct _virDomainTPMDef { struct { virDomainTPMVersion version; virDomainChrSourceDef *source; + virDomainTPMSourceType source_type; char *source_path; char *logfile; unsigned int debug; @@ -4277,6 +4285,7 @@ VIR_ENUM_DECL(virDomainRNGBackend); VIR_ENUM_DECL(virDomainTPMModel); VIR_ENUM_DECL(virDomainTPMBackend); VIR_ENUM_DECL(virDomainTPMVersion); +VIR_ENUM_DECL(virDomainTPMSourceType); VIR_ENUM_DECL(virDomainTPMPcrBank); VIR_ENUM_DECL(virDomainMemoryModel); VIR_ENUM_DECL(virDomainMemoryBackingModel); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index efb5f00d77..8d91fb0dd6 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-source"/> </interleave> <optional> <attribute name="persistent_state"> @@ -5981,6 +5982,19 @@ </optional> </define> + <define name="tpm-backend-emulator-source"> + <optional> + <element name="source"> + <attribute name="type"> + <value>file</value> + </attribute> + <attribute name="path"> + <ref name="absFilePath"/> + </attribute> + </element> + </optional> + </define> + <define name="tpm-backend-emulator-encryption"> <optional> <element name="encryption"> diff --git a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml index 8a613db456..3d6300f544 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml @@ -34,6 +34,7 @@ <sha256/> <sha512/> </active_pcr_banks> + <source type='file' path='/path/to/state'/> </backend> </tpm> <audio id='1' type='none'/> -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> Learn to parse a directory for the TPM state. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> --- docs/formatdomain.rst | 3 ++- src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 1 + src/conf/schemas/domaincommon.rng | 24 ++++++++++++++----- .../qemuxmlconfdata/tpm-emulator-tpm2-enc.xml | 1 + 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 9104c95a4a..fccff47d08 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8195,7 +8195,8 @@ Example: usage of the TPM Emulator ``type`` The type of storage. It's possible to provide "file" to utilize a single - file or block device where the TPM state will be stored. + file or block device where the TPM state will be stored, or "dir" for the + directory where the files will be stored. ``path`` The path to the TPM state storage. diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9dd8b6b55d..3a32e50890 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1326,6 +1326,7 @@ VIR_ENUM_IMPL(virDomainTPMSourceType, VIR_DOMAIN_TPM_SOURCE_TYPE_LAST, "default", "file", + "dir", ); VIR_ENUM_IMPL(virDomainTPMPcrBank, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 7a70f68177..45c52107e8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1466,6 +1466,7 @@ typedef enum { typedef enum { VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT = 0, VIR_DOMAIN_TPM_SOURCE_TYPE_FILE, + VIR_DOMAIN_TPM_SOURCE_TYPE_DIR, VIR_DOMAIN_TPM_SOURCE_TYPE_LAST } virDomainTPMSourceType; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 8d91fb0dd6..8360eeae3f 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -5985,12 +5985,24 @@ <define name="tpm-backend-emulator-source"> <optional> <element name="source"> - <attribute name="type"> - <value>file</value> - </attribute> - <attribute name="path"> - <ref name="absFilePath"/> - </attribute> + <choice> + <group> + <attribute name="type"> + <value>file</value> + </attribute> + <attribute name="path"> + <ref name="absFilePath"/> + </attribute> + </group> + <group> + <attribute name="type"> + <value>dir</value> + </attribute> + <attribute name="path"> + <ref name="absDirPath"/> + </attribute> + </group> + </choice> </element> </optional> </define> diff --git a/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml b/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml index 9c2279b28b..e0c657645d 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml @@ -30,6 +30,7 @@ <tpm model='tpm-tis'> <backend type='emulator' version='2.0'> <encryption secret='32ee7e76-2178-47a1-ab7b-269e6e348015'/> + <source type='dir' path='/some/dir'/> </backend> </tpm> <audio id='1' type='none'/> -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> When swtpm reports "nvram-backend-dir", it can accepts a single file or block device where TPM state will be stored. --tpmstate must be backend-uri=file://<path>. Teach the storage to use custom directory or file source location. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> --- src/qemu/qemu_tpm.c | 77 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 2d9cd59643..bf94b6ac0d 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -340,9 +340,28 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, } +static char * +qemuTPMGetSwtpmSetupStateArg(const virDomainTPMSourceType source_type, + const char *source_path) +{ + switch (source_type) { + case VIR_DOMAIN_TPM_SOURCE_TYPE_FILE: + /* the file:// prefix is supported since swtpm_setup 0.7.0 */ + /* assume the capability check for swtpm is redundant. */ + return g_strdup_printf("file://%s", source_path); + case VIR_DOMAIN_TPM_SOURCE_TYPE_DIR: + case VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT: + case VIR_DOMAIN_TPM_SOURCE_TYPE_LAST: + default: + return g_strdup_printf("%s", source_path); + } +} + + /* * qemuTPMEmulatorRunSetup * + * @source_type: type of storage * @source_path: path to the directory for TPM state * @vmname: the name of the VM * @vmuuid: the UUID of the VM @@ -360,7 +379,8 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, * certificates for it. */ static int -qemuTPMEmulatorRunSetup(const char *source_path, +qemuTPMEmulatorRunSetup(const virDomainTPMSourceType source_type, + const char *source_path, const char *vmname, const unsigned char *vmuuid, bool privileged, @@ -376,6 +396,7 @@ qemuTPMEmulatorRunSetup(const char *source_path, char uuid[VIR_UUID_STRING_BUFLEN]; g_autofree char *vmid = NULL; g_autofree char *swtpm_setup = virTPMGetSwtpmSetup(); + g_autofree char *tpm_state = qemuTPMGetSwtpmSetupStateArg(source_type, source_path); if (!swtpm_setup) return -1; @@ -413,7 +434,7 @@ qemuTPMEmulatorRunSetup(const char *source_path, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--vmid", vmid, "--logfile", logfile, "--createek", @@ -424,7 +445,7 @@ qemuTPMEmulatorRunSetup(const char *source_path, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--logfile", logfile, "--overwrite", NULL); @@ -465,6 +486,7 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * qemuTPMEmulatorReconfigure * * + * @source_type: type of storage * @source_path: 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' @@ -478,7 +500,8 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * Reconfigure the active PCR banks of a TPM 2. */ static int -qemuTPMEmulatorReconfigure(const char *source_path, +qemuTPMEmulatorReconfigure(const virDomainTPMSourceType source_type, + const char *source_path, uid_t swtpm_user, gid_t swtpm_group, virBitmap *activePcrBanks, @@ -490,6 +513,7 @@ qemuTPMEmulatorReconfigure(const char *source_path, int exitstatus; g_autofree char *activePcrBanksStr = NULL; g_autofree char *swtpm_setup = virTPMGetSwtpmSetup(); + g_autofree char *tpm_state = qemuTPMGetSwtpmSetupStateArg(source_type, source_path); if (!swtpm_setup) return -1; @@ -510,7 +534,7 @@ qemuTPMEmulatorReconfigure(const char *source_path, return -1; virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--logfile", logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", @@ -557,6 +581,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, { g_autoptr(virCommand) cmd = NULL; bool created = false; + bool run_setup = false; g_autofree char *swtpm = virTPMGetSwtpm(); int pwdfile_fd = -1; int migpwdfile_fd = -1; @@ -567,6 +592,18 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, if (!swtpm) return NULL; + if (tpm->data.emulator.source_type == VIR_DOMAIN_TPM_SOURCE_TYPE_FILE) { + if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_DIR)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("%1$s does not support file storage"), + swtpm); + goto error; + } + create_storage = false; + /* setup is run with --not-overwrite */ + run_setup = true; + } + /* Do not create storage and run swtpm_setup on incoming migration over * shared storage */ @@ -574,15 +611,18 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, if (incomingMigration && on_shared_storage) create_storage = false; - if (create_storage && - qemuTPMEmulatorCreateStorage(tpm, &created, swtpm_user, swtpm_group) < 0) - return NULL; + if (create_storage) { + if (qemuTPMEmulatorCreateStorage(tpm, &created, swtpm_user, swtpm_group) < 0) + return NULL; + run_setup = created; + } if (tpm->data.emulator.hassecretuuid) secretuuid = tpm->data.emulator.secretuuid; - if (created && - qemuTPMEmulatorRunSetup(tpm->data.emulator.source_path, vmname, vmuuid, + if (run_setup && + qemuTPMEmulatorRunSetup(tpm->data.emulator.source_type, + tpm->data.emulator.source_path, vmname, vmuuid, privileged, swtpm_user, swtpm_group, tpm->data.emulator.logfile, tpm->data.emulator.version, @@ -590,7 +630,8 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, goto error; if (!incomingMigration && - qemuTPMEmulatorReconfigure(tpm->data.emulator.source_path, + qemuTPMEmulatorReconfigure(tpm->data.emulator.source_type, + tpm->data.emulator.source_path, swtpm_user, swtpm_group, tpm->data.emulator.activePcrBanks, tpm->data.emulator.logfile, @@ -609,8 +650,18 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, tpm->data.emulator.source->data.nix.path); virCommandAddArg(cmd, "--tpmstate"); - virCommandAddArgFormat(cmd, "dir=%s,mode=0600", - tpm->data.emulator.source_path); + switch (tpm->data.emulator.source_type) { + case VIR_DOMAIN_TPM_SOURCE_TYPE_FILE: + virCommandAddArgFormat(cmd, "backend-uri=file://%s", + tpm->data.emulator.source_path); + break; + case VIR_DOMAIN_TPM_SOURCE_TYPE_DIR: + case VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT: + case VIR_DOMAIN_TPM_SOURCE_TYPE_LAST: + virCommandAddArgFormat(cmd, "dir=%s,mode=0600", + tpm->data.emulator.source_path); + break; + } virCommandAddArg(cmd, "--log"); if (tpm->data.emulator.debug != 0) -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> With upcoming v0.10 swtpm (commit https://github.com/stefanberger/swtpm/commit/aa483aeb6df87ed56ccf3d5778d6fd8...), file locking with "lock" option is now supported and reflected in "tpmstate-opt-lock" capability. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> --- src/qemu/qemu_tpm.c | 11 +++++++++-- src/util/virtpm.c | 1 + src/util/virtpm.h | 1 + tests/testutilsqemu.c | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index bf94b6ac0d..edd10ca2f6 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -344,16 +344,23 @@ static char * qemuTPMGetSwtpmSetupStateArg(const virDomainTPMSourceType source_type, const char *source_path) { + const char *lock = ",lock"; + + if (!virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_TPMSTATE_OPT_LOCK)) { + VIR_WARN("This swtpm version doesn't support explicit locking"); + lock = ""; + } + switch (source_type) { case VIR_DOMAIN_TPM_SOURCE_TYPE_FILE: /* the file:// prefix is supported since swtpm_setup 0.7.0 */ /* assume the capability check for swtpm is redundant. */ - return g_strdup_printf("file://%s", source_path); + return g_strdup_printf("file://%s%s", source_path, lock); case VIR_DOMAIN_TPM_SOURCE_TYPE_DIR: case VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT: case VIR_DOMAIN_TPM_SOURCE_TYPE_LAST: default: - return g_strdup_printf("%s", source_path); + return g_strdup_printf("%s%s", source_path, lock); } } diff --git a/src/util/virtpm.c b/src/util/virtpm.c index 298caaad80..8dcd3f90d9 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -52,6 +52,7 @@ VIR_ENUM_IMPL(virTPMSwtpmSetupFeature, "cmdarg-reconfigure-pcr-banks", "tpm-1.2", "tpm-2.0", + "tpmstate-opt-lock", ); /** diff --git a/src/util/virtpm.h b/src/util/virtpm.h index 99dbcc1dc8..279cb7e976 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -44,6 +44,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_TPMSTATE_OPT_LOCK, VIR_TPM_SWTPM_SETUP_FEATURE_LAST } virTPMSwtpmSetupFeature; diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 4daee432e5..f40bfa873c 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_TPMSTATE_OPT_LOCK: case VIR_TPM_SWTPM_SETUP_FEATURE_LAST: break; } -- 2.47.0

ping! On Tue, Oct 22, 2024 at 6:15 PM <marcandre.lureau@redhat.com> wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Hi,
When swtpm capabilities reports "nvram-backend-dir", it can accepts a single file or block device where TPM state will be stored.
--tpmstate must be backend-uri=file://.
v5: - fix indentation - update doc about state sharing/locking - add r-b from Stefan
v4: - add "qemu: explicit swtpm state locking" - add r-b from Stefan, first patch only atm
v3: - changed to <source type='file/dir' path='..'/>
v2: - add <source dir='..'/> support as well (Daniel)
Related: https://issues.redhat.com/browse/CNV-35250
Marc-André Lureau (6): util: check swtpm nvram-backend-{dir,file} capabilities tpm: rename 'storagepath' to 'source_path' schema: add TPM emulator <source type='file' path='..'> schema: add TPM emulator <source type='dir' path='..'> qemu_tpm: handle file/block storage source qemu: explicit swtpm state locking
docs/formatdomain.rst | 22 ++++ src/conf/domain_conf.c | 31 ++++- src/conf/domain_conf.h | 12 +- src/conf/schemas/domaincommon.rng | 26 ++++ src/qemu/qemu_tpm.c | 114 +++++++++++++----- src/security/security_selinux.c | 6 +- src/util/virtpm.c | 3 + src/util/virtpm.h | 3 + .../qemuxmlconfdata/tpm-emulator-tpm2-enc.xml | 1 + tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + tests/testutilsqemu.c | 1 + 11 files changed, 187 insertions(+), 33 deletions(-)
-- 2.47.0

On Wed, Oct 30, 2024 at 03:35:57PM +0400, Marc-André Lureau wrote:
ping!
Pushed now
On Tue, Oct 22, 2024 at 6:15 PM <marcandre.lureau@redhat.com> wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Hi,
When swtpm capabilities reports "nvram-backend-dir", it can accepts a single file or block device where TPM state will be stored.
--tpmstate must be backend-uri=file://.
v5: - fix indentation - update doc about state sharing/locking - add r-b from Stefan
v4: - add "qemu: explicit swtpm state locking" - add r-b from Stefan, first patch only atm
v3: - changed to <source type='file/dir' path='..'/>
v2: - add <source dir='..'/> support as well (Daniel)
Related: https://issues.redhat.com/browse/CNV-35250
Marc-André Lureau (6): util: check swtpm nvram-backend-{dir,file} capabilities tpm: rename 'storagepath' to 'source_path' schema: add TPM emulator <source type='file' path='..'> schema: add TPM emulator <source type='dir' path='..'> qemu_tpm: handle file/block storage source qemu: explicit swtpm state locking
docs/formatdomain.rst | 22 ++++ src/conf/domain_conf.c | 31 ++++- src/conf/domain_conf.h | 12 +- src/conf/schemas/domaincommon.rng | 26 ++++ src/qemu/qemu_tpm.c | 114 +++++++++++++----- src/security/security_selinux.c | 6 +- src/util/virtpm.c | 3 + src/util/virtpm.h | 3 + .../qemuxmlconfdata/tpm-emulator-tpm2-enc.xml | 1 + tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + tests/testutilsqemu.c | 1 + 11 files changed, 187 insertions(+), 33 deletions(-)
-- 2.47.0

Hi Martin On Tue, Nov 5, 2024 at 6:42 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Wed, Oct 30, 2024 at 03:35:57PM +0400, Marc-André Lureau wrote:
ping!
Pushed now
thanks, though you should have updated the Since v10.9.0 tag in docs/formatdomain.rst
On Tue, Oct 22, 2024 at 6:15 PM <marcandre.lureau@redhat.com> wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Hi,
When swtpm capabilities reports "nvram-backend-dir", it can accepts a single file or block device where TPM state will be stored.
--tpmstate must be backend-uri=file://.
v5: - fix indentation - update doc about state sharing/locking - add r-b from Stefan
v4: - add "qemu: explicit swtpm state locking" - add r-b from Stefan, first patch only atm
v3: - changed to <source type='file/dir' path='..'/>
v2: - add <source dir='..'/> support as well (Daniel)
Related: https://issues.redhat.com/browse/CNV-35250
Marc-André Lureau (6): util: check swtpm nvram-backend-{dir,file} capabilities tpm: rename 'storagepath' to 'source_path' schema: add TPM emulator <source type='file' path='..'> schema: add TPM emulator <source type='dir' path='..'> qemu_tpm: handle file/block storage source qemu: explicit swtpm state locking
docs/formatdomain.rst | 22 ++++ src/conf/domain_conf.c | 31 ++++- src/conf/domain_conf.h | 12 +- src/conf/schemas/domaincommon.rng | 26 ++++ src/qemu/qemu_tpm.c | 114 +++++++++++++----- src/security/security_selinux.c | 6 +- src/util/virtpm.c | 3 + src/util/virtpm.h | 3 + .../qemuxmlconfdata/tpm-emulator-tpm2-enc.xml | 1 + tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + tests/testutilsqemu.c | 1 + 11 files changed, 187 insertions(+), 33 deletions(-)
-- 2.47.0

On a Tuesday in 2024, Marc-André Lureau wrote:
Hi Martin
On Tue, Nov 5, 2024 at 6:42 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Wed, Oct 30, 2024 at 03:35:57PM +0400, Marc-André Lureau wrote:
ping!
Pushed now
thanks, though you should have updated the Since v10.9.0 tag in docs/formatdomain.rst
Done. Jano
participants (4)
-
Ján Tomko
-
Marc-André Lureau
-
marcandre.lureau@redhat.com
-
Martin Kletzander