2022-11-11 23:29:59

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 00/11] Encrypted Hibernation

We are exploring enabling hibernation in some new scenarios. However,
our security team has a few requirements, listed below:
1. The hibernate image must be encrypted with protection derived from
both the platform (eg TPM) and user authentication data (eg
password).
2. Hibernation must not be a vector by which a malicious userspace can
escalate to the kernel.

Requirement #1 can be achieved solely with uswsusp, however requirement
2 necessitates mechanisms in the kernel to guarantee integrity of the
hibernate image. The kernel needs a way to authenticate that it generated
the hibernate image being loaded, and that the image has not been tampered
with. Adding support for in-kernel AEAD encryption with a TPM-sealed key
allows us to achieve both requirements with a single computation pass.

Matthew Garrett published a series [1] that aligns closely with this
goal. His series utilized the fact that PCR23 is a resettable PCR that
can be blocked from access by usermode. The TPM can create a sealed key
tied to PCR23 in two ways. First, the TPM can attest to the value of
PCR23 when the key was created, which the kernel can use on resume to
verify that the kernel must have created the key (since it is the only
one capable of modifying PCR23). It can also create a policy that enforces
PCR23 be set to a specific value as a condition of unsealing the key,
preventing usermode from unsealing the key by talking directly to the
TPM.

This series adopts that primitive as a foundation, tweaking and building
on it a bit. Where Matthew's series used the TPM-backed key to encrypt a
hash of the image, this series uses the key directly as a gcm(aes)
encryption key, which the kernel uses to encrypt and decrypt the
hibernate image in chunks of 16 pages. This provides both encryption and
integrity, which turns out to be a noticeable performance improvement over
separate passes for encryption and hashing.

The series also introduces the concept of mixing user key material into
the encryption key. This allows usermode to introduce key material
based on unspecified external authentication data (in our case derived
from something like the user password or PIN), without requiring
usermode to do a separate encryption pass.

Matthew also documented issues his series had [2] related to generating
fake images by booting alternate kernels without the PCR23 limiting.
With access to PCR23 on the same machine, usermode can create fake
hibernate images that are indistinguishable to the new kernel from
genuine ones. His post outlines a solution that involves adding more
PCRs into the creation data and policy, with some gyrations to make this
work well on a standard PC.

Our approach would be similar: on our machines PCR 0 indicates whether
the system is booted in secure/verified mode or developer mode. By
adding PCR0 to the policy, we can reject hibernate images made in
developer mode while in verified mode (or vice versa).

Additionally, mixing in the user authentication data limits both
data exfiltration attacks (eg a stolen laptop) and forged hibernation
image attacks to attackers that already know the authentication data (eg
user's password). This, combined with our relatively sealed userspace
(dm-verity on the rootfs), and some judicious clearing of the hibernate
image (such as across an OS update) further reduce the risk of an online
attack. The remaining attack space of a forgery from someone with
physical access to the device and knowledge of the authentication data
is out of scope for us, given that flipping to developer mode or
reflashing RO firmware trivially achieves the same thing.

A couple of patches still need to be written on top of this series. The
generalized functionality to OR in additional PCRs via Kconfig (like PCR
0 or 5) still needs to be added. We'll also need a patch that disallows
unencrypted forms of resume from hibernation, to fully close the door
to malicious userspace. However, I wanted to get this series out first
and get reactions from upstream before continuing to add to it.

[1] https://patchwork.kernel.org/project/linux-pm/cover/[email protected]/
[2] https://mjg59.dreamwidth.org/58077.html

Changes in v5:
- Change to co-developed by Matthew (Kees)
- Change tags on RESTRICT_PCR patch (Kees)
- Rename to TCG_TPM2_RESTRICT_PCR
- Do nothing on TPM1.2 devices (Jarkko, Doug)
- Factored some math out to a helper function (Kees)
- Constified src in tpm2_key_encode().
- Make Matthew's tag match author
- Removed default n in Kconfig (Kees)
- Expanded commit message (Jarkko)
- Use Suggested-by tag instead of made up Sourced-from (Kees)
- ENCRYPTED_HIBERNATION should depend on TCG_TPM2_RESTRCT_PCR
- Remove pad struct member (Kees)
- Use a struct to access creation data (Kees)
- Build PCR bitmask programmatically in creation data (Kees)

Changes in v4:
- Open code tpm2_pcr_reset implementation in tpm-interface.c (Jarkko)
- Rename interface symbol to tpm2_pcr_reset, fix kerneldocs (Jarkko)
- Augment the commit message (Jarkko)
- Local ordering and whitespace changes (Jarkko)
- s/tpm_pcr_reset/tpm2_pcr_reset/ due to change in other patch
- Variable ordering and whitespace fixes (Jarkko)
- Add NULL check explanation in teardown (Jarkko)
- Change strlen+1 to sizeof for static buffer (Jarkko)
- Fix nr_allocated_banks loop overflow (found via KASAN)
- Local variable reordering (Jarkko)
- Local variable ordering (Jarkko)

Changes in v3:
- Unify tpm1/2_pcr_reset prototypes (Jarkko)
- Wait no, remove the TPM1 stuff altogether (Jarkko)
- Remove extra From tag and blank in commit msg (Jarkko).
- Split find_and_validate_cc() export to its own patch (Jarkko)
- Rename tpm_find_and_validate_cc() to tpm2_find_and_validate_cc().
- Fix up commit message (Jarkko)
- tpm2_find_and_validate_cc() was split (Jarkko)
- Simply fully restrict TPM1 since v2 failed to account for tunnelled
transport sessions (Stefan and Jarkko).
- Fix SoB and -- note ordering (Kees)
- Add comments describing the TPM2 spec type names for the new fields
in tpm2key.asn1 (Kees)
- Add len buffer checks in tpm2_key_encode() (Kees)
- Clarified creationpcrs documentation (Ben)
- Changed funky tag to suggested-by (Kees). Matthew, holler if you want
something different.
- ENCRYPTED_HIBERNATION needs TRUSTED_KEYS builtin for
key_type_trusted.
- Remove KEYS dependency since it's covered by TRUSTED_KEYS (Kees)
- Changed funky tag to Co-developed-by (Kees). Matthew, holler if you
want something different.
- Changed funky tag to Co-developed-by (Kees)

Changes in v2:
- Fixed sparse warnings
- Adjust hash len by 2 due to new ASN.1 storage, and add underflow
check.
- Rework load/create_kernel_key() to eliminate a label (Andrey)
- Call put_device() needed from calling tpm_default_chip().
- Add missing static on snapshot_encrypted_byte_count()
- Fold in only the used kernel key bytes to the user key.
- Make the user key length 32 (Eric)
- Use CRYPTO_LIB_SHA256 for less boilerplate (Eric)
- Fixed some sparse warnings
- Use CRYPTO_LIB_SHA256 to get rid of sha256_data() (Eric)
- Adjusted offsets due to new ASN.1 format, and added a creation data
length check.
- 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().

Evan Green (10):
tpm: Add support for in-kernel resetting of PCRs
tpm: Export and rename tpm2_find_and_validate_cc()
tpm: Allow PCR 23 to be restricted to kernel-only use
security: keys: trusted: Include TPM2 creation data
security: keys: trusted: Verify creation data
PM: hibernate: Add kernel-based encryption
PM: hibernate: Use TPM-backed keys to encrypt image
PM: hibernate: Mix user key in encrypted hibernate
PM: hibernate: Verify the digest encryption key
PM: hibernate: seal the encryption key with a PCR policy

Matthew Garrett (1):
security: keys: trusted: Allow storage of PCR values in creation data

Documentation/power/userland-swsusp.rst | 8 +
.../security/keys/trusted-encrypted.rst | 6 +
drivers/char/tpm/Kconfig | 12 +
drivers/char/tpm/tpm-dev-common.c | 6 +
drivers/char/tpm/tpm-interface.c | 47 +
drivers/char/tpm/tpm.h | 15 +
drivers/char/tpm/tpm2-cmd.c | 29 +-
drivers/char/tpm/tpm2-space.c | 8 +-
include/keys/trusted-type.h | 9 +
include/linux/tpm.h | 19 +
include/uapi/linux/suspend_ioctls.h | 28 +-
kernel/power/Kconfig | 15 +
kernel/power/Makefile | 1 +
kernel/power/power.h | 1 +
kernel/power/snapenc.c | 1105 +++++++++++++++++
kernel/power/snapshot.c | 5 +
kernel/power/user.c | 44 +-
kernel/power/user.h | 117 ++
security/keys/trusted-keys/tpm2key.asn1 | 15 +-
security/keys/trusted-keys/trusted_tpm1.c | 9 +
security/keys/trusted-keys/trusted_tpm2.c | 355 +++++-
21 files changed, 1797 insertions(+), 57 deletions(-)
create mode 100644 kernel/power/snapenc.c
create mode 100644 kernel/power/user.h

--
2.38.1.431.g37b22c650d-goog



2022-11-11 23:30:26

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 08/11] PM: hibernate: Use TPM-backed keys to encrypt image

When using encrypted hibernate images, have the TPM create a key for us
and seal it. By handing back a sealed blob instead of the raw key, we
prevent usermode from being able to decrypt and tamper with the
hibernate image on a different machine.

We'll also go through the motions of having PCR23 set to a known value at
the time of key creation and unsealing. Currently there's nothing that
enforces the contents of PCR23 as a condition to unseal the key blob,
that will come in a later change.

Suggested-by: Matthew Garrett <[email protected]>
Signed-off-by: Evan Green <[email protected]>
Reviewed-by: Kees Cook <[email protected]>

---
Matthew's incarnation of this patch is at:
https://patchwork.kernel.org/project/linux-pm/patch/[email protected]/

Changes in v5:
- Use Suggested-by tag instead of made up Sourced-from (Kees)
- ENCRYPTED_HIBERNATION should depend on TCG_TPM2_RESTRCT_PCR

Changes in v4:
- s/tpm_pcr_reset/tpm2_pcr_reset/ due to change in other patch
- Variable ordering and whitespace fixes (Jarkko)
- Add NULL check explanation in teardown (Jarkko)
- Change strlen+1 to sizeof for static buffer (Jarkko)
- Fix nr_allocated_banks loop overflow (found via KASAN)

Changes in v3:
- ENCRYPTED_HIBERNATION needs TRUSTED_KEYS builtin for
key_type_trusted.
- Remove KEYS dependency since it's covered by TRUSTED_KEYS (Kees)

Changes in v2:
- Rework load/create_kernel_key() to eliminate a label (Andrey)
- Call put_device() needed from calling tpm_default_chip().

kernel/power/Kconfig | 2 +
kernel/power/snapenc.c | 211 +++++++++++++++++++++++++++++++++++++++--
kernel/power/user.h | 1 +
3 files changed, 205 insertions(+), 9 deletions(-)

diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 2bde64bddae403..420024f46992b2 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -96,6 +96,8 @@ config ENCRYPTED_HIBERNATION
bool "Encryption support for userspace snapshots"
depends on HIBERNATION_SNAPSHOT_DEV
depends on CRYPTO_AEAD2=y
+ depends on TCG_TPM2_RESTRICT_PCR
+ depends on TRUSTED_KEYS=y
help
Enable support for kernel-based encryption of hibernation snapshots
created by uswsusp tools.
diff --git a/kernel/power/snapenc.c b/kernel/power/snapenc.c
index 0d055ea6203a5b..f1db4eddb3c34c 100644
--- a/kernel/power/snapenc.c
+++ b/kernel/power/snapenc.c
@@ -4,13 +4,23 @@
#include <linux/crypto.h>
#include <crypto/aead.h>
#include <crypto/gcm.h>
+#include <keys/trusted-type.h>
+#include <linux/key-type.h>
#include <linux/random.h>
#include <linux/mm.h>
+#include <linux/tpm.h>
#include <linux/uaccess.h>

#include "power.h"
#include "user.h"

+/* sha256("To sleep, perchance to dream") */
+static struct tpm_digest known_digest = { .alg_id = TPM_ALG_SHA256,
+ .digest = {0x92, 0x78, 0x3d, 0x79, 0x2d, 0x00, 0x31, 0xb0, 0x55, 0xf9,
+ 0x1e, 0x0d, 0xce, 0x83, 0xde, 0x1d, 0xc4, 0xc5, 0x8e, 0x8c,
+ 0xf1, 0x22, 0x38, 0x6c, 0x33, 0xb1, 0x14, 0xb7, 0xec, 0x05,
+ 0x5f, 0x49}};
+
/* Encrypt more data from the snapshot into the staging area. */
static int snapshot_encrypt_refill(struct snapshot_data *data)
{
@@ -313,6 +323,16 @@ void snapshot_teardown_encryption(struct snapshot_data *data)
{
int i;

+ /*
+ * Do NULL checks so this function can safely be called from error paths
+ * and other places where this context may not be fully set up.
+ */
+ if (data->key) {
+ key_revoke(data->key);
+ key_put(data->key);
+ data->key = NULL;
+ }
+
if (data->aead_req) {
aead_request_free(data->aead_req);
data->aead_req = NULL;
@@ -381,10 +401,82 @@ static int snapshot_setup_encryption_common(struct snapshot_data *data)
return rc;
}

+static int snapshot_create_kernel_key(struct snapshot_data *data)
+{
+ /* Create a key sealed by the SRK. */
+ char *keyinfo = "new\t32\tkeyhandle=0x81000000";
+ const struct cred *cred = current_cred();
+ struct tpm_digest *digests = NULL;
+ struct key *key = NULL;
+ struct tpm_chip *chip;
+ int ret, i;
+
+ chip = tpm_default_chip();
+ if (!chip)
+ return -ENODEV;
+
+ if (!(tpm_is_tpm2(chip))) {
+ ret = -ENODEV;
+ goto out_dev;
+ }
+
+ ret = tpm2_pcr_reset(chip, 23);
+ if (ret)
+ goto out;
+
+ digests = kcalloc(chip->nr_allocated_banks, sizeof(struct tpm_digest),
+ GFP_KERNEL);
+ if (!digests) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < chip->nr_allocated_banks; i++) {
+ digests[i].alg_id = chip->allocated_banks[i].alg_id;
+ if (digests[i].alg_id == known_digest.alg_id)
+ memcpy(&digests[i], &known_digest, sizeof(known_digest));
+ }
+
+ ret = tpm_pcr_extend(chip, 23, digests);
+ if (ret != 0)
+ goto out;
+
+ key = key_alloc(&key_type_trusted, "swsusp", GLOBAL_ROOT_UID,
+ GLOBAL_ROOT_GID, cred, 0, KEY_ALLOC_NOT_IN_QUOTA,
+ NULL);
+
+ if (IS_ERR(key)) {
+ ret = PTR_ERR(key);
+ key = NULL;
+ goto out;
+ }
+
+ ret = key_instantiate_and_link(key, keyinfo, sizeof(keyinfo), NULL,
+ NULL);
+ if (ret != 0)
+ goto out;
+
+ data->key = key;
+ key = NULL;
+
+out:
+ if (key) {
+ key_revoke(key);
+ key_put(key);
+ }
+
+ kfree(digests);
+ tpm2_pcr_reset(chip, 23);
+
+out_dev:
+ put_device(&chip->dev);
+ return ret;
+}
+
int snapshot_get_encryption_key(struct snapshot_data *data,
struct uswsusp_key_blob __user *key)
{
- u8 aead_key[SNAPSHOT_ENCRYPTION_KEY_SIZE];
+ struct trusted_key_payload *payload;
u8 nonce[USWSUSP_KEY_NONCE_SIZE];
int rc;

@@ -400,21 +492,28 @@ int snapshot_get_encryption_key(struct snapshot_data *data,
get_random_bytes(nonce, sizeof(nonce));
memcpy(&data->nonce_low, &nonce[0], sizeof(data->nonce_low));
memcpy(&data->nonce_high, &nonce[8], sizeof(data->nonce_high));
- /* Build a random key */
- get_random_bytes(aead_key, sizeof(aead_key));
- rc = crypto_aead_setkey(data->aead_tfm, aead_key, sizeof(aead_key));
+
+ /* Create a kernel key, and set it. */
+ rc = snapshot_create_kernel_key(data);
+ if (rc)
+ goto fail;
+
+ payload = data->key->payload.data[0];
+ /* Install the key */
+ rc = crypto_aead_setkey(data->aead_tfm, payload->key, SNAPSHOT_ENCRYPTION_KEY_SIZE);
if (rc)
goto fail;

- /* Hand the key back to user mode (to be changed!) */
- rc = put_user(sizeof(struct uswsusp_key_blob), &key->blob_len);
+ /* Hand the key back to user mode in sealed form. */
+ rc = put_user(payload->blob_len, &key->blob_len);
if (rc)
goto fail;

- rc = copy_to_user(&key->blob, &aead_key, sizeof(aead_key));
+ rc = copy_to_user(&key->blob, &payload->blob, payload->blob_len);
if (rc)
goto fail;

+ /* The nonce just gets handed back in the clear. */
rc = copy_to_user(&key->nonce, &nonce, sizeof(nonce));
if (rc)
goto fail;
@@ -426,10 +525,99 @@ int snapshot_get_encryption_key(struct snapshot_data *data,
return rc;
}

+static int snapshot_load_kernel_key(struct snapshot_data *data,
+ struct uswsusp_key_blob *blob)
+{
+
+ char *keytemplate = "load\t%s\tkeyhandle=0x81000000";
+ const struct cred *cred = current_cred();
+ struct tpm_digest *digests = NULL;
+ char *blobstring = NULL;
+ struct key *key = NULL;
+ struct tpm_chip *chip;
+ char *keyinfo = NULL;
+ int i, ret;
+
+ chip = tpm_default_chip();
+ if (!chip)
+ return -ENODEV;
+
+ if (!(tpm_is_tpm2(chip))) {
+ ret = -ENODEV;
+ goto out_dev;
+ }
+
+ ret = tpm2_pcr_reset(chip, 23);
+ if (ret)
+ goto out;
+
+ digests = kcalloc(chip->nr_allocated_banks, sizeof(struct tpm_digest),
+ GFP_KERNEL);
+ if (!digests)
+ goto out;
+
+ for (i = 0; i < chip->nr_allocated_banks; i++) {
+ digests[i].alg_id = chip->allocated_banks[i].alg_id;
+ if (digests[i].alg_id == known_digest.alg_id)
+ memcpy(&digests[i], &known_digest, sizeof(known_digest));
+ }
+
+ ret = tpm_pcr_extend(chip, 23, digests);
+ if (ret != 0)
+ goto out;
+
+ blobstring = kmalloc(blob->blob_len * 2, GFP_KERNEL);
+ if (!blobstring) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ bin2hex(blobstring, blob->blob, blob->blob_len);
+ keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring);
+ 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);
+
+ if (IS_ERR(key)) {
+ ret = PTR_ERR(key);
+ key = NULL;
+ goto out;
+ }
+
+ ret = key_instantiate_and_link(key, keyinfo, strlen(keyinfo) + 1, NULL,
+ NULL);
+ if (ret != 0)
+ goto out;
+
+ data->key = key;
+ key = NULL;
+
+out:
+ if (key) {
+ key_revoke(key);
+ key_put(key);
+ }
+
+ kfree(keyinfo);
+ kfree(blobstring);
+ kfree(digests);
+ tpm2_pcr_reset(chip, 23);
+
+out_dev:
+ put_device(&chip->dev);
+ return ret;
+}
+
int snapshot_set_encryption_key(struct snapshot_data *data,
struct uswsusp_key_blob __user *key)
{
struct uswsusp_key_blob blob;
+ struct trusted_key_payload *payload;
int rc;

/* It's too late if data's been pushed in. */
@@ -445,13 +633,18 @@ int snapshot_set_encryption_key(struct snapshot_data *data,
if (rc)
goto crypto_setup_fail;

- if (blob.blob_len != sizeof(struct uswsusp_key_blob)) {
+ if (blob.blob_len > sizeof(key->blob)) {
rc = -EINVAL;
goto crypto_setup_fail;
}

+ rc = snapshot_load_kernel_key(data, &blob);
+ if (rc)
+ goto crypto_setup_fail;
+
+ payload = data->key->payload.data[0];
rc = crypto_aead_setkey(data->aead_tfm,
- blob.blob,
+ payload->key,
SNAPSHOT_ENCRYPTION_KEY_SIZE);

if (rc)
diff --git a/kernel/power/user.h b/kernel/power/user.h
index ac429782abff85..6c86fb64ebe13e 100644
--- a/kernel/power/user.h
+++ b/kernel/power/user.h
@@ -31,6 +31,7 @@ struct snapshot_data {
uint64_t crypt_total;
uint64_t nonce_low;
uint64_t nonce_high;
+ struct key *key;
#endif

};
--
2.38.1.431.g37b22c650d-goog


2022-11-11 23:31:23

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

In addition to the private key and public key, the TPM2_Create
command may also return creation data, a creation hash, and a creation
ticket. These fields allow the TPM to attest to the contents of a
specified set of PCRs at the time the trusted key was created. Encrypted
hibernation will use this to ensure that PCRs settable only by the
kernel were set properly at the time of creation, indicating this is an
authentic hibernate key.

Encode these additional parameters into the ASN.1 created to represent
the key blob. The new fields are made optional so that they don't bloat
key blobs which don't need them, and to ensure interoperability with
old blobs.

Signed-off-by: Evan Green <[email protected]>
---

Changes in v5:
- Factored some math out to a helper function (Kees)
- Constified src in tpm2_key_encode().

Changes in v3:
- Fix SoB and -- note ordering (Kees)
- Add comments describing the TPM2 spec type names for the new fields
in tpm2key.asn1 (Kees)
- Add len buffer checks in tpm2_key_encode() (Kees)

This is a replacement for Matthew's original patch here:
https://patchwork.kernel.org/patch/12096489/

That patch was written before the exported key format was switched to
ASN.1. This patch accomplishes the same thing (saving, loading, and
getting pointers to the creation data) while utilizing the new ASN.1
format.

---
include/keys/trusted-type.h | 8 +
security/keys/trusted-keys/tpm2key.asn1 | 15 +-
security/keys/trusted-keys/trusted_tpm2.c | 253 +++++++++++++++++++---
3 files changed, 245 insertions(+), 31 deletions(-)

diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index 4eb64548a74f1a..209086fed240a5 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -22,15 +22,23 @@
#define MAX_BLOB_SIZE 512
#define MAX_PCRINFO_SIZE 64
#define MAX_DIGEST_SIZE 64
+#define MAX_CREATION_DATA 412
+#define MAX_TK 76

struct trusted_key_payload {
struct rcu_head rcu;
unsigned int key_len;
unsigned int blob_len;
+ unsigned int creation_len;
+ unsigned int creation_hash_len;
+ unsigned int tk_len;
unsigned char migratable;
unsigned char old_format;
unsigned char key[MAX_KEY_SIZE + 1];
unsigned char blob[MAX_BLOB_SIZE];
+ unsigned char *creation;
+ unsigned char *creation_hash;
+ unsigned char *tk;
};

struct trusted_key_options {
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad60068..608f8d9ca95fa8 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -7,5 +7,18 @@ TPMKey ::= SEQUENCE {
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
parent INTEGER ({tpm2_key_parent}),
pubkey OCTET STRING ({tpm2_key_pub}),
- privkey OCTET STRING ({tpm2_key_priv})
+ privkey OCTET STRING ({tpm2_key_priv}),
+ ---
+ --- A TPM2B_CREATION_DATA struct as returned from the TPM2_Create command.
+ ---
+ creationData [1] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_data}),
+ ---
+ --- A TPM2B_DIGEST of the creationHash as returned from the TPM2_Create
+ --- command.
+ ---
+ creationHash [2] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_hash}),
+ ---
+ --- A TPMT_TK_CREATION ticket as returned from the TPM2_Create command.
+ ---
+ creationTk [3] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_tk})
}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 2b2c8eb258d5bd..ff2aede8986236 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -28,24 +28,86 @@ static struct tpm2_hash tpm2_hash_map[] = {

static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };

+/* Helper function to advance past a __be16 length + buffer safely */
+static const u8 *get_sized_section(const u8 *src, const u8 *end, u16 *len)
+{
+ u32 length;
+
+ if (src + sizeof(u16) > end)
+ return NULL;
+
+ /* Include the size field in the returned section length. */
+ length = get_unaligned_be16(src) + sizeof(u16);
+ *len = length;
+ if (*len != length)
+ return NULL;
+
+ src += *len;
+ if (src > end)
+ return NULL;
+
+ return src;
+}
+
static int tpm2_key_encode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u8 *src, u32 len)
+ const u8 *src, u32 len)
{
const int SCRATCH_SIZE = PAGE_SIZE;
+ const u8 *end = src + len;
u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
u8 *work = scratch, *work1;
u8 *end_work = scratch + SCRATCH_SIZE;
- u8 *priv, *pub;
+ const u8 *priv, *pub;
+ const u8 *creation_data = NULL, *creation_hash = NULL, *creation_tk = NULL;
+ u16 creation_data_len, creation_hash_len = 0, creation_tk_len = 0;
u16 priv_len, pub_len;
+ int rc;

- priv_len = get_unaligned_be16(src) + 2;
priv = src;
+ src = get_sized_section(src, end, &priv_len);
+ if (!src)
+ return -EINVAL;

- src += priv_len;
-
- pub_len = get_unaligned_be16(src) + 2;
pub = src;
+ src = get_sized_section(src, end, &pub_len);
+ if (!src)
+ return -EINVAL;
+
+ creation_data = src;
+ src = get_sized_section(src, end, &creation_data_len);
+ if (!src)
+ return -EINVAL;
+
+ /*
+ * If the creation data has content, pull out the creation hash and
+ * ticket as well. Otherwise pretend it doesn't exist.
+ */
+ if (creation_data_len > sizeof(u16)) {
+ creation_hash = src;
+ src = get_sized_section(src, end, &creation_hash_len);
+ if (!src)
+ return -EINVAL;
+
+ /*
+ * The creation ticket (TPMT_TK_CREATION) consists of a 2 byte
+ * tag, 4 byte handle, and then a TPM2B_DIGEST, which is a 2
+ * byte length followed by data.
+ */
+ if (src + 8 > end)
+ return -EINVAL;
+
+ creation_tk = src;
+ src = get_sized_section(src + 6, end, &creation_tk_len);
+ if (!src)
+ return -EINVAL;
+
+ creation_tk_len += 6;
+
+ } else {
+ creation_data_len = 0;
+ creation_data = NULL;
+ }

if (!scratch)
return -ENOMEM;
@@ -63,26 +125,81 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
}

