[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20221103105558.v4.11.Ifce072ae1ef1ce39bd681fff55af13a054045d9f@changeid>
Date: Thu, 3 Nov 2022 11:01:19 -0700
From: Evan Green <evgreen@...omium.org>
To: linux-kernel@...r.kernel.org
Cc: corbet@....net, linux-pm@...r.kernel.org, rjw@...ysocki.net,
gwendal@...omium.org, apronin@...omium.org,
Pavel Machek <pavel@....cz>, Kees Cook <keescook@...omium.org>,
Matthew Garrett <mgarrett@...ora.tech>,
linux-integrity@...r.kernel.org, jejb@...ux.ibm.com,
zohar@...ux.ibm.com, dlunev@...gle.com,
Eric Biggers <ebiggers@...nel.org>,
Ben Boeckel <me@...boeckel.net>, jarkko@...nel.org,
Evan Green <evgreen@...omium.org>,
Matthew Garrett <mjg59@...gle.com>,
Len Brown <len.brown@...el.com>,
Matthew Garrett <matthewgarrett@...gle.com>,
"Rafael J. Wysocki" <rafael@...nel.org>, axelj <axelj@...s.com>
Subject: [PATCH v4 11/11] PM: hibernate: seal the encryption key with a PCR policy
The key blob is not secret, and by default the TPM will happily unseal
it regardless of system state. We can protect against that by sealing
the secret with a PCR policy - if the current PCR state doesn't match,
the TPM will refuse to release the secret. For now let's just seal it to
PCR 23. In the long term we may want a more flexible policy around this,
such as including PCR 7 for PCs or 0 for Chrome OS.
Link: https://lore.kernel.org/all/20210220013255.1083202-10-matthewgarrett@google.com/
Co-developed-by: Matthew Garrett <mjg59@...gle.com>
Signed-off-by: Matthew Garrett <mjg59@...gle.com>
Signed-off-by: Evan Green <evgreen@...omium.org>
---
Changes in v4:
- Local variable ordering (Jarkko)
Changes in v3:
- Changed funky tag to Co-developed-by (Kees)
Changes in v2:
- Fix sparse warnings
- Fix session type comment (Andrey)
- Eliminate extra label in get/create_kernel_key() (Andrey)
- Call tpm_try_get_ops() before calling tpm2_flush_context().
include/linux/tpm.h | 4 +
kernel/power/snapenc.c | 166 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 165 insertions(+), 5 deletions(-)
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 9c2ee3e30ffa5d..252a8a92a7ff5b 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -233,18 +233,22 @@ enum tpm2_command_codes {
TPM2_CC_CONTEXT_LOAD = 0x0161,
TPM2_CC_CONTEXT_SAVE = 0x0162,
TPM2_CC_FLUSH_CONTEXT = 0x0165,
+ TPM2_CC_START_AUTH_SESSION = 0x0176,
TPM2_CC_VERIFY_SIGNATURE = 0x0177,
TPM2_CC_GET_CAPABILITY = 0x017A,
TPM2_CC_GET_RANDOM = 0x017B,
TPM2_CC_PCR_READ = 0x017E,
+ TPM2_CC_POLICY_PCR = 0x017F,
TPM2_CC_PCR_EXTEND = 0x0182,
TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185,
TPM2_CC_HASH_SEQUENCE_START = 0x0186,
+ TPM2_CC_POLICY_GET_DIGEST = 0x0189,
TPM2_CC_CREATE_LOADED = 0x0191,
TPM2_CC_LAST = 0x0193, /* Spec 1.36 */
};
enum tpm2_permanent_handles {
+ TPM2_RH_NULL = 0x40000007,
TPM2_RS_PW = 0x40000009,
};
diff --git a/kernel/power/snapenc.c b/kernel/power/snapenc.c
index 2f421061498246..23f4d09ced578b 100644
--- a/kernel/power/snapenc.c
+++ b/kernel/power/snapenc.c
@@ -438,6 +438,111 @@ void snapshot_teardown_encryption(struct snapshot_data *data)
memset(data->user_key, 0, sizeof(data->user_key));
}
+static int tpm_setup_policy(struct tpm_chip *chip, int *session_handle)
+{
+ struct tpm_header *head;
+ struct tpm_buf buf;
+ char nonce[32] = {0x00};
+ int rc;
+
+ rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS,
+ TPM2_CC_START_AUTH_SESSION);
+ if (rc)
+ return rc;
+
+ /* Decrypt key */
+ tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+
+ /* Auth entity */
+ tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+
+ /* Nonce - blank is fine here */
+ tpm_buf_append_u16(&buf, sizeof(nonce));
+ tpm_buf_append(&buf, nonce, sizeof(nonce));
+
+ /* Encrypted secret - empty */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* Session type - policy */
+ tpm_buf_append_u8(&buf, 0x01);
+
+ /* Encryption type - NULL */
+ tpm_buf_append_u16(&buf, TPM_ALG_NULL);
+
+ /* Hash type - SHA256 */
+ tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
+
+ rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
+ if (rc)
+ goto out;
+
+ head = (struct tpm_header *)buf.data;
+ if (be32_to_cpu(head->length) != sizeof(struct tpm_header) +
+ sizeof(u32) + sizeof(u16) + sizeof(nonce)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ *session_handle = be32_to_cpu(*(__be32 *)&buf.data[10]);
+ memcpy(nonce, &buf.data[16], sizeof(nonce));
+ tpm_buf_destroy(&buf);
+ rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_POLICY_PCR);
+ if (rc)
+ return rc;
+
+ tpm_buf_append_u32(&buf, *session_handle);
+
+ /* PCR digest - read from the PCR, we'll verify creation data later */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* One PCR */
+ tpm_buf_append_u32(&buf, 1);
+
+ /* SHA256 banks */
+ tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
+
+ /* Select PCR 23 */
+ tpm_buf_append_u32(&buf, 0x03000080);
+ rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
+ if (rc)
+ goto out;
+
+out:
+ tpm_buf_destroy(&buf);
+ return rc;
+}
+
+static int tpm_policy_get_digest(struct tpm_chip *chip, int handle,
+ char *digest)
+{
+ struct tpm_header *head;
+ struct tpm_buf buf;
+ int rc;
+
+ rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_POLICY_GET_DIGEST);
+ if (rc)
+ return rc;
+
+ tpm_buf_append_u32(&buf, handle);
+ rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
+
+ if (rc)
+ goto out;
+
+ head = (struct tpm_header *)buf.data;
+ if (be32_to_cpu(head->length) != sizeof(struct tpm_header) +
+ sizeof(u16) + SHA256_DIGEST_SIZE) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ memcpy(digest, &buf.data[12], SHA256_DIGEST_SIZE);
+
+out:
+ tpm_buf_destroy(&buf);
+ return rc;
+}
+
static int snapshot_setup_encryption_common(struct snapshot_data *data)
{
int i, rc;
@@ -492,11 +597,16 @@ static int snapshot_setup_encryption_common(struct snapshot_data *data)
static int snapshot_create_kernel_key(struct snapshot_data *data)
{
/* Create a key sealed by the SRK. */
- char *keyinfo = "new\t32\tkeyhandle=0x81000000\tcreationpcrs=0x00800000";
+ const char *keytemplate =
+ "new\t32\tkeyhandle=0x81000000\tcreationpcrs=0x00800000\tpolicydigest=%s";
const struct cred *cred = current_cred();
struct tpm_digest *digests = NULL;
+ char policy[SHA256_DIGEST_SIZE];
+ char *policydigest = NULL;
+ int session_handle = -1;
struct key *key = NULL;
struct tpm_chip *chip;
+ char *keyinfo = NULL;
int ret, i;
chip = tpm_default_chip();
@@ -529,6 +639,28 @@ static int snapshot_create_kernel_key(struct snapshot_data *data)
if (ret != 0)
goto out;
+ policydigest = kmalloc(SHA256_DIGEST_SIZE * 2 + 1, GFP_KERNEL);
+ if (!policydigest) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = tpm_setup_policy(chip, &session_handle);
+ if (ret != 0)
+ goto out;
+
+ ret = tpm_policy_get_digest(chip, session_handle, policy);
+ if (ret != 0)
+ goto out;
+
+ bin2hex(policydigest, policy, SHA256_DIGEST_SIZE);
+ policydigest[SHA256_DIGEST_SIZE * 2] = '\0';
+ keyinfo = kasprintf(GFP_KERNEL, keytemplate, policydigest);
+ if (!keyinfo) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
key = key_alloc(&key_type_trusted, "swsusp", GLOBAL_ROOT_UID,
GLOBAL_ROOT_GID, cred, 0, KEY_ALLOC_NOT_IN_QUOTA,
NULL);
@@ -539,7 +671,7 @@ static int snapshot_create_kernel_key(struct snapshot_data *data)
goto out;
}
- ret = key_instantiate_and_link(key, keyinfo, sizeof(keyinfo), NULL,
+ ret = key_instantiate_and_link(key, keyinfo, strlen(keyinfo) + 1, NULL,
NULL);
if (ret != 0)
goto out;
@@ -553,7 +685,16 @@ static int snapshot_create_kernel_key(struct snapshot_data *data)
key_put(key);
}
+ if (session_handle != -1) {
+ if (tpm_try_get_ops(chip) == 0) {
+ tpm2_flush_context(chip, session_handle);
+ tpm_put_ops(chip);
+ }
+ }
+
kfree(digests);
+ kfree(keyinfo);
+ kfree(policydigest);
tpm2_pcr_reset(chip, 23);
out_dev:
@@ -617,12 +758,13 @@ static int snapshot_load_kernel_key(struct snapshot_data *data,
struct uswsusp_key_blob *blob)
{
- char *keytemplate = "load\t%s\tkeyhandle=0x81000000";
+ char *keytemplate = "load\t%s\tkeyhandle=0x81000000\tpolicyhandle=0x%x";
const struct cred *cred = current_cred();
struct trusted_key_payload *payload;
char certhash[SHA256_DIGEST_SIZE];
struct tpm_digest *digests = NULL;
char *blobstring = NULL;
+ int session_handle = -1;
struct key *key = NULL;
struct tpm_chip *chip;
char *keyinfo = NULL;
@@ -658,14 +800,21 @@ static int snapshot_load_kernel_key(struct snapshot_data *data,
if (ret != 0)
goto out;
- blobstring = kmalloc(blob->blob_len * 2, GFP_KERNEL);
+ ret = tpm_setup_policy(chip, &session_handle);
+ if (ret != 0)
+ goto out;
+
+ blobstring = kmalloc(blob->blob_len * 2 + 1, GFP_KERNEL);
if (!blobstring) {
ret = -ENOMEM;
goto out;
}
bin2hex(blobstring, blob->blob, blob->blob_len);
- keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring);
+ blobstring[blob->blob_len * 2] = '\0';
+ keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring,
+ session_handle);
+
if (!keyinfo) {
ret = -ENOMEM;
goto out;
@@ -748,6 +897,13 @@ static int snapshot_load_kernel_key(struct snapshot_data *data,
key_put(key);
}
+ if (session_handle != -1) {
+ if (tpm_try_get_ops(chip) == 0) {
+ tpm2_flush_context(chip, session_handle);
+ tpm_put_ops(chip);
+ }
+ }
+
kfree(keyinfo);
kfree(blobstring);
kfree(digests);
--
2.38.1.431.g37b22c650d-goog
Powered by blists - more mailing lists