]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
qemu: Read back the profile name after creation of a TPM instance
authorStefan Berger <stefanb@linux.ibm.com>
Wed, 13 Nov 2024 17:39:51 +0000 (12:39 -0500)
committerMichal Privoznik <mprivozn@redhat.com>
Mon, 18 Nov 2024 07:46:49 +0000 (08:46 +0100)
Get the JSON profile that the swtpm instance was created with from the
output of 'swtpm socket --tpm2 --print-info 0x20 --tpmstate ...'. Get the
name of the profile from the JSON and set it in the current and persistent
emulator descriptions as 'name' attribute and have the persistent
description stored with this update. The user should avoid setting this
'name' attribute since it is meant to be read-only. The following is
an example of how the XML could look like:

  <profile source='local:restricted' name='custom:restricted'/>

If the user provided no profile node, and therefore swtpm_setup picked its
default profile, the XML may now shows the 'name' attribute with the name
of the profile. This makes the 'source' attribute now optional.

  <profile name='default-v1'/>

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
docs/formatdomain.rst
src/conf/domain_conf.c
src/conf/domain_conf.h
src/conf/schemas/domaincommon.rng
src/qemu/qemu_extdevice.c
src/qemu/qemu_tpm.c
src/qemu/qemu_tpm.h
src/util/virtpm.c
src/util/virtpm.h
tests/qemuxmlconfdata/tpm-emulator-crb-profile.xml

index b8a0f59ad33e97ac2fa955a006a380b0efde77b7..f0b79d5f8d6b976e72ac3ccf34ddd8f84b720b32 100644 (file)
@@ -8135,7 +8135,7 @@ Example: usage of the TPM Emulator
            <active_pcr_banks>
                <sha256/>
            </active_pcr_banks>
-           <profile source='local:restricted' removeDisabled='check'/>
+           <profile source='local:restricted' removeDisabled='check' name='custom:restricted'/>
          </backend>
        </tpm>
      </devices>
@@ -8233,12 +8233,14 @@ Example: usage of the TPM Emulator
 ``profile``
    The ``profile`` node is used to set a profile for a TPM 2.0 given in the
    source attribute. This profile will be set when the TPM is initially
-   created and after that cannot be changed anymore. If no profile is provided,
-   then swtpm will use the latest built-in 'default' profile or the default
-   profile set in swtpm_setup.conf. Otherwise swtpm_setup will search for a
-   profile with the given name with appended .json suffix in a configurable
-   local and then in a distro directory. If none could be found in either, it
-   will fall back trying to use a built-in one.
+   created and after that cannot be changed anymore. Once a profile has been
+   set the name attribute will be updated with the name of the profile that
+   is running. If no profile is provided, then swtpm will use the latest
+   built-in 'default' profile or the default profile set in swtpm_setup.conf.
+   Otherwise swtpm_setup will search for a profile with the given name with
+   appended .json suffix in a configurable local and then in a distro
+   directory. If none could be found in either, it will fall back trying to
+   use a built-in one.
 
    The built-in 'null' profile provides backwards compatibility with
    libtpms v0.9 but also restricts the user to use only TPM features that were
index bec44eece15576477a450650e4ef81ba82176a5b..295707ec1f157193cfb36ad619d81b799e534a67 100644 (file)
@@ -3479,6 +3479,7 @@ void virDomainTPMDefFree(virDomainTPMDef *def)
         g_free(def->data.emulator.logfile);
         virBitmapFree(def->data.emulator.activePcrBanks);
         g_free(def->data.emulator.profile.source);
+        g_free(def->data.emulator.profile.name);
         break;
     case VIR_DOMAIN_TPM_TYPE_EXTERNAL:
         virObjectUnref(def->data.external.source);
