All of lore.kernel.org
 help / color / mirror / Atom feed
From: Matthew Garrett <matthewgarrett@google.com>
To: linux-kernel@vger.kernel.org
Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org,
	keyrings@vger.kernel.org, zohar@linux.ibm.com,
	jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net,
	rjw@rjwysocki.net, Matthew Garrett <matthewgarrett@google.com>,
	Matthew Garrett <mjg59@google.com>
Subject: [PATCH 9/9] pm: hibernate: seal the encryption key with a PCR policy
Date: Sat, 20 Feb 2021 01:32:55 +0000	[thread overview]
Message-ID: <20210220013255.1083202-10-matthewgarrett@google.com> (raw)
In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com>

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.

Signed-off-by: Matthew Garrett <mjg59@google.com>
---
 include/linux/tpm.h |   4 ++
 kernel/power/tpm.c  | 161 ++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 160 insertions(+), 5 deletions(-)

diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index f6970986b097..2e0141978c87 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -225,18 +225,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/tpm.c b/kernel/power/tpm.c
index 34e6cfb98ce4..5de27c2f08be 100644
--- a/kernel/power/tpm.c
+++ b/kernel/power/tpm.c
@@ -125,6 +125,118 @@ static int swsusp_enc_dec(struct trusted_key_payload *payload, char *buf,
 	return ret;
 }
 
