The systemd-cred offers a convenient way to talk to host's TPM. While its original intent is to be used in systemd unit files, it offers two additional commands: encrypt and decrypt that can be used independent of the rest of systemd. And these are the ones we need. They offer a convenient way to encrypt/decrypt plaintext using a key that's stored in host's TPM (when --with-key=tpm is passed). In addition, there's 'has-tpm2' command which allows us to detect whether host and the OS have everything needed to utilize the TPM. Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> --- src/libvirt_private.syms | 3 + src/util/virsecret.c | 170 +++++++++++++++++++++++++++++++++++++++ src/util/virsecret.h | 10 +++ 3 files changed, 183 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 396bd80844..1d1bba915a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3362,6 +3362,9 @@ virSecretLookupDefClear; virSecretLookupDefCopy; virSecretLookupFormatSecret; virSecretLookupParseSecret; +virSecretTPMAvailable; +virSecretTPMDecrypt; +virSecretTPMEncrypt; # util/virsecureerase.h diff --git a/src/util/virsecret.c b/src/util/virsecret.c index 8a220a37ec..11aa146b6d 100644 --- a/src/util/virsecret.c +++ b/src/util/virsecret.c @@ -23,10 +23,13 @@ #include "datatypes.h" #include "viralloc.h" +#include "vircommand.h" #include "virerror.h" #include "virlog.h" #include "virsecret.h" +#include "virutil.h" #include "viruuid.h" +#include "virfile.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -176,3 +179,170 @@ virSecretGetSecretString(virConnectPtr conn, return 0; } + + +/** + * virSecretTPMAvailable: + * + * Checks whether systemd-creds is available and whether host supports + * TPM. Use this prior calling other virSecretTPM*() APIs. + * + * Returns: 1 in case TPM is available, + * 0 in case TPM is NOT available, + * -1 otherwise. + */ +int +virSecretTPMAvailable(void) +{ + g_autoptr(virCommand) cmd = NULL; + g_autofree char *out = NULL; + int exitstatus; + int rc; + + cmd = virCommandNewArgList("systemd-creds", "has-tpm2", NULL); + + virCommandSetOutputBuffer(cmd, &out); + + rc = virCommandRun(cmd, &exitstatus); + + if (rc < 0) { + /* systemd-creds not available */ + return -1; + } + + if (!out || !*out) { + /* systemd-creds reported nothing. Ouch. */ + return 0; + } + + /* systemd-creds can report one of these: + * + * yes - TPM is available and recognized by FW, kernel, etc. + * no - TPM is not available or not recognized + * partial - otherwise + */ + + if (STRPREFIX(out, "yes\n")) + return 1; + + return 0; +} + + +/** + * virSecretTPMEncrypt: + * @name: credential name + * @value: credential value + * @value_size: size of @value + * @base64: encrypted @value, base64 encoded + * + * Encrypts given plaintext (@value) with a secret key automatically + * derived from the system's TPM2 chip. Ciphertext is base64 encoded and + * stored into @base64. Pass the same @name to virSecretTPMDecrypt(). + * + * Returns: 0 on success, + * -1 otherwise (with error reported). + */ +int +virSecretTPMEncrypt(const char *name, + unsigned const char *value, + size_t value_size, + char **base64) +{ + g_autoptr(virCommand) cmd = NULL; + g_autofree char *errBuf = NULL; + int pipeFD[2] = { -1, -1 }; + int ret = -1; + + if (virPipe(pipeFD) < 0) + return -1; + + if (virSetCloseExec(pipeFD[1]) < 0) { + virReportSystemError(errno, "%s", + _("Unable to set cloexec flag")); + goto cleanup; + } + + cmd = virCommandNewArgList("systemd-creds", "encrypt", + "--with-key=tpm2", NULL); + virCommandAddArgFormat(cmd, "--name=%s", name); + virCommandAddArgList(cmd, "-", "-", NULL); + + virCommandSetInputFD(cmd, pipeFD[0]); + virCommandSetOutputBuffer(cmd, base64); + virCommandSetErrorBuffer(cmd, &errBuf); + virCommandDoAsyncIO(cmd); + + if (virCommandRunAsync(cmd, NULL) < 0) + goto cleanup; + + if (safewrite(pipeFD[1], value, value_size) != value_size) { + virReportSystemError(errno, "%s", + _("Unable to pass secret value to systemd-cred")); + goto cleanup; + } + + if (VIR_CLOSE(pipeFD[1]) < 0) { + virReportSystemError(errno, "%s", _("unable to close pipe")); + goto cleanup; + } + + if (virCommandWait(cmd, NULL) < 0) { + if (errBuf && *errBuf) { + VIR_WARN("systemd-creds reported: %s", errBuf); + } + goto cleanup; + } + + ret = 0; + cleanup: + virCommandAbort(cmd); + VIR_FORCE_CLOSE(pipeFD[0]); + VIR_FORCE_CLOSE(pipeFD[1]); + return ret; +} + + +/** + * virSecretTPMDecrypt: + * @name: credential name + * @base64: encrypted @value, base64 encoded + * @value: credential value, + * @value_size: size of @value + * + * Decrypts given ciphertext, base64 encoded (@base64) and stores + * plaintext into @value and its size into @value_size. + * + * Returns: 0 on success, + * -1 otherwise (with error reported). + */ +int +virSecretTPMDecrypt(const char *name, + const char *base64, + unsigned char **value, + size_t *value_size) +{ + g_autoptr(virCommand) cmd = NULL; + g_autofree char *out = NULL; + g_autofree char *errBuf = NULL; + + cmd = virCommandNewArgList("systemd-creds", "decrypt", + "--transcode=base64", NULL); + virCommandAddArgFormat(cmd, "--name=%s", name); + virCommandAddArgList(cmd, "-", "-", NULL); + + virCommandSetInputBuffer(cmd, base64); + virCommandSetOutputBuffer(cmd, &out); + virCommandSetErrorBuffer(cmd, &errBuf); + + if (virCommandRun(cmd, NULL) < 0) { + if (errBuf && *errBuf) { + VIR_WARN("systemd-creds reported: %s", errBuf); + } + return -1; + } + + *value = g_base64_decode(out, value_size); + + return 0; +} diff --git a/src/util/virsecret.h b/src/util/virsecret.h index c803f0fe33..a5ca764bb7 100644 --- a/src/util/virsecret.h +++ b/src/util/virsecret.h @@ -62,3 +62,13 @@ int virSecretGetSecretString(virConnectPtr conn, size_t *ret_secret_size) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5) G_GNUC_WARN_UNUSED_RESULT; + +int virSecretTPMAvailable(void); +int virSecretTPMEncrypt(const char *name, + unsigned const char *value, + size_t value_size, + char **base64); +int virSecretTPMDecrypt(const char *name, + const char *base64, + unsigned char **value, + size_t *value_size); -- 2.43.0 _______________________________________________ Devel mailing list -- devel@xxxxxxxxxxxxxxxxx To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxx