[PATCH v3 2/2] qemu: tpm: Extend TPM domain XML with PCR banks to activate

Stefan Berger stefanb at linux.ibm.com
Wed Nov 3 17:04:23 UTC 2021


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 at linux.ibm.com>
---
 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




More information about the libvir-list mailing list