dryang / rpms / systemd

Forked from rpms/systemd a year ago
Clone
8d419f
From 98997b288819f4dac7b2ca19c199d71e733bfa92 Mon Sep 17 00:00:00 2001
8d419f
From: Grigori Goronzy <greg@chown.ath.cx>
8d419f
Date: Wed, 16 Feb 2022 22:13:42 +0100
8d419f
Subject: [PATCH] tpm2: support policies with PIN
8d419f
8d419f
Modify TPM2 authentication policy to optionally include an authValue, i.e.
8d419f
a password/PIN. We use the "PIN" terminology since it's used by other
8d419f
systems such as Windows, even though the PIN is not necessarily numeric.
8d419f
8d419f
The pin is hashed via SHA256 to allow for arbitrary length PINs.
8d419f
8d419f
v2: fix tpm2_seal in sd-repart
8d419f
v3: applied review feedback
8d419f
(cherry picked from commit 2f5a892aa0d70aa4f1f10c8dba495ad52bc02bc3)
8d419f
8d419f
Related: #2087652
8d419f
---
8d419f
 src/cryptenroll/cryptenroll-tpm2.c            |  4 +-
8d419f
 src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c |  2 +-
8d419f
 src/cryptsetup/cryptsetup-tpm2.c              |  2 +-
8d419f
 src/partition/repart.c                        |  2 +-
8d419f
 src/shared/creds-util.c                       |  2 +
8d419f
 src/shared/tpm2-util.c                        | 76 ++++++++++++++++++-
8d419f
 src/shared/tpm2-util.h                        |  8 +-
8d419f
 7 files changed, 85 insertions(+), 11 deletions(-)
8d419f
8d419f
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
8d419f
index 801014af11..f5f6b87d0f 100644
8d419f
--- a/src/cryptenroll/cryptenroll-tpm2.c
8d419f
+++ b/src/cryptenroll/cryptenroll-tpm2.c
8d419f
@@ -80,7 +80,7 @@ int enroll_tpm2(struct crypt_device *cd,
8d419f
 
8d419f
         assert_se(node = crypt_get_device_name(cd));
8d419f
 
8d419f
-        r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
8d419f
+        r = tpm2_seal(device, pcr_mask, NULL, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
8d419f
         if (r < 0)
8d419f
                 return r;
8d419f
 
8d419f
@@ -97,7 +97,7 @@ int enroll_tpm2(struct crypt_device *cd,
8d419f
 
8d419f
         /* Quick verification that everything is in order, we are not in a hurry after all. */
8d419f
         log_debug("Unsealing for verification...");
8d419f
-        r = tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
8d419f
+        r = tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, NULL, &secret2, &secret2_size);
8d419f
         if (r < 0)
8d419f
                 return r;
8d419f
 
8d419f
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
8d419f
index 3d39dfa884..de189c7bed 100644
8d419f
--- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
8d419f
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
8d419f
@@ -41,7 +41,7 @@ int acquire_luks2_key(
8d419f
                         pcr_mask, pcr_bank,
8d419f
                         primary_alg,
8d419f
                         key_data, key_data_size,
8d419f
-                        policy_hash, policy_hash_size,
8d419f
+                        policy_hash, policy_hash_size, NULL,
8d419f
                         ret_decrypted_key, ret_decrypted_key_size);
8d419f
 }
8d419f
 
8d419f
diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c
8d419f
index cb139518a7..05d76a684d 100644
8d419f
--- a/src/cryptsetup/cryptsetup-tpm2.c
8d419f
+++ b/src/cryptsetup/cryptsetup-tpm2.c
8d419f
@@ -64,7 +64,7 @@ int acquire_tpm2_key(
8d419f
                 blob = loaded_blob;
8d419f
         }
8d419f
 
8d419f
-        return tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
8d419f
+        return tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, policy_hash, policy_hash_size, NULL, ret_decrypted_key, ret_decrypted_key_size);
8d419f
 }
