qemu: tpm: Extend TPM domain XML with PCR banks to activate

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@linux.ibm.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Stefan Berger 2021-11-03 13:04:23 -04:00 committed by Michal Privoznik
parent 506c3a39d6
commit a5bbe1a8b6
10 changed files with 216 additions and 5 deletions

View File

@ -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

View File

@ -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">
<empty/>
</element>
</optional>
<optional>
<element name="sha256">
<empty/>
</element>
</optional>
<optional>
<element name="sha384">
<empty/>
</element>
</optional>
<optional>
<element name="sha512">
<empty/>
</element>
</optional>
</interleave>
</element>
</optional>
</define>
<define name="vsock">
<element name="vsock">
<optional>

View File

@ -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);
virBufferAddLit(buf, "</backend>\n");
} else {
virBufferAddLit(buf, "/>\n");
}
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
virBufferAddLit(buf, "/>\n");
break;
case VIR_DOMAIN_TPM_TYPE_LAST:
break;

View File

@ -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);

View File

@ -667,6 +667,8 @@ virDomainTPMBackendTypeToString;
virDomainTPMDefFree;
virDomainTPMModelTypeFromString;
virDomainTPMModelTypeToString;
virDomainTPMPcrBankTypeFromString;
virDomainTPMPcrBankTypeToString;
virDomainUSBDeviceDefForeach;
virDomainVideoDefaultRAM;
virDomainVideoDefClear;

View File

@ -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);

View File

@ -47,6 +47,7 @@ VIR_ENUM_IMPL(virTPMSwtpmSetupFeature,
"cmdarg-pwdfile-fd",
"cmdarg-create-config-files",
"tpm12-not-need-root",
"cmdarg-reconfigure-pcr-banks",
);
/**

View File

@ -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;

View File

@ -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>

View File

@ -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'>