+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);
+
+	/* Policy type - session */
+	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(int) + sizeof(u16) + sizeof(nonce)) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	*session_handle = be32_to_cpu(*(int *)&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 tpm_certify_creationdata(struct tpm_chip *chip,
 				    struct trusted_key_payload *payload)
 {
@@ -182,11 +294,14 @@ int swsusp_encrypt_digest(struct swsusp_header *header)
 	const struct cred *cred = current_cred();
 	struct trusted_key_payload *payload;
 	struct tpm_digest *digests = NULL;
+	char policy[SHA256_DIGEST_SIZE];
+	char *policydigest = NULL;
 	struct tpm_chip *chip;
 	struct key *key;
+	int session_handle;
 	int ret, i;
-
-	char *keyinfo = "new\t32\tkeyhandle=0x81000001\tcreationpcrs=0x00800000";
+	char *keyinfo = NULL;
+	char *keytemplate = "new\t32\tkeyhandle=0x81000001\tcreationpcrs=0x00800000\tpolicydigest=%s";
 
 	chip = tpm_default_chip();
 
@@ -213,10 +328,35 @@ int swsusp_encrypt_digest(struct swsusp_header *header)
 			memcpy(&digests[i], &digest, sizeof(digest));
 	}
 
+	policydigest = kmalloc(SHA256_DIGEST_SIZE * 2 + 1, GFP_KERNEL);
+	if (!policydigest) {
+		ret = -ENOMEM;
+		goto reset;
+	}
+
 	ret = tpm_pcr_extend(chip, 23, digests);
 	if (ret != 0)
 		goto reset;
 
+	ret = tpm_setup_policy(chip, &session_handle);
+
+	if (ret != 0)
+		goto reset;
+
+	ret = tpm_policy_get_digest(chip, session_handle, policy);
+
+	if (ret != 0)
+		goto reset;
+
+	bin2hex(policydigest, policy, SHA256_DIGEST_SIZE);
+	policydigest[64] = '\0';
+
+	keyinfo = kasprintf(GFP_KERNEL, keytemplate, policydigest);
+	if (!keyinfo) {
+		ret = -ENOMEM;
+		goto reset;
+	}
+
 	key = key_alloc(&key_type_trusted, "swsusp", GLOBAL_ROOT_UID,
 			GLOBAL_ROOT_GID, cred, 0, KEY_ALLOC_NOT_IN_QUOTA,
 			NULL);
@@ -228,6 +368,7 @@ int swsusp_encrypt_digest(struct swsusp_header *header)
 
 	ret = key_instantiate_and_link(key, keyinfo, strlen(keyinfo) + 1, NULL,
 				       NULL);
+
 	if (ret < 0)
 		goto error;
 
@@ -244,6 +385,8 @@ int swsusp_encrypt_digest(struct swsusp_header *header)
 	key_revoke(key);
 	key_put(key);
 reset:
+	kfree(keyinfo);
+	kfree(policydigest);
 	kfree(digests);
 	tpm_pcr_reset(chip, 23);
 	return ret;
@@ -252,13 +395,14 @@ int swsusp_encrypt_digest(struct swsusp_header *header)
 int swsusp_decrypt_digest(struct swsusp_header *header)
 {
 	const struct cred *cred = current_cred();
-	char *keytemplate = "load\t%s\tkeyhandle=0x81000001";
+	char *keytemplate = "load\t%s\tkeyhandle=0x81000001\tpolicyhandle=0x%x";
 	struct trusted_key_payload *payload;
 	struct tpm_digest *digests = NULL;
 	char certhash[SHA256_DIGEST_SIZE];
 	char *blobstring = NULL;
 	char *keyinfo = NULL;
 	struct tpm_chip *chip;
+	int session_handle;
 	struct key *key;
 	int i, ret;
 
@@ -291,15 +435,22 @@ int swsusp_decrypt_digest(struct swsusp_header *header)
 	if (ret != 0)
 		goto reset;
 
-	blobstring = kmalloc(header->blob_len * 2, GFP_KERNEL);
+	ret = tpm_setup_policy(chip, &session_handle);
+
+	if (ret != 0)
+		goto reset;
+
+	blobstring = kmalloc(header->blob_len * 2 + 1, GFP_KERNEL);
 	if (!blobstring) {
 		ret = -ENOMEM;
 		goto reset;
 	}
 
 	bin2hex(blobstring, header->blob, header->blob_len);
+	blobstring[header->blob_len * 2] = '\0';
 
-	keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring);
+	keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring,
+			    session_handle);
 	if (!keyinfo) {
 		ret = -ENOMEM;
 		goto reset;
-- 
2.30.0.617.g56c4b15f3c-goog


  parent reply	other threads:[~2021-02-20  1:35 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-20  1:32 [PATCH 0/9] Enable hibernation when Lockdown is enabled Matthew Garrett
2021-02-20  1:32 ` [PATCH 1/9] tpm: Add support for in-kernel resetting of PCRs Matthew Garrett
2021-02-20  2:52   ` Jarkko Sakkinen
2021-02-20  1:32 ` [PATCH 2/9] tpm: Allow PCR 23 to be restricted to kernel-only use Matthew Garrett
2021-02-20  3:02   ` Jarkko Sakkinen
2021-02-24 17:12   ` Jarkko Sakkinen
2021-02-24 18:00   ` James Bottomley
2021-02-28  7:59     ` Matthew Garrett
2021-02-20  1:32 ` [PATCH 3/9] security: keys: trusted: Parse out individual components of the key blob Matthew Garrett
2021-02-20  3:05   ` Jarkko Sakkinen
2021-02-22  7:36     ` Matthew Garrett
2021-02-24 17:22       ` Jarkko Sakkinen
2021-02-20  1:32 ` [PATCH 4/9] security: keys: trusted: Store the handle of a loaded key Matthew Garrett
2021-02-20  3:06   ` Jarkko Sakkinen
2021-02-20  1:32 ` [PATCH 5/9] security: keys: trusted: Allow storage of PCR values in creation data Matthew Garrett
2021-02-20  3:09   ` Jarkko Sakkinen
2021-02-21 19:44     ` Ben Boeckel
2021-02-22  7:41     ` Matthew Garrett
2021-02-20  1:32 ` [PATCH 6/9] pm: hibernate: Optionally store and verify a hash of the image Matthew Garrett
2021-05-05 18:18   ` Evan Green
2021-02-20  1:32 ` [PATCH 7/9] pm: hibernate: Optionally use TPM-backed keys to protect image integrity Matthew Garrett
2021-02-20  2:20   ` Randy Dunlap
2021-02-22  7:41     ` Matthew Garrett
2021-02-20  1:32 ` [PATCH 8/9] pm: hibernate: Verify the digest encryption key Matthew Garrett
2021-02-20  1:32 ` Matthew Garrett [this message]
2021-05-04 21:56 ` [PATCH 0/9] Enable hibernation when Lockdown is enabled Evan Green
2021-05-05  3:18   ` Jarkko Sakkinen
2021-05-05  3:19     ` Jarkko Sakkinen
2021-05-05 18:02       ` Evan Green

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210220013255.1083202-10-matthewgarrett@google.com \
    --to=matthewgarrett@google.com \
    --cc=corbet@lwn.net \
    --cc=jarkko@kernel.org \
    --cc=jejb@linux.ibm.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=mjg59@google.com \
    --cc=rjw@rjwysocki.net \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.