/*
- * Assume both octet strings will encode to a 2 byte definite length
+ * Assume each octet string will encode to a 2 byte definite length.
+ * Each optional octet string consumes one extra byte.
*
- * Note: For a well behaved TPM, this warning should never
- * trigger, so if it does there's something nefarious going on
+ * Note: For a well behaved TPM, this warning should never trigger, so
+ * if it does there's something nefarious going on
*/
- if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
- "BUG: scratch buffer is too small"))
- return -EINVAL;
+ if (WARN(work - scratch + pub_len + priv_len + creation_data_len +
+ creation_hash_len + creation_tk_len + (7 * 5) + 3 >
+ SCRATCH_SIZE,
+ "BUG: scratch buffer is too small")) {
+ rc = -EINVAL;
+ goto err;
+ }

work = asn1_encode_integer(work, end_work, options->keyhandle);
work = asn1_encode_octet_string(work, end_work, pub, pub_len);
work = asn1_encode_octet_string(work, end_work, priv, priv_len);
+ if (creation_data_len) {
+ u8 *scratch2 = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+ u8 *work2;
+ u8 *end_work2 = scratch2 + SCRATCH_SIZE;
+
+ if (!scratch2) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ work2 = asn1_encode_octet_string(scratch2,
+ end_work2,
+ creation_data,
+ creation_data_len);
+
+ work = asn1_encode_tag(work,
+ end_work,
+ 1,
+ scratch2,
+ work2 - scratch2);
+
+ work2 = asn1_encode_octet_string(scratch2,
+ end_work2,
+ creation_hash,
+ creation_hash_len);
+
+ work = asn1_encode_tag(work,
+ end_work,
+ 2,
+ scratch2,
+ work2 - scratch2);
+
+ work2 = asn1_encode_octet_string(scratch2,
+ end_work2,
+ creation_tk,
+ creation_tk_len);
+
+ work = asn1_encode_tag(work,
+ end_work,
+ 3,
+ scratch2,
+ work2 - scratch2);
+
+ kfree(scratch2);
+ }

work1 = payload->blob;
work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob),
scratch, work - scratch);
- if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed"))
- return PTR_ERR(work1);
+ if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) {
+ rc = PTR_ERR(work1);
+ goto err;
+ }

return work1 - payload->blob;
+err:
+ kfree(scratch);
+ return rc;
}

struct tpm2_key_context {
@@ -91,15 +208,21 @@ struct tpm2_key_context {
u32 pub_len;
const u8 *priv;
u32 priv_len;
+ const u8 *creation_data;
+ u32 creation_data_len;
+ const u8 *creation_hash;
+ u32 creation_hash_len;
+ const u8 *creation_tk;
+ u32 creation_tk_len;
};

static int tpm2_key_decode(struct trusted_key_payload *payload,
- struct trusted_key_options *options,
- u8 **buf)
+ struct trusted_key_options *options)
{
+ u64 data_len;
int ret;
struct tpm2_key_context ctx;
- u8 *blob;
+ u8 *blob, *buf;

memset(&ctx, 0, sizeof(ctx));

@@ -108,21 +231,57 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
if (ret < 0)
return ret;

- if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
+ data_len = ctx.priv_len + ctx.pub_len + ctx.creation_data_len +
+ ctx.creation_hash_len + ctx.creation_tk_len;
+
+ if (data_len > MAX_BLOB_SIZE)
return -EINVAL;

- blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
- if (!blob)
+ buf = kmalloc(data_len + 4, GFP_KERNEL);
+ if (!buf)
return -ENOMEM;

- *buf = blob;
+ blob = buf;
options->keyhandle = ctx.parent;

memcpy(blob, ctx.priv, ctx.priv_len);
blob += ctx.priv_len;

memcpy(blob, ctx.pub, ctx.pub_len);
+ blob += ctx.pub_len;
+ if (ctx.creation_data_len) {
+ memcpy(blob, ctx.creation_data, ctx.creation_data_len);
+ blob += ctx.creation_data_len;
+ }

+ if (ctx.creation_hash_len) {
+ memcpy(blob, ctx.creation_hash, ctx.creation_hash_len);
+ blob += ctx.creation_hash_len;
+ }
+
+ if (ctx.creation_tk_len) {
+ memcpy(blob, ctx.creation_tk, ctx.creation_tk_len);
+ blob += ctx.creation_tk_len;
+ }
+
+ /*
+ * Copy the buffer back into the payload blob since the creation
+ * info will be used after loading.
+ */
+ payload->blob_len = blob - buf;
+ memcpy(payload->blob, buf, payload->blob_len);
+ if (ctx.creation_data_len) {
+ payload->creation = payload->blob + ctx.priv_len + ctx.pub_len;
+ payload->creation_len = ctx.creation_data_len;
+ payload->creation_hash = payload->creation + ctx.creation_data_len;
+ payload->creation_hash_len = ctx.creation_hash_len;
+ payload->tk = payload->creation_hash +
+ payload->creation_hash_len;
+
+ payload->tk_len = ctx.creation_tk_len;
+ }
+
+ kfree(buf);
return 0;
}

@@ -185,6 +344,42 @@ int tpm2_key_priv(void *context, size_t hdrlen,
return 0;
}

+int tpm2_key_creation_data(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->creation_data = value;
+ ctx->creation_data_len = vlen;
+
+ return 0;
+}
+
+int tpm2_key_creation_hash(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->creation_hash = value;
+ ctx->creation_hash_len = vlen;
+
+ return 0;
+}
+
+int tpm2_key_creation_tk(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->creation_tk = value;
+ ctx->creation_tk_len = vlen;
+
+ return 0;
+}
+
/**
* tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
*
@@ -229,6 +424,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_options *options)
{
int blob_len = 0;
+ unsigned int offset;
struct tpm_buf buf;
u32 hash;
u32 flags;
@@ -317,13 +513,14 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
rc = -E2BIG;
goto out;
}
- if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
+ offset = TPM_HEADER_SIZE + 4;
+ if (tpm_buf_length(&buf) < offset + blob_len) {
rc = -EFAULT;
goto out;
}

blob_len = tpm2_key_encode(payload, options,
- &buf.data[TPM_HEADER_SIZE + 4],
+ &buf.data[offset],
blob_len);

out:
@@ -370,13 +567,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
int rc;
u32 attrs;

- rc = tpm2_key_decode(payload, options, &blob);
- if (rc) {
- /* old form */
- blob = payload->blob;
+ rc = tpm2_key_decode(payload, options);
+ if (rc)
payload->old_format = 1;
- }

+ blob = payload->blob;
/* new format carries keyhandle but old format doesn't */
if (!options->keyhandle)
return -EINVAL;
@@ -433,8 +628,6 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
(__be32 *) &buf.data[TPM_HEADER_SIZE]);

out:
- if (blob != payload->blob)
- kfree(blob);
tpm_buf_destroy(&buf);

if (rc > 0)
--
2.38.1.431.g37b22c650d-goog


2022-11-11 23:31:28

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
restricts usermode's ability to extend or reset PCR 23.

Under certain circumstances it might be desirable to enable the creation
of TPM-backed secrets that are only accessible to the kernel. In an
ideal world this could be achieved by using TPM localities, but these
don't appear to be available on consumer systems. An alternative is to
simply block userland from modifying one of the resettable PCRs, leaving
it available to the kernel. If the kernel ensures that no userland can
access the TPM while it is carrying out work, it can reset PCR 23,
extend it to an arbitrary value, create or load a secret, and then reset
the PCR again. Even if userland somehow obtains the sealed material, it
will be unable to unseal it since PCR 23 will never be in the
appropriate state.

This Kconfig is only properly supported for systems with TPM2 devices.
For systems with TPM1 devices, having this Kconfig enabled completely
restricts usermode's access to the TPM. TPM1 contains support for
tunnelled transports, which usermode could use to smuggle commands
through that this Kconfig is attempting to restrict.

Link: https://lore.kernel.org/lkml/[email protected]/
Co-developed-by: Matthew Garrett <[email protected]>
Signed-off-by: Matthew Garrett <[email protected]>
Signed-off-by: Evan Green <[email protected]>

---

Changes in v5:
- Change tags on RESTRICT_PCR patch (Kees)
- Rename to TCG_TPM2_RESTRICT_PCR
- Do nothing on TPM1.2 devices (Jarkko, Doug)

Changes in v4:
- Augment the commit message (Jarkko)

Changes in v3:
- Fix up commit message (Jarkko)
- tpm2_find_and_validate_cc() was split (Jarkko)
- Simply fully restrict TPM1 since v2 failed to account for tunnelled
transport sessions (Stefan and Jarkko).

Changes in v2:
- Fixed sparse warnings

drivers/char/tpm/Kconfig | 12 ++++++++++++
drivers/char/tpm/tpm-dev-common.c | 6 ++++++
drivers/char/tpm/tpm.h | 12 ++++++++++++
drivers/char/tpm/tpm2-cmd.c | 22 ++++++++++++++++++++++
4 files changed, 52 insertions(+)

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 927088b2c3d3f2..e6d3aa9f6c694f 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -211,4 +211,16 @@ config TCG_FTPM_TEE
This driver proxies for firmware TPM running in TEE.

source "drivers/char/tpm/st33zp24/Kconfig"
+
+config TCG_TPM2_RESTRICT_PCR
+ bool "Restrict userland access to PCR 23 on TPM2 devices"
+ depends on TCG_TPM
+ help
+ If set, block userland from extending or resetting PCR 23 on TPM2.0
+ and later systems. This allows the PCR to be restricted to in-kernel
+ use, preventing userland from being able to make use of data sealed to
+ the TPM by the kernel. This is required for secure hibernation
+ support, but should be left disabled if any userland may require
+ access to PCR23. This is a TPM2-only feature, enabling this on a TPM1
+ machine is effectively a no-op.
endif # TCG_TPM
diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
index dc4c0a0a512903..66d15a2a967443 100644
--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -198,6 +198,12 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
priv->response_read = false;
*off = 0;

