[libvirt] [PATCH 14/18] tpm: Use fd to pass password to swtpm_setup and swtpm

Marc-André Lureau marcandre.lureau at redhat.com
Tue Jul 9 20:25:10 UTC 2019


On Tue, Jul 9, 2019 at 9:24 PM Stefan Berger <stefanb at linux.vnet.ibm.com> wrote:
>
> Allow vTPM state encryption when swtpm_setup and swtpm support
> passing a passphrase using a file descriptor.
>
> This patch enables the encryption of the vTPM state only. It does
> not encrypt the state during migration, so the destination secret
> does not need to have the same password at this point.

You could add that it is addressed in the following patch

>
> Signed-off-by: Stefan Berger <stefanb at linux.ibm.com>
> ---
>  src/libvirt_private.syms |   2 +
>  src/qemu/qemu_tpm.c      | 101 ++++++++++++++++++++++++++++++++++++++-
>  src/tpm/virtpm.c         |  16 +++++++
>  src/tpm/virtpm.h         |   3 ++
>  4 files changed, 120 insertions(+), 2 deletions(-)
>
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index d2045895a1..d693f7facb 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1456,6 +1456,8 @@ virTPMEmulatorInit;
>  virTPMGetSwtpm;
>  virTPMGetSwtpmIoctl;
>  virTPMGetSwtpmSetup;
> +virTPMSwtpmCapsGet;
> +virTPMSwtpmSetupCapsGet;
>
>
>  # util/viralloc.h
> diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c
> index 2afa8db448..6e7d38b7e0 100644
> --- a/src/qemu/qemu_tpm.c
> +++ b/src/qemu/qemu_tpm.c
> @@ -43,6 +43,8 @@
>  #include "dirname.h"
>  #include "qemu_tpm.h"
>  #include "virtpm.h"
> +#include "secret_util.h"
> +#include "virtpm_conf.h"
>
>  #define VIR_FROM_THIS VIR_FROM_NONE
>
> @@ -372,6 +374,60 @@ qemuTPMEmulatorPrepareHost(virDomainTPMDefPtr tpm,
>      return ret;
>  }
>
> +/*
> + * qemuTPMSetupEncryption
> + *
> + * @encryption: pointer to virStorageEncryption holding secret
> + *
> + * Returns file descriptor representing the read-end of a pipe.
> + * The passphrase can be read from this pipe. Returns < 0 in case
> + * of error.
> + *
> + * This function reads the passphrase and writes it into the
> + * write-end of a pipe so that the read-end of the pipe can be
> + * passed to the emulator for reading the passphrase from.
> + */
> +static int
> +qemuTPMSetupEncryption(virStorageEncryptionPtr encryption)
> +{
> +    int ret = -1;
> +    int pipefd[2] = { -1, -1 };
> +    virConnectPtr conn;
> +    uint8_t *secret = NULL;
> +    size_t secret_len;
> +
> +    conn = virGetConnectSecret();
> +    if (!conn)
> +        return -1;
> +
> +    if (virSecretGetSecretString(conn, &encryption->secrets[0]->seclookupdef,
> +                                 VIR_SECRET_USAGE_TYPE_VTPM,
> +                                 &secret, &secret_len) < 0)
> +        goto error;
> +
> +    if (pipe(pipefd) == -1) {
> +        virReportSystemError(errno, "%s",
> +                             _("Unable to create pipe"));
> +        goto error;
> +    }
> +
> +    if (safewrite(pipefd[1], secret, secret_len) != secret_len)
> +        goto error;

Hmm, I am not sure you can reliably buffer data on a pipe end and
close it before the other end read. Got any documentation pointer
about that?

> +
> +    ret = pipefd[0];
> +
> + cleanup:
> +    VIR_FREE(secret);
> +    VIR_FORCE_CLOSE(pipefd[1]);
> +    virObjectUnref(conn);
> +
> +    return ret;
> +
> + error:
> +    VIR_FORCE_CLOSE(pipefd[0]);
> +
> +    goto cleanup;
> +}
>
>  /*
>   * qemuTPMEmulatorRunSetup
> @@ -386,6 +442,7 @@ qemuTPMEmulatorPrepareHost(virDomainTPMDefPtr tpm,
>   * @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
> + * @encryption: pointer to virStorageEncryption holding secret
>   *
>   * Setup the external swtpm by creating endorsement key and
>   * certificates for it.
> @@ -398,13 +455,15 @@ qemuTPMEmulatorRunSetup(const char *storagepath,
>                          uid_t swtpm_user,
>                          gid_t swtpm_group,
>                          const char *logfile,
> -                        const virDomainTPMVersion tpmversion)
> +                        const virDomainTPMVersion tpmversion,
> +                        virStorageEncryptionPtr encryption)
>  {
>      virCommandPtr cmd = NULL;
>      int exitstatus;
>      int ret = -1;
>      char uuid[VIR_UUID_STRING_BUFLEN];
>      char *vmid = NULL;
> +    int pwdfile_fd = -1;
>
>      if (!privileged && tpmversion == VIR_DOMAIN_TPM_VERSION_1_2)
>          return virFileWriteStr(logfile,
> @@ -434,6 +493,22 @@ qemuTPMEmulatorRunSetup(const char *storagepath,
>          break;
>      }
>
> +    if (encryption) {
> +        if (!virTPMSwtpmSetupCapsGet(
> +                VIR_TPM_SWTPM_SETUP_FEATURE_CMDARG_PWDFILE_FD)) {
> +            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
> +                _("%s does not support passing a passphrase using a file "
> +                  "descriptor"), virTPMGetSwtpmSetup());
> +            goto cleanup;
> +        }
> +        if ((pwdfile_fd = qemuTPMSetupEncryption(encryption)) < 0)
> +            goto cleanup;
> +
> +        virCommandAddArg(cmd, "--pwdfile-fd");
> +        virCommandAddArgFormat(cmd, "%d", pwdfile_fd);
> +        virCommandAddArgList(cmd, "--cipher", "aes-256-cbc", NULL);
> +        virCommandPassFD(cmd, pwdfile_fd, VIR_COMMAND_PASS_FD_CLOSE_PARENT);
> +    }
>
>      virCommandAddArgList(cmd,
>                           "--tpm-state", storagepath,
> @@ -461,6 +536,7 @@ qemuTPMEmulatorRunSetup(const char *storagepath,
>   cleanup:
>      VIR_FREE(vmid);
>      virCommandFree(cmd);
> +    VIR_FORCE_CLOSE(pwdfile_fd);


virCommandPassFD() doc says:
"The parent should cease using the @fd when this call completes"

>
>      return ret;
>  }
> @@ -496,6 +572,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm,
>      virCommandPtr cmd = NULL;
>      bool created = false;
>      char *pidfile;
> +    int pwdfile_fd = -1;
>
>      if (qemuTPMCreateEmulatorStorage(tpm->data.emulator.storagepath,
>                                       &created, swtpm_user, swtpm_group) < 0)
> @@ -504,7 +581,8 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm,
>      if (created &&
>          qemuTPMEmulatorRunSetup(tpm->data.emulator.storagepath, vmname, vmuuid,
>                                  privileged, swtpm_user, swtpm_group,
> -                                tpm->data.emulator.logfile, tpm->version) < 0)
> +                                tpm->data.emulator.logfile, tpm->version,
> +                                tpm->data.emulator.encryption) < 0)
>          goto error;
>
>      unlink(tpm->data.emulator.source.data.nix.path);
> @@ -547,11 +625,30 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm,
>      virCommandAddArgFormat(cmd, "file=%s", pidfile);
>      VIR_FREE(pidfile);
>
> +    if (tpm->data.emulator.encryption) {
> +        if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD)) {
> +            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
> +                  _("%s does not support passing passphrase via file descriptor"),
> +                  virTPMGetSwtpm());
> +            goto error;
> +        }
> +
> +        pwdfile_fd = qemuTPMSetupEncryption(tpm->data.emulator.encryption);
> +        if (pwdfile_fd < 0)
> +            goto error;
> +
> +        virCommandAddArg(cmd, "--key");
> +        virCommandAddArgFormat(cmd, "pwdfd=%d,mode=aes-256-cbc,kdf=pbkdf2",
> +                               pwdfile_fd);
> +        virCommandPassFD(cmd, pwdfile_fd, VIR_COMMAND_PASS_FD_CLOSE_PARENT);
> +    }
> +
>      return cmd;
>
>   error:
>      if (created)
>          qemuTPMDeleteEmulatorStorage(tpm);
> +    VIR_FORCE_CLOSE(pwdfile_fd);

same, and similarly in next patch



>
>      virCommandFree(cmd);
>
> diff --git a/src/tpm/virtpm.c b/src/tpm/virtpm.c
> index 42dd2b1bb2..7e95dbad6f 100644
> --- a/src/tpm/virtpm.c
> +++ b/src/tpm/virtpm.c
> @@ -312,3 +312,19 @@ virTPMEmulatorInit(void)
>
>      return 0;
>  }
> +
> +bool
> +virTPMSwtpmCapsGet(unsigned int cap)
> +{
> +    if (virTPMEmulatorInit() < 0)
> +        return false;
> +    return virBitmapIsBitSet(swtpm_caps, cap);
> +}
> +
> +bool
> +virTPMSwtpmSetupCapsGet(unsigned int cap)
> +{
> +    if (virTPMEmulatorInit() < 0)
> +        return false;
> +    return virBitmapIsBitSet(swtpm_setup_caps, cap);
> +}
> diff --git a/src/tpm/virtpm.h b/src/tpm/virtpm.h
> index 66d55fb231..a8bb6e1ba0 100644
> --- a/src/tpm/virtpm.h
> +++ b/src/tpm/virtpm.h
> @@ -26,3 +26,6 @@ const char *virTPMGetSwtpm(void);
>  const char *virTPMGetSwtpmSetup(void);
>  const char *virTPMGetSwtpmIoctl(void);
>  int virTPMEmulatorInit(void);
> +
> +bool virTPMSwtpmCapsGet(unsigned int cap);
> +bool virTPMSwtpmSetupCapsGet(unsigned int cap);
> --
> 2.20.1
>




More information about the libvir-list mailing list