8d419f
 
8d419f
 int find_tpm2_auto_data(
8d419f
diff --git a/src/partition/repart.c b/src/partition/repart.c
8d419f
index 2f70796e58..adfec0b9f3 100644
8d419f
--- a/src/partition/repart.c
8d419f
+++ b/src/partition/repart.c
8d419f
@@ -2655,7 +2655,7 @@ static int partition_encrypt(
8d419f
                 uint16_t pcr_bank, primary_alg;
8d419f
                 int keyslot;
8d419f
 
8d419f
-                r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
8d419f
+                r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, NULL, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
8d419f
                 if (r < 0)
8d419f
                         return log_error_errno(r, "Failed to seal to TPM2: %m");
8d419f
 
8d419f
diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c
8d419f
index 4d0681bc10..c4dcc396ac 100644
8d419f
--- a/src/shared/creds-util.c
8d419f
+++ b/src/shared/creds-util.c
8d419f
@@ -534,6 +534,7 @@ int encrypt_credential_and_warn(
8d419f
 
8d419f
                 r = tpm2_seal(tpm2_device,
8d419f
                               tpm2_pcr_mask,
8d419f
+                              NULL,
8d419f
                               &tpm2_key,
8d419f
                               &tpm2_key_size,
8d419f
                               &tpm2_blob,
8d419f
@@ -803,6 +804,7 @@ int decrypt_credential_and_warn(
8d419f
                                 le32toh(t->blob_size),
8d419f
                                 t->policy_hash_and_blob + le32toh(t->blob_size),
8d419f
                                 le32toh(t->policy_hash_size),
8d419f
+                                NULL,
8d419f
                                 &tpm2_key,
8d419f
                                 &tpm2_key_size);
8d419f
                 if (r < 0)
8d419f
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
8d419f
index 70a2929432..aca7b69ab5 100644
8d419f
--- a/src/shared/tpm2-util.c
8d419f
+++ b/src/shared/tpm2-util.c
8d419f
@@ -14,6 +14,7 @@
8d419f
 #include "hexdecoct.h"
8d419f
 #include "memory-util.h"
8d419f
 #include "random-util.h"
8d419f
+#include "sha256.h"
8d419f
 #include "time-util.h"
8d419f
 
8d419f
 static void *libtss2_esys_dl = NULL;
8d419f
@@ -30,10 +31,12 @@ TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_
8d419f
 TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context,  TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
8d419f
 TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL;
8d419f
 TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues);
8d419f
+TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3) = NULL;
8d419f
 TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest) = NULL;
8d419f
 TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL;
8d419f
 TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
8d419f
 TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
8d419f
+TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL;
8d419f
 TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL;
8d419f
 
8d419f
 const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL;
8d419f
@@ -58,10 +61,12 @@ int dlopen_tpm2(void) {
8d419f
                         DLSYM_ARG(Esys_Initialize),
8d419f
                         DLSYM_ARG(Esys_Load),
8d419f
                         DLSYM_ARG(Esys_PCR_Read),
8d419f
+                        DLSYM_ARG(Esys_PolicyAuthValue),
8d419f
                         DLSYM_ARG(Esys_PolicyGetDigest),
8d419f
                         DLSYM_ARG(Esys_PolicyPCR),
8d419f
                         DLSYM_ARG(Esys_StartAuthSession),
8d419f
                         DLSYM_ARG(Esys_Startup),
8d419f
+                        DLSYM_ARG(Esys_TR_SetAuth),
8d419f
                         DLSYM_ARG(Esys_Unseal));
8d419f
         if (r < 0)
8d419f
                 return r;
8d419f
@@ -594,6 +599,7 @@ static int tpm2_make_pcr_session(
8d419f
                 ESYS_CONTEXT *c,
8d419f
                 uint32_t pcr_mask,
8d419f
                 uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
8d419f
+                bool use_pin,
8d419f
                 ESYS_TR *ret_session,
8d419f
                 TPM2B_DIGEST **ret_policy_digest,
8d419f
                 TPMI_ALG_HASH *ret_pcr_bank) {
8d419f
@@ -669,6 +675,21 @@ static int tpm2_make_pcr_session(
8d419f
                 goto finish;
8d419f
         }
8d419f
 
8d419f
+        if (use_pin) {
8d419f
+                rc = sym_Esys_PolicyAuthValue(
8d419f
+                                c,
8d419f
+                                session,
8d419f
+                                ESYS_TR_NONE,
8d419f
+                                ESYS_TR_NONE,
8d419f
+                                ESYS_TR_NONE);
8d419f
+                if (rc != TSS2_RC_SUCCESS) {
8d419f
+                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
8d419f
+                                            "Failed to add authValue policy to TPM: %s",
8d419f
+                                            sym_Tss2_RC_Decode(rc));
8d419f
+                        goto finish;
8d419f
+                }
8d419f
+        }
8d419f
+
8d419f
         if (DEBUG_LOGGING || ret_policy_digest) {
8d419f
                 log_debug("Acquiring policy digest.");
8d419f
 
8d419f
@@ -717,9 +738,22 @@ finish:
8d419f
         return r;
8d419f
 }
8d419f
 
8d419f
+static void hash_pin(const char *pin, size_t len, uint8_t ret_digest[static SHA256_DIGEST_SIZE]) {
8d419f
+        struct sha256_ctx hash;
8d419f
+
8d419f
+        assert(pin);
8d419f
+
8d419f
+        sha256_init_ctx(&hash);
8d419f
+        sha256_process_bytes(pin, len, &hash);
8d419f
+        sha256_finish_ctx(&hash, ret_digest);
8d419f
+
8d419f
+        explicit_bzero_safe(&hash, sizeof(hash));
8d419f
+}
8d419f
+
8d419f
 int tpm2_seal(
8d419f
                 const char *device,
8d419f
                 uint32_t pcr_mask,
8d419f
+                const char *pin,
8d419f
                 void **ret_secret,
8d419f
                 size_t *ret_secret_size,
8d419f
                 void **ret_blob,
8d419f
@@ -782,7 +816,8 @@ int tpm2_seal(
8d419f
         if (r < 0)
8d419f
                 return r;
8d419f
 
8d419f
-        r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, NULL, &policy_digest, &pcr_bank);
8d419f
+        r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, !!pin, NULL, &policy_digest,
8d419f
+                                  &pcr_bank);
8d419f
         if (r < 0)
8d419f
                 goto finish;
8d419f
 
8d419f
@@ -813,6 +848,10 @@ int tpm2_seal(
8d419f
                 .size = sizeof(hmac_sensitive.sensitive),
8d419f
                 .sensitive.data.size = 32,
8d419f
         };
8d419f
+        if (pin) {
8d419f
+                hash_pin(pin, strlen(pin), hmac_sensitive.sensitive.userAuth.buffer);
8d419f
+                hmac_sensitive.sensitive.userAuth.size = SHA256_DIGEST_SIZE;
8d419f
+        }
8d419f
         assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
8d419f
 
8d419f
         (void) tpm2_credit_random(c.esys_context);
8d419f
@@ -910,6 +949,7 @@ int tpm2_seal(
8d419f
         r = 0;
8d419f
 
8d419f
 finish:
8d419f
+        explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive));
8d419f
         primary = flush_context_verbose(c.esys_context, primary);