@@ -10925,10 +10926,7 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt,
 
         if ((profile = virXPathNode("./backend/profile[1]", ctxt))) {
             def->data.emulator.profile.source = virXMLPropString(profile, "source");
-            if (!def->data.emulator.profile.source) {
-                virReportError(VIR_ERR_XML_ERROR, "%s", _("missing profile source"));
-                goto error;
-            }
+            def->data.emulator.profile.name = virXMLPropString(profile, "name");
             if (virXMLPropEnum(profile, "removeDisabled",
                                virDomainTPMProfileRemoveDisabledTypeFromString,
                                VIR_XML_PROP_NONZERO,
@@ -25139,16 +25137,23 @@ virDomainTPMDefFormat(virBuffer *buf,
                               virDomainTPMSourceTypeTypeToString(def->data.emulator.source_type));
             virBufferEscapeString(&backendChildBuf, " path='%s'/>\n", def->data.emulator.source_path);
         }
-        if (def->data.emulator.profile.source) {
+        if (def->data.emulator.profile.source ||
+            def->data.emulator.profile.name) {
             g_auto(virBuffer) profileAttrBuf = VIR_BUFFER_INITIALIZER;
 
-            virBufferAsprintf(&profileAttrBuf, " source='%s'",
-                              def->data.emulator.profile.source);
+            if (def->data.emulator.profile.source) {
+                virBufferAsprintf(&profileAttrBuf, " source='%s'",
+                                  def->data.emulator.profile.source);
+            }
             if (def->data.emulator.profile.removeDisabled) {
                virBufferAsprintf(&profileAttrBuf, " removeDisabled='%s'",
                                  virDomainTPMProfileRemoveDisabledTypeToString(def->data.emulator.profile.removeDisabled));
             }
 
+            if (def->data.emulator.profile.name) {
+                virBufferAsprintf(&profileAttrBuf, " name='%s'",
+                                  def->data.emulator.profile.name);
+            }
             virXMLFormatElement(&backendChildBuf, "profile", &profileAttrBuf, NULL);
         }
         break;
index 5959e82262ee08fb769c6e591f3077dfe30441f7..a187ab40833fbc5f0f4c5e90e161b5be1280b7ec 100644 (file)
@@ -1494,6 +1494,7 @@ struct _virDomainTPMEmulatorDef {
     virBitmap *activePcrBanks;
     struct {
         char *source; /* 'source' profile was created from */
+        char *name;   /* name read from active profile */
         virDomainTPMProfileRemoveDisabled removeDisabled;
     } profile;
 };
index 91711470f1f1260143bf864031c1177c01398739..bfd0044805fdb5c6938a0b1a7bb8c681b63dd758 100644 (file)
   <define name="tpm-backend-emulator-profile">
     <optional>
       <element name="profile">
-        <attribute name="source">
-          <ref name="profileName"/>
-        </attribute>
+        <optional>
+          <attribute name="source">
+            <ref name="profileName"/>
+          </attribute>
+        </optional>
         <optional>
           <attribute name="removeDisabled">
             <choice>
             </choice>
           </attribute>
         </optional>
+        <optional>
+          <attribute name="name">
+            <ref name="profileName"/>
+          </attribute>
+        </optional>
       </element>
     </optional>
   </define>
index dc1bb56237a5f94847788e589b4b0438a580a4f1..a6f31f977359a853b18b0c7c0e798365620b08a3 100644 (file)
@@ -175,6 +175,7 @@ qemuExtDevicesStart(virQEMUDriver *driver,
                     virDomainObj *vm,
                     bool incomingMigration)
 {
+    virDomainDef *persistentDef = vm->newDef;
     virDomainDef *def = vm->def;
     size_t i;
 
@@ -189,9 +190,11 @@ qemuExtDevicesStart(virQEMUDriver *driver,
 
     for (i = 0; i < def->ntpms; i++) {
         virDomainTPMDef *tpm = def->tpms[i];
+        virDomainTPMDef *persistentTPMDef = persistentDef->tpms[i];
 
         if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR &&
-            qemuExtTPMStart(driver, vm, tpm, incomingMigration) < 0)
+            qemuExtTPMStart(driver, vm, tpm, persistentTPMDef,
+                            incomingMigration) < 0)
             return -1;
     }
 
index 6d56a845a4e380d5444be716471976b7877ba525..f223dcb9aeabbc6a4182f50d50b943eaf396f81f 100644 (file)
@@ -628,15 +628,89 @@ qemuTPMVirCommandSwtpmAddTPMState(virCommand *cmd,
     }
 }
 
+/* qemuTPMEmulatorUpdateProfileName:
+ *
+ * @emulator: TPM emulator definition
+ * @persistentTPMDef: TPM definition from the persistent domain definition
+ * @cfg: virQEMUDriverConfig
+ * @saveDef: whether caller should save the persistent domain def
+ */
+static int
+qemuTPMEmulatorUpdateProfileName(virDomainTPMEmulatorDef *emulator,
+                                 virDomainTPMDef *persistentTPMDef,
+                                 const virQEMUDriverConfig *cfg,
+                                 bool *saveDef)
+{
+    g_autoptr(virJSONValue) object = NULL;
+    g_autofree char *stderr_buf = NULL;
+    g_autofree char *stdout_buf = NULL;
+    g_autoptr(virCommand) cmd = NULL;
+    g_autofree char *swtpm = NULL;
+    virJSONValue *active_profile;
+    const char *profile_name;
+    int exitstatus;
+
+    if (emulator->version != VIR_DOMAIN_TPM_VERSION_2_0 ||
+        !virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PRINT_INFO))
+        return 0;
+
+    swtpm = virTPMGetSwtpm();
+    if (!swtpm)
+        return -1;
+
+    cmd = virCommandNew(swtpm);
+
+    virCommandSetUID(cmd, cfg->swtpm_user); /* should be uid of 'tss' or 'root' */
+    virCommandSetGID(cmd, cfg->swtpm_group);
+
+    virCommandAddArgList(cmd, "socket", "--print-info", "0x20", "--tpm2", NULL);
+
+    qemuTPMVirCommandSwtpmAddTPMState(cmd, emulator);
+
+    if (qemuTPMVirCommandSwtpmAddEncryption(cmd, emulator, swtpm) < 0)
+        return -1;
+
+    virCommandClearCaps(cmd);
+
+    virCommandSetOutputBuffer(cmd, &stdout_buf);
+    virCommandSetErrorBuffer(cmd, &stderr_buf);
+
+    if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Could not run '%1$s --print-info'. exitstatus: %2$d; stderr: %3$s"),
+                          swtpm, exitstatus, stderr_buf);
+        return -1;
+    }
+
+    if (!(object = virJSONValueFromString(stdout_buf)))
+        return -1;
+
+    if (!(active_profile = virJSONValueObjectGetObject(object, "ActiveProfile")))
+        return -1;
+
+    profile_name = virJSONValueObjectGetString(active_profile, "Name");
+
+    g_free(emulator->profile.name);
+    emulator->profile.name = g_strdup(profile_name);
+
+    *saveDef = true;
+    g_free(persistentTPMDef->data.emulator.profile.name);
+    persistentTPMDef->data.emulator.profile.name = g_strdup(profile_name);
+
+    return 0;
+}
+
 /*
  * qemuTPMEmulatorBuildCommand:
  *
  * @tpm: TPM definition
+ * @persistentTPMDef: TPM definition from the persistent domain definition
  * @vmname: The name of the VM
  * @vmuuid: The UUID of the VM
  * @privileged: whether we are running in privileged mode
  * @cfg: virQEMUDriverConfig
  * @incomingMigration: whether we have an incoming migration
+ * @saveDef: whether caller should save the persistent domain def
  *
  * Create the virCommand use for starting the emulator
  * Do some initializations on the way, such as creation of storage
@@ -644,11 +718,13 @@ qemuTPMVirCommandSwtpmAddTPMState(virCommand *cmd,
  */
 static virCommand *
 qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
