Extend the TPM backend XML with a node 'active_pcr_banks' that allows a user to specify the PCR banks to activate before starting a VM. Valid choices for PCR banks are sha1, sha256, sha384 and sha512. When the XML node is provided, the set of active PCR banks is 'enforced' by running swtpm_setup before every start of the VM. The activation requires that swtpm_setup v0.7 or later is installed and may not have any effect otherwise. <tpm model='tpm-tis'> <backend type='emulator' version='2.0'> <active_pcr_banks> <sha256/> <sha384/> </active_pcr_banks> </backend> </tpm> Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2016599 Signed-off-by: Stefan Berger <stefanb@xxxxxxxxxxxxx> --- docs/formatdomain.rst | 12 +++ docs/schemas/domaincommon.rng | 30 ++++++ src/conf/domain_conf.c | 50 +++++++++- src/conf/domain_conf.h | 11 +++ src/libvirt_private.syms | 2 + src/qemu/qemu_tpm.c | 98 +++++++++++++++++++ src/util/virtpm.c | 1 + src/util/virtpm.h | 1 + tests/qemuxml2argvdata/tpm-emulator-tpm2.xml | 7 +- .../tpm-emulator-tpm2.x86_64-latest.xml | 7 +- 10 files changed, 215 insertions(+), 4 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 0651975c88..eb8c973cf1 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -7539,6 +7539,9 @@ Example: usage of the TPM Emulator <tpm model='tpm-tis'> <backend type='emulator' version='2.0'> <encryption secret='6dd3e4a5-1d76-44ce-961f-f119f5aad935'/> + <active_pcr_banks> + <sha256/> + </active_pcr_banks> </backend> </tpm> </devices> @@ -7598,6 +7601,15 @@ Example: usage of the TPM Emulator This attribute only works with the ``emulator`` backend. The accepted values are ``yes`` and ``no``. :since:`Since 7.0.0` +``active_pcr_banks`` + The ``active_pcr_banks`` node is used to define which of the PCR banks + of a TPM 2.0 to activate. Valid names are for example sha1, sha256, sha384, + and sha512. If this node is provided, the set of PCR banks are activated + before every start of a VM and this step is logged in the swtpm's log. + This attribute requires that swtpm_setup v0.7 or later is installed + and may not have any effect otherwise. The selection of PCR banks only works + with the ``emulator`` backend. since:`Since 7.10.0` + ``encryption`` The ``encryption`` element allows the state of a TPM emulator to be encrypted. The ``secret`` must reference a secret object that holds the diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 67df13d90d..4fe3e04af5 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -5323,6 +5323,7 @@ <value>emulator</value> </attribute> <ref name="tpm-backend-emulator-encryption"/> + <ref name="tpm-backend-emulator-active-pcr-banks"/> <optional> <attribute name="persistent_state"> <choice> @@ -5366,6 +5367,35 @@ </optional> </define> + <define name="tpm-backend-emulator-active-pcr-banks"> + <optional> + <element name="active_pcr_banks"> + <interleave> + <optional> + <element name="sha1"> + <text/> + </element> + </optional> + <optional> + <element name="sha256"> + <text/> + </element> + </optional> + <optional> + <element name="sha384"> + <text/> + </element> + </optional> + <optional> + <element name="sha512"> + <text/> + </element> + </optional> + </interleave> + </element> + </optional> + </define> + <define name="vsock"> <element name="vsock"> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 4644d18120..da0c64b460 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1258,6 +1258,14 @@ VIR_ENUM_IMPL(virDomainTPMVersion, "2.0", ); +VIR_ENUM_IMPL(virDomainTPMPcrBank, + VIR_DOMAIN_TPM_PCR_BANK_LAST, + "sha1", + "sha256", + "sha384", + "sha512", +); + VIR_ENUM_IMPL(virDomainIOMMUModel, VIR_DOMAIN_IOMMU_MODEL_LAST, "intel", @@ -11735,6 +11743,10 @@ virDomainSmartcardDefParseXML(virDomainXMLOption *xmlopt, * <tpm model='tpm-tis'> * <backend type='emulator' version='2.0'> * <encryption secret='32ee7e76-2178-47a1-ab7b-269e6e348015'/> + * <active_pcr_banks> + * <sha256/> + * <sha384/> + * </active_pcr_banks> * </backend> * </tpm> * @@ -11753,6 +11765,8 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, virDomainTPMDef *def; VIR_XPATH_NODE_AUTORESTORE(ctxt) int nbackends; + int nnodes; + size_t i; g_autofree char *path = NULL; g_autofree char *model = NULL; g_autofree char *backend = NULL; @@ -11760,6 +11774,8 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, g_autofree char *secretuuid = NULL; g_autofree char *persistent_state = NULL; g_autofree xmlNodePtr *backends = NULL; + g_autofree xmlNodePtr *nodes = NULL; + int bank; def = g_new0(virDomainTPMDef, 1); @@ -11841,6 +11857,19 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, goto error; } } + if (def->version == VIR_DOMAIN_TPM_VERSION_2_0) { + if ((nnodes = virXPathNodeSet("./backend/active_pcr_banks/*", ctxt, &nodes)) < 0) + break; + for (i = 0; i < nnodes; i++) { + if ((bank = virDomainTPMPcrBankTypeFromString((const char *)nodes[i]->name)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported PCR banks '%s'"), + nodes[i]->name); + goto error; + } + def->data.emulator.activePcrBanks |= (1 << bank); + } + } break; case VIR_DOMAIN_TPM_TYPE_LAST: goto error; @@ -25440,10 +25469,27 @@ virDomainTPMDefFormat(virBuffer *buf, virBufferAsprintf(buf, "<encryption secret='%s'/>\n", virUUIDFormat(def->data.emulator.secretuuid, uuidstr)); virBufferAdjustIndent(buf, -2); + } + if (def->data.emulator.activePcrBanks) { + size_t i; + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + virBufferAddLit(buf, "<active_pcr_banks>\n"); + virBufferAdjustIndent(buf, 2); + for (i = VIR_DOMAIN_TPM_PCR_BANK_SHA1; i < VIR_DOMAIN_TPM_PCR_BANK_LAST; i++) { + if ((def->data.emulator.activePcrBanks & (1 << i))) + virBufferAsprintf(buf, "<%s/>\n", + virDomainTPMPcrBankTypeToString(i)); + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</active_pcr_banks>\n"); + virBufferAdjustIndent(buf, -2); + } + if (def->data.emulator.hassecretuuid || + def->data.emulator.activePcrBanks) virBufferAddLit(buf, "</backend>\n"); - } else { + else virBufferAddLit(buf, "/>\n"); - } break; case VIR_DOMAIN_TPM_TYPE_LAST: break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index cb6d8975b8..ab9a7d66f8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1363,6 +1363,15 @@ typedef enum { VIR_DOMAIN_TPM_VERSION_LAST } virDomainTPMVersion; +typedef enum { + VIR_DOMAIN_TPM_PCR_BANK_SHA1, + VIR_DOMAIN_TPM_PCR_BANK_SHA256, + VIR_DOMAIN_TPM_PCR_BANK_SHA384, + VIR_DOMAIN_TPM_PCR_BANK_SHA512, + + VIR_DOMAIN_TPM_PCR_BANK_LAST +} virDomainPcrBank; + #define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0" struct _virDomainTPMDef { @@ -1381,6 +1390,7 @@ struct _virDomainTPMDef { unsigned char secretuuid[VIR_UUID_BUFLEN]; bool hassecretuuid; bool persistent_state; + unsigned int activePcrBanks; } emulator; } data; }; @@ -3941,6 +3951,7 @@ VIR_ENUM_DECL(virDomainRNGBackend); VIR_ENUM_DECL(virDomainTPMModel); VIR_ENUM_DECL(virDomainTPMBackend); VIR_ENUM_DECL(virDomainTPMVersion); +VIR_ENUM_DECL(virDomainTPMPcrBank); VIR_ENUM_DECL(virDomainMemoryModel); VIR_ENUM_DECL(virDomainMemoryBackingModel); VIR_ENUM_DECL(virDomainMemorySource); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 55ae7d5b6f..69ff1ca430 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -667,6 +667,8 @@ virDomainTPMBackendTypeToString; virDomainTPMDefFree; virDomainTPMModelTypeFromString; virDomainTPMModelTypeToString; +virDomainTPMPcrBankTypeFromString; +virDomainTPMPcrBankTypeToString; virDomainUSBDeviceDefForeach; virDomainVideoDefaultRAM; virDomainVideoDefClear; diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 94a0d3a1f9..b305313ad2 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -565,6 +565,96 @@ qemuTPMEmulatorRunSetup(const char *storagepath, } +static char * +qemuTPMPcrBankBitmapToStr(unsigned int pcrBanks) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + const char *comma = ""; + size_t i; + + for (i = VIR_DOMAIN_TPM_PCR_BANK_SHA1; i < VIR_DOMAIN_TPM_PCR_BANK_LAST; i++) { + if (pcrBanks & (1 << i)) { + virBufferAsprintf(&buf, "%s%s", + comma, virDomainTPMPcrBankTypeToString(i)); + comma = ","; + } + } + return virBufferContentAndReset(&buf); +} + + +/* + * qemuTPMEmulatorReconfigure + * + * + * @storagepath: 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 + * @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, + uid_t swtpm_user, + gid_t swtpm_group, + unsigned int activePcrBanks, + const char *logfile, + const virDomainTPMVersion tpmversion, + const unsigned char *secretuuid) +{ + g_autoptr(virCommand) cmd = NULL; + int exitstatus; + g_autofree char *activePcrBanksStr; + g_autofree char *swtpm_setup = virTPMGetSwtpmSetup(); + VIR_AUTOCLOSE pwdfile_fd = -1; + + if (!swtpm_setup) + return -1; + + if (tpmversion != VIR_DOMAIN_TPM_VERSION_2_0 || + (activePcrBanksStr = qemuTPMPcrBankBitmapToStr(activePcrBanks)) == NULL || + !virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_RECONFIGURE_PCR_BANKS)) + return 0; + + cmd = virCommandNew(swtpm_setup); + if (!cmd) + return -1; + + virCommandSetUID(cmd, swtpm_user); + virCommandSetGID(cmd, swtpm_group); + + virCommandAddArgList(cmd, "--tpm2", NULL); + + if (qemuTPMVirCommandAddEncryption(cmd, swtpm_setup, secretuuid) < 0) + return -1; + + virCommandAddArgList(cmd, + "--tpm-state", storagepath, + "--logfile", logfile, + "--pcr-banks", activePcrBanksStr, + "--reconfigure", + NULL); + + virCommandClearCaps(cmd); + + if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not run '%s --reconfigure'. exitstatus: %d; " + "Check error log '%s' for details."), + swtpm_setup, exitstatus, logfile); + return -1; + } + + return 0; +} + + /* * qemuTPMEmulatorBuildCommand: * @@ -619,6 +709,14 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, secretuuid, incomingMigration) < 0) goto error; + if (!incomingMigration && + qemuTPMEmulatorReconfigure(tpm->data.emulator.storagepath, + swtpm_user, swtpm_group, + tpm->data.emulator.activePcrBanks, + tpm->data.emulator.logfile, tpm->version, + secretuuid) < 0) + goto error; + unlink(tpm->data.emulator.source.data.nix.path); cmd = virCommandNew(swtpm); diff --git a/src/util/virtpm.c b/src/util/virtpm.c index 40d9272e66..7fa870b803 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -47,6 +47,7 @@ VIR_ENUM_IMPL(virTPMSwtpmSetupFeature, "cmdarg-pwdfile-fd", "cmdarg-create-config-files", "tpm12-not-need-root", + "cmdarg-reconfigure-pcr-banks", ); /** diff --git a/src/util/virtpm.h b/src/util/virtpm.h index b75eb84f31..defea6c106 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -40,6 +40,7 @@ typedef enum { VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_PWDFILE_FD, VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_CREATE_CONFIG_FILES, VIR_TPM_SWTPM_SETUP_FEATURE_TPM12_NOT_NEED_ROOT, + VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_RECONFIGURE_PCR_BANKS, VIR_TPM_SWTPM_SETUP_FEATURE_LAST } virTPMSwtpmSetupFeature; diff --git a/tests/qemuxml2argvdata/tpm-emulator-tpm2.xml b/tests/qemuxml2argvdata/tpm-emulator-tpm2.xml index 3e2f485ee7..68db8b9232 100644 --- a/tests/qemuxml2argvdata/tpm-emulator-tpm2.xml +++ b/tests/qemuxml2argvdata/tpm-emulator-tpm2.xml @@ -23,7 +23,12 @@ <input type='mouse' bus='ps2'/> <input type='keyboard' bus='ps2'/> <tpm model='tpm-tis'> - <backend type='emulator' version='2.0'/> + <backend type='emulator' version='2.0'> + <active_pcr_banks> + <sha256/> + <sha512/> + </active_pcr_banks> + </backend> </tpm> <memballoon model='virtio'/> </devices> diff --git a/tests/qemuxml2xmloutdata/tpm-emulator-tpm2.x86_64-latest.xml b/tests/qemuxml2xmloutdata/tpm-emulator-tpm2.x86_64-latest.xml index fe4e1aba19..edab6db123 100644 --- a/tests/qemuxml2xmloutdata/tpm-emulator-tpm2.x86_64-latest.xml +++ b/tests/qemuxml2xmloutdata/tpm-emulator-tpm2.x86_64-latest.xml @@ -28,7 +28,12 @@ <input type='mouse' bus='ps2'/> <input type='keyboard' bus='ps2'/> <tpm model='tpm-tis'> - <backend type='emulator' version='2.0'/> + <backend type='emulator' version='2.0'> + <active_pcr_banks> + <sha256/> + <sha512/> + </active_pcr_banks> + </backend> </tpm> <audio id='1' type='none'/> <memballoon model='virtio'> -- 2.31.1