+ if (priv->chip->flags & TPM_CHIP_FLAG_TPM2) {
+ ret = tpm2_cmd_restricted(priv->chip, priv->data_buffer, size);
+ if (ret)
+ goto out;
+ }
+
/*
* If in nonblocking mode schedule an async job to send
* the command return the size.
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index f1e0f490176f01..7fb746d210f59d 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -245,4 +245,16 @@ void tpm_bios_log_setup(struct tpm_chip *chip);
void tpm_bios_log_teardown(struct tpm_chip *chip);
int tpm_dev_common_init(void);
void tpm_dev_common_exit(void);
+
+#ifdef CONFIG_TCG_TPM2_RESTRICT_PCR
+#define TPM_RESTRICTED_PCR 23
+
+int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size);
+#else
+static inline int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer,
+ size_t size)
+{
+ return 0;
+}
+#endif
#endif
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 303ce2ea02a4b0..3bc5546fddc792 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -778,3 +778,25 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc)

return -1;
}
+
+#ifdef CONFIG_TCG_TPM2_RESTRICT_PCR
+int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size)
+{
+ int cc = tpm2_find_and_validate_cc(chip, NULL, buffer, size);
+ __be32 *handle;
+
+ switch (cc) {
+ case TPM2_CC_PCR_EXTEND:
+ case TPM2_CC_PCR_RESET:
+ if (size < (TPM_HEADER_SIZE + sizeof(u32)))
+ return -EINVAL;
+
+ handle = (__be32 *)&buffer[TPM_HEADER_SIZE];
+ if (be32_to_cpu(*handle) == TPM_RESTRICTED_PCR)
+ return -EPERM;
+ break;
+ }
+
+ return 0;
+}
+#endif
--
2.38.1.431.g37b22c650d-goog


2022-11-11 23:35:53

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 02/11] tpm: Export and rename tpm2_find_and_validate_cc()

Export tpm_find_and_validate_cc() since it will be needed by an upcoming
change allowing access to certain PCRs to be restricted to the kernel.
In order to export it consistently, and because it's a tpm2-only
function, rename it to tpm2_find_and_validate_cc().

Signed-off-by: Evan Green <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
Acked-by: Jarkko Sakkinen <[email protected]>

---

(no changes since v3)

Changes in v3:
- Split find_and_validate_cc() export to its own patch (Jarkko)
- Rename tpm_find_and_validate_cc() to tpm2_find_and_validate_cc().

drivers/char/tpm/tpm.h | 3 +++
drivers/char/tpm/tpm2-space.c | 8 ++++----
2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 24ee4e1cc452a0..f1e0f490176f01 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -231,6 +231,9 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
int tpm2_init_space(struct tpm_space *space, unsigned int buf_size);
void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space);
void tpm2_flush_space(struct tpm_chip *chip);
+int tpm2_find_and_validate_cc(struct tpm_chip *chip,
+ struct tpm_space *space,
+ const void *cmd, size_t len);
int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,
size_t cmdsiz);
int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, void *buf,
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index ffb35f0154c16c..ca34cc006e7f8d 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -262,9 +262,9 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
return 0;
}

-static int tpm_find_and_validate_cc(struct tpm_chip *chip,
- struct tpm_space *space,
- const void *cmd, size_t len)
+int tpm2_find_and_validate_cc(struct tpm_chip *chip,
+ struct tpm_space *space,
+ const void *cmd, size_t len)
{
const struct tpm_header *header = (const void *)cmd;
int i;
@@ -306,7 +306,7 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,
if (!space)
return 0;

- cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz);
+ cc = tpm2_find_and_validate_cc(chip, space, cmd, cmdsiz);
if (cc < 0)
return cc;

--
2.38.1.431.g37b22c650d-goog


2022-11-11 23:36:50

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 06/11] security: keys: trusted: Verify creation data

If a loaded key contains creation data, ask the TPM to verify that
creation data. This allows users like encrypted hibernate to know that
the loaded and parsed creation data has not been tampered with.

Suggested-by: Matthew Garrett <[email protected]>
Signed-off-by: Evan Green <[email protected]>
Reviewed-by: Kees Cook <[email protected]>

---
Source material for this change is at:
https://patchwork.kernel.org/project/linux-pm/patch/[email protected]/

(no changes since v3)

Changes in v3:
- Changed funky tag to suggested-by (Kees). Matthew, holler if you want
something different.

Changes in v2:
- Adjust hash len by 2 due to new ASN.1 storage, and add underflow
check.

include/linux/tpm.h | 1 +
security/keys/trusted-keys/trusted_tpm2.c | 77 ++++++++++++++++++++++-
2 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 70134e6551745f..9c2ee3e30ffa5d 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -224,6 +224,7 @@ enum tpm2_command_codes {
TPM2_CC_SELF_TEST = 0x0143,
TPM2_CC_STARTUP = 0x0144,
TPM2_CC_SHUTDOWN = 0x0145,
+ TPM2_CC_CERTIFYCREATION = 0x014A,
TPM2_CC_NV_READ = 0x014E,
TPM2_CC_CREATE = 0x0153,
TPM2_CC_LOAD = 0x0157,
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 3d84c3d41bdee1..402933f8c99ede 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -730,6 +730,74 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}

+/**
+ * tpm2_certify_creation() - execute a TPM2_CertifyCreation command
+ *
+ * @chip: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @blob_handle: the loaded TPM handle of the key
+ *
+ * Return: 0 on success
+ * -EINVAL on tpm error status
+ * < 0 error from tpm_send or tpm_buf_init
+ */
+static int tpm2_certify_creation(struct tpm_chip *chip,
+ struct trusted_key_payload *payload,
+ u32 blob_handle)
+{
+ struct tpm_header *head;
+ struct tpm_buf buf;
+ int rc;
+
+ rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CERTIFYCREATION);
+ if (rc)
+ return rc;
+
+ /* Use TPM_RH_NULL for signHandle */
+ tpm_buf_append_u32(&buf, 0x40000007);
+
+ /* Object handle */
+ tpm_buf_append_u32(&buf, blob_handle);
+
+ /* Auth */
+ tpm_buf_append_u32(&buf, 9);
+ tpm_buf_append_u32(&buf, TPM2_RS_PW);
+ tpm_buf_append_u16(&buf, 0);
+ tpm_buf_append_u8(&buf, 0);
+ tpm_buf_append_u16(&buf, 0);
+
+ /* Qualifying data */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* Creation data hash */
+ if (payload->creation_hash_len < 2) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ tpm_buf_append_u16(&buf, payload->creation_hash_len - 2);
+ tpm_buf_append(&buf, payload->creation_hash + 2,
+ payload->creation_hash_len - 2);
+
+ /* signature scheme */
+ tpm_buf_append_u16(&buf, TPM_ALG_NULL);
+
+ /* creation ticket */
+ tpm_buf_append(&buf, payload->tk, payload->tk_len);
+
+ rc = tpm_transmit_cmd(chip, &buf, 6, "certifying creation data");
+ if (rc)
+ goto out;
+
+ head = (struct tpm_header *)buf.data;
+
+ if (be32_to_cpu(head->return_code) != TPM2_RC_SUCCESS)
+ rc = -EINVAL;
+out:
+ tpm_buf_destroy(&buf);
+ return rc;
+}
+
/**
* tpm2_unseal_trusted() - unseal the payload of a trusted key
*
@@ -755,8 +823,15 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
goto out;

rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
- tpm2_flush_context(chip, blob_handle);
+ if (rc)
+ goto flush;
+
+ if (payload->creation_len)
+ rc = tpm2_certify_creation(chip, payload, blob_handle);

+
+flush:
+ tpm2_flush_context(chip, blob_handle);
out:
tpm_put_ops(chip);

--
2.38.1.431.g37b22c650d-goog


2022-11-11 23:40:45

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 10/11] PM: hibernate: Verify the digest encryption key

We want to ensure that the key used to encrypt the digest was created by
the kernel during hibernation. To do this we request that the TPM
include information about the value of PCR 23 at the time of key
creation in the sealed blob. On resume, we can make sure that the PCR
information in the creation data blob (already certified by the TPM to
be accurate) corresponds to the expected value. Since only
the kernel can touch PCR 23, if an attacker generates a key themselves
the value of PCR 23 will have been different, allowing us to reject the
key and boot normally instead of resuming.

Co-developed-by: Matthew Garrett <[email protected]>
Signed-off-by: Matthew Garrett <[email protected]>
Signed-off-by: Evan Green <[email protected]>

---
Matthew's original version of this patch is here:
https://patchwork.kernel.org/project/linux-pm/patch/[email protected]/

I moved the TPM2_CC_CERTIFYCREATION code into a separate change in the
trusted key code because the blob_handle was being flushed and was no
longer valid for use in CC_CERTIFYCREATION after the key was loaded. As
an added benefit of moving the certification into the trusted keys code,
we can drop the other patch from the original series that squirrelled
the blob_handle away.

Changes in v5:
- Use a struct to access creation data (Kees)
- Build PCR bitmask programmatically in creation data (Kees)

Changes in v4:
- Local variable reordering (Jarkko)

Changes in v3:
- Changed funky tag to Co-developed-by (Kees). Matthew, holler if you
want something different.

Changes in v2:
- Fixed some sparse warnings
- Use CRYPTO_LIB_SHA256 to get rid of sha256_data() (Eric)
- Adjusted offsets due to new ASN.1 format, and added a creation data
length check.

kernel/power/snapenc.c | 122 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 120 insertions(+), 2 deletions(-)

diff --git a/kernel/power/snapenc.c b/kernel/power/snapenc.c
index 0b38642628f7ce..f32c7347a330a4 100644
--- a/kernel/power/snapenc.c
+++ b/kernel/power/snapenc.c
@@ -22,6 +22,12 @@ static struct tpm_digest known_digest = { .alg_id = TPM_ALG_SHA256,
0xf1, 0x22, 0x38, 0x6c, 0x33, 0xb1, 0x14, 0xb7, 0xec, 0x05,
0x5f, 0x49}};

+/* sha256(sha256(empty_pcr | known_digest)) */
+static const char expected_digest[] = {0x2f, 0x96, 0xf2, 0x1b, 0x70, 0xa9, 0xe8,
+ 0x42, 0x25, 0x8e, 0x66, 0x07, 0xbe, 0xbc, 0xe3, 0x1f, 0x2c, 0x84, 0x4a,
+ 0x3f, 0x85, 0x17, 0x31, 0x47, 0x9a, 0xa5, 0x53, 0xbb, 0x23, 0x0c, 0x32,
+ 0xf3};
+
/* Derive a key from the kernel and user keys for data encryption. */
static int snapshot_use_user_key(struct snapshot_data *data)
{
@@ -491,7 +497,7 @@ 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";
+ char *keyinfo = "new\t32\tkeyhandle=0x81000000\tcreationpcrs=0x00800000";
const struct cred *cred = current_cred();
struct tpm_digest *digests = NULL;
struct key *key = NULL;
@@ -612,17 +618,57 @@ int snapshot_get_encryption_key(struct snapshot_data *data,
return rc;
}

+/* Currently only PCR23 is included in the creation data. */
+#define SNAPSHOT_KEY_PCR_COUNT 1
+
+/* The standard set of 24 PCRs takes 3 bytes to represent as a bitmask. */
+#define SNAPSHOT_KEY_PCR_SELECTION_BYTES 3
+
+/*
+ * The TPM loves to return variable length structures. This is the form of
+ * TPM2B_CREATION_DATA expected and verified for the snapshot key.
+ */
+struct snapshot_key_creation_data {
+ __be16 size;
+ /* TPMS_CREATION_DATA, the hashed portion */
+ struct {
+ /* TPML_PCR_SELECTION */
+ struct {
+ __be32 count;
+ /* TPMS_PCR_SELECTION */
+ struct {
+ __be16 hash_algo;
+ u8 size;
+ u8 select[SNAPSHOT_KEY_PCR_SELECTION_BYTES];
+ } __packed pcr_selections;
+ } __packed pcr_select;
+
+ /* TPM2B_DIGEST */
+ struct {
+ __be16 size;
+ u8 digest[SHA256_DIGEST_SIZE];
+ } __packed pcr_digest[SNAPSHOT_KEY_PCR_COUNT];
+
+ /* ... additional fields not verified ... */
+ } creation;
+} __packed;
+
static int snapshot_load_kernel_key(struct snapshot_data *data,
struct uswsusp_key_blob *blob)
{

char *keytemplate = "load\t%s\tkeyhandle=0x81000000";
+ struct snapshot_key_creation_data *creation;
const struct cred *cred = current_cred();
+ struct trusted_key_payload *payload;
+ char certhash[SHA256_DIGEST_SIZE];
struct tpm_digest *digests = NULL;
+ unsigned int creation_hash_length;
char *blobstring = NULL;
struct key *key = NULL;
struct tpm_chip *chip;
char *keyinfo = NULL;
+ u32 pcr_selection = 0;
int i, ret;

chip = tpm_default_chip();
@@ -640,8 +686,10 @@ static int snapshot_load_kernel_key(struct snapshot_data *data,

digests = kcalloc(chip->nr_allocated_banks, sizeof(struct tpm_digest),
GFP_KERNEL);
- if (!digests)
+ if (!digests) {
+ ret = -ENOMEM;
goto out;
+ }

for (i = 0; i < chip->nr_allocated_banks; i++) {
digests[i].alg_id = chip->allocated_banks[i].alg_id;
@@ -681,6 +729,76 @@ static int snapshot_load_kernel_key(struct snapshot_data *data,
if (ret != 0)
goto out;

+ /* Verify the creation hash matches the creation data. */
+ payload = key->payload.data[0];
+ creation = (struct snapshot_key_creation_data *)payload->creation;
+ if (!creation || !payload->creation_hash ||
+ (payload->creation_len < sizeof(*creation)) ||
+ (payload->creation_hash_len - 2 != SHA256_DIGEST_SIZE)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ creation_hash_length =
+ payload->creation_len -
+ offsetof(struct snapshot_key_creation_data, creation);
+
+ sha256((const u8 *)&creation->creation, creation_hash_length, certhash);
+ if (memcmp(payload->creation_hash + sizeof(__be16), certhash, SHA256_DIGEST_SIZE) != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* We now know that the creation data is authentic - parse it */
+
+ /* Verify TPML_PCR_SELECTION.count */
+ if (be32_to_cpu(creation->creation.pcr_select.count) !=
+ SNAPSHOT_KEY_PCR_COUNT) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Verify the PCRs are SHA256. */
+ if (be16_to_cpu(creation->creation.pcr_select.pcr_selections.hash_algo) !=
+ TPM_ALG_SHA256) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Gather the PCR selection bitmask. */
+ if (creation->creation.pcr_select.pcr_selections.size !=
+ SNAPSHOT_KEY_PCR_SELECTION_BYTES) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = SNAPSHOT_KEY_PCR_SELECTION_BYTES - 1; i >= 0; i--) {
+ pcr_selection <<= 8;
+ pcr_selection |=
+ creation->creation.pcr_select.pcr_selections.select[i];
+ }
+
+ /* Verify PCR 23 is selected. */
+ if (pcr_selection != (1 << 23)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Verify the first and only PCR hash is the expected size. */
+ if (be16_to_cpu(creation->creation.pcr_digest[0].size) !=
+ SHA256_DIGEST_SIZE) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Verify PCR 23 contained the expected value when the key was created. */
+ if (memcmp(&creation->creation.pcr_digest[0].digest, expected_digest,
+ SHA256_DIGEST_SIZE) != 0) {
+
+ ret = -EINVAL;
+ goto out;
+ }
+
data->key = key;
key = NULL;

--
2.38.1.431.g37b22c650d-goog


2022-11-11 23:55:34

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 05/11] security: keys: trusted: Allow storage of PCR values in creation data

From: Matthew Garrett <[email protected]>

When TPMs generate keys, they can also generate some information
describing the state of the PCRs at creation time. This data can then
later be certified by the TPM, allowing verification of the PCR values.
This allows us to determine the state of the system at the time a key
was generated. Add an additional argument to the trusted key creation
options, allowing the user to provide the set of PCRs that should have
their values incorporated into the creation data.

Link: https://lore.kernel.org/lkml/[email protected]/
Signed-off-by: Matthew Garrett <[email protected]>
Signed-off-by: Evan Green <[email protected]>
Reviewed-by: Ben Boeckel <[email protected]>
Reviewed-by: Kees Cook <[email protected]>

---

Changes in v5:
- Make Matthew's tag match author

Changes in v3:
- Clarified creationpcrs documentation (Ben)

.../security/keys/trusted-encrypted.rst | 6 +++++
include/keys/trusted-type.h | 1 +
security/keys/trusted-keys/trusted_tpm1.c | 9 +++++++
security/keys/trusted-keys/trusted_tpm2.c | 25 +++++++++++++++++--
4 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index 9bc9db8ec6517c..a1872964fe862f 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -199,6 +199,12 @@ Usage::
policyhandle= handle to an authorization policy session that defines the
same policy and with the same hash algorithm as was used to
seal the key.
+ creationpcrs= hex integer representing the set of PCRs to be
+ included in the creation data. For each bit set, the
+ corresponding PCR will be included in the key creation
+ data. Bit 0 corresponds to PCR0. Currently only the first
+ PC standard 24 PCRs are supported on the currently active
+ bank. Leading zeroes are optional. TPM2 only.

"keyctl print" returns an ascii hex copy of the sealed key, which is in standard
TPM_STORED_DATA format. The key length for new keys are always in bytes.
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index 209086fed240a5..8523d41507b2a4 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -54,6 +54,7 @@ struct trusted_key_options {
uint32_t policydigest_len;
unsigned char policydigest[MAX_DIGEST_SIZE];
uint32_t policyhandle;
+ uint32_t creation_pcrs;
};

struct trusted_key_ops {
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index aa108bea6739b3..2975827c01bec0 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -713,6 +713,7 @@ enum {
Opt_hash,
Opt_policydigest,
Opt_policyhandle,
+ Opt_creationpcrs,
};

static const match_table_t key_tokens = {
@@ -725,6 +726,7 @@ static const match_table_t key_tokens = {
{Opt_hash, "hash=%s"},
{Opt_policydigest, "policydigest=%s"},
{Opt_policyhandle, "policyhandle=%s"},
+ {Opt_creationpcrs, "creationpcrs=%s"},
{Opt_err, NULL}
};

@@ -858,6 +860,13 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
return -EINVAL;
opt->policyhandle = handle;
break;
+ case Opt_creationpcrs:
+ if (!tpm2)
+ return -EINVAL;
+ res = kstrtoint(args[0].from, 16, &opt->creation_pcrs);
+ if (res < 0)
+ return -EINVAL;
+ break;
default:
return -EINVAL;
}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index ff2aede8986236..3d84c3d41bdee1 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -428,7 +428,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct tpm_buf buf;
u32 hash;
u32 flags;
- int i;
+ int i, j;
int rc;

for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
@@ -497,7 +497,28 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
tpm_buf_append_u16(&buf, 0);

/* creation PCR */
- tpm_buf_append_u32(&buf, 0);
+ if (options->creation_pcrs) {
+ /* One bank */
+ tpm_buf_append_u32(&buf, 1);
+ /* Which bank to use */
+ tpm_buf_append_u16(&buf, hash);
+ /* Length of the PCR bitmask */
+ tpm_buf_append_u8(&buf, 3);
+ /* PCR bitmask */
+ for (i = 0; i < 3; i++) {
+ char tmp = 0;
+
+ for (j = 0; j < 8; j++) {
+ char bit = (i * 8) + j;
+
+ if (options->creation_pcrs & (1 << bit))
+ tmp |= (1 << j);
+ }
+ tpm_buf_append_u8(&buf, tmp);
+ }
+ } else {
+ tpm_buf_append_u32(&buf, 0);
+ }

if (buf.flags & TPM_BUF_OVERFLOW) {
rc = -E2BIG;
--
2.38.1.431.g37b22c650d-goog


2022-11-12 00:06:05

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 09/11] PM: hibernate: Mix user key in encrypted hibernate

Usermode may have their own data protection requirements when it comes
to encrypting the hibernate image. For example, users may want a policy
where the hibernate image is protected by a key derived both from
platform-level security as well as authentication data (such as a
password or PIN). This way, even if the platform is compromised (ie a
stolen laptop), sensitive data cannot be exfiltrated via the hibernate
image without additional data (like the user's password).

The kernel is already doing the encryption, but will be protecting its
key with the TPM alone. Allow usermode to mix in key content of their own
for the data portion of the hibernate image, so that the image
encryption key is determined both by a TPM-backed secret and
user-defined data.

To mix the user key in, we hash the kernel key followed by the user key,
and use the resulting hash as the new key. This allows usermode to mix
in its key material without giving it too much control over what key is
actually driving the encryption (which might be used to attack the
secret kernel key).

Limiting this to the data portion allows the kernel to receive the page
map and prepare its giant allocation even if this user key is not yet
available (ie the user has not yet finished typing in their password).
Once the user key becomes available, the data portion can be pushed
through to the kernel as well. This enables "preloading" scenarios,
where the hibernate image is loaded off of disk while the additional
key material (eg password) is being collected.

One annoyance of the "preloading" scheme is that hibernate image memory
is effectively double-allocated: first by the usermode process pulling
encrypted contents off of disk and holding it, and second by the kernel
in its giant allocation in prepare_image(). An interesting future
optimization would be to allow the kernel to accept and store encrypted
page data before the user key is available. This would remove the
double allocation problem, as usermode could push the encrypted pages
loaded from disk immediately without storing them. The kernel could defer
decryption of the data until the user key is available, while still
knowing the correct page locations to store the encrypted data in.

Signed-off-by: Evan Green <[email protected]>
---

Changes in v5:
- Remove pad struct member (Kees)

Changes in v2:
- Add missing static on snapshot_encrypted_byte_count()
- Fold in only the used kernel key bytes to the user key.
- Make the user key length 32 (Eric)
- Use CRYPTO_LIB_SHA256 for less boilerplate (Eric)

include/uapi/linux/suspend_ioctls.h | 17 ++-
kernel/power/Kconfig | 1 +
kernel/power/power.h | 1 +
kernel/power/snapenc.c | 166 ++++++++++++++++++++++++++--
kernel/power/snapshot.c | 5 +
kernel/power/user.c | 4 +
kernel/power/user.h | 13 +++
7 files changed, 195 insertions(+), 12 deletions(-)

diff --git a/include/uapi/linux/suspend_ioctls.h b/include/uapi/linux/suspend_ioctls.h
index b73026ef824bb9..7612874608bae4 100644
--- a/include/uapi/linux/suspend_ioctls.h
+++ b/include/uapi/linux/suspend_ioctls.h
@@ -14,6 +14,7 @@ struct resume_swap_area {
} __attribute__((packed));

#define USWSUSP_KEY_NONCE_SIZE 16
+#define USWSUSP_USER_KEY_SIZE 32

/*
* This structure is used to pass the kernel's hibernate encryption key in
@@ -22,9 +23,20 @@ struct resume_swap_area {
struct uswsusp_key_blob {
__u32 blob_len;
__u8 blob[512];
- __u8 nonce[USWSUSP_KEY_NONCE_SIZE];
+ __u8 nonce[USWSUSP_KEY_NONCE_SIZE] __nonstring;
} __attribute__((packed));

+/*
+ * Allow user mode to fold in key material for the data portion of the hibernate
+ * image.
+ */
+struct uswsusp_user_key {
+ /* Kernel returns the metadata size. */
+ __kernel_loff_t meta_size;
+ __u32 key_len;
+ __u8 key[USWSUSP_USER_KEY_SIZE] __nonstring;
+};
+
#define SNAPSHOT_IOC_MAGIC '3'
#define SNAPSHOT_FREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
#define SNAPSHOT_UNFREEZE _IO(SNAPSHOT_IOC_MAGIC, 2)
@@ -42,6 +54,7 @@ struct uswsusp_key_blob {
#define SNAPSHOT_AVAIL_SWAP_SIZE _IOR(SNAPSHOT_IOC_MAGIC, 19, __kernel_loff_t)
#define SNAPSHOT_ALLOC_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 20, __kernel_loff_t)
#define SNAPSHOT_ENABLE_ENCRYPTION _IOWR(SNAPSHOT_IOC_MAGIC, 21, struct uswsusp_key_blob)
-#define SNAPSHOT_IOC_MAXNR 21
+#define SNAPSHOT_SET_USER_KEY _IOWR(SNAPSHOT_IOC_MAGIC, 22, struct uswsusp_user_key)
+#define SNAPSHOT_IOC_MAXNR 22

#endif /* _LINUX_SUSPEND_IOCTLS_H */
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 420024f46992b2..5c1f8f3f7482d7 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -98,6 +98,7 @@ config ENCRYPTED_HIBERNATION
depends on CRYPTO_AEAD2=y
depends on TCG_TPM2_RESTRICT_PCR
depends on TRUSTED_KEYS=y
+ select CRYPTO_LIB_SHA256
help
Enable support for kernel-based encryption of hibernation snapshots
created by uswsusp tools.
diff --git a/kernel/power/power.h b/kernel/power/power.h
index b4f43394320961..5955e5cf692302 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -151,6 +151,7 @@ struct snapshot_handle {

extern unsigned int snapshot_additional_pages(struct zone *zone);
extern unsigned long snapshot_get_image_size(void);
+extern unsigned long snapshot_get_meta_page_count(void);
extern int snapshot_read_next(struct snapshot_handle *handle);
extern int snapshot_write_next(struct snapshot_handle *handle);
extern void snapshot_write_finalize(struct snapshot_handle *handle);
diff --git a/kernel/power/snapenc.c b/kernel/power/snapenc.c
index f1db4eddb3c34c..0b38642628f7ce 100644
--- a/kernel/power/snapenc.c
+++ b/kernel/power/snapenc.c
@@ -6,6 +6,7 @@
#include <crypto/gcm.h>
#include <keys/trusted-type.h>
#include <linux/key-type.h>
+#include <crypto/sha.h>
#include <linux/random.h>
#include <linux/mm.h>
#include <linux/tpm.h>
@@ -21,6 +22,44 @@ static struct tpm_digest known_digest = { .alg_id = TPM_ALG_SHA256,
0xf1, 0x22, 0x38, 0x6c, 0x33, 0xb1, 0x14, 0xb7, 0xec, 0x05,
0x5f, 0x49}};

+/* Derive a key from the kernel and user keys for data encryption. */
+static int snapshot_use_user_key(struct snapshot_data *data)
+{
+ u8 digest[SHA256_DIGEST_SIZE];
+ struct trusted_key_payload *payload = data->key->payload.data[0];
+ struct sha256_state sha256_state;
+
+ /*
+ * Hash the kernel key and the user key together. This folds in the user
+ * key, but not in a way that gives the user mode predictable control
+ * over the key bits.
+ */
+ sha256_init(&sha256_state);
+
+ BUILD_BUG_ON(sizeof(payload->key) < SNAPSHOT_ENCRYPTION_KEY_SIZE);
+
+ sha256_update(&sha256_state, payload->key, SNAPSHOT_ENCRYPTION_KEY_SIZE);
+ sha256_update(&sha256_state, data->user_key, sizeof(data->user_key));
+ sha256_final(&sha256_state, digest);
+
+ BUILD_BUG_ON(SNAPSHOT_ENCRYPTION_KEY_SIZE > SHA256_DIGEST_SIZE);
+
+ return crypto_aead_setkey(data->aead_tfm,
+ digest,
+ SNAPSHOT_ENCRYPTION_KEY_SIZE);
+}
+
+/* Check to see if it's time to switch to the user key, and do it if so. */
+static int snapshot_check_user_key_switch(struct snapshot_data *data)
+{
+ if (data->user_key_valid && data->meta_size &&
+ data->crypt_total == data->meta_size) {
+ return snapshot_use_user_key(data);
+ }
+
+ return 0;
+}
+
/* Encrypt more data from the snapshot into the staging area. */
static int snapshot_encrypt_refill(struct snapshot_data *data)
{
@@ -31,6 +70,15 @@ static int snapshot_encrypt_refill(struct snapshot_data *data)
int pg_idx;
int res;

+ if (data->crypt_total == 0) {
+ data->meta_size = snapshot_get_meta_page_count() << PAGE_SHIFT;
+
+ } else {
+ res = snapshot_check_user_key_switch(data);
+ if (res)
+ return res;
+ }
+
/*
* The first buffer is the associated data, set to the offset to prevent
* attacks that rearrange chunks.
@@ -41,6 +89,11 @@ static int snapshot_encrypt_refill(struct snapshot_data *data)
for (pg_idx = 0; pg_idx < CHUNK_SIZE; pg_idx++) {
void *buf = data->crypt_pages[pg_idx];

+ /* Stop at the meta page boundary to potentially switch keys. */
+ if (total &&
+ ((data->crypt_total + total) == data->meta_size))
+ break;
+
res = snapshot_read_next(&data->handle);
if (res < 0)
return res;
@@ -113,10 +166,10 @@ static int snapshot_decrypt_drain(struct snapshot_data *data)
sg_set_buf(&data->sg[1 + pg_idx], data->crypt_pages[pg_idx], PAGE_SIZE);

/*
- * It's possible this is the final decrypt, and there are fewer than
- * CHUNK_SIZE pages. If this is the case we would have just written the
- * auth tag into the first few bytes of a new page. Copy to the tag if
- * so.
+ * It's possible this is the final decrypt, or the final decrypt of the
+ * meta region, and there are fewer than CHUNK_SIZE pages. If this is
+ * the case we would have just written the auth tag into the first few
+ * bytes of a new page. Copy to the tag if so.
*/
if ((page_count < CHUNK_SIZE) &&
(data->crypt_offset - total) == sizeof(data->auth_tag)) {
@@ -171,7 +224,14 @@ static int snapshot_decrypt_drain(struct snapshot_data *data)
total += PAGE_SIZE;
}

+ if (data->crypt_total == 0)
+ data->meta_size = snapshot_get_meta_page_count() << PAGE_SHIFT;
+
data->crypt_total += total;
+ res = snapshot_check_user_key_switch(data);
+ if (res)
+ return res;
+
return 0;
}

@@ -220,8 +280,26 @@ static ssize_t snapshot_write_next_encrypted(struct snapshot_data *data,
if (data->crypt_offset < (PAGE_SIZE * CHUNK_SIZE)) {
size_t pg_idx = data->crypt_offset >> PAGE_SHIFT;
size_t pg_off = data->crypt_offset & (PAGE_SIZE - 1);
+ size_t size_avail = PAGE_SIZE;
*buf = data->crypt_pages[pg_idx] + pg_off;
- return PAGE_SIZE - pg_off;
+
+ /*
+ * If this is the boundary where the meta pages end, then just
+ * return enough for the auth tag.
+ */
+ if (data->meta_size && (data->crypt_total < data->meta_size)) {
+ uint64_t total_done =
+ data->crypt_total + data->crypt_offset;
+
+ if ((total_done >= data->meta_size) &&
+ (total_done <
+ (data->meta_size + SNAPSHOT_AUTH_TAG_SIZE))) {
+
+ size_avail = SNAPSHOT_AUTH_TAG_SIZE;
+ }
+ }
+
+ return size_avail - pg_off;
}

/* Use offsets just beyond the size to return the tag. */
@@ -303,9 +381,15 @@ ssize_t snapshot_write_encrypted(struct snapshot_data *data,
break;
}

- /* Drain the encrypted buffer if it's full. */
+ /*
+ * Drain the encrypted buffer if it's full, or if we hit the end
+ * of the meta pages and need a key change.
+ */
if ((data->crypt_offset >=
- ((PAGE_SIZE * CHUNK_SIZE) + SNAPSHOT_AUTH_TAG_SIZE))) {
+ ((PAGE_SIZE * CHUNK_SIZE) + SNAPSHOT_AUTH_TAG_SIZE)) ||
+ (data->meta_size && (data->crypt_total < data->meta_size) &&
+ ((data->crypt_total + data->crypt_offset) ==
+ (data->meta_size + SNAPSHOT_AUTH_TAG_SIZE)))) {

int rc;

@@ -349,6 +433,8 @@ void snapshot_teardown_encryption(struct snapshot_data *data)
data->crypt_pages[i] = NULL;
}
}
+
+ memset(data->user_key, 0, sizeof(data->user_key));
}

static int snapshot_setup_encryption_common(struct snapshot_data *data)
@@ -358,6 +444,7 @@ static int snapshot_setup_encryption_common(struct snapshot_data *data)
data->crypt_total = 0;
data->crypt_offset = 0;
data->crypt_size = 0;
+ data->user_key_valid = false;
memset(data->crypt_pages, 0, sizeof(data->crypt_pages));
/* This only works once per hibernate. */
if (data->aead_tfm)
@@ -660,15 +747,74 @@ int snapshot_set_encryption_key(struct snapshot_data *data,
return rc;
}

-loff_t snapshot_get_encrypted_image_size(loff_t raw_size)
+static loff_t snapshot_encrypted_byte_count(loff_t plain_size)
{
- loff_t pages = raw_size >> PAGE_SHIFT;
+ loff_t pages = plain_size >> PAGE_SHIFT;
loff_t chunks = (pages + (CHUNK_SIZE - 1)) / CHUNK_SIZE;
/*
* The encrypted size is the normal size, plus a stitched in
* authentication tag for every chunk of pages.
*/
- return raw_size + (chunks * SNAPSHOT_AUTH_TAG_SIZE);
+ return plain_size + (chunks * SNAPSHOT_AUTH_TAG_SIZE);
+}
+
+static loff_t snapshot_get_meta_data_size(void)
+{
+ loff_t pages = snapshot_get_meta_page_count();
+
+ return snapshot_encrypted_byte_count(pages << PAGE_SHIFT);
+}
+
+int snapshot_set_user_key(struct snapshot_data *data,
+ struct uswsusp_user_key __user *key)
+{
+ struct uswsusp_user_key user_key;
+ unsigned int key_len;
+ int rc;
+ loff_t size;
+
+ /*
+ * Return the metadata size, the number of bytes that can be fed in before
+ * the user data key is needed at resume time.
+ */
+ size = snapshot_get_meta_data_size();
+ rc = put_user(size, &key->meta_size);
+ if (rc)
+ return rc;
+
+ rc = copy_from_user(&user_key, key, sizeof(struct uswsusp_user_key));
+ if (rc)
+ return rc;
+
+ BUILD_BUG_ON(sizeof(data->user_key) < sizeof(user_key.key));
+
+ key_len = min_t(__u32, user_key.key_len, sizeof(data->user_key));
+ if (key_len < 8)
+ return -EINVAL;
+
+ /* Don't allow it if it's too late. */
+ if (data->crypt_total > data->meta_size)
+ return -EBUSY;
+
+ memset(data->user_key, 0, sizeof(data->user_key));
+ memcpy(data->user_key, user_key.key, key_len);
+ data->user_key_valid = true;
+ /* Install the key if the user is just under the wire. */
+ rc = snapshot_check_user_key_switch(data);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+loff_t snapshot_get_encrypted_image_size(loff_t raw_size)
+{
+ loff_t pages = raw_size >> PAGE_SHIFT;
+ loff_t meta_size;
+
+ pages -= snapshot_get_meta_page_count();
+ meta_size = snapshot_get_meta_data_size();
+ return snapshot_encrypted_byte_count(pages << PAGE_SHIFT) + meta_size;
}

int snapshot_finalize_decrypted_image(struct snapshot_data *data)
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index c20ca5fb9adc87..d8a30f3eaaf4c6 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -2083,6 +2083,11 @@ unsigned long snapshot_get_image_size(void)
return nr_copy_pages + nr_meta_pages + 1;
}

+unsigned long snapshot_get_meta_page_count(void)
+{
+ return nr_meta_pages + 1;
+}
+
static int init_header(struct swsusp_info *info)
{
memset(info, 0, sizeof(struct swsusp_info));
diff --git a/kernel/power/user.c b/kernel/power/user.c
index bba5cdbd2c0239..a66e32c9596da8 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -427,6 +427,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
error = snapshot_set_encryption_key(data, (void __user *)arg);
break;

+ case SNAPSHOT_SET_USER_KEY:
+ error = snapshot_set_user_key(data, (void __user *)arg);
+ break;
+
default:
error = -ENOTTY;

diff --git a/kernel/power/user.h b/kernel/power/user.h
index 6c86fb64ebe13e..d75fd287b4c3de 100644
--- a/kernel/power/user.h
+++ b/kernel/power/user.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */

#include <linux/crypto.h>
+#include <linux/suspend_ioctls.h>
#include <crypto/aead.h>
#include <crypto/aes.h>

@@ -32,6 +33,9 @@ struct snapshot_data {
uint64_t nonce_low;
uint64_t nonce_high;
struct key *key;
+ u8 user_key[USWSUSP_USER_KEY_SIZE] __nonstring;
+ bool user_key_valid;
+ uint64_t meta_size;
#endif

};
@@ -55,6 +59,9 @@ int snapshot_get_encryption_key(struct snapshot_data *data,
int snapshot_set_encryption_key(struct snapshot_data *data,
struct uswsusp_key_blob __user *key);

+int snapshot_set_user_key(struct snapshot_data *data,
+ struct uswsusp_user_key __user *key);
+
loff_t snapshot_get_encrypted_image_size(loff_t raw_size);

int snapshot_finalize_decrypted_image(struct snapshot_data *data);
@@ -89,6 +96,12 @@ static int snapshot_set_encryption_key(struct snapshot_data *data,
return -ENOTTY;
}

+static int snapshot_set_user_key(struct snapshot_data *data,
+ struct uswsusp_user_key __user *key)
+{
+ return -ENOTTY;
+}
+
static loff_t snapshot_get_encrypted_image_size(loff_t raw_size)
{
return raw_size;
--
2.38.1.431.g37b22c650d-goog


2022-11-12 00:06:31

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 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/[email protected]/
Co-developed-by: Matthew Garrett <[email protected]>
Signed-off-by: Matthew Garrett <[email protected]>
Signed-off-by: Evan Green <[email protected]>

---

(no changes since v4)

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 f32c7347a330a4..d3e1657674aaa1 100644
--- a/kernel/power/snapenc.c
+++ b/kernel/power/snapenc.c
@@ -443,6 +443,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;
@@ -497,11 +602,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();
@@ -534,6 +644,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);
@@ -544,7 +676,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;
@@ -558,7 +690,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:
@@ -657,7 +798,7 @@ 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";
struct snapshot_key_creation_data *creation;
const struct cred *cred = current_cred();
struct trusted_key_payload *payload;
@@ -665,6 +806,7 @@ static int snapshot_load_kernel_key(struct snapshot_data *data,
struct tpm_digest *digests = NULL;
unsigned int creation_hash_length;
char *blobstring = NULL;
+ int session_handle = -1;
struct key *key = NULL;
struct tpm_chip *chip;
char *keyinfo = NULL;
@@ -701,14 +843,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;
@@ -808,6 +957,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


2022-11-12 00:26:32

by Evan Green

[permalink] [raw]
Subject: [PATCH v5 01/11] tpm: Add support for in-kernel resetting of PCRs

Add an internal command for resetting a PCR. This will be used by the
encrypted hibernation code to set PCR23 to a known value. The
hibernation code will seal the hibernation key with a policy specifying
PCR23 be set to this known value as a mechanism to ensure that the
hibernation key is genuine. But to do this repeatedly, resetting the PCR
is necessary as well.

Link: https://lore.kernel.org/lkml/[email protected]/
Co-developed-by: Matthew Garrett <[email protected]>
Signed-off-by: Matthew Garrett <[email protected]>
Signed-off-by: Evan Green <[email protected]>

---

Changes in v5:
- Change to co-developed by Matthew (Kees)

Changes in v4:
- Open code tpm2_pcr_reset implementation in tpm-interface.c (Jarkko)
- Rename interface symbol to tpm2_pcr_reset, fix kerneldocs (Jarkko)

Changes in v3:
- Unify tpm1/2_pcr_reset prototypes (Jarkko)
- Wait no, remove the TPM1 stuff altogether (Jarkko)
- Remove extra From tag and blank in commit msg (Jarkko).

drivers/char/tpm/tpm-interface.c | 47 ++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm2-cmd.c | 7 -----
include/linux/tpm.h | 14 ++++++++++
3 files changed, 61 insertions(+), 7 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 1621ce8187052c..886277b2654e3b 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -342,6 +342,53 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);

+/**
+ * tpm2_pcr_reset - Reset the specified PCR
+ * @chip: A &struct tpm_chip instance, %NULL for the default chip
+ * @pcr_idx: The PCR to be reset
+ *
+ * Return: Same as with tpm_transmit_cmd(), or ENOTTY for TPM1 devices.
+ */
+int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx)
+{
+ struct tpm2_null_auth_area auth_area;
+ struct tpm_buf buf;
+ int rc;
+
+ chip = tpm_find_get_ops(chip);
+ if (!chip)
+ return -ENODEV;
+
+ if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
+ rc = -ENOTTY;
+ goto out;
+ }
+
+ rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_RESET);
+ if (rc)
+ goto out;
+
+ tpm_buf_append_u32(&buf, pcr_idx);
+
+ auth_area.handle = cpu_to_be32(TPM2_RS_PW);
+ auth_area.nonce_size = 0;
+ auth_area.attributes = 0;
+ auth_area.auth_size = 0;
+
+ tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
+ tpm_buf_append(&buf, (const unsigned char *)&auth_area,
+ sizeof(auth_area));
+
+ rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to reset a PCR");
+
+ tpm_buf_destroy(&buf);
+
+out:
+ tpm_put_ops(chip);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm2_pcr_reset);
+
/**
* tpm_send - send a TPM command
* @chip: a &struct tpm_chip instance, %NULL for the default chip
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 65d03867e114c5..303ce2ea02a4b0 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -216,13 +216,6 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
return rc;
}

-struct tpm2_null_auth_area {
- __be32 handle;
- __be16 nonce_size;
- u8 attributes;
- __be16 auth_size;
-} __packed;
-
/**
* tpm2_pcr_extend() - extend a PCR value
*
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index dfeb25a0362dee..70134e6551745f 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -219,6 +219,7 @@ enum tpm2_command_codes {
TPM2_CC_HIERARCHY_CONTROL = 0x0121,
TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129,
TPM2_CC_CREATE_PRIMARY = 0x0131,
+ TPM2_CC_PCR_RESET = 0x013D,
TPM2_CC_SEQUENCE_COMPLETE = 0x013E,
TPM2_CC_SELF_TEST = 0x0143,
TPM2_CC_STARTUP = 0x0144,
@@ -293,6 +294,13 @@ struct tpm_header {
};
} __packed;

+struct tpm2_null_auth_area {
+ __be32 handle;
+ __be16 nonce_size;
+ u8 attributes;
+ __be16 auth_size;
+} __packed;
+
/* A string buffer type for constructing TPM commands. This is based on the
* ideas of string buffer code in security/keys/trusted.h but is heap based
* in order to keep the stack usage minimal.
@@ -423,6 +431,7 @@ extern ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
size_t min_rsp_body_length, const char *desc);
extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digest);
+extern int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx);
extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests);
extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen);
@@ -440,6 +449,11 @@ static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx,
return -ENODEV;
}

+static inline int tpm2_pcr_reset(struct tpm_chip *chip, int pcr_idx)
+{
+ return -ENODEV;
+}
+
static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests)
{
--
2.38.1.431.g37b22c650d-goog


2022-11-13 20:39:37

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 01/11] tpm: Add support for in-kernel resetting of PCRs

On Fri, Nov 11, 2022 at 03:16:26PM -0800, Evan Green wrote:
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index 1621ce8187052c..886277b2654e3b 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -342,6 +342,53 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> }
> EXPORT_SYMBOL_GPL(tpm_pcr_extend);
>
> +/**
> + * tpm2_pcr_reset - Reset the specified PCR

Should this function be in drivers/char/tpm/tpm2-cmd.c instead of here?

> + * @chip: A &struct tpm_chip instance, %NULL for the default chip
> + * @pcr_idx: The PCR to be reset
> + *
> + * Return: Same as with tpm_transmit_cmd(), or ENOTTY for TPM1 devices.
> + */
> +int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx)

The callers of this function assume it returns a negative errno value. But
actually it can return positive TPM2_RC_* error codes as well. Probably you
should make it only return negative errno values.

> +{
> + struct tpm2_null_auth_area auth_area;
> + struct tpm_buf buf;
> + int rc;
> +
> + chip = tpm_find_get_ops(chip);
> + if (!chip)
> + return -ENODEV;
> +
> + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
> + rc = -ENOTTY;
> + goto out;
> + }
> +
> + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_RESET);
> + if (rc)
> + goto out;
> +
> + tpm_buf_append_u32(&buf, pcr_idx);
> +
> + auth_area.handle = cpu_to_be32(TPM2_RS_PW);
> + auth_area.nonce_size = 0;
> + auth_area.attributes = 0;
> + auth_area.auth_size = 0;
> +
> + tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));

sizeof(struct tpm2_null_auth_area) => sizeof(auth_area)

> + tpm_buf_append(&buf, (const unsigned char *)&auth_area,

unsigned char => u8

Also, since the code to append a "null" authorization area appears in both
tpm2_pcr_reset() and tpm2_pcr_extend(), perhaps it should be refactored into a
helper function?

> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index dfeb25a0362dee..70134e6551745f 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -219,6 +219,7 @@ enum tpm2_command_codes {
> TPM2_CC_HIERARCHY_CONTROL = 0x0121,
> TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129,
> TPM2_CC_CREATE_PRIMARY = 0x0131,
> + TPM2_CC_PCR_RESET = 0x013D,
> TPM2_CC_SEQUENCE_COMPLETE = 0x013E,
> TPM2_CC_SELF_TEST = 0x0143,
> TPM2_CC_STARTUP = 0x0144,
> @@ -293,6 +294,13 @@ struct tpm_header {
> };
> } __packed;
>
> +struct tpm2_null_auth_area {
> + __be32 handle;
> + __be16 nonce_size;
> + u8 attributes;
> + __be16 auth_size;
> +} __packed;

struct tpm2_null_auth_area is only used by code in drivers/char/tpm/, so should
its declaration go in the internal header drivers/char/tpm/tpm.h instead?

- Eric

2022-11-13 21:09:25

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Fri, Nov 11, 2022 at 03:16:28PM -0800, Evan Green wrote:
> Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled

TCG_TPM_RESTRICT_PCR => TCG_TPM2_RESTRICT_PCR

> For systems with TPM1 devices, having this Kconfig enabled completely
> restricts usermode's access to the TPM.

This doesn't appear to actually be the case.

> +config TCG_TPM2_RESTRICT_PCR
> + bool "Restrict userland access to PCR 23 on TPM2 devices"
> + depends on TCG_TPM

I assume you also considered making this a once-settable sysctl, or similar?
I guess this kconfig is fine for now, but IMO it does violate the concept of
"kernel provides mechanism, not policy".

> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 303ce2ea02a4b0..3bc5546fddc792 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -778,3 +778,25 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
>
> return -1;
> }
> +
> +#ifdef CONFIG_TCG_TPM2_RESTRICT_PCR
> +int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size)
> +{
> + int cc = tpm2_find_and_validate_cc(chip, NULL, buffer, size);
> + __be32 *handle;
> +
> + switch (cc) {
> + case TPM2_CC_PCR_EXTEND:
> + case TPM2_CC_PCR_RESET:
> + if (size < (TPM_HEADER_SIZE + sizeof(u32)))
> + return -EINVAL;
> +
> + handle = (__be32 *)&buffer[TPM_HEADER_SIZE];
> + if (be32_to_cpu(*handle) == TPM_RESTRICTED_PCR)
> + return -EPERM;

get_unaligned_be32((__be32 *)&buffer[TPM_HEADER_SIZE]),
to avoid an unaligned memory access.

> + break;
> + }
> +
> + return 0;

So, if tpm2_find_and_validate_cc() returns an error code, the command is *not*
restricted, even if it uses one of the forbidden command codes. Are you sure
there are no loopholes here?

- Eric

2022-11-13 21:48:43

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

On Fri, Nov 11, 2022 at 03:16:29PM -0800, Evan Green wrote:
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
> index f57f869ad60068..608f8d9ca95fa8 100644
> --- a/security/keys/trusted-keys/tpm2key.asn1
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -7,5 +7,18 @@ TPMKey ::= SEQUENCE {
> emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
> parent INTEGER ({tpm2_key_parent}),
> pubkey OCTET STRING ({tpm2_key_pub}),
> - privkey OCTET STRING ({tpm2_key_priv})
> + privkey OCTET STRING ({tpm2_key_priv}),
> + ---
> + --- A TPM2B_CREATION_DATA struct as returned from the TPM2_Create command.
> + ---
> + creationData [1] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_data}),
> + ---
> + --- A TPM2B_DIGEST of the creationHash as returned from the TPM2_Create
> + --- command.
> + ---
> + creationHash [2] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_hash}),
> + ---
> + --- A TPMT_TK_CREATION ticket as returned from the TPM2_Create command.
> + ---
> + creationTk [3] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_tk})
> }