8d419f
         return r;
8d419f
 }
8d419f
@@ -923,6 +963,7 @@ int tpm2_unseal(
8d419f
                 size_t blob_size,
8d419f
                 const void *known_policy_hash,
8d419f
                 size_t known_policy_hash_size,
8d419f
+                const char *pin,
8d419f
                 void **ret_secret,
8d419f
                 size_t *ret_secret_size) {
8d419f
 
8d419f
@@ -978,7 +1019,7 @@ int tpm2_unseal(
8d419f
         if (r < 0)
8d419f
                 return r;
8d419f
 
8d419f
-        r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, &session, &policy_digest, NULL);
8d419f
+        r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, !!pin, &session, &policy_digest, NULL);
8d419f
         if (r < 0)
8d419f
                 goto finish;
8d419f
 
8d419f
@@ -1005,11 +1046,38 @@ int tpm2_unseal(
8d419f
                         &public,
8d419f
                         &hmac_key);
8d419f
         if (rc != TSS2_RC_SUCCESS) {
8d419f
-                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
8d419f
-                                    "Failed to load HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
8d419f
+                /* If we're in dictionary attack lockout mode, we should see a lockout error here, which we
8d419f
+                 * need to translate for the caller. */
8d419f
+                if (rc == TPM2_RC_LOCKOUT)
8d419f
+                        r = log_error_errno(
8d419f
+                                        SYNTHETIC_ERRNO(ENOLCK),
8d419f
+                                        "TPM2 device is in dictionary attack lockout mode.");
8d419f
+                else
8d419f
+                        r = log_error_errno(
8d419f
+                                        SYNTHETIC_ERRNO(ENOTRECOVERABLE),
8d419f
+                                        "Failed to load HMAC key in TPM: %s",
8d419f
+                                        sym_Tss2_RC_Decode(rc));
8d419f
                 goto finish;
8d419f
         }