+                            virDomainTPMDef *persistentTPMDef,
                             const char *vmname,
                             const unsigned char *vmuuid,
                             bool privileged,
                             const virQEMUDriverConfig *cfg,
-                            bool incomingMigration)
+                            bool incomingMigration,
+                            bool *saveDef)
 {
     g_autoptr(virCommand) cmd = NULL;
     bool created = false;
@@ -697,6 +773,11 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
                                 incomingMigration) < 0)
         goto error;
 
+    if (run_setup && !incomingMigration &&
+        qemuTPMEmulatorUpdateProfileName(&tpm->data.emulator, persistentTPMDef,
+                                         cfg, saveDef) < 0)
+        goto error;
+
     if (!incomingMigration &&
         qemuTPMEmulatorReconfigure(&tpm->data.emulator, cfg, secretuuid) < 0)
         goto error;
@@ -996,6 +1077,7 @@ qemuExtTPMEmulatorSetupCgroup(const char *swtpmStateDir,
  * @driver: QEMU driver
  * @vm: the domain object
  * @tpm: TPM definition
+ * @persistentTPMDef: TPM definition from persistent domain definition
  * @shortName: short and unique name of the domain
  * @incomingMigration: whether we have an incoming migration
  *
@@ -1008,6 +1090,7 @@ qemuTPMEmulatorStart(virQEMUDriver *driver,
                      virDomainObj *vm,
                      const char *shortName,
                      virDomainTPMDef *tpm,
+                     virDomainTPMDef *persistentTPMDef,
                      bool incomingMigration)
 {
     g_autoptr(virCommand) cmd = NULL;
@@ -1016,6 +1099,7 @@ qemuTPMEmulatorStart(virQEMUDriver *driver,
     g_autofree char *pidfile = NULL;
     virTimeBackOffVar timebackoff;
     const unsigned long long timeout = 1000; /* ms */
+    bool saveDef = false;
     pid_t pid = -1;
     bool lockMetadataException = false;
 
@@ -1024,12 +1108,18 @@ qemuTPMEmulatorStart(virQEMUDriver *driver,
     /* stop any left-over TPM emulator for this VM */
     qemuTPMEmulatorStop(cfg->swtpmStateDir, shortName);
 
-    if (!(cmd = qemuTPMEmulatorBuildCommand(tpm, vm->def->name, vm->def->uuid,
+    if (!(cmd = qemuTPMEmulatorBuildCommand(tpm, persistentTPMDef,
+                                            vm->def->name, vm->def->uuid,
                                             driver->privileged,
                                             cfg,
-                                            incomingMigration)))
+                                            incomingMigration,
+                                            &saveDef)))
         return -1;
 
+    if (saveDef &&
+        virDomainDefSave(vm->newDef, driver->xmlopt, cfg->configDir) < 0)
+        goto error;
+
     if (qemuExtDeviceLogCommand(driver, vm, cmd, "TPM Emulator") < 0)
         return -1;
 
@@ -1213,6 +1303,7 @@ int
 qemuExtTPMStart(virQEMUDriver *driver,
                 virDomainObj *vm,
                 virDomainTPMDef *tpm,
+                virDomainTPMDef *persistentTPMDef,
                 bool incomingMigration)
 {
     g_autofree char *shortName = virDomainDefGetShortName(vm->def);
@@ -1220,7 +1311,8 @@ qemuExtTPMStart(virQEMUDriver *driver,
     if (!shortName)
         return -1;
 
-    return qemuTPMEmulatorStart(driver, vm, shortName, tpm, incomingMigration);
+    return qemuTPMEmulatorStart(driver, vm, shortName, tpm, persistentTPMDef,
+                                incomingMigration);
 }
 
 
index 3071dc3f71d96ae9428ad043dac65dd28aa870fc..7096060a2aa2dcf5e3129ea5265e503d220df234 100644 (file)
@@ -44,9 +44,10 @@ void qemuExtTPMCleanupHost(virQEMUDriver *driver,
 int qemuExtTPMStart(virQEMUDriver *driver,
                     virDomainObj *vm,
                     virDomainTPMDef *def,
+                    virDomainTPMDef *persistentDefTPM,
                     bool incomingMigration)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
-    ATTRIBUTE_NONNULL(3)
+    ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4)
     G_GNUC_WARN_UNUSED_RESULT;
 
 void qemuExtTPMStop(virQEMUDriver *driver,
index 1c736b0229dd248895236dcf577e5b287324c1b3..4016ad8fc4d6aecf9e80d82bc9a8f4968b58aff3 100644 (file)
@@ -42,6 +42,7 @@ VIR_ENUM_IMPL(virTPMSwtpmFeature,
               "cmdarg-migration",
               "nvram-backend-dir",
               "nvram-backend-file",
+              "cmdarg-print-info",
 );
 
 VIR_ENUM_IMPL(virTPMSwtpmSetupFeature,
index 9ca09c2d803ca57a585583f5b94c8a594328032b..03fb92629a6fa9e6b0628b0b698bfa815c9afb5f 100644 (file)
@@ -33,6 +33,7 @@ typedef enum {
     VIR_TPM_SWTPM_FEATURE_CMDARG_MIGRATION,
     VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_DIR,
     VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_FILE,
+    VIR_TPM_SWTPM_FEATURE_CMDARG_PRINT_INFO,
 
     VIR_TPM_SWTPM_FEATURE_LAST
 } virTPMSwtpmFeature;
index b8473cd8948f3c52745e18cf398d3cf753b8a802..40224f43a7984dd9782b02db7f44fb46ea7a8b5e 100644 (file)
@@ -29,7 +29,7 @@
     <input type='keyboard' bus='ps2'/>
     <tpm model='tpm-crb'>
       <backend type='emulator' version='2.0'>
-        <profile source='local:restricted' removeDisabled='check'/>
+        <profile source='local:restricted' removeDisabled='check' name='custom:restricted'/>
       </backend>
     </tpm>
     <audio id='1' type='none'/>