The commit that added this file claimed:

"The benefit of the ASN.1 format is that it's a standard and thus the
exported key can be used by userspace tools (openssl_tpm2_engine,
openconnect and tpm2-tss-engine"

Are these new fields in compliance with whatever standard that was referring to?

Or was that just referring to ASN.1 itself?

> +/* Helper function to advance past a __be16 length + buffer safely */
> +static const u8 *get_sized_section(const u8 *src, const u8 *end, u16 *len)
> +{
> + u32 length;
> +
> + if (src + sizeof(u16) > end)
> + return NULL;

'end - src < sizeof(u16)', so the pointer isn't advanced past the end.

> +
> + /* Include the size field in the returned section length. */
> + length = get_unaligned_be16(src) + sizeof(u16);
> + *len = length;
> + if (*len != length)
> + return NULL;
> +
> + src += *len;
> + if (src > end)
> + return NULL;
> +
> + return src;

Similarly:

if (end - src < *len)
return NULL;

return src + *len;

> + /*
> + * The creation ticket (TPMT_TK_CREATION) consists of a 2 byte
> + * tag, 4 byte handle, and then a TPM2B_DIGEST, which is a 2
> + * byte length followed by data.
> + */
> + if (src + 8 > end)

end - src < 8

And actually it really should be 6 instead of 8, to match the code below.
get_sized_section() already validates that there are at least 2 more bytes.

> + return -EINVAL;
> +
> + creation_tk = src;
> + src = get_sized_section(src + 6, end, &creation_tk_len);
> + if (!src)
> + return -EINVAL;
> +
> + creation_tk_len += 6;
> +
> + } else {
> + creation_data_len = 0;
> + creation_data = NULL;
> + }
>
> if (!scratch)
> return -ENOMEM;
> @@ -63,26 +125,81 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> }
>
> /*
> - * Assume both octet strings will encode to a 2 byte definite length
> + * Assume each octet string will encode to a 2 byte definite length.
> + * Each optional octet string consumes one extra byte.
> *
> - * Note: For a well behaved TPM, this warning should never
> - * trigger, so if it does there's something nefarious going on
> + * Note: For a well behaved TPM, this warning should never trigger, so
> + * if it does there's something nefarious going on
> */
> - if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
> - "BUG: scratch buffer is too small"))
> - return -EINVAL;
> + if (WARN(work - scratch + pub_len + priv_len + creation_data_len +
> + creation_hash_len + creation_tk_len + (7 * 5) + 3 >
> + SCRATCH_SIZE,
> + "BUG: scratch buffer is too small")) {
> + rc = -EINVAL;
> + goto err;
> + }

This appears to be fixing a memory leak in the error case.

The same memory leak also still appears above in:

if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
return PTR_ERR(w);

Maybe both should be fixed in a separate patch.

> + work2 = asn1_encode_octet_string(scratch2,
> + end_work2,
> + creation_data,
> + creation_data_len);
> +
> + work = asn1_encode_tag(work,
> + end_work,
> + 1,
> + scratch2,
> + work2 - scratch2);

There's no helper function to do these two steps together?

> +
> - if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed"))
> - return PTR_ERR(work1);
> + if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) {
> + rc = PTR_ERR(work1);
> + goto err;
> + }
>
> return work1 - payload->blob;
> +err:
> + kfree(scratch);
> + return rc;

Is this another memory leak fix that is unrelated to the functionality added by
this patch?

Also, isn't 'scratch' still being leaked in the success case?

> static int tpm2_key_decode(struct trusted_key_payload *payload,
> - struct trusted_key_options *options,
> - u8 **buf)
> + struct trusted_key_options *options)
> {
> + u64 data_len;
> int ret;
> struct tpm2_key_context ctx;
> - u8 *blob;
> + u8 *blob, *buf;
>
> memset(&ctx, 0, sizeof(ctx));
>
> @@ -108,21 +231,57 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
> if (ret < 0)
> return ret;
>
> - if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
> + data_len = ctx.priv_len + ctx.pub_len + ctx.creation_data_len +
> + ctx.creation_hash_len + ctx.creation_tk_len;

It's unclear why 'data_len' is a u64, given that the value assigned to it always
fits in a u32. Perhaps you intended to do the additions with 64-bit numbers so
that they can't overflow.

But shouldn't the lengths already be bounded by size of the ASN.1 blob before
even reaching here, anyway?

> +
> + if (data_len > MAX_BLOB_SIZE)
> return -EINVAL;
>
> - blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
> - if (!blob)
> + buf = kmalloc(data_len + 4, GFP_KERNEL);
> + if (!buf)
> return -ENOMEM;

It's unclear what the '+ 4' is for.

> @@ -229,6 +424,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> struct trusted_key_options *options)
> {
> int blob_len = 0;
> + unsigned int offset;
> struct tpm_buf buf;
> u32 hash;
> u32 flags;
> @@ -317,13 +513,14 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> rc = -E2BIG;
> goto out;
> }
> - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
> + offset = TPM_HEADER_SIZE + 4;
> + if (tpm_buf_length(&buf) < offset + blob_len) {
> rc = -EFAULT;
> goto out;
> }
>
> blob_len = tpm2_key_encode(payload, options,
> - &buf.data[TPM_HEADER_SIZE + 4],
> + &buf.data[offset],
> blob_len);

This hunk of the patch doesn't seem to serve any purpose.

- Eric

2022-11-13 22:45:27

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 06/11] security: keys: trusted: Verify creation data

On Fri, Nov 11, 2022 at 03:16:31PM -0800, Evan Green wrote:
> security: keys: trusted: Verify creation data
>
> If a loaded key contains creation data, ask the TPM to verify that
> creation data. This allows users like encrypted hibernate to know that
> the loaded and parsed creation data has not been tampered with.

I don't understand what the purpose of this is.

I thought that the way to "seal" a key to a TPM PCR is to include the PCR in the
"policy".

Are you doing that too? What is the purpose of using the "creation data"?

> + /* Auth */
> + tpm_buf_append_u32(&buf, 9);
> + tpm_buf_append_u32(&buf, TPM2_RS_PW);
> + tpm_buf_append_u16(&buf, 0);
> + tpm_buf_append_u8(&buf, 0);
> + tpm_buf_append_u16(&buf, 0);

This is struct tpm2_null_auth_area, so this is another place that could take
advantage of a new helper function to append it.

> + /* Creation data hash */
> + if (payload->creation_hash_len < 2) {
> + rc = -EINVAL;
> + goto out;
> + }
> +
> + tpm_buf_append_u16(&buf, payload->creation_hash_len - 2);
> + tpm_buf_append(&buf, payload->creation_hash + 2,
> + payload->creation_hash_len - 2);

So the first two bytes of creation_hash are a redundant length field that needs
to be ignored here? Perhaps tpm2_key_encode() shouldn't include that redundant
length field?

> +
> + /* signature scheme */
> + tpm_buf_append_u16(&buf, TPM_ALG_NULL);
> +
> + /* creation ticket */
> + tpm_buf_append(&buf, payload->tk, payload->tk_len);
> +
> + rc = tpm_transmit_cmd(chip, &buf, 6, "certifying creation data");
> + if (rc)
> + goto out;

This is another instance of the bug where a positive TPM2_RC_* code is being
returned from a function that is supposed to return a negative errno value.

- Eric

2022-11-13 22:46:16

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 05/11] security: keys: trusted: Allow storage of PCR values in creation data

On Fri, Nov 11, 2022 at 03:16:30PM -0800, Evan Green wrote:
> + creationpcrs= hex integer representing the set of PCRs to be
> + included in the creation data. For each bit set, the
> + corresponding PCR will be included in the key creation
> + data. Bit 0 corresponds to PCR0. Currently only the first
> + PC standard 24 PCRs are supported on the currently active
> + bank. Leading zeroes are optional. TPM2 only.

What does "currently active bank" mean?

> + /* PCR bitmask */
> + for (i = 0; i < 3; i++) {
> + char tmp = 0;
> +
> + for (j = 0; j < 8; j++) {
> + char bit = (i * 8) + j;
> +
> + if (options->creation_pcrs & (1 << bit))
> + tmp |= (1 << j);
> + }
> + tpm_buf_append_u8(&buf, tmp);
> + }

Why not just:

tpm_buf_append_u8(&buf, options->creation_pcrs);
tpm_buf_append_u8(&buf, options->creation_pcrs >> 8);
tpm_buf_append_u8(&buf, options->creation_pcrs >> 16);

Also what if bit 24 or above is set? Should an error be returned?

- Eric

2022-11-13 23:53:26

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 08/11] PM: hibernate: Use TPM-backed keys to encrypt image

On Fri, Nov 11, 2022 at 03:16:33PM -0800, Evan Green wrote:
> We'll also go through the motions of having PCR23 set to a known value at
> the time of key creation and unsealing. Currently there's nothing that
> enforces the contents of PCR23 as a condition to unseal the key blob,
> that will come in a later change.

This patch doesn't really make sense without the later patch
"PM: hibernate: seal the encryption key with a PCR policy".

Maybe they should be merged into one patch?

> +/* sha256("To sleep, perchance to dream") */
> +static struct tpm_digest known_digest = { .alg_id = TPM_ALG_SHA256,
> + .digest = {0x92, 0x78, 0x3d, 0x79, 0x2d, 0x00, 0x31, 0xb0, 0x55, 0xf9,
> + 0x1e, 0x0d, 0xce, 0x83, 0xde, 0x1d, 0xc4, 0xc5, 0x8e, 0x8c,
> + 0xf1, 0x22, 0x38, 0x6c, 0x33, 0xb1, 0x14, 0xb7, 0xec, 0x05,
> + 0x5f, 0x49}};

This can be const.

> +static int snapshot_create_kernel_key(struct snapshot_data *data)
> +{
> + /* Create a key sealed by the SRK. */
> + char *keyinfo = "new\t32\tkeyhandle=0x81000000";
> + const struct cred *cred = current_cred();
> + struct tpm_digest *digests = NULL;
> + struct key *key = NULL;
> + struct tpm_chip *chip;
> + int ret, i;
> +
> + chip = tpm_default_chip();
> + if (!chip)
> + return -ENODEV;
> +
> + if (!(tpm_is_tpm2(chip))) {
> + ret = -ENODEV;
> + goto out_dev;
> + }

tpm_is_tpm2() can return an error value.

> + digests = kcalloc(chip->nr_allocated_banks, sizeof(struct tpm_digest),
> + GFP_KERNEL);
> + if (!digests) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + for (i = 0; i < chip->nr_allocated_banks; i++) {
> + digests[i].alg_id = chip->allocated_banks[i].alg_id;
> + if (digests[i].alg_id == known_digest.alg_id)
> + memcpy(&digests[i], &known_digest, sizeof(known_digest));
> + }
> +
> + ret = tpm_pcr_extend(chip, 23, digests);
> + if (ret != 0)
> + goto out;

So, PCR 23 will not actually be extended properly if there is not a SHA-256
bank? Wouldn't it make more sense to return an error in that case?

> +static int snapshot_load_kernel_key(struct snapshot_data *data,
> + struct uswsusp_key_blob *blob)
> +{
> +
> + char *keytemplate = "load\t%s\tkeyhandle=0x81000000";

Make this const.

> + blobstring = kmalloc(blob->blob_len * 2, GFP_KERNEL);
> + if (!blobstring) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + bin2hex(blobstring, blob->blob, blob->blob_len);
> + keyinfo = kasprintf(GFP_KERNEL, keytemplate, blobstring);

There's no need for bin2hex(). Just use the %*phN format specifier instead:

const char *keytemplate = "load\t%*phN\tkeyhandle=0x81000000";

keyinfo = kasprintf(GFP_KERNEL, keytemplate, blob->blob_len, blob->blob);

- Eric

2022-11-13 23:54:02

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 09/11] PM: hibernate: Mix user key in encrypted hibernate

On Fri, Nov 11, 2022 at 03:16:34PM -0800, Evan Green wrote:
> Limiting this to the data portion allows the kernel to receive the page
> map and prepare its giant allocation even if this user key is not yet
> available (ie the user has not yet finished typing in their password).

What is meant by the "data portion"?

> +int snapshot_set_user_key(struct snapshot_data *data,
> + struct uswsusp_user_key __user *key)
> +{
> + struct uswsusp_user_key user_key;
> + unsigned int key_len;
> + int rc;
> + loff_t size;
> +
> + /*
> + * Return the metadata size, the number of bytes that can be fed in before
> + * the user data key is needed at resume time.
> + */
> + size = snapshot_get_meta_data_size();
> + rc = put_user(size, &key->meta_size);
> + if (rc)
> + return rc;
> +
> + rc = copy_from_user(&user_key, key, sizeof(struct uswsusp_user_key));
> + if (rc)
> + return rc;

This isn't correctly checking the return value of copy_from_user().

> +
> + BUILD_BUG_ON(sizeof(data->user_key) < sizeof(user_key.key));
> +
> + key_len = min_t(__u32, user_key.key_len, sizeof(data->user_key));
> + if (key_len < 8)
> + return -EINVAL;

Shouldn't -EINVAL also be returned if key_len is too large?

- Eric

2022-11-14 00:07:00

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 10/11] PM: hibernate: Verify the digest encryption key

On Fri, Nov 11, 2022 at 03:16:35PM -0800, Evan Green wrote:
> We want to ensure that the key used to encrypt the digest

What is meant by "the key used to encrypt the digest"?

> was created by
> the kernel during hibernation. To do this we request that the TPM
> include information about the value of PCR 23 at the time of key
> creation in the sealed blob. On resume, we can make sure that the PCR
> information in the creation data blob (already certified by the TPM to
> be accurate) corresponds to the expected value. Since only
> the kernel can touch PCR 23, if an attacker generates a key themselves
> the value of PCR 23 will have been different, allowing us to reject the
> key and boot normally instead of resuming.

It seems that PCR 23 is already included in the authorization policy for the
key, so why is this extra part needed?

- Eric

2022-11-14 00:13:49

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v5 11/11] PM: hibernate: seal the encryption key with a PCR policy

On Fri, Nov 11, 2022 at 03:16:36PM -0800, Evan Green wrote:
> +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));

In general, hardcoded nonces are a huge red flag. If it's fine here, it would
be helpful to leave a comment explaining why that is.

> + rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
> + if (rc)
> + goto out;

This is another instance of the bug where TPM2_RC_* codes are being returned
from a function that is expected to return -errno values.

> + *session_handle = be32_to_cpu(*(__be32 *)&buf.data[10]);

get_unaligned_be32, to avoid an unaligned memory access.

> @@ -497,11 +602,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();
> @@ -534,6 +644,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;
> + }

With the %*phN format specifier, there would be no need for bin2hex().

- Eric

2022-11-14 04:12:55

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