8d419f
 
8d419f
+        if (pin) {
8d419f
+                TPM2B_AUTH auth = {
8d419f
+                        .size = SHA256_DIGEST_SIZE
8d419f
+                };
8d419f
+
8d419f
+                hash_pin(pin, strlen(pin), auth.buffer);
8d419f
+
8d419f
+                rc = sym_Esys_TR_SetAuth(c.esys_context, hmac_key, &auth);
8d419f
+                explicit_bzero_safe(&auth, sizeof(auth));
8d419f
+                if (rc != TSS2_RC_SUCCESS) {
8d419f
+                        r = log_error_errno(
8d419f
+                                        SYNTHETIC_ERRNO(ENOTRECOVERABLE),
8d419f
+                                        "Failed to load PIN in TPM: %s",
8d419f
+                                        sym_Tss2_RC_Decode(rc));
8d419f
+                        goto finish;
8d419f
+                }
8d419f
+        }
8d419f
+
8d419f
         log_debug("Unsealing HMAC key.");
8d419f
 
8d419f
         rc = sym_Esys_Unseal(
8d419f
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
8d419f
index cb57a847e2..784e9fd11e 100644
8d419f
--- a/src/shared/tpm2-util.h
8d419f
+++ b/src/shared/tpm2-util.h
8d419f
@@ -1,6 +1,8 @@
8d419f
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8d419f
 #pragma once
8d419f
 
8d419f
+#include <stdbool.h>
8d419f
+
8d419f
 #include "json.h"
8d419f
 #include "macro.h"
8d419f
 
8d419f
@@ -20,10 +22,12 @@ extern TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1
8d419f
 extern TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context,  TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion);
8d419f
 extern TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle);
8d419f
 extern TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues);
8d419f
+extern TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3);
8d419f
 extern TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest);
8d419f
 extern TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs);
8d419f
 extern TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle);
8d419f
 extern TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType);
8d419f
+extern TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue);
8d419f
 extern TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData);
8d419f
 
8d419f
 extern const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc);
8d419f
@@ -35,8 +39,8 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], siz
8d419f
 
8d419f
 int dlopen_tpm2(void);
8d419f
 
8d419f
-int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg);
8d419f
-int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size);
8d419f
+int tpm2_seal(const char *device, uint32_t pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg);
8d419f
+int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, const char *pin, void **ret_secret, size_t *ret_secret_size);
8d419f
 
8d419f
 #endif
8d419f