:p
atchew
Login
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://. 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 (5): 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 docs/formatdomain.rst | 20 ++++ src/conf/domain_conf.c | 31 ++++- src/conf/domain_conf.h | 12 +- src/conf/schemas/domaincommon.rng | 26 +++++ src/qemu/qemu_tpm.c | 110 +++++++++++++----- src/security/security_selinux.c | 4 +- src/util/virtpm.c | 2 + src/util/virtpm.h | 2 + .../qemuxmlconfdata/tpm-emulator-tpm2-enc.xml | 1 + tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 10 files changed, 176 insertions(+), 33 deletions(-) -- 2.45.2.827.g557ae147e6
From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.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 XXXXXXX..XXXXXXX 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -XXX,XX +XXX,XX @@ 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.45.2.827.g557ae147e6
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> --- src/conf/domain_conf.c | 2 +- src/conf/domain_conf.h | 2 +- src/qemu/qemu_tpm.c | 48 ++++++++++++++++----------------- src/security/security_selinux.c | 4 +-- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -XXX,XX +XXX,XX @@ 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) { @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ 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)); } @@ -XXX,XX +XXX,XX @@ 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 @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorRunSetup(const char *storagepath, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--vmid", vmid, "--logfile", logfile, "--createek", @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorRunSetup(const char *storagepath, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--logfile", logfile, "--overwrite", NULL); @@ -XXX,XX +XXX,XX @@ 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 @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorReconfigure(const char *storagepath, return -1; virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--logfile", logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", @@ -XXX,XX +XXX,XX @@ 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) == 1; + on_shared_storage = virFileIsSharedFS(tpm->data.emulator.source_path) == 1; if (incomingMigration && on_shared_storage) create_storage = false; @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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) @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorCleanupHost(virDomainTPMDef *tpm, * storage. */ if (outgoingMigration && - virFileIsSharedFS(tpm->data.emulator.storagepath) == 1) + virFileIsSharedFS(tpm->data.emulator.source_path) == 1) return; /* @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorStart(virQEMUDriver *driver, virCommandSetErrorFD(cmd, &errfd); if (incomingMigration && - virFileIsSharedFS(tpm->data.emulator.storagepath) == 1) { + virFileIsSharedFS(tpm->data.emulator.source_path) == 1) { /* security labels must have been set up on source already */ setTPMStateLabel = false; } @@ -XXX,XX +XXX,XX @@ qemuTPMHasSharedStorage(virDomainDef *def) switch (tpm->type) { case VIR_DOMAIN_TPM_TYPE_EMULATOR: - return virFileIsSharedFS(tpm->data.emulator.storagepath) == 1; + return virFileIsSharedFS(tpm->data.emulator.source_path) == 1; case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: case VIR_DOMAIN_TPM_TYPE_EXTERNAL: case VIR_DOMAIN_TPM_TYPE_LAST: diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index XXXXXXX..XXXXXXX 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -XXX,XX +XXX,XX @@ virSecuritySELinuxSetTPMLabels(virSecurityManager *mgr, if (setTPMStateLabel) { ret = virSecuritySELinuxSetFileLabels(mgr, - def->tpms[i]->data.emulator.storagepath, + def->tpms[i]->data.emulator.source_path, seclabel); } @@ -XXX,XX +XXX,XX @@ virSecuritySELinuxRestoreTPMLabels(virSecurityManager *mgr, if (restoreTPMStateLabel) { ret = virSecuritySELinuxRestoreFileLabels(mgr, - def->tpms[i]->data.emulator.storagepath); + def->tpms[i]->data.emulator.source_path); } if (ret == 0 && -- 2.45.2.827.g557ae147e6
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> --- docs/formatdomain.rst | 19 ++++++++++++++ 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, 71 insertions(+) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -XXX,XX +XXX,XX @@ 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. + + 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.8.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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -XXX,XX +XXX,XX @@ 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", @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -XXX,XX +XXX,XX @@ 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 { @@ -XXX,XX +XXX,XX @@ struct _virDomainTPMDef { struct { virDomainTPMVersion version; virDomainChrSourceDef *source; + virDomainTPMSourceType source_type; char *source_path; char *logfile; unsigned int debug; @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -XXX,XX +XXX,XX @@ <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"> @@ -XXX,XX +XXX,XX @@ </optional> </define> + <define name="tpm-backend-emulator-source"> + <optional> + <element name="source"> + <attribute name="type"> + <value>file</value> + </attribute> + <attribute name="path"> + <ref name="filePath"/> + </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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml @@ -XXX,XX +XXX,XX @@ <sha256/> <sha512/> </active_pcr_banks> + <source type='file' path='/path/to/state'/> </backend> </tpm> <audio id='1' type='none'/> -- 2.45.2.827.g557ae147e6
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> --- 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 XXXXXXX..XXXXXXX 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -XXX,XX +XXX,XX @@ <define name="tpm-backend-emulator-source"> <optional> <element name="source"> - <attribute name="type"> - <value>file</value> - </attribute> - <attribute name="path"> - <ref name="filePath"/> - </attribute> + <choice> + <group> + <attribute name="type"> + <value>file</value> + </attribute> + <attribute name="path"> + <ref name="filePath"/> + </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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml @@ -XXX,XX +XXX,XX @@ <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.45.2.827.g557ae147e6
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> --- src/qemu/qemu_tpm.c | 78 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -XXX,XX +XXX,XX @@ 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: + return g_strdup_printf("%s", source_path); + } + + return NULL; +} + + /* * 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 @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorRunSetup(const char *source_path, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--vmid", vmid, "--logfile", logfile, "--createek", @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorRunSetup(const char *source_path, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--logfile", logfile, "--overwrite", NULL); @@ -XXX,XX +XXX,XX @@ 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' @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorReconfigure(const char *source_path, return -1; virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--logfile", logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ 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 */ @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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.45.2.827.g557ae147e6
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 XXXXXXX..XXXXXXX 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -XXX,XX +XXX,XX @@ 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) { @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ 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)); } @@ -XXX,XX +XXX,XX @@ 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 @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorRunSetup(const char *storagepath, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--vmid", vmid, "--logfile", logfile, "--createek", @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorRunSetup(const char *storagepath, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--logfile", logfile, "--overwrite", NULL); @@ -XXX,XX +XXX,XX @@ 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 @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorReconfigure(const char *storagepath, return -1; virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--logfile", logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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) @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorCleanupHost(virQEMUDriver *driver, * storage. */ if (outgoingMigration && - virFileIsSharedFS(tpm->data.emulator.storagepath, cfg->sharedFilesystems) == 1) + virFileIsSharedFS(tpm->data.emulator.source_path, cfg->sharedFilesystems) == 1) return; /* @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -XXX,XX +XXX,XX @@ virSecuritySELinuxSetTPMLabels(virSecurityManager *mgr, if (setTPMStateLabel) { ret = virSecuritySELinuxSetFileLabels(mgr, - def->tpms[i]->data.emulator.storagepath, + def->tpms[i]->data.emulator.source_path, seclabel); } @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -XXX,XX +XXX,XX @@ 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", @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -XXX,XX +XXX,XX @@ 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 { @@ -XXX,XX +XXX,XX @@ struct _virDomainTPMDef { struct { virDomainTPMVersion version; virDomainChrSourceDef *source; + virDomainTPMSourceType source_type; char *source_path; char *logfile; unsigned int debug; @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -XXX,XX +XXX,XX @@ <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"> @@ -XXX,XX +XXX,XX @@ </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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml @@ -XXX,XX +XXX,XX @@ <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 XXXXXXX..XXXXXXX 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -XXX,XX +XXX,XX @@ <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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml @@ -XXX,XX +XXX,XX @@ <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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -XXX,XX +XXX,XX @@ 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 @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorRunSetup(const char *source_path, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--vmid", vmid, "--logfile", logfile, "--createek", @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorRunSetup(const char *source_path, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--logfile", logfile, "--overwrite", NULL); @@ -XXX,XX +XXX,XX @@ 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' @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ qemuTPMEmulatorReconfigure(const char *source_path, return -1; virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--logfile", logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ 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 */ @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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/aa483aeb6df87ed56ccf3d5778d6fd8019089bda), 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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -XXX,XX +XXX,XX @@ 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