On Sun, 2022-11-13 at 13:20 -0800, Eric Biggers wrote:
> On Fri, Nov 11, 2022 at 03:16:29PM -0800, Evan Green wrote:
> > diff --git a/security/keys/trusted-keys/tpm2key.asn1
> > b/security/keys/trusted-keys/tpm2key.asn1
> > index f57f869ad60068..608f8d9ca95fa8 100644
> > --- a/security/keys/trusted-keys/tpm2key.asn1
> > +++ b/security/keys/trusted-keys/tpm2key.asn1
> > @@ -7,5 +7,18 @@ TPMKey ::= SEQUENCE {
> >         emptyAuth       [0] EXPLICIT BOOLEAN OPTIONAL,
> >         parent          INTEGER ({tpm2_key_parent}),
> >         pubkey          OCTET STRING ({tpm2_key_pub}),
> > -       privkey         OCTET STRING ({tpm2_key_priv})
> > +       privkey         OCTET STRING ({tpm2_key_priv}),
> > +       ---
> > +       --- A TPM2B_CREATION_DATA struct as returned from the
> > TPM2_Create command.
> > +       ---
> > +       creationData    [1] EXPLICIT OCTET STRING OPTIONAL
> > ({tpm2_key_creation_data}),
> > +       ---
> > +       --- A TPM2B_DIGEST of the creationHash as returned from the
> > TPM2_Create
> > +       --- command.
> > +       ---
> > +       creationHash    [2] EXPLICIT OCTET STRING OPTIONAL
> > ({tpm2_key_creation_hash}),
> > +       ---
> > +       --- A TPMT_TK_CREATION ticket as returned from the
> > TPM2_Create command.
> > +       ---
> > +       creationTk      [3] EXPLICIT OCTET STRING OPTIONAL
> > ({tpm2_key_creation_tk})
> >         }
>
> The commit that added this file claimed:
>
>         "The benefit of the ASN.1 format is that it's a standard and
> thus the
>         exported key can be used by userspace tools
> (openssl_tpm2_engine,
>         openconnect and tpm2-tss-engine"
>
> Are these new fields in compliance with whatever standard that was
> referring to?

Not really, no. The current use case (and draft standard) is already
using [1] for policies and [2] for importable keys:

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/tree/doc/draft-bottomley-tpm2-keys.xml

I'm actually planning to use [3] for signed policies. There's no
reason why you can't use [4] though. Since the creation data, hash and
ticket are likely used as a job lot, it strikes me they should be a
single numbered optional sequence instead of individually numbered,
since you're unlikely to have one without the others.

James


2022-11-14 17:01:59

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

On Mon, 2022-11-14 at 08:32 -0800, Evan Green wrote:
> On Sun, Nov 13, 2022 at 7:32 PM James Bottomley <[email protected]>
> wrote:
> >
> > On Sun, 2022-11-13 at 13:20 -0800, Eric Biggers wrote:
> > > On Fri, Nov 11, 2022 at 03:16:29PM -0800, Evan Green wrote:
> > > > diff --git a/security/keys/trusted-keys/tpm2key.asn1
> > > > b/security/keys/trusted-keys/tpm2key.asn1
> > > > index f57f869ad60068..608f8d9ca95fa8 100644
> > > > --- a/security/keys/trusted-keys/tpm2key.asn1
> > > > +++ b/security/keys/trusted-keys/tpm2key.asn1
> > > > @@ -7,5 +7,18 @@ TPMKey ::= SEQUENCE {
> > > >         emptyAuth       [0] EXPLICIT BOOLEAN OPTIONAL,
> > > >         parent          INTEGER ({tpm2_key_parent}),
> > > >         pubkey          OCTET STRING ({tpm2_key_pub}),
> > > > -       privkey         OCTET STRING ({tpm2_key_priv})
> > > > +       privkey         OCTET STRING ({tpm2_key_priv}),
> > > > +       ---
> > > > +       --- A TPM2B_CREATION_DATA struct as returned from the
> > > > TPM2_Create command.
> > > > +       ---
> > > > +       creationData    [1] EXPLICIT OCTET STRING OPTIONAL
> > > > ({tpm2_key_creation_data}),
> > > > +       ---
> > > > +       --- A TPM2B_DIGEST of the creationHash as returned from
> > > > the
> > > > TPM2_Create
> > > > +       --- command.
> > > > +       ---
> > > > +       creationHash    [2] EXPLICIT OCTET STRING OPTIONAL
> > > > ({tpm2_key_creation_hash}),
> > > > +       ---
> > > > +       --- A TPMT_TK_CREATION ticket as returned from the
> > > > TPM2_Create command.
> > > > +       ---
> > > > +       creationTk      [3] EXPLICIT OCTET STRING OPTIONAL
> > > > ({tpm2_key_creation_tk})
> > > >         }
> > >
> > > The commit that added this file claimed:
> > >
> > >         "The benefit of the ASN.1 format is that it's a standard
> > > and thus the
> > >         exported key can be used by userspace tools
> > > (openssl_tpm2_engine,
> > >         openconnect and tpm2-tss-engine"
> > >
> > > Are these new fields in compliance with whatever standard that
> > > was referring to?
> >
> > Not really, no.  The current use case (and draft standard) is
> > already using [1] for policies and [2] for importable keys:
> >
> > https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/tree/doc/draft-bottomley-tpm2-keys.xml
> >
> > I'm actually planning to use [3] for signed policies.  There's no
> > reason why you can't use [4] though.  Since the creation data, hash
> > and ticket are likely used as a job lot, it strikes me they should
> > be a single numbered optional sequence instead of individually
> > numbered, since you're unlikely to have one without the others.
>
> Thanks, I was hoping James might pipe up and tell me what to do.
> Grouping them as a single numbered optional sequence sounds
> reasonable to me. Is your draft too far along to squeeze this in?

Not at all. The draft only becomes frozen once I submit it to the IETF
which, so far thanks to lack of any reviewers I haven't done (That's
why I was also thinking of adding signed policies).

> If it is and I'm on my own to draft up and submit this, I would
> definitely appreciate any pointers on getting started you might have.
>
> I notice the draft and the code seem to be out of alignment.

The kernel code is out of alignment just because development moves a
bit slowly. Policy based keys were submitted a long time ago as part
of the original move to interoperable sealed keys based on ASN.1:

https://lore.kernel.org/all/[email protected]/

But eventually the policy part was split out and forgotten about. I
think the only complete implementation of the draft standard is the
openssl_tpm2_engine.

> I'm unfamiliar with this process, is the idea to get through all the
> iterations and land the standard, then fix up the code? What happens
> to existing data handed out in the old format?

No, it doesn't matter at all. That's the whole point of using ASN.1
explicit optionals: the ASN.1 is always backwards compatible. If I
ever submit the draft, there'll have to be a new RFC to add new
explicit optionals, but keys conforming to the old RFC will still be
valid under the new one.

Of course, since openssl_tpm2_engine is the complete reference
implementation that means I'll have to add the creation PCRs
implementation to it ... unless you'd like to do it?

Regards,

James


2022-11-14 17:25:16

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Fri, 2022-11-11 at 15:16 -0800, Evan Green wrote:
> Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
> restricts usermode's ability to extend or reset PCR 23.

Could I re ask the question here that I asked of Matthew's patch set:

https://lore.kernel.org/all/[email protected]/

Which was could we use an NVRAM index in the TPM instead of a PCR? The
reason for asking was that PCRs are rather precious and might get more
so now that Lennart has some grand scheme for using more of them in his
unified boot project. Matthew promised to play with the idea but never
got back to the patch set to say whether he investigated this or not.

James


2022-11-14 17:39:50

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

On Sun, Nov 13, 2022 at 7:32 PM James Bottomley <[email protected]> wrote:
>
> On Sun, 2022-11-13 at 13:20 -0800, Eric Biggers wrote:
> > On Fri, Nov 11, 2022 at 03:16:29PM -0800, Evan Green wrote:
> > > diff --git a/security/keys/trusted-keys/tpm2key.asn1
> > > b/security/keys/trusted-keys/tpm2key.asn1
> > > index f57f869ad60068..608f8d9ca95fa8 100644
> > > --- a/security/keys/trusted-keys/tpm2key.asn1
> > > +++ b/security/keys/trusted-keys/tpm2key.asn1
> > > @@ -7,5 +7,18 @@ TPMKey ::= SEQUENCE {
> > > emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
> > > parent INTEGER ({tpm2_key_parent}),
> > > pubkey OCTET STRING ({tpm2_key_pub}),
> > > - privkey OCTET STRING ({tpm2_key_priv})
> > > + privkey OCTET STRING ({tpm2_key_priv}),
> > > + ---
> > > + --- A TPM2B_CREATION_DATA struct as returned from the
> > > TPM2_Create command.
> > > + ---
> > > + creationData [1] EXPLICIT OCTET STRING OPTIONAL
> > > ({tpm2_key_creation_data}),
> > > + ---
> > > + --- A TPM2B_DIGEST of the creationHash as returned from the
> > > TPM2_Create
> > > + --- command.
> > > + ---
> > > + creationHash [2] EXPLICIT OCTET STRING OPTIONAL
> > > ({tpm2_key_creation_hash}),
> > > + ---
> > > + --- A TPMT_TK_CREATION ticket as returned from the
> > > TPM2_Create command.
> > > + ---
> > > + creationTk [3] EXPLICIT OCTET STRING OPTIONAL
> > > ({tpm2_key_creation_tk})
> > > }
> >
> > The commit that added this file claimed:
> >
> > "The benefit of the ASN.1 format is that it's a standard and
> > thus the
> > exported key can be used by userspace tools
> > (openssl_tpm2_engine,
> > openconnect and tpm2-tss-engine"
> >
> > Are these new fields in compliance with whatever standard that was
> > referring to?
>
> Not really, no. The current use case (and draft standard) is already
> using [1] for policies and [2] for importable keys:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/tree/doc/draft-bottomley-tpm2-keys.xml
>
> I'm actually planning to use [3] for signed policies. There's no
> reason why you can't use [4] though. Since the creation data, hash and
> ticket are likely used as a job lot, it strikes me they should be a
> single numbered optional sequence instead of individually numbered,
> since you're unlikely to have one without the others.

Thanks, I was hoping James might pipe up and tell me what to do.
Grouping them as a single numbered optional sequence sounds reasonable
to me. Is your draft too far along to squeeze this in? If it is and
I'm on my own to draft up and submit this, I would definitely
appreciate any pointers on getting started you might have.

I notice the draft and the code seem to be out of alignment. I'm
unfamiliar with this process, is the idea to get through all the
iterations and land the standard, then fix up the code? What happens
to existing data handed out in the old format?

-Evan

2022-11-14 18:35:37

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

On Mon, Nov 14, 2022 at 8:56 AM James Bottomley <[email protected]> wrote:
>
> On Mon, 2022-11-14 at 08:32 -0800, Evan Green wrote:
> > On Sun, Nov 13, 2022 at 7:32 PM James Bottomley <[email protected]>
> > wrote:
> > >
> > > On Sun, 2022-11-13 at 13:20 -0800, Eric Biggers wrote:
> > > > On Fri, Nov 11, 2022 at 03:16:29PM -0800, Evan Green wrote:
> > > > > diff --git a/security/keys/trusted-keys/tpm2key.asn1
> > > > > b/security/keys/trusted-keys/tpm2key.asn1
> > > > > index f57f869ad60068..608f8d9ca95fa8 100644
> > > > > --- a/security/keys/trusted-keys/tpm2key.asn1
> > > > > +++ b/security/keys/trusted-keys/tpm2key.asn1
> > > > > @@ -7,5 +7,18 @@ TPMKey ::= SEQUENCE {
> > > > > emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
> > > > > parent INTEGER ({tpm2_key_parent}),
> > > > > pubkey OCTET STRING ({tpm2_key_pub}),
> > > > > - privkey OCTET STRING ({tpm2_key_priv})
> > > > > + privkey OCTET STRING ({tpm2_key_priv}),
> > > > > + ---
> > > > > + --- A TPM2B_CREATION_DATA struct as returned from the
> > > > > TPM2_Create command.
> > > > > + ---
> > > > > + creationData [1] EXPLICIT OCTET STRING OPTIONAL
> > > > > ({tpm2_key_creation_data}),
> > > > > + ---
> > > > > + --- A TPM2B_DIGEST of the creationHash as returned from
> > > > > the
> > > > > TPM2_Create
> > > > > + --- command.
> > > > > + ---
> > > > > + creationHash [2] EXPLICIT OCTET STRING OPTIONAL
> > > > > ({tpm2_key_creation_hash}),
> > > > > + ---
> > > > > + --- A TPMT_TK_CREATION ticket as returned from the
> > > > > TPM2_Create command.
> > > > > + ---
> > > > > + creationTk [3] EXPLICIT OCTET STRING OPTIONAL
> > > > > ({tpm2_key_creation_tk})
> > > > > }
> > > >
> > > > The commit that added this file claimed:
> > > >
> > > > "The benefit of the ASN.1 format is that it's a standard
> > > > and thus the
> > > > exported key can be used by userspace tools
> > > > (openssl_tpm2_engine,
> > > > openconnect and tpm2-tss-engine"
> > > >
> > > > Are these new fields in compliance with whatever standard that
> > > > was referring to?
> > >
> > > Not really, no. The current use case (and draft standard) is
> > > already using [1] for policies and [2] for importable keys:
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/tree/doc/draft-bottomley-tpm2-keys.xml
> > >
> > > I'm actually planning to use [3] for signed policies. There's no
> > > reason why you can't use [4] though. Since the creation data, hash
> > > and ticket are likely used as a job lot, it strikes me they should
> > > be a single numbered optional sequence instead of individually
> > > numbered, since you're unlikely to have one without the others.
> >
> > Thanks, I was hoping James might pipe up and tell me what to do.
> > Grouping them as a single numbered optional sequence sounds
> > reasonable to me. Is your draft too far along to squeeze this in?
>
> Not at all. The draft only becomes frozen once I submit it to the IETF
> which, so far thanks to lack of any reviewers I haven't done (That's
> why I was also thinking of adding signed policies).
>
> > If it is and I'm on my own to draft up and submit this, I would
> > definitely appreciate any pointers on getting started you might have.
> >
> > I notice the draft and the code seem to be out of alignment.
>
> The kernel code is out of alignment just because development moves a
> bit slowly. Policy based keys were submitted a long time ago as part
> of the original move to interoperable sealed keys based on ASN.1:
>
> https://lore.kernel.org/all/[email protected]/
>
> But eventually the policy part was split out and forgotten about. I
> think the only complete implementation of the draft standard is the
> openssl_tpm2_engine.
>
> > I'm unfamiliar with this process, is the idea to get through all the
> > iterations and land the standard, then fix up the code? What happens
> > to existing data handed out in the old format?
>
> No, it doesn't matter at all. That's the whole point of using ASN.1
> explicit optionals: the ASN.1 is always backwards compatible. If I
> ever submit the draft, there'll have to be a new RFC to add new
> explicit optionals, but keys conforming to the old RFC will still be
> valid under the new one.

Ah I see, with the optionals in mind things do line up again.

>
> Of course, since openssl_tpm2_engine is the complete reference
> implementation that means I'll have to add the creation PCRs
> implementation to it ... unless you'd like to do it?

I am willing to help as I'm the one making the mess. How does it
sequence along with your draft submission (before, after,
simultaneous)?

2022-11-14 18:39:44

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

On Mon, 2022-11-14 at 09:43 -0800, Evan Green wrote:
> On Mon, Nov 14, 2022 at 8:56 AM James Bottomley <[email protected]>
> wrote:
[...]
> > Of course, since openssl_tpm2_engine is the complete reference
> > implementation that means I'll have to add the creation PCRs
> > implementation to it ... unless you'd like to do it?
>
> I am willing to help as I'm the one making the mess. How does it
> sequence along with your draft submission (before, after,
> simultaneous)?

At the moment, just send patches. The openssl_tpm2_engine is developed
on a groups.io mailing list:

https://groups.io/g/openssl-tpm2-engine/

You need an IETF specific tool (xml2rfc) to build the rfc from the xml,
but it's available in most distros as python3-xml2rfc. If you don't
want to learn the IETF XML I can help you code up the patch to add that
to the draft spec.

Regards,

James


2022-11-27 16:12:30

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/11] tpm: Add support for in-kernel resetting of PCRs

On Fri, Nov 11, 2022 at 03:16:26PM -0800, Evan Green wrote:
> Add an internal command for resetting a PCR. This will be used by the
> encrypted hibernation code to set PCR23 to a known value. The
> hibernation code will seal the hibernation key with a policy specifying
> PCR23 be set to this known value as a mechanism to ensure that the
> hibernation key is genuine. But to do this repeatedly, resetting the PCR
> is necessary as well.
>
> Link: https://lore.kernel.org/lkml/[email protected]/
> Co-developed-by: Matthew Garrett <[email protected]>
> Signed-off-by: Matthew Garrett <[email protected]>
> Signed-off-by: Evan Green <[email protected]>
>
> ---
>
> Changes in v5:
> - Change to co-developed by Matthew (Kees)
>
> Changes in v4:
> - Open code tpm2_pcr_reset implementation in tpm-interface.c (Jarkko)
> - Rename interface symbol to tpm2_pcr_reset, fix kerneldocs (Jarkko)
>
> Changes in v3:
> - Unify tpm1/2_pcr_reset prototypes (Jarkko)
> - Wait no, remove the TPM1 stuff altogether (Jarkko)
> - Remove extra From tag and blank in commit msg (Jarkko).
>
> drivers/char/tpm/tpm-interface.c | 47 ++++++++++++++++++++++++++++++++
> drivers/char/tpm/tpm2-cmd.c | 7 -----
> include/linux/tpm.h | 14 ++++++++++
> 3 files changed, 61 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index 1621ce8187052c..886277b2654e3b 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -342,6 +342,53 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> }
> EXPORT_SYMBOL_GPL(tpm_pcr_extend);
>
> +/**
> + * tpm2_pcr_reset - Reset the specified PCR
> + * @chip: A &struct tpm_chip instance, %NULL for the default chip
> + * @pcr_idx: The PCR to be reset
> + *
> + * Return: Same as with tpm_transmit_cmd(), or ENOTTY for TPM1 devices.
> + */
> +int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx)
> +{
> + struct tpm2_null_auth_area auth_area;
> + struct tpm_buf buf;
> + int rc;
> +
> + chip = tpm_find_get_ops(chip);
> + if (!chip)
> + return -ENODEV;
> +
> + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
> + rc = -ENOTTY;
> + goto out;
> + }
> +
> + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_RESET);
> + if (rc)
> + goto out;
> +
> + tpm_buf_append_u32(&buf, pcr_idx);
> +
> + auth_area.handle = cpu_to_be32(TPM2_RS_PW);
> + auth_area.nonce_size = 0;
> + auth_area.attributes = 0;
> + auth_area.auth_size = 0;
> +
> + tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
> + tpm_buf_append(&buf, (const unsigned char *)&auth_area,
> + sizeof(auth_area));
> +
> + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to reset a PCR");
> +
> + tpm_buf_destroy(&buf);
> +
> +out:
> + tpm_put_ops(chip);
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(tpm2_pcr_reset);
> +
> /**
> * tpm_send - send a TPM command
> * @chip: a &struct tpm_chip instance, %NULL for the default chip
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 65d03867e114c5..303ce2ea02a4b0 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -216,13 +216,6 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> return rc;
> }
>
> -struct tpm2_null_auth_area {
> - __be32 handle;
> - __be16 nonce_size;
> - u8 attributes;
> - __be16 auth_size;
> -} __packed;
> -
> /**
> * tpm2_pcr_extend() - extend a PCR value
> *
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index dfeb25a0362dee..70134e6551745f 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -219,6 +219,7 @@ enum tpm2_command_codes {
> TPM2_CC_HIERARCHY_CONTROL = 0x0121,
> TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129,
> TPM2_CC_CREATE_PRIMARY = 0x0131,
> + TPM2_CC_PCR_RESET = 0x013D,
> TPM2_CC_SEQUENCE_COMPLETE = 0x013E,
> TPM2_CC_SELF_TEST = 0x0143,
> TPM2_CC_STARTUP = 0x0144,
> @@ -293,6 +294,13 @@ struct tpm_header {
> };
> } __packed;
>
> +struct tpm2_null_auth_area {
> + __be32 handle;
> + __be16 nonce_size;
> + u8 attributes;
> + __be16 auth_size;
> +} __packed;
> +
> /* A string buffer type for constructing TPM commands. This is based on the
> * ideas of string buffer code in security/keys/trusted.h but is heap based
> * in order to keep the stack usage minimal.
> @@ -423,6 +431,7 @@ extern ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
> size_t min_rsp_body_length, const char *desc);
> extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> struct tpm_digest *digest);
> +extern int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx);
> extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> struct tpm_digest *digests);
> extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen);
> @@ -440,6 +449,11 @@ static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx,
> return -ENODEV;
> }
>
> +static inline int tpm2_pcr_reset(struct tpm_chip *chip, int pcr_idx)
> +{
> + return -ENODEV;
> +}
> +
> static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> struct tpm_digest *digests)
> {
> --
> 2.38.1.431.g37b22c650d-goog
>

Reviewed-by: Jarkko Sakkinen <[email protected]>

BR, Jarkko

2022-11-27 16:26:53

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 01/11] tpm: Add support for in-kernel resetting of PCRs

On Sun, Nov 27, 2022 at 06:06:13PM +0200, Jarkko Sakkinen wrote:
> On Fri, Nov 11, 2022 at 03:16:26PM -0800, Evan Green wrote:
> > Add an internal command for resetting a PCR. This will be used by the
> > encrypted hibernation code to set PCR23 to a known value. The
> > hibernation code will seal the hibernation key with a policy specifying
> > PCR23 be set to this known value as a mechanism to ensure that the
> > hibernation key is genuine. But to do this repeatedly, resetting the PCR
> > is necessary as well.
> >
> > Link: https://lore.kernel.org/lkml/[email protected]/
> > Co-developed-by: Matthew Garrett <[email protected]>
> > Signed-off-by: Matthew Garrett <[email protected]>
> > Signed-off-by: Evan Green <[email protected]>
> >
> > ---
> >
> > Changes in v5:
> > - Change to co-developed by Matthew (Kees)
> >
> > Changes in v4:
> > - Open code tpm2_pcr_reset implementation in tpm-interface.c (Jarkko)
> > - Rename interface symbol to tpm2_pcr_reset, fix kerneldocs (Jarkko)
> >
> > Changes in v3:
> > - Unify tpm1/2_pcr_reset prototypes (Jarkko)
> > - Wait no, remove the TPM1 stuff altogether (Jarkko)
> > - Remove extra From tag and blank in commit msg (Jarkko).
> >
> > drivers/char/tpm/tpm-interface.c | 47 ++++++++++++++++++++++++++++++++
> > drivers/char/tpm/tpm2-cmd.c | 7 -----
> > include/linux/tpm.h | 14 ++++++++++
> > 3 files changed, 61 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> > index 1621ce8187052c..886277b2654e3b 100644
> > --- a/drivers/char/tpm/tpm-interface.c
> > +++ b/drivers/char/tpm/tpm-interface.c
> > @@ -342,6 +342,53 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> > }
> > EXPORT_SYMBOL_GPL(tpm_pcr_extend);
> >
> > +/**
> > + * tpm2_pcr_reset - Reset the specified PCR
> > + * @chip: A &struct tpm_chip instance, %NULL for the default chip
> > + * @pcr_idx: The PCR to be reset
> > + *
> > + * Return: Same as with tpm_transmit_cmd(), or ENOTTY for TPM1 devices.
> > + */
> > +int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx)
> > +{
> > + struct tpm2_null_auth_area auth_area;
> > + struct tpm_buf buf;
> > + int rc;
> > +
> > + chip = tpm_find_get_ops(chip);
> > + if (!chip)
> > + return -ENODEV;
> > +
> > + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
> > + rc = -ENOTTY;
> > + goto out;
> > + }
> > +
> > + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_RESET);
> > + if (rc)
> > + goto out;
> > +
> > + tpm_buf_append_u32(&buf, pcr_idx);
> > +
> > + auth_area.handle = cpu_to_be32(TPM2_RS_PW);
> > + auth_area.nonce_size = 0;
> > + auth_area.attributes = 0;
> > + auth_area.auth_size = 0;
> > +
> > + tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
> > + tpm_buf_append(&buf, (const unsigned char *)&auth_area,
> > + sizeof(auth_area));
> > +
> > + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to reset a PCR");
> > +
> > + tpm_buf_destroy(&buf);
> > +
> > +out:
> > + tpm_put_ops(chip);
> > + return rc;
> > +}
> > +EXPORT_SYMBOL_GPL(tpm2_pcr_reset);
> > +
> > /**
> > * tpm_send - send a TPM command
> > * @chip: a &struct tpm_chip instance, %NULL for the default chip
> > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> > index 65d03867e114c5..303ce2ea02a4b0 100644
> > --- a/drivers/char/tpm/tpm2-cmd.c
> > +++ b/drivers/char/tpm/tpm2-cmd.c
> > @@ -216,13 +216,6 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> > return rc;
> > }
> >
> > -struct tpm2_null_auth_area {
> > - __be32 handle;
> > - __be16 nonce_size;
> > - u8 attributes;
> > - __be16 auth_size;
> > -} __packed;
> > -
> > /**
> > * tpm2_pcr_extend() - extend a PCR value
> > *
> > diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> > index dfeb25a0362dee..70134e6551745f 100644
> > --- a/include/linux/tpm.h
> > +++ b/include/linux/tpm.h
> > @@ -219,6 +219,7 @@ enum tpm2_command_codes {
> > TPM2_CC_HIERARCHY_CONTROL = 0x0121,
> > TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129,
> > TPM2_CC_CREATE_PRIMARY = 0x0131,
> > + TPM2_CC_PCR_RESET = 0x013D,
> > TPM2_CC_SEQUENCE_COMPLETE = 0x013E,
> > TPM2_CC_SELF_TEST = 0x0143,
> > TPM2_CC_STARTUP = 0x0144,
> > @@ -293,6 +294,13 @@ struct tpm_header {
> > };
> > } __packed;
> >
> > +struct tpm2_null_auth_area {
> > + __be32 handle;
> > + __be16 nonce_size;
> > + u8 attributes;
> > + __be16 auth_size;
> > +} __packed;
> > +
> > /* A string buffer type for constructing TPM commands. This is based on the
> > * ideas of string buffer code in security/keys/trusted.h but is heap based
> > * in order to keep the stack usage minimal.
> > @@ -423,6 +431,7 @@ extern ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
> > size_t min_rsp_body_length, const char *desc);
> > extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> > struct tpm_digest *digest);
> > +extern int tpm2_pcr_reset(struct tpm_chip *chip, u32 pcr_idx);
> > extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> > struct tpm_digest *digests);
> > extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen);
> > @@ -440,6 +449,11 @@ static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx,
> > return -ENODEV;
> > }
> >
> > +static inline int tpm2_pcr_reset(struct tpm_chip *chip, int pcr_idx)
> > +{
> > + return -ENODEV;
> > +}
> > +
> > static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> > struct tpm_digest *digests)
> > {
> > --
> > 2.38.1.431.g37b22c650d-goog
> >
>
> Reviewed-by: Jarkko Sakkinen <[email protected]>

Please ignore, wrong patch, sorry.

BR, Jarkko

2022-11-27 16:47:44

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Fri, Nov 11, 2022 at 03:16:28PM -0800, Evan Green wrote:
> Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
> restricts usermode's ability to extend or reset PCR 23.

TCG_TPM_KERNEL_PCR would be a more descriptive name, and the
description should be less abstract, e.g.

"Introduce TCG_TPM_RESTRICT_PCR to Kconfig. If enabled, filter out
TPM2_CC_PCR_{EXTEND, RESET} concerning PCR 23 in tpm_common_write()."

> Under certain circumstances it might be desirable to enable the creation
> of TPM-backed secrets that are only accessible to the kernel. In an
> ideal world this could be achieved by using TPM localities, but these
> don't appear to be available on consumer systems. An alternative is to
> simply block userland from modifying one of the resettable PCRs, leaving
> it available to the kernel. If the kernel ensures that no userland can
> access the TPM while it is carrying out work, it can reset PCR 23,
> extend it to an arbitrary value, create or load a secret, and then reset
> the PCR again. Even if userland somehow obtains the sealed material, it
> will be unable to unseal it since PCR 23 will never be in the
> appropriate state.

This should be the first paragraph (motivation).

> This Kconfig is only properly supported for systems with TPM2 devices.
> For systems with TPM1 devices, having this Kconfig enabled completely
> restricts usermode's access to the TPM. TPM1 contains support for
> tunnelled transports, which usermode could use to smuggle commands
> through that this Kconfig is attempting to restrict.
>
> Link: https://lore.kernel.org/lkml/[email protected]/
> Co-developed-by: Matthew Garrett <[email protected]>
> Signed-off-by: Matthew Garrett <[email protected]>
> Signed-off-by: Evan Green <[email protected]>
>
> ---
>
> Changes in v5:
> - Change tags on RESTRICT_PCR patch (Kees)
> - Rename to TCG_TPM2_RESTRICT_PCR
> - Do nothing on TPM1.2 devices (Jarkko, Doug)
>
> Changes in v4:
> - Augment the commit message (Jarkko)
>
> Changes in v3:
> - Fix up commit message (Jarkko)
> - tpm2_find_and_validate_cc() was split (Jarkko)
> - Simply fully restrict TPM1 since v2 failed to account for tunnelled
> transport sessions (Stefan and Jarkko).
>
> Changes in v2:
> - Fixed sparse warnings
>
> drivers/char/tpm/Kconfig | 12 ++++++++++++
> drivers/char/tpm/tpm-dev-common.c | 6 ++++++
> drivers/char/tpm/tpm.h | 12 ++++++++++++
> drivers/char/tpm/tpm2-cmd.c | 22 ++++++++++++++++++++++
> 4 files changed, 52 insertions(+)
>
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index 927088b2c3d3f2..e6d3aa9f6c694f 100644
> --- a/drivers/char/tpm/Kconfig
> +++ b/drivers/char/tpm/Kconfig
> @@ -211,4 +211,16 @@ config TCG_FTPM_TEE
> This driver proxies for firmware TPM running in TEE.
>
> source "drivers/char/tpm/st33zp24/Kconfig"
> +
> +config TCG_TPM2_RESTRICT_PCR
> + bool "Restrict userland access to PCR 23 on TPM2 devices"
> + depends on TCG_TPM
> + help
> + If set, block userland from extending or resetting PCR 23 on TPM2.0
> + and later systems. This allows the PCR to be restricted to in-kernel
> + use, preventing userland from being able to make use of data sealed to
> + the TPM by the kernel. This is required for secure hibernation
> + support, but should be left disabled if any userland may require
> + access to PCR23. This is a TPM2-only feature, enabling this on a TPM1
> + machine is effectively a no-op.
> endif # TCG_TPM
> diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
> index dc4c0a0a512903..66d15a2a967443 100644
> --- a/drivers/char/tpm/tpm-dev-common.c
> +++ b/drivers/char/tpm/tpm-dev-common.c
> @@ -198,6 +198,12 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
> priv->response_read = false;
> *off = 0;
>
> + if (priv->chip->flags & TPM_CHIP_FLAG_TPM2) {
> + ret = tpm2_cmd_restricted(priv->chip, priv->data_buffer, size);
> + if (ret)
> + goto out;
> + }
> +
> /*
> * If in nonblocking mode schedule an async job to send
> * the command return the size.
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index f1e0f490176f01..7fb746d210f59d 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -245,4 +245,16 @@ void tpm_bios_log_setup(struct tpm_chip *chip);
> void tpm_bios_log_teardown(struct tpm_chip *chip);
> int tpm_dev_common_init(void);
> void tpm_dev_common_exit(void);
> +
> +#ifdef CONFIG_TCG_TPM2_RESTRICT_PCR
> +#define TPM_RESTRICTED_PCR 23
> +
> +int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size);
> +#else
> +static inline int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer,
> + size_t size)
> +{
> + return 0;
> +}
> +#endif

Why do you need to export this? That was not discussed in the commit
message.

The function name is quite undescriptive IMHO.

> #endif
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 303ce2ea02a4b0..3bc5546fddc792 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -778,3 +778,25 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
>
> return -1;
> }
> +
> +#ifdef CONFIG_TCG_TPM2_RESTRICT_PCR
> +int tpm2_cmd_restricted(struct tpm_chip *chip, u8 *buffer, size_t size)
> +{
> + int cc = tpm2_find_and_validate_cc(chip, NULL, buffer, size);

Please discuss this call in the commit message.

> + __be32 *handle;
> +
> + switch (cc) {
> + case TPM2_CC_PCR_EXTEND:
> + case TPM2_CC_PCR_RESET:
> + if (size < (TPM_HEADER_SIZE + sizeof(u32)))
> + return -EINVAL;
> +
> + handle = (__be32 *)&buffer[TPM_HEADER_SIZE];
> + if (be32_to_cpu(*handle) == TPM_RESTRICTED_PCR)
> + return -EPERM;
> + break;
> + }
> +
> + return 0;
> +}
> +#endif
> --
> 2.38.1.431.g37b22c650d-goog
>

BR, Jarkko

2022-11-27 16:56:32

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Mon, Nov 14, 2022 at 12:11:20PM -0500, James Bottomley wrote:
> On Fri, 2022-11-11 at 15:16 -0800, Evan Green wrote:
> > Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
> > restricts usermode's ability to extend or reset PCR 23.
>
> Could I re ask the question here that I asked of Matthew's patch set:
>
> https://lore.kernel.org/all/[email protected]/
>
> Which was could we use an NVRAM index in the TPM instead of a PCR? The
> reason for asking was that PCRs are rather precious and might get more
> so now that Lennart has some grand scheme for using more of them in his
> unified boot project. Matthew promised to play with the idea but never
> got back to the patch set to say whether he investigated this or not.

Even for PCR case it would be better to have it configurable through
kernel command-line, including a disabled state, which would the
default.

This would be backwards compatible, and if designed properly, could
more easily extended for NV index later on.

BR, Jarkko

2022-11-27 16:58:56

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Sun, 2022-11-27 at 18:33 +0200, Jarkko Sakkinen wrote:
> On Mon, Nov 14, 2022 at 12:11:20PM -0500, James Bottomley wrote:
> > On Fri, 2022-11-11 at 15:16 -0800, Evan Green wrote:
> > > Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
> > > restricts usermode's ability to extend or reset PCR 23.
> >
> > Could I re ask the question here that I asked of Matthew's patch
> > set:
> >
> > https://lore.kernel.org/all/[email protected]/
> >
> > Which was could we use an NVRAM index in the TPM instead of a PCR? 
> > The reason for asking was that PCRs are rather precious and might
> > get more so now that Lennart has some grand scheme for using more
> > of them in his unified boot project.  Matthew promised to play with
> > the idea but never got back to the patch set to say whether he
> > investigated this or not.
>
> Even for PCR case it would be better to have it configurable through
> kernel command-line, including a disabled state, which would the
> default.
>
> This would be backwards compatible, and if designed properly, could
> more easily extended for NV index later on.


Um how? The observation is in the above referenced email is that PCR23
is reserved in the TCG literature for application usage. If any
application is actually using PCR23 based on that spec then revoking
access to user space will cause it to break. This is an ABI change
which is not backwards compatible. You can call it a distro problem if
it's command line configurable, but the default would be what most
distros take, so it's rather throwing them under the bus if there is an
application using it.

Of course, if no application is actually using PCR23, then it's
probably OK to use it in the kernel and make it invisible to user
space, but no evidence about this has actually been presented.

James

2022-11-30 21:10:47

by Dr. Greg

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Sun, Nov 27, 2022 at 11:41:26AM -0500, James Bottomley wrote:

Good afternoon, I hope the week is going well for everyone.

> On Sun, 2022-11-27 at 18:33 +0200, Jarkko Sakkinen wrote:
> > On Mon, Nov 14, 2022 at 12:11:20PM -0500, James Bottomley wrote:
> > > On Fri, 2022-11-11 at 15:16 -0800, Evan Green wrote:
> > > > Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
> > > > restricts usermode's ability to extend or reset PCR 23.
> > >
> > > Could I re ask the question here that I asked of Matthew's patch
> > > set:
> > >
> > > https://lore.kernel.org/all/[email protected]/
> > >
> > > Which was could we use an NVRAM index in the TPM instead of a PCR???
> > > The reason for asking was that PCRs are rather precious and might
> > > get more so now that Lennart has some grand scheme for using more
> > > of them in his unified boot project.?? Matthew promised to play with
> > > the idea but never got back to the patch set to say whether he
> > > investigated this or not.
> >
> > Even for PCR case it would be better to have it configurable through
> > kernel command-line, including a disabled state, which would the
> > default.
> >
> > This would be backwards compatible, and if designed properly, could
> > more easily extended for NV index later on.
>
> Um how? The observation is in the above referenced email is that PCR23
> is reserved in the TCG literature for application usage. If any
> application is actually using PCR23 based on that spec then revoking
> access to user space will cause it to break. This is an ABI change
> which is not backwards compatible. You can call it a distro problem if
> it's command line configurable, but the default would be what most
> distros take, so it's rather throwing them under the bus if there is an
> application using it.
>
> Of course, if no application is actually using PCR23, then it's
> probably OK to use it in the kernel and make it invisible to user
> space, but no evidence about this has actually been presented.

If there isn't, there will be in in the next week or so, if we can
stay on schedule. Otherwise, I fear that Casey Schaufler, who I
believe is holding his breath, may turn irretrievably blue.... :-)

The Trust Orchestration System, Quixote, that we are releasing for
Linux uses PCR23 to generate an attestation of the functional state
value for an internally modeled security domain.

TSEM, the LSM based kernel component in all of this, supports the
ability to implement multiple 'domains', nee namespaces, each of which
can have a security modeling function attached to it. Each internally
modeled domain has to have the ability to independently attest the
functional value of the security model implemented for the
domain/namespace.

We have found, and I believe others will find that, particularly the
resettable registers, are too precious to be constrained from general
usage. We actually just finished lifting the PCR23 extension
functionality out of the TSEM driver and into userspace because having
it in the kernel was too constraining.

With respect to making the behavior a command-line option. We've
slogged through 2+ years of conversations with sizable players who
have indicated that if the 'distys' don't implement something, it
isn't a relevant Linux technology, so a command-line option poses a
barrier to innovation.

> James

Have a good day.

As always,
Dr. Greg

The Quixote Project - Flailing at the Travails of Cybersecurity

2022-11-30 21:52:45

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On 11/30/2022 12:22 PM, Dr. Greg wrote:
> On Sun, Nov 27, 2022 at 11:41:26AM -0500, James Bottomley wrote:
>
> Good afternoon, I hope the week is going well for everyone.
>
>> On Sun, 2022-11-27 at 18:33 +0200, Jarkko Sakkinen wrote:
>>> On Mon, Nov 14, 2022 at 12:11:20PM -0500, James Bottomley wrote:
>>>> On Fri, 2022-11-11 at 15:16 -0800, Evan Green wrote:
>>>>> Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
>>>>> restricts usermode's ability to extend or reset PCR 23.
>>>> Could I re ask the question here that I asked of Matthew's patch
>>>> set:
>>>>
>>>> https://lore.kernel.org/all/[email protected]/
>>>>
>>>> Which was could we use an NVRAM index in the TPM instead of a PCR???
>>>> The reason for asking was that PCRs are rather precious and might
>>>> get more so now that Lennart has some grand scheme for using more
>>>> of them in his unified boot project.?? Matthew promised to play with
>>>> the idea but never got back to the patch set to say whether he
>>>> investigated this or not.
>>> Even for PCR case it would be better to have it configurable through
>>> kernel command-line, including a disabled state, which would the
>>> default.
>>>
>>> This would be backwards compatible, and if designed properly, could
>>> more easily extended for NV index later on.
>> Um how? The observation is in the above referenced email is that PCR23
>> is reserved in the TCG literature for application usage. If any
>> application is actually using PCR23 based on that spec then revoking
>> access to user space will cause it to break. This is an ABI change
>> which is not backwards compatible. You can call it a distro problem if
>> it's command line configurable, but the default would be what most
>> distros take, so it's rather throwing them under the bus if there is an
>> application using it.
>>
>> Of course, if no application is actually using PCR23, then it's
>> probably OK to use it in the kernel and make it invisible to user
>> space, but no evidence about this has actually been presented.
> If there isn't, there will be in in the next week or so, if we can
> stay on schedule. Otherwise, I fear that Casey Schaufler, who I
> believe is holding his breath, may turn irretrievably blue.... :-)

Sorry to disappoint, but my supply of apoplexy is firmly rooted elsewhere
for the time being. :-( Also, you overestimate my interest in things
TPM related.

> The Trust Orchestration System, Quixote, that we are releasing for
> Linux uses PCR23 to generate an attestation of the functional state
> value for an internally modeled security domain.
>
> TSEM, the LSM based kernel component in all of this, supports the
> ability to implement multiple 'domains', nee namespaces, each of which
> can have a security modeling function attached to it. Each internally
> modeled domain has to have the ability to independently attest the
> functional value of the security model implemented for the
> domain/namespace.

I am very interested to see TSEM. I have heard nothing of it to date.

> We have found, and I believe others will find that, particularly the
> resettable registers, are too precious to be constrained from general
> usage. We actually just finished lifting the PCR23 extension
> functionality out of the TSEM driver and into userspace because having
> it in the kernel was too constraining.
>
> With respect to making the behavior a command-line option. We've
> slogged through 2+ years of conversations with sizable players who
> have indicated that if the 'distys' don't implement something, it
> isn't a relevant Linux technology, so a command-line option poses a
> barrier to innovation.
>
>> James
> Have a good day.
>
> As always,
> Dr. Greg
>
> The Quixote Project - Flailing at the Travails of Cybersecurity

2022-12-02 01:37:09

by Dr. Greg

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Wed, Nov 30, 2022 at 01:34:28PM -0800, Casey Schaufler wrote:

Good evening to everyone.

> On 11/30/2022 12:22 PM, Dr. Greg wrote:
> > On Sun, Nov 27, 2022 at 11:41:26AM -0500, James Bottomley wrote:
> >> Of course, if no application is actually using PCR23, then it's
> >> probably OK to use it in the kernel and make it invisible to user
> >> space, but no evidence about this has actually been presented.
> >
> > If there isn't, there will be in in the next week or so, if we can
> > stay on schedule. Otherwise, I fear that Casey Schaufler, who I
> > believe is holding his breath, may turn irretrievably blue.... :-)
>
> Sorry to disappoint, but my supply of apoplexy is firmly rooted
> elsewhere for the time being. :-( Also, you overestimate my interest
> in things TPM related.

I was being too clever by half, my comment had nothing to do with your
interest, or lack thereof about TPM's.... :-)

I had replied to one of the threads where LSM stacking and IMA
integration issues were being discussed and I commented that TSEM may
contribute to those conversations. You had replied back and said that
sending teasers was unfair, I was suggesting with my comment that you
were holding your breath waiting for the release of TSEM.... :-)

On a related note to this thread, a major component of Quixote/TSEM is
the notion of raising the question and opportunity for shaping what
TPM's should be when they grow up, given the limited resources they
bring to the table, let alone the notion that they are about
retrospective rather than prospective trust.

> I am very interested to see TSEM. I have heard nothing of it to
> date.

Hardly anyone has, small team, very focused, working in a deep dive
for the last couple of years to bring this forward.

Hopefully it will prove of interest and utility, I don't believe there
is a reference in the literature to an equivalent approach.

Have a good evening.

As always,
Dr. Greg

The Quixote Project - Flailing at the Travails of Cybersecurity

2022-12-02 21:50:55

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

On Mon, 2022-11-14 at 13:00 -0500, James Bottomley wrote:
> On Mon, 2022-11-14 at 09:43 -0800, Evan Green wrote:
> > On Mon, Nov 14, 2022 at 8:56 AM James Bottomley
> > <[email protected]>
> > wrote:
> [...]
> > > Of course, since openssl_tpm2_engine is the complete reference
> > > implementation that means I'll have to add the creation PCRs
> > > implementation to it ... unless you'd like to do it?
> >
> > I am willing to help as I'm the one making the mess. How does it
> > sequence along with your draft submission (before, after,
> > simultaneous)?
>
> At the moment, just send patches.  The openssl_tpm2_engine is
> developed on a groups.io mailing list:
>
> https://groups.io/g/openssl-tpm2-engine/
>
> You need an IETF specific tool (xml2rfc) to build the rfc from the
> xml, but it's available in most distros as python3-xml2rfc.  If you
> don't want to learn the IETF XML I can help you code up the patch to
> add that to the draft spec.

Just as a heads up, the patch series implementing signed policy (and
thus taking option [3]) is on the mailing list for review:

https://groups.io/g/openssl-tpm2-engine/message/296

With apologies for the awful lack of threading in the groups.io
interface.

So you don't have to build the RFC yourself, I published the proposed
update on my website:

https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt

If you want to use option [4] for the creation data, it's available.

Regards,

James


2022-12-05 20:48:58

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data

On Fri, Dec 2, 2022 at 1:03 PM James Bottomley <[email protected]> wrote:
>
> On Mon, 2022-11-14 at 13:00 -0500, James Bottomley wrote:
> > On Mon, 2022-11-14 at 09:43 -0800, Evan Green wrote:
> > > On Mon, Nov 14, 2022 at 8:56 AM James Bottomley
> > > <[email protected]>
> > > wrote:
> > [...]
> > > > Of course, since openssl_tpm2_engine is the complete reference
> > > > implementation that means I'll have to add the creation PCRs
> > > > implementation to it ... unless you'd like to do it?
> > >
> > > I am willing to help as I'm the one making the mess. How does it
> > > sequence along with your draft submission (before, after,
> > > simultaneous)?
> >
> > At the moment, just send patches. The openssl_tpm2_engine is
> > developed on a groups.io mailing list:
> >
> > https://groups.io/g/openssl-tpm2-engine/
> >
> > You need an IETF specific tool (xml2rfc) to build the rfc from the
> > xml, but it's available in most distros as python3-xml2rfc. If you
> > don't want to learn the IETF XML I can help you code up the patch to
> > add that to the draft spec.
>
> Just as a heads up, the patch series implementing signed policy (and
> thus taking option [3]) is on the mailing list for review:
>
> https://groups.io/g/openssl-tpm2-engine/message/296
>
> With apologies for the awful lack of threading in the groups.io
> interface.
>
> So you don't have to build the RFC yourself, I published the proposed
> update on my website:
>
> https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
> https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
>
> If you want to use option [4] for the creation data, it's available.

Perfect, thanks James!
-Evan

2022-12-08 00:03:29

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v5 00/11] Encrypted Hibernation

Hello, it's me again!

On Fri, Nov 11, 2022 at 3:19 PM Evan Green <[email protected]> wrote:
>
> We are exploring enabling hibernation in some new scenarios. However,
> our security team has a few requirements, listed below:
> 1. The hibernate image must be encrypted with protection derived from
> both the platform (eg TPM) and user authentication data (eg
> password).
> 2. Hibernation must not be a vector by which a malicious userspace can
> escalate to the kernel.
>
> Requirement #1 can be achieved solely with uswsusp, however requirement
> 2 necessitates mechanisms in the kernel to guarantee integrity of the
> hibernate image. The kernel needs a way to authenticate that it generated
> the hibernate image being loaded, and that the image has not been tampered
> with. Adding support for in-kernel AEAD encryption with a TPM-sealed key
> allows us to achieve both requirements with a single computation pass.
>
> Matthew Garrett published a series [1] that aligns closely with this
> goal. His series utilized the fact that PCR23 is a resettable PCR that
> can be blocked from access by usermode. The TPM can create a sealed key
> tied to PCR23 in two ways. First, the TPM can attest to the value of
> PCR23 when the key was created, which the kernel can use on resume to
> verify that the kernel must have created the key (since it is the only
> one capable of modifying PCR23). It can also create a policy that enforces
> PCR23 be set to a specific value as a condition of unsealing the key,
> preventing usermode from unsealing the key by talking directly to the
> TPM.
>
> This series adopts that primitive as a foundation, tweaking and building
> on it a bit. Where Matthew's series used the TPM-backed key to encrypt a
> hash of the image, this series uses the key directly as a gcm(aes)
> encryption key, which the kernel uses to encrypt and decrypt the
> hibernate image in chunks of 16 pages. This provides both encryption and
> integrity, which turns out to be a noticeable performance improvement over
> separate passes for encryption and hashing.
>
> The series also introduces the concept of mixing user key material into
> the encryption key. This allows usermode to introduce key material
> based on unspecified external authentication data (in our case derived
> from something like the user password or PIN), without requiring
> usermode to do a separate encryption pass.
>
> Matthew also documented issues his series had [2] related to generating
> fake images by booting alternate kernels without the PCR23 limiting.
> With access to PCR23 on the same machine, usermode can create fake
> hibernate images that are indistinguishable to the new kernel from
> genuine ones. His post outlines a solution that involves adding more
> PCRs into the creation data and policy, with some gyrations to make this
> work well on a standard PC.
>
> Our approach would be similar: on our machines PCR 0 indicates whether
> the system is booted in secure/verified mode or developer mode. By
> adding PCR0 to the policy, we can reject hibernate images made in
> developer mode while in verified mode (or vice versa).
>
> Additionally, mixing in the user authentication data limits both
> data exfiltration attacks (eg a stolen laptop) and forged hibernation
> image attacks to attackers that already know the authentication data (eg
> user's password). This, combined with our relatively sealed userspace
> (dm-verity on the rootfs), and some judicious clearing of the hibernate
> image (such as across an OS update) further reduce the risk of an online
> attack. The remaining attack space of a forgery from someone with
> physical access to the device and knowledge of the authentication data
> is out of scope for us, given that flipping to developer mode or
> reflashing RO firmware trivially achieves the same thing.
>
> A couple of patches still need to be written on top of this series. The
> generalized functionality to OR in additional PCRs via Kconfig (like PCR
> 0 or 5) still needs to be added. We'll also need a patch that disallows
> unencrypted forms of resume from hibernation, to fully close the door
> to malicious userspace. However, I wanted to get this series out first
> and get reactions from upstream before continuing to add to it.
>
> [1] https://patchwork.kernel.org/project/linux-pm/cover/[email protected]/
> [2] https://mjg59.dreamwidth.org/58077.html
>

Doug found a practical problem with this design. The security of this
mechanism depends on the kernel being able to prevent usermode from
manipulating PCR23. While this series has managed to add that gating
to the standard /dev/tpm interface, at least on ChromeOS, there are
still many "dangerous toys" lying around that might allow a malicious
root to communicate directly with the TPM. This raw access could allow
usermode to extend PCR23 manually and forge malicious hibernate images
that appear genuine. Examples of raw access include 1) i2cget -F, 2)
unbinding the driver and binding i2c-dev instead, 3) using /dev/mem to
manipulate the i2c controller registers directly, and 4) my favorite,
remuxing the i2c pins to GPIO and bitbanging.

We did some brainstorming and came up with a pivot that has the
benefits of 1) reusing a decent chunk of this series, 2) not taking
PCR23 away from usermode (which based on other comments seemed like it
might not fly anyway), and 3) pushing the TPM interaction back down
into usermode. The new element we take advantage of is that our early
userspace is still considered trusted, as we sign the rootfs and
protect it with dm-verity.

The idea is to have early userspace ask the TPM to create a sealed key
bound to a (non-resettable) PCR. We then save the blob to disk, extend
the PCR (to prevent future unsealings in this boot), and push the key
material up to the kernel for use as a "hibernate seed". The kernel
will hold this seed in memory, and at hibernate time will use it to
encrypt a randomly generated "bulk key". The bulk key is then used to
encrypt the main hibernate image. So on disk at hibernate, we have 1)
the encrypted hibernate image, protected by the bulk key, 2) the bulk
key, protected by the seed, and finally 3) the seed, a TPM-protected
key blob that can only be unsealed when a PCR is set to its boot
value. In our own userspace implementation we'd seal this against a
firmware PCR as well, to differentiate between Verified mode and
Developer mode.

At resume time, early userspace would find the blob, successfully
unseal it (because the PCRs had reset back to the value that matches
the policy), and push the recovered seed to the kernel. It can then
push the encrypted bulk key and encrypted hibernate image. On our
systems, this works fine as the PCRs seem to always reset across
hibernate. Is that true generally as well?

So my plan for the next spin of this series looks something like:
* Drop the tpm: and security: subsystem patches
* Keep the gist of the PM: patches as is, but instead of the TPM stuff...
* Introduce two new sysfs files, one to allow usermode to save the
seed into kernel memory, and another to lock out future changes to the
hibernate seed (until the next reboot).
* Use the hibernate seed to encrypt a randomly generated bulk key,
which is then used to encrypt the main hibernate image.
* Keep the "PM: mix user key in" patch, as we still need the image to
be encrypted with a key based on user authentication data, which this
new mechanism alone doesn't provide.

Anyone have any big objections to that plan, or see new gaping holes
in the idea? In the end I think it's actually a little nicer, as it
decouples all of the TPM-specific machinery from the concept of secure
hibernate, as well as not trying to police PCR access. Casey and Greg,
I'm going to guess you don't want to be CCed on the next spin, given
that I'm dropping the notion of taking PCR23 away from userspace.
Please holler if you would like to be CCed.

-Evan

2023-01-03 21:21:10

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Mon, Nov 14, 2022 at 9:11 AM James Bottomley <[email protected]> wrote:
>
> On Fri, 2022-11-11 at 15:16 -0800, Evan Green wrote:
> > Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
> > restricts usermode's ability to extend or reset PCR 23.
>
> Could I re ask the question here that I asked of Matthew's patch set:
>
> https://lore.kernel.org/all/[email protected]/
>
> Which was could we use an NVRAM index in the TPM instead of a PCR? The
> reason for asking was that PCRs are rather precious and might get more
> so now that Lennart has some grand scheme for using more of them in his
> unified boot project. Matthew promised to play with the idea but never
> got back to the patch set to say whether he investigated this or not.

Is there any way to get key creation data to include NV indexes? If
not, no, we can't use NVRAM.

2023-01-03 22:00:49

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Tue, Jan 3, 2023 at 2:43 PM Matthew Garrett <[email protected]> wrote:
>
> On Mon, Nov 14, 2022 at 9:11 AM James Bottomley <[email protected]> wrote:
> >
> > On Fri, 2022-11-11 at 15:16 -0800, Evan Green wrote:
> > > Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
> > > restricts usermode's ability to extend or reset PCR 23.
> >
> > Could I re ask the question here that I asked of Matthew's patch set:
> >
> > https://lore.kernel.org/all/[email protected]/
> >
> > Which was could we use an NVRAM index in the TPM instead of a PCR? The
> > reason for asking was that PCRs are rather precious and might get more
> > so now that Lennart has some grand scheme for using more of them in his
> > unified boot project. Matthew promised to play with the idea but never
> > got back to the patch set to say whether he investigated this or not.
>
> Is there any way to get key creation data to include NV indexes?

Not that I am aware of and the spec seems to be a no.

> If not, no, we can't use NVRAM.

What's the use case of using the creation data and ticket in this
context? Who gets the
creationData and the ticket?
Could a user supplied outsideInfo work? IIRC I saw some patches flying around
where the sessions will get encrypted and presumably correctly as well. This
would allow the transfer of that outsideInfo, like the NV Index PCR value to
be included and integrity protected by the session HMAC.

2023-01-03 22:03:12

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Tue, Jan 3, 2023 at 1:05 PM William Roberts <[email protected]> wrote:

> What's the use case of using the creation data and ticket in this
> context? Who gets the
> creationData and the ticket?
> Could a user supplied outsideInfo work? IIRC I saw some patches flying around
> where the sessions will get encrypted and presumably correctly as well. This
> would allow the transfer of that outsideInfo, like the NV Index PCR value to
> be included and integrity protected by the session HMAC.

The goal is to ensure that the key was generated by the kernel. In the
absence of the creation data, an attacker could generate a hibernation
image using their own key and trick the kernel into resuming arbitrary
code. We don't have any way to pass secret data from the hibernate
kernel to the resume kernel, so I don't think there's any easy way to
do it with outsideinfo.

2023-01-10 16:10:09

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Tue, Jan 3, 2023 at 2:43 PM Matthew Garrett <[email protected]> wrote:
>
> On Mon, Nov 14, 2022 at 9:11 AM James Bottomley <[email protected]> wrote:
> >
> > On Fri, 2022-11-11 at 15:16 -0800, Evan Green wrote:
> > > Introduce a new Kconfig, TCG_TPM_RESTRICT_PCR, which if enabled
> > > restricts usermode's ability to extend or reset PCR 23.
> >
> > Could I re ask the question here that I asked of Matthew's patch set:
> >
> > https://lore.kernel.org/all/[email protected]/
> >
> > Which was could we use an NVRAM index in the TPM instead of a PCR? The
> > reason for asking was that PCRs are rather precious and might get more
> > so now that Lennart has some grand scheme for using more of them in his
> > unified boot project. Matthew promised to play with the idea but never
> > got back to the patch set to say whether he investigated this or not.
>
> Is there any way to get key creation data to include NV indexes? If
> not, no, we can't use NVRAM.

No theirs not, but there's room for qualifyingData. So some background
on this is they use the PCR value to verify it's the right key.
Since it's added by the TPM within the trust boundary its an
unforgeable value, unlike the qdata.

I think there are better ways to verify it's the right key, i.e. the
ability to wield the key, or if you have some ability to remember
state, you
could verify the name which is cryptgraphically bound to the private
key and thus TPM2_Load will fail. From what I understand they
have no ability to remember state as their verifying and executing a
resume kernel, but I am not well versed in that area of the kernel.
It makes me think that they are checking against a hardcoded known
PCR23 state and rolling it to prevent other keys from being
generated. I would consider policy locality for controlling who can
use the key and couple with policynv if revoking long lasting keys
is a need. If they are ephemeral, theirs the NULL hierarchy.

A lot of this is conjecture, as Matthew just stopped responding.
Perhaps they are away or busy, but I just wanted to weigh in on this.

2023-01-14 15:09:53

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> <[email protected]> wrote:
>
> > What's the use case of using the creation data and ticket in this
> > context? Who gets the creationData and the ticket?
> > Could a user supplied outsideInfo work? IIRC I saw some patches
> > flying around where the sessions will get encrypted and presumably
> > correctly as well. This would allow the transfer of that
> > outsideInfo, like the NV Index PCR value to be included and
> > integrity protected by the session HMAC.
>
> The goal is to ensure that the key was generated by the kernel. In
> the absence of the creation data, an attacker could generate a
> hibernation image using their own key and trick the kernel into
> resuming arbitrary code. We don't have any way to pass secret data
> from the hibernate kernel to the resume kernel, so I don't think
> there's any easy way to do it with outsideinfo.

Can we go back again to why you can't use locality? It's exactly
designed for this since locality is part of creation data. Currently
everything only uses locality 0, so it's impossible for anyone on Linux
to produce a key with anything other than 0 in the creation data for
locality. However, the dynamic launch people are proposing that the
Kernel should use Locality 2 for all its operations, which would allow
you to distinguish a key created by the kernel from one created by a
user by locality.

I think the previous objection was that not all TPMs implement
locality, but then not all laptops have TPMs either, so if you ever
come across one which has a TPM but no locality, it's in a very similar
security boat to one which has no TPM.

James

2023-01-14 15:37:19

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Sat, Jan 14, 2023 at 8:55 AM James Bottomley <[email protected]> wrote:
>
> On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > <[email protected]> wrote:
> >
> > > What's the use case of using the creation data and ticket in this
> > > context? Who gets the creationData and the ticket?
> > > Could a user supplied outsideInfo work? IIRC I saw some patches
> > > flying around where the sessions will get encrypted and presumably
> > > correctly as well. This would allow the transfer of that
> > > outsideInfo, like the NV Index PCR value to be included and
> > > integrity protected by the session HMAC.
> >
> > The goal is to ensure that the key was generated by the kernel. In
> > the absence of the creation data, an attacker could generate a
> > hibernation image using their own key and trick the kernel into
> > resuming arbitrary code. We don't have any way to pass secret data
> > from the hibernate kernel to the resume kernel, so I don't think
> > there's any easy way to do it with outsideinfo.
>
> Can we go back again to why you can't use locality? It's exactly
> designed for this since locality is part of creation data. Currently
> everything only uses locality 0, so it's impossible for anyone on Linux
> to produce a key with anything other than 0 in the creation data for
> locality. However, the dynamic launch people are proposing that the
> Kernel should use Locality 2 for all its operations, which would allow
> you to distinguish a key created by the kernel from one created by a
> user by locality.
>
> I think the previous objection was that not all TPMs implement
> locality, but then not all laptops have TPMs either, so if you ever
> come across one which has a TPM but no locality, it's in a very similar
> security boat to one which has no TPM.
>

I also usually stick to features within the PTP spec[1], which includes
the locality support.

+2 for locality, I responded somewhere that I also support locality. I
was thinking more of TPM2_PolicyLocality I didn't realize that's
within the creationData. I was thinking more along the lines of, can I
wield the key over "did my locality create it". I'm not sure
what other protections are on the key,are there any protections
preventing them from
wielding it and using it to sign something nefarious?

1. https://trustedcomputinggroup.org/wp-content/uploads/TCG_PC_Client_Platform_TPM_Profile_PTP_Specification_Family_2.0_Revision_1.3v22.pdf

> James
>

2023-01-15 03:15:46

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Sat, Jan 14, 2023 at 6:55 AM James Bottomley <[email protected]> wrote:
> Can we go back again to why you can't use locality? It's exactly
> designed for this since locality is part of creation data. Currently
> everything only uses locality 0, so it's impossible for anyone on Linux
> to produce a key with anything other than 0 in the creation data for
> locality. However, the dynamic launch people are proposing that the
> Kernel should use Locality 2 for all its operations, which would allow
> you to distinguish a key created by the kernel from one created by a
> user by locality.
>
> I think the previous objection was that not all TPMs implement
> locality, but then not all laptops have TPMs either, so if you ever
> come across one which has a TPM but no locality, it's in a very similar
> security boat to one which has no TPM.

It's not a question of TPM support, it's a question of platform
support. Intel chipsets that don't support TXT simply don't forward
requests with non-0 locality. Every Windows-sticker laptop since 2014
has shipped with a TPM, but the number that ship with TXT support is a
very small percentage of that. I agree that locality is the obvious
solution for a whole bunch of problems, but it's just not usable in
the generic case.

2023-01-15 14:51:43

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Sat, Jan 14, 2023 at 9:05 PM Matthew Garrett <[email protected]> wrote:
>
> On Sat, Jan 14, 2023 at 6:55 AM James Bottomley <[email protected]> wrote:
> > Can we go back again to why you can't use locality? It's exactly
> > designed for this since locality is part of creation data. Currently
> > everything only uses locality 0, so it's impossible for anyone on Linux
> > to produce a key with anything other than 0 in the creation data for
> > locality. However, the dynamic launch people are proposing that the
> > Kernel should use Locality 2 for all its operations, which would allow
> > you to distinguish a key created by the kernel from one created by a
> > user by locality.
> >
> > I think the previous objection was that not all TPMs implement
> > locality, but then not all laptops have TPMs either, so if you ever
> > come across one which has a TPM but no locality, it's in a very similar
> > security boat to one which has no TPM.
>
> It's not a question of TPM support, it's a question of platform
> support. Intel chipsets that don't support TXT simply don't forward
> requests with non-0 locality. Every Windows-sticker laptop since 2014
> has shipped with a TPM, but the number that ship with TXT support is a
> very small percentage of that. I agree that locality is the obvious
> solution for a whole bunch of problems, but it's just not usable in
> the generic case.

Instead of walling off a PCR, why not wall off an NV Index PCR and
use a policy?

2023-01-17 23:49:46

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Sat, 2023-01-14 at 19:05 -0800, Matthew Garrett wrote:
> On Sat, Jan 14, 2023 at 6:55 AM James Bottomley <[email protected]>
> wrote:
> > Can we go back again to why you can't use locality?  It's exactly
> > designed for this since locality is part of creation data. 
> > Currently everything only uses locality 0, so it's impossible for
> > anyone on Linux to produce a key with anything other than 0 in the
> > creation data for locality.  However, the dynamic launch people are
> > proposing that the Kernel should use Locality 2 for all its
> > operations, which would allow you to distinguish a key created by
> > the kernel from one created by a user by locality.
> >
> > I think the previous objection was that not all TPMs implement
> > locality, but then not all laptops have TPMs either, so if you ever
> > come across one which has a TPM but no locality, it's in a very
> > similar security boat to one which has no TPM.
>
> It's not a question of TPM support, it's a question of platform
> support. Intel chipsets that don't support TXT simply don't forward
> requests with non-0 locality. Every Windows-sticker laptop since 2014
> has shipped with a TPM, but the number that ship with TXT support is
> a very small percentage of that. I agree that locality is the obvious
> solution for a whole bunch of problems, but it's just not usable in
> the generic case.

How sure are you of this statement? Of all the Laptops I have with
TPM2 (a sample size of 2), my old Dell XPS-13 (a 9350 bought in 2016
with a TPM 1.2 that was firmware upgraded to 2.0) has a Nuvoton TIS TPM
that doesn't respond on any locality other than 0. However, my more
modern Inspiron 13 2-in-1 (a 7391 from 2019 recently bought
refurbished) has an Intel PTT TPM using the CRB interface and responds
fine on locality 1 and also indicates that locality in the creation
data. Neither of these laptops has TXT nor the SMX extensions, so that
would seem to indicate your statement above isn't universal.

James

2023-01-21 03:47:49

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > <[email protected]> wrote:
> >
> > > What's the use case of using the creation data and ticket in this
> > > context? Who gets the creationData and the ticket?
> > > Could a user supplied outsideInfo work? IIRC I saw some patches
> > > flying around where the sessions will get encrypted and presumably
> > > correctly as well. This would allow the transfer of that
> > > outsideInfo, like the NV Index PCR value to be included and
> > > integrity protected by the session HMAC.
> >
> > The goal is to ensure that the key was generated by the kernel. In
> > the absence of the creation data, an attacker could generate a
> > hibernation image using their own key and trick the kernel into
> > resuming arbitrary code. We don't have any way to pass secret data
> > from the hibernate kernel to the resume kernel, so I don't think
> > there's any easy way to do it with outsideinfo.
>
> Can we go back again to why you can't use locality? It's exactly
> designed for this since locality is part of creation data. Currently
> everything only uses locality 0, so it's impossible for anyone on Linux
> to produce a key with anything other than 0 in the creation data for
> locality. However, the dynamic launch people are proposing that the
> Kernel should use Locality 2 for all its operations, which would allow
> you to distinguish a key created by the kernel from one created by a
> user by locality.
>
> I think the previous objection was that not all TPMs implement
> locality, but then not all laptops have TPMs either, so if you ever
> come across one which has a TPM but no locality, it's in a very similar
> security boat to one which has no TPM.

Kernel could try to use locality 2 and use locality 0 as fallback.

BR, Jarkko

2023-01-23 17:48:45

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]> wrote:
>
> On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > <[email protected]> wrote:
> > >
> > > > What's the use case of using the creation data and ticket in this
> > > > context? Who gets the creationData and the ticket?
> > > > Could a user supplied outsideInfo work? IIRC I saw some patches
> > > > flying around where the sessions will get encrypted and presumably
> > > > correctly as well. This would allow the transfer of that
> > > > outsideInfo, like the NV Index PCR value to be included and
> > > > integrity protected by the session HMAC.
> > >
> > > The goal is to ensure that the key was generated by the kernel. In
> > > the absence of the creation data, an attacker could generate a
> > > hibernation image using their own key and trick the kernel into
> > > resuming arbitrary code. We don't have any way to pass secret data
> > > from the hibernate kernel to the resume kernel, so I don't think
> > > there's any easy way to do it with outsideinfo.
> >
> > Can we go back again to why you can't use locality? It's exactly
> > designed for this since locality is part of creation data. Currently
> > everything only uses locality 0, so it's impossible for anyone on Linux
> > to produce a key with anything other than 0 in the creation data for
> > locality. However, the dynamic launch people are proposing that the
> > Kernel should use Locality 2 for all its operations, which would allow
> > you to distinguish a key created by the kernel from one created by a
> > user by locality.
> >
> > I think the previous objection was that not all TPMs implement
> > locality, but then not all laptops have TPMs either, so if you ever
> > come across one which has a TPM but no locality, it's in a very similar
> > security boat to one which has no TPM.
>
> Kernel could try to use locality 2 and use locality 0 as fallback.

I don't think that would work for Matthew, they need something
reliable to indicate key provenance.

I was informed that all 5 localities should be supported starting
with Gen 7 Kaby Lake launched in 2016. Don't know if this is
still "too new".

>
> BR, Jarkko

2023-01-24 12:05:59

by Dr. Greg

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Mon, Jan 23, 2023 at 11:48:25AM -0600, William Roberts wrote:

Good morning, I hope the week is going well for everyone.

> On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]> wrote:
> >
> > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > <[email protected]> wrote:
> > > >
> > > > > What's the use case of using the creation data and ticket in this
> > > > > context? Who gets the creationData and the ticket?
> > > > > Could a user supplied outsideInfo work? IIRC I saw some patches
> > > > > flying around where the sessions will get encrypted and presumably
> > > > > correctly as well. This would allow the transfer of that
> > > > > outsideInfo, like the NV Index PCR value to be included and
> > > > > integrity protected by the session HMAC.
> > > >
> > > > The goal is to ensure that the key was generated by the kernel. In
> > > > the absence of the creation data, an attacker could generate a
> > > > hibernation image using their own key and trick the kernel into
> > > > resuming arbitrary code. We don't have any way to pass secret data
> > > > from the hibernate kernel to the resume kernel, so I don't think
> > > > there's any easy way to do it with outsideinfo.
> > >
> > > Can we go back again to why you can't use locality? It's exactly
> > > designed for this since locality is part of creation data. Currently
> > > everything only uses locality 0, so it's impossible for anyone on Linux
> > > to produce a key with anything other than 0 in the creation data for
> > > locality. However, the dynamic launch people are proposing that the
> > > Kernel should use Locality 2 for all its operations, which would allow
> > > you to distinguish a key created by the kernel from one created by a
> > > user by locality.
> > >
> > > I think the previous objection was that not all TPMs implement
> > > locality, but then not all laptops have TPMs either, so if you ever
> > > come across one which has a TPM but no locality, it's in a very similar
> > > security boat to one which has no TPM.
> >
> > Kernel could try to use locality 2 and use locality 0 as fallback.

> I don't think that would work for Matthew, they need something
> reliable to indicate key provenance.

Indeed, I was going to mention that. Falling back means that the
security guarantee is lost, perhaps silently and lost on the owner of
the system, if they are not paying attention to things like the boot
logs.

One of the persistent challenges with these hardware security
technologies is that they need to be ubiquitous to be useful,
something that has historically plagued all of these technologies.

> I was informed that all 5 localities should be supported starting
> with Gen 7 Kaby Lake launched in 2016. Don't know if this is still
> "too new".

It will be necessary, and important, to differentiate between
'supported' and 'available'.

Historically, security features have been SKU'ified, in other words,
made available only on specific SKU's, even when the platform writ
large has the necessary support. These SKU's are designed to be
directed at various verticals or OEM's who are perceived to be willing
to pay more for enhanced security.

I've had conversations on whether or not hardware technologies would
be available and the conversation usually ends with the equivalent of:
"Show us the business case for supporting this."

Which translates, roughly, into how much money are we going to make if
we offer this.

Unfortunately, without being ubiquitous, as you note for a long period
of time, there is no development interest, which in turn translates
into no market 'pull'. A rather troublesome dilemma for security
innovation.

As always,
Dr. Greg

The Quixote Project - Flailing at the Travails of Cybersecurity

2023-01-24 12:38:49

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Mon, 2023-01-23 at 11:48 -0600, William Roberts wrote:
> On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]>
> wrote:
> >
> > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > <[email protected]> wrote:
> > > >
> > > > > What's the use case of using the creation data and ticket in
> > > > > this context? Who gets the creationData and the ticket?
> > > > > Could a user supplied outsideInfo work? IIRC I saw some
> > > > > patches flying around where the sessions will get encrypted
> > > > > and presumably correctly as well. This would allow the
> > > > > transfer of that outsideInfo, like the NV Index PCR value to
> > > > > be included and integrity protected by the session HMAC.
> > > >
> > > > The goal is to ensure that the key was generated by the kernel.
> > > > In the absence of the creation data, an attacker could generate
> > > > a hibernation image using their own key and trick the kernel
> > > > into resuming arbitrary code. We don't have any way to pass
> > > > secret data from the hibernate kernel to the resume kernel, so
> > > > I don't think there's any easy way to do it with outsideinfo.
> > >
> > > Can we go back again to why you can't use locality?  It's exactly
> > > designed for this since locality is part of creation data. 
> > > Currently everything only uses locality 0, so it's impossible for
> > > anyone on Linux to produce a key with anything other than 0 in
> > > the creation data for locality.  However, the dynamic launch
> > > people are proposing that the Kernel should use Locality 2 for
> > > all its operations, which would allow you to distinguish a key
> > > created by the kernel from one created by a user by locality.
> > >
> > > I think the previous objection was that not all TPMs implement
> > > locality, but then not all laptops have TPMs either, so if you
> > > ever come across one which has a TPM but no locality, it's in a
> > > very similar security boat to one which has no TPM.
> >
> > Kernel could try to use locality 2 and use locality 0 as fallback.
>
> I don't think that would work for Matthew, they need something
> reliable to indicate key provenance.

No, I think it would be good enough: locality 0 means anyone (including
the kernel on a machine which doesn't function correctly) could have
created this key. Locality 2 would mean only the kernel could have
created this key.

By the time the kernel boots and before it loads the hibernation image
it will know the answer to the question "does my TPM support locality
2", so it can use that in its security assessment: if the kernel
supports locality 2 and the key wasn't created in locality 2 then
assume an attack. Obviously, if the kernel doesn't support locality 2
then the hibernation resume has to accept any old key, but that's the
same as the situation today.

> I was informed that all 5 localities should be supported starting
> with Gen 7 Kaby Lake launched in 2016. Don't know if this is
> still "too new".

It's probably good enough. Current laptops which can't use locality 2
are in the same position as now, but newer ones can provide more
security guarantees.

There is, however, another wrinkle: can Kaby Lake be persuaded, though
bios settings perhaps, to shut off the non zero localities? This would
allow for a downgrade attack where you shut off locality 2 then present
a forged locality 0 key and hibernation image; the kernel will think,
because it can't access locality 2, that it's in a reduced security
environment so the key might be OK. We could fix this by requiring
Kaby Lake and beyond to have locality 2 and refusing to hibernate if it
can't be accessed and building "is this Kaby lake or beyond" into the
check for should I have locality 2, but this is getting complex and
error prone.

James


2023-01-24 15:06:09

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Tue, Jan 24, 2023 at 6:38 AM James Bottomley <[email protected]> wrote:
>
> On Mon, 2023-01-23 at 11:48 -0600, William Roberts wrote:
> > On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]>
> > wrote:
> > >
> > > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > > <[email protected]> wrote:
> > > > >
> > > > > > What's the use case of using the creation data and ticket in
> > > > > > this context? Who gets the creationData and the ticket?
> > > > > > Could a user supplied outsideInfo work? IIRC I saw some
> > > > > > patches flying around where the sessions will get encrypted
> > > > > > and presumably correctly as well. This would allow the
> > > > > > transfer of that outsideInfo, like the NV Index PCR value to
> > > > > > be included and integrity protected by the session HMAC.
> > > > >
> > > > > The goal is to ensure that the key was generated by the kernel.
> > > > > In the absence of the creation data, an attacker could generate
> > > > > a hibernation image using their own key and trick the kernel
> > > > > into resuming arbitrary code. We don't have any way to pass
> > > > > secret data from the hibernate kernel to the resume kernel, so
> > > > > I don't think there's any easy way to do it with outsideinfo.
> > > >
> > > > Can we go back again to why you can't use locality? It's exactly
> > > > designed for this since locality is part of creation data.
> > > > Currently everything only uses locality 0, so it's impossible for
> > > > anyone on Linux to produce a key with anything other than 0 in
> > > > the creation data for locality. However, the dynamic launch
> > > > people are proposing that the Kernel should use Locality 2 for
> > > > all its operations, which would allow you to distinguish a key
> > > > created by the kernel from one created by a user by locality.
> > > >
> > > > I think the previous objection was that not all TPMs implement
> > > > locality, but then not all laptops have TPMs either, so if you
> > > > ever come across one which has a TPM but no locality, it's in a
> > > > very similar security boat to one which has no TPM.
> > >
> > > Kernel could try to use locality 2 and use locality 0 as fallback.
> >
> > I don't think that would work for Matthew, they need something
> > reliable to indicate key provenance.
>
> No, I think it would be good enough: locality 0 means anyone (including
> the kernel on a machine which doesn't function correctly) could have
> created this key. Locality 2 would mean only the kernel could have
> created this key.

That's exactly what I was saying, for this feature to be functional
2 localities need to be supported.

>
> By the time the kernel boots and before it loads the hibernation image
> it will know the answer to the question "does my TPM support locality
> 2", so it can use that in its security assessment: if the kernel
> supports locality 2 and the key wasn't created in locality 2 then
> assume an attack. Obviously, if the kernel doesn't support locality 2
> then the hibernation resume has to accept any old key, but that's the
> same as the situation today.
>

Yep, we had this conversation offline on a thread, i'm in agreement here
as well.

> > I was informed that all 5 localities should be supported starting
> > with Gen 7 Kaby Lake launched in 2016. Don't know if this is
> > still "too new".
>
> It's probably good enough. Current laptops which can't use locality 2
> are in the same position as now, but newer ones can provide more
> security guarantees.
>
> There is, however, another wrinkle: can Kaby Lake be persuaded, though
> bios settings perhaps, to shut off the non zero localities?

I have no idea, and I don't have one handy, but I can ask around.

> This would
> allow for a downgrade attack where you shut off locality 2 then present
> a forged locality 0 key and hibernation image; the kernel will think,
> because it can't access locality 2, that it's in a reduced security
> environment so the key might be OK. We could fix this by requiring
> Kaby Lake and beyond to have locality 2 and refusing to hibernate if it
> can't be accessed and building "is this Kaby lake or beyond" into the
> check for should I have locality 2, but this is getting complex and
> error prone.
>
> James
>

2023-01-26 17:08:04

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Mon, Jan 23, 2023 at 11:48:25AM -0600, William Roberts wrote:
> On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]> wrote:
> >
> > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > <[email protected]> wrote:
> > > >
> > > > > What's the use case of using the creation data and ticket in this
> > > > > context? Who gets the creationData and the ticket?
> > > > > Could a user supplied outsideInfo work? IIRC I saw some patches
> > > > > flying around where the sessions will get encrypted and presumably
> > > > > correctly as well. This would allow the transfer of that
> > > > > outsideInfo, like the NV Index PCR value to be included and
> > > > > integrity protected by the session HMAC.
> > > >
> > > > The goal is to ensure that the key was generated by the kernel. In
> > > > the absence of the creation data, an attacker could generate a
> > > > hibernation image using their own key and trick the kernel into
> > > > resuming arbitrary code. We don't have any way to pass secret data
> > > > from the hibernate kernel to the resume kernel, so I don't think
> > > > there's any easy way to do it with outsideinfo.
> > >
> > > Can we go back again to why you can't use locality? It's exactly
> > > designed for this since locality is part of creation data. Currently
> > > everything only uses locality 0, so it's impossible for anyone on Linux
> > > to produce a key with anything other than 0 in the creation data for
> > > locality. However, the dynamic launch people are proposing that the
> > > Kernel should use Locality 2 for all its operations, which would allow
> > > you to distinguish a key created by the kernel from one created by a
> > > user by locality.
> > >
> > > I think the previous objection was that not all TPMs implement
> > > locality, but then not all laptops have TPMs either, so if you ever
> > > come across one which has a TPM but no locality, it's in a very similar
> > > security boat to one which has no TPM.
> >
> > Kernel could try to use locality 2 and use locality 0 as fallback.
>
> I don't think that would work for Matthew, they need something
> reliable to indicate key provenance.
>
> I was informed that all 5 localities should be supported starting
> with Gen 7 Kaby Lake launched in 2016. Don't know if this is
> still "too new".

What about having opt-in flag that distributions can then enable?

BR, Jarkko

2023-01-26 17:12:47

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Thu, Jan 26, 2023 at 05:07:43PM +0000, Jarkko Sakkinen wrote:
> On Mon, Jan 23, 2023 at 11:48:25AM -0600, William Roberts wrote:
> > On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]> wrote:
> > >
> > > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > > <[email protected]> wrote:
> > > > >
> > > > > > What's the use case of using the creation data and ticket in this
> > > > > > context? Who gets the creationData and the ticket?
> > > > > > Could a user supplied outsideInfo work? IIRC I saw some patches
> > > > > > flying around where the sessions will get encrypted and presumably
> > > > > > correctly as well. This would allow the transfer of that
> > > > > > outsideInfo, like the NV Index PCR value to be included and
> > > > > > integrity protected by the session HMAC.
> > > > >
> > > > > The goal is to ensure that the key was generated by the kernel. In
> > > > > the absence of the creation data, an attacker could generate a
> > > > > hibernation image using their own key and trick the kernel into
> > > > > resuming arbitrary code. We don't have any way to pass secret data
> > > > > from the hibernate kernel to the resume kernel, so I don't think
> > > > > there's any easy way to do it with outsideinfo.
> > > >
> > > > Can we go back again to why you can't use locality? It's exactly
> > > > designed for this since locality is part of creation data. Currently
> > > > everything only uses locality 0, so it's impossible for anyone on Linux
> > > > to produce a key with anything other than 0 in the creation data for
> > > > locality. However, the dynamic launch people are proposing that the
> > > > Kernel should use Locality 2 for all its operations, which would allow
> > > > you to distinguish a key created by the kernel from one created by a
> > > > user by locality.
> > > >
> > > > I think the previous objection was that not all TPMs implement
> > > > locality, but then not all laptops have TPMs either, so if you ever
> > > > come across one which has a TPM but no locality, it's in a very similar
> > > > security boat to one which has no TPM.
> > >
> > > Kernel could try to use locality 2 and use locality 0 as fallback.
> >
> > I don't think that would work for Matthew, they need something
> > reliable to indicate key provenance.
> >
> > I was informed that all 5 localities should be supported starting
> > with Gen 7 Kaby Lake launched in 2016. Don't know if this is
> > still "too new".
>
> What about having opt-in flag that distributions can then enable?

This is more intrusive but still worth of consideration: add opt-in
kernel command-line flag for no locality. I.e. require locality support
unless explicitly stated otherwise.

I'd presume that legacy production cases are a rarity but really is
something that is beyond me, and could potentially draw wrong conclusions.

BR, Jarkko

2023-01-26 17:20:46

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Thu, Jan 26, 2023 at 11:12 AM Jarkko Sakkinen <[email protected]> wrote:
>
> On Thu, Jan 26, 2023 at 05:07:43PM +0000, Jarkko Sakkinen wrote:
> > On Mon, Jan 23, 2023 at 11:48:25AM -0600, William Roberts wrote:
> > > On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]> wrote:
> > > >
> > > > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > > > <[email protected]> wrote:
> > > > > >
> > > > > > > What's the use case of using the creation data and ticket in this
> > > > > > > context? Who gets the creationData and the ticket?
> > > > > > > Could a user supplied outsideInfo work? IIRC I saw some patches
> > > > > > > flying around where the sessions will get encrypted and presumably
> > > > > > > correctly as well. This would allow the transfer of that
> > > > > > > outsideInfo, like the NV Index PCR value to be included and
> > > > > > > integrity protected by the session HMAC.
> > > > > >
> > > > > > The goal is to ensure that the key was generated by the kernel. In
> > > > > > the absence of the creation data, an attacker could generate a
> > > > > > hibernation image using their own key and trick the kernel into
> > > > > > resuming arbitrary code. We don't have any way to pass secret data
> > > > > > from the hibernate kernel to the resume kernel, so I don't think
> > > > > > there's any easy way to do it with outsideinfo.
> > > > >
> > > > > Can we go back again to why you can't use locality? It's exactly
> > > > > designed for this since locality is part of creation data. Currently
> > > > > everything only uses locality 0, so it's impossible for anyone on Linux
> > > > > to produce a key with anything other than 0 in the creation data for
> > > > > locality. However, the dynamic launch people are proposing that the
> > > > > Kernel should use Locality 2 for all its operations, which would allow
> > > > > you to distinguish a key created by the kernel from one created by a
> > > > > user by locality.
> > > > >
> > > > > I think the previous objection was that not all TPMs implement
> > > > > locality, but then not all laptops have TPMs either, so if you ever
> > > > > come across one which has a TPM but no locality, it's in a very similar
> > > > > security boat to one which has no TPM.
> > > >
> > > > Kernel could try to use locality 2 and use locality 0 as fallback.
> > >
> > > I don't think that would work for Matthew, they need something
> > > reliable to indicate key provenance.
> > >
> > > I was informed that all 5 localities should be supported starting
> > > with Gen 7 Kaby Lake launched in 2016. Don't know if this is
> > > still "too new".
> >
> > What about having opt-in flag that distributions can then enable?
>
> This is more intrusive but still worth of consideration: add opt-in
> kernel command-line flag for no locality. I.e. require locality support
> unless explicitly stated otherwise.
>
> I'd presume that legacy production cases are a rarity but really is
> something that is beyond me, and could potentially draw wrong conclusions.
>

One thing that was never answered for me, is that there was nowhere safe
to store some information about the expected key or a secret. That would
be the most obvious solution, so I am assuming that's a no.

2023-01-26 17:21:40

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Tue, Jan 24, 2023 at 07:38:04AM -0500, James Bottomley wrote:
> On Mon, 2023-01-23 at 11:48 -0600, William Roberts wrote:
> > On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]>
> > wrote:
> > >
> > > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > > <[email protected]> wrote:
> > > > >
> > > > > > What's the use case of using the creation data and ticket in
> > > > > > this context? Who gets the creationData and the ticket?
> > > > > > Could a user supplied outsideInfo work? IIRC I saw some
> > > > > > patches flying around where the sessions will get encrypted
> > > > > > and presumably correctly as well. This would allow the
> > > > > > transfer of that outsideInfo, like the NV Index PCR value to
> > > > > > be included and integrity protected by the session HMAC.
> > > > >
> > > > > The goal is to ensure that the key was generated by the kernel.
> > > > > In the absence of the creation data, an attacker could generate
> > > > > a hibernation image using their own key and trick the kernel
> > > > > into resuming arbitrary code. We don't have any way to pass
> > > > > secret data from the hibernate kernel to the resume kernel, so
> > > > > I don't think there's any easy way to do it with outsideinfo.
> > > >
> > > > Can we go back again to why you can't use locality?? It's exactly
> > > > designed for this since locality is part of creation data.?
> > > > Currently everything only uses locality 0, so it's impossible for
> > > > anyone on Linux to produce a key with anything other than 0 in
> > > > the creation data for locality.? However, the dynamic launch
> > > > people are proposing that the Kernel should use Locality 2 for
> > > > all its operations, which would allow you to distinguish a key
> > > > created by the kernel from one created by a user by locality.
> > > >
> > > > I think the previous objection was that not all TPMs implement
> > > > locality, but then not all laptops have TPMs either, so if you
> > > > ever come across one which has a TPM but no locality, it's in a
> > > > very similar security boat to one which has no TPM.
> > >
> > > Kernel could try to use locality 2 and use locality 0 as fallback.
> >
> > I don't think that would work for Matthew, they need something
> > reliable to indicate key provenance.
>
> No, I think it would be good enough: locality 0 means anyone (including
> the kernel on a machine which doesn't function correctly) could have
> created this key. Locality 2 would mean only the kernel could have
> created this key.
>
> By the time the kernel boots and before it loads the hibernation image
> it will know the answer to the question "does my TPM support locality
> 2", so it can use that in its security assessment: if the kernel
> supports locality 2 and the key wasn't created in locality 2 then
> assume an attack. Obviously, if the kernel doesn't support locality 2
> then the hibernation resume has to accept any old key, but that's the
> same as the situation today.

This sounds otherwise great to me but why bother even allowing a
machine with no-locality TPM to be involved with hibernate? Simply
detect locality support during driver initialization and disallow
sealed hibernation (or whatever the feature was called) if localities
were not detected.

I get supporting old hardware with old features but it does not make
sense to maintain new features with hardware, which clearly does not
scale, right?

BR, Jarkko

2023-01-26 17:32:42

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Thu, Jan 26, 2023 at 11:21 AM Jarkko Sakkinen <[email protected]> wrote:
>
> On Tue, Jan 24, 2023 at 07:38:04AM -0500, James Bottomley wrote:
> > On Mon, 2023-01-23 at 11:48 -0600, William Roberts wrote:
> > > On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]>
> > > wrote:
> > > >
> > > > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > > > <[email protected]> wrote:
> > > > > >
> > > > > > > What's the use case of using the creation data and ticket in
> > > > > > > this context? Who gets the creationData and the ticket?
> > > > > > > Could a user supplied outsideInfo work? IIRC I saw some
> > > > > > > patches flying around where the sessions will get encrypted
> > > > > > > and presumably correctly as well. This would allow the
> > > > > > > transfer of that outsideInfo, like the NV Index PCR value to
> > > > > > > be included and integrity protected by the session HMAC.
> > > > > >
> > > > > > The goal is to ensure that the key was generated by the kernel.
> > > > > > In the absence of the creation data, an attacker could generate
> > > > > > a hibernation image using their own key and trick the kernel
> > > > > > into resuming arbitrary code. We don't have any way to pass
> > > > > > secret data from the hibernate kernel to the resume kernel, so
> > > > > > I don't think there's any easy way to do it with outsideinfo.
> > > > >
> > > > > Can we go back again to why you can't use locality? It's exactly
> > > > > designed for this since locality is part of creation data.
> > > > > Currently everything only uses locality 0, so it's impossible for
> > > > > anyone on Linux to produce a key with anything other than 0 in
> > > > > the creation data for locality. However, the dynamic launch
> > > > > people are proposing that the Kernel should use Locality 2 for
> > > > > all its operations, which would allow you to distinguish a key
> > > > > created by the kernel from one created by a user by locality.
> > > > >
> > > > > I think the previous objection was that not all TPMs implement
> > > > > locality, but then not all laptops have TPMs either, so if you
> > > > > ever come across one which has a TPM but no locality, it's in a
> > > > > very similar security boat to one which has no TPM.
> > > >
> > > > Kernel could try to use locality 2 and use locality 0 as fallback.
> > >
> > > I don't think that would work for Matthew, they need something
> > > reliable to indicate key provenance.
> >
> > No, I think it would be good enough: locality 0 means anyone (including
> > the kernel on a machine which doesn't function correctly) could have
> > created this key. Locality 2 would mean only the kernel could have
> > created this key.
> >
> > By the time the kernel boots and before it loads the hibernation image
> > it will know the answer to the question "does my TPM support locality
> > 2", so it can use that in its security assessment: if the kernel
> > supports locality 2 and the key wasn't created in locality 2 then
> > assume an attack. Obviously, if the kernel doesn't support locality 2
> > then the hibernation resume has to accept any old key, but that's the
> > same as the situation today.
>
> This sounds otherwise great to me but why bother even allowing a
> machine with no-locality TPM to be involved with hibernate? Simply
> detect locality support during driver initialization and disallow
> sealed hibernation (or whatever the feature was called) if localities
> were not detected.
>
> I get supporting old hardware with old features but it does not make
> sense to maintain new features with hardware, which clearly does not
> scale, right?
>
> BR, Jarkko

Here's a thought, what if we had a static/cmd line configurable
no-auth NV Index and writelocked it with the expected key information,
name or something. I guess the problem is atomicity with write/lock,
but can't the kernel lock out all other users?

An attacker would need to issue tpm2_startup, which in this case would DOS
the kernel in both scenarios. If an attacker already wrote and locked the NV
index, that would also be a DOS. If they already wrote it, the kernel simply
writes whatever they want. Is there an attack I am missing?

I guess the issue here would be setup, since creating the NV index requires
hierarchy auth, does the kernel have platform auth or is that already shut down
by firmware (I can't recall)? A null hierarchy volatile lockable index would be
nice for this, too bad that doesn't exist.

2023-01-26 21:31:02

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Thu, Jan 26, 2023 at 11:32:22AM -0600, William Roberts wrote:
> On Thu, Jan 26, 2023 at 11:21 AM Jarkko Sakkinen <[email protected]> wrote:
> >
> > On Tue, Jan 24, 2023 at 07:38:04AM -0500, James Bottomley wrote:
> > > On Mon, 2023-01-23 at 11:48 -0600, William Roberts wrote:
> > > > On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]>
> > > > wrote:
> > > > >
> > > > > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > > > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > > > > <[email protected]> wrote:
> > > > > > >
> > > > > > > > What's the use case of using the creation data and ticket in
> > > > > > > > this context? Who gets the creationData and the ticket?
> > > > > > > > Could a user supplied outsideInfo work? IIRC I saw some
> > > > > > > > patches flying around where the sessions will get encrypted
> > > > > > > > and presumably correctly as well. This would allow the
> > > > > > > > transfer of that outsideInfo, like the NV Index PCR value to
> > > > > > > > be included and integrity protected by the session HMAC.
> > > > > > >
> > > > > > > The goal is to ensure that the key was generated by the kernel.
> > > > > > > In the absence of the creation data, an attacker could generate
> > > > > > > a hibernation image using their own key and trick the kernel
> > > > > > > into resuming arbitrary code. We don't have any way to pass
> > > > > > > secret data from the hibernate kernel to the resume kernel, so
> > > > > > > I don't think there's any easy way to do it with outsideinfo.
> > > > > >
> > > > > > Can we go back again to why you can't use locality? It's exactly
> > > > > > designed for this since locality is part of creation data.
> > > > > > Currently everything only uses locality 0, so it's impossible for
> > > > > > anyone on Linux to produce a key with anything other than 0 in
> > > > > > the creation data for locality. However, the dynamic launch
> > > > > > people are proposing that the Kernel should use Locality 2 for
> > > > > > all its operations, which would allow you to distinguish a key
> > > > > > created by the kernel from one created by a user by locality.
> > > > > >
> > > > > > I think the previous objection was that not all TPMs implement
> > > > > > locality, but then not all laptops have TPMs either, so if you
> > > > > > ever come across one which has a TPM but no locality, it's in a
> > > > > > very similar security boat to one which has no TPM.
> > > > >
> > > > > Kernel could try to use locality 2 and use locality 0 as fallback.
> > > >
> > > > I don't think that would work for Matthew, they need something
> > > > reliable to indicate key provenance.
> > >
> > > No, I think it would be good enough: locality 0 means anyone (including
> > > the kernel on a machine which doesn't function correctly) could have
> > > created this key. Locality 2 would mean only the kernel could have
> > > created this key.
> > >
> > > By the time the kernel boots and before it loads the hibernation image
> > > it will know the answer to the question "does my TPM support locality
> > > 2", so it can use that in its security assessment: if the kernel
> > > supports locality 2 and the key wasn't created in locality 2 then
> > > assume an attack. Obviously, if the kernel doesn't support locality 2
> > > then the hibernation resume has to accept any old key, but that's the
> > > same as the situation today.
> >
> > This sounds otherwise great to me but why bother even allowing a
> > machine with no-locality TPM to be involved with hibernate? Simply
> > detect locality support during driver initialization and disallow
> > sealed hibernation (or whatever the feature was called) if localities
> > were not detected.
> >
> > I get supporting old hardware with old features but it does not make
> > sense to maintain new features with hardware, which clearly does not
> > scale, right?
> >
> > BR, Jarkko
>
> Here's a thought, what if we had a static/cmd line configurable
> no-auth NV Index and writelocked it with the expected key information,
> name or something. I guess the problem is atomicity with write/lock,
> but can't the kernel lock out all other users?
>
> An attacker would need to issue tpm2_startup, which in this case would DOS
> the kernel in both scenarios. If an attacker already wrote and locked the NV
> index, that would also be a DOS. If they already wrote it, the kernel simply
> writes whatever they want. Is there an attack I am missing?
>
> I guess the issue here would be setup, since creating the NV index requires
> hierarchy auth, does the kernel have platform auth or is that already shut down
> by firmware (I can't recall)? A null hierarchy volatile lockable index would be
> nice for this, too bad that doesn't exist.

How do you see this would better when compared to finding a way to use
locality, which could potentially be made to somewhat simple to setup
(practically zero config)?

BR, Jarkko

2023-01-26 22:02:18

by William Roberts

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Thu, Jan 26, 2023 at 3:30 PM Jarkko Sakkinen <[email protected]> wrote:
>
> On Thu, Jan 26, 2023 at 11:32:22AM -0600, William Roberts wrote:
> > On Thu, Jan 26, 2023 at 11:21 AM Jarkko Sakkinen <[email protected]> wrote:
> > >
> > > On Tue, Jan 24, 2023 at 07:38:04AM -0500, James Bottomley wrote:
> > > > On Mon, 2023-01-23 at 11:48 -0600, William Roberts wrote:
> > > > > On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]>
> > > > > wrote:
> > > > > >
> > > > > > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > > > > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > > > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > > > > > <[email protected]> wrote:
> > > > > > > >
> > > > > > > > > What's the use case of using the creation data and ticket in
> > > > > > > > > this context? Who gets the creationData and the ticket?
> > > > > > > > > Could a user supplied outsideInfo work? IIRC I saw some
> > > > > > > > > patches flying around where the sessions will get encrypted
> > > > > > > > > and presumably correctly as well. This would allow the
> > > > > > > > > transfer of that outsideInfo, like the NV Index PCR value to
> > > > > > > > > be included and integrity protected by the session HMAC.
> > > > > > > >
> > > > > > > > The goal is to ensure that the key was generated by the kernel.
> > > > > > > > In the absence of the creation data, an attacker could generate
> > > > > > > > a hibernation image using their own key and trick the kernel
> > > > > > > > into resuming arbitrary code. We don't have any way to pass
> > > > > > > > secret data from the hibernate kernel to the resume kernel, so
> > > > > > > > I don't think there's any easy way to do it with outsideinfo.
> > > > > > >
> > > > > > > Can we go back again to why you can't use locality? It's exactly
> > > > > > > designed for this since locality is part of creation data.
> > > > > > > Currently everything only uses locality 0, so it's impossible for
> > > > > > > anyone on Linux to produce a key with anything other than 0 in
> > > > > > > the creation data for locality. However, the dynamic launch
> > > > > > > people are proposing that the Kernel should use Locality 2 for
> > > > > > > all its operations, which would allow you to distinguish a key
> > > > > > > created by the kernel from one created by a user by locality.
> > > > > > >
> > > > > > > I think the previous objection was that not all TPMs implement
> > > > > > > locality, but then not all laptops have TPMs either, so if you
> > > > > > > ever come across one which has a TPM but no locality, it's in a
> > > > > > > very similar security boat to one which has no TPM.
> > > > > >
> > > > > > Kernel could try to use locality 2 and use locality 0 as fallback.
> > > > >
> > > > > I don't think that would work for Matthew, they need something
> > > > > reliable to indicate key provenance.
> > > >
> > > > No, I think it would be good enough: locality 0 means anyone (including
> > > > the kernel on a machine which doesn't function correctly) could have
> > > > created this key. Locality 2 would mean only the kernel could have
> > > > created this key.
> > > >
> > > > By the time the kernel boots and before it loads the hibernation image
> > > > it will know the answer to the question "does my TPM support locality
> > > > 2", so it can use that in its security assessment: if the kernel
> > > > supports locality 2 and the key wasn't created in locality 2 then
> > > > assume an attack. Obviously, if the kernel doesn't support locality 2
> > > > then the hibernation resume has to accept any old key, but that's the
> > > > same as the situation today.
> > >
> > > This sounds otherwise great to me but why bother even allowing a
> > > machine with no-locality TPM to be involved with hibernate? Simply
> > > detect locality support during driver initialization and disallow
> > > sealed hibernation (or whatever the feature was called) if localities
> > > were not detected.
> > >
> > > I get supporting old hardware with old features but it does not make
> > > sense to maintain new features with hardware, which clearly does not
> > > scale, right?
> > >
> > > BR, Jarkko
> >
> > Here's a thought, what if we had a static/cmd line configurable
> > no-auth NV Index and writelocked it with the expected key information,
> > name or something. I guess the problem is atomicity with write/lock,
> > but can't the kernel lock out all other users?
> >
> > An attacker would need to issue tpm2_startup, which in this case would DOS
> > the kernel in both scenarios. If an attacker already wrote and locked the NV
> > index, that would also be a DOS. If they already wrote it, the kernel simply
> > writes whatever they want. Is there an attack I am missing?
> >
> > I guess the issue here would be setup, since creating the NV index requires
> > hierarchy auth, does the kernel have platform auth or is that already shut down
> > by firmware (I can't recall)? A null hierarchy volatile lockable index would be
> > nice for this, too bad that doesn't exist.
>
> How do you see this would better when compared to finding a way to use
> locality, which could potentially be made to somewhat simple to setup
> (practically zero config)?
>

I never said it was better, I said here is a thought for discussion.
If we had to support older hardware (I could care less about things
that don't support localities, but some might not), this could be an
avenue to support them without walling off a PCR. I pointed out the
downsides, and argument could be made that when localities is not
supported then walling off PCR23 is the better approach if older
hardware is an issue. This all hinges on do we care about things
that don't support multiple localities. I don't, im for if you have locality
support you get the feature else you don't.


> BR, Jarkko

2023-02-07 23:20:26

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v5 03/11] tpm: Allow PCR 23 to be restricted to kernel-only use

On Thu, Jan 26, 2023 at 04:01:55PM -0600, William Roberts wrote:
> On Thu, Jan 26, 2023 at 3:30 PM Jarkko Sakkinen <[email protected]> wrote:
> >
> > On Thu, Jan 26, 2023 at 11:32:22AM -0600, William Roberts wrote:
> > > On Thu, Jan 26, 2023 at 11:21 AM Jarkko Sakkinen <[email protected]> wrote:
> > > >
> > > > On Tue, Jan 24, 2023 at 07:38:04AM -0500, James Bottomley wrote:
> > > > > On Mon, 2023-01-23 at 11:48 -0600, William Roberts wrote:
> > > > > > On Fri, Jan 20, 2023 at 9:29 PM Jarkko Sakkinen <[email protected]>
> > > > > > wrote:
> > > > > > >
> > > > > > > On Sat, Jan 14, 2023 at 09:55:37AM -0500, James Bottomley wrote:
> > > > > > > > On Tue, 2023-01-03 at 13:10 -0800, Matthew Garrett wrote:
> > > > > > > > > On Tue, Jan 3, 2023 at 1:05 PM William Roberts
> > > > > > > > > <[email protected]> wrote:
> > > > > > > > >
> > > > > > > > > > What's the use case of using the creation data and ticket in
> > > > > > > > > > this context? Who gets the creationData and the ticket?
> > > > > > > > > > Could a user supplied outsideInfo work? IIRC I saw some
> > > > > > > > > > patches flying around where the sessions will get encrypted
> > > > > > > > > > and presumably correctly as well. This would allow the
> > > > > > > > > > transfer of that outsideInfo, like the NV Index PCR value to
> > > > > > > > > > be included and integrity protected by the session HMAC.
> > > > > > > > >
> > > > > > > > > The goal is to ensure that the key was generated by the kernel.
> > > > > > > > > In the absence of the creation data, an attacker could generate
> > > > > > > > > a hibernation image using their own key and trick the kernel
> > > > > > > > > into resuming arbitrary code. We don't have any way to pass
> > > > > > > > > secret data from the hibernate kernel to the resume kernel, so
> > > > > > > > > I don't think there's any easy way to do it with outsideinfo.
> > > > > > > >
> > > > > > > > Can we go back again to why you can't use locality? It's exactly
> > > > > > > > designed for this since locality is part of creation data.
> > > > > > > > Currently everything only uses locality 0, so it's impossible for
> > > > > > > > anyone on Linux to produce a key with anything other than 0 in
> > > > > > > > the creation data for locality. However, the dynamic launch
> > > > > > > > people are proposing that the Kernel should use Locality 2 for
> > > > > > > > all its operations, which would allow you to distinguish a key
> > > > > > > > created by the kernel from one created by a user by locality.
> > > > > > > >
> > > > > > > > I think the previous objection was that not all TPMs implement
> > > > > > > > locality, but then not all laptops have TPMs either, so if you
> > > > > > > > ever come across one which has a TPM but no locality, it's in a
> > > > > > > > very similar security boat to one which has no TPM.
> > > > > > >
> > > > > > > Kernel could try to use locality 2 and use locality 0 as fallback.
> > > > > >
> > > > > > I don't think that would work for Matthew, they need something
> > > > > > reliable to indicate key provenance.
> > > > >
> > > > > No, I think it would be good enough: locality 0 means anyone (including
> > > > > the kernel on a machine which doesn't function correctly) could have
> > > > > created this key. Locality 2 would mean only the kernel could have
> > > > > created this key.
> > > > >
> > > > > By the time the kernel boots and before it loads the hibernation image
> > > > > it will know the answer to the question "does my TPM support locality
> > > > > 2", so it can use that in its security assessment: if the kernel
> > > > > supports locality 2 and the key wasn't created in locality 2 then
> > > > > assume an attack. Obviously, if the kernel doesn't support locality 2
> > > > > then the hibernation resume has to accept any old key, but that's the
> > > > > same as the situation today.
> > > >
> > > > This sounds otherwise great to me but why bother even allowing a
> > > > machine with no-locality TPM to be involved with hibernate? Simply
> > > > detect locality support during driver initialization and disallow
> > > > sealed hibernation (or whatever the feature was called) if localities
> > > > were not detected.
> > > >
> > > > I get supporting old hardware with old features but it does not make
> > > > sense to maintain new features with hardware, which clearly does not
> > > > scale, right?
> > > >
> > > > BR, Jarkko
> > >
> > > Here's a thought, what if we had a static/cmd line configurable
> > > no-auth NV Index and writelocked it with the expected key information,
> > > name or something. I guess the problem is atomicity with write/lock,
> > > but can't the kernel lock out all other users?
> > >
> > > An attacker would need to issue tpm2_startup, which in this case would DOS
> > > the kernel in both scenarios. If an attacker already wrote and locked the NV
> > > index, that would also be a DOS. If they already wrote it, the kernel simply
> > > writes whatever they want. Is there an attack I am missing?
> > >
> > > I guess the issue here would be setup, since creating the NV index requires
> > > hierarchy auth, does the kernel have platform auth or is that already shut down
> > > by firmware (I can't recall)? A null hierarchy volatile lockable index would be
> > > nice for this, too bad that doesn't exist.
> >
> > How do you see this would better when compared to finding a way to use
> > locality, which could potentially be made to somewhat simple to setup
> > (practically zero config)?
> >
>
> I never said it was better, I said here is a thought for discussion.
> If we had to support older hardware (I could care less about things
> that don't support localities, but some might not), this could be an
> avenue to support them without walling off a PCR. I pointed out the
> downsides, and argument could be made that when localities is not
> supported then walling off PCR23 is the better approach if older
> hardware is an issue. This all hinges on do we care about things
> that don't support multiple localities. I don't, im for if you have locality
> support you get the feature else you don't.

Probably does not make much sense to care for this feature.

BR